summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY492
-rw-r--r--Makefile22
-rw-r--r--RELEASE-NOTES1025
-rw-r--r--StartProfiler.php2
-rw-r--r--Test.php623
-rw-r--r--UPGRADE34
-rw-r--r--api.php25
-rw-r--r--api.php51
-rw-r--r--config/index.php291
-rw-r--r--config/index.php56
-rw-r--r--docs/README6
-rw-r--r--docs/design.txt25
-rw-r--r--docs/hooks.txt332
-rw-r--r--docs/memcached.txt2
-rw-r--r--docs/schema.txt3
-rw-r--r--docs/title.txt9
-rw-r--r--extensions/LLAuthPlugin.php15
-rw-r--r--img_auth.php86
-rw-r--r--img_auth.php51
-rw-r--r--includes/AjaxDispatcher.php51
-rw-r--r--includes/AjaxFunctions.php34
-rw-r--r--includes/AjaxResponse.php32
-rw-r--r--includes/Article.php345
-rw-r--r--includes/AuthPlugin.php5
-rw-r--r--includes/AutoLoader.php50
-rw-r--r--includes/BagOStuff.php134
-rw-r--r--includes/Block.php23
-rw-r--r--includes/CacheDependency.php2
-rw-r--r--includes/CategoryPage.php37
-rw-r--r--includes/Categoryfinder.php2
-rw-r--r--includes/ChangesList.php11
-rw-r--r--includes/CoreParserFunctions.php27
-rw-r--r--includes/Credits.php2
-rw-r--r--includes/Database.php173
-rw-r--r--includes/DatabaseFunctions.php2
-rw-r--r--includes/DatabaseOracle.php18
-rw-r--r--includes/DatabasePostgres.php98
-rw-r--r--includes/DateFormatter.php2
-rw-r--r--includes/DefaultSettings.php319
-rw-r--r--includes/Defines.php58
-rw-r--r--includes/DifferenceEngine.php153
-rw-r--r--includes/DjVuImage.php2
-rw-r--r--includes/EditPage.php258
-rw-r--r--includes/EmaillingJob.php25
-rw-r--r--includes/EnotifNotifyJob.php26
-rw-r--r--includes/Exception.php2
-rw-r--r--includes/Exif.php4
-rw-r--r--includes/Export.php4
-rw-r--r--includes/ExternalEdit.php4
-rw-r--r--includes/ExternalStore.php7
-rw-r--r--includes/ExternalStoreDB.php2
-rw-r--r--includes/ExternalStoreHttp.php2
-rw-r--r--includes/FakeTitle.php2
-rw-r--r--includes/FileDeleteForm.php220
-rw-r--r--includes/FileRevertForm.php165
-rw-r--r--includes/FileStore.php8
-rw-r--r--includes/GlobalFunctions.php186
-rw-r--r--includes/HTMLCacheUpdate.php32
-rw-r--r--includes/HTMLFileCache.php2
-rw-r--r--includes/HTMLForm.php53
-rw-r--r--includes/HistoryBlob.php2
-rw-r--r--includes/Hooks.php16
-rw-r--r--includes/HttpFunctions.php38
-rw-r--r--includes/IP.php47
-rw-r--r--includes/ImageFunctions.php111
-rw-r--r--includes/ImageGallery.php103
-rw-r--r--includes/ImagePage.php544
-rw-r--r--includes/ImageQueryPage.php8
-rw-r--r--includes/JobQueue.php112
-rw-r--r--includes/Licenses.php2
-rw-r--r--includes/LinkBatch.php33
-rw-r--r--includes/LinkCache.php2
-rw-r--r--includes/LinkFilter.php2
-rw-r--r--includes/Linker.php574
-rw-r--r--includes/LinksUpdate.php16
-rw-r--r--includes/LoadBalancer.php2
-rw-r--r--includes/LogPage.php71
-rw-r--r--includes/MacBinary.php4
-rw-r--r--includes/MagicWord.php178
-rw-r--r--includes/Math.php49
-rw-r--r--includes/MediaTransformOutput.php98
-rw-r--r--includes/MemcachedSessions.php2
-rw-r--r--includes/MessageCache.php33
-rw-r--r--includes/Metadata.php2
-rw-r--r--includes/MimeMagic.php538
-rw-r--r--includes/Namespace.php88
-rw-r--r--includes/ObjectCache.php4
-rw-r--r--includes/OutputHandler.php75
-rw-r--r--includes/OutputPage.php281
-rw-r--r--includes/PageHistory.php26
-rw-r--r--includes/PageQueryPage.php2
-rw-r--r--includes/Pager.php122
-rw-r--r--includes/Parser.php420
-rw-r--r--includes/ParserCache.php2
-rw-r--r--includes/ParserOptions.php2
-rw-r--r--includes/ParserOutput.php59
-rw-r--r--includes/PatrolLog.php18
-rw-r--r--includes/Profiler.php5
-rw-r--r--includes/ProfilerSimple.php2
-rw-r--r--includes/ProfilerSimpleUDP.php2
-rw-r--r--includes/ProfilerStub.php2
-rw-r--r--includes/ProtectionForm.php90
-rw-r--r--includes/ProxyTools.php2
-rw-r--r--includes/QueryPage.php18
-rw-r--r--includes/RawPage.php2
-rw-r--r--includes/RecentChange.php72
-rw-r--r--includes/RefreshLinksJob.php48
-rw-r--r--includes/Revision.php2
-rw-r--r--includes/Sanitizer.php92
-rw-r--r--includes/SearchEngine.php21
-rw-r--r--includes/SearchMySQL.php6
-rw-r--r--includes/SearchMySQL4.php2
-rw-r--r--includes/SearchOracle.php2
-rw-r--r--includes/SearchPostgres.php3
-rw-r--r--includes/SearchTsearch2.php2
-rw-r--r--includes/SearchUpdate.php2
-rw-r--r--includes/Setup.php74
-rw-r--r--includes/SiteConfiguration.php2
-rw-r--r--includes/SiteStats.php62
-rw-r--r--includes/Skin.php104
-rw-r--r--includes/SkinTemplate.php60
-rw-r--r--includes/SpecialAllmessages.php5
-rw-r--r--includes/SpecialAllpages.php112
-rw-r--r--includes/SpecialAncientpages.php2
-rw-r--r--includes/SpecialBlockip.php69
-rw-r--r--includes/SpecialBlockme.php2
-rw-r--r--includes/SpecialBooksources.php22
-rw-r--r--includes/SpecialBrokenRedirects.php8
-rw-r--r--includes/SpecialCategories.php13
-rw-r--r--includes/SpecialConfirmemail.php23
-rw-r--r--includes/SpecialContributions.php129
-rw-r--r--includes/SpecialDeadendpages.php4
-rw-r--r--includes/SpecialDisambiguations.php4
-rw-r--r--includes/SpecialDoubleRedirects.php9
-rw-r--r--includes/SpecialEmailuser.php11
-rw-r--r--includes/SpecialExport.php46
-rw-r--r--includes/SpecialFewestrevisions.php2
-rw-r--r--includes/SpecialImagelist.php20
-rw-r--r--includes/SpecialImport.php31
-rw-r--r--includes/SpecialIpblocklist.php168
-rw-r--r--includes/SpecialListredirects.php2
-rw-r--r--includes/SpecialListusers.php28
-rw-r--r--includes/SpecialLockdb.php2
-rw-r--r--includes/SpecialLog.php106
-rw-r--r--includes/SpecialLonelypages.php4
-rw-r--r--includes/SpecialLongpages.php2
-rw-r--r--includes/SpecialMIMEsearch.php31
-rw-r--r--includes/SpecialMostcategories.php3
-rw-r--r--includes/SpecialMostimages.php2
-rw-r--r--includes/SpecialMostlinked.php2
-rw-r--r--includes/SpecialMostlinkedcategories.php2
-rw-r--r--includes/SpecialMostlinkedtemplates.php131
-rw-r--r--includes/SpecialMostrevisions.php2
-rw-r--r--includes/SpecialMovepage.php65
-rw-r--r--includes/SpecialNewimages.php5
-rw-r--r--includes/SpecialNewpages.php22
-rw-r--r--includes/SpecialPage.php30
-rw-r--r--includes/SpecialPopularpages.php2
-rw-r--r--includes/SpecialPreferences.php295
-rw-r--r--includes/SpecialPrefixindex.php26
-rw-r--r--includes/SpecialProtectedpages.php132
-rw-r--r--includes/SpecialRandompage.php2
-rw-r--r--includes/SpecialRandomredirect.php2
-rw-r--r--includes/SpecialRecentchanges.php16
-rw-r--r--includes/SpecialRecentchangeslinked.php3
-rw-r--r--includes/SpecialResetpass.php4
-rw-r--r--includes/SpecialRevisiondelete.php2
-rw-r--r--includes/SpecialSearch.php15
-rw-r--r--includes/SpecialShortpages.php2
-rw-r--r--includes/SpecialSpecialpages.php6
-rw-r--r--includes/SpecialStatistics.php89
-rw-r--r--includes/SpecialUncategorizedcategories.php2
-rw-r--r--includes/SpecialUncategorizedimages.php2
-rw-r--r--includes/SpecialUncategorizedpages.php2
-rw-r--r--includes/SpecialUncategorizedtemplates.php31
-rw-r--r--includes/SpecialUndelete.php146
-rw-r--r--includes/SpecialUnlockdb.php2
-rw-r--r--includes/SpecialUnusedcategories.php4
-rw-r--r--includes/SpecialUnusedimages.php2
-rw-r--r--includes/SpecialUnusedtemplates.php5
-rw-r--r--includes/SpecialUnwatchedpages.php2
-rw-r--r--includes/SpecialUpload.php900
-rw-r--r--includes/SpecialUploadMogile.php2
-rw-r--r--includes/SpecialUserlogin.php34
-rw-r--r--includes/SpecialUserlogout.php2
-rw-r--r--includes/SpecialUserrights.php225
-rw-r--r--includes/SpecialVersion.php17
-rw-r--r--includes/SpecialWantedcategories.php2
-rw-r--r--includes/SpecialWantedpages.php66
-rw-r--r--includes/SpecialWatchlist.php194
-rw-r--r--includes/SpecialWhatlinkshere.php35
-rw-r--r--includes/SpecialWithoutinterwiki.php10
-rw-r--r--includes/SquidUpdate.php21
-rw-r--r--includes/StreamFile.php5
-rw-r--r--includes/StringUtils.php2
-rw-r--r--includes/StubObject.php2
-rw-r--r--includes/Title.php338
-rw-r--r--includes/User.php284
-rw-r--r--includes/UserMailer.php195
-rw-r--r--includes/Utf8Case.php2
-rw-r--r--includes/WatchedItem.php17
-rw-r--r--includes/WatchlistEditor.php493
-rw-r--r--includes/WebRequest.php92
-rw-r--r--includes/WebResponse.php2
-rw-r--r--includes/WebStart.php2
-rw-r--r--includes/Wiki.php39
-rw-r--r--includes/WikiError.php2
-rw-r--r--includes/Xml.php180
-rw-r--r--includes/XmlFunctions.php5
-rw-r--r--includes/ZhClient.php1
-rw-r--r--includes/ZhConversion.php1
-rw-r--r--includes/api/ApiBase.php165
-rw-r--r--includes/api/ApiFeedWatchlist.php121
-rw-r--r--includes/api/ApiFormatBase.php41
-rw-r--r--includes/api/ApiFormatJson.php31
-rw-r--r--includes/api/ApiFormatJson_json.php2
-rw-r--r--includes/api/ApiFormatPhp.php6
-rw-r--r--includes/api/ApiFormatWddx.php6
-rw-r--r--includes/api/ApiFormatXml.php6
-rw-r--r--includes/api/ApiFormatYaml.php6
-rw-r--r--includes/api/ApiFormatYaml_spyc.php2
-rw-r--r--includes/api/ApiHelp.php8
-rw-r--r--includes/api/ApiLogin.php139
-rw-r--r--includes/api/ApiMain.php268
-rw-r--r--includes/api/ApiOpenSearch.php14
-rw-r--r--includes/api/ApiPageSet.php115
-rw-r--r--includes/api/ApiQuery.php253
-rw-r--r--includes/api/ApiQueryAllLinks.php179
-rw-r--r--includes/api/ApiQueryAllUsers.php204
-rw-r--r--includes/api/ApiQueryAllpages.php104
-rw-r--r--includes/api/ApiQueryBacklinks.php125
-rw-r--r--includes/api/ApiQueryBase.php247
-rw-r--r--includes/api/ApiQueryCategories.php157
-rw-r--r--includes/api/ApiQueryCategoryMembers.php238
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php200
-rw-r--r--includes/api/ApiQueryExternalLinks.php93
-rw-r--r--includes/api/ApiQueryImageInfo.php156
-rw-r--r--includes/api/ApiQueryImages.php118
-rw-r--r--includes/api/ApiQueryInfo.php126
-rw-r--r--includes/api/ApiQueryLangLinks.php94
-rw-r--r--includes/api/ApiQueryLinks.php162
-rw-r--r--includes/api/ApiQueryLogEvents.php155
-rw-r--r--includes/api/ApiQueryRecentChanges.php117
-rw-r--r--includes/api/ApiQueryRevisions.php187
-rw-r--r--includes/api/ApiQuerySearch.php151
-rw-r--r--includes/api/ApiQuerySiteinfo.php202
-rw-r--r--includes/api/ApiQueryUserContributions.php231
-rw-r--r--includes/api/ApiQueryUserInfo.php133
-rw-r--r--includes/api/ApiQueryWatchlist.php133
-rw-r--r--includes/api/ApiResult.php42
-rw-r--r--includes/cbt/CBTCompiler.php2
-rw-r--r--includes/cbt/CBTProcessor.php2
-rw-r--r--includes/filerepo/ArchivedFile.php108
-rw-r--r--includes/filerepo/FSRepo.php530
-rw-r--r--includes/filerepo/File.php1133
-rw-r--r--includes/filerepo/FileRepo.php404
-rw-r--r--includes/filerepo/FileRepoStatus.php171
-rw-r--r--includes/filerepo/ForeignDBFile.php42
-rw-r--r--includes/filerepo/ForeignDBRepo.php57
-rw-r--r--includes/filerepo/ICRepo.php313
-rw-r--r--includes/filerepo/LocalFile.php1573
-rw-r--r--includes/filerepo/LocalRepo.php65
-rw-r--r--includes/filerepo/OldLocalFile.php232
-rw-r--r--includes/filerepo/README41
-rw-r--r--includes/filerepo/RepoGroup.php150
-rw-r--r--includes/filerepo/UnregisteredLocalFile.php109
-rw-r--r--includes/media/BMP.php10
-rw-r--r--includes/media/Bitmap.php91
-rw-r--r--includes/media/DjVu.php16
-rw-r--r--includes/media/Generic.php179
-rw-r--r--includes/media/SVG.php21
-rw-r--r--includes/memcached-client.php40
-rw-r--r--includes/normal/CleanUpTest.php2
-rw-r--r--includes/normal/RandomTest.php2
-rw-r--r--includes/normal/Utf8Test.php2
-rw-r--r--includes/normal/UtfNormal.php55
-rw-r--r--includes/normal/UtfNormalBench.php2
-rw-r--r--includes/normal/UtfNormalGenerate.php2
-rw-r--r--includes/normal/UtfNormalTest.php2
-rw-r--r--includes/normal/UtfNormalUtil.php2
-rw-r--r--includes/proxy_check.php2
-rw-r--r--includes/templates/NoLocalSettings.php5
-rw-r--r--includes/templates/Userlogin.php49
-rw-r--r--index.php2
-rw-r--r--index.php51
-rw-r--r--install-utils.inc19
-rw-r--r--languages/Language.php159
-rw-r--r--languages/LanguageConverter.php4
-rw-r--r--languages/Names.php29
-rw-r--r--languages/classes/LanguageAr.php1
-rw-r--r--languages/classes/LanguageAz.php2
-rw-r--r--languages/classes/LanguageBe.php2
-rw-r--r--languages/classes/LanguageBe_tarask.php88
-rw-r--r--languages/classes/LanguageBg.php2
-rw-r--r--languages/classes/LanguageBs.php2
-rw-r--r--languages/classes/LanguageCs.php2
-rw-r--r--languages/classes/LanguageCu.php2
-rw-r--r--languages/classes/LanguageEo.php2
-rw-r--r--languages/classes/LanguageEt.php2
-rw-r--r--languages/classes/LanguageFi.php13
-rw-r--r--languages/classes/LanguageFr.php2
-rw-r--r--languages/classes/LanguageGa.php2
-rw-r--r--languages/classes/LanguageGsw.php2
-rw-r--r--languages/classes/LanguageHe.php2
-rw-r--r--languages/classes/LanguageHr.php2
-rw-r--r--languages/classes/LanguageHu.php2
-rw-r--r--languages/classes/LanguageHy.php2
-rw-r--r--languages/classes/LanguageJa.php2
-rw-r--r--languages/classes/LanguageKk.deps.php2
-rw-r--r--languages/classes/LanguageKk.php25
-rw-r--r--languages/classes/LanguageKk_kz.php2
-rw-r--r--languages/classes/LanguageKo.php2
-rw-r--r--languages/classes/LanguageKsh.php2
-rw-r--r--languages/classes/LanguageKu.deps.php12
-rw-r--r--languages/classes/LanguageKu.php240
-rw-r--r--languages/classes/LanguageKu_ku.php35
-rw-r--r--languages/classes/LanguageLa.php2
-rw-r--r--languages/classes/LanguageLt.php2
-rw-r--r--languages/classes/LanguageLv.php2
-rw-r--r--languages/classes/LanguagePt_br.php2
-rw-r--r--languages/classes/LanguageRmy.php2
-rw-r--r--languages/classes/LanguageRu.php2
-rw-r--r--languages/classes/LanguageSk.php2
-rw-r--r--languages/classes/LanguageSl.php2
-rw-r--r--languages/classes/LanguageSr.deps.php2
-rw-r--r--languages/classes/LanguageSr.php2
-rw-r--r--languages/classes/LanguageSr_ec.php2
-rw-r--r--languages/classes/LanguageSr_el.deps.php2
-rw-r--r--languages/classes/LanguageSr_el.php2
-rw-r--r--languages/classes/LanguageTr.php2
-rw-r--r--languages/classes/LanguageTyv.php2
-rw-r--r--languages/classes/LanguageUk.php2
-rw-r--r--languages/classes/LanguageWa.php2
-rw-r--r--languages/classes/LanguageZh.deps.php2
-rw-r--r--languages/classes/LanguageZh.php4
-rw-r--r--languages/classes/LanguageZh_cn.php2
-rw-r--r--languages/classes/LanguageZh_yue.php2
-rw-r--r--languages/messages/MessagesAb.php2
-rw-r--r--languages/messages/MessagesAf.php1452
-rw-r--r--languages/messages/MessagesAn.php2
-rw-r--r--languages/messages/MessagesAr.php1760
-rw-r--r--languages/messages/MessagesArc.php2
-rw-r--r--languages/messages/MessagesAs.php22
-rw-r--r--languages/messages/MessagesAst.php2
-rw-r--r--languages/messages/MessagesAv.php1
-rw-r--r--languages/messages/MessagesAy.php2
-rw-r--r--languages/messages/MessagesAz.php13
-rw-r--r--languages/messages/MessagesBa.php4
-rw-r--r--languages/messages/MessagesBar.php2
-rw-r--r--languages/messages/MessagesBat_smg.php2
-rw-r--r--languages/messages/MessagesBcl.php913
-rw-r--r--languages/messages/MessagesBe.php257
-rw-r--r--languages/messages/MessagesBe_tarask.php1112
-rw-r--r--languages/messages/MessagesBg.php958
-rw-r--r--languages/messages/MessagesBh.php22
-rw-r--r--languages/messages/MessagesBm.php2
-rw-r--r--languages/messages/MessagesBn.php2
-rw-r--r--languages/messages/MessagesBo.php22
-rw-r--r--languages/messages/MessagesBpy.php54
-rw-r--r--languages/messages/MessagesBr.php280
-rw-r--r--languages/messages/MessagesBs.php1961
-rw-r--r--languages/messages/MessagesCa.php199
-rw-r--r--languages/messages/MessagesCe.php2
-rw-r--r--languages/messages/MessagesCs.php259
-rw-r--r--languages/messages/MessagesCsb.php640
-rw-r--r--languages/messages/MessagesCu.php180
-rw-r--r--languages/messages/MessagesCv.php7
-rw-r--r--languages/messages/MessagesCy.php1231
-rw-r--r--languages/messages/MessagesDa.php3424
-rw-r--r--languages/messages/MessagesDe.php1123
-rw-r--r--languages/messages/MessagesDv.php2
-rw-r--r--languages/messages/MessagesDz.php22
-rw-r--r--languages/messages/MessagesEl.php3151
-rw-r--r--languages/messages/MessagesEn.php3678
-rw-r--r--languages/messages/MessagesEnRTL.php2
-rw-r--r--languages/messages/MessagesEo.php783
-rw-r--r--languages/messages/MessagesEs.php3637
-rw-r--r--languages/messages/MessagesEt.php1756
-rw-r--r--languages/messages/MessagesEu.php2754
-rw-r--r--languages/messages/MessagesExt.php839
-rw-r--r--languages/messages/MessagesFa.php3036
-rw-r--r--languages/messages/MessagesFi.php448
-rw-r--r--languages/messages/MessagesFiu_vro.php245
-rw-r--r--languages/messages/MessagesFo.php6
-rw-r--r--languages/messages/MessagesFr.php788
-rw-r--r--languages/messages/MessagesFrc.php583
-rw-r--r--languages/messages/MessagesFrp.php2206
-rw-r--r--languages/messages/MessagesFur.php1400
-rw-r--r--languages/messages/MessagesFy.php1186
-rw-r--r--languages/messages/MessagesGa.php2381
-rw-r--r--languages/messages/MessagesGl.php797
-rw-r--r--languages/messages/MessagesGn.php2
-rw-r--r--languages/messages/MessagesGsw.php1486
-rw-r--r--languages/messages/MessagesGu.php22
-rw-r--r--languages/messages/MessagesHak.php1637
-rw-r--r--languages/messages/MessagesHe.php609
-rw-r--r--languages/messages/MessagesHi.php22
-rw-r--r--languages/messages/MessagesHr.php2700
-rw-r--r--languages/messages/MessagesHsb.php902
-rw-r--r--languages/messages/MessagesHt.php84
-rw-r--r--languages/messages/MessagesHu.php38
-rw-r--r--languages/messages/MessagesHy.php88
-rw-r--r--languages/messages/MessagesIa.php1102
-rw-r--r--languages/messages/MessagesId.php680
-rw-r--r--languages/messages/MessagesIi.php2
-rw-r--r--languages/messages/MessagesIs.php1972
-rw-r--r--languages/messages/MessagesIt.php631
-rw-r--r--languages/messages/MessagesJa.php387
-rw-r--r--languages/messages/MessagesJbo.php3
-rw-r--r--languages/messages/MessagesJv.php8
-rw-r--r--languages/messages/MessagesKa.php1209
-rw-r--r--languages/messages/MessagesKaa.php2
-rw-r--r--languages/messages/MessagesKab.php1084
-rw-r--r--languages/messages/MessagesKg.php2
-rw-r--r--languages/messages/MessagesKk.php2
-rw-r--r--languages/messages/MessagesKk_cn.php1150
-rw-r--r--languages/messages/MessagesKk_kz.php1130
-rw-r--r--languages/messages/MessagesKk_tr.php1179
-rw-r--r--languages/messages/MessagesKm.php22
-rw-r--r--languages/messages/MessagesKn.php643
-rw-r--r--languages/messages/MessagesKo.php496
-rw-r--r--languages/messages/MessagesKrj.php162
-rw-r--r--languages/messages/MessagesKs.php22
-rw-r--r--languages/messages/MessagesKsh.php2687
-rw-r--r--languages/messages/MessagesKu.php736
-rw-r--r--languages/messages/MessagesKu_arab.php34
-rw-r--r--languages/messages/MessagesKu_latn.php913
-rw-r--r--languages/messages/MessagesKv.php2
-rw-r--r--languages/messages/MessagesLa.php146
-rw-r--r--languages/messages/MessagesLg.php5
-rw-r--r--languages/messages/MessagesLi.php92
-rw-r--r--languages/messages/MessagesLn.php2
-rw-r--r--languages/messages/MessagesLo.php692
-rw-r--r--languages/messages/MessagesLt.php581
-rw-r--r--languages/messages/MessagesLv.php177
-rw-r--r--languages/messages/MessagesMi.php2
-rw-r--r--languages/messages/MessagesMk.php3076
-rw-r--r--languages/messages/MessagesMl.php79
-rw-r--r--languages/messages/MessagesMn.php9
-rw-r--r--languages/messages/MessagesMr.php23
-rw-r--r--languages/messages/MessagesMs.php1400
-rw-r--r--languages/messages/MessagesMt.php2
-rw-r--r--languages/messages/MessagesMy.php205
-rw-r--r--languages/messages/MessagesMzn.php2
-rw-r--r--languages/messages/MessagesNah.php2
-rw-r--r--languages/messages/MessagesNap.php2
-rw-r--r--languages/messages/MessagesNds.php997
-rw-r--r--languages/messages/MessagesNds_nl.php3
-rw-r--r--languages/messages/MessagesNe.php22
-rw-r--r--languages/messages/MessagesNew.php22
-rw-r--r--languages/messages/MessagesNl.php1127
-rw-r--r--languages/messages/MessagesNn.php2284
-rw-r--r--languages/messages/MessagesNo.php3177
-rw-r--r--languages/messages/MessagesNon.php2
-rw-r--r--languages/messages/MessagesNv.php3
-rw-r--r--languages/messages/MessagesOc.php280
-rw-r--r--languages/messages/MessagesOr.php22
-rw-r--r--languages/messages/MessagesOs.php403
-rw-r--r--languages/messages/MessagesPa.php1302
-rw-r--r--languages/messages/MessagesPi.php22
-rw-r--r--languages/messages/MessagesPl.php3161
-rw-r--r--languages/messages/MessagesPms.php874
-rw-r--r--languages/messages/MessagesPs.php2
-rw-r--r--languages/messages/MessagesPt.php3549
-rw-r--r--languages/messages/MessagesPt_br.php1208
-rw-r--r--languages/messages/MessagesQu.php2
-rw-r--r--languages/messages/MessagesRmy.php309
-rw-r--r--languages/messages/MessagesRo.php478
-rw-r--r--languages/messages/MessagesRoa_rup.php214
-rw-r--r--languages/messages/MessagesRu.php625
-rw-r--r--languages/messages/MessagesSa.php24
-rw-r--r--languages/messages/MessagesSah.php1348
-rw-r--r--languages/messages/MessagesSc.php993
-rw-r--r--languages/messages/MessagesScn.php2
-rw-r--r--languages/messages/MessagesSd.php2
-rw-r--r--languages/messages/MessagesSe.php1345
-rw-r--r--languages/messages/MessagesSk.php555
-rw-r--r--languages/messages/MessagesSl.php81
-rw-r--r--languages/messages/MessagesSn.php181
-rw-r--r--languages/messages/MessagesSo.php585
-rw-r--r--languages/messages/MessagesSq.php2847
-rw-r--r--languages/messages/MessagesSr.php2
-rw-r--r--languages/messages/MessagesSr_ec.php2848
-rw-r--r--languages/messages/MessagesSr_el.php2854
-rw-r--r--languages/messages/MessagesSr_jc.php2
-rw-r--r--languages/messages/MessagesSr_jl.php2
-rw-r--r--languages/messages/MessagesSu.php512
-rw-r--r--languages/messages/MessagesSv.php709
-rw-r--r--languages/messages/MessagesTa.php1246
-rw-r--r--languages/messages/MessagesTe.php1568
-rw-r--r--languages/messages/MessagesTg.php2
-rw-r--r--languages/messages/MessagesTh.php2279
-rw-r--r--languages/messages/MessagesTi.php180
-rw-r--r--languages/messages/MessagesTlh.php2
-rw-r--r--languages/messages/MessagesTn.php160
-rw-r--r--languages/messages/MessagesTpi.php268
-rw-r--r--languages/messages/MessagesTr.php2032
-rw-r--r--languages/messages/MessagesTt.php384
-rw-r--r--languages/messages/MessagesTy.php2
-rw-r--r--languages/messages/MessagesTyv.php399
-rw-r--r--languages/messages/MessagesUdm.php2
-rw-r--r--languages/messages/MessagesUg.php2
-rw-r--r--languages/messages/MessagesUk.php280
-rw-r--r--languages/messages/MessagesUr.php18
-rw-r--r--languages/messages/MessagesUz.php16
-rw-r--r--languages/messages/MessagesVec.php2287
-rw-r--r--languages/messages/MessagesVi.php2083
-rw-r--r--languages/messages/MessagesVls.php2
-rw-r--r--languages/messages/MessagesVo.php104
-rw-r--r--languages/messages/MessagesWa.php2573
-rw-r--r--languages/messages/MessagesWar.php136
-rw-r--r--languages/messages/MessagesWo.php8
-rw-r--r--languages/messages/MessagesXal.php2
-rw-r--r--languages/messages/MessagesYi.php1700
-rw-r--r--languages/messages/MessagesZa.php2
-rw-r--r--languages/messages/MessagesZea.php2
-rw-r--r--languages/messages/MessagesZh.php2
-rw-r--r--languages/messages/MessagesZh_classical.php845
-rw-r--r--languages/messages/MessagesZh_cn.php577
-rw-r--r--languages/messages/MessagesZh_hk.php2
-rw-r--r--languages/messages/MessagesZh_sg.php2
-rw-r--r--languages/messages/MessagesZh_tw.php614
-rw-r--r--languages/messages/MessagesZh_yue.php3417
-rw-r--r--maintenance/FiveUpgrade.inc54
-rw-r--r--maintenance/README9
-rw-r--r--maintenance/addwiki.php2
-rw-r--r--maintenance/archives/patch-archive-page_id.sql6
-rw-r--r--maintenance/archives/patch-archive-user-index.sql4
-rw-r--r--maintenance/archives/patch-categorylinksindex.sql11
-rw-r--r--maintenance/archives/patch-image-user-index.sql8
-rw-r--r--maintenance/archives/patch-img_sha1.sql8
-rw-r--r--maintenance/archives/patch-ipb_emailban.sql4
-rw-r--r--maintenance/archives/patch-oi_metadata.sql17
-rw-r--r--maintenance/archives/patch-oldimage-user-index.sql8
-rw-r--r--maintenance/archives/patch-page_restrictions.sql2
-rw-r--r--maintenance/archives/patch-rc_deleted.sql2
-rw-r--r--maintenance/archives/populateSha1.php43
-rw-r--r--maintenance/archives/upgradeWatchlist.php2
-rw-r--r--maintenance/attachLatest.php2
-rw-r--r--maintenance/attribute.php2
-rw-r--r--maintenance/benchmarkPurge.php1
-rw-r--r--maintenance/changePassword.php39
-rw-r--r--maintenance/checkUsernames.php2
-rw-r--r--maintenance/cleanupCaps.php2
-rw-r--r--maintenance/cleanupImages.php7
-rw-r--r--maintenance/cleanupSpam.php2
-rw-r--r--maintenance/cleanupTitles.php2
-rw-r--r--maintenance/cleanupWatchlist.php2
-rw-r--r--maintenance/clear_interwiki_cache.php2
-rw-r--r--maintenance/clear_stats.php2
-rw-r--r--maintenance/commandLine.inc16
-rw-r--r--maintenance/convertLinks.php2
-rw-r--r--maintenance/counter.php2
-rw-r--r--maintenance/createAndPromote.php27
-rw-r--r--maintenance/deleteArchivedFiles.inc56
-rw-r--r--maintenance/deleteArchivedFiles.php30
-rw-r--r--maintenance/deleteArchivedRevisions.inc35
-rw-r--r--maintenance/deleteArchivedRevisions.php30
-rw-r--r--maintenance/deleteBatch.php2
-rw-r--r--maintenance/deleteDefaultMessages.php6
-rw-r--r--maintenance/deleteImageMemcached.php2
-rw-r--r--maintenance/deleteOldRevisions.php1
-rw-r--r--maintenance/deleteOrphanedRevisions.inc.php1
-rw-r--r--maintenance/deleteOrphanedRevisions.php1
-rw-r--r--maintenance/deleteRevision.php2
-rw-r--r--maintenance/dumpBackup.php5
-rw-r--r--maintenance/dumpHTML.inc4
-rw-r--r--maintenance/dumpHTML.php62
-rw-r--r--maintenance/dumpInterwiki.php2
-rw-r--r--maintenance/dumpLinks.php2
-rw-r--r--maintenance/dumpSisterSites.php2
-rw-r--r--maintenance/dumpTextPass.php18
-rw-r--r--maintenance/dumpUploads.php1
-rw-r--r--maintenance/edit.php2
-rw-r--r--maintenance/eval.php2
-rw-r--r--maintenance/findhooks.php2
-rw-r--r--maintenance/fixSlaveDesync.php2
-rw-r--r--maintenance/fixTimestamps.php2
-rw-r--r--maintenance/fixUserRegistration.php2
-rw-r--r--maintenance/fuzz-tester.php38
-rw-r--r--maintenance/generateSitemap.php6
-rw-r--r--maintenance/getLagTimes.php1
-rw-r--r--maintenance/getSlaveServer.php2
-rw-r--r--maintenance/importDump.php3
-rw-r--r--maintenance/importImages.inc.php20
-rw-r--r--maintenance/importImages.php137
-rw-r--r--maintenance/importLogs.php2
-rw-r--r--maintenance/importTextFile.php1
-rw-r--r--maintenance/importUseModWiki.php4
-rw-r--r--maintenance/initEditCount.php2
-rw-r--r--maintenance/initStats.php1
-rw-r--r--maintenance/installExtension.php2
-rw-r--r--maintenance/interwiki.sql15
-rw-r--r--maintenance/language/alltrans.php2
-rw-r--r--maintenance/language/checkExtensions.php269
-rw-r--r--maintenance/language/checkLanguage.inc99
-rw-r--r--maintenance/language/checkLanguage.php42
-rw-r--r--maintenance/language/date-formats.php2
-rw-r--r--maintenance/language/diffLanguage.php2
-rw-r--r--maintenance/language/digit2html.php24
-rw-r--r--maintenance/language/dumpMessages.php2
-rw-r--r--maintenance/language/function-list.php2
-rw-r--r--maintenance/language/lang2po.php2
-rw-r--r--maintenance/language/langmemusage.php2
-rw-r--r--maintenance/language/messageTypes.inc70
-rw-r--r--maintenance/language/messages.inc250
-rw-r--r--maintenance/language/rebuildLanguage.php2
-rw-r--r--maintenance/language/splitLanguageFiles.php2
-rw-r--r--maintenance/language/transstat.php2
-rw-r--r--maintenance/language/validate.php2
-rw-r--r--maintenance/language/writeMessagesArray.inc25
-rw-r--r--maintenance/mcc.php3
-rw-r--r--maintenance/mctest.php4
-rw-r--r--maintenance/moveBatch.php2
-rw-r--r--maintenance/namespace2sql.php2
-rw-r--r--maintenance/namespaceDupes.php113
-rw-r--r--maintenance/nextJobDB.php22
-rw-r--r--maintenance/nukeNS.php1
-rw-r--r--maintenance/nukePage.php1
-rw-r--r--maintenance/ora/tables.sql16
-rw-r--r--maintenance/orphans.php1
-rw-r--r--maintenance/ourusers.php2
-rw-r--r--maintenance/parserTests.inc68
-rw-r--r--maintenance/parserTests.php2
-rw-r--r--maintenance/parserTests.txt137
-rw-r--r--maintenance/parserTestsParserHook.php2
-rw-r--r--maintenance/parserTestsParserTime.php2
-rw-r--r--maintenance/parserTestsStaticParserHook.php2
-rw-r--r--maintenance/postgres/compare_schemas.pl14
-rw-r--r--maintenance/postgres/tables.sql26
-rw-r--r--maintenance/purgeList.php2
-rw-r--r--maintenance/purgeOldText.php1
-rw-r--r--maintenance/reassignEdits.inc.php1
-rw-r--r--maintenance/reassignEdits.php1
-rw-r--r--maintenance/rebuildImages.php143
-rw-r--r--maintenance/rebuildInterwiki.php2
-rw-r--r--maintenance/rebuildall.php2
-rw-r--r--maintenance/rebuildrecentchanges.inc85
-rw-r--r--maintenance/rebuildrecentchanges.php3
-rw-r--r--maintenance/rebuildtextindex.php14
-rw-r--r--maintenance/refreshImageCount.php2
-rw-r--r--maintenance/refreshLinks.inc38
-rw-r--r--maintenance/refreshLinks.php4
-rw-r--r--maintenance/removeUnusedAccounts.php2
-rw-r--r--maintenance/renamewiki.php1
-rw-r--r--maintenance/renderDump.php5
-rw-r--r--maintenance/runJobs.php21
-rw-r--r--maintenance/showJobs.php2
-rw-r--r--maintenance/showStats.php4
-rw-r--r--maintenance/sql.php4
-rw-r--r--maintenance/stats.php2
-rw-r--r--maintenance/storage/checkStorage.php5
-rw-r--r--maintenance/storage/compressOld.php2
-rw-r--r--maintenance/storage/dumpRev.php1
-rw-r--r--maintenance/storage/moveToExternal.php2
-rw-r--r--maintenance/storage/resolveStubs.php3
-rw-r--r--maintenance/tables.sql50
-rw-r--r--maintenance/undelete.php33
-rw-r--r--maintenance/update.php2
-rw-r--r--maintenance/updateArticleCount.inc.php2
-rw-r--r--maintenance/updateArticleCount.php1
-rw-r--r--maintenance/updateRestrictions.php6
-rw-r--r--maintenance/updateSearchIndex.php2
-rw-r--r--maintenance/updateSpecialPages.php2
-rw-r--r--maintenance/updaters.inc209
-rw-r--r--maintenance/upgrade1_5.php2
-rw-r--r--maintenance/userOptions.inc27
-rw-r--r--maintenance/userOptions.php2
-rw-r--r--maintenance/waitForSlave.php2
-rw-r--r--maintenance/wikipedia-interwiki.sql2
-rw-r--r--math/texutil.ml20
-rw-r--r--redirect.php3
-rw-r--r--redirect.php51
-rw-r--r--serialized/serialize-localisation.php2
-rw-r--r--serialized/serialize.php2
-rw-r--r--skins/ArchLinux.deps.php3
-rw-r--r--skins/ArchLinux.php13
-rw-r--r--skins/Chick.deps.php4
-rw-r--r--skins/Chick.php2
-rw-r--r--skins/CologneBlue.php2
-rw-r--r--skins/MonoBook.deps.php4
-rw-r--r--skins/MonoBook.php11
-rw-r--r--skins/MySkin.deps.php4
-rw-r--r--skins/MySkin.php2
-rw-r--r--skins/Nostalgia.php2
-rw-r--r--skins/Simple.deps.php4
-rw-r--r--skins/Simple.php2
-rw-r--r--skins/Standard.php6
-rw-r--r--skins/archlinux/Opera95Fixes.css10
-rw-r--r--skins/archlinux/main.css641
-rw-r--r--skins/archlinux/rtl.css12
-rw-r--r--skins/chick/main.css39
-rw-r--r--skins/common/ajax.js21
-rw-r--r--skins/common/ajaxwatch.js99
-rw-r--r--skins/common/block.js16
-rw-r--r--skins/common/cologneblue.css13
-rw-r--r--skins/common/commonPrint.css3
-rw-r--r--skins/common/common_rtl.css9
-rw-r--r--skins/common/diff.css74
-rw-r--r--skins/common/diff.js20
-rw-r--r--skins/common/images/spinner.gifbin0 -> 2285 bytes
-rw-r--r--skins/common/oldshared.css465
-rw-r--r--skins/common/protect.js172
-rw-r--r--skins/common/shared.css48
-rw-r--r--skins/common/upload.js175
-rw-r--r--skins/common/wikibits.js292
-rw-r--r--skins/disabled/HTMLDump.php2
-rw-r--r--skins/disabled/MonoBook.tpl2
-rw-r--r--skins/disabled/MonoBookCBT.php2
-rw-r--r--skins/monobook/Opera95Fixes.css10
-rw-r--r--skins/monobook/main.css637
-rw-r--r--skins/monobook/rtl.css8
-rw-r--r--skins/monobook/user.gifbin932 -> 923 bytes
-rw-r--r--skins/simple/main.css28
-rw-r--r--t/00-test.t2
-rw-r--r--t/inc/IP.t1
-rw-r--r--t/inc/Language.t62
-rw-r--r--t/inc/Licenses.t6
-rw-r--r--t/inc/Sanitizer.t3
-rw-r--r--t/inc/Title.t2
-rw-r--r--t/inc/Xml.t3
-rw-r--r--t/maint/bom.t38
-rw-r--r--t/maint/eol-style.t6
-rw-r--r--t/maint/php-lint.t4
-rw-r--r--t/maint/php-tag.t6
-rw-r--r--t/maint/unix-newlines.t23
-rw-r--r--tests/ArticleTest.php44
-rw-r--r--tests/DatabaseTest.php19
-rw-r--r--tests/GlobalTest.php53
-rw-r--r--tests/ImageFunctionsTest.php48
-rw-r--r--tests/LocalFileTest.php90
-rw-r--r--tests/Makefile6
-rw-r--r--tests/MediaWiki_TestCase.php51
-rw-r--r--tests/README9
-rw-r--r--tests/SearchEngineTest.php37
-rw-r--r--tests/SearchMySQL4Test.php13
-rw-r--r--tests/run-test.php7
-rw-r--r--thumb.php104
-rw-r--r--thumb.php51
739 files changed, 102791 insertions, 63477 deletions
diff --git a/HISTORY b/HISTORY
index 6e710a09..f98333a7 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,4 +1,494 @@
-Change notes from older releases. For current info see RELEASE-NOTES.
+Change notes from older releases. For current info see RELEASE-NOTES.
+
+== MediaWiki 1.10 ==
+
+This is the Spring 2007 branch release of MediaWiki.
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept
+"ready to run", and in fact runs our own sites on Wikipedia.
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments
+will be made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain
+it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
+
+== Configuration changes ==
+
+* A new switch $wgCommandLineDarkBg used by maintenance scripts (parserTests.php).
+ It lets you specify if your terminal use a dark background, the colorized
+ output will be made lighter making things easier to read.
+* The minimum permissions needed to edit a page in each namespace can now be
+ customized via the $wgNamespaceProtection array. By default, editing pages in
+ the MediaWiki namespace requires "editinterface" permission, as before.
+* Allow restriction of autoconfirmed permission by edit count. New global setting
+ $wgAutoConfirmCount (defaulting to zero, naturally).
+* Added rate limiter for Special:Emailuser
+* Private logs can now be created using $wgLogRestrictions
+* (Bug 8590) limited HTML is now always enabled ($wgUserHtml = true).
+* Deprecated $wgUseImageResize, thumbnailing will be enabled unconditionally.
+
+== New features since 1.9 ==
+
+* (bug 6937) Introduce "statistics-footer" message, appended to
+ Special:Statistics
+* (bug 6638) List block flags in block log entries
+* (bugs 5051, 5376) Tooltips and accesskeys no longer require JavaScript
+* Added SkinTemplateOutputPageBeforeExec hook before SkinTemplate::outputPage()
+ starts page output
+ (http://lists.wikimedia.org/pipermail/wikitech-l/2007-January/028554.html)
+* Introduce "cascading protection" -- implicit protection on pages transcluded
+ into a page protected with this option enabled
+* (bug 8567) Added hook RawPageViewBeforeOutput just before the text is blown
+ out in action=raw, so extensions might influence the output.
+* (bug 3446) Add user preference to hide page content below diffs, can be
+ overridden by adding diffonly=1 or diffonly=0 to the URL of the diff page
+* Add 'purge' privilege to replace the hardcoded check for login state in
+ determining whether action=purge can be done via GET. Switching the
+ permission on for anons can be helpful for benchmarking.
+* (bug 7842) Link back to deleted revision list from deleted revision preview
+* (bug 8619) Add user-aware "unblock" link to Special:Blockip
+* (bug 8522) Provide a "delete" link on Special:Brokenredirects for users with
+ the appropriate permission
+* (bug 8628) Add user-aware block list link to Special:Blockip
+* (bug 8621) Log revisions marked as patrolled
+* Introduce "BookInformation" hook; see docs/hooks.txt for more details
+* Add title prefix search for Special:Undelete
+* Remove full-archive list from Special:Undelete
+* (bug 8136) Introduce 'ArticleUndelete' hook; see docs/hooks.txt for more info
+* (bug 8712) Expose user groups as a JavaScript global
+* Introduce 'CustomEditor' hook; see docs/hooks.txt for more information
+* New special page, Special:Protectedpages, which shows all protected pages
+ and their protection status (full protection status is not pulled out due
+ to performance considerations, so it just shows "full protected" or
+ "semi protected".
+* (bug 4133) Allow page protections to be made with an expiry date, in the same
+ format as block expiry dates. Existing protections are assumed to be infinite,
+ as are protections made with the new field left blank.
+* (bug 8535) Allow certain vertical alignment attributes to be used as image
+ keywords
+* (bug 6987) Allow perrow, widths, and heights attributes for <gallery>
+* (bug 3678) Allow disabling MediaWiki:Aboutsite in the same way as
+ MediaWiki:Disclaimers; Also means that if any of the footer links are
+ disabled in the wiki's default language (by setting to "-"), they'll also
+ be disabled in other languages too (e.g. if the user specifies uselang=fr).
+* Sort log types in Special:Log
+* Added a classname ("mw-toolbar-editbutton") and unique IDs to the edit
+ toolbar buttons
+* Hide irrelevant block options in Special:Blockip based on whether an
+ IP address/range or username is listed. (Dynamic using JS.)
+* (bug 9032) Make quickbarSettings localizable through Special:Allmessages
+* (bug 7782) Standardisation of file info at image description pages.
+* (bug 1035) View contributions / recentchanges for an IP range.
+* (bug 8747) When unwatching pages from Special:Watchlist/edit, put the
+ confirmation messages in a proper list with a CSS class and id.
+* (bug 9118) Show relevant log fragments on deletion confirmatio page
+* (bug 9009) Add username entry field to Special:Contributions
+* (bug 1723) Article size in history
+* (bug 9223) Disallow magic tilde sequences in page titles and usernames
+* (bug 6997) Link from Special:log/block to unblock form
+* (bug 9117) Link from Special:log/delete to undelete form
+* Link from Special:log/protect to change protection form
+* (bug 1196) Add IPv6 support added to blocks, more consistancy for IPv6 contribs
+* (bug 3984) Searching in logs by title%
+* Show thumbnail of existing image if image exists already under this filename
+* (bug 5546) Watchlist reflects logged actions like move, protection, undelete
+* Support protocols other than HTTP in LinkFilter, use $wgUrlProtocols
+* (bug 3069) Warning on upload of scaled down images
+* Warning on upload of images with uppercase extension if image with lowercase
+ extension exists
+* (bug 4624) Namespace selection for Special:Whatlinkshere
+* Introduce PageHistoryBeforeList and PageHistoryLineEnding hooks; see docs/hooks.txt
+ for more information
+* (bug 9397) Introduce "sp-contributions-footer" and "sp-contributions-footer-anon"
+ messages, shown at the end of Special:Contributions as appropriate for the target
+* (bug 8421) Expose current action in JavaScript globals (as 'wgAction')
+* (bug 9069) Use galleries in query pages dedicated to images
+* (bug 9177) Installer now warns of various conditions affecting session.save_path
+ which can lead to broken session storage
+* (bug 9046) Special page to list pages without language links
+* (bug 9508) Special page to list articles with the fewest revisions
+* Introduce 'FileUpload' hook; see docs/hooks.txt for more information
+* Introduce 'SearchUpdate' hook; see docs/hooks.txt for more information
+* Introduce 'mywatchlist' message; used on personal menu to link to watchlist page
+* Introduce magic word {{NUMBEROFEDITS}}
+* Introduced media handlers for file-type specific operations.
+* Improved error reporting for image thumbnailing
+* Added sharpening option for ImageMagick thumbnailing
+* (bug 9656) Autosummaries will be generated for deletion of pages longer than
+ 500 characters
+* Predefined block reasons added to Special:Blockip
+* (bug 9196) Installer now check that zend.ze1_compatibility_mode is off
+* (bug 9697) Introduce 'InternalParseBeforeLinks' hook; see docs/hooks.txt for more information
+* 'contribsub' message changed to 'contribsub2' with two parameters to permit
+ better localization. Change is reverse-compatible and can be ignored for
+ most wikis.
+* Adding a 'reason' field to Special:Userrights
+
+== Bugfixes since 1.9 ==
+
+* (bug 7292) Fix site statistics when moving pages in/out of content namespaces
+* (bug 8531) Correct local name of Lingála
+* Made the PLURAL: parser function return singular on -1 per default
+* Fixed up the AjaxSearch
+* Fix SpecialVersion->formatCredits input. Version and Url parameters should be
+ null to be treated properly with isset.
+* Page restrictions moved into a new, dedicated table
+* Correct tooltip accesskey hint for Opera on the Macintosh
+ (uses Shift-Esc-, not Ctrl-).
+* (bug 8002) Math should render left-to-right even in right-to-left wikis
+* Pass e-mail and real name fields to AuthPlugin::addUser, as additional
+ optional fields, which may be considered useful at registration time.
+* PostgreSQL upgrade scripts fixed and updated
+* (bug 8613) Fix error when viewing "Recent Changes" and using Postgres.
+* Initialise site_stats table at upgrade time if data was missing
+* (bug 7250) Updated Unicode normalization tables to Unicode 5.0
+* Unmaintained Oracle support files have been removed.
+* Use browser default for printing size, don't force to 11pt
+* (bug 8632) Fix regression in page protection null edit update
+* (bug 8407) Disallow indexing of "printable" versions
+* (bug 8643) Correctly escape the page-specific CSS class for non-Monobook skins
+* (bug 8629) Document $wgFilterCallback
+* (bug 1000) Clarify warning about memory_limit in installer
+* Suppress PHP warning about set_time_limit in installer when safe mode is on
+* (bug 3000) Fall back to SCRIPT_NAME plus QUERY_STRING when REQUEST_URI is
+ not available, as on IIS with PHP-CGI
+* Missing interwiki row for English Wikipedia restored (as "wikipedia:")
+* use configured cache servers for mctest.php
+* bucket details in mcc.php
+* fix input validation and remove debugging code in compressOld
+* full ID range for moveToExternal
+* fix resolveStubs.php for compatibility with older serialized data
+* maximum line length for bar graphs in getLagTimes.php
+* recognize specieswiki in rebuildInterwiki.inc
+* profile unicode cleanup in Xml
+* log slow parses in Article.php
+* profile wfMsgReal
+* log mkdir failures
+* profile AutoLoader
+* rebuild empty DjVu metadata containing ''
+* security fix for DjVu metadata retrieval
+* Undelete page list can use plural marker
+* (bug 8638) Fix update from 1.4 and earlier
+* (bug 8641) Fix order of updates to ipblocks table
+* (bug 8678) Fix detection of self-links for numeric titles in Parser
+* (bug 6171) Magically close tags in tables when not using Tidy.
+* Sanitizer now correctly escapes lonely '>' occurring before the first wikitag.
+* Ignore self closing on closing tags ( '</div />' now gives '</div>')
+* (bug 8673) Minor fix for web service API content-type header
+* Fix API revision list on PHP 5.2.1; bad reference assignment
+* (bug 8688) Handle underscores/spaces in Special:Blockip and Special:Ipblocklist
+ in a consistent manner
+* (bug 8701) Check database lock status when blocking/unblocking users
+* ParserOptions and ParserOutput classes are now in their own files
+* (bug 8708) Namespace translations for Zealandic language
+* Renamed constructor methods to PHP 5 __construct reserved name
+* (bug 8715) Warn users when editing an interface message whether or not the
+ message page exists
+* ar: fix the 'create a new page' on search page when no exact match found
+* (bug 8703) Corrected talk and image namespace name for Limburgish (li)
+* (bug 8671) Expose "wpDestFile" as a parameter to "uploadtext"
+* (bug 8403) Respect bad image list exceptions in galleries on wiki pages
+* Allow sending per-user contribution requests to "contributions" query group
+* (bug 3717) Update user count for AuthPlugin account autocreation
+* (bug 8719) Firefox release notes lie! Fix tooltips for Firefox 2 on x11;
+ accesskeys default settings appear to be same as Windows.
+* Added an option to make Linker::userToolLinks() show the contribs link
+ red when the user has no edits. Linker::userToolLinksRedContribs() is an
+ alias to that which should be used to make it more self documentating.
+* (bug 8749) Bring MySQL 5 table defs back into sync
+* (bug 8751) Set session cookies to HTTPS-only to match other cookies
+* (bug 8652) Catch exceptions generated by malformed XML in multipage media
+* (bug 8782) Help text in Makefile
+* (bug 8777) Suppress 'previous' link on Special:Allpages when at first page
+* (bug 8774) Fix path for GNU FDL rights icon on new installs
+* Fix multipage selector drop-down for DjVu images to work when title
+ is passed as a query string parameter; we have to pass the title as
+ a form parameter or it gets dropped from the form submission URL
+* (bug 8819) Fix full path disclosure in with skins dependencies
+* Fixed bug affecting HTML formatting in sortable table column titles
+* Merged table sorting code into wikibits.js
+* (bug 8711) Stop floats in previews from spilling into edit area
+* (bug 8858) Safer handling when $wgImageLimits is changed. Added a note
+ in DefaultSettings to make it clear.
+* (bug 4268) Fixed data-loss bug in compressOld batch text compression
+ affecting pages which had null edits (move, protect, etc) as second
+ edit in a batch group. Isolated and patched by Travis Derouin.
+* Fix for paths in 1.4->1.5 special-case updater script
+* (bug 8789) AJAX search: IE users can now use the return key
+* (bug 6844) Use <ins> and <del> tags to emphase the differences
+* (bug 6684) Fix improper javascript array iteration
+* (bug 4347) use MailAddress object for reply-to
+* Add AlphabeticPager abstract class
+* Use faster AlphabeticPager for Special:Categories
+* (bug 8875) Show printable link in MonoBook sidebar for locally nonexistent
+ pages; perhaps useful for categories and shared images
+* Clean up session checks to better handle the case where the session was
+ opened during the current request. May help with some caching corner
+ cases.
+* (bug 8897) Fix whitespace removal for interlanguage links with link prefix
+* Add 'ParserTestTables' hook to expand the list of temporary tables copied
+ by the parser test harness; use for extensions which require the presence
+ of other tables while they work.
+* Message names changed for AlphabeticPager introduced with r19758
+ for better localisations.
+* (bug 8944) The deprecated is_a() function is used in StubObjects.php
+* (bug 8992) Fix a remaining raw use of REQUEST_URI in history
+* (bug 8999) User.php gives "undefined user editcount" PHP notice.
+* (bug 8984) Fix a database error in Special:Recentchangeslinked
+ when using the Postgres database.
+* Moved the main ob_start() from the default LocalSettings.php to WebStart.php.
+ The ob_start() section should preferably be removed from older
+ LocalSettings.php files.
+* Give Content-Length header for HTTP/1.0 clients.
+* Partial support for Flash cross-domain-policy filtering.
+* Lazy-initialize site_stats row on load when empty. Somewhat kinder to
+ dump-based installations, avoiding PHP warnings when NUMBEROFARTICLES
+ and such are used.
+* Add 'charset' to Content-Type headers on various HTTP error responses
+ to forestall additional UTF-7-autodetect XSS issues. PHP sends only
+ 'text/html' by default when the script didn't specify more details,
+ which some inconsiderate browsers consider a license to autodetect
+ the deadly, hard-to-escape UTF-7.
+ This fixes an issue with the Ajax interface error message on MSIE when
+ $wgUseAjax is enabled (not default configuration); this UTF-7 variant
+ on a previously fixed attack vector was discovered by Moshe BA from BugSec:
+ http://www.bugsec.com/articles.php?Security=24
+* Trackback responses now specify XML content type
+* (bug 9044) Send a comment with action=raw pages in CSS/JS output mode
+ to work around IE/Mac bug where empty pages time out verrrrryyyyy slowly,
+ particularly with new keepalive-friendly HTTP on Wikipedia
+* (bug 8919) Suppress paging links and related messages where there are no
+ rows to list for query pages
+* (bug 9057) Standardize MediaWiki: namespace for oc
+* (bug 8132) Suppress "Pages in this category" heading in categories when
+ there are none
+* (bug 8958) Handle search operators better when using tsearch2 (Postgres)
+* (bug 8799) Use redirect table for Special:BrokenRedirects and
+ Special:DoubleRedirects
+* (bug 8918) Enable PLURAL option for MediaWiki:showingresults and
+ MediaWiki:showingresultsnum
+* (bug 9122) Fix minor display issue in RTL with section edit link margin
+* (bug 5805) Enable PLURAL option for some messages of watchlist and statistic
+* (bug 3953) Work around poor display of parenthesis in the in other
+ languages section of MonoBook skin
+* (bug 8539) Enable PLURAL option for another message of recentchanges.
+* (bug 8728) MediaWiki:Badfiletype splitted into 3 messages
+* (bug 9131) Allow SpecialContributions to work with Postgres
+* (bug 9155) Allow footer info to wrap in Monobook
+* (bug 8847) Strip spurious #fragments from request URI to fix redirect
+ loops on some server configurations
+* (bug 9097) column "pr_pagetype" does not exist
+* (bug 9217) Balance wfProfile calls in Skin::outputPage
+* (bug 9222) PostgreSQL updater should not be version-specific
+* Fix fallback implementation of mb_strlen so it works and isn't insanely
+ slow for large strings, since it's used for page edit lengths
+* (bug 8815) Setting password in initUser() breaks LdapAuthentication plugin
+* (bug 9256) Add a quick note to index.php header comments
+* Make Special:Listusers caseinsensitive for first letter
+* Default tidy.conf has been moved from extensions module into includes.
+* Ignore lonely '''''
+* (bug 9244) When calling edit page for nonexistent section, generate error
+ inside of just discarding edits, since edit links sometimes go to the wrong
+ place.
+* (bug 9019) No warning during upload if image description page exists, but no
+ image
+* (bug 8582) Allow thumbnailing when imagesize has a space.
+* (bug 8716) Change math_inputhash and math_outputhash to bytea for Postgres
+* (bug 9343) Correct internal name for Wolof language
+* (bug 9363) Fix Postgres error on Recentchangeslinked
+* (bug 5142) Fixed call of hook ArticleViewHeader
+* (bug 4777) Separate prev/next messages for Special:Whatlinkshere
+* Merge approx 15 missing Wikipedia language codes into wikipedia-interwiki.sql
+ based on Jeff Merkey's mediawiki-1.9.3.WG-20070316.tar.gz.bz2 archive.
+* (bug 9411) Fix for shared image descriptions using query-string titles
+* (bug 4756) Add user tool links for self created accounts at special:log
+ instead of sometimes broken block links from newuserlog extension
+* (bug 5817) Special:Recentchangeslinked now shows red link for nonexistent
+ target page instead of silently redirecting
+* (bug 8914) Don't transform colons in {{anchorencode:}}
+* (bug 9241) Handle edit section links and include size links for cached
+ templates the same as the first transclusion.
+* (bug 9466) "Rollback failed" page doesn't format edit comment
+* (bug 9472) Invalid XHTML on cached special pages
+* (bug 9472) Invalid XHTML on Special:Newpages
+* (bug 4764) "My contributions" not bold when viewing own contributions
+* (bug 9194) Add {{PLURAL:...}} to navigation bar of Special:Whatlinkshere
+* (bug 9033) Use a more specific error message when users are not able/allowed
+ to edit page protection levels due to a block, database lock or permissions
+* Fixed $wgFeedLimit
+* (bug 9270) Corrected help namespace name for Dutch Lower Saxon (nds-nl)
+* (bug 929, 4215) Expose "rcdays" user preference in Special:Preferences
+* (bug 9554) Extension-provided group name messages not used
+* (bug 9565) Translate template namespace name for Hindi (hi)
+* (bug 8599) Correct localized names of zh-variants
+* (bug 3366) Require skins based on SkinTemplate to override the skinname
+ property.
+* (bug 9220) Removed obsoletes functions in install-utils.inc.
+* Removed obsoletes Title::getRelatedCache and Title:touchArray
+* (bug 7285) Check MySQL username length during install
+* (bug 6910) Correct date/time formats in Vietnamese (vi)
+* (bug 9608) Correctly use ORDER BY in dumpLinks.php
+* (bug 9609) Correctly use ORDER BY in SpecialWhatlinkshere.php
+* Special:Random and Special:Randomredirect now try harder to send the user to
+ a random page, and will give an error message if none really can be found
+ instead of sending the user to the main page like they used to
+* Fix object variable used for displaying "not-patrolled" CSS class on list
+* Fixed interaction of page parameter to ImagePage with the HTML file cache
+* Fixed MIME type for SVG files, will be silently changed from image/svg
+ to image/svg+xml after loading from the database.
+* Workaround for djvutoxml bug #1704049 (poor performance). Use djvudump
+ instead.
+* Fixed odd behaviour in ImagePage on DjVu thumbnailing errors
+* (bug 5439) "Go" title search will now jump to shared/foreign Image: and
+ MediaWiki: pages that have not been locally edited.
+* (bug 9630) Limits links in Whatlinkshere forgot about namespace filter
+* Fixed upgrade for the non-standard MySQL schemas
+* Disable MySQL's strict mode at session start for MySQL 4.1+, to avoid the
+ various problems that occur when it is on.
+* (bug 9585) Fix regression in tidy usage in Special:Undelete previews
+* (bug 3826) Normalize some invalid cookie name characters when setting
+ up $wgCookiePrefix. Completes application of patch by Anders Kaseorg.
+* (bug 9649) Fix RTL form alignment for Special:Movepage
+* (bug 9582) Members of bot group now mark edits patrolled by default
+* (bug 9669) Fix limit ordering for rebuildrecentchanges; broken since
+ converted from 1.4 to 1.5 schema
+* (bug 9682) Revert PHP 5.1 dependency on warning suppression for SVN info
+* (bug 5959) Anchors dropped from stub links
+* (bug 3348) Some additional weak password checks: password which is same
+ as username will now be rejected.
+* (bug 8602) Converted Special:Contributions to use an IndexPager. The
+ interpretation of the offset parameter has changed, and the go parameter
+ has been removed.
+* (bug 6204) Fixes for indentation with $wgMaxTocLevel:
+ - don't emit too many list close tags after an invisible header
+ - don't emit too many final list close tags if last header is invisible
+ - don't emit TOC when there are no visible headers
+* (bug 7629) Fix $wgBrowserBlackList to avoid false positive on MSIE
+ when certain plugins are present which alter the user agent
+
+
+== Maintenance ==
+
+* New script maintenance/language/checkExtensioni18n.php used to check i18n
+ progress in the extension repository.
+* Running maintenance/parserTests.php with '--record' option, will now
+ automatically attempt to create the required tables
+* --purge option to do additional parser-cache purging for purgeList.php
+* Fix hardcoded background color in parserTests.php
+* parserTests.php : removed the 'light' option for --color argument, replacing
+ it with a new global switch : $wgCommandLineDarkBg
+* (bug 8780) Clarify message for command-line scripts if LocalSettings.php
+ exists but is not readable
+* dumpBackup / importDump now work with PostgreSQL
+* (bug 8975) Use "Maintenance script" as the default username for importImages.php
+ and importTextFile.php scripts
+* (bug 8933) Fix maintenance/reassignEdits.php script
+* (bug 9440) Added "mediawikiwiki" interwiki prefix to MediaWiki.org
+* (bug 2979) Import now gracefully skips invalid titles with a warning
+* Restore '--norc' option for maintenance/importTextFile.php
+* Help information for maintenance/importTextFile.php now easier to read on
+ consoles
+* Doxygen documentation now show the revision number of each file, generate
+ graphs using dot and include a search engine.
+
+
+== Languages updated ==
+
+* Arabic (ar)
+* Aramaic (arc)
+* Aymara (ay)
+* Belarusian normative (be)
+* Belarusian alternative (be-x-old)
+* Bulgarian (bg)
+* Bihara (bh)
+* Breton (br)
+* Catalan (ca)
+* Czech (cs)
+* Danish (da)
+* German (de)
+* Greek (el)
+* Esperanto (eo)
+* Spanish (es)
+* Estonian (et)
+* Basque (eu)
+* Finnish (fi)
+* Võro (fiu-vro)
+* French (fr)
+* Hebrew (he)
+* Hindi (hi)
+* Upper Sorbian (hsb)
+* Hungarian (hu)
+* Armenian (hy)
+* Indonesian (id)
+* Italian (it)
+* Japanese (ja)
+* Javanese (jv)
+* Georgian (ka)
+* Kabyle (kab)
+* Kazakh (kk)
+* Korean (ko)
+* Kashmiri (ks)
+* Ripuarian (ksh)
+* Latin (la)
+* Luganda (lg)
+* Limburgish (li)
+* Lithuanian (lt)
+* Latvian (lv)
+* Marathi (mr)
+* Low Saxon (nds)
+* Dutch Lower Saxon (nds-nl)
+* Nepali (ne)
+* Nepal Bhasa (new)
+* Dutch (nl)
+* Occitan (oc)
+* Pali (pi)
+* Polish (pl)
+* Romanian (ro)
+* Russian (ru)
+* Sanskrit (sa)
+* Sicilian (scn)
+* Slovak (sk)
+* Sundanese (su)
+* Swedish (sv)
+* Tahitian (ty)
+* Ukrainian (uk)
+* Urdu (ur)
+* Uzbek (uz)
+* Vietnamese (vi)
+* Zealandic (zea)
+* Old Chinese / Late Middle Chinese (zh-classical)
+* Chinese (PRC) (zh-cn)
+* Chinese (Taiwan) (zh-tw)
+* Cantonese (zh-yue)
+
+== Compatibility ==
+
+MediaWiki 1.10 requires PHP 5 (5.1 recommended). PHP 4 is no longer supported.
+
+PHP 5.0.x fails on 64-bit systems due to serious bugs with array processing:
+http://bugs.php.net/bug.php?id=34879
+Upgrade affected systems to PHP 5.1 or higher.
+
+MySQL 3.23.x is no longer supported; some older hosts may need to upgrade.
+At this time we still recommend 4.0, but 4.1/5.0 will work fine in most cases.
+
+
+== Upgrading ==
+
+1.10 has several database changes since 1.9, and will not work without schema
+updates.
+
+If upgrading from before 1.7, you may want to run refreshLinks.php to ensure
+new database fields are filled with data.
+
+If you are upgrading from MediaWiki 1.4.x or earlier, some major database
+changes are made, and there is a slightly higher chance that things could
+break. Don't forget to always back up your database before upgrading!
+
+See the file UPGRADE for more detailed upgrade instructions.
= MediaWiki release notes =
diff --git a/Makefile b/Makefile
index b659cad2..b9d5b1cf 100644
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,24 @@
# This Makefile is used to test some MediaWiki functions. If you
# want to install MediaWiki, point your browser to ./config/
#
+
+# Configuration:
+PROVE_BIN="prove"
+
+# Describe our tests:
+BASE_TEST=$(wildcard t/*.t)
+INCLUDES_TESTS=$(wildcard t/inc/*t)
+MAINTENANCE_TESTS=$(wildcard t/maint/*t)
+
+# Build groups:
+FAST_TESTS=$(BASE_TEST) $(INCLUDES_TESTS)
+ALL_TESTS=$(BASE_TEST) $(INCLUDES_TESTS) $(MAINTENANCE_TESTS)
+
test: Test.php
- prove -r t
+ $(PROVE_BIN) $(ALL_TESTS)
+
+fast: Test.php
+ $(PROVE_BIN) $(FAST_TESTS)
-verbose:
- prove -v -r t | egrep -v '^ok'
+verbose: Test.php
+ $(PROVE_BIN) -v $(ALL_TESTS) | egrep -v '^ok'
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index ac1b7a47..94fec251 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -3,11 +3,25 @@
Security reminder: MediaWiki does not require PHP's register_globals
setting since version 1.2.0. If you have it on, turn it *off* if you can.
-== MediaWiki 1.10.2 ==
+== MediaWiki 1.11.0 ==
September 10, 2007
-This is a security fix update to the Spring 2007 quarterly release snapshot.
+This is the Fall 2007 snapshot release of MediaWiki.
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept
+"ready to run", and in fact runs our own sites on Wikipedia.
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments
+will be made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain
+it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
+
+== Changes since 1.11.0rc1 ==
+
A possible HTML/XSS injection vector in the API pretty-printing mode has
been found and fixed.
@@ -17,6 +31,8 @@ LocalSettings.php:
$wgEnableAPI = false;
+(This is the default setting in 1.8.x.)
+
Not vulnerable versions:
* 1.11 >= 1.11.0
* 1.10 >= 1.10.2
@@ -34,515 +50,574 @@ the faulty function, however the BotQuery extension is similarly
vulnerable unless updated to the latest SVN version.
-== MediaWiki 1.10.1 ==
-
-July 13, 2007
-
-This is a bugfix update to the Spring 2007 quarterly release snapshot.
-A number of fixes to improve compatibility with PostgreSQL, some versions
-of MySQL, and some PHP configurations are included.
-
-Changes since 1.10.0:
-
+== Configuration changes since 1.10 ==
+
+* $wgThumbUpright - Adjust width of upright images when parameter 'upright' is
+ used
+* $wgAddGroups, $wgRemoveGroups - Finer control over who can assign which
+ usergroups
+* $wgEnotifImpersonal, $wgEnotifUseJobQ - Bulk mail options for large sites
+* $wgShowHostnames - Expose server host names through the API and HTML comments
+* $wgSaveDeletedFiles has been removed, the feature is now enabled unconditionally
+
+== New features since 1.10 ==
+
+* (bug 8868) Separate "blocked" message for autoblocks
+* Adding expiry of block to block messages
+* Links to redirect pages in categories are wrapped in
+ <span class="redirect-in-category"></span>
+* Introduced 'ImageOpenShowImageInlineBefore' hook; see docs/hooks.txt for
+ more information
+* (bug 9628) Show warnings about slave lag on Special:Contributions,
+ Special:Watchlist
+* (bug 8818) Expose "wpDestFile" as parameter $1 to "uploaddisabledtext"
+* Introducing new image keyword 'upright' and corresponding variable
+ $wgThumbUpright. This allows better proportional view of upright images
+ related to landscape images on a page without nailing the width of upright
+ images to a fix value which makes views for anon unproportional and user
+ preferences useless
+* (bug 6072) Introducing 'border' keyword to the [[Image:]] syntax
+* Introducing 'frameless' keyword to [[Image:]] syntax which respects the
+ user preferences for image width like 'thumb' but without a frame.
+* (bug 7960) Link to "what links here" for each "what links here" entry
+* Added support for configuration of an arbitrary number of commons-style
+ file repositories.
+* Added a Content-Disposition header to thumb.php output
+* Improved thumb.php error handling
+* Display file history on local image description pages of shared images
+* Added $wgArticleRobotPolicies
+* (bug 10076) Additional parameter $7 added to MediaWiki:Blockedtext
+ containing, the ip, ip range, or username whose block is affecting the
+* (bug 7691) Show relevant lines from the deletion log when re-creating a
+ previously deleted article
+* Added variables 'wgRestrictionEdit' and 'wgRestrictionMove' for JS to header
+* (bug 9898) Allow viewing all namespaces in Special:Newpages
+* (bug 10139) Introduce 'EditSectionLink' and 'EditSectionLinkForOther' hooks;
+ see docs/hooks.txt for details
+* (bug 9769) Provide "watch this page" toggle on protection form
+* (bug 9886) Provide clear example "stub link" in Special:Preferences
+* (bug 10055) Populate email address and real name properties of User objects
+ passed to the 'AbortNewAccount' hook
+* Show result of Special:Booksources in wiki content language always, it's
+ normally better maintained than the generic list from the standard message
+ files
+* (bug 7997) Allow users to be blocked from using Special:Emailuser
+* (bug 8989) Blacklist 'mhtml' and 'mht' files from upload
+* (bug 8760) Allow wiki links in "protectexpiry" message
+* (bug 5908) Add "DEFAULTSORTKEY" and "DEFAULTCATEGORYSORT" aliases for
+ "DEFAULTSORT" magic word
+* (bug 10181) Support the XCache object caching mechanism
+* (bug 9058) Introduce '--aconf' option for all maintenance scripts, to provide
+ a path to the AdminSettings.php file
+* (bug 8781) Remind users to check file permissions for LocalSettings.php
+ post-installation
+* Use shared.css for all skins and oldshared.css in place of common.css for
+ pre-Monobook skins. As always, modifications should go in-wiki to MediaWiki:
+ Common.css and MediaWiki:Monobook.css.
+* (bug 8869) Introduce Special:Uncategorizedtemplates
+* (bug 8734) Different log message when article protection level is changed
+* (bug 8458, 10338) Limit custom signature length to $wgMaxSigChars Unicode
+ characters
+* (bug 10096) Added an ability to query interwiki map table
+* On reupload, add a null revision to the image description page
+* Group log output by date
+* Kurdish interface latin/arabic writing system with transliteration
+* Support wiki text in all query page headers
+* Add 'Orphanedpages' as an alias to Special:Lonelypages
+* (bug 9328) Use "revision-info-current" message in place of "revision-info"
+ when viewing the current revision of a page, if available
+* (bug 8890) Enable wiki text for "license" message
+* Throw a showstopper exception when a hook function fails to return a value.
+ Forgetting to give a 'true' return value is a very common error which tends
+ to cause hard-to-track-down interactions between extensions.
+* Use $wgJobClasses to determine the correct Job to instantiate for a particular
+ queued task; allows extensions to introduce custom jobs
+* (bug 10326) AJAX-based page watching and unwatching has been cleaned up and
+ enabled by default.
+* Added option to install to MyISAM
+* (bug 9250) Remove hardcoded minimum image name length of three characters
+* Fixed DISPLAYTITLE behaviour to reject titles which don't normalise to the
+ same title as the current page, and enabled per default
+* Wrap site CSS and JavaScript in a <pre> tag, like user JS/CSS
+* (bug 10196) Add classes and dir="ltr" to the <pre>s on CSS and JS pages (new
+ classes: mw-code, mw-css, mw-js)
+* (bug 6711) Add $wgAddGroups and $wgRemoveGroups to allow finer control over
+ usergroup assignment.
+* Introduce 'UserEffectiveGroups' hook; see docs/hooks.txt for more information
+* (bug 10387) Detect and handle '.php5' extension environments at install time
+* Introduce 'ShowRawCssJs' hook; see docs/hooks.txt for more information
+* (bug 10404) Show rights log for the selected user in Special:Userrights
+* New javascript for upload page that will show a warning if a file with the
+ "destination filename" already exists.
+* Add 'editsection-brackets' message to allow localization (or removal) of the
+ brackets in the "[edit]" link for sections
+* (bug 10437) Move texvc styling to shared.css
+* Introduce "raw editing" mode for the watchlist, to allow bulk additions,
+ removals, and convenient exporting of watchlist contents
+* Show "undo" links in page histories
+* Option to jump to specified time period in user contributions
+* Improved feedback on "rollback success" page
+* Show distinct 'namespaceprotected' message to users when namespace protection
+ prevents page editing
+* (bug 9936) Per-edit suppression of preview-on-first edit with "preview=no"
+* Allow showing a one-off preview on first edit with "preview=yes"
+* (bug 9151) Remove timed redirects on "Return to X" pages for accessibility.
+* Link to user logs in toolbox when viewing a user page
+* (bug 10508) Allow HTML attributes on <gallery>
+* (bug 1962) Allow HTML attributes on <math>
+* (bug 10530) Introduce optional "sp-contributions-explain" message for
+ additional explanation in Special:Contributions
+* (bug 10520) Preview licences during upload via AJAX (toggle with
+ $wgAjaxLicensePreview)
+* New Parser::setTransparentTagHook for parser extension and template
+ compatibility
+* Introduced 'ContributionsToolLinks' hook; see docs/hooks.txt for more
+ information
+* Add a message if category is empty
+* Add CSS compatibility for Opera 9.5
+* Remove largely untested handheld stylesheet, which was causing more trouble
+ than good. Proper handheld support will be added at a future date. For now,
+ display should be acceptable either with CSS turned off or when using a so-
+ phisticated handheld browser.
+* (bug 3173) Option to offer exported pages as a download, rather than displaying
+ inline, as in most browsers
+* Pass the user as an argument to 'isValidPassword' hook callbacks; see
+ docs/hooks.txt for more information
+* Introduce 'UserGetRights' hook; see docs/hooks.txt for more information
+* (bug 9595) Pass new Revision to the 'ArticleInsertComplete' and
+ 'ArticleSaveComplete' hooks; see docs/hooks.txt for more information
+* (bug 9575) Accept upload description from GET parameters
+* Skip the difference engine cache when 'action=purge' is used while requesting
+ a difference page, to allow refreshing the cache in case of errors
+* (bug 10701) Link to Special:Listusers in default Special:Statistics messages
+* Improved file history presentation
+* (bug 10739) Users can now enter comments when reverting files
+* Improved handling of permissions errors
+* (bug 10793) "Mark patrolled" links will now be shown for users with
+ patrol permissions on all eligible diff pages
+* (bug 10655) Show standard tool links for blocked users in block log messages
+* Show standard tool links for blocked users in Special:Ipblocklist
+* Miscellaneous aesthetic improvements to Special:Ipblocklist
+* (bug 10826) Added link trail with Cyrillic characters for Mongolian language
+* (bug 10859) Introduce 'UserGetImplicitGroups' hook; see docs/hooks.txt for
+ more information
+* (bug 10832) Include user information when viewing a deleted revision
+* (bug 10872) Fall back to sane defaults when generating protection selector
+ labels for custom restriction levels
+* Show edit count in user preferences
+* Improved support for audio/video extensions
+* (bug 10937) Distinguish overwritten files in upload log
+* Introduce 'ArticleUpdateBeforeRedirect' hook; see docs/hooks.txt for more
+ information
+* Confirmation is now required when deleting old versions of files
+* (bug 7535) Users can now enter comments when deleting old versions of files
+* (bug 11001) Submit Special:Newpages as a GET, rather than a POST request
+* The <strong></strong> around links to watched pages in change lists now
+ has a class - "mw-watched"
+* (bug 9002) Provide a "view/restore deleted edits" link on Special:Upload
+ when a destination filename is provided that corresponds with previous
+ deleted files
+* Make the "invalid special page" message clearer
+* Add accesskey 's' and tooltip to 'upload file' button at Special:Upload
+* Introduced 'SkinAfterBottomScripts' hook; see docs/hooks.txt for
+ more information
+* (bug 11095) Honour "preview on first edit" preference when preloading
+ text for a non-existent page
+* (bug 11022) Use a more accurate page title for Special:Whatlinkshere and
+ Special:Recentchangeslinked
+* Add link to user contributions in normal watchlist edit mode
+* (bug 9426) Add 'newsectionheaderdefaultlevel' message to allow
+ modification of the heading formatting for new sections when section=new
+ argument is supplied
+* (bug 10836) Add 'newsectionsummary' message to allow modification of the
+ text that prefixes a new section link in Recent Changes
+
+== Bugfixes since 1.10 ==
+
+* (bug 9712) Use Arabic comma in date/time formats for Arabic and Farsi
+* (bug 9670) Follow redirects when render edit section links to transcluded
+ templates.
+* (bug 6204) Fix incorrect unindentation with $wgMaxTocLevel
+* (bug 3431) Suppress "next page" link in Special:Search at end of results
+* Don't show unblock form if the user doesn't have permission to use it
+ (cosmetic change, no vulnerabilities existed)
+* Subtitle success message when unblocking a block ID instead of a pseudo link
+ like [[User:#123|#123]]
+* Use the standard HTTP fetch functions when retrieving remote wiki pages
+ through transwiki, so we can take advantage of cURL goodies if available
+* Disable user JavaScript on Special:Userlogin, Special:Resetpass and
+ Special:Preferences, to avoid a compromised script sniffing passwords, etc.
+* (bug 9854, 3770) Clip overflow text in gallery boxes for visual cleanliness
+ instead of letting it flow outside the box or trigger ugly scroll bars.
+* Tooltips for print version and permalink
+* Links to the MediaWiki namespace for system messages having their default
+ values are no longer shown as nonexistent (e.g., in red)
+* Special:Ipblocklist differentiates between empty list and no search results.
+* (bug 5375) profiling does not respect read-only mode.
+* (bug 7070) monobook/user.gif has antialias artifacts
+* (bug 9123) Safer way when applying $wgLocalTZoffset
+* (bug 9896) Documentation for $wgSquidServers and X-FORWARDED-FOR
* (bug 9417) Uploading new versions of images when using Postgres no longer
throws warnings.
* (bug 9908) Using tsearch2 with Postgres 8.1 no longer gives an error.
+* (bug 1438) Fix for diff table layout on very wide lines.
+ Diff style rules have been broken out to common/diff.css,
+ and the dupes removed from the default skin files.
+ Skins can still override the default rules.
+* (bug 1229) Balance columns in diff display evenly
+* Right-align diff line numbers in RTL language display
+* (bug 9332) Fix instructions in tests/README
+* (bug 9813) Reject usernames containing '#' to avoid silent truncation
+ of fragments during the normalisation process
+* (bug 7989) RSS feeds content now use black text when using white background.
+* (bug 9971) Typo in a french language message.
* (bug 9973) Changed size was shown in advanced recentchanges collapsible items
with $wgRCShowChangedSized = false.
-* Fixed installation on MyISAM or old InnoDB with charset=utf8, was giving
- overlong key errors.
-* Fixed zero-padding issues with MySQL 5 binary schema
+* Fix PHP strict standards warning in enhanced recent changes.
+* (bug 5850) Added hexadecimal html entities comments for $digitTransformTable
+ entries.
+* (bug 7432) Change language name for Aromanian (roa-rup)
+* (bug 908) Unexistent special pages now generate a red link.
+* (bug 7899) Added \hline and \vline to the list of allowed TeX commands
+* (bug 7993) support mathematical symbol classes
+* (bug 10007) Allow Block IP to work with Postgrs again.
+* Add Google Wireless Transcoder to the Unicode editing blacklist
+* (bug 10083) Fix for Special:Version breakage on PHP 5.2 with some hooks
+* (bug 3624) TeX: \ker, \hom, \arg, \dim treated like \sin & \cos
+* (bug 10132, 10134) Restore back-compatibility Image::imageUrl() function
+* (bug 10113) Fix double-click for view source on protected pages
+* (bug 10117) Special:Wantedpages doesn't handle invalid titles in result
+ set [now prints out a warning]
+* (bug 10118) Introduced Special:Mostlinkedtemplates, report which lists
+ templates with a high number of inclusion links
+* (bug 10104) Fixed Database::getLag() for PostgreSQL and Oracle
* (bug 9820) session.save_path check no longer halts installation, but
warns of possible bad values
* (bug 9978) Fixed session.save_path validation when using extended
configuration format, e.g. "5;/tmp"
-
-
-== MediaWiki 1.10.0 ==
-
-May 9, 2007
-
-This is the quarterly release snapshot for Spring 2007. See below for a full
-list of changes since the 1.9.x series.
-
-Changes since 1.10.0rc2:
-
-* (bug 9808) Fix regression that ignored user 'rclimit' option for Special:Contributions
-
-
-== MediaWiki 1.10.0rc2 ==
-
-May 4, 2007
-
-THIS IS A RELEASE CANDIDATE MADE AVAILABLE FOR TESTING!
-A FINAL 1.10.0 RELEASE WILL APPEAR WITHIN A FEW DAYS.
-
-Changes since 1.10.0rc1:
-* Various l10n fixes and updates
-* Fix for upgrade of page_restrictions table
-* (bug 9780) Fix normalization of titles with initial colon followed by whitespace
-* Fix for regression in upload: wrong size info saved into image table
-* Avoid cyclic stub problems when authorization hooks do funny things with
- the user and the database at load time
-
-== MediaWiki 1.10.0rc1 ==
-
-April 30, 2007
-
-THIS IS A RELEASE CANDIDATE MADE AVAILABLE FOR TESTING!
-A FINAL 1.10.0 RELEASE WILL APPEAR WITHIN A FEW DAYS.
-
-MediaWiki is now using a "continuous integration" development model with
-quarterly snapshot releases. The latest development code is always kept
-"ready to run", and in fact runs our own sites on Wikipedia.
-
-Release branches will continue to receive security updates for about a year
-from first release, but nonessential bugfixes and feature developments
-will be made on the development trunk and appear in the next quarterly release.
-
-Those wishing to use the latest code instead of a branch release can obtain
-it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
-
-== Configuration changes ==
-
-* A new switch $wgCommandLineDarkBg used by maintenance scripts (parserTests.php).
- It lets you specify if your terminal use a dark background, the colorized
- output will be made lighter making things easier to read.
-* The minimum permissions needed to edit a page in each namespace can now be
- customized via the $wgNamespaceProtection array. By default, editing pages in
- the MediaWiki namespace requires "editinterface" permission, as before.
-* Allow restriction of autoconfirmed permission by edit count. New global setting
- $wgAutoConfirmCount (defaulting to zero, naturally).
-* Added rate limiter for Special:Emailuser
-* Private logs can now be created using $wgLogRestrictions
-* (Bug 8590) limited HTML is now always enabled ($wgUserHtml = true).
-* Deprecated $wgUseImageResize, thumbnailing will be enabled unconditionally.
-
-== New features since 1.9 ==
-
-* (bug 6937) Introduce "statistics-footer" message, appended to
- Special:Statistics
-* (bug 6638) List block flags in block log entries
-* (bugs 5051, 5376) Tooltips and accesskeys no longer require JavaScript
-* Added SkinTemplateOutputPageBeforeExec hook before SkinTemplate::outputPage()
- starts page output
- (http://lists.wikimedia.org/pipermail/wikitech-l/2007-January/028554.html)
-* Introduce "cascading protection" -- implicit protection on pages transcluded
- into a page protected with this option enabled
-* (bug 8567) Added hook RawPageViewBeforeOutput just before the text is blown
- out in action=raw, so extensions might influence the output.
-* (bug 3446) Add user preference to hide page content below diffs, can be
- overridden by adding diffonly=1 or diffonly=0 to the URL of the diff page
-* Add 'purge' privilege to replace the hardcoded check for login state in
- determining whether action=purge can be done via GET. Switching the
- permission on for anons can be helpful for benchmarking.
-* (bug 7842) Link back to deleted revision list from deleted revision preview
-* (bug 8619) Add user-aware "unblock" link to Special:Blockip
-* (bug 8522) Provide a "delete" link on Special:Brokenredirects for users with
- the appropriate permission
-* (bug 8628) Add user-aware block list link to Special:Blockip
-* (bug 8621) Log revisions marked as patrolled
-* Introduce "BookInformation" hook; see docs/hooks.txt for more details
-* Add title prefix search for Special:Undelete
-* Remove full-archive list from Special:Undelete
-* (bug 8136) Introduce 'ArticleUndelete' hook; see docs/hooks.txt for more info
-* (bug 8712) Expose user groups as a JavaScript global
-* Introduce 'CustomEditor' hook; see docs/hooks.txt for more information
-* New special page, Special:Protectedpages, which shows all protected pages
- and their protection status (full protection status is not pulled out due
- to performance considerations, so it just shows "full protected" or
- "semi protected".
-* (bug 4133) Allow page protections to be made with an expiry date, in the same
- format as block expiry dates. Existing protections are assumed to be infinite,
- as are protections made with the new field left blank.
-* (bug 8535) Allow certain vertical alignment attributes to be used as image
- keywords
-* (bug 6987) Allow perrow, widths, and heights attributes for <gallery>
-* (bug 3678) Allow disabling MediaWiki:Aboutsite in the same way as
- MediaWiki:Disclaimers; Also means that if any of the footer links are
- disabled in the wiki's default language (by setting to "-"), they'll also
- be disabled in other languages too (e.g. if the user specifies uselang=fr).
-* Sort log types in Special:Log
-* Added a classname ("mw-toolbar-editbutton") and unique IDs to the edit
- toolbar buttons
-* Hide irrelevant block options in Special:Blockip based on whether an
- IP address/range or username is listed. (Dynamic using JS.)
-* (bug 9032) Make quickbarSettings localizable through Special:Allmessages
-* (bug 7782) Standardisation of file info at image description pages.
-* (bug 1035) View contributions / recentchanges for an IP range.
-* (bug 8747) When unwatching pages from Special:Watchlist/edit, put the
- confirmation messages in a proper list with a CSS class and id.
-* (bug 9118) Show relevant log fragments on deletion confirmatio page
-* (bug 9009) Add username entry field to Special:Contributions
-* (bug 1723) Article size in history
-* (bug 9223) Disallow magic tilde sequences in page titles and usernames
-* (bug 6997) Link from Special:log/block to unblock form
-* (bug 9117) Link from Special:log/delete to undelete form
-* Link from Special:log/protect to change protection form
-* (bug 1196) Add IPv6 support added to blocks, more consistancy for IPv6 contribs
-* (bug 3984) Searching in logs by title%
-* Show thumbnail of existing image if image exists already under this filename
-* (bug 5546) Watchlist reflects logged actions like move, protection, undelete
-* Support protocols other than HTTP in LinkFilter, use $wgUrlProtocols
-* (bug 3069) Warning on upload of scaled down images
-* Warning on upload of images with uppercase extension if image with lowercase
- extension exists
-* (bug 4624) Namespace selection for Special:Whatlinkshere
-* Introduce PageHistoryBeforeList and PageHistoryLineEnding hooks; see docs/hooks.txt
- for more information
-* (bug 9397) Introduce "sp-contributions-footer" and "sp-contributions-footer-anon"
- messages, shown at the end of Special:Contributions as appropriate for the target
-* (bug 8421) Expose current action in JavaScript globals (as 'wgAction')
-* (bug 9069) Use galleries in query pages dedicated to images
-* (bug 9177) Installer now warns of various conditions affecting session.save_path
- which can lead to broken session storage
-* (bug 9046) Special page to list pages without language links
-* (bug 9508) Special page to list articles with the fewest revisions
-* Introduce 'FileUpload' hook; see docs/hooks.txt for more information
-* Introduce 'SearchUpdate' hook; see docs/hooks.txt for more information
-* Introduce 'mywatchlist' message; used on personal menu to link to watchlist page
-* Introduce magic word {{NUMBEROFEDITS}}
-* Introduced media handlers for file-type specific operations.
-* Improved error reporting for image thumbnailing
-* Added sharpening option for ImageMagick thumbnailing
-* (bug 9656) Autosummaries will be generated for deletion of pages longer than
- 500 characters
-* Predefined block reasons added to Special:Blockip
-* (bug 9196) Installer now check that zend.ze1_compatibility_mode is off
-* (bug 9697) Introduce 'InternalParseBeforeLinks' hook; see docs/hooks.txt for more information
-* 'contribsub' message changed to 'contribsub2' with two parameters to permit
- better localization. Change is reverse-compatible and can be ignored for
- most wikis.
-* Adding a 'reason' field to Special:Userrights
-
-== Bugfixes since 1.9 ==
-
-* (bug 7292) Fix site statistics when moving pages in/out of content namespaces
-* (bug 8531) Correct local name of Lingála
-* Made the PLURAL: parser function return singular on -1 per default
-* Fixed up the AjaxSearch
-* Fix SpecialVersion->formatCredits input. Version and Url parameters should be
- null to be treated properly with isset.
-* Page restrictions moved into a new, dedicated table
-* Correct tooltip accesskey hint for Opera on the Macintosh
- (uses Shift-Esc-, not Ctrl-).
-* (bug 8002) Math should render left-to-right even in right-to-left wikis
-* Pass e-mail and real name fields to AuthPlugin::addUser, as additional
- optional fields, which may be considered useful at registration time.
-* PostgreSQL upgrade scripts fixed and updated
-* (bug 8613) Fix error when viewing "Recent Changes" and using Postgres.
-* Initialise site_stats table at upgrade time if data was missing
-* (bug 7250) Updated Unicode normalization tables to Unicode 5.0
-* Unmaintained Oracle support files have been removed.
-* Use browser default for printing size, don't force to 11pt
-* (bug 8632) Fix regression in page protection null edit update
-* (bug 8407) Disallow indexing of "printable" versions
-* (bug 8643) Correctly escape the page-specific CSS class for non-Monobook skins
-* (bug 8629) Document $wgFilterCallback
-* (bug 1000) Clarify warning about memory_limit in installer
-* Suppress PHP warning about set_time_limit in installer when safe mode is on
-* (bug 3000) Fall back to SCRIPT_NAME plus QUERY_STRING when REQUEST_URI is
- not available, as on IIS with PHP-CGI
-* Missing interwiki row for English Wikipedia restored (as "wikipedia:")
-* use configured cache servers for mctest.php
-* bucket details in mcc.php
-* fix input validation and remove debugging code in compressOld
-* full ID range for moveToExternal
-* fix resolveStubs.php for compatibility with older serialized data
-* maximum line length for bar graphs in getLagTimes.php
-* recognize specieswiki in rebuildInterwiki.inc
-* profile unicode cleanup in Xml
-* log slow parses in Article.php
-* profile wfMsgReal
-* log mkdir failures
-* profile AutoLoader
-* rebuild empty DjVu metadata containing ''
-* security fix for DjVu metadata retrieval
-* Undelete page list can use plural marker
-* (bug 8638) Fix update from 1.4 and earlier
-* (bug 8641) Fix order of updates to ipblocks table
-* (bug 8678) Fix detection of self-links for numeric titles in Parser
-* (bug 6171) Magically close tags in tables when not using Tidy.
-* Sanitizer now correctly escapes lonely '>' occurring before the first wikitag.
-* Ignore self closing on closing tags ( '</div />' now gives '</div>')
-* (bug 8673) Minor fix for web service API content-type header
-* Fix API revision list on PHP 5.2.1; bad reference assignment
-* (bug 8688) Handle underscores/spaces in Special:Blockip and Special:Ipblocklist
- in a consistent manner
-* (bug 8701) Check database lock status when blocking/unblocking users
-* ParserOptions and ParserOutput classes are now in their own files
-* (bug 8708) Namespace translations for Zealandic language
-* Renamed constructor methods to PHP 5 __construct reserved name
-* (bug 8715) Warn users when editing an interface message whether or not the
- message page exists
-* ar: fix the 'create a new page' on search page when no exact match found
-* (bug 8703) Corrected talk and image namespace name for Limburgish (li)
-* (bug 8671) Expose "wpDestFile" as a parameter to "uploadtext"
-* (bug 8403) Respect bad image list exceptions in galleries on wiki pages
-* Allow sending per-user contribution requests to "contributions" query group
-* (bug 3717) Update user count for AuthPlugin account autocreation
-* (bug 8719) Firefox release notes lie! Fix tooltips for Firefox 2 on x11;
- accesskeys default settings appear to be same as Windows.
-* Added an option to make Linker::userToolLinks() show the contribs link
- red when the user has no edits. Linker::userToolLinksRedContribs() is an
- alias to that which should be used to make it more self documentating.
-* (bug 8749) Bring MySQL 5 table defs back into sync
-* (bug 8751) Set session cookies to HTTPS-only to match other cookies
-* (bug 8652) Catch exceptions generated by malformed XML in multipage media
-* (bug 8782) Help text in Makefile
-* (bug 8777) Suppress 'previous' link on Special:Allpages when at first page
-* (bug 8774) Fix path for GNU FDL rights icon on new installs
-* Fix multipage selector drop-down for DjVu images to work when title
- is passed as a query string parameter; we have to pass the title as
- a form parameter or it gets dropped from the form submission URL
-* (bug 8819) Fix full path disclosure in with skins dependencies
-* Fixed bug affecting HTML formatting in sortable table column titles
-* Merged table sorting code into wikibits.js
-* (bug 8711) Stop floats in previews from spilling into edit area
-* (bug 8858) Safer handling when $wgImageLimits is changed. Added a note
- in DefaultSettings to make it clear.
-* (bug 4268) Fixed data-loss bug in compressOld batch text compression
- affecting pages which had null edits (move, protect, etc) as second
- edit in a batch group. Isolated and patched by Travis Derouin.
-* Fix for paths in 1.4->1.5 special-case updater script
-* (bug 8789) AJAX search: IE users can now use the return key
-* (bug 6844) Use <ins> and <del> tags to emphase the differences
-* (bug 6684) Fix improper javascript array iteration
-* (bug 4347) use MailAddress object for reply-to
-* Add AlphabeticPager abstract class
-* Use faster AlphabeticPager for Special:Categories
-* (bug 8875) Show printable link in MonoBook sidebar for locally nonexistent
- pages; perhaps useful for categories and shared images
-* Clean up session checks to better handle the case where the session was
- opened during the current request. May help with some caching corner
- cases.
-* (bug 8897) Fix whitespace removal for interlanguage links with link prefix
-* Add 'ParserTestTables' hook to expand the list of temporary tables copied
- by the parser test harness; use for extensions which require the presence
- of other tables while they work.
-* Message names changed for AlphabeticPager introduced with r19758
- for better localisations.
-* (bug 8944) The deprecated is_a() function is used in StubObjects.php
-* (bug 8992) Fix a remaining raw use of REQUEST_URI in history
-* (bug 8999) User.php gives "undefined user editcount" PHP notice.
-* (bug 8984) Fix a database error in Special:Recentchangeslinked
- when using the Postgres database.
-* Moved the main ob_start() from the default LocalSettings.php to WebStart.php.
- The ob_start() section should preferably be removed from older
- LocalSettings.php files.
-* Give Content-Length header for HTTP/1.0 clients.
-* Partial support for Flash cross-domain-policy filtering.
-* Lazy-initialize site_stats row on load when empty. Somewhat kinder to
- dump-based installations, avoiding PHP warnings when NUMBEROFARTICLES
- and such are used.
-* Add 'charset' to Content-Type headers on various HTTP error responses
- to forestall additional UTF-7-autodetect XSS issues. PHP sends only
- 'text/html' by default when the script didn't specify more details,
- which some inconsiderate browsers consider a license to autodetect
- the deadly, hard-to-escape UTF-7.
- This fixes an issue with the Ajax interface error message on MSIE when
- $wgUseAjax is enabled (not default configuration); this UTF-7 variant
- on a previously fixed attack vector was discovered by Moshe BA from BugSec:
- http://www.bugsec.com/articles.php?Security=24
-* Trackback responses now specify XML content type
-* (bug 9044) Send a comment with action=raw pages in CSS/JS output mode
- to work around IE/Mac bug where empty pages time out verrrrryyyyy slowly,
- particularly with new keepalive-friendly HTTP on Wikipedia
-* (bug 8919) Suppress paging links and related messages where there are no
- rows to list for query pages
-* (bug 9057) Standardize MediaWiki: namespace for oc
-* (bug 8132) Suppress "Pages in this category" heading in categories when
- there are none
-* (bug 8958) Handle search operators better when using tsearch2 (Postgres)
-* (bug 8799) Use redirect table for Special:BrokenRedirects and
- Special:DoubleRedirects
-* (bug 8918) Enable PLURAL option for MediaWiki:showingresults and
- MediaWiki:showingresultsnum
-* (bug 9122) Fix minor display issue in RTL with section edit link margin
-* (bug 5805) Enable PLURAL option for some messages of watchlist and statistic
-* (bug 3953) Work around poor display of parenthesis in the in other
- languages section of MonoBook skin
-* (bug 8539) Enable PLURAL option for another message of recentchanges.
-* (bug 8728) MediaWiki:Badfiletype splitted into 3 messages
-* (bug 9131) Allow SpecialContributions to work with Postgres
-* (bug 9155) Allow footer info to wrap in Monobook
-* (bug 8847) Strip spurious #fragments from request URI to fix redirect
- loops on some server configurations
-* (bug 9097) column "pr_pagetype" does not exist
-* (bug 9217) Balance wfProfile calls in Skin::outputPage
-* (bug 9222) PostgreSQL updater should not be version-specific
-* Fix fallback implementation of mb_strlen so it works and isn't insanely
- slow for large strings, since it's used for page edit lengths
-* (bug 8815) Setting password in initUser() breaks LdapAuthentication plugin
-* (bug 9256) Add a quick note to index.php header comments
-* Make Special:Listusers caseinsensitive for first letter
-* Default tidy.conf has been moved from extensions module into includes.
-* Ignore lonely '''''
-* (bug 9244) When calling edit page for nonexistent section, generate error
- inside of just discarding edits, since edit links sometimes go to the wrong
- place.
-* (bug 9019) No warning during upload if image description page exists, but no
- image
-* (bug 8582) Allow thumbnailing when imagesize has a space.
-* (bug 8716) Change math_inputhash and math_outputhash to byte for Postgres
-* (bug 9343) Correct internal name for Wolof language
-* (bug 9363) Fix Postgres error on Recentchangeslinked
-* (bug 5142) Fixed call of hook ArticleViewHeader
-* (bug 4777) Separate prev/next messages for Special:Whatlinkshere
-* Merge approx 15 missing Wikipedia language codes into wikipedia-interwiki.sql
- based on Jeff Merkey's mediawiki-1.9.3.WG-20070316.tar.gz.bz2 archive.
-* (bug 9411) Fix for shared image descriptions using query-string titles
-* (bug 4756) Add user tool links for self created accounts at special:log
- instead of sometimes broken block links from newuserlog extension
-* (bug 5817) Special:Recentchangeslinked now shows red link for nonexistent
- target page instead of silently redirecting
-* (bug 8914) Don't transform colons in {{anchorencode:}}
-* (bug 9241) Handle edit section links and include size links for cached
- templates the same as the first transclusion.
-* (bug 9466) "Rollback failed" page doesn't format edit comment
-* (bug 9472) Invalid XHTML on cached special pages
-* (bug 9472) Invalid XHTML on Special:Newpages
-* (bug 4764) "My contributions" not bold when viewing own contributions
-* (bug 9194) Add {{PLURAL:...}} to navigation bar of Special:Whatlinkshere
-* (bug 9033) Use a more specific error message when users are not able/allowed
- to edit page protection levels due to a block, database lock or permissions
-* Fixed $wgFeedLimit
-* (bug 9270) Corrected help namespace name for Dutch Lower Saxon (nds-nl)
-* (bug 929, 4215) Expose "rcdays" user preference in Special:Preferences
-* (bug 9554) Extension-provided group name messages not used
-* (bug 9565) Translate template namespace name for Hindi (hi)
-* (bug 8599) Correct localized names of zh-variants
-* (bug 3366) Require skins based on SkinTemplate to override the skinname
- property.
-* (bug 9220) Removed obsoletes functions in install-utils.inc.
-* Removed obsoletes Title::getRelatedCache and Title:touchArray
-* (bug 7285) Check MySQL username length during install
-* (bug 6910) Correct date/time formats in Vietnamese (vi)
-* (bug 9608) Correctly use ORDER BY in dumpLinks.php
-* (bug 9609) Correctly use ORDER BY in SpecialWhatlinkshere.php
-* Special:Random and Special:Randomredirect now try harder to send the user to
- a random page, and will give an error message if none really can be found
- instead of sending the user to the main page like they used to
-* Fix object variable used for displaying "not-patrolled" CSS class on list
-* Fixed interaction of page parameter to ImagePage with the HTML file cache
-* Fixed MIME type for SVG files, will be silently changed from image/svg
- to image/svg+xml after loading from the database.
-* Workaround for djvutoxml bug #1704049 (poor performance). Use djvudump
- instead.
-* Fixed odd behaviour in ImagePage on DjVu thumbnailing errors
-* (bug 5439) "Go" title search will now jump to shared/foreign Image: and
- MediaWiki: pages that have not been locally edited.
-* (bug 9630) Limits links in Whatlinkshere forgot about namespace filter
-* Fixed upgrade for the non-standard MySQL schemas
-* Disable MySQL's strict mode at session start for MySQL 4.1+, to avoid the
- various problems that occur when it is on.
-* (bug 9585) Fix regression in tidy usage in Special:Undelete previews
-* (bug 3826) Normalize some invalid cookie name characters when setting
- up $wgCookiePrefix. Completes application of patch by Anders Kaseorg.
-* (bug 9649) Fix RTL form alignment for Special:Movepage
-* (bug 9582) Members of bot group now mark edits patrolled by default
-* (bug 9669) Fix limit ordering for rebuildrecentchanges; broken since
- converted from 1.4 to 1.5 schema
-* (bug 9682) Revert PHP 5.1 dependency on warning suppression for SVN info
-* (bug 5959) Anchors dropped from stub links
-* (bug 3348) Some additional weak password checks: password which is same
- as username will now be rejected.
-* (bug 8602) Converted Special:Contributions to use an IndexPager. The
- interpretation of the offset parameter has changed, and the go parameter
- has been removed.
-* (bug 7629) Fix $wgBrowserBlackList to avoid false positive on MSIE
- when certain plugins are present which alter the user agent
-
-
-== Maintenance ==
-
-* New script maintenance/language/checkExtensioni18n.php used to check i18n
- progress in the extension repository.
-* Running maintenance/parserTests.php with '--record' option, will now
- automatically attempt to create the required tables
-* --purge option to do additional parser-cache purging for purgeList.php
-* Fix hardcoded background color in parserTests.php
-* parserTests.php : removed the 'light' option for --color argument, replacing
- it with a new global switch : $wgCommandLineDarkBg
-* (bug 8780) Clarify message for command-line scripts if LocalSettings.php
- exists but is not readable
-* dumpBackup / importDump now work with PostgreSQL
-* (bug 8975) Use "Maintenance script" as the default username for importImages.php
- and importTextFile.php scripts
-* (bug 8933) Fix maintenance/reassignEdits.php script
-* (bug 9440) Added "mediawikiwiki" interwiki prefix to MediaWiki.org
-* (bug 2979) Import now gracefully skips invalid titles with a warning
-* Restore '--norc' option for maintenance/importTextFile.php
-* Help information for maintenance/importTextFile.php now easier to read on
- consoles
-* Doxygen documentation now show the revision number of each file, generate
- graphs using dot and include a search engine.
-
-
-== Languages updated ==
-
+* Don't generate a diff link in the patrol log if the page doesn't exist
+* (bug 10067) Translations for former skins removed from message files
+* (bug 9993) Force $wgShowExceptionDetails on during installation
+* (bug 9980) Validate administrator username and password during
+ installation
+* (bug 9383) Don't set a default value for BLOB column in rc-deleted
+ database patch
+* (bug 10149) Don't show full template list on section-0 edit
+* (bug 9909) Ensure access to binary fields in the math table use encodeBlob()
+ and decodeBlob()
+* (bug 6743) Don't link broken image links to the upload form when uploads
+ are disabled
+* (bug 9679) Improve documentation for $wgSiteNotice
+* (bug 10215) Show custom editing introduction when editing existing pages
+* (bug 10223) Fix edit link in noarticletext localizations for fr, oc
+* (bug 10247) Fix IP address regex to avoid false positive IPv6 matches
+* (bug 9948) Workaround for diff regression with old Mozilla versions
+* (bug 10265) Fix regression in category image gallery paging
+* (bug 8577) Fix some weird misapplications of time zones.
+ {{CURRENT*}} functions now consistently use UTC as intended, while
+ {{LOCAL*}} functions return local time per server config or $wgLocaltimezone.
+ Signature dates for Japanese and other languages including weekday now show
+ the correct day to match the rest of the time in local time.
+* Escape the output of magic variables that return page name or part of it
+* (bug 10309) Initialise parser state properly in extractSections(), fixes
+ some cases where section edits broke because tags were improperly stripped
+* Avoid PHP notice errors when doing HTTP proxy purges for an empty list
+* As intended, *skip* the HTTP proxy purges when doing HTCP purges
+* (bug 9696) Fix handling of brace transformations in "pagemovedtext"
+* (bug 10325) Fix regression in form action on Special:Listusers
+* Fixed installation on MyISAM or old InnoDB with charset=utf8, was giving
+ overlong key errors.
+* Fixed zero-padding issues with MySQL 5 binary schema
+* (bug 10344) Don't follow a redirect after changing its protection level
+* (bug 10333) Correct date format in Slovenian
+* (bug 10160) Show error message for unknown namespace on Special:Allpages and
+ Special:Prefixindex; making forms prettier for RTL wikis.
+* (bug 10334) Replace normal spaces before percent (%) signs with non-breaking
+ spaces
+* (bug 10372) namespaceDupes.php no longer ignores namespace aliases
+* (bug 10198) namespaceDupes.php no longer ignores interwiki prefixes
+* namespaceDupes.php should work better for initial-lowercase wikis
+* (bug 10377) "Permanent links" to revisions still work if the page is moved
+ and the redirect deleted
+* (bug 7071) Properly handle an 'oldid' passed to view or edit that doesn't
+ match the given title. Fixes inconsistencies with talk, history, edit links.
+* (bug 10397) Fix AJAX watch error fallback when we receive a bogus result
+* (bug 10396) Fix AJAX error when $wgScriptPath/index.php is not valid;
+ using $wgScript now included in JS info
+* Use native XMLHttpRequest class in preference to ActiveX on IE 7; this
+ avoids the "ActiveX "Do you want to allow ActiveX?" prompt when something
+ security settings are cranked this way and AJAX-y gets used.
+* Delay AJAX watch initialization until click so IE 6 with ugly security
+ settings doesn't prompt you until you use the link.
+* (bug 10401) Provide non-redirecting link to original title in Special:Movepage
+* Fix broken handling of log views for page titles consisting of one
+ or more zeros, e.g. "0", "00" etc.
+* Fix read permission check for special pages with subpage parameters, e.g.
+ Special:Confirmemail
+* Fix read permission check for unreadable page titles which are numerically
+ equivalent to a whitelisted title
+* '?>' closing tag removed from all files to help avoid problems with extraneous
+ whitespace (broken XML feeds, etc.)
+* Don't use garbled parser cache output when viewing custom CSS or JavaScript
+ pages
+* (bug 10406) Fix Special:Listusers filter form for non-ASCII localizations
+* Fix empty message checks for message names containing &
+ This corrects some odd behavior with sidebar items and custom namespaces
+ containing ampersands.
+* (bug 10375) Change thousands separator character to &nbsp; for Latin (la)
+* (bug 10477) Fix AJAX watch for Farsi on Firefox: JavaScript encoding tweak
+* (bug 10496) Fix broken DISTINCT option logic in database backend
+* Fix CSS media declaration for "screen, projection"; was causing some
+ validation issues
+* (bug 10495) $wgMemcachedDebug set twice in includes/DefaultSettings.php
+* (bug 10316) Prevent inconsistent cached skin settings in gen=js by setting
+ the intended skin directly in the URL.
+* (bug 9903) Don't mark redirects in categories as stubs
+* (bug 6965) Cannot include "Template:R" with {{R}} (magic word conflict)
+* Padding parser functions now work with strings like '0' that evaluate to false
+* (bug 10332) Title->userCan( 'edit' ) may return false positive
+* Fix bug with <nowiki> in front of links for wikis where linkPrefixExtension is true
+* (bug 10552) Suppress rollback link in history for single-revision pages
+* (bug 10538) Gracefully handle invalid input on move success page
+* Fix for Esperanto double-x-encoding in move success page
+* (bug 10526) Fix toolbar/insertTags behavior for IE 6/7 and Opera (8+)
+ Now matches the selection behavior on Mozilla / Safari.
+ Patch by Alex Smotrov.
+* Don't show non-functional toolbar buttons on Opera 7 anymore
+* (bug 9151) Fix relative subpage links with section fragments
+* (bug 10560) Adding a space between category letter heading and "continues"
+* (bug 4650) Keep impossibly large/small counts off Special:Statistics
+* (bug 10608) PHP notice when installing with PostgreSQL
+* (bug 10615) Fix for transwiki import when CURL not available
+* (bug 8054) Return search page for empty search requests with ugly URLs
+* (bug 10572) Force refresh after clearing visitation timestamps on watchlist
+* (bug 10631) Warn when illegal characters are removed from filename at upload
+* Fix several JavaScript bugs under MSIE 5/Macintosh
+* (bug 10591) Use Arabic numerals (0,1,2...) for the Malayam language
+* (bug 10642) Fix shift-click checkbox behavior for Opera 9.0+ and 6.0
+* Work around Safari bug with pages ending in ".gz" or ".tgz"
+* Removed obsolete maintenance/changeuser.sql script; use RenameUser extension
+* (bug 2735) "Preview" shown in title bar for action=submit on special pages
+* Removed "restore" links from the deletion log embedded in Special:Undelete
+* Improved error reporting and robustness for file delete/undelete.
+* Improved speed of file delete by storing the SHA-1 hash in image/oldimage
+* Fixed leading zero in base 36 SHA-1 hash
+* Protection form no longer produces JavaScript errors
+* (bug 10741) File histories show "delete" links for non-sysops
+* (bug 10744) Treat "noarticletext" and "noarticletextanon" as wiki text when
+ used on a non-existent page with "action=info"
+* Fix escaping of raw message text when used on a non-existent page with
+ "action=info"
+* (bug 10683) Fix inconsistent handling of URL-encoded titles in links
+ used in redirects (i.e. they now work)
+* (bug 8878) Changes to $dateFormats in German localization (removing unused,
+ nonexistent formats, putting time after date)
+* (bug 10769) Database::update() should return boolean result
+* Fix preference checkbox display for right-to-left languages which caused
+ them to be hidden in IE in some cases
+* Fix upload form display in right-to-left languages
+* Fixed regression in blocking of username '0'
+* (bug 9437) Don't overwrite edit form submission handler when setting up
+ edit box scroll position preserve/restore behaviour
+* (bug 10805) Fix "undo" link when viewing the diff of the most recent
+ change to a page using "diff=0"
+* (bug 10765) img_auth.php will now refuse logged-out requests where
+ $wgWhitelistRead is undefined, instead of (incorrectly) honouring them
+* Fixed img_auth.php file name extraction for whitelist checking
+* Tweak spacing of email preference display
+* Table sorting JavaScript prefers textContent over innerText to allow hidden
+ sort keys to work on Safari
+* (bug 4530) Fix local name of Kurdish language
+* (bug 10830) Fix local name of Haitian Creole language
+* Fix invalid XHTML in Special:Protectedpages
+* Fix comments in contributions and log pages for right-to-left languages
+* Make installer include_path-independent, so it should work on hosts which
+ disable user setting of PHP include_path setting
+* glob() is horribly unreliable and doesn't work on some systems, including
+ free.fr shared hosting. No longer using it in Language::getLanguageNames()
+* (bug 10763) Fix multi-insert logic for PostgreSQL
+* Fix invalid XHTML when viewing a deleted revision
+* Fix syntax error in translations of magic words in Romanian language
+* (bug 8737) Fix warnings caused by incorrect use of `/dev/null` when piping
+ process error output under Windows
+* (bug 7890) Don't list redirects to special pages in Special:BrokenRedirects
+* (bug 10783) Resizing PNG-24 images with GD no longer causes all alpha
+ channel transparency to be lost and transparent pixels to be turned black
+* (bug 9339) General error pages were transforming messages and their parameters
+ in the wrong order
+* (bug 9026) Incorrect heading numbering when viewing Special:Statistics with
+ "auto-numbered headings" enabled
+* Fixed invalid XHTML in Special:Upload
+* (bug 11013) Make sure dl() is available before attempting to use it to check
+ available databases in installer
+* Resizing transparent GIF images with GD now retains transparency by skipping
+ resampling
+* (bug 11065) Fix regression in handling of wiki-formatted EXIF metadata
+* Double encoding broke Special:Newpages for some languages
+* Adding a newline before the statistics footer, to prevent parsing problems
+* Preventing the TOC from appearing in Special:Statistics
+* (bug 11082) Fix check for fully-specced table names in Database::tableName
+* (bug 11067) Fix regression in upload conflict thumbnail display
+* (bug 10985) Resolved cached entries on Special:DoubleRedirects were being
+ supressed, breaking paging - now strikes out "fixed" results
+* (bug 8393) <sup> and <sub> need to be preserved (without attributes) for
+ entries in the table of contents
+* (bug 11114) Fix regression in read-only mode error display during editing
+* Force non-MySQL databases to use an ORDER BY in SpecialAllpages to ensure
+ that the first page_title is truly the first page title.
+* (bug 10836) Change the summary on creating of new section
+* Inclusion of Special:Wantedpages now works again
+
+== API changes since 1.10 ==
+
+Full API documentation is available at http://www.mediawiki.org/wiki/API
+
+* New properties: links, templates, images, langlinks, categories, external
+ links
+* Breaking Change: imagelinks renamed into imageusage (il->iu)
+* Bug fix: incorrect generator behavior in some cases
+* JSON format allows an optional callback function to wrap the result.
+* Login module disabled until a more secure solution can be implemented
+* (bug 9938) Querying by revision identifier returns the most recent revision
+ for the corresponding page, rather than the requested revision
+* (bug 8772) Filter page revision queries by user
+* (bug 9927) User contributions queries do not accept IP addresses
+* Watchlist feed now reports a proper feed item when the user is not logged in
+* Watchlist feed date bug fixed - automatically shows one last day
+* Watchlist feed now allows to specify number of hours to monitor
+* list=allpages now returns a list instead of a map in JSON format
+* Breaking Change: in json, revisions are now returned as a list, not as a map.
+* Add: prop=info can show page is new flag, current page length, and visit
+ counter.
+* Change: Query watchlist now shows flags only when explicitly requested with
+ wlparam=flags
+* rc_this_oldid (textid) is no longer accessible from query watchlist
+* action=usercontribs: additional filtering by ucshow=; selection of needed
+ fields with ucprop=; the textid (rev_text_id) is no longer being exposed
+* (bug 9970) Breaking Change: backlinks, embeddedin and imageusage now return
+ lists in JSON instead of a map, and do not return anything when titles do
+ not exist
+* (bug 9121) Introduced indexpageids query parameter to list the page_id
+ values of all returned page items
+* (bug 10147) Now interwiki titles are not processed but added to a separate
+ "interwiki" section of the output.
+* Added categorymembers list to query for pages in a category.
+* (bug 10260) Show page protection status
+* (bug 10392) Include MediaWiki version details in version output
+* (bug 10411) Site language in meta=siteinfo
+* (bug 10391) action=help doesn't return help if format is fancy markup
+* backlinks, embeddedin and imageusage lists should use (bl|ei|iu)title parameter
+ instead of titles. Titles for these lists is obsolete and might stop working soon.
+* Added prop=imageinfo - gets image properties and upload history
+* (bug 10211) Added db server replication lag information in meta=siteinfo
+* Added external url search within wiki pages (list=exturlusage)
+* Added link enumeration (list=alllinks)
+* Added registered users enumeration (list=allusers)
+* Added full text search in titles and content (list=search)
+* (bug 10684) Expanded list=allusers functionality
+* Possible breaking change: prop=revisions no longer includes pageid for rvprop=ids
+* Added rvprop=size to prop=revisions (The size will not be shown if it is NULL in the database)
+* list=allpages now allows to filter by article min/max size and protection status
+* Added site statistics (siprop=statistics for meta=siteinfo)
+* (bug 10902) Unable to fetch user contributions from IP addresses
+* `list=usercontribs` no longer requires that the user exist
+* (bug 10971) `aufrom` parameter doesn't work with spaces
+* Fix username handling issue with `auprefix` parameter
+* Treat underscores as spaces for `aufrom` and `auprefix` parameters
+* Added edit/delete/... token retrieval to prop=info
+* Added meta=userinfo - logged-in user information, group membership, rights
+* (bug 11072) Fix regression in API image history query
+* (bug 11115) Adding SHA1 hash to imageinfo query
+* (bug 10898) API does not return an edit token for non-existent pages
+* (bug 10890) Timestamp support for categorymembers query
+* (bug 10980) Add exclude redirects on backlinks
+* IPv6 titles in User namespace are normalized (run cleanupTitles.php to fix any old stray pages)
+
+== Maintenance script changes since 1.10 ==
+
+* Add support for wgMaxTocLevel option in parserTests
+* (bug 6823) Disable article view counter in maintenance/dumpHTML.php
+* Fix maintenance/importImages.php so it doesn't barf PHP errors when no
+ suitable files are found, and make the list of extensions an option (defaults
+ to $wgFileExtensions)
+* Add option to maintenance/createAndPromote.php to give the user bureaucrat
+ permissions (--bureaucrat)
+* Allow overwriting existing files with a conflicting name using
+ maintenance/importImages.php
+* (bug 10266) Use native newlines when rebuilding a messages file.
+
+== Languages updated since 1.10 ==
+
+* Afrikaans (af)
* Arabic (ar)
-* Aramaic (arc)
-* Aymara (ay)
-* Belarusian normative (be)
-* Belarusian alternative (be-x-old)
+* Bikol (bcl)
* Bulgarian (bg)
-* Bihara (bh)
-* Breton (br)
* Catalan (ca)
-* Czech (cs)
* Danish (da)
* German (de)
* Greek (el)
* Esperanto (eo)
* Spanish (es)
* Estonian (et)
-* Basque (eu)
+* Extremaduran (ext)
+* Farsi (fa)
* Finnish (fi)
-* Võro (fiu-vro)
+* Vöro (fiu-vro)
* French (fr)
+* Français Cadien (frc) (new)
+* Franco-Provençal/Arpetan (frp)
+* Galician (gl)
+* Hakka (hak)
* Hebrew (he)
-* Hindi (hi)
* Upper Sorbian (hsb)
-* Hungarian (hu)
-* Armenian (hy)
+* Haitian (ht)
* Indonesian (id)
+* Icelandic (is)
* Italian (it)
* Japanese (ja)
-* Javanese (jv)
* Georgian (ka)
* Kabyle (kab)
* Kazakh (kk)
* Korean (ko)
-* Kashmiri (ks)
-* Ripuarian (ksh)
+* Kinaray-a (krj) (new)
+* Kurdish (ku)
* Latin (la)
-* Luganda (lg)
-* Limburgish (li)
+* Lao (lo)
* Lithuanian (lt)
-* Latvian (lv)
-* Marathi (mr)
-* Low Saxon (nds)
-* Dutch Lower Saxon (nds-nl)
-* Nepali (ne)
-* Nepal Bhasa (new)
+* Latviešu (lv)
+* Malayalam (ml)
+* Bahasa Melayu (ms)
+* Burmese (my)
+* Low German (nds)
* Dutch (nl)
+* Norwegian (no)
* Occitan (oc)
-* Pali (pi)
+* Punjabi (Gurmukhi) (pa)
* Polish (pl)
+* Piedmontese (pms)
+* Portuguese (pt)
+* Romani (rmy)
* Romanian (ro)
+* Aromanian (roa-rup)
* Russian (ru)
-* Sanskrit (sa)
-* Sicilian (scn)
+* Sakha (sah)
+* Sango (se) (new)
* Slovak (sk)
+* Slovenian (sl)
+* Shona (sn)
+* Somali (so)
+* Albanian (sq)
* Sundanese (su)
* Swedish (sv)
-* Tahitian (ty)
-* Ukrainian (uk)
-* Urdu (ur)
-* Uzbek (uz)
-* Vietnamese (vi)
-* Zealandic (zea)
+* Tamil (ta)
+* Thai (th)
+* Tigrinya (ti)
+* Setswana (tn)
+* Tok Pisin (tpi)
+* Uyghur (ug)
+* Volapük (vo)
+* Winaray (war) (new)
+* Yiddish (yi)
* Old Chinese / Late Middle Chinese (zh-classical)
* Chinese (PRC) (zh-cn)
* Chinese (Taiwan) (zh-tw)
@@ -550,7 +625,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
== Compatibility ==
-MediaWiki 1.10 requires PHP 5 (5.1 recommended). PHP 4 is no longer supported.
+MediaWiki 1.11 requires PHP 5 (5.1 recommended). PHP 4 is no longer supported.
PHP 5.0.x fails on 64-bit systems due to serious bugs with array processing:
http://bugs.php.net/bug.php?id=34879
@@ -562,7 +637,7 @@ At this time we still recommend 4.0, but 4.1/5.0 will work fine in most cases.
== Upgrading ==
-1.10 has several database changes since 1.9, and will not work without schema
+1.11 has several database changes since 1.10, and will not work without schema
updates.
If upgrading from before 1.7, you may want to run refreshLinks.php to ensure
@@ -583,7 +658,7 @@ cases, but this is not recommended on live sites. (This must be set for
MathML to display properly in Mozilla.)
-For notes on 1.9.x and older releases, see HISTORY.
+For notes on 1.10.x and older releases, see HISTORY.
=== Online documentation ===
diff --git a/StartProfiler.php b/StartProfiler.php
index 8fc3ff88..3fcf69e6 100644
--- a/StartProfiler.php
+++ b/StartProfiler.php
@@ -19,4 +19,4 @@ require_once( dirname(__FILE__).'/includes/ProfilerStub.php' );
* Configuration of the profiler output can be done in LocalSettings.php
*/
-?>
+
diff --git a/Test.php b/Test.php
index bad931a4..d6a2cf91 100644
--- a/Test.php
+++ b/Test.php
@@ -1,304 +1,495 @@
<?php
+# See the end of this file for documentation
-/*
-
-=head1 NAME
-
-Test.php - L<Test::More> for PHP
-
-=head1 SYNOPSIS
-
- require 'Test.php';
-
- plan( $num ); # plan $num tests
- # or
- plan( 'no_plan' ); # We don't know how many
- # or
- plan( 'skip_all' ); # Skip all tests
- # or
- plan( 'skip_all', $reason ); # Skip all tests with a reason
-
- diag( 'message in test output' ) # Trailing \n not required
-
- # $test_name is always optional and should be a short description of
- # the test, e.g. "some_function() returns an integer"
+# The latest release of this test framework can always be found on CPAN:
+# http://search.cpan.org/search?query=Test.php
- # Various ways to say "ok"
- ok( $got == $expected, $test_name );
-
- # Compare with == and !=
- is( $got, $expected, $test_name );
- isnt( $got, $expected, $test_name );
-
- # Run a preg match on some data
- like( $got, $regex, $test_name );
- unlike( $got, $regex, $test_name );
-
- # Compare something with a given comparison operator
- cmp_ok( $got, '==', $expected, $test_name );
- # Compare something with a comparison function (should return bool)
- cmp_ok( $got, $func, $expected, $test_name );
-
- # Recursively check datastructures for equalness
- is_deeply( $got, $expected, $test_name );
-
- # Always pass or fail a test under an optional name
- pass( $test_name );
- fail( $test_name );
+register_shutdown_function('_test_ends');
-=head1 DESCRIPTION
+$__Test = array(
+ # How many tests are planned
+ 'planned' => null,
-F<Test.php> is an implementation of Perl's L<Test::More> and Pugs's B<Test> for
-PHP. Like those two modules it produces TAP output (see L<TAP>) which
-can then be gathered, formatted and summarized by a program that
-understands TAP such as L<prove(1)>.
+ # How many tests we've run, if 'planned' is still null by the time we're
+ # done we report the total count at the end
+ 'run' => 0,
-=cut
-
-*/
-
-register_shutdown_function('test_ends');
-
-$Test = array(
- 'run' => 0,
- 'failed' => 0,
- 'badpass' => 0,
- 'planned' => null
+ # Are are we currently within todo_start()/todo_end() ?
+ 'todo' => array(),
);
-function plan( $plan, $why = '' )
+function plan($plan, $why = '')
{
- global $Test;
-
- $Test['planned'] = true;
-
- switch ( $plan )
- {
- case 'no_plan':
- $Test['planned'] = false;
- break;
- case 'skip_all';
- printf( "1..0%s\n", $why ? " # Skip $why" : '' );
- exit;
- default:
- printf( "1..%d\n", $plan );
- break;
- }
+ global $__Test;
+
+ $__Test['planned'] = true;
+
+ switch ($plan)
+ {
+ case 'no_plan':
+ $__Test['planned'] = false;
+ break;
+ case 'skip_all';
+ printf("1..0%s\n", $why ? " # Skip $why" : '');
+ exit;
+ default:
+ printf("1..%d\n", $plan);
+ break;
+ }
}
-function pass( $desc = '' )
+function pass($desc = '')
{
- return proclaim(true, $desc);
+ return _proclaim(true, $desc);
}
-function fail( $desc = '' )
+function fail($desc = '')
{
- return proclaim( false, $desc );
+ return _proclaim(false, $desc);
}
-function ok( $cond, $desc = '' ) {
- return proclaim( $cond, $desc );
+function ok($cond, $desc = '') {
+ return _proclaim($cond, $desc);
}
-function is( $got, $expected, $desc = '' ) {
- $pass = $got == $expected;
- return proclaim( $pass, $desc, /* todo */ false, $got, $expected );
+function is($got, $expected, $desc = '') {
+ $pass = $got == $expected;
+ return _proclaim($pass, $desc, /* todo */ false, $got, $expected);
}
-function isnt( $got, $expected, $desc = '' ) {
- $pass = $got != $expected;
- return proclaim( $pass, $desc, /* todo */ false, $got, $expected, /* negated */ true );
+function isnt($got, $expected, $desc = '') {
+ $pass = $got != $expected;
+ return _proclaim($pass, $desc, /* todo */ false, $got, $expected, /* negated */ true);
}
-function like( $got, $expected, $desc = '' ) {
- $pass = preg_match( $expected, $got );
- return proclaim( $pass, $desc, /* todo */ false, $got, $expected );
+function like($got, $expected, $desc = '') {
+ $pass = preg_match($expected, $got);
+ return _proclaim($pass, $desc, /* todo */ false, $got, $expected);
}
-function unlike( $got, $expected, $desc = '' ) {
- $pass = ! preg_match( $expected, $got );
- return proclaim( $pass, $desc, /* todo */ false, $got, $expected, /* negated */ true );
+function unlike($got, $expected, $desc = '') {
+ $pass = !preg_match($expected, $got);
+ return _proclaim($pass, $desc, /* todo */ false, $got, $expected, /* negated */ true);
}
function cmp_ok($got, $op, $expected, $desc = '')
{
- $pass = null;
-
- /* See http://www.php.net/manual/en/language.operators.comparison.php */
- switch ($op)
- {
- case '==':
- $pass = $got == $expected;
- break;
- case '===':
- $pass = $got === $expected;
- break;
- case '!=':
- case '<>':
- $pass = $got != $expected;
- break;
- case '!==':
- $pass = $got !== $expected;
- break;
- case '<':
- $pass = $got < $expected;
- break;
- case '>':
- $pass = $got > $expected;
- break;
- case '<=':
- $pass = $got <= $expected;
- break;
- case '>=':
- $pass = $got >= $expected;
- break;
- default:
- if ( function_exists( $op ) ) {
- $pass = $op( $got, $expected );
- } else {
- die("No such operator or function $op\n");
- }
- }
-
- return proclaim( $pass, $desc, /* todo */ false, $got, "$op $expected" );
+ $pass = null;
+
+ # See http://www.php.net/manual/en/language.operators.comparison.php
+ switch ($op)
+ {
+ case '==':
+ $pass = $got == $expected;
+ break;
+ case '===':
+ $pass = $got === $expected;
+ break;
+ case '!=':
+ case '<>':
+ $pass = $got != $expected;
+ break;
+ case '!==':
+ $pass = $got !== $expected;
+ break;
+ case '<':
+ $pass = $got < $expected;
+ break;
+ case '>':
+ $pass = $got > $expected;
+ break;
+ case '<=':
+ $pass = $got <= $expected;
+ break;
+ case '>=':
+ $pass = $got >= $expected;
+ break;
+ default:
+ if (function_exists($op)) {
+ $pass = $op($got, $expected);
+ } else {
+ die("No such operator or function $op\n");
+ }
+ }
+
+ return _proclaim($pass, $desc, /* todo */ false, $got, "$got $op $expected");
}
function diag($message)
{
if (is_array($message))
- {
- $message = implode("\n", $message);
- }
-
- $messages = explode("\n", $message);
+ {
+ $message = implode("\n", $message);
+ }
- foreach ($messages as $msg)
- {
- echo "# $msg\n";
+ foreach (explode("\n", $message) as $line)
+ {
+ echo "# $line\n";
}
}
-function include_ok( $file, $desc = '' )
+function include_ok($file, $desc = '')
{
$pass = include $file;
- return proclaim( $pass, $desc == '' ? "include $file" : $desc );
+ return _proclaim($pass, $desc == '' ? "include $file" : $desc);
}
-function require_ok( $file, $desc = '' )
+function require_ok($file, $desc = '')
{
$pass = require $file;
- return proclaim( $pass, $desc == '' ? "require $file" : $desc );
+ return _proclaim($pass, $desc == '' ? "require $file" : $desc);
}
-function is_deeply( $got, $expected, $desc = '' )
+function is_deeply($got, $expected, $desc = '')
{
- // hack
- $s_got = serialize( $got );
- $s_exp = serialize( $expected );
+ $diff = _cmp_deeply($got, $expected);
+ $pass = is_null($diff);
+
+ if (!$pass) {
+ $got = strlen($diff['gpath']) ? ($diff['gpath'] . ' = ' . $diff['got'])
+ : _repl($got);
+ $expected = strlen($diff['epath']) ? ($diff['epath'] . ' = ' . $diff['expected'])
+ : _repl($expected);
+ }
- $pass = $s_got == $s_exp;
+ _proclaim($pass, $desc, /* todo */ false, $got, $expected);
+}
- proclaim( $pass, $desc, /* todo */ false, $got, $expected );
+function isa_ok($obj, $expected, $desc = '')
+{
+ $pass = is_a($obj, $expected);
+ _proclaim($pass, $desc, /* todo */ false, $name, $expected);
}
-function isa_ok( $obj, $expected, $desc = '' ) {
- $name = get_class( $obj );
- $pass = $name == $expected;
- proclaim( $pass, $desc, /* todo */ false, $name, $expected );
-}
+function todo_start($why = '')
+{
+ global $__Test;
-function proclaim(
- $cond, // bool
- $desc = '',
- $todo = false,
- $got = null,
- $expected = null,
- $negate = false ) {
+ $__Test['todo'][] = $why;
+}
- global $Test;
+function todo_end()
+{
+ global $__Test;
- $Test['run'] += 1;
+ if (count($__Test['todo']) == 0) {
+ die("todo_end() called without a matching todo_start() call");
+ } else {
+ array_pop($__Test['todo']);
+ }
+}
+
+#
+# The code below consists of private utility functions for the above functions
+#
- # TODO: force_todo
+function _proclaim(
+ $cond, # bool
+ $desc = '',
+ $todo = false,
+ $got = null,
+ $expected = null,
+ $negate = false) {
- # Everything after the first # is special, so escape user-supplied messages
- $desc = str_replace( '#', '\\#', $desc );
- $desc = str_replace( "\n", '\\n', $desc );
+ global $__Test;
- $ok = $cond ? "ok" : "not ok";
- $directive = $todo === false ? '' : '# TODO aoeu';
+ $__Test['run'] += 1;
- printf( "%s %d %s%s\n", $ok, $Test['run'], $desc, $directive );
+ # We're in a TODO block via todo_start()/todo_end(). TODO via specific
+ # functions is currently unimplemented and will probably stay that way
+ if (count($__Test['todo'])) {
+ $todo = true;
+ }
- if ( ! $cond ) {
- report_failure( $desc, $got, $expected, $negate, $todo );
- }
+ # Everything after the first # is special, so escape user-supplied messages
+ $desc = str_replace('#', '\\#', $desc);
+ $desc = str_replace("\n", '\\n', $desc);
- return $cond;
-}
+ $ok = $cond ? "ok" : "not ok";
+ $directive = '';
-function report_failure( $desc, $got, $expected, $negate, $todo ) {
- # Every public function in this file proclaim which then calls
- # this function, so our culprit is the third item in the stack
- $caller = debug_backtrace();
- $call = $caller['2'];
-
- diag(
- sprintf( " Failed%stest '%s'\n in %s at line %d\n got: %s\n expected: %s",
- $todo ? ' TODO ' : ' ',
- $desc,
- $call['file'],
- $call['line'],
- $got,
- $expected
- )
- );
+ if ($todo) {
+ $todo_idx = count($__Test['todo']) - 1;
+ $directive .= ' # TODO ' . $__Test['todo'][$todo_idx];
+ }
+
+ printf("%s %d %s%s\n", $ok, $__Test['run'], $desc, $directive);
+
+ # report a failure
+ if (!$cond) {
+ # Every public function in this file calls _proclaim so our culprit is
+ # the second item in the stack
+ $caller = debug_backtrace();
+ $call = $caller['1'];
+
+ diag(
+ sprintf(" Failed%stest '%s'\n in %s at line %d\n got: %s\n expected: %s",
+ $todo ? ' TODO ' : ' ',
+ $desc,
+ $call['file'],
+ $call['line'],
+ $got,
+ $expected
+ )
+ );
+ }
+
+ return $cond;
}
-function test_ends ()
+function _test_ends()
{
- global $Test;
+ global $__Test;
+
+ if (count($__Test['todo']) != 0) {
+ $todos = join("', '", $__Test['todo']);
+ die("Missing todo_end() for '$todos'");
+ }
- if ( $Test['planned'] === false ) {
- printf( "1..%d\n", $Test['run'] );
- }
+ if (!$__Test['planned']) {
+ printf("1..%d\n", $__Test['run']);
+ }
}
-/*
+#
+# All of the below is for is_deeply()
+#
+
+function _repl($obj, $deep = true) {
+ if (is_string($obj)) {
+ return "'" . $obj . "'";
+ } else if (is_numeric($obj)) {
+ return $obj;
+ } else if (is_null($obj)) {
+ return 'null';
+ } else if (is_bool($obj)) {
+ return $obj ? 'true' : 'false';
+ } else if (is_array($obj)) {
+ return _repl_array($obj, $deep);
+ }else {
+ return gettype($obj);
+ }
+}
-=head1 TODO
+function _diff($gpath, $got, $epath, $expected) {
+ return array(
+ 'gpath' => $gpath,
+ 'got' => $got,
+ 'epath' => $epath,
+ 'expected' => $expected
+ );
+}
-=over
+function _idx($obj, $path = '') {
+ return $path . '[' . _repl($obj) . ']';
+}
-=item * Fully document this file
+function _cmp_deeply($got, $exp, $path = '') {
+ if (is_array($exp)) {
+
+ if (!is_array($got)) {
+ return _diff($path, _repl($got), $path, _repl($exp));
+ }
+
+ $gk = array_keys($got);
+ $ek = array_keys($exp);
+ $mc = max(count($gk), count($ek));
+
+ for ($el = 0; $el < $mc; $el++) {
+ # One array shorter than the other?
+ if ($el >= count($ek)) {
+ return _diff(_idx($gk[$el], $path), _repl($got[$gk[$el]]),
+ 'missing', 'nothing');
+ } else if ($el >= count($gk)) {
+ return _diff('missing', 'nothing',
+ _idx($ek[$el], $path), _repl($exp[$ek[$el]]));
+ }
+
+ # Keys differ?
+ if ($gk[$el] != $ek[$el]) {
+ return _diff(_idx($gk[$el], $path), _repl($got[$gk[$el]]),
+ _idx($ek[$el], $path), _repl($exp[$ek[$el]]));
+ }
+
+ # Recurse
+ $rc = _cmp_deeply($got[$gk[$el]], $exp[$ek[$el]], _idx($gk[$el], $path));
+ if (!is_null($rc)) {
+ return $rc;
+ }
+ }
+ }
+ else {
+ # Default to serialize hack
+ if (serialize($got) != serialize($exp)) {
+ return _diff($path, _repl($got), $path, _repl($exp));
+ }
+ }
+
+ return null;
+}
-=item *
+function _plural($n, $singular, $plural = null) {
+ if (is_null($plural)) {
+ $plural = $singular . 's';
+ }
+ return $n == 1 ? "$n $singular" : "$n $plural";
+}
-Add TODO support, maybe via C<ok(0, "foo # TODO fix this")>
-C<ok(1, "foo", array( 'todo' => 'fix this'))>.
+function _repl_array($obj, $deep) {
+ if ($deep) {
+ $slice = array_slice($obj, 0, 3); # Increase from 3 to show more
+ $repl = array();
+ $next = 0;
+ foreach ($slice as $idx => $el) {
+ $elrep = _repl($el, false);
+ if (is_numeric($idx) && $next == $idx) {
+ // Numeric index
+ $next++;
+ } else {
+ // Out of sequence or non-numeric
+ $elrep = _repl($idx, false) . ' => ' . $elrep;
+ }
+ $repl[] = $elrep;
+ }
+ $more = count($obj) - count($slice);
+ if ($more > 0) {
+ $repl[] = '... ' . _plural($more, 'more element') . ' ...';
+ }
+ return 'array(' . join(', ', $repl) . ')';
+ }
+ else {
+ return 'array(' . count($obj) . ')';
+ }
+}
-=back
+/*
-=head1 SEE ALSO
+=head1 NAME
-=over
+Test.php - TAP test framework for PHP with a L<Test::More>-like interface
-=item L<TAP> - The TAP protocol
+=head1 SYNOPSIS
-=item L<Test::More>
+ #!/usr/bin/env php
+ <?php
+ require 'Test.php';
+
+ plan($num); # plan $num tests
+ # or
+ plan('no_plan'); # We don't know how many
+ # or
+ plan('skip_all'); # Skip all tests
+ # or
+ plan('skip_all', $reason); # Skip all tests with a reason
+
+ diag('message in test output') # Trailing \n not required
+
+ # $test_name is always optional and should be a short description of
+ # the test, e.g. "some_function() returns an integer"
+
+ # Various ways to say "ok"
+ ok($got == $expected, $test_name);
+
+ # Compare with == and !=
+ is($got, $expected, $test_name);
+ isnt($got, $expected, $test_name);
+
+ # Run a preg regex match on some data
+ like($got, $regex, $test_name);
+ unlike($got, $regex, $test_name);
+
+ # Compare something with a given comparison operator
+ cmp_ok($got, '==', $expected, $test_name);
+ # Compare something with a comparison function (should return bool)
+ cmp_ok($got, $func, $expected, $test_name);
+
+ # Recursively check datastructures for equalness
+ is_deeply($got, $expected, $test_name);
+
+ # Always pass or fail a test under an optional name
+ pass($test_name);
+ fail($test_name);
+
+ # TODO tests, these are expected to fail but won't fail the test run,
+ # unexpected success will be reported
+ todo_start("integer arithmetic still working");
+ ok(1 + 2 == 3);
+ {
+ # TODOs can be nested
+ todo_start("string comparison still working")
+ is("foo", "bar");
+ todo_end();
+ }
+ todo_end();
+ ?>
+
+=head1 DESCRIPTION
-=item Pugs's Test.pm
+F<Test.php> is an implementation of Perl's L<Test::More> for PHP. Like
+Test::More it produces language agnostic TAP output (see L<TAP>) which
+can then be gathered, formatted and summarized by a program that
+understands TAP such as prove(1).
+
+=head1 HOWTO
+
+First place the F<Test.php> in the project root or somewhere else in
+the include path where C<require> and C<include> will find it.
+
+Then make a place to put your tests in, it's customary to place TAP
+tests in a directory named F<t> under the root but they can be
+anywhere you like. Make a test in this directory or one of its subdirs
+and try running it with php(1):
+
+ $ php t/pass.t
+ 1..1
+ ok 1 This dummy test passed
+
+The TAP output consists of very simple output, of course reading
+larger output is going to be harder which is where prove(1) comes
+in. prove is a harness program that reads test output and produces
+reports based on it:
+
+ $ prove t/pass.t
+ t/pass....ok
+ All tests successful.
+ Files=1, Tests=1, 0 wallclock secs ( 0.03 cusr + 0.02 csys = 0.05 CPU)
+
+To run all the tests in the F<t> directory recursively use C<prove -r
+t>. This can be put in a F<Makefile> under a I<test> target, for
+example:
+
+ test: Test.php
+ prove -r t
+
+For reference the example test file above looks like this, the shebang
+on the first line is needed so that prove(1) and other test harness
+programs know they're dealing with a PHP file.
+
+ #!/usr/bin/env php
+ <?php
+
+ require 'Test.php';
+
+ plan(1);
+ pass('This dummy test passed');
+ ?>
+
+=head1 SEE ALSO
-=back
+L<TAP> - The TAP protocol
=head1 AUTHOR
-Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org> and Andy Armstrong <andy@hexten.net>
=head1 LICENSING
-This program is free software; you can redistribute it and/or modify it
-under the same terms as Perl itself.
+The author or authors of this code dedicate any and all copyright
+interest in this code to the public domain. We make this dedication
+for the benefit of the public at large and to the detriment of our
+heirs and successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights this
+code under copyright law.
=cut
diff --git a/UPGRADE b/UPGRADE
index 40d5b202..fb600ff2 100644
--- a/UPGRADE
+++ b/UPGRADE
@@ -16,7 +16,7 @@ is available at http://www.mediawiki.org/wiki/Manual:Upgrading_MediaWiki.
=== Consult the release notes ===
Before doing anything, stop and consult the release notes supplied with the new
-version of the software. This detail bug fixes, new features and functionality,
+version of the software. These detail bug fixes, new features and functionality,
and any particular points that may need to be noted during the upgrade
procedure.
@@ -27,32 +27,34 @@ you take a complete backup of your wiki database and files and verify it. While
the upgrade scripts are somewhat robust, there is no guarantee that things will
not fail, leaving the database in an inconsistent state.
-Refer to the MySQL or Postgres documentation for information on backing up a
-database. For information on making copies of files, consult the documentation
-for your operating system.
+http://www.mediawiki.org/wiki/Manual:Backing_up_a_wiki provides an overview of
+the upgrade process. You should also refer to the documentation for your
+database management system for information on backing up a database, and to
+your operating system documentation for information on making copies of files.
=== Perform the file upgrade ===
-Having downloaded the desired new version of the software, either as a package
-from SourceForge, or via an export from Subversion, decompress the files as
-needed, and replace the existing MediaWiki files with the new.
+Download the files for the new version of the software. These are available
+as a compressed "tar" archive from the Wikimedia Download Service
+(http://download.wikimedia.org/mediawiki).
-You should preserve:
+You can also obtain the new files directly from our Subversion source code
+repository, via a checkout or export operation.
-* The LocalSettings.php file
-* The AdminSettings.php file, where it exists
-* The extensions directory
-* The images directory
+Replace the existing MediaWiki files with the new. You should preserve the
+LocalSettings.php file, AdminSettings.php file (if present), and the
+"extensions" and "images" directories.
-If using an alternative uploads directory, preserve this; and if using custom
-skins, preserve these too. The core code is now updated.
+Depending upon your configuration, you may also need to preserve additional
+directories, including a custom upload directory ($wgUploadDirectory),
+deleted file archives, and any custom skins.
=== Perform the database upgrade ===
You will need an AdminSettings.php file set up in the correct format; see
AdminSettings.sample in the wiki root for more information and examples.
-From the command line, browse to the maintenance directory and run the
+From the command line, browse to the "maintenance" directory and run the
update.php script to check and update the schema. This will insert missing
tables, update existing tables, and move data around as needed. In most cases,
this is successful and nothing further needs to be done.
@@ -72,6 +74,8 @@ procedure, and especially after upgrading; check that page views and edits work
normally and that special pages continue to function, etc. and correct errors
and quirks which reveal themselves.
+You should also test any extensions, and upgrade these if necessary.
+
== Upgrading from 1.8 wikis ==
MediaWiki 1.9 and later no longer keep default localized message text
diff --git a/api.php b/api.php
index d3274dc4..fa85573d 100644
--- a/api.php
+++ b/api.php
@@ -1,10 +1,9 @@
<?php
-
-/**
+/*
* API for MediaWiki 1.8+
*
-* Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+* Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* 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
@@ -22,6 +21,17 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+/**
+ * This file is the entry point for all API queries. It begins by checking
+ * whether the API is enabled on this wiki; if not, it informs the user that
+ * s/he should set $wgEnableAPI to true and exits. Otherwise, it constructs
+ * a new ApiMain using the parameter passed to it as an argument in the URL
+ * ('?action=') and with write-enabled set to the value of $wgEnableWriteAPI
+ * as specified in LocalSettings.php. It then invokes "execute()" on the
+ * ApiMain object instance, which produces output in the format sepecified
+ * in the URL.
+ */
+
// Initialise common code
require (dirname(__FILE__) . '/includes/WebStart.php');
@@ -34,9 +44,16 @@ if (!$wgEnableAPI) {
die(-1);
}
+/* Construct an ApiMain with the arguments passed via the URL. What we get back
+ * is some form of an ApiMain, possibly even one that produces an error message,
+ * but we don't care here, as that is handled by the ctor.
+ */
$processor = new ApiMain($wgRequest, $wgEnableWriteAPI);
+
+// Process data & print results
$processor->execute();
+// Log what the user did, for book-keeping purposes.
wfProfileOut('api.php');
wfLogProfilingData();
-?>
+
diff --git a/api.php5 b/api.php5
new file mode 100644
index 00000000..64088872
--- /dev/null
+++ b/api.php5
@@ -0,0 +1 @@
+<?php require 'api.php'; ?>
diff --git a/config/index.php b/config/index.php
index e719e47e..274a1531 100644
--- a/config/index.php
+++ b/config/index.php
@@ -29,10 +29,6 @@ $wgRequestTime = microtime( true );
# Attempt to set up the include path, to fix problems with relative includes
$IP = dirname( dirname( __FILE__ ) );
define( 'MW_INSTALL_PATH', $IP );
-$sep = PATH_SEPARATOR;
-if( !ini_set( "include_path", ".$sep$IP$sep$IP/includes$sep$IP/languages" ) ) {
- set_include_path( ".$sep$IP$sep$IP/includes$sep$IP/languages" );
-}
# Define an entry point and include some files
define( "MEDIAWIKI", true );
@@ -40,14 +36,21 @@ define( "MEDIAWIKI_INSTALL", true );
// Run version checks before including other files
// so people don't see a scary parse error.
-require_once( "install-utils.inc" );
+require_once( "$IP/install-utils.inc" );
install_version_checks();
-require_once( "includes/Defines.php" );
-require_once( "includes/DefaultSettings.php" );
-require_once( "includes/MagicWord.php" );
-require_once( "includes/Namespace.php" );
-require_once( "includes/ProfilerStub.php" );
+require_once( "$IP/includes/Defines.php" );
+require_once( "$IP/includes/DefaultSettings.php" );
+require_once( "$IP/includes/AutoLoader.php" );
+require_once( "$IP/includes/MagicWord.php" );
+require_once( "$IP/includes/Namespace.php" );
+require_once( "$IP/includes/ProfilerStub.php" );
+require_once( "$IP/includes/GlobalFunctions.php" );
+require_once( "$IP/includes/Hooks.php" );
+
+# If we get an exception, the user needs to know
+# all the details
+$wgShowExceptionDetails = true;
## Databases we support:
@@ -146,6 +149,15 @@ $ourdb['postgres']['rootuser'] = 'postgres';
font-size: 85%;
padding-top: 3em;
}
+
+ span.success-message {
+ font-weight: bold;
+ font-size: 110%;
+ color: green;
+ }
+ .success-box {
+ font-size: 130%;
+ }
</style>
<script type="text/javascript">
@@ -185,7 +197,8 @@ $ourdb['postgres']['rootuser'] = 'postgres';
/* Check for existing configurations and bug out! */
if( file_exists( "../LocalSettings.php" ) ) {
- dieout( "<p><strong>Setup has completed, <a href='../index.php'>your wiki</a> is configured.</strong></p>
+ $script = defined('MW_INSTALL_PHP5_EXT') ? 'index.php5' : 'index.php';
+ dieout( "<p><strong>Setup has completed, <a href='../$script'>your wiki</a> is configured.</strong></p>
<p>Please delete the /config directory for extra security.</p></div></div></div></div>" );
}
@@ -215,8 +228,8 @@ if( !is_writable( "." ) ) {
}
-require_once( "install-utils.inc" );
-require_once( "maintenance/updaters.inc" );
+require_once( "$IP/install-utils.inc" );
+require_once( "$IP/maintenance/updaters.inc" );
class ConfigData {
function getEncoded( $data ) {
@@ -227,21 +240,26 @@ class ConfigData {
function getSysopName() { return $this->getEncoded( $this->SysopName ); }
function getSysopPass() { return $this->getEncoded( $this->SysopPass ); }
- function setSchema( $schema ) {
+ function setSchema( $schema, $engine ) {
$this->DBschema = $schema;
+ if ( !preg_match( '/^\w*$/', $engine ) ){
+ $engine = 'InnoDB';
+ }
switch ( $this->DBschema ) {
case 'mysql5':
- $this->DBTableOptions = 'ENGINE=InnoDB, DEFAULT CHARSET=utf8';
+ $this->DBTableOptions = "ENGINE=$engine, DEFAULT CHARSET=utf8";
$this->DBmysql5 = 'true';
break;
case 'mysql5-binary':
- $this->DBTableOptions = 'ENGINE=InnoDB, DEFAULT CHARSET=binary';
+ $this->DBTableOptions = "ENGINE=$engine, DEFAULT CHARSET=binary";
$this->DBmysql5 = 'true';
break;
default:
- $this->DBTableOptions = 'TYPE=InnoDB';
+ $this->DBTableOptions = "TYPE=$engine";
$this->DBmysql5 = 'false';
}
+ $this->DBengine = $engine;
+
# Set the global for use during install
global $wgDBTableOptions;
$wgDBTableOptions = $this->DBTableOptions;
@@ -269,22 +287,20 @@ define( 'MW_NO_OUTPUT_BUFFER', 1 );
$conf = new ConfigData;
install_version_checks();
+$self = 'Installer'; # Maintenance script name, to please Setup.php
print "<li>PHP " . phpversion() . " installed</li>\n";
-## Temporarily turn off all errors as we try to discover installed databases
-$olderrnum = error_reporting(0);
-
+error_reporting( 0 );
$phpdatabases = array();
foreach (array_keys($ourdb) as $db) {
$compname = $ourdb[$db]['compile'];
- if (extension_loaded($compname) or dl($compname . '.' . PHP_SHLIB_SUFFIX)) {
+ if( extension_loaded( $compname ) || ( mw_have_dl() && dl( "{$compname}." . PHP_SHLIB_SUFFIX ) ) ) {
array_push($phpdatabases, $db);
$ourdb[$db]['havedriver'] = 1;
}
}
-
-error_reporting($olderrornum);
+error_reporting( E_ALL );
if (!$phpdatabases) {
print "Could not find a suitable database driver!<ul>";
@@ -298,15 +314,16 @@ if (!$phpdatabases) {
}
print "<li>Found database drivers for:";
+$DefaultDBtype = '';
foreach (array_keys($ourdb) AS $db) {
if ($ourdb[$db]['havedriver']) {
- $DefaultDBtype = $db;
+ if ( $DefaultDBtype == '' ) {
+ $DefaultDBtype = $db;
+ }
print " ".$ourdb[$db]['fullname'];
}
}
print "</li>\n";
-if (count($phpdatabases) != 1)
- $DefaultDBtype = '';
if( ini_get( "register_globals" ) ) {
?>
@@ -373,10 +390,11 @@ if( ini_get( "safe_mode" ) ) {
$sapi = php_sapi_name();
print "<li>PHP server API is $sapi; ";
+$script = defined('MW_INSTALL_PHP5_EXT') ? 'index.php5' : 'index.php';
if( $wgUsePathInfo ) {
- print "ok, using pretty URLs (<tt>index.php/Page_Title</tt>)";
+ print "ok, using pretty URLs (<tt>$script/Page_Title</tt>)";
} else {
- print "using ugly URLs (<tt>index.php?title=Page_Title</tt>)";
+ print "using ugly URLs (<tt>$script?title=Page_Title</tt>)";
}
print "</li>\n";
@@ -445,6 +463,10 @@ if ( $conf->turck ) {
print "<li><a href=\"http://turck-mmcache.sourceforge.net/\">Turck MMCache</a> installed</li>\n";
}
+$conf->xcache = function_exists( 'xcache_get' );
+if( $conf->xcache )
+ print "<li><a href=\"http://trac.lighttpd.net/xcache/\">XCache</a> installed</li>";
+
$conf->apc = function_exists('apc_fetch');
if ($conf->apc ) {
print "<li><a href=\"http://www.php.net/apc\">APC</a> installed</li>";
@@ -456,10 +478,11 @@ if ( $conf->eaccel ) {
print "<li><a href=\"http://eaccelerator.sourceforge.net/\">eAccelerator</a> installed</li>\n";
}
-if( !$conf->turck && !$conf->eaccel && !$conf->apc ) {
+if( !( $conf->turck || $conf->eaccel || $conf->apc || $conf->xcache ) ) {
echo( '<li>Couldn\'t find <a href="http://turck-mmcache.sourceforge.net">Turck MMCache</a>,
- <a href="http://eaccelerator.sourceforge.net">eAccelerator</a>, or
- <a href="http://www.php.net/apc">APC</a>. Object caching functions cannot be used.</li>' );
+ <a href="http://eaccelerator.sourceforge.net">eAccelerator</a>,
+ <a href="http://www.php.net/apc">APC</a> or <a href="http://trac.lighttpd.net/xcache/">XCache</a>;
+ cannot use these for object caching.</li>' );
}
$conf->diff3 = false;
@@ -470,7 +493,7 @@ $diff3locations = array_merge(
"/opt/csw/bin",
"/usr/gnu/bin",
"/usr/sfw/bin" ),
- explode( $sep, getenv( "PATH" ) ) );
+ explode( PATH_SEPARATOR, getenv( "PATH" ) ) );
$diff3names = array( "gdiff3", "diff3", "diff3.exe" );
$diff3versioninfo = array( '$1 --version 2>&1', 'diff3 (GNU diffutils)' );
@@ -521,9 +544,22 @@ print "<li>Installation directory: <tt>" . htmlspecialchars( $conf->IP ) . "</tt
$path = ($_SERVER["PHP_SELF"] === '')
? $_SERVER["SCRIPT_NAME"]
: $_SERVER["PHP_SELF"];
+
$conf->ScriptPath = preg_replace( '{^(.*)/config.*$}', '$1', $path );
print "<li>Script URI path: <tt>" . htmlspecialchars( $conf->ScriptPath ) . "</tt></li>\n";
+
+
+// We may be installing from *.php5 extension file, if so, print message
+$conf->ScriptExtension = '.php';
+if (defined('MW_INSTALL_PHP5_EXT')) {
+ $conf->ScriptExtension = '.php5';
+ print "<li>Installing MediaWiki with <tt>php5</tt> file extensions</li>\n";
+} else {
+ print "<li>Installing MediaWiki with <tt>php</tt> file extensions</li>\n";
+}
+
+
print "<li style='font-weight:bold;color:green;font-size:110%'>Environment checked. You can install MediaWiki.</li>\n";
$conf->posted = ($_SERVER["REQUEST_METHOD"] == "POST");
@@ -547,11 +583,13 @@ print "<li style='font-weight:bold;color:green;font-size:110%'>Environment check
$conf->RootUser = importPost( "RootUser", "root" );
$conf->RootPW = importPost( "RootPW", "" );
$useRoot = importCheck( 'useroot', false );
+ $conf->LanguageCode = importPost( "LanguageCode", "en" );
## MySQL specific:
$conf->DBprefix = importPost( "DBprefix" );
- $conf->setSchema( importPost( "DBschema", "mysql4" ) );
- $conf->LanguageCode = importPost( "LanguageCode", "en" );
+ $conf->setSchema(
+ importPost( "DBschema", "mysql4" ),
+ importPost( "DBengine", "InnoDB" ) );
## Postgres specific:
$conf->DBport = importPost( "DBport", "5432" );
@@ -580,11 +618,47 @@ if( !preg_match( '/^[A-Za-z_0-9]*$/', $conf->DBprefix ) ) {
$errs["DBprefix"] = "Invalid table prefix";
}
-if( $conf->SysopPass == "" ) {
- $errs["SysopPass"] = "Must not be blank";
-}
-if( $conf->SysopPass != $conf->SysopPass2 ) {
- $errs["SysopPass2"] = "Passwords don't match!";
+error_reporting( E_ALL );
+
+/**
+ * Initialise $wgLang and $wgContLang to something so we can
+ * call case-folding methods. Per Brion, this is English for
+ * now, although we could be clever and initialise to the
+ * user-selected language.
+ */
+$wgContLang = Language::factory( 'en' );
+$wgLang = $wgContLang;
+
+/**
+ * We're messing about with users, so we need a stub
+ * authentication plugin...
+ */
+$wgAuth = new AuthPlugin();
+
+/**
+ * Validate the initial administrator account; username,
+ * password checks, etc.
+ */
+if( $conf->SysopName ) {
+ # Check that the user can be created
+ $u = User::newFromName( $conf->SysopName );
+ if( is_a($u, 'User') ) { // please do not use instanceof, it breaks PHP4
+ # Various password checks
+ if( $conf->SysopPass != '' ) {
+ if( $conf->SysopPass == $conf->SysopPass2 ) {
+ if( !$u->isValidPassword( $conf->SysopPass ) ) {
+ $errs['SysopPass'] = "Bad password";
+ }
+ } else {
+ $errs['SysopPass2'] = "Passwords don't match";
+ }
+ } else {
+ $errs['SysopPass'] = "Cannot be blank";
+ }
+ unset( $u );
+ } else {
+ $errs['SysopName'] = "Bad username";
+ }
}
$conf->License = importRequest( "License", "none" );
@@ -678,7 +752,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
$wgCommandLineMode = true;
$wgUseDatabaseMessages = false; /* FIXME: For database failure */
- require_once( "includes/Setup.php" );
+ require_once( "$IP/includes/Setup.php" );
chdir( "config" );
$wgTitle = Title::newFromText( "Installation script" );
@@ -839,7 +913,6 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
print "<li>There are already MediaWiki tables in this database. Checking if updates are needed...</li>\n";
if ( $conf->DBtype == 'mysql') {
-
# Determine existing default character set
if ( $wgDatabase->tableExists( "revision" ) ) {
$revision = $wgDatabase->escapeLike( $conf->DBprefix . 'revision' );
@@ -848,21 +921,35 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
if ( !$row ) {
echo "<li>SHOW TABLE STATUS query failed!</li>\n";
$existingSchema = false;
- } elseif ( preg_match( '/^latin1/', $row->Collation ) ) {
- $existingSchema = 'mysql4';
- } elseif ( preg_match( '/^utf8/', $row->Collation ) ) {
- $existingSchema = 'mysql5';
- } elseif ( preg_match( '/^binary/', $row->Collation ) ) {
- $existingSchema = 'mysql5-binary';
+ $existingEngine = false;
} else {
- $existingSchema = false;
- echo "<li><strong>Warning:</strong> Unrecognised existing collation</li>\n";
+ if ( preg_match( '/^latin1/', $row->Collation ) ) {
+ $existingSchema = 'mysql4';
+ } elseif ( preg_match( '/^utf8/', $row->Collation ) ) {
+ $existingSchema = 'mysql5';
+ } elseif ( preg_match( '/^binary/', $row->Collation ) ) {
+ $existingSchema = 'mysql5-binary';
+ } else {
+ $existingSchema = false;
+ echo "<li><strong>Warning:</strong> Unrecognised existing collation</li>\n";
+ }
+ if ( isset( $row->Engine ) ) {
+ $existingEngine = $row->Engine;
+ } else {
+ $existingEngine = $row->Type;
+ }
}
if ( $existingSchema && $existingSchema != $conf->DBschema ) {
print "<li><strong>Warning:</strong> you requested the {$conf->DBschema} schema, " .
"but the existing database has the $existingSchema schema. This upgrade script ".
"can't convert it, so it will remain $existingSchema.</li>\n";
- $conf->setSchema( $existingSchema );
+ $conf->setSchema( $existingSchema, $conf->DBengine );
+ }
+ if ( $existingEngine && $existingEngine != $conf->DBengine ) {
+ print "<li><strong>Warning:</strong> you requested the {$conf->DBengine} storage " .
+ "engine, but the existing database uses the $existingEngine engine. This upgrade " .
+ "script can't convert it, so it will remain $existingEngine.</li>\n";
+ $conf->setSchema( $conf->DBschema, $existingEngine );
}
}
@@ -890,6 +977,24 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
print "</pre>\n";
print "<ul><li>Finished update checks.</li>\n";
} else {
+ # Determine available storage engines if possible
+ if ( $conf->DBtype == 'mysql' && version_compare( $myver, "4.1.2", "ge" ) ) {
+ $res = $wgDatabase->query( 'SHOW ENGINES' );
+ $found = false;
+ while ( $row = $wgDatabase->fetchObject( $res ) ) {
+ if ( $row->Engine == $conf->DBengine ) {
+ $found = true;
+ break;
+ }
+ }
+ $wgDatabase->freeResult( $res );
+ if ( !$found && $conf->DBengine != 'MyISAM' ) {
+ echo "<li><strong>Warning:</strong> {$conf->DBengine} storage engine not available, " .
+ "using MyISAM instead</li>\n";
+ $conf->setSchema( $conf->DBschema, 'MyISAM' );
+ }
+ }
+
# FIXME: Check for errors
print "<li>Creating tables...";
if ($conf->DBtype == 'mysql') {
@@ -967,7 +1072,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
/* Write out the config file now that all is well */
print "<li style=\"list-style: none\">\n";
print "<p>Creating LocalSettings.php...</p>\n\n";
- $localSettings = "<" . "?php$endl$local$endl\r\n";
+ $localSettings = "<" . "?php$endl$local";
// Fix up a common line-ending problem (due to CVS on Windows)
$localSettings = str_replace( "\r\n", "\n", $localSettings );
$f = fopen( "LocalSettings.php", 'xt' );
@@ -979,13 +1084,13 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
}
if(fwrite( $f, $localSettings ) ) {
fclose( $f );
+ print "</li></ul><hr/>\n";
writeSuccessMessage();
} else {
fclose( $f );
die("<p class='error'>An error occured while writing the config/LocalSettings.php file. Check user rights and disk space then try again.</p>\n");
-
+ print "</li></ul>\n";
}
- print "</li>\n";
} while( false );
}
@@ -1003,7 +1108,7 @@ if( count( $errs ) ) {
}
?>
-<form action="index.php" name="config" method="post">
+<form action="<?php echo defined('MW_INSTALL_PHP5_EXT') ? 'index.php5' : 'index.php'; ?>" name="config" method="post">
<h2>Site config</h2>
@@ -1054,7 +1159,8 @@ if( count( $errs ) ) {
<li><?php
aField( $conf, "License", "A Creative Commons license - ", "radio", "cc" );
$partner = "MediaWiki";
- $exit = urlencode( "$wgServer{$conf->ScriptPath}/config/index.php?License=cc&RightsUrl=[license_url]&RightsText=[license_name]&RightsCode=[license_code]&RightsIcon=[license_button]" );
+ $script = defined('MW_INSTALL_PHP5_EXT') ? 'index.php5' : 'index.php';
+ $exit = urlencode( "$wgServer{$conf->ScriptPath}/config/$script?License=cc&RightsUrl=[license_url]&RightsText=[license_name]&RightsCode=[license_code]&RightsIcon=[license_button]" );
$icon = urlencode( "$wgServer$wgUploadPath/wiki.png" );
$ccApp = htmlspecialchars( "http://creativecommons.org/license/?partner=$partner&exit_url=$exit&partner_icon_url=$icon" );
print "<a href=\"$ccApp\" target='_blank'>choose</a>";
@@ -1087,6 +1193,8 @@ if( count( $errs ) ) {
<p class="config-desc">
An admin can lock/delete pages, block users from editing, and do other maintenance tasks.<br />
A new account will be added only when creating a new wiki database.
+ <br /><br />
+ The password cannot be the same as the username.
</p>
<div class="config-input">
@@ -1100,6 +1208,11 @@ if( count( $errs ) ) {
aField( $conf, "Shm", "Turck MMCache", "radio", "turck" );
echo "</li>";
}
+ if( $conf->xcache ) {
+ echo( '<li>' );
+ aField( $conf, 'Shm', 'XCache', 'radio', 'xcache' );
+ echo( '</li>' );
+ }
if ( $conf->apc ) {
echo "<li>";
aField( $conf, "Shm", "APC", "radio", "apc" );
@@ -1116,10 +1229,11 @@ if( count( $errs ) ) {
<div style="clear:left"><?php aField( $conf, "MCServers", "Memcached servers:", "text" ) ?></div>
</div>
<p class="config-desc">
- Using a shared memory system such as Turck MMCache, APC, eAccelerator, or Memcached
- will speed up MediaWiki significantly. Memcached is the best solution but needs to be
- installed. Specify the server addresses and ports in a comma-separated list. Only
- use Turck shared memory if the wiki will be running on a single Apache server.
+ An object caching system such as memcached will provide a significant performance boost,
+ but needs to be installed. Provide the server addresses and ports in a comma-separated list.
+ <br /><br />
+ MediaWiki can also detect and support eAccelerator, Turck MMCache, APC, and XCache, but
+ these should not be used if the wiki will be running on multiple application servers.
</p>
</div>
@@ -1249,7 +1363,19 @@ if( count( $errs ) ) {
<p>Avoid exotic characters; something like <tt>mw_</tt> is good.</p>
</div>
- <div class="config-input"><label class="column">Database charset</label>
+ <div class="config-input"><label class="column">Storage Engine</label>
+ <div>Select one:</div>
+ <ul class="plain">
+ <li><?php aField( $conf, "DBengine", "InnoDB", "radio", "InnoDB" ); ?></li>
+ <li><?php aField( $conf, "DBengine", "MyISAM", "radio", "MyISAM" ); ?></li>
+ </ul>
+ </div>
+ <p class="config-desc">
+ InnoDB is best for public web installations, since it has good concurrency
+ support. MyISAM may be faster in single-user installations. MyISAM databases
+ tend to get corrupted more often than InnoDB databases.
+ </p>
+ <div class="config-input"><label class="column">Database character set</label>
<div>Select one:</div>
<ul class="plain">
<li><?php aField( $conf, "DBschema", "Backwards-compatible UTF-8", "radio", "mysql4" ); ?></li>
@@ -1280,7 +1406,7 @@ if( count( $errs ) ) {
so it is recommended that you create a new user. The above schemas are generally correct:
only change them if you are sure you need to.</p>
</div>
- </div>
+ </fieldset>
<div class="config-input" style="padding:2em 0 3em">
<label class='column'>&nbsp;</label>
@@ -1304,24 +1430,35 @@ window.onload = toggleDBarea('<?php echo $conf->DBtype; ?>',
/* -------------------------------------------------------------------------------------- */
function writeSuccessMessage() {
+ $script = defined('MW_INSTALL_PHP5_EXT') ? 'index.php5' : 'index.php';
if ( ini_get( 'safe_mode' ) && !ini_get( 'open_basedir' ) ) {
echo <<<EOT
+<div class="success-box">
<p>Installation successful!</p>
<p>To complete the installation, please do the following:
<ol>
<li>Download config/LocalSettings.php with your FTP client or file manager</li>
<li>Upload it to the parent directory</li>
<li>Delete config/LocalSettings.php</li>
- <li>Start using <a href='../index.php'>your wiki</a>!
+ <li>Start using <a href='../$script'>your wiki</a>!
</ol>
<p>If you are in a shared hosting environment, do <strong>not</strong> just move LocalSettings.php
remotely. LocalSettings.php is currently owned by the user your webserver is running under,
which means that anyone on the same server can read your database password! Downloading
it and uploading it again will hopefully change the ownership to a user ID specific to you.</p>
+</div>
EOT;
} else {
- echo "<p><span style='font-weight:bold;color:green;font-size:110%'>Installation successful!</span> Move the <tt>config/LocalSettings.php</tt> file into the parent directory, then follow
- <strong><a href='../index.php'>this link</a></strong> to your wiki.</p>\n";
+ echo <<<EOT
+<div class="success-box">
+<p>
+<span class="success-message">Installation successful!</span>
+Move the <tt>config/LocalSettings.php</tt> file to the parent directory, then follow
+<a href="../$script"> this link</a> to your wiki.</p>
+<p>You should change file permissions for <tt>LocalSettings.php</tt> as required to
+prevent other users on the server reading passwords and altering configuration data.</p>
+</div>
+EOT;
}
}
@@ -1351,6 +1488,7 @@ function writeLocalSettings( $conf ) {
$mcservers = var_export( $conf->MCServerArray, true );
break;
case 'turck':
+ case 'xcache':
case 'apc':
case 'eaccel':
$cacheType = 'CACHE_ACCEL';
@@ -1425,7 +1563,7 @@ if( defined( 'MW_INSTALL_PATH' ) ) {
\$path = array( \$IP, \"\$IP/includes\", \"\$IP/languages\" );
set_include_path( implode( PATH_SEPARATOR, \$path ) . PATH_SEPARATOR . get_include_path() );
-require_once( \"includes/DefaultSettings.php\" );
+require_once( \"\$IP/includes/DefaultSettings.php\" );
# If PHP's memory limit is very low, some operations may fail.
" . ($conf->raiseMemory ? '' : '# ' ) . "ini_set( 'memory_limit', '20M' );" . "
@@ -1443,6 +1581,7 @@ if ( \$wgCommandLineMode ) {
## The URL base path to the directory containing the wiki;
## defaults for all runtime URL paths are based off of this.
\$wgScriptPath = \"{$slconf['ScriptPath']}\";
+\$wgScriptExtension = \"{$slconf['ScriptExtension']}\";
## For more information on customizing the URLs please see:
## http://www.mediawiki.org/wiki/Manual:Short_URL
@@ -1454,7 +1593,8 @@ if ( \$wgCommandLineMode ) {
\$wgPasswordSender = \"{$slconf['PasswordSender']}\";
## For a detailed description of the following switches see
-## http://meta.wikimedia.org/Enotif and http://meta.wikimedia.org/Eauthent
+## http://www.mediawiki.org/wiki/Extension:Email_notification
+## and http://www.mediawiki.org/wiki/Extension:Email_notification
## There are many more options for fine tuning available see
## /includes/DefaultSettings.php
## UPO means: this is also a user preference option
@@ -1467,19 +1607,21 @@ if ( \$wgCommandLineMode ) {
\$wgDBname = \"{$slconf['DBname']}\";
\$wgDBuser = \"{$slconf['DBuser']}\";
\$wgDBpassword = \"{$slconf['DBpassword']}\";
-\$wgDBport = \"{$slconf['DBport']}\";
+
+# MySQL specific settings
\$wgDBprefix = \"{$slconf['DBprefix']}\";
# MySQL table options to use during installation or update
\$wgDBTableOptions = \"{$slconf['DBTableOptions']}\";
-# Schemas for Postgres
-\$wgDBmwschema = \"{$slconf['DBmwschema']}\";
-\$wgDBts2schema = \"{$slconf['DBts2schema']}\";
-
# Experimental charset support for MySQL 4.1/5.0.
\$wgDBmysql5 = {$conf->DBmysql5};
+# Postgres specific settings
+\$wgDBport = \"{$slconf['DBport']}\";
+\$wgDBmwschema = \"{$slconf['DBmwschema']}\";
+\$wgDBts2schema = \"{$slconf['DBts2schema']}\";
+
## Shared memory settings
\$wgMainCacheType = $cacheType;
\$wgMemCachedServers = $mcservers;
@@ -1610,9 +1752,9 @@ function aField( &$conf, $field, $text, $type = "text", $value = "", $onclick =
}
function getLanguageList() {
- global $wgLanguageNames;
+ global $wgLanguageNames, $IP;
if( !isset( $wgLanguageNames ) ) {
- require_once( "languages/Names.php" );
+ require_once( "$IP/languages/Names.php" );
}
$codes = array();
@@ -1722,8 +1864,7 @@ function database_switcher($db) {
global $ourdb;
$color = $ourdb[$db]['bgcolor'];
$full = $ourdb[$db]['fullname'];
- print "<div id='$db' style='display:none; background: $color'>\n";
- print "<h3>$full specific options:</h3>\n";
+ print "<fieldset id='$db'><legend>$full specific options</legend>\n";
}
function printListItem( $item ) {
diff --git a/config/index.php5 b/config/index.php5
new file mode 100644
index 00000000..1be08780
--- /dev/null
+++ b/config/index.php5
@@ -0,0 +1,6 @@
+<?php
+
+define('MW_INSTALL_PHP5_EXT', 1);
+require './index.php';
+
+?>
diff --git a/docs/README b/docs/README
index 43ac8ef5..1ec3986b 100644
--- a/docs/README
+++ b/docs/README
@@ -1,9 +1,9 @@
-[July 5th 2005]
+[May 31st 2007]
The 'docs' directory contain various text files that should help you
understand the most importants classes in MediaWiki.
-API documentation is sometime generated and uploaded at:
+API documentation is automatically generated and updated daily at:
http://svn.wikimedia.org/doc/
You can get a fresh version using 'make doc' or mwdocgen.php
@@ -13,5 +13,5 @@ in the ../maintenance/ directory.
For end user / administrators, most of the documentation
is located online at:
- http://meta.wikimedia.org/wiki/Help:Help
+ http://www.mediawiki.org/wiki/Project:Help
diff --git a/docs/design.txt b/docs/design.txt
index 8f24d0d8..1a35d5b0 100644
--- a/docs/design.txt
+++ b/docs/design.txt
@@ -34,8 +34,7 @@ Primary source files/objects:
calling output() to send it all. It could be easily changed
to send incrementally if that becomes useful, but I prefer
the flexibility. This should also do the output encoding.
- The system allocates a global one in $wgOut. This class
- also handles converting wikitext format to HTML.
+ The system allocates a global one in $wgOut.
Title
Represents the title of an article, and does all the work
@@ -69,7 +68,9 @@ Primary source files/objects:
Language
Represents the language used for incidental text, and also
has some character encoding functions and other locale stuff.
- A global one is allocated in $wgLang.
+ The current user interface language is instantiated as $wgLang,
+ and the local content language as $wgContLang; be sure to use
+ the *correct* language object depending upon the circumstances.
LinkCache
Keeps information on existence of articles. See LINKCACHE.TXT.
@@ -94,13 +95,14 @@ Naming/coding conventions:
its own line or the statement that opened the block--that's
confusing as hell.
- - PHP doesn't have "private" member variables of functions,
- so I've used the comment "/* private */" in some places to
- indicate my intent. Don't access things marked that way
- from outside the class def--use the accessor functions (or
- make your own if you need them). Yes, even some globals
- are marked private, because PHP is broken and doesn't
- allow static class variables.
+ - Certain functions and class members are marked with
+ /* private */, rather than being marked as such. This is a
+ hold-over from PHP 4, which didn't support proper visibilities.
+ You should not access things marked in this manner outside the
+ class/inheritance line as this code is subjected to be updated
+ in a manner that enforces this at some time in the near future,
+ and things will break. New code should use the standard method of
+ setting visibilities as normal.
- Member variables are generally "mXxx" to distinguish them.
This should make it easier to spot errors of forgetting the
@@ -123,5 +125,4 @@ Naming/coding conventions:
Other conventions: Top-level functions are wfFuncname(), names
of session variables are wsName, cookies wcName, and form field
- values wpName ("p" for "POST").
-
+ values wpName ("p" for "POST"). \ No newline at end of file
diff --git a/docs/hooks.txt b/docs/hooks.txt
index 9f5d289f..9614bead 100644
--- a/docs/hooks.txt
+++ b/docs/hooks.txt
@@ -35,20 +35,20 @@ order of a title before displaying the article; the other converts the
title to all uppercase letters. Currently, in MediaWiki code, we
would handle this as follows (note: not real code, here):
- function showAnArticle($article) {
- global $wgReverseTitle, $wgCapitalizeTitle;
-
- if ($wgReverseTitle) {
- wfReverseTitle($article);
- }
-
- if ($wgCapitalizeTitle) {
- wfCapitalizeTitle($article);
- }
-
- # code to actually show the article goes here
- }
-
+ function showAnArticle($article) {
+ global $wgReverseTitle, $wgCapitalizeTitle;
+
+ if ($wgReverseTitle) {
+ wfReverseTitle($article);
+ }
+
+ if ($wgCapitalizeTitle) {
+ wfCapitalizeTitle($article);
+ }
+
+ # code to actually show the article goes here
+ }
+
An extension writer, or a local admin, will often add custom code to
the function -- with or without a global variable. For example,
someone wanting email notification when an article is shown may add:
@@ -75,15 +75,15 @@ Using a hook-running strategy, we can avoid having all this
option-specific stuff in our mainline code. Using hooks, the function
becomes:
- function showAnArticle($article) {
+ function showAnArticle($article) {
- if (wfRunHooks('ArticleShow', array(&$article))) {
-
- # code to actually show the article goes here
-
- wfRunHooks('ArticleShowComplete', array(&$article));
+ if (wfRunHooks('ArticleShow', array(&$article))) {
+
+ # code to actually show the article goes here
+
+ wfRunHooks('ArticleShowComplete', array(&$article));
+ }
}
- }
We've cleaned up the code here by removing clumps of weird,
infrequently used code and moving them off somewhere else. It's much
@@ -96,24 +96,24 @@ having little title-reversing if-blocks spread all over the codebase
in showAnArticle, deleteAnArticle, exportArticle, etc., we can
concentrate it all in an extension file:
- function reverseArticleTitle($article) {
- # ...
- }
+ function reverseArticleTitle($article) {
+ # ...
+ }
- function reverseForExport($article) {
- # ...
- }
+ function reverseForExport($article) {
+ # ...
+ }
The setup function for the extension just has to add its hook
functions to the appropriate events:
- setupTitleReversingExtension() {
- global $wgHooks;
-
- $wgHooks['ArticleShow'][] = 'reverseArticleTitle';
- $wgHooks['ArticleDelete'][] = 'reverseArticleTitle';
- $wgHooks['ArticleExport'][] = 'reverseForExport';
- }
+ setupTitleReversingExtension() {
+ global $wgHooks;
+
+ $wgHooks['ArticleShow'][] = 'reverseArticleTitle';
+ $wgHooks['ArticleDelete'][] = 'reverseArticleTitle';
+ $wgHooks['ArticleExport'][] = 'reverseForExport';
+ }
Having all this code related to the title-reversion option in one
place means that it's easier to read and understand; you don't have to
@@ -124,8 +124,8 @@ used -- making for some slight savings in memory and load-up
performance at runtime. Admins who want to have all the reversed
titles can add:
- require_once('extensions/ReverseTitle.php');
-
+ require_once('extensions/ReverseTitle.php');
+
...to their LocalSettings.php file; those of us who don't want or need
it can just leave it out.
@@ -143,31 +143,31 @@ A hook is a chunk of code run at some particular event. It consists of:
Hooks are registered by adding them to the global $wgHooks array for a
given event. All the following are valid ways to define hooks:
- $wgHooks['EventName'][] = 'someFunction'; # function, no data
- $wgHooks['EventName'][] = array('someFunction', $someData);
- $wgHooks['EventName'][] = array('someFunction'); # weird, but OK
-
- $wgHooks['EventName'][] = $object; # object only
- $wgHooks['EventName'][] = array($object, 'someMethod');
- $wgHooks['EventName'][] = array($object, 'someMethod', $someData);
- $wgHooks['EventName'][] = array($object); # weird but OK
+ $wgHooks['EventName'][] = 'someFunction'; # function, no data
+ $wgHooks['EventName'][] = array('someFunction', $someData);
+ $wgHooks['EventName'][] = array('someFunction'); # weird, but OK
+
+ $wgHooks['EventName'][] = $object; # object only
+ $wgHooks['EventName'][] = array($object, 'someMethod');
+ $wgHooks['EventName'][] = array($object, 'someMethod', $someData);
+ $wgHooks['EventName'][] = array($object); # weird but OK
When an event occurs, the function (or object method) will be called
with the optional data provided as well as event-specific parameters.
The above examples would result in the following code being executed
when 'EventName' happened:
- # function, no data
- someFunction($param1, $param2)
- # function with data
- someFunction($someData, $param1, $param2)
-
- # object only
- $object->onEventName($param1, $param2)
- # object with method
- $object->someMethod($param1, $param2)
- # object with method and data
- $object->someMethod($someData, $param1, $param2)
+ # function, no data
+ someFunction($param1, $param2)
+ # function with data
+ someFunction($someData, $param1, $param2)
+
+ # object only
+ $object->onEventName($param1, $param2)
+ # object with method
+ $object->someMethod($param1, $param2)
+ # object with method and data
+ $object->someMethod($someData, $param1, $param2)
Note that when an object is the hook, and there's no specified method,
the default method called is 'onEventName'. For different events this
@@ -176,8 +176,8 @@ would be different: 'onArticleSave', 'onUserLogin', etc.
The extra data is useful if we want to use the same function or object
for different purposes. For example:
- $wgHooks['ArticleSaveComplete'][] = array('ircNotify', 'TimStarling');
- $wgHooks['ArticleSaveComplete'][] = array('ircNotify', 'brion');
+ $wgHooks['ArticleSaveComplete'][] = array('ircNotify', 'TimStarling');
+ $wgHooks['ArticleSaveComplete'][] = array('ircNotify', 'brion');
This code would result in ircNotify being run twice when an article is
saved: once for 'TimStarling', and once for 'brion'.
@@ -195,12 +195,12 @@ the main functionality. For example, if you wanted to authenticate
users to a custom system (LDAP, another PHP program, whatever), you
could do:
- $wgHooks['UserLogin'][] = array('ldapLogin', $ldapServer);
-
- function ldapLogin($username, $password) {
- # log user into LDAP
- return false;
- }
+ $wgHooks['UserLogin'][] = array('ldapLogin', $ldapServer);
+
+ function ldapLogin($username, $password) {
+ # log user into LDAP
+ return false;
+ }
Returning false makes less sense for events where the action is
complete, and will normally be ignored.
@@ -210,14 +210,15 @@ complete, and will normally be ignored.
A calling function or method uses the wfRunHooks() function to run
the hooks related to a particular event, like so:
- class Article {
- # ...
- function protect() {
- global $wgUser;
- if (wfRunHooks('ArticleProtect', array(&$this, &$wgUser))) {
- # protect the article
- wfRunHooks('ArticleProtectComplete', array(&$this, &$wgUser));
- }
+ class Article {
+ # ...
+ function protect() {
+ global $wgUser;
+ if (wfRunHooks('ArticleProtect', array(&$this, &$wgUser))) {
+ # protect the article
+ wfRunHooks('ArticleProtectComplete', array(&$this, &$wgUser));
+ }
+ }
}
wfRunHooks() returns true if the calling function should continue
@@ -237,6 +238,13 @@ protocol came about after MediaWiki 1.4rc1.
This is a list of known events and parameters; please add to it if
you're going to add events to the MediaWiki code.
+'AbortLogin': Return false to cancel account login.
+$user: the User object being authenticated against
+$password: the password being submitted, not yet checked for validity
+&$retval: a LoginForm class constant to return from authenticateUserData();
+ default is LoginForm::ABORTED. Note that the client may be using
+ a machine API rather than the HTML user interface.
+
'AbortNewAccount': Return false to cancel account creation.
$user: the User object about to be created (read-only, incomplete)
$message: out parameter: error message to display on abort
@@ -244,6 +252,15 @@ $message: out parameter: error message to display on abort
'AddNewAccount': after a user account is created
$user: the User object that was created. (Parameter added in 1.7)
+'AjaxAddScript': Called in output page just before the initialisation
+of the javascript ajax engine. The hook is only called when ajax
+is enabled ( $wgUseAjax = true; ).
+
+'AlternateEdit': before checking if an user can edit a page and
+before showing the edit form ( EditPage::edit() ). This is triggered
+on &action=edit.
+$EditPage : the EditPage object
+
'ArticleDelete': before an article is deleted
$article: the article (object) being deleted
$user: the user (object) deleting the article
@@ -254,6 +271,17 @@ $article: the article that was deleted
$user: the user that deleted the article
$reason: the reason the article was deleted
+'ArticleInsertComplete': After an article is created
+$article: Article created
+$user: User creating the article
+$text: New content
+$summary: Edit summary/comment
+$isMinor: Whether or not the edit was marked as minor
+$isWatch: (No longer used)
+$section: (No longer used)
+$flags: Flags passed to Article::doEdit()
+$revision: New Revision of the article
+
'ArticleProtect': before an article is protected
$article: the article being protected
$user: the user doing the protection
@@ -277,6 +305,17 @@ $isminor: minor flag
$iswatch: watch flag
$section: section #
+'ArticleSaveComplete': After an article has been updated
+$article: Article modified
+$user: User performing the modification
+$text: New content
+$summary: Edit summary/comment
+$isMinor: Whether or not the edit was marked as minor
+$isWatch: (No longer used)
+$section: (No longer used)
+$flags: Flags passed to Article::doEdit()
+$revision: New Revision of the article
+
'ArticleSaveComplete': after an article is saved
$article: the article (object) saved
$user: the user (object) who saved the article
@@ -286,11 +325,22 @@ $isminor: minor flag
$iswatch: watch flag
$section: section #
+wfRunHooks( 'ArticleSaveComplete', array( &$this, &$wgUser, $text, $summary, $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+
'ArticleUndeleted': When one or more revisions of an article are restored
$title: Title corresponding to the article restored
$create: Whether or not the restoration caused the page to be created
(i.e. it didn't exist before)
+'ArticleViewHeader': Before the parser cache is about to be tried for article viewing.
+&$pcache: whether to try the parser cache or not
+&$outputDone: whether the output for this page finished or not
+
+'ArticleUpdateBeforeRedirect': After a page is updated (usually on save), before the user is redirected back to the page
+&$article: the article
+&$sectionanchor: The section anchor link (e.g. "#overview" )
+&$extraq: Extra query parameters which can be added via hooked functions
+
'AuthPluginSetup': update or replace authentication plugin object ($wgAuth)
Gives a chance for an extension to set it programattically to a variable class.
&$auth: the $wgAuth object, probably a stub
@@ -305,6 +355,30 @@ $name: Image name being checked
Change $bad and return false to override. If an image is "bad", it is not
rendered inline in wiki pages or galleries in category pages.
+'BeforeGalleryFindFile': before an image is fetched for a gallery
+&$gallery,: the gallery object
+&$nt: the image title
+&$time: image timestamp
+
+'BeforePageDisplay': Prior to outputting a page
+$out: OutputPage object
+
+'BeforeParserFetchTemplateAndtitle': before a template is fetched by Parser
+&$parser: Parser object
+&$title: title of the template
+&$skip: skip this template and link it?
+&$id: the id of the revision being parsed
+
+'BeforeParserMakeImageLinkObj': before an image is rendered by Parser
+&$parser: Parser object
+&$nt: the image title
+&$skip: skip this image and link it?
+&$time: the image timestamp
+
+'BeforeParserrenderImageGallery': before an image gallery is rendered by Parser
+&$parser: Parser object
+&$ig: ImageGallery object
+
'BlockIp': before an IP address or user is blocked
$block: the Block object about to be saved
$user: the user _doing_ the block (not the one being blocked)
@@ -317,6 +391,14 @@ $user: the user who did the block (not the one being blocked)
$isbn: ISBN to show information for
$output: OutputPage object in use
+'CategoryPageView': before viewing a categorypage in CategoryPage::view
+$catpage: CategoryPage instance
+
+'ContributionsToolLinks': Change tool links above Special:Contributions
+$id: User identifier
+$title: User page title
+&$tools: Array of tool links
+
'CustomEditor': When invoking the page editor
$article: Article being edited
$user: User performing the edit
@@ -354,6 +436,21 @@ Alternatively, modifying $error and returning true will cause the contents of $e
to be echoed at the top of the edit form as wikitext. Return true without altering
$error to allow the edit to proceed.
+'EditSectionLink': Override the return value of Linker::editSectionLink()
+$skin: Skin rendering the UI
+$title: Title being linked to
+$section: Section to link to
+$link: Default link
+$result: Result (alter this to override the generated links)
+
+'EditSectionLinkForOther': Override the return value of Linker::editSectionLinkForOther()
+$skin: Skin rendering the UI
+$title: Title being linked to
+$section: Section to link to
+$hint: Anchor title/tooltip attributes
+$link: Default link
+$result: Result (alter this to override the generated links)
+
'EmailConfirmed': When checking that the user's email address is "confirmed"
$user: User being checked
$confirmed: Whether or not the email address is confirmed
@@ -396,12 +493,34 @@ $title: Title object of page
$url: string value as output (out parameter, can modify)
$query: query options passed to Title::getFullURL()
+'ImageOpenShowImageInlineBefore': Call potential extension just before showing the image on an image page
+$imagePage: ImagePage object ($this)
+$output: $wgOut
+
+'InitPreferencesForm': called at the end of PreferencesForm's constructor
+$form: the PreferencesForm
+$request: the web request to initialized from
+
'InternalParseBeforeLinks': during Parser's internalParse method before links but
after noinclude/includeonly/onlyinclude and other processing.
&$this: Parser object
&$text: string containing partially parsed text
&$this->mStripState: Parser's internal StripState object
+'isValidPassword': Override the result of User::isValidPassword()
+$password: Desired password
+&$result: Set this and return false to override the internal checks
+$user: User the password is being validated for
+
+'LinksUpdateConstructed': At the end of LinksUpdate() is contruction.
+&$linksUpdate: the LinkUpdate object
+
+'LoginAuthenticateAudit': a login attempt for a valid user account either succeeded or failed.
+ No return data is accepted; this hook is for auditing only.
+$user: the User object being authenticated against
+$password: the password being submitted and found wanting
+$retval: a LoginForm class constant with authenticateUserData() return value (SUCCESS, WRONG_PASS, etc)
+
'LogPageValidTypes': action being logged. DEPRECATED: Use $wgLogTypes
&$type: array of strings
@@ -474,12 +593,31 @@ $form : PreferencesForm object
&$obj: RawPage object
&$text: The text that's going to be the output
+'RenderPreferencesForm': called at the end of PreferencesForm::mainPrefsForm
+$form: the PreferencesForm
+$out: output page to render to, probably $wgOut
+
+'ResetPreferences': called at the end of PreferencesForm::resetPrefs
+$form: the PreferencesForm
+$user: the User object to load preferences from
+
+'SavePreferences': called at the end of PreferencesForm::savePreferences;
+ returning false prevents the preferences from being saved.
+$form: the PreferencesForm
+$user: the User object to save preferences to
+$message: change this to set an error message (ignored if the hook does notreturn fals)
+
'SearchUpdate': Prior to search update completion
$id : Page id
$namespace : Page namespace
$title : Page title
$text : Current text being indexed
+'ShowRawCssJs': Customise the output of raw CSS and JavaScript in page views
+$text: Text being shown
+$title: Title of the custom script/stylesheet page
+$output: Current OutputPage object
+
'SiteNoticeBefore': Before the sitenotice/anonnotice is composed
&$siteNotice: HTML returned as the sitenotice
Return true to allow the normal method of notice selection/rendering to work,
@@ -489,10 +627,23 @@ or change the value of $siteNotice and return false to alter it.
&$siteNotice: HTML sitenotice
Alter the contents of $siteNotice to add to/alter the sitenotice/anonnotice.
+'SkinAfterBottomScripts': At the end of Skin::bottomScripts()
+$skin: Skin object
+&$text: bottomScripts Text
+Append to $text to add additional text/scripts after the stock bottom scripts.
+
+'SkinTemplateContentActions': Alter the "content action" links in SkinTemplates
+&$content_actions: Content actions
+[See http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/examples/Content_action.php
+for an example]
+
'SkinTemplateOutputPageBeforeExec': Before SkinTemplate::outputPage() starts page output
&$sktemplate: SkinTemplate object
&$tpl: Template engine object
+'SpecialContributionsBeforeMainOutput': Before the form on Special:Contributions
+$id: User identifier
+
'TitleMoveComplete': after moving an article (title)
$old: old title
$nt: new title
@@ -513,6 +664,10 @@ $article: article object to be removed
$user: user that was watching
$article: article object removed
+'UnwatchArticleComplete': after a watch is removed from an article
+$user: user that watched
+$article: article object that was watched
+
'UploadForm:initial': before the upload form is generated
$form: UploadForm object
You might set the member-variables $uploadFormTextTop and
@@ -559,6 +714,17 @@ $user : User object that was changed
$add : Array of strings corresponding to groups added
$remove: Array of strings corresponding to groups removed
+'UserGetImplicitGroups': Called in User::getImplicitGroups()
+&$groups: List of implicit (automatically-assigned) groups
+
+'UserGetRights': Called in User::getRights()
+$user: User to get rights for
+&$rights: Current rights
+
+'UserEffectiveGroups': Called in User::getEffectiveGroups()
+$user: User to get groups for
+&$groups: Current effective groups
+
'WatchArticle': before a watch is added to an article
$user: user that will watch
$article: article object to be watched
@@ -567,28 +733,6 @@ $article: article object to be watched
$user: user that watched
$article: article object watched
-'UnwatchArticleComplete': after a watch is removed from an article
-$user: user that watched
-$article: article object that was watched
-
-'CategoryPageView': before viewing a categorypage in CategoryPage::view
-$catpage: CategoryPage instance
-
-'SkinTemplateContentActions': after building the $content_action array right
- before returning it, see Content_action.php in
- the extensions/examples/ directory
- ( http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/examples/ )
- for a demonstration of how to use this hook.
-$content_actions: The array of content actions
-
-'BeforePageDisplay': Called just before outputting a page (all kinds of,
- articles, special, history, preview, diff, edit, ...)
- Can be used to set custom CSS/JS
-$out: OutputPage object
-
-'AjaxAddScript': Called in output page just before the initialisation
-of the javascript ajax engine. The hook is only called when ajax
-is enabled ( $wgUseAjax = true; ).
More hooks might be available but undocumented, you can execute
-./maintenance/findhooks.php to find hidden one.
+./maintenance/findhooks.php to find hidden one. \ No newline at end of file
diff --git a/docs/memcached.txt b/docs/memcached.txt
index d4e2915f..3addd965 100644
--- a/docs/memcached.txt
+++ b/docs/memcached.txt
@@ -53,7 +53,7 @@ on port 11000, using up to 64MB of memory)
In your LocalSettings.php file, set:
- $wgMainCacheType = CACHE_MEMCACHED;;
+ $wgMainCacheType = CACHE_MEMCACHED;
$wgMemCachedServers = array( "127.0.0.1:11000" );
The wiki should then use memcached to cache various data. To use
diff --git a/docs/schema.txt b/docs/schema.txt
index f7348462..365576cc 100644
--- a/docs/schema.txt
+++ b/docs/schema.txt
@@ -4,3 +4,6 @@ which is called from the installation script.
That file has been commented with details of the usage for
each table and field.
+
+Historical information and some other notes are available at
+http://www.mediawiki.org/wiki/Manual:Database_layout
diff --git a/docs/title.txt b/docs/title.txt
index 5d9bd417..fd449c54 100644
--- a/docs/title.txt
+++ b/docs/title.txt
@@ -10,15 +10,15 @@ attributes of the title. This is intended to be an
immutable "value" class, so there are no mutator functions.
To get a new instance, call one of the static factory
-methods WikiTitle::newFromURL(), WikiTitle::newFromDBKey(),
-or WikiTitle::newFromText(). Once instantiated, the
+methods Title::newFromURL(), Title::newFromDBKey(),
+or Title::newFromText(). Once instantiated, the
other non-static accessor methods can be used, such as
getText(), getDBKey(), getNamespace(), etc.
-The prefix rules: a title consists of an optional Interwiki
+The prefix rules: a title consists of an optional interwiki
prefix (such as "m:" for meta or "de:" for German), followed
by an optional namespace, followed by the remainder of the
-title. Both Interwiki prefixes and namespace prefixes have
+title. Both interwiki prefixes and namespace prefixes have
the same rules: they contain only letters, digits, space, and
underscore, must start with a letter, are case insensitive,
and spaces and underscores are interchangeable. Prefixes end
@@ -74,4 +74,3 @@ it returns 0. For all external articles it returns 0. All of
the IDs for all instances of Title created during a request are
cached, so they can be looked up quickly while rendering wiki
text with lots of internal links.
-
diff --git a/extensions/LLAuthPlugin.php b/extensions/LLAuthPlugin.php
index 1910dcde..59bf19e7 100644
--- a/extensions/LLAuthPlugin.php
+++ b/extensions/LLAuthPlugin.php
@@ -21,7 +21,7 @@ class LLAuthPlugin extends AuthPlugin {
return ($length >= 6 && $length <= 25);
}
- function __destruct()
+ function __destruct()
{
if (!is_null($this->dbLink))
{
@@ -117,7 +117,7 @@ class LLAuthPlugin extends AuthPlugin {
return true;
}
- function initUser( &$user ) {
+ function initUser( $user, $autocreate=false ) {
$data = $this->getUserData($user->getName());
$user->setEmail($data['email']);
$user->confirmEmail();
@@ -128,7 +128,16 @@ class LLAuthPlugin extends AuthPlugin {
function getCanonicalName( $username ) {
// fix bug #122
$data = $this->getUserData($username);
- return $data['name'];
+ // needed for update.php
+ if (is_null($data))
+ {
+ return $username;
+ }
+ else
+ {
+ // make sure that first char is uppercase
+ return strtoupper(substr($data['name'], 0, 1)).substr($data['name'], 1);
+ }
}
}
diff --git a/img_auth.php b/img_auth.php
index 11684b37..2e8f6240 100644
--- a/img_auth.php
+++ b/img_auth.php
@@ -1,63 +1,89 @@
<?php
+
/**
- * Image download authorisation script
+ * Image authorisation script
+ *
+ * To use this:
+ *
+ * Set $wgUploadDirectory to a non-public directory (not web accessible)
+ * Set $wgUploadPath to point to this file
*
- * To use, in LocalSettings.php set $wgUploadDirectory to point to a non-public
- * directory, and $wgUploadPath to point to this file. Also set $wgWhitelistRead
- * to an array of pages you want everyone to be able to access. Your server must
- * support PATH_INFO, CGI-based configurations generally don't.
+ * Your server needs to support PATH_INFO; CGI-based configurations
+ * usually don't.
*/
+
define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
-require_once( './includes/WebStart.php' );
+require_once( dirname( __FILE__ ) . '/includes/WebStart.php' );
wfProfileIn( 'img_auth.php' );
-require_once( './includes/StreamFile.php' );
+require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' );
+// Extract path and image information
if( !isset( $_SERVER['PATH_INFO'] ) ) {
- wfDebugLog( 'img_auth', "missing PATH_INFO" );
+ wfDebugLog( 'img_auth', 'Missing PATH_INFO' );
wfForbidden();
}
-# Get filenames/directories
-wfDebugLog( 'img_auth', "PATH_INFO is: " . $_SERVER['PATH_INFO'] );
+$path = $_SERVER['PATH_INFO'];
$filename = realpath( $wgUploadDirectory . $_SERVER['PATH_INFO'] );
-$realUploadDirectory = realpath( $wgUploadDirectory );
-$imageName = $wgContLang->getNsText( NS_IMAGE ) . ":" . wfBaseName( $_SERVER['PATH_INFO'] );
+$realUpload = realpath( $wgUploadDirectory );
+wfDebugLog( 'img_auth', "\$path is {$path}" );
+wfDebugLog( 'img_auth', "\$filename is {$filename}" );
-# Check if the filename is in the correct directory
-if ( substr( $filename, 0, strlen( $realUploadDirectory ) ) != $realUploadDirectory ) {
- wfDebugLog( 'img_auth', "requested path not in upload dir: $filename" );
+// Basic directory traversal check
+if( substr( $filename, 0, strlen( $realUpload ) ) != $realUpload ) {
+ wfDebugLog( 'img_auth', 'Requested path not in upload directory' );
wfForbidden();
}
-if ( is_array( $wgWhitelistRead ) && !in_array( $imageName, $wgWhitelistRead ) && !$wgUser->getID() ) {
- wfDebugLog( 'img_auth', "not logged in and requested file not in whitelist: $imageName" );
+// Extract the file name and chop off the size specifier
+// (e.g. 120px-Foo.png => Foo.png)
+$name = wfBaseName( $path );
+if( preg_match( '!\d+px-(.*)!i', $name, $m ) )
+ $name = $m[1];
+wfDebugLog( 'img_auth', "\$name is {$name}" );
+
+$title = Title::makeTitleSafe( NS_IMAGE, $name );
+if( !$title instanceof Title ) {
+ wfDebugLog( 'img_auth', "Unable to construct a valid Title from `{$name}`" );
+ wfForbidden();
+}
+$title = $title->getPrefixedText();
+
+// Check the whitelist if needed
+if( !$wgUser->getId() && ( !is_array( $wgWhitelistRead ) || !in_array( $title, $wgWhitelistRead ) ) ) {
+ wfDebugLog( 'img_auth', "Not logged in and `{$title}` not in whitelist." );
wfForbidden();
}
if( !file_exists( $filename ) ) {
- wfDebugLog( 'img_auth', "requested file does not exist: $filename" );
+ wfDebugLog( 'img_auth', "`{$filename}` does not exist" );
wfForbidden();
}
if( is_dir( $filename ) ) {
- wfDebugLog( 'img_auth', "requested file is a directory: $filename" );
+ wfDebugLog( 'img_auth', "`{$filename}` is a directory" );
wfForbidden();
}
-# Write file
-wfDebugLog( 'img_auth', "streaming file: $filename" );
+// Stream the requested file
+wfDebugLog( 'img_auth', "Streaming `{$filename}`" );
wfStreamFile( $filename );
wfLogProfilingData();
+/**
+ * Issue a standard HTTP 403 Forbidden header and a basic
+ * error message, then end the script
+ */
function wfForbidden() {
header( 'HTTP/1.0 403 Forbidden' );
header( 'Content-Type: text/html; charset=utf-8' );
- print
-"<html><body>
-<h1>Access denied</h1>
-<p>You need to log in to access files on this server</p>
-</body></html>";
+ echo <<<END
+<html>
+<body>
+<h1>Access Denied</h1>
+<p>You need to log in to access files on this server.</p>
+</body>
+</html>
+END;
wfLogProfilingData();
- exit;
-}
-
-?>
+ exit();
+} \ No newline at end of file
diff --git a/img_auth.php5 b/img_auth.php5
new file mode 100644
index 00000000..2065de93
--- /dev/null
+++ b/img_auth.php5
@@ -0,0 +1 @@
+<?php require './img_auth.php'; ?>
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index ca129029..7b85ed20 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -1,10 +1,9 @@
<?php
+/**
+ * Handle ajax requests and send them to the proper handler.
+ */
-if( !defined( 'MEDIAWIKI' ) ) {
- die( 1 );
-}
-
-if ( ! $wgUseAjax ) {
+if( !(defined( 'MEDIAWIKI' ) && $wgUseAjax ) ) {
die( 1 );
}
@@ -15,10 +14,16 @@ require_once( 'AjaxFunctions.php' );
* @addtogroup Ajax
*/
class AjaxDispatcher {
- var $mode;
- var $func_name;
- var $args;
+ /** The way the request was made, either a 'get' or a 'post' */
+ private $mode;
+ /** Name of the requested handler */
+ private $func_name;
+
+ /** Arguments passed */
+ private $args;
+
+ /** Load up our object with user supplied data */
function __construct() {
wfProfileIn( __METHOD__ );
@@ -32,24 +37,41 @@ class AjaxDispatcher {
$this->mode = "post";
}
- if ($this->mode == "get") {
+ switch( $this->mode ) {
+
+ case 'get':
$this->func_name = isset( $_GET["rs"] ) ? $_GET["rs"] : '';
if (! empty($_GET["rsargs"])) {
$this->args = $_GET["rsargs"];
} else {
$this->args = array();
}
- } else {
+ break;
+
+ case 'post':
$this->func_name = isset( $_POST["rs"] ) ? $_POST["rs"] : '';
if (! empty($_POST["rsargs"])) {
$this->args = $_POST["rsargs"];
} else {
$this->args = array();
}
+ break;
+
+ default:
+ return;
+ # Or we could throw an exception:
+ #throw new MWException( __METHOD__ . ' called without any data (mode empty).' );
+
}
+
wfProfileOut( __METHOD__ );
}
+ /** Pass the request to our internal function.
+ * BEWARE! Data are passed as they have been supplied by the user,
+ * they should be carefully handled in the function processing the
+ * request.
+ */
function performAction() {
global $wgAjaxExportList, $wgOut;
@@ -62,8 +84,13 @@ class AjaxDispatcher {
wfHttpError( 400, 'Bad Request',
"unknown function " . (string) $this->func_name );
} else {
+ if ( strpos( $this->func_name, '::' ) !== false ) {
+ $func = explode( '::', $this->func_name, 2 );
+ } else {
+ $func = $this->func_name;
+ }
try {
- $result = call_user_func_array($this->func_name, $this->args);
+ $result = call_user_func_array($func, $this->args);
if ( $result === false || $result === NULL ) {
wfHttpError( 500, 'Internal Error',
@@ -93,4 +120,4 @@ class AjaxDispatcher {
}
}
-?>
+
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index 86f853db..4fb76dcc 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -136,22 +136,29 @@ function wfSajaxSearch( $term ) {
/**
* Called for AJAX watch/unwatch requests.
- * @param $pageID Integer ID of the page to be watched/unwatched
+ * @param $pagename Prefixed title string for page to watch/unwatch
* @param $watch String 'w' to watch, 'u' to unwatch
- * @return String '<w#>' or '<u#>' on successful watch or unwatch, respectively, or '<err#>' on error (invalid XML in case we want to add HTML sometime)
+ * @return String '<w#>' or '<u#>' on successful watch or unwatch,
+ * respectively, followed by an HTML message to display in the alert box; or
+ * '<err#>' on error
*/
-function wfAjaxWatch($pageID = "", $watch = "") {
- if(wfReadOnly())
- return '<err#>'; // redirect to action=(un)watch, which will display the database lock message
+function wfAjaxWatch($pagename = "", $watch = "") {
+ if(wfReadOnly()) {
+ // redirect to action=(un)watch, which will display the database lock
+ // message
+ return '<err#>';
+ }
- if(('w' !== $watch && 'u' !== $watch) || !is_numeric($pageID))
+ if('w' !== $watch && 'u' !== $watch) {
return '<err#>';
+ }
$watch = 'w' === $watch;
- $pageID = intval($pageID);
- $title = Title::newFromID($pageID);
- if(!$title)
+ $title = Title::newFromText($pagename);
+ if(!$title) {
+ // Invalid title
return '<err#>';
+ }
$article = new Article($title);
$watching = $title->userIsWatching();
@@ -170,7 +177,10 @@ function wfAjaxWatch($pageID = "", $watch = "") {
$dbw->commit();
}
}
-
- return $watch ? '<w#>' : '<u#>';
+ if( $watch ) {
+ return '<w#>'.wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+ } else {
+ return '<u#>'.wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+ }
}
-?>
+
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index cb4af1b5..8fa08539 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -8,14 +8,27 @@ if( !defined( 'MEDIAWIKI' ) ) {
* @addtogroup Ajax
*/
class AjaxResponse {
- var $mCacheDuration;
- var $mVary;
- var $mDisabled;
- var $mText;
- var $mResponseCode;
- var $mLastModified;
- var $mContentType;
+ /** Number of seconds to get the response cached by a proxy */
+ private $mCacheDuration;
+
+ /** HTTP header Content-Type */
+ private $mContentType;
+
+ /** @todo document */
+ private $mDisabled;
+
+ /** Date for the HTTP header Last-modified */
+ private $mLastModified;
+
+ /** HTTP response code */
+ private $mResponseCode;
+
+ /** HTTP Vary header */
+ private $mVary;
+
+ /** Content of our HTTP response */
+ private $mText;
function __construct( $text = NULL ) {
$this->mCacheDuration = NULL;
@@ -52,18 +65,21 @@ class AjaxResponse {
$this->mDisabled = true;
}
+ /** Add content to the response */
function addText( $text ) {
if ( ! $this->mDisabled && $text ) {
$this->mText .= $text;
}
}
+ /** Output text */
function printText() {
if ( ! $this->mDisabled ) {
print $this->mText;
}
}
+ /** Construct the header and output it */
function sendHeaders() {
global $wgUseSquid, $wgUseESI;
@@ -204,4 +220,4 @@ class AjaxResponse {
return true;
}
}
-?>
+
diff --git a/includes/Article.php b/includes/Article.php
index 0130ceba..7ba55c54 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -37,6 +37,18 @@ class Article {
/**@}}*/
/**
+ * Constants used by internal components to get rollback results
+ */
+ const SUCCESS = 0; // Operation successful
+ const PERM_DENIED = 1; // Permission denied
+ const BLOCKED = 2; // User has been blocked
+ const READONLY = 3; // Wiki is in read-only mode
+ const BAD_TOKEN = 4; // Invalid token specified
+ const BAD_TITLE = 5; // $this is not a valid Article
+ const ALREADY_ROLLED = 6; // Someone else already rolled this back. $from and $summary will be set
+ const ONLY_AUTHOR = 7; // User is the only author of the page
+
+ /**
* Constructor and clear the article
* @param $title Reference to a Title object.
* @param $oldId Integer revision ID, null to fetch from request, zero for current
@@ -257,13 +269,16 @@ class Article {
'page_random',
'page_touched',
'page_latest',
- 'page_len' ) ;
- wfRunHooks( 'ArticlePageDataBefore', array( &$this , &$fields ) ) ;
- $row = $dbr->selectRow( 'page',
+ 'page_len',
+ );
+ wfRunHooks( 'ArticlePageDataBefore', array( &$this, &$fields ) );
+ $row = $dbr->selectRow(
+ 'page',
$fields,
$conditions,
- 'Article::pageData' );
- wfRunHooks( 'ArticlePageDataAfter', array( &$this , &$row ) ) ;
+ __METHOD__
+ );
+ wfRunHooks( 'ArticlePageDataAfter', array( &$this, &$row ) );
return $row ;
}
@@ -500,6 +515,10 @@ class Article {
* @return bool
*/
function isCurrent() {
+ # If no oldid, this is the current version.
+ if ($this->getOldID() == 0)
+ return true;
+
return $this->exists() &&
isset( $this->mRevision ) &&
$this->mRevision->isCurrent();
@@ -604,7 +623,7 @@ class Article {
function view() {
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgUseRCPatrol, $wgParser;
- global $wgUseTrackbacks, $wgNamespaceRobotPolicies;
+ global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
$sk = $wgUser->getSkin();
wfProfileIn( __METHOD__ );
@@ -632,6 +651,8 @@ class Article {
# Discourage indexing of printable versions, but encourage following
if( $wgOut->isPrintable() ) {
$policy = 'noindex,follow';
+ } elseif ( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
+ $policy = $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()];
} elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
# Honour customised robot policies for this namespace
$policy = $wgNamespaceRobotPolicies[$ns];
@@ -678,10 +699,12 @@ class Article {
}
# Should the parser cache be used?
- $pcache = $wgEnableParserCache &&
- intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 &&
- $this->exists() &&
- empty( $oldid );
+ $pcache = $wgEnableParserCache
+ && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
+ && $this->exists()
+ && empty( $oldid )
+ && !$this->mTitle->isCssOrJsPage()
+ && !$this->mTitle->isCssJsSubpage();
wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" );
if ( $wgUser->getOption( 'stubthreshold' ) ) {
wfIncrStats( 'pcache_miss_stub' );
@@ -718,9 +741,12 @@ class Article {
}
$outputDone = false;
- wfRunHooks( 'ArticleViewHeader', array( &$this ) );
+ wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$pcache ) );
if ( $pcache ) {
if ( $wgOut->tryParserCache( $this, $wgUser ) ) {
+ // Ensure that UI elements requiring revision ID have
+ // the correct version information.
+ $wgOut->setRevisionId( $this->mLatest );
$outputDone = true;
}
}
@@ -768,15 +794,24 @@ class Article {
}
if( !$outputDone ) {
$wgOut->setRevisionId( $this->getRevIdFetched() );
- # wrap user css and user js in pre and don't parse
- # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found
- if (
- $ns == NS_USER &&
- preg_match('/\\/[\\w]+\\.(?:css|js)$/', $this->mTitle->getDBkey())
- ) {
- $wgOut->addWikiText( wfMsg('clearyourcache'));
- $wgOut->addHTML( '<pre>'.htmlspecialchars($this->mContent)."\n</pre>" );
- } else if ( $rt = Title::newFromRedirect( $text ) ) {
+
+ // Pages containing custom CSS or JavaScript get special treatment
+ if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
+ $wgOut->addHtml( wfMsgExt( 'clearyourcache', 'parse' ) );
+
+ // Give hooks a chance to customise the output
+ if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
+ // Wrap the whole lot in a <pre> and don't parse
+ $m = array();
+ preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
+ $wgOut->addHtml( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
+ $wgOut->addHtml( htmlspecialchars( $this->mContent ) );
+ $wgOut->addHtml( "\n</pre>\n" );
+ }
+
+ }
+
+ elseif ( $rt = Title::newFromRedirect( $text ) ) {
# Display redirect
$imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
$imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
@@ -869,8 +904,8 @@ class Article {
$rmvtxt = "";
if ($wgUser->isAllowed( 'trackback' )) {
$delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid="
- . $o->tb_id . "&token=" . $wgUser->editToken());
- $rmvtxt = wfMsg('trackbackremove', $delurl);
+ . $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
+ $rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
$tbtext .= wfMsg(strlen($o->tb_ex) ? 'trackbackexcerpt' : 'trackback',
$o->tb_title,
@@ -1146,7 +1181,7 @@ class Article {
if( $section == 'new' ) {
# Inserting a new section
- $subject = $summary ? "== {$summary} ==\n\n" : '';
+ $subject = $summary ? wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n" : '';
$text = strlen( trim( $oldtext ) ) > 0
? "{$oldtext}\n\n{$subject}{$text}"
: "{$subject}{$text}";
@@ -1172,7 +1207,7 @@ class Article {
# If this is a comment, add the summary as headline
if ( $comment && $summary != "" ) {
- $text = "== {$summary} ==\n\n".$text;
+ $text = wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n".$text;
}
$this->doEdit( $text, $summary, $flags );
@@ -1219,7 +1254,10 @@ class Article {
}
}
- $this->doRedirect( $this->isRedirect( $text ), $sectionanchor );
+ $extraq = ''; // Give extensions a chance to modify URL query on update
+ wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraq ) );
+
+ $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraq );
}
return $good;
}
@@ -1359,6 +1397,7 @@ class Article {
$dbw->commit();
}
} else {
+ $revision = null;
// Keep the same revision ID, but do some updates on it
$revisionId = $this->getRevIdFetched();
// Update page_touched, this is usually implicit in the page update
@@ -1426,19 +1465,18 @@ class Article {
# Clear caches
Article::onArticleCreate( $this->mTitle );
- wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text,
- $summary, $flags & EDIT_MINOR,
- null, null, &$flags ) );
+ wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
}
if ( $good && !( $flags & EDIT_DEFER_UPDATES ) ) {
wfDoUpdates();
}
- wfRunHooks( 'ArticleSaveComplete',
- array( &$this, &$wgUser, $text,
- $summary, $flags & EDIT_MINOR,
- null, null, &$flags ) );
+ if ( $good ) {
+ wfRunHooks( 'ArticleSaveComplete', array( &$this, &$wgUser, $text, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+ }
wfProfileOut( __METHOD__ );
return $good;
@@ -1458,12 +1496,14 @@ class Article {
* @param boolean $noRedir Add redirect=no
* @param string $sectionAnchor section to redirect to, including "#"
*/
- function doRedirect( $noRedir = false, $sectionAnchor = '' ) {
+ function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) {
global $wgOut;
if ( $noRedir ) {
$query = 'redirect=no';
+ if( $extraq )
+ $query .= "&$query";
} else {
- $query = '';
+ $query = $extraq;
}
$wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
}
@@ -1690,7 +1730,13 @@ class Article {
}
# Prepare a null revision to be added to the history
- $comment = $wgContLang->ucfirst( wfMsgForContent( $protect ? 'protectedarticle' : 'unprotectedarticle', $this->mTitle->getPrefixedText() ) );
+ $modified = $current != '' && $protect;
+ if ( $protect ) {
+ $comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle';
+ } else {
+ $comment_type = 'unprotectedarticle';
+ }
+ $comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $this->mTitle->getPrefixedText() ) );
foreach( $limit as $action => $restrictions ) {
# Check if the group level required to edit also can protect pages
@@ -1744,7 +1790,7 @@ class Article {
$log = new LogPage( 'protect' );
if( $protect ) {
- $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
+ $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
@@ -1994,11 +2040,10 @@ class Article {
/**
- * Fetch deletion log
+ * Show relevant lines from the deletion log
*/
- function showLogExtract( &$out ) {
- # Show relevant lines from the deletion log:
- $out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ function showLogExtract( $out ) {
+ $out->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . '</h2>' );
$logViewer = new LogViewer(
new LogReader(
new FauxRequest(
@@ -2080,7 +2125,8 @@ class Article {
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len'
+ 'ar_len' => 'rev_len',
+ 'ar_page_id' => 'page_id',
), array(
'page_id' => $id,
'page_id = rev_page'
@@ -2132,60 +2178,52 @@ class Article {
}
/**
- * Revert a modification
- */
- function rollback() {
- global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
-
+ * Roll back the most recent consecutive set of edits to a page
+ * from the same user; fails if there are no eligible edits to
+ * roll back to, e.g. user is the sole contributor
+ *
+ * @param string $fromP - Name of the user whose edits to rollback.
+ * @param string $summary - Custom summary. Set to default summary if empty.
+ * @param string $token - Rollback token.
+ * @param bool $bot - If true, mark all reverted edits as bot.
+ *
+ * @param array $resultDetails contains result-specific dict of additional values
+ * ALREADY_ROLLED : 'current' (rev)
+ * SUCCESS : 'summary' (str), 'current' (rev), 'target' (rev)
+ *
+ * @return self::SUCCESS on succes, self::* on failure
+ */
+ public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) {
+ global $wgUser, $wgUseRCPatrol;
+ $resultDetails = null;
+
if( $wgUser->isAllowed( 'rollback' ) ) {
if( $wgUser->isBlocked() ) {
- $wgOut->blockedPage();
- return;
+ return self::BLOCKED;
}
} else {
- $wgOut->permissionRequired( 'rollback' );
- return;
+ return self::PERM_DENIED;
}
-
+
if ( wfReadOnly() ) {
- $wgOut->readOnlyPage( $this->getContent() );
- return;
- }
- if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ),
- array( $this->mTitle->getPrefixedText(),
- $wgRequest->getVal( 'from' ) ) ) ) {
- $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
- $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
- return;
+ return self::READONLY;
}
- $dbw = wfGetDB( DB_MASTER );
-
- # Enhanced rollback, marks edits rc_bot=1
- $bot = $wgRequest->getBool( 'bot' );
+ if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
+ return self::BAD_TOKEN;
- # Replace all this user's current edits with the next one down
+ $dbw = wfGetDB( DB_MASTER );
# Get the last editor
$current = Revision::newFromTitle( $this->mTitle );
if( is_null( $current ) ) {
# Something wrong... no page?
- $wgOut->addHTML( wfMsg( 'notanarticle' ) );
- return;
+ return self::BAD_TITLE;
}
- $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) );
+ $from = str_replace( '_', ' ', $fromP );
if( $from != $current->getUserText() ) {
- $wgOut->setPageTitle( wfMsg('rollbackfailed') );
- $wgOut->addWikiText( wfMsg( 'alreadyrolled',
- htmlspecialchars( $this->mTitle->getPrefixedText()),
- htmlspecialchars( $from ),
- htmlspecialchars( $current->getUserText() ) ) );
- if( $current->getComment() != '') {
- $wgOut->addHTML(
- wfMsg( 'editcomment',
- $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
- }
- return;
+ $resultDetails = array( 'current' => $current );
+ return self::ALREADY_ROLLED;
}
# Get the last edit not by this guy
@@ -2203,11 +2241,9 @@ class Article {
);
if( $s === false ) {
# Something wrong
- $wgOut->setPageTitle(wfMsg('rollbackfailed'));
- $wgOut->addHTML( wfMsg( 'cantrollback' ) );
- return;
+ return self::ONLY_AUTHOR;
}
-
+
$set = array();
if ( $bot ) {
# Mark all reverted edits as bot
@@ -2220,27 +2256,100 @@ class Article {
if ( $set ) {
$dbw->update( 'recentchanges', $set,
- array( /* WHERE */
- 'rc_cur_id' => $current->getPage(),
- 'rc_user_text' => $current->getUserText(),
- "rc_timestamp > '{$s->rev_timestamp}'",
- ), __METHOD__
- );
+ array( /* WHERE */
+ 'rc_cur_id' => $current->getPage(),
+ 'rc_user_text' => $current->getUserText(),
+ "rc_timestamp > '{$s->rev_timestamp}'",
+ ), __METHOD__
+ );
}
# Get the edit summary
$target = Revision::newFromId( $s->rev_id );
- $newComment = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
- $newComment = $wgRequest->getText( 'summary', $newComment );
+ if( empty( $summary ) )
+ $summary = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
+
+ # Save
+ $flags = EDIT_UPDATE | EDIT_MINOR;
+ if( $bot )
+ $flags |= EDIT_FORCE_BOT;
+ $this->doEdit( $target->getText(), $summary, $flags );
+
+ $resultDetails = array(
+ 'summary' => $summary,
+ 'current' => $current,
+ 'target' => $target,
+ );
+ return self::SUCCESS;
+ }
- # Save it!
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
- $wgOut->addHTML( '<h2>' . htmlspecialchars( $newComment ) . "</h2>\n<hr />\n" );
+ /**
+ * User interface for rollback operations
+ */
+ function rollback() {
+ global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
- $this->updateArticle( $target->getText(), $newComment, 1, $this->mTitle->userIsWatching(), $bot );
+ $details = null;
+ $result = $this->doRollback(
+ $wgRequest->getVal( 'from' ),
+ $wgRequest->getText( 'summary' ),
+ $wgRequest->getVal( 'token' ),
+ $wgRequest->getBool( 'bot' ),
+ $details
+ );
+
+ switch( $result ) {
+ case self::BLOCKED:
+ $wgOut->blockedPage();
+ break;
+ case self::PERM_DENIED:
+ $wgOut->permissionRequired( 'rollback' );
+ break;
+ case self::READONLY:
+ $wgOut->readOnlyPage( $this->getContent() );
+ break;
+ case self::BAD_TOKEN:
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
+ break;
+ case self::BAD_TITLE:
+ $wgOut->addHtml( wfMsg( 'notanarticle' ) );
+ break;
+ case self::ALREADY_ROLLED:
+ $current = $details['current'];
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addWikiText(
+ wfMsg( 'alreadyrolled',
+ htmlspecialchars( $this->mTitle->getPrefixedText() ),
+ htmlspecialchars( $wgRequest->getVal( 'from' ) ),
+ htmlspecialchars( $current->getUserText() )
+ )
+ );
+ if( $current->getComment() != '' ) {
+ $wgOut->addHtml( wfMsg( 'editcomment',
+ $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
+ }
+ break;
+ case self::ONLY_AUTHOR:
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addHtml( wfMsg( 'cantrollback' ) );
+ break;
+ case self::SUCCESS:
+ $current = $details['current'];
+ $target = $details['target'];
+ $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
+ . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
+ $new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
+ . $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
+ $wgOut->addHtml( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
+ $wgOut->returnToMain( false, $this->mTitle );
+ break;
+ default:
+ throw new MWException( __METHOD__ . ": Unknown return value `{$result}`" );
+ }
- $wgOut->returnToMain( false );
}
@@ -2268,7 +2377,7 @@ class Article {
/**
* Do standard deferred updates after page edit.
* Update links tables, site stats, search index and message cache.
- * Every 1000th edit, prune the recent changes table.
+ * Every 100th edit, prune the recent changes table.
*
* @private
* @param $text New text of the article
@@ -2296,12 +2405,11 @@ class Article {
$u = new LinksUpdate( $this->mTitle, $poutput );
$u->doUpdate();
- if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
- wfSeedRandom();
- if ( 0 == mt_rand( 0, 999 ) ) {
- # Periodically flush old entries from the recentchanges table.
+ if( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
+ if ( 0 == mt_rand( 0, 99 ) ) {
+ // Flush old entries from the `recentchanges` table; we do this on
+ // random requests so as to avoid an increase in writes for no good reason
global $wgRCMaxAge;
-
$dbw = wfGetDB( DB_MASTER );
$cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
$recentchanges = $dbw->tableName( 'recentchanges' );
@@ -2410,7 +2518,12 @@ class Article {
$userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
. $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
- $r = "\n\t\t\t\t<div id=\"mw-revision-info\">" . wfMsg( 'revision-info', $td, $userlinks ) . "</div>\n" .
+ $m = wfMsg( 'revision-info-current' );
+ $infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
+ ? 'revision-info-current'
+ : 'revision-info';
+
+ $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsg( $infomsg, $td, $userlinks ) . "</div>\n" .
"\n\t\t\t\t<div id=\"mw-revision-nav\">" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
@@ -2701,16 +2814,22 @@ class Article {
$page = $this->mTitle->getSubjectPage();
$wgOut->setPagetitle( $page->getPrefixedText() );
- $wgOut->setSubtitle( wfMsg( 'infosubtitle' ));
-
- # first, see if the page exists at all.
- $exists = $page->getArticleId() != 0;
- if( !$exists ) {
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- $wgOut->addHTML(wfMsgWeirdKey ( $this->mTitle->getText() ) );
+ $wgOut->setPageTitleActionText( wfMsg( 'info_short' ) );
+ $wgOut->setSubtitle( wfMsg( 'infosubtitle' ) );
+
+ if( !$this->mTitle->exists() ) {
+ $wgOut->addHtml( '<div class="noarticletext">' );
+ if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ // This doesn't quite make sense; the user is asking for
+ // information about the _page_, not the message... -- RC
+ $wgOut->addHtml( htmlspecialchars( wfMsgWeirdKey( $this->mTitle->getText() ) ) );
} else {
- $wgOut->addHTML(wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' ) );
+ $msg = $wgUser->isLoggedIn()
+ ? 'noarticletext'
+ : 'noarticletextanon';
+ $wgOut->addHtml( wfMsgExt( $msg, 'parse' ) );
}
+ $wgOut->addHtml( '</div>' );
} else {
$dbr = wfGetDB( DB_SLAVE );
$wl_clause = array(
@@ -2959,5 +3078,3 @@ class Article {
}
}
-
-?>
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index 9395032f..87a79438 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -219,9 +219,10 @@ class AuthPlugin {
* forget the & on your function declaration.
*
* @param $user User object.
+ * @param $autocreate bool True if user is being autocreated on login
* @public
*/
- function initUser( &$user ) {
+ function initUser( $user, $autocreate=false ) {
# Override this to do something.
}
@@ -234,4 +235,4 @@ class AuthPlugin {
}
}
-?>
+
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 3d7bcdf3..25c728cd 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -22,6 +22,7 @@ function __autoload($className) {
'TurckBagOStuff' => 'includes/BagOStuff.php',
'APCBagOStuff' => 'includes/BagOStuff.php',
'eAccelBagOStuff' => 'includes/BagOStuff.php',
+ 'XCacheBagOStuff' => 'includes/BagOStuff.php',
'DBABagOStuff' => 'includes/BagOStuff.php',
'Block' => 'includes/Block.php',
'HTMLFileCache' => 'includes/HTMLFileCache.php',
@@ -93,17 +94,20 @@ function __autoload($className) {
'HistoryBlobStub' => 'includes/HistoryBlob.php',
'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php',
- 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
'Http' => 'includes/HttpFunctions.php',
- 'Image' => 'includes/Image.php',
- 'ArchivedFile' => 'includes/Image.php',
'IP' => 'includes/IP.php',
'ThumbnailImage' => 'includes/Image.php',
'ImageGallery' => 'includes/ImageGallery.php',
'ImagePage' => 'includes/ImagePage.php',
'ImageHistoryList' => 'includes/ImagePage.php',
'ImageRemote' => 'includes/ImageRemote.php',
+ 'FileDeleteForm' => 'includes/FileDeleteForm.php',
+ 'FileRevertForm' => 'includes/FileRevertForm.php',
'Job' => 'includes/JobQueue.php',
+ 'EmaillingJob' => 'includes/EmaillingJob.php',
+ 'EnotifNotifyJob' => 'includes/EnotifNotifyJob.php',
+ 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
+ 'RefreshLinksJob' => 'includes/RefreshLinksJob.php',
'Licenses' => 'includes/Licenses.php',
'License' => 'includes/Licenses.php',
'LinkBatch' => 'includes/LinkBatch.php',
@@ -115,6 +119,7 @@ function __autoload($className) {
'LogPage' => 'includes/LogPage.php',
'MacBinary' => 'includes/MacBinary.php',
'MagicWord' => 'includes/MagicWord.php',
+ 'MagicWordArray' => 'includes/MagicWord.php',
'MathRenderer' => 'includes/Math.php',
'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
'ThumbnailImage' => 'includes/MediaTransformOutput.php',
@@ -191,6 +196,7 @@ function __autoload($className) {
'MostimagesPage' => 'includes/SpecialMostimages.php',
'MostlinkedPage' => 'includes/SpecialMostlinked.php',
'MostlinkedCategoriesPage' => 'includes/SpecialMostlinkedcategories.php',
+ 'SpecialMostlinkedtemplates' => 'includes/SpecialMostlinkedtemplates.php',
'MostrevisionsPage' => 'includes/SpecialMostrevisions.php',
'FewestrevisionsPage' => 'includes/SpecialFewestrevisions.php',
'MovePageForm' => 'includes/SpecialMovepage.php',
@@ -209,6 +215,7 @@ function __autoload($className) {
'ShortPagesPage' => 'includes/SpecialShortpages.php',
'UncategorizedCategoriesPage' => 'includes/SpecialUncategorizedcategories.php',
'UncategorizedPagesPage' => 'includes/SpecialUncategorizedpages.php',
+ 'UncategorizedTemplatesPage' => 'includes/SpecialUncategorizedtemplates.php',
'PageArchive' => 'includes/SpecialUndelete.php',
'UndeleteForm' => 'includes/SpecialUndelete.php',
'DBUnlockForm' => 'includes/SpecialUnlockdb.php',
@@ -244,10 +251,28 @@ function __autoload($className) {
'WikiError' => 'includes/WikiError.php',
'WikiErrorMsg' => 'includes/WikiError.php',
'WikiXmlError' => 'includes/WikiError.php',
- 'XCacheBagOStuff' => 'includes/BagOStuff.php',
'Xml' => 'includes/Xml.php',
'ZhClient' => 'includes/ZhClient.php',
'memcached' => 'includes/memcached-client.php',
+ 'EmaillingJob' => 'includes/JobQueue.php',
+ 'WatchlistEditor' => 'includes/WatchlistEditor.php',
+
+ # filerepo
+ 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
+ 'File' => 'includes/filerepo/File.php',
+ 'FileRepo' => 'includes/filerepo/FileRepo.php',
+ 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
+ 'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php',
+ 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
+ 'FSRepo' => 'includes/filerepo/FSRepo.php',
+ 'Image' => 'includes/filerepo/LocalFile.php',
+ 'LocalFile' => 'includes/filerepo/LocalFile.php',
+ 'LocalFileDeleteBatch' => 'includes/filerepo/LocalFile.php',
+ 'LocalFileRestoreBatch' => 'includes/filerepo/LocalFile.php',
+ 'LocalRepo' => 'includes/filerepo/LocalRepo.php',
+ 'OldLocalFile' => 'includes/filerepo/OldLocalFile.php',
+ 'RepoGroup' => 'includes/filerepo/RepoGroup.php',
+ 'UnregisteredLocalFile' => 'includes/filerepo/UnregisteredLocalFile.php',
# Media
'BitmapHandler' => 'includes/media/Bitmap.php',
@@ -287,14 +312,27 @@ function __autoload($className) {
'ApiPageSet' => 'includes/api/ApiPageSet.php',
'ApiQuery' => 'includes/api/ApiQuery.php',
'ApiQueryAllpages' => 'includes/api/ApiQueryAllpages.php',
+ 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
+ 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.php',
'ApiQueryBase' => 'includes/api/ApiQueryBase.php',
+ 'ApiQueryGeneratorBase' => 'includes/api/ApiQueryBase.php',
'ApiQueryBacklinks' => 'includes/api/ApiQueryBacklinks.php',
+ 'ApiQueryCategories' => 'includes/api/ApiQueryCategories.php',
+ 'ApiQueryCategoryMembers' => 'includes/api/ApiQueryCategoryMembers.php',
'ApiQueryContributions' => 'includes/api/ApiQueryUserContributions.php',
+ 'ApiQueryExternalLinks' => 'includes/api/ApiQueryExternalLinks.php',
+ 'ApiQueryExtLinksUsage' => 'includes/api/ApiQueryExtLinksUsage.php',
+ 'ApiQueryImages' => 'includes/api/ApiQueryImages.php',
+ 'ApiQueryImageInfo' => 'includes/api/ApiQueryImageInfo.php',
'ApiQueryInfo' => 'includes/api/ApiQueryInfo.php',
+ 'ApiQueryLangLinks' => 'includes/api/ApiQueryLangLinks.php',
+ 'ApiQueryLinks' => 'includes/api/ApiQueryLinks.php',
'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php',
'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
+ 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
+ 'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php',
'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
'ApiResult' => 'includes/api/ApiResult.php',
);
@@ -346,6 +384,4 @@ function wfLoadAllExtensions() {
require( $file );
}
}
-}
-
-?>
+} \ No newline at end of file
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index e8abb74e..a40d020e 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -172,7 +172,7 @@ class HashBagOStuff extends BagOStuff {
*/
var $bag;
- function HashBagOStuff() {
+ function __construct() {
$this->bag = array();
}
@@ -222,7 +222,7 @@ abstract class SqlBagOStuff extends BagOStuff {
var $table;
var $lastexpireall = 0;
- function SqlBagOStuff($tablename = 'objectcache') {
+ function __construct($tablename = 'objectcache') {
$this->table = $tablename;
}
@@ -238,8 +238,8 @@ abstract class SqlBagOStuff extends BagOStuff {
}
if($row=$this->_fetchobject($res)) {
$this->_debug("get: retrieved data; exp time is " . $row->exptime);
- if ( $row->exptime != $this->_maxdatetime() &&
- wfTimestamp( TS_UNIX, $row->exptime ) < time() )
+ if ( $row->exptime != $this->_maxdatetime() &&
+ wfTimestamp( TS_UNIX, $row->exptime ) < time() )
{
$this->_debug("get: key has expired, deleting");
$this->delete($key);
@@ -253,6 +253,9 @@ abstract class SqlBagOStuff extends BagOStuff {
}
function set($key,$value,$exptime=0) {
+ if ( wfReadOnly() ) {
+ return false;
+ }
$exptime = intval($exptime);
if($exptime < 0) $exptime = 0;
if($exptime == 0) {
@@ -272,6 +275,9 @@ abstract class SqlBagOStuff extends BagOStuff {
}
function delete($key,$time=0) {
+ if ( wfReadOnly() ) {
+ return false;
+ }
$this->_query(
"DELETE FROM $0 WHERE keyname='$1'", $key );
return true; /* ? */
@@ -339,12 +345,18 @@ abstract class SqlBagOStuff extends BagOStuff {
function expireall() {
/* Remove any items that have expired */
+ if ( wfReadOnly() ) {
+ return false;
+ }
$now = $this->_fromunixtime( time() );
$this->_query( "DELETE FROM $0 WHERE exptime < '$now'" );
}
function deleteall() {
/* Clear *all* items from cache table */
+ if ( wfReadOnly() ) {
+ return false;
+ }
$this->_query( "DELETE FROM $0" );
}
@@ -495,55 +507,22 @@ class TurckBagOStuff extends BagOStuff {
*
*/
class APCBagOStuff extends BagOStuff {
- public function get( $key ) {
- return apc_fetch($key);
- }
-
- public function set( $key, $value, $exptime = 0 ) {
- return apc_store($key, $value, $exptime);
- }
-
- public function delete( $key, $time = 0 ) {
- return apc_delete( $key );
- }
-
- public function add($key, $value, $exptime=0) {
- return apc_add( $key, $value, $exptime );
- }
-}
-
-
-/**
- * Wrapper for XCache object caching functions
- *
- */
-class XCacheBagOStuff extends BagOStuff {
- public function get( $key ) {
- return (xcache_isset($key) ? unserialize(xcache_get($key)) : false);
- }
-
- public function set( $key, $value, $exptime = 0 ) {
- return xcache_set($key, serialize($value), $exptime);
- }
-
- public function delete( $key, $time = 0 ) {
- return xcache_unset( $key );
- }
-
- public function incr($key, $value=1) {
- return (xcache_isset($key) && xcache_inc($key. $value));
- }
-
- public function decr($key, $value=1) {
- return (xcache_isset($key) && xcache_dec($key. $value));
+ function get($key) {
+ $val = apc_fetch($key);
+ if ( is_string( $val ) ) {
+ $val = unserialize( $val );
+ }
+ return $val;
}
-
- public function add($key, $value, $exptime=0) {
- return (!xcache_isset($key) && $this->set( $key, $value, $exptime ));
+
+ function set($key, $value, $exptime=0) {
+ apc_store($key, serialize($value), $exptime);
+ return true;
}
-
- public function replace($key, $value, $exptime=0) {
- return (xcache_isset($key) && $this->set( $key, $value, $exptime ));
+
+ function delete($key, $time=0) {
+ apc_delete($key);
+ return true;
}
}
@@ -586,11 +565,57 @@ class eAccelBagOStuff extends BagOStuff {
}
/**
+ * Wrapper for XCache object caching functions; identical interface
+ * to the APC wrapper
+ */
+class XCacheBagOStuff extends BagOStuff {
+
+ /**
+ * Get a value from the XCache object cache
+ *
+ * @param string $key Cache key
+ * @return mixed
+ */
+ public function get( $key ) {
+ $val = xcache_get( $key );
+ if( is_string( $val ) )
+ $val = unserialize( $val );
+ return $val;
+ }
+
+ /**
+ * Store a value in the XCache object cache
+ *
+ * @param string $key Cache key
+ * @param mixed $value Object to store
+ * @param int $expire Expiration time
+ * @return bool
+ */
+ public function set( $key, $value, $expire = 0 ) {
+ xcache_set( $key, serialize( $value ), $expire );
+ return true;
+ }
+
+ /**
+ * Remove a value from the XCache object cache
+ *
+ * @param string $key Cache key
+ * @param int $time Not used in this implementation
+ * @return bool
+ */
+ public function delete( $key, $time = 0 ) {
+ xcache_unset( $key );
+ return true;
+ }
+
+}
+
+/**
* @todo document
*/
class DBABagOStuff extends BagOStuff {
var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
-
+
function __construct( $handler = 'db3', $dir = false ) {
if ( $dir === false ) {
global $wgTmpDirectory;
@@ -617,7 +642,7 @@ class DBABagOStuff extends BagOStuff {
if ( !is_string( $blob ) ) {
return array( null, 0 );
} else {
- return array(
+ return array(
unserialize( substr( $blob, 11 ) ),
intval( substr( $blob, 0, 10 ) )
);
@@ -684,6 +709,7 @@ class DBABagOStuff extends BagOStuff {
function delete( $key, $time = 0 ) {
wfProfileIn( __METHOD__ );
+ wfDebug( __METHOD__."($key)\n" );
$handle = $this->getWriter();
if ( !$handle ) {
return false;
@@ -718,5 +744,5 @@ class DBABagOStuff extends BagOStuff {
return $ret;
}
}
+
-?>
diff --git a/includes/Block.php b/includes/Block.php
index 94bfa5b4..3688d7cf 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -15,7 +15,8 @@
class Block
{
/* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
- $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName;
+ $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName,
+ $mBlockEmail;
/* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster, $mByName;
const EB_KEEP_EXPIRED = 1;
@@ -24,7 +25,7 @@ class Block
function __construct( $address = '', $user = 0, $by = 0, $reason = '',
$timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
- $hideName = 0 )
+ $hideName = 0, $blockEmail = 0 )
{
$this->mId = 0;
# Expand valid IPv6 addresses
@@ -40,7 +41,7 @@ class Block
$this->mExpiry = self::decodeExpiry( $expiry );
$this->mEnableAutoblock = $enableAutoblock;
$this->mHideName = $hideName;
-
+ $this->mBlockEmail = $blockEmail;
$this->mForUpdate = false;
$this->mFromMaster = false;
$this->mByName = false;
@@ -76,7 +77,7 @@ class Block
$this->mAddress = $this->mReason = $this->mTimestamp = '';
$this->mId = $this->mAnonOnly = $this->mCreateAccount =
$this->mEnableAutoblock = $this->mAuto = $this->mUser =
- $this->mBy = $this->mHideName = 0;
+ $this->mBy = $this->mHideName = $this->mBlockEmail = 0;
$this->mByName = false;
}
@@ -262,6 +263,7 @@ class Block
$this->mAnonOnly = $row->ipb_anon_only;
$this->mCreateAccount = $row->ipb_create_account;
$this->mEnableAutoblock = $row->ipb_enable_autoblock;
+ $this->mBlockEmail = $row->ipb_block_email;
$this->mHideName = $row->ipb_deleted;
$this->mId = $row->ipb_id;
$this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
@@ -371,6 +373,7 @@ class Block
# Unset ipb_enable_autoblock for IP blocks, makes no sense
if ( !$this->mUser ) {
$this->mEnableAutoblock = 0;
+ $this->mBlockEmail = 0; //Same goes for email...
}
# Don't collide with expired blocks
@@ -392,7 +395,8 @@ class Block
'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
'ipb_range_start' => $this->mRangeStart,
'ipb_range_end' => $this->mRangeEnd,
- 'ipb_deleted' => $this->mHideName
+ 'ipb_deleted' => $this->mHideName,
+ 'ipb_block_email' => $this->mBlockEmail
), 'Block::insert', array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
@@ -438,9 +442,6 @@ class Block
* @return bool Whether or not an autoblock was inserted.
*/
function doAutoblock( $autoblockip, $justInserted = false ) {
- # Check if this IP address is already blocked
- $dbw = wfGetDB( DB_MASTER );
-
# If autoblocks are disabled, go away.
if ( !$this->mEnableAutoblock ) {
return;
@@ -627,11 +628,11 @@ class Block
/**
* Decode expiry which has come from the DB
*/
- static function decodeExpiry( $expiry ) {
+ static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
if ( $expiry == '' || $expiry == Block::infinity() ) {
return Block::infinity();
} else {
- return wfTimestamp( TS_MW, $expiry );
+ return wfTimestamp( $timestampType, $expiry );
}
}
@@ -698,4 +699,4 @@ class Block
}
}
-?>
+
diff --git a/includes/CacheDependency.php b/includes/CacheDependency.php
index bb5c5437..1d48c383 100644
--- a/includes/CacheDependency.php
+++ b/includes/CacheDependency.php
@@ -344,4 +344,4 @@ class ConstantDependency extends CacheDependency {
}
}
-?>
+
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 356f9ea2..76a388a6 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -90,6 +90,11 @@ class CategoryViewer {
$this->getImageSection() .
$this->getCategoryBottom();
+ // Give a proper message if category is empty
+ if ( $r == '' ) {
+ $r = wfMsgExt( 'category-empty', array( 'parse' ) );
+ }
+
wfProfileOut( __METHOD__ );
return $r;
}
@@ -101,7 +106,7 @@ class CategoryViewer {
$this->children_start_char = array();
if( $this->showGallery ) {
$this->gallery = new ImageGallery();
- $this->gallery->setParsing();
+ $this->gallery->setHideBadImages();
}
}
@@ -147,7 +152,7 @@ class CategoryViewer {
/**
* Add a page in the image namespace
*/
- function addImage( $title, $sortkey, $pageLength ) {
+ function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) {
if ( $this->showGallery ) {
$image = new Image( $title );
if( $this->flip ) {
@@ -156,18 +161,18 @@ class CategoryViewer {
$this->gallery->add( $image );
}
} else {
- $this->addPage( $title, $sortkey, $pageLength );
+ $this->addPage( $title, $sortkey, $pageLength, $isRedirect );
}
}
/**
* Add a miscellaneous page
*/
- function addPage( $title, $sortkey, $pageLength ) {
+ function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) {
global $wgContLang;
- $this->articles[] = $this->getSkin()->makeSizeLinkObj(
- $pageLength, $title, $wgContLang->convert( $title->getPrefixedText() )
- );
+ $this->articles[] = $isRedirect
+ ? '<span class="redirect-in-category">' . $this->getSkin()->makeKnownLinkObj( $title ) . '</span>'
+ : $this->getSkin()->makeSizeLinkObj( $pageLength, $title );
$this->articles_start_char[] = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
}
@@ -194,7 +199,7 @@ class CategoryViewer {
}
$res = $dbr->select(
array( 'page', 'categorylinks' ),
- array( 'page_title', 'page_namespace', 'page_len', 'cl_sortkey' ),
+ array( 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey' ),
array( $pageCondition,
'cl_from = page_id',
'cl_to' => $this->title->getDBKey()),
@@ -219,23 +224,25 @@ class CategoryViewer {
if( $title->getNamespace() == NS_CATEGORY ) {
$this->addSubcategory( $title, $x->cl_sortkey, $x->page_len );
- } elseif( $title->getNamespace() == NS_IMAGE ) {
- $this->addImage( $title, $x->cl_sortkey, $x->page_len );
+ } elseif( $this->showGallery && $title->getNamespace() == NS_IMAGE ) {
+ $this->addImage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
} else {
- $this->addPage( $title, $x->cl_sortkey, $x->page_len );
+ $this->addPage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
}
}
$dbr->freeResult( $res );
}
function getCategoryTop() {
- $r = "<br style=\"clear:both;\"/>\n";
+ $r = '';
if( $this->until != '' ) {
$r .= $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit );
} elseif( $this->nextPage != '' || $this->from != '' ) {
$r .= $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit );
}
- return $r;
+ return $r == ''
+ ? $r
+ : "<br style=\"clear:both;\"/>\n" . $r;
}
function getSubcategorySection() {
@@ -352,7 +359,7 @@ class CategoryViewer {
}
$cont_msg = "";
if ( $articles_start_char[$index] == $prev_start_char )
- $cont_msg = wfMsgHtml('listingcontinuesabbrev');
+ $cont_msg = ' ' . wfMsgHtml( 'listingcontinuesabbrev' );
$r .= "<h3>" . htmlspecialchars( $articles_start_char[$index] ) . "$cont_msg</h3>\n<ul>";
$prev_start_char = $articles_start_char[$index];
}
@@ -423,4 +430,4 @@ class CategoryViewer {
}
-?>
+
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index 7faae935..b94dcf5e 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -189,4 +189,4 @@ class Categoryfinder {
} # END OF CLASS "Categoryfinder"
-?>
+
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index 751e1226..8d0f9508 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -9,7 +9,7 @@ class RCCacheEntry extends RecentChange
var $curlink , $difflink, $lastlink , $usertalklink , $versionlink ;
var $userlink, $timestamp, $watched;
- function newFromParent( $rc ) {
+ static function newFromParent( $rc ) {
$rc2 = new RCCacheEntry;
$rc2->mAttribs = $rc->mAttribs;
$rc2->mExtra = $rc->mExtra;
@@ -171,7 +171,8 @@ class ChangesList {
? 'rcid='.$rc->mAttribs['rc_id']
: '';
$articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
- if($watched) $articlelink = '<strong>'.$articlelink.'</strong>';
+ if( $watched )
+ $articlelink = "<strong class=\"mw-watched\">{$articlelink}</strong>";
global $wgContLang;
$articlelink .= $wgContLang->getDirMark();
@@ -204,7 +205,7 @@ class ChangesList {
*/
function usePatrol() {
global $wgUseRCPatrol, $wgUser;
- return( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) );
+ return( $wgUseRCPatrol && ($wgUser->isAllowed('patrol') || $wgUser->isAllowed('patrolmarks')) );
}
/**
@@ -568,7 +569,7 @@ class EnhancedChangesList extends ChangesList {
function maybeWatchedLink( $link, $watched=false ) {
if( $watched ) {
// FIXME: css style might be more appropriate
- return '<strong>' . $link . '</strong>';
+ return '<strong class="mw-watched">' . $link . '</strong>';
} else {
return $link;
}
@@ -703,4 +704,4 @@ class EnhancedChangesList extends ChangesList {
}
}
-?>
+
diff --git a/includes/CoreParserFunctions.php b/includes/CoreParserFunctions.php
index 72ceb45f..a5f45016 100644
--- a/includes/CoreParserFunctions.php
+++ b/includes/CoreParserFunctions.php
@@ -97,15 +97,20 @@ class CoreParserFunctions {
return $parser->getFunctionLang()->convertPlural( $text, $arg0, $arg1, $arg2, $arg3, $arg4 );
}
- static function displaytitle( $parser, $param = '' ) {
- $parserOptions = new ParserOptions;
- $local_parser = clone $parser;
- $t2 = $local_parser->parse ( $param, $parser->mTitle, $parserOptions, false );
- $parser->mOutput->mHTMLtitle = $t2->GetText();
-
- # Add subtitle
- $t = $parser->mTitle->getPrefixedText();
- $parser->mOutput->mSubtitle .= wfMsg('displaytitle', $t);
+ /**
+ * Override the title of the page when viewed,
+ * provided we've been given a title which
+ * will normalise to the canonical title
+ *
+ * @param Parser $parser Parent parser
+ * @param string $text Desired title text
+ * @return string
+ */
+ static function displaytitle( $parser, $text = '' ) {
+ $text = trim( Sanitizer::decodeCharReferences( $text ) );
+ $title = Title::newFromText( $text );
+ if( $title instanceof Title && $title->getFragment() == '' && $title->equals( $parser->mTitle ) )
+ $parser->mOutput->setDisplayTitle( $text );
return '';
}
@@ -156,7 +161,7 @@ class CoreParserFunctions {
static function pad( $string = '', $length = 0, $char = 0, $direction = STR_PAD_RIGHT ) {
$length = min( max( $length, 0 ), 500 );
$char = substr( $char, 0, 1 );
- return ( $string && (int)$length > 0 && strlen( trim( (string)$char ) ) > 0 )
+ return ( $string !== '' && (int)$length > 0 && strlen( trim( (string)$char ) ) > 0 )
? str_pad( $string, $length, (string)$char, $direction )
: $string;
}
@@ -193,4 +198,4 @@ class CoreParserFunctions {
return '';
}
}
-?>
+
diff --git a/includes/Credits.php b/includes/Credits.php
index 87382a86..580a8d92 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -185,4 +185,4 @@ function creditOthersLink($article) {
return $skin->makeKnownLink($article->mTitle->getPrefixedText(), wfMsg('others'), 'action=credits');
}
-?>
+
diff --git a/includes/Database.php b/includes/Database.php
index 3fd6ad16..4f8c7d5e 100644
--- a/includes/Database.php
+++ b/includes/Database.php
@@ -214,7 +214,7 @@ border=\"0\" ALT=\"Google\"></A>
$cache = new HTMLFileCache( $t );
if( $cache->isFileCached() ) {
- // FIXME: $msg is not defined on the next line.
+ // @todo, FIXME: $msg is not defined on the next line.
$msg = '<p style="color: red"><b>'.$msg."<br />\n" .
$cachederror . "</b></p>\n";
@@ -441,6 +441,14 @@ class Database {
}
/**
+ * Returns true if this database does an implicit order by when the column has an index
+ * For example: SELECT page_title FROM page LIMIT 1
+ */
+ function implicitOrderby() {
+ return true;
+ }
+
+ /**
* Returns true if this database can do a native search on IP columns
* e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
*/
@@ -448,6 +456,13 @@ class Database {
return false;
}
+ /**
+ * Returns true if this database can use functional indexes
+ */
+ function functionalIndexes() {
+ return false;
+ }
+
/**#@+
* Get function
*/
@@ -582,7 +597,7 @@ class Database {
@/**/$this->mConn = mysql_connect( $server, $user, $password, true );
}
if ($this->mConn === false) {
- $iplus = $i + 1;
+ #$iplus = $i + 1;
#wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
}
}
@@ -678,9 +693,12 @@ class Database {
* Usually aborts on failure. If errors are explicitly ignored, returns success.
*
* @param $sql String: SQL query
- * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST comment (you can use __METHOD__ or add some extra info)
- * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors... maybe best to catch the exception instead?
- * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure if $tempIgnore set
+ * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST
+ * comment (you can use __METHOD__ or add some extra info)
+ * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors...
+ * maybe best to catch the exception instead?
+ * @return true for a successful write query, ResultWrapper object for a successful read query,
+ * or false on failure if $tempIgnore set
* @throws DBQueryError Thrown when the database returns an error of any kind
*/
public function query( $sql, $fname = '', $tempIgnore = false ) {
@@ -765,7 +783,7 @@ class Database {
wfProfileOut( $queryProf );
wfProfileOut( $totalProf );
}
- return $ret;
+ return $this->resultObject( $ret );
}
/**
@@ -909,6 +927,9 @@ class Database {
* Free a result object
*/
function freeResult( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
if ( !@/**/mysql_free_result( $res ) ) {
throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
}
@@ -924,6 +945,9 @@ class Database {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@/**/$row = mysql_fetch_object( $res );
if( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
@@ -940,6 +964,9 @@ class Database {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@/**/$row = mysql_fetch_array( $res );
if ( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
@@ -951,6 +978,9 @@ class Database {
* Get the number of rows in a result object
*/
function numRows( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@/**/$n = mysql_num_rows( $res );
if( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) );
@@ -962,14 +992,24 @@ class Database {
* Get the number of fields in a result object
* See documentation for mysql_num_fields()
*/
- function numFields( $res ) { return mysql_num_fields( $res ); }
+ function numFields( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return mysql_num_fields( $res );
+ }
/**
* Get a field name in a result object
* See documentation for mysql_field_name():
* http://www.php.net/mysql_field_name
*/
- function fieldName( $res, $n ) { return mysql_field_name( $res, $n ); }
+ function fieldName( $res, $n ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return mysql_field_name( $res, $n );
+ }
/**
* Get the inserted value of an auto-increment row
@@ -987,7 +1027,12 @@ class Database {
* Change the position of the cursor in a result object
* See mysql_data_seek()
*/
- function dataSeek( $res, $row ) { return mysql_data_seek( $res, $row ); }
+ function dataSeek( $res, $row ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return mysql_data_seek( $res, $row );
+ }
/**
* Get the last error number
@@ -1091,6 +1136,7 @@ class Database {
}
if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
+ if ( isset( $options['HAVING'] ) ) $preLimitTail .= " HAVING {$options['HAVING']}";
if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
//if (isset($options['LIMIT'])) {
@@ -1101,7 +1147,7 @@ class Database {
if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $postLimitTail .= ' FOR UPDATE';
if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $postLimitTail .= ' LOCK IN SHARE MODE';
- if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
+ if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
# Various MySQL extensions
if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) $startOpts .= ' /*! STRAIGHT_JOIN */';
@@ -1175,7 +1221,6 @@ class Database {
if (isset($options['EXPLAIN'])) {
$sql = 'EXPLAIN ' . $sql;
}
-
return $this->query( $sql, $fname );
}
@@ -1352,9 +1397,9 @@ class Database {
function fieldInfo( $table, $field ) {
$table = $this->tableName( $table );
$res = $this->query( "SELECT * FROM $table LIMIT 1" );
- $n = mysql_num_fields( $res );
+ $n = mysql_num_fields( $res->result );
for( $i = 0; $i < $n; $i++ ) {
- $meta = mysql_fetch_field( $res, $i );
+ $meta = mysql_fetch_field( $res->result, $i );
if( $field == $meta->name ) {
return new MySQLField($meta);
}
@@ -1366,6 +1411,9 @@ class Database {
* mysql_field_type() wrapper
*/
function fieldType( $res, $index ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
return mysql_field_type( $res, $index );
}
@@ -1455,6 +1503,7 @@ class Database {
* (for the log)
* @param array $options An array of UPDATE options, can be one or
* more of IGNORE, LOW_PRIORITY
+ * @return bool
*/
function update( $table, $values, $conds, $fname = 'Database::update', $options = array() ) {
$table = $this->tableName( $table );
@@ -1463,7 +1512,7 @@ class Database {
if ( $conds != '*' ) {
$sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
}
- $this->query( $sql, $fname );
+ return $this->query( $sql, $fname );
}
/**
@@ -1498,8 +1547,15 @@ class Database {
$list .= "($value)";
} elseif ( ($mode == LIST_SET) && is_numeric( $field ) ) {
$list .= "$value";
- } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array ($value) ) {
+ } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array($value) ) {
$list .= $field." IN (".$this->makeList($value).") ";
+ } elseif( is_null($value) ) {
+ if ( $mode == LIST_AND || $mode == LIST_OR ) {
+ $list .= "$field IS ";
+ } elseif ( $mode == LIST_SET ) {
+ $list .= "$field = ";
+ }
+ $list .= 'NULL';
} else {
if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
$list .= "$field = ";
@@ -1534,7 +1590,7 @@ class Database {
global $wgSharedDB;
# Skip quoted literals
if ( $name{0} != '`' ) {
- if ( $this->mTablePrefix !== '' && strpos( '.', $name ) === false ) {
+ if ( $this->mTablePrefix !== '' && strpos( $name, '.' ) === false ) {
$name = "{$this->mTablePrefix}$name";
}
if ( isset( $wgSharedDB ) && "{$this->mTablePrefix}user" == $name ) {
@@ -1570,7 +1626,7 @@ class Database {
* This is handy when you need to construct SQL for joins
*
* Example:
- * list( $user, $watchlist ) = $dbr->tableNames('user','watchlist');
+ * list( $user, $watchlist ) = $dbr->tableNamesN('user','watchlist');
* $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
* WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
*/
@@ -2001,7 +2057,12 @@ class Database {
*/
function resultObject( $result ) {
if( empty( $result ) ) {
- return NULL;
+ return false;
+ } elseif ( $result instanceof ResultWrapper ) {
+ return $result;
+ } elseif ( $result === true ) {
+ // Successful write query
+ return $result;
} else {
return new ResultWrapper( $this, $result );
}
@@ -2046,8 +2107,7 @@ class Database {
*/
function getLag() {
$res = $this->query( 'SHOW PROCESSLIST' );
- # Find slave SQL thread. Assumed to be the second one running, which is a bit
- # dubious, but unfortunately there's no easy rigorous way
+ # Find slave SQL thread
while ( $row = $this->fetchObject( $res ) ) {
/* This should work for most situations - when default db
* for thread is not specified, it had no events executed,
@@ -2176,7 +2236,7 @@ class Database {
$cmd = $this->replaceVars( $cmd );
$res = $this->query( $cmd, __METHOD__, true );
if ( $resultCallback ) {
- call_user_func( $resultCallback, $this->resultObject( $res ) );
+ call_user_func( $resultCallback, $res );
}
if ( false === $res ) {
@@ -2244,40 +2304,55 @@ class DatabaseMysql extends Database {
* Result wrapper for grabbing data queried by someone else
* @addtogroup Database
*/
-class ResultWrapper {
- var $db, $result;
+class ResultWrapper implements Iterator {
+ var $db, $result, $pos = 0, $currentRow = null;
/**
- * @todo document
+ * Create a new result object from a result resource and a Database object
*/
- function ResultWrapper( &$database, $result ) {
- $this->db =& $database;
- $this->result =& $result;
+ function ResultWrapper( $database, $result ) {
+ $this->db = $database;
+ if ( $result instanceof ResultWrapper ) {
+ $this->result = $result->result;
+ } else {
+ $this->result = $result;
+ }
}
/**
- * @todo document
+ * Get the number of rows in a result object
*/
function numRows() {
return $this->db->numRows( $this->result );
}
/**
- * @todo document
+ * Fetch the next row from the given result object, in object form.
+ * Fields can be retrieved with $row->fieldname, with fields acting like
+ * member variables.
+ *
+ * @param $res SQL result object as returned from Database::query(), etc.
+ * @return MySQL row object
+ * @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject() {
return $this->db->fetchObject( $this->result );
}
/**
- * @todo document
+ * Fetch the next row from the given result object, in associative array
+ * form. Fields are retrieved with $row['fieldname'].
+ *
+ * @param $res SQL result object as returned from Database::query(), etc.
+ * @return MySQL row object
+ * @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow() {
return $this->db->fetchRow( $this->result );
}
/**
- * @todo document
+ * Free a result object
*/
function free() {
$this->db->freeResult( $this->result );
@@ -2285,16 +2360,48 @@ class ResultWrapper {
unset( $this->db );
}
+ /**
+ * Change the position of the cursor in a result object
+ * See mysql_data_seek()
+ */
function seek( $row ) {
$this->db->dataSeek( $this->result, $row );
}
-
+
+ /*********************
+ * Iterator functions
+ * Note that using these in combination with the non-iterator functions
+ * above may cause rows to be skipped or repeated.
+ */
+
function rewind() {
if ($this->numRows()) {
$this->db->dataSeek($this->result, 0);
}
+ $this->pos = 0;
+ $this->currentRow = null;
+ }
+
+ function current() {
+ if ( is_null( $this->currentRow ) ) {
+ $this->next();
+ }
+ return $this->currentRow;
+ }
+
+ function key() {
+ return $this->pos;
}
+ function next() {
+ $this->pos++;
+ $this->currentRow = $this->fetchObject();
+ return $this->currentRow;
+ }
+
+ function valid() {
+ return $this->current() !== false;
+ }
}
-?>
+
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
index 4b31b4f0..a4a0444f 100644
--- a/includes/DatabaseFunctions.php
+++ b/includes/DatabaseFunctions.php
@@ -399,4 +399,4 @@ function wfUseIndexClause( $index, $dbi = DB_SLAVE ) {
return false;
}
}
-?>
+
diff --git a/includes/DatabaseOracle.php b/includes/DatabaseOracle.php
index 2b720df7..38485481 100644
--- a/includes/DatabaseOracle.php
+++ b/includes/DatabaseOracle.php
@@ -128,6 +128,9 @@ class DatabaseOracle extends Database {
function implicitGroupby() {
return false;
}
+ function implicitOrderby() {
+ return false;
+ }
function searchableIPs() {
return true;
}
@@ -659,7 +662,7 @@ echo "error!\n";
#if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
#if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
- if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
+ if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
$useIndex = $this->useIndexClause( $options['USE INDEX'] );
@@ -675,11 +678,20 @@ echo "error!\n";
}
function ping() {
- wfDebug( "Function ping() not written for DatabasePostgres.php yet");
+ wfDebug( "Function ping() not written for DatabaseOracle.php yet");
return true;
}
+ /**
+ * How lagged is this slave?
+ *
+ * @return int
+ */
+ public function getLag() {
+ # Not implemented for Oracle
+ return 0;
+ }
} // end DatabaseOracle class
-?>
+
diff --git a/includes/DatabasePostgres.php b/includes/DatabasePostgres.php
index 07b3339d..32c061a0 100644
--- a/includes/DatabasePostgres.php
+++ b/includes/DatabasePostgres.php
@@ -102,9 +102,15 @@ class DatabasePostgres extends Database {
function implicitGroupby() {
return false;
}
+ function implicitOrderby() {
+ return false;
+ }
function searchableIPs() {
return true;
}
+ function functionalIndexes() {
+ return true;
+ }
static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0)
{
@@ -155,6 +161,7 @@ class DatabasePostgres extends Database {
$this->mOpened = true;
## If this is the initial connection, setup the schema stuff and possibly create the user
+ ## TODO: Move this out of open()
if (defined('MEDIAWIKI_INSTALL')) {
global $wgDBname, $wgDBuser, $wgDBpassword, $wgDBsuperuser, $wgDBmwschema,
$wgDBts2schema;
@@ -504,12 +511,18 @@ class DatabasePostgres extends Database {
}
function freeResult( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
if ( !@pg_free_result( $res ) ) {
throw new DBUnexpectedError($this, "Unable to free Postgres result\n" );
}
}
function fetchObject( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@$row = pg_fetch_object( $res );
# FIXME: HACK HACK HACK HACK debug
@@ -523,6 +536,9 @@ class DatabasePostgres extends Database {
}
function fetchRow( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@$row = pg_fetch_array( $res );
if( pg_last_error($this->mConn) ) {
throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
@@ -531,14 +547,27 @@ class DatabasePostgres extends Database {
}
function numRows( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@$n = pg_num_rows( $res );
if( pg_last_error($this->mConn) ) {
throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
}
return $n;
}
- function numFields( $res ) { return pg_num_fields( $res ); }
- function fieldName( $res, $n ) { return pg_field_name( $res, $n ); }
+ function numFields( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return pg_num_fields( $res );
+ }
+ function fieldName( $res, $n ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return pg_field_name( $res, $n );
+ }
/**
* This must be called after nextSequenceVal
@@ -547,7 +576,13 @@ class DatabasePostgres extends Database {
return $this->mInsertId;
}
- function dataSeek( $res, $row ) { return pg_result_seek( $res, $row ); }
+ function dataSeek( $res, $row ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return pg_result_seek( $res, $row );
+ }
+
function lastError() {
if ( $this->mConn ) {
return pg_last_error();
@@ -561,7 +596,7 @@ class DatabasePostgres extends Database {
}
function affectedRows() {
- if( !isset( $this->mLastResult ) )
+ if( !isset( $this->mLastResult ) or ! $this->mLastResult )
return 0;
return pg_affected_rows( $this->mLastResult );
@@ -601,13 +636,9 @@ class DatabasePostgres extends Database {
if ( !$res ) {
return NULL;
}
-
while ( $row = $this->fetchObject( $res ) ) {
if ( $row->indexname == $index ) {
return $row;
-
- // BUG: !!!! This code needs to be synced up with database.php
-
}
}
return false;
@@ -666,7 +697,7 @@ class DatabasePostgres extends Database {
$sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
if ( $multi ) {
- if ( $wgDBversion >= 8.1 ) {
+ if ( $wgDBversion >= 8.2 ) {
$first = true;
foreach ( $args as $row ) {
if ( $first ) {
@@ -920,10 +951,10 @@ class DatabasePostgres extends Database {
. "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
. "AND c.relkind IN ('" . implode("','", $types) . "')";
$res = $this->query( $SQL );
- $count = $res ? pg_num_rows($res) : 0;
+ $count = $res ? $res->numRows() : 0;
if ($res)
$this->freeResult( $res );
- return $count;
+ return $count ? true : false;
}
/*
@@ -953,7 +984,7 @@ END;
$this->addQuotes($trigger)));
if (!$res)
return NULL;
- $rows = pg_num_rows($res);
+ $rows = $res->numRows();
$this->freeResult($res);
return $rows;
}
@@ -977,7 +1008,7 @@ END;
$res = $this->query($SQL);
if (!$res)
return NULL;
- $rows = pg_num_rows($res);
+ $rows = $res->numRows();
$this->freeResult($res);
return $rows;
}
@@ -990,7 +1021,12 @@ END;
$SQL = "SELECT rolname FROM pg_catalog.pg_namespace n, pg_catalog.pg_roles r "
."WHERE n.nspowner=r.oid AND n.nspname = '$eschema'";
$res = $this->query( $SQL );
- $owner = $res ? pg_num_rows($res) ? pg_fetch_result($res, 0, 0) : false : false;
+ if ( $res && $res->numRows() ) {
+ $row = $res->fetchObject();
+ $owner = $row->rolname;
+ } else {
+ $owner = false;
+ }
if ($res)
$this->freeResult($res);
return $owner;
@@ -1008,7 +1044,7 @@ END;
. "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
. "AND a.attrelid = c.oid AND a.attname = '$ecol'";
$res = $this->query( $SQL, $fname );
- $count = $res ? pg_num_rows($res) : 0;
+ $count = $res ? $res->numRows() : 0;
if ($res)
$this->freeResult( $res );
return $count;
@@ -1102,10 +1138,10 @@ END;
$this->doQuery("COMMIT");
}
- function encodeBlob($b) {
- return array('bytea',pg_escape_bytea($b));
+ function encodeBlob( $b ) {
+ return pg_escape_bytea( $b );
}
- function decodeBlob($b) {
+ function decodeBlob( $b ) {
return pg_unescape_bytea( $b );
}
@@ -1133,8 +1169,7 @@ END;
}
/**
- * Returns an optional USE INDEX clause to go after the table, and a
- * string to go at the end of the query
+ * Various select options
*
* @private
*
@@ -1144,7 +1179,7 @@ END;
*/
function makeSelectOptions( $options ) {
$preLimitTail = $postLimitTail = '';
- $startOpts = '';
+ $startOpts = $useIndex = '';
$noKeyOptions = array();
foreach ( $options as $key => $option ) {
@@ -1154,6 +1189,7 @@ END;
}
if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY " . $options['GROUP BY'];
+ if ( isset( $options['HAVING'] ) ) $preLimitTail .= " HAVING {$options['HAVING']}";
if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY " . $options['ORDER BY'];
//if (isset($options['LIMIT'])) {
@@ -1164,13 +1200,7 @@ END;
if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $postLimitTail .= ' FOR UPDATE';
if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $postLimitTail .= ' LOCK IN SHARE MODE';
- if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
-
- if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
- $useIndex = $this->useIndexClause( $options['USE INDEX'] );
- } else {
- $useIndex = '';
- }
+ if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
}
@@ -1183,8 +1213,16 @@ END;
wfDebug( "Function ping() not written for DatabasePostgres.php yet");
return true;
}
-
+
+ /**
+ * How lagged is this slave?
+ *
+ */
+ public function getLag() {
+ # Not implemented for PostgreSQL
+ return false;
+ }
} // end DatabasePostgres class
-?>
+
diff --git a/includes/DateFormatter.php b/includes/DateFormatter.php
index 88a64453..bbad6d15 100644
--- a/includes/DateFormatter.php
+++ b/includes/DateFormatter.php
@@ -282,4 +282,4 @@ class DateFormatter
}
}
-?>
+
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index d8f9a621..1ed8779a 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -27,11 +27,11 @@ if( !defined( 'MEDIAWIKI' ) ) {
* Create a site configuration object
* Not used for much in a default install
*/
-require_once( 'includes/SiteConfiguration.php' );
+require_once( "$IP/includes/SiteConfiguration.php" );
$wgConf = new SiteConfiguration;
/** MediaWiki version number */
-$wgVersion = '1.10.2';
+$wgVersion = '1.11.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
@@ -125,8 +125,9 @@ $wgUsePathInfo =
* in LocalSettings.php. Generally you should not need to change this
* unless you don't like seeing "index.php".
*/
-$wgScript = false; /// defaults to "{$wgScriptPath}/index.php"
-$wgRedirectScript = false; /// defaults to "{$wgScriptPath}/redirect.php"
+$wgScriptExtension = '.php'; /// extension to append to script names by default
+$wgScript = false; /// defaults to "{$wgScriptPath}/index{$wgScriptExtension}"
+$wgRedirectScript = false; /// defaults to "{$wgScriptPath}/redirect{$wgScriptExtension}"
/**#@-*/
@@ -163,16 +164,6 @@ $wgUploadBaseUrl = "";
/**#@-*/
/**
- * By default deleted files are simply discarded; to save them and
- * make it possible to undelete images, create a directory which
- * is writable to the web server but is not exposed to the internet.
- *
- * Set $wgSaveDeletedFiles to true and set up the save path in
- * $wgFileStore['deleted']['directory'].
- */
-$wgSaveDeletedFiles = false;
-
-/**
* New file storage paths; currently used only for deleted files.
* Set it like this:
*
@@ -180,10 +171,61 @@ $wgSaveDeletedFiles = false;
*
*/
$wgFileStore = array();
-$wgFileStore['deleted']['directory'] = null; // Don't forget to set this.
+$wgFileStore['deleted']['directory'] = false;// Defaults to $wgUploadDirectory/deleted
$wgFileStore['deleted']['url'] = null; // Private
$wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
+/**#@+
+ * File repository structures
+ *
+ * $wgLocalFileRepo is a single repository structure, and $wgForeignFileRepo is
+ * a an array of such structures. Each repository structure is an associative
+ * array of properties configuring the repository.
+ *
+ * Properties required for all repos:
+ * class The class name for the repository. May come from the core or an extension.
+ * The core repository classes are LocalRepo, ForeignDBRepo, FSRepo.
+ *
+ * name A unique name for the repository.
+ *
+ * For all core repos:
+ * url Base public URL
+ * 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
+ * handler instead.
+ * initialCapital Equivalent to $wgCapitalLinks, determines whether filenames implicitly
+ * start with a capital letter. The current implementation may give incorrect
+ * description page links when the local $wgCapitalLinks and initialCapital
+ * are mismatched.
+ * pathDisclosureProtection
+ * May be 'paranoid' to remove all parameters from error messages, 'none' to
+ * leave the paths in unchanged, or 'simple' to replace paths with
+ * placeholders. Default for LocalRepo is 'simple'.
+ *
+ * These settings describe a foreign MediaWiki installation. They are optional, and will be ignored
+ * for local repositories:
+ * descBaseUrl URL of image description pages, e.g. http://en.wikipedia.org/wiki/Image:
+ * scriptDirUrl URL of the MediaWiki installation, equivalent to $wgScriptPath, e.g.
+ * http://en.wikipedia.org/w
+ *
+ * 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.
+ *
+ * ForeignDBRepo:
+ * dbType, dbServer, dbUser, dbPassword, dbName, dbFlags
+ * equivalent to the corresponding member of $wgDBservers
+ * tablePrefix Table prefix, the foreign wiki's $wgDBprefix
+ * hasSharedCache True if the wiki's shared cache is accessible via the local $wgMemc
+ *
+ * The default is to initialise these arrays from the MW<1.11 backwards compatible settings:
+ * $wgUploadPath, $wgThumbnailScriptPath, $wgSharedUploadDirectory, etc.
+ */
+$wgLocalFileRepo = false;
+$wgForeignFileRepos = array();
+/**#@-*/
+
/**
* Allowed title characters -- regex character class
* Don't change this unless you know what you're doing
@@ -261,34 +303,34 @@ $wgAntivirus= NULL;
*
* @global array $wgAntivirusSetup
*/
-$wgAntivirusSetup= array(
+$wgAntivirusSetup = array(
#setup for clamav
'clamav' => array (
'command' => "clamscan --no-summary ",
- 'codemap'=> array (
- "0"=> AV_NO_VIRUS, #no virus
- "1"=> AV_VIRUS_FOUND, #virus found
- "52"=> AV_SCAN_ABORTED, #unsupported file format (probably imune)
- "*"=> AV_SCAN_FAILED, #else scan failed
+ 'codemap' => array (
+ "0" => AV_NO_VIRUS, # no virus
+ "1" => AV_VIRUS_FOUND, # virus found
+ "52" => AV_SCAN_ABORTED, # unsupported file format (probably imune)
+ "*" => AV_SCAN_FAILED, # else scan failed
),
- 'messagepattern'=> '/.*?:(.*)/sim',
+ '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
+ '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',
+ 'messagepattern' => '/.*?Infection:(.*)$/m',
),
);
@@ -355,6 +397,10 @@ $wgActionPaths = array();
* no file of the given name is found in the local repository (for [[Image:..]],
* [[Media:..]] links). Thumbnails will also be looked for and generated in this
* directory.
+ *
+ * Note that these configuration settings can now be defined on a per-
+ * repository basis for an arbitrary number of file repositories, using the
+ * $wgForeignFileRepos variable.
*/
$wgUseSharedUploads = false;
/** Full path on the web server where shared uploads can be found */
@@ -392,7 +438,7 @@ $wgUploadNavigationUrl = false;
* apache servers don't have read/write access to the thumbnail path.
*
* Example:
- * $wgThumbnailScriptPath = "{$wgScriptPath}/thumb.php";
+ * $wgThumbnailScriptPath = "{$wgScriptPath}/thumb{$wgScriptExtension}";
*/
$wgThumbnailScriptPath = false;
$wgSharedThumbnailScriptPath = false;
@@ -600,6 +646,19 @@ $wgDBmysql5 = false;
$wgLocalDatabases = array();
/**
+ * For multi-wiki clusters with multiple master servers; if an alternate
+ * is listed for the requested database, a connection to it will be opened
+ * instead of to the current wiki's regular master server when cross-wiki
+ * data operations are done from here.
+ *
+ * Requires that the other server be accessible by network, with the same
+ * username/password as the primary.
+ *
+ * eg $wgAlternateMaster['enwiki'] = 'ariel';
+ */
+$wgAlternateMaster = array();
+
+/**
* Object cache settings
* See Defines.php for types
*/
@@ -619,7 +678,6 @@ $wgLinkCacheMemcached = false; # Not fully tested
$wgUseMemCached = false;
$wgMemCachedDebug = false; # Will be set to false in Setup.php, if the server isn't working
$wgMemCachedServers = array( '127.0.0.1:11000' );
-$wgMemCachedDebug = false;
$wgMemCachedPersistent = false;
/**
@@ -806,6 +864,7 @@ $wgRedirectSources = false;
$wgShowIPinHeader = true; # For non-logged in users
$wgMaxNameChars = 255; # Maximum number of bytes in username
+$wgMaxSigChars = 255; # Maximum number of Unicode characters in signature
$wgMaxArticleSize = 2048; # Maximum article size in kilobytes
$wgExtraSubtitle = '';
@@ -869,8 +928,12 @@ $wgColorErrors = true;
$wgShowExceptionDetails = false;
/**
- * disable experimental dmoz-like category browsing. Output things like:
- * Encyclopedia > Music > Style of Music > Jazz
+ * Expose backend server host names through the API and various HTML comments
+ */
+$wgShowHostnames = false;
+
+/**
+ * Use experimental, DMOZ-like category browser
*/
$wgUseCategoryBrowser = false;
@@ -920,9 +983,10 @@ $wgHitcounterUpdateFreq = 1;
# Basic user rights and block settings
$wgSysopUserBans = true; # Allow sysops to ban logged-in users
-$wgSysopRangeBans = true; # Allow sysops to ban IP ranges
-$wgAutoblockExpiry = 86400; # Number of seconds before autoblock entries expire
+$wgSysopRangeBans = true; # Allow sysops to ban IP ranges
+$wgAutoblockExpiry = 86400; # Number of seconds before autoblock entries expire
$wgBlockAllowsUTEdit = false; # Blocks allow users to edit their own user talk page
+$wgSysopEmailBans = true; # Allow sysops to ban users from accessing Emailuser
# Pages anonymous user may see as an array, e.g.:
# array ( "Main Page", "Special:Userlogin", "Wikipedia:Help");
@@ -947,6 +1011,11 @@ $wgEmailConfirmToEdit=false;
* combined with the permissions of all groups that a given user is listed
* in in the user_groups table.
*
+ * Note: Don't set $wgGroupPermissions = array(); unless you know what you're
+ * doing! This will wipe all permissions, and may mean that your users are
+ * unable to perform certain essential tasks or access new functionality
+ * when new permissions are introduced and default grants established.
+ *
* Functionality to make pages inaccessible has not been extensively tested
* for security. Use at your own risk!
*
@@ -1009,6 +1078,7 @@ $wgGroupPermissions['sysop']['unwatchedpages'] = true;
$wgGroupPermissions['sysop']['autoconfirmed'] = true;
$wgGroupPermissions['sysop']['upload_by_url'] = true;
$wgGroupPermissions['sysop']['ipblock-exempt'] = true;
+$wgGroupPermissions['sysop']['blockemail'] = true;
// Permission to change users' group assignments
$wgGroupPermissions['bureaucrat']['userrights'] = true;
@@ -1033,8 +1103,14 @@ $wgGroupPermissions['bureaucrat']['userrights'] = true;
$wgRestrictionTypes = array( 'edit', 'move' );
/**
- * Set of permission keys that can be selected via action=protect.
- * 'autoconfirm' allows all registerd users if $wgAutoConfirmAge is 0.
+ * Rights which can be required for each protection level (via action=protect)
+ *
+ * You can add a new protection level that requires a specific
+ * permission by manipulating this array. The ordering of elements
+ * dictates the order on the protection form's lists.
+ *
+ * '' will be ignored (i.e. unprotected)
+ * 'sysop' is quietly rewritten to 'protect' for backwards compatibility
*/
$wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
@@ -1074,7 +1150,20 @@ $wgAutoConfirmAge = 0;
$wgAutoConfirmCount = 0;
//$wgAutoConfirmCount = 50;
-
+/**
+ * These settings can be used to give finer control over who can assign which
+ * groups at Special:Userrights. Example configuration:
+ *
+ * // Bureaucrat can add any group
+ * $wgAddGroups['bureaucrat'] = true;
+ * // Bureaucrats can only remove bots and sysops
+ * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' );
+ * // Sysops can make bots
+ * $wgAddGroups['sysop'] = array( 'bot' );
+ * // Sysops can disable other sysops in an emergency, and disable bots
+ * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' );
+ */
+$wgAddGroups = $wgRemoveGroups = array();
# Proxy scanner settings
#
@@ -1125,7 +1214,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '63';
+$wgStyleVersion = '97';
# Server-side caching:
@@ -1174,6 +1263,21 @@ $wgEnotifRevealEditorAddress = false; # UPO; reply-to address may be filled with
$wgEnotifMinorEdits = true; # UPO; false: "minor edits" on pages do not trigger notification mails.
# # Attention: _every_ change on a user_talk page trigger a notification mail (if the user is not yet notified)
+# Send a generic mail instead of a personalised mail for each user. This
+# always uses UTC as the time zone, and doesn't include the username.
+#
+# For pages with many users watching, this can significantly reduce mail load.
+# Has no effect when using sendmail rather than SMTP;
+
+$wgEnotifImpersonal = false;
+
+# Maximum number of users to mail at once when using impersonal mail. Should
+# match the limit on your mail server.
+$wgEnotifMaxRecips = 500;
+
+# Send mails via the job queue.
+$wgEnotifUseJobQ = false;
+
/**
* Array of usernames who will be sent a notification email for every change which occurs on a wiki
*/
@@ -1230,7 +1334,11 @@ $wgSquidMaxage = 18000;
/**
* A list of proxy servers (ips if possible) to purge on changes don't specify
- * ports here (80 is default)
+ * ports here (80 is default). When mediawiki is running behind a proxy, its
+ * address should be listed in $wgSquidServers otherwise mediawiki won't rely
+ * on the X-FORWARDED-FOR header to determine the user IP address and
+ * all users will appear to come from the proxy IP address. Don't use domain
+ * names here, only IP adresses.
*/
# $wgSquidServers = array('127.0.0.1');
$wgSquidServers = array();
@@ -1283,6 +1391,18 @@ $wgWantedPagesThreshold = 1;
$wgAllowSlowParserFunctions = false;
/**
+ * Maps jobs to their handling classes; extensions
+ * can add to this to provide custom jobs
+ */
+$wgJobClasses = array(
+ 'refreshLinks' => 'RefreshLinksJob',
+ 'htmlCacheUpdate' => 'HTMLCacheUpdateJob',
+ 'html_cache_update' => 'HTMLCacheUpdateJob', // backwards-compatible
+ 'sendMail' => 'EmaillingJob',
+ 'enotifNotify' => 'EnotifNotifyJob',
+);
+
+/**
* To use inline TeX, you need to compile 'texvc' (in the 'math' subdirectory of
* the MediaWiki package and have latex, dvips, gs (ghostscript), andconvert
* (ImageMagick) installed and available in the PATH.
@@ -1322,7 +1442,11 @@ $wgDebugFunctionEntry = 0;
/** Lots of debugging output from SquidUpdate.php */
$wgDebugSquid = false;
+/** Whereas to count the number of time an article is viewed.
+ * Does not work if pages are cached (for example with squid).
+ */
$wgDisableCounters = false;
+
$wgDisableTextSearch = false;
$wgDisableSearchContext = false;
/**
@@ -1335,6 +1459,13 @@ $wgEnableUploads = false;
/**
* 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 folloing lines to the
+ * "Windows extensions" section of php.ini:
+ *
+ * extension=extensions/php_mbstring.dll
+ * extension=extensions/php_exif.dll
*/
$wgShowEXIF = function_exists( 'exif_read_data' );
@@ -1373,7 +1504,7 @@ $wgAntiLockFlags = 0;
$wgDiff3 = '/usr/bin/diff3';
/**
- * We can also compress text in the old revisions table. If this is set on, old
+ * We can also compress text stored in the 'text' table. If this is set on, new
* revisions will be compressed on page save if zlib support is available. Any
* compressed revisions will be decompressed on load regardless of this setting
* *but will not be readable at all* if zlib support is not available.
@@ -1389,7 +1520,7 @@ $wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' );
/** Files with these extensions will never be allowed as uploads. */
$wgFileBlacklist = array(
# HTML may contain cookie-stealing JavaScript and web bugs
- 'html', 'htm', 'js', 'jsb',
+ 'html', 'htm', 'js', 'jsb', 'mhtml', 'mht',
# PHP scripts may execute arbitrary code on the server
'php', 'phtml', 'php3', 'php4', 'php5', 'phps',
# Other types that may be interpreted by some servers
@@ -1445,10 +1576,15 @@ $wgNamespacesToBeSearchedDefault = array(
NS_MAIN => true,
);
-/** If set, a bold ugly notice will show up at the top of every page. */
+/**
+ * Site notice shown at the top of each page
+ *
+ * This message can contain wiki text, and can also be set through the
+ * MediaWiki:Sitenotice page. You can also provide a separate message for
+ * logged-out users using the MediaWiki:Anonnotice page.
+ */
$wgSiteNotice = '';
-
#
# Images settings
#
@@ -1463,6 +1599,7 @@ $wgMediaHandlers = array(
'image/gif' => 'BitmapHandler',
'image/x-ms-bmp' => 'BmpHandler',
'image/svg+xml' => 'SvgHandler',
+ 'image/svg' => 'SvgHandler',
'image/vnd.djvu' => 'DjVuHandler',
);
@@ -1780,6 +1917,28 @@ $wgExtensionFunctions = array();
$wgSkinExtensionFunctions = array();
/**
+ * Extension messages files
+ * Associative array mapping extension name to the filename where messages can be found.
+ * The file must create a variable called $messages.
+ * When the messages are needed, the extension should call wfLoadMessagesFile()
+ */
+$wgExtensionMessagesFiles = array();
+
+/**
+ * Parser output hooks.
+ * This is an associative array where the key is an extension-defined tag
+ * (typically the extension name), and the value is a PHP callback.
+ * These will be called as an OutputPageParserOutput hook, if the relevant
+ * tag has been registered with the parser output object.
+ *
+ * Registration is done with $pout->addOutputHook( $tag, $data ).
+ *
+ * The callback has the form:
+ * function outputHook( $outputPage, $parserOutput, $data ) { ... }
+ */
+$wgParserOutputHooks = array();
+
+/**
* List of valid skin names.
* The key should be the name in all lower case, the value should be a display name.
* The default skins will be added later, by Skin::getSkinNames(). Use
@@ -1937,6 +2096,13 @@ $wgThumbLimits = array(
);
/**
+ * Adjust width of upright images when parameter 'upright' is used
+ * This allows a nicer look for upright images without the need to fix the width
+ * by hardcoded px in wiki sourcecode.
+ */
+$wgThumbUpright = 0.75;
+
+/**
* On category pages, show thumbnail gallery for images belonging to that
* category instead of listing them as articles.
*/
@@ -1978,7 +2144,13 @@ $wgBrowserBlackList = array(
* @link http://en.wikipedia.org/w/index.php?title=User%3A%C6var_Arnfj%F6r%F0_Bjarmason%2Ftestme&diff=12356041&oldid=12355864
* @link http://en.wikipedia.org/wiki/Template%3AOS9
*/
- '/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/'
+ '/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
+
+ /**
+ * Google wireless transcoder, seems to eat a lot of chars alive
+ * http://it.wikipedia.org/w/index.php?title=Luciano_Ligabue&diff=prev&oldid=8857361
+ */
+ '/^Mozilla\/4\.0 \(compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;\)/'
);
/**
@@ -2075,7 +2247,7 @@ $wgLogTypes = array( '',
* Extensions with custom log types may add to this array.
*/
$wgLogNames = array(
- '' => 'log',
+ '' => 'all-logs-page',
'block' => 'blocklogpage',
'protect' => 'protectlogpage',
'rights' => 'rightslog',
@@ -2114,12 +2286,14 @@ $wgLogActions = array(
'block/block' => 'blocklogentry',
'block/unblock' => 'unblocklogentry',
'protect/protect' => 'protectedarticle',
+ 'protect/modify' => 'modifiedarticleprotection',
'protect/unprotect' => 'unprotectedarticle',
'rights/rights' => 'rightslogentry',
'delete/delete' => 'deletedarticle',
'delete/restore' => 'undeletedarticle',
'delete/revision' => 'revdelete-logentry',
'upload/upload' => 'uploadedimage',
+ 'upload/overwrite' => 'overwroteimage',
'upload/revert' => 'uploadedimage',
'move/move' => '1movedto2',
'move/move_redir' => '1movedto2_redir',
@@ -2178,6 +2352,16 @@ $wgNoFollowNsExceptions = array();
$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
+ * canonicalised text form title and the value is a robot policy.
+ * Example:
+ * $wgArticleRobotPolicies = array( 'Main Page' => 'noindex' );
+ */
+$wgArticleRobotPolicies = array();
+
+/**
* Specifies the minimal length of a user password. If set to
* 0, empty passwords are allowed.
*/
@@ -2385,7 +2569,7 @@ $wgUpdateRowsPerQuery = 10;
/**
* Enable AJAX framework
*/
-$wgUseAjax = false;
+$wgUseAjax = true;
/**
* Enable auto suggestion for the search bar
@@ -2405,12 +2589,22 @@ $wgAjaxExportList = array( );
* Requires $wgUseAjax to be true too.
* Causes wfAjaxWatch to be added to $wgAjaxExportList
*/
-$wgAjaxWatch = false;
+$wgAjaxWatch = true;
+
+/**
+ * Enable AJAX check for file overwrite, pre-upload
+ */
+$wgAjaxUploadDestCheck = true;
+
+/**
+ * Enable previewing licences via AJAX
+ */
+$wgAjaxLicensePreview = true;
/**
* Allow DISPLAYTITLE to change title display
*/
-$wgAllowDisplayTitle = false ;
+$wgAllowDisplayTitle = true;
/**
* Array of usernames which may not be registered or logged in from
@@ -2491,13 +2685,28 @@ $wgDjvuPostProcessor = 'pnmtojpeg';
$wgDjvuOutputExtension = 'jpg';
/**
-* Enable direct access to the data API
-* through api.php
-*/
+ * Enable the MediaWiki API for convenient access to
+ * machine-readable data via api.php
+ *
+ * See http://www.mediawiki.org/wiki/API
+ */
$wgEnableAPI = true;
+
+/**
+ * Allow the API to be used to perform write operations
+ * (page edits, rollback, etc.) when an authorised user
+ * accesses it
+ */
$wgEnableWriteAPI = false;
/**
+ * API module extensions
+ * Associative array mapping module name to class name.
+ * Extension modules may override the core modules.
+ */
+$wgAPIModules = array();
+
+/**
* Parser test suite files to be run by parserTests.php when no specific
* filename is passed to it.
*
@@ -2532,4 +2741,10 @@ $wgEnableCascadingProtection = true;
*/
$wgDisableOutputCompression = false;
-?>
+/**
+ * If lag is higher than $wgSlaveLagWarning, show a warning in some special
+ * pages (like watchlist). If the lag is higher than $wgSlaveLagCritical,
+ * show a more obvious warning.
+ */
+$wgSlaveLagWarning = 10;
+$wgSlaveLagCritical = 30;
diff --git a/includes/Defines.php b/includes/Defines.php
index 98e76277..c923c256 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -205,5 +205,61 @@ define( 'LIST_SET', 2 );
define( 'LIST_NAMES', 3);
define( 'LIST_OR', 4);
+/**
+ * Unicode and normalisation related
+ */
+define( 'UNICODE_HANGUL_FIRST', 0xac00 );
+define( 'UNICODE_HANGUL_LAST', 0xd7a3 );
+
+define( 'UNICODE_HANGUL_LBASE', 0x1100 );
+define( 'UNICODE_HANGUL_VBASE', 0x1161 );
+define( 'UNICODE_HANGUL_TBASE', 0x11a7 );
+
+define( 'UNICODE_HANGUL_LCOUNT', 19 );
+define( 'UNICODE_HANGUL_VCOUNT', 21 );
+define( 'UNICODE_HANGUL_TCOUNT', 28 );
+define( 'UNICODE_HANGUL_NCOUNT', UNICODE_HANGUL_VCOUNT * UNICODE_HANGUL_TCOUNT );
+
+define( 'UNICODE_HANGUL_LEND', UNICODE_HANGUL_LBASE + UNICODE_HANGUL_LCOUNT - 1 );
+define( 'UNICODE_HANGUL_VEND', UNICODE_HANGUL_VBASE + UNICODE_HANGUL_VCOUNT - 1 );
+define( 'UNICODE_HANGUL_TEND', UNICODE_HANGUL_TBASE + UNICODE_HANGUL_TCOUNT - 1 );
+
+define( 'UNICODE_SURROGATE_FIRST', 0xd800 );
+define( 'UNICODE_SURROGATE_LAST', 0xdfff );
+define( 'UNICODE_MAX', 0x10ffff );
+define( 'UNICODE_REPLACEMENT', 0xfffd );
+
+
+define( 'UTF8_HANGUL_FIRST', "\xea\xb0\x80" /*codepointToUtf8( UNICODE_HANGUL_FIRST )*/ );
+define( 'UTF8_HANGUL_LAST', "\xed\x9e\xa3" /*codepointToUtf8( UNICODE_HANGUL_LAST )*/ );
+
+define( 'UTF8_HANGUL_LBASE', "\xe1\x84\x80" /*codepointToUtf8( UNICODE_HANGUL_LBASE )*/ );
+define( 'UTF8_HANGUL_VBASE', "\xe1\x85\xa1" /*codepointToUtf8( UNICODE_HANGUL_VBASE )*/ );
+define( 'UTF8_HANGUL_TBASE', "\xe1\x86\xa7" /*codepointToUtf8( UNICODE_HANGUL_TBASE )*/ );
+
+define( 'UTF8_HANGUL_LEND', "\xe1\x84\x92" /*codepointToUtf8( UNICODE_HANGUL_LEND )*/ );
+define( 'UTF8_HANGUL_VEND', "\xe1\x85\xb5" /*codepointToUtf8( UNICODE_HANGUL_VEND )*/ );
+define( 'UTF8_HANGUL_TEND', "\xe1\x87\x82" /*codepointToUtf8( UNICODE_HANGUL_TEND )*/ );
+
+define( 'UTF8_SURROGATE_FIRST', "\xed\xa0\x80" /*codepointToUtf8( UNICODE_SURROGATE_FIRST )*/ );
+define( 'UTF8_SURROGATE_LAST', "\xed\xbf\xbf" /*codepointToUtf8( UNICODE_SURROGATE_LAST )*/ );
+define( 'UTF8_MAX', "\xf4\x8f\xbf\xbf" /*codepointToUtf8( UNICODE_MAX )*/ );
+define( 'UTF8_REPLACEMENT', "\xef\xbf\xbd" /*codepointToUtf8( UNICODE_REPLACEMENT )*/ );
+#define( 'UTF8_REPLACEMENT', '!' );
+
+define( 'UTF8_OVERLONG_A', "\xc1\xbf" );
+define( 'UTF8_OVERLONG_B', "\xe0\x9f\xbf" );
+define( 'UTF8_OVERLONG_C', "\xf0\x8f\xbf\xbf" );
+
+# These two ranges are illegal
+define( 'UTF8_FDD0', "\xef\xb7\x90" /*codepointToUtf8( 0xfdd0 )*/ );
+define( 'UTF8_FDEF', "\xef\xb7\xaf" /*codepointToUtf8( 0xfdef )*/ );
+define( 'UTF8_FFFE', "\xef\xbf\xbe" /*codepointToUtf8( 0xfffe )*/ );
+define( 'UTF8_FFFF', "\xef\xbf\xbf" /*codepointToUtf8( 0xffff )*/ );
+
+define( 'UTF8_HEAD', false );
+define( 'UTF8_TAIL', true );
+
+
+
-?>
diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php
index af65ce3a..99bb4798 100644
--- a/includes/DifferenceEngine.php
+++ b/includes/DifferenceEngine.php
@@ -6,6 +6,14 @@
*/
/**
+ * Constant to indicate diff cache compatibility.
+ * Bump this when changing the diff formatting in a way that
+ * fixes important bugs or such to force cached diff views to
+ * clear.
+ */
+define( 'MW_DIFF_VERSION', '1.11a' );
+
+/**
* @todo document
* @public
* @addtogroup DifferenceEngine
@@ -148,8 +156,42 @@ CONTROL;
} else {
$rollback = '';
}
- if( $wgUseRCPatrol && $this->mRcidMarkPatrolled != 0 && $wgUser->isAllowed( 'patrol' ) ) {
- $patrol = ' [' . $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'markaspatrolleddiff' ), "action=markpatrolled&rcid={$this->mRcidMarkPatrolled}" ) . ']';
+
+ // Prepare a change patrol link, if applicable
+ if( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) ) {
+ // If we've been given an explicit change identifier, use it; saves time
+ if( $this->mRcidMarkPatrolled ) {
+ $rcid = $this->mRcidMarkPatrolled;
+ } else {
+ // Look for an unpatrolled change corresponding to this diff
+ $change = RecentChange::newFromConds(
+ array(
+ // Add redundant timestamp condition so we can use the
+ // existing index
+ 'rc_timestamp' => $this->mNewRev->getTimestamp(),
+ 'rc_this_oldid' => $this->mNewid,
+ 'rc_last_oldid' => $this->mOldid,
+ 'rc_patrolled' => 0,
+ ),
+ __METHOD__
+ );
+ if( $change instanceof RecentChange ) {
+ $rcid = $change->mAttribs['rc_id'];
+ } else {
+ // None found
+ $rcid = 0;
+ }
+ }
+ // Build the link
+ if( $rcid ) {
+ $patrol = ' [' . $sk->makeKnownLinkObj(
+ $this->mTitle,
+ wfMsgHtml( 'markaspatrolleddiff' ),
+ "action=markpatrolled&rcid={$rcid}"
+ ) . ']';
+ } else {
+ $patrol = '';
+ }
} else {
$patrol = '';
}
@@ -176,14 +218,14 @@ CONTROL;
wfMsg( 'minoreditletter') ) . ' ';
}
- $oldHeader = "<strong>{$this->mOldtitle}</strong><br />" .
- $sk->revUserTools( $this->mOldRev ) . "<br />" .
- $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly ) . "<br />" .
- $prevlink;
- $newHeader = "<strong>{$this->mNewtitle}</strong><br />" .
- $sk->revUserTools( $this->mNewRev ) . " $rollback<br />" .
- $newminor . $sk->revComment( $this->mNewRev, !$diffOnly ) . "<br />" .
- $nextlink . $patrol;
+ $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $this->mOldtitle . '</strong></div>' .
+ '<div id="mw-diff-otitle2">' . $sk->revUserTools( $this->mOldRev ) . "</div>" .
+ '<div id="mw-diff-otitle3">' . $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly ) . "</div>" .
+ '<div id="mw-diff-otitle4">' . $prevlink . '</div>';
+ $newHeader = '<div id="mw-diff-ntitle1"><strong>' .$this->mNewtitle . '</strong></div>' .
+ '<div id="mw-diff-ntitle2">' . $sk->revUserTools( $this->mNewRev ) . " $rollback</div>" .
+ '<div id="mw-diff-ntitle3">' . $newminor . $sk->revComment( $this->mNewRev, !$diffOnly ) . "</div>" .
+ '<div id="mw-diff-ntitle4">' . $nextlink . $patrol . '</div>';
$this->showDiff( $oldHeader, $newHeader );
@@ -282,24 +324,39 @@ CONTROL;
* Returns false if the diff could not be generated, otherwise returns true
*/
function showDiff( $otitle, $ntitle ) {
- global $wgOut;
- $diff = $this->getDiff( $otitle, $ntitle );
+ global $wgOut, $wgRequest;
+ $diff = $this->getDiff( $otitle, $ntitle, $wgRequest->getVal( 'action' ) == 'purge' );
if ( $diff === false ) {
$wgOut->addWikitext( wfMsg( 'missingarticle', "<nowiki>(fixme, bug)</nowiki>" ) );
return false;
} else {
+ $this->showDiffStyle();
$wgOut->addHTML( $diff );
return true;
}
}
+
+ /**
+ * Add style sheets and supporting JS for diff display.
+ */
+ function showDiffStyle() {
+ global $wgStylePath, $wgStyleVersion, $wgOut;
+ $wgOut->addStyle( 'common/diff.css' );
+
+ // JS is needed to detect old versions of Mozilla to work around an annoyance bug.
+ $wgOut->addScript( "<script type=\"text/javascript\" src=\"$wgStylePath/common/diff.js?$wgStyleVersion\"></script>" );
+ }
/**
- * Get diff table, including header
- * Note that the interface has changed, it's no longer static.
- * Returns false on error
+ * Get complete diff table, including header
+ *
+ * @param Title $otitle Old title
+ * @param Title $ntitle New title
+ * @param bool $skipCache Skip the diff cache for this request?
+ * @return mixed
*/
- function getDiff( $otitle, $ntitle ) {
- $body = $this->getDiffBody();
+ function getDiff( $otitle, $ntitle, $skipCache = false ) {
+ $body = $this->getDiffBody( $skipCache );
if ( $body === false ) {
return false;
} else {
@@ -310,19 +367,20 @@ CONTROL;
/**
* Get the diff table body, without header
- * Results are cached
- * Returns false on error
+ *
+ * @param bool $skipCache Skip cache for this request?
+ * @return mixed
*/
- function getDiffBody() {
+ function getDiffBody( $skipCache = false ) {
global $wgMemc;
$fname = 'DifferenceEngine::getDiffBody';
wfProfileIn( $fname );
// Cacheable?
$key = false;
- if ( $this->mOldid && $this->mNewid ) {
+ if ( $this->mOldid && $this->mNewid && !$skipCache ) {
// Try cache
- $key = wfMemcKey( 'diff', 'oldid', $this->mOldid, 'newid', $this->mNewid );
+ $key = wfMemcKey( 'diff', 'version', MW_DIFF_VERSION, 'oldid', $this->mOldid, 'newid', $this->mNewid );
$difftext = $wgMemc->get( $key );
if ( $difftext ) {
wfIncrStats( 'diff_cache_hit' );
@@ -488,10 +546,14 @@ CONTROL;
$ntitle = '<span class="history-deleted">'.$ntitle.'</span>';
}
$header = "
- <table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>
+ <table class='diff'>
+ <col class='diff-marker' />
+ <col class='diff-content' />
+ <col class='diff-marker' />
+ <col class='diff-content' />
<tr>
- <td colspan='2' width='50%' align='center' class='diff-otitle'>{$otitle}</td>
- <td colspan='2' width='50%' align='center' class='diff-ntitle'>{$ntitle}</td>
+ <td colspan='2' class='diff-otitle'>{$otitle}</td>
+ <td colspan='2' class='diff-ntitle'>{$ntitle}</td>
</tr>
";
@@ -530,16 +592,15 @@ CONTROL;
}
// Load the new revision object
- if( $this->mNewid ) {
- $this->mNewRev = Revision::newFromId( $this->mNewid );
- } else {
- $this->mNewRev = Revision::newFromTitle( $this->mTitle );
- }
-
- if( is_null( $this->mNewRev ) ) {
+ $this->mNewRev = $this->mNewid
+ ? Revision::newFromId( $this->mNewid )
+ : Revision::newFromTitle( $this->mTitle );
+ if( !$this->mNewRev instanceof Revision )
return false;
- }
-
+
+ // Update the new revision ID in case it was 0 (makes life easier doing UI stuff)
+ $this->mNewid = $this->mNewRev->getId();
+
// Set assorted variables
$timestamp = $wgLang->timeanddate( $this->mNewRev->getTimestamp(), true );
$this->mNewPage = $this->mNewRev->getTitle();
@@ -588,7 +649,8 @@ CONTROL;
$oldEdit = $this->mOldPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mOldid );
$this->mOldtitle = "<a href='$oldLink'>" . htmlspecialchars( wfMsg( 'revisionasof', $t ) )
. "</a> (<a href='$oldEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
- //now that we considered old rev, we can make undo link (bug 8133, multi-edit undo)
+
+ // Add an "undo" link
$newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undoafter=' . $this->mOldid . '&undo=' . $this->mNewid);
$this->mNewtitle .= " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
}
@@ -1721,8 +1783,8 @@ class TableDiffFormatter extends DiffFormatter
}
function _block_header( $xbeg, $xlen, $ybeg, $ylen ) {
- $r = '<tr><td colspan="2" align="left"><strong><!--LINE '.$xbeg."--></strong></td>\n" .
- '<td colspan="2" align="left"><strong><!--LINE '.$ybeg."--></strong></td></tr>\n";
+ $r = '<tr><td colspan="2" class="diff-lineno"><!--LINE '.$xbeg."--></td>\n" .
+ '<td colspan="2" class="diff-lineno"><!--LINE '.$ybeg."--></td></tr>\n";
return $r;
}
@@ -1738,17 +1800,25 @@ class TableDiffFormatter extends DiffFormatter
# HTML-escape parameter before calling this
function addedLine( $line ) {
- return "<td>+</td><td class='diff-addedline'>{$line}</td>";
+ return $this->wrapLine( '+', 'diff-addedline', $line );
}
# HTML-escape parameter before calling this
function deletedLine( $line ) {
- return "<td>-</td><td class='diff-deletedline'>{$line}</td>";
+ return $this->wrapLine( '-', 'diff-deletedline', $line );
}
# HTML-escape parameter before calling this
function contextLine( $line ) {
- return "<td> </td><td class='diff-context'>{$line}</td>";
+ return $this->wrapLine( ' ', 'diff-context', $line );
+ }
+
+ private function wrapLine( $marker, $class, $line ) {
+ if( $line !== '' ) {
+ // The <div> wrapper is needed for 'overflow: auto' style to scroll properly
+ $line = "<div>$line</div>";
+ }
+ return "<td class='diff-marker'>$marker</td><td class='$class'>$line</td>";
}
function emptyLine() {
@@ -1801,4 +1871,5 @@ class TableDiffFormatter extends DiffFormatter
}
}
-?>
+
+
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index 1e423565..b48aaffd 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -104,7 +104,9 @@ class DjVuImage {
}
function getInfo() {
+ wfSuppressWarnings();
$file = fopen( $this->mFilename, 'rb' );
+ wfRestoreWarnings();
if( $file === false ) {
wfDebug( __METHOD__ . ": missing or failed file read\n" );
return false;
diff --git a/includes/EditPage.php b/includes/EditPage.php
index bec6e300..cceb053d 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -20,6 +20,7 @@ class EditPage {
var $firsttime;
var $lastDelete;
var $mTokenOk = false;
+ var $mTokenOkExceptSuffix = false;
var $mTriedSave = false;
var $tooBig = false;
var $kblength = false;
@@ -97,6 +98,11 @@ class EditPage {
$text = $this->mArticle->getContent();
+ if ($undo > 0 && $undoafter > 0 && $undo < $undoafter) {
+ # If they got undoafter and undo round the wrong way, switch them
+ list( $undo, $undoafter ) = array( $undoafter, $undo );
+ }
+
if ( $undo > 0 && $undo > $undoafter ) {
# Undoing a specific edit overrides section editing; section-editing
# doesn't work with undoing.
@@ -292,7 +298,6 @@ class EditPage {
*/
function edit() {
global $wgOut, $wgUser, $wgRequest, $wgTitle;
- global $wgEmailConfirmToEdit;
if ( ! wfRunHooks( 'AlternateEdit', array( &$this ) ) )
return;
@@ -313,57 +318,38 @@ class EditPage {
return;
}
- if ( ! $this->mTitle->userCan( 'edit' ) ) {
- wfDebug( "$fname: user can't edit\n" );
- $wgOut->readOnlyPage( $this->getContent(), true );
- wfProfileOut( $fname );
- return;
- }
- wfDebug( "$fname: Checking blocks\n" );
- if ( !$this->preview && !$this->diff && $wgUser->isBlockedFrom( $this->mTitle, !$this->save ) ) {
- # When previewing, don't check blocked state - will get caught at save time.
- # Also, check when starting edition is done against slave to improve performance.
- wfDebug( "$fname: user is blocked\n" );
- $this->blockedPage();
- wfProfileOut( $fname );
- return;
- }
- if ( !$wgUser->isAllowed('edit') ) {
- if ( $wgUser->isAnon() ) {
- wfDebug( "$fname: user must log in\n" );
- $this->userNotLoggedInPage();
- wfProfileOut( $fname );
- return;
- } else {
- wfDebug( "$fname: read-only page\n" );
- $wgOut->readOnlyPage( $this->getContent(), true );
- wfProfileOut( $fname );
- return;
+ $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser);
+ if( !$this->mTitle->exists() )
+ $permErrors += $this->mTitle->getUserPermissionsErrors( 'create', $wgUser);
+
+ # Ignore some permissions errors.
+ $remove = array();
+ foreach( $permErrors as $error ) {
+ if ($this->preview || $this->diff &&
+ ($error[0] == 'blockedtext' || $error[0] == 'autoblockedtext'))
+ {
+ // Don't worry about blocks when previewing/diffing
+ $remove[] = $error;
+ }
+
+ if ($error[0] == 'readonlytext')
+ {
+ if ($this->edit) {
+ $this->formtype = 'preview';
+ } elseif ($this->save || $this->preview || $this->diff) {
+ $remove[] = $error;
+ }
}
}
- if ($wgEmailConfirmToEdit && !$wgUser->isEmailConfirmed()) {
- wfDebug("$fname: user must confirm e-mail address\n");
- $this->userNotConfirmedPage();
- wfProfileOut($fname);
- return;
- }
- if ( !$this->mTitle->userCan( 'create' ) && !$this->mTitle->exists() ) {
- wfDebug( "$fname: no create permission\n" );
- $this->noCreatePermission();
+ # array_diff returns elements in $permErrors that are not in $remove.
+ $permErrors = array_diff( $permErrors, $remove );
+
+ if ( !empty($permErrors) )
+ {
+ wfDebug( "$fname: User can't edit\n" );
+ $wgOut->readOnlyPage( $this->getContent(), true, $permErrors );
wfProfileOut( $fname );
return;
- }
- if ( wfReadOnly() ) {
- wfDebug( "$fname: read-only mode is engaged\n" );
- if( $this->save || $this->preview ) {
- $this->formtype = 'preview';
- } else if ( $this->diff ) {
- $this->formtype = 'diff';
- } else {
- $wgOut->readOnlyPage( $this->getContent() );
- wfProfileOut( $fname );
- return;
- }
} else {
if ( $this->save ) {
$this->formtype = 'save';
@@ -410,9 +396,10 @@ class EditPage {
}
}
- if(!$this->mTitle->getArticleID() && ('initial' == $this->formtype || $this->firsttime )) { # new article
+ # Show applicable editing introductions
+ if( $this->formtype == 'initial' || $this->firsttime )
$this->showIntro();
- }
+
if( $this->mTitle->isTalkPage() ) {
$wgOut->addWikiText( wfMsg( 'talkpagetext' ) );
}
@@ -449,17 +436,30 @@ class EditPage {
}
/**
- * Return true if this page should be previewed when the edit form
- * is initially opened.
+ * Should we show a preview when the edit form is first shown?
+ *
* @return bool
- * @private
*/
- function previewOnOpen() {
- global $wgUser;
- return $this->section != 'new' &&
- ( ( $wgUser->getOption( 'previewonfirst' ) && $this->mTitle->exists() ) ||
- ( $this->mTitle->getNamespace() == NS_CATEGORY &&
- !$this->mTitle->exists() ) );
+ private function previewOnOpen() {
+ global $wgRequest, $wgUser;
+ if( $wgRequest->getVal( 'preview' ) == 'yes' ) {
+ // Explicit override from request
+ return true;
+ } elseif( $wgRequest->getVal( 'preview' ) == 'no' ) {
+ // Explicit override from request
+ return false;
+ } elseif( $this->section == 'new' ) {
+ // Nothing *to* preview for new sections
+ return false;
+ } elseif( ( $wgRequest->getVal( 'preload' ) !== '' || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
+ // Standard preference behaviour
+ return true;
+ } elseif( !$this->mTitle->exists() && $this->mTitle->getNamespace() == NS_CATEGORY ) {
+ // Categories are special
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -547,9 +547,10 @@ class EditPage {
$this->summary = '';
$this->edittime = '';
$this->starttime = wfTimestampNow();
+ $this->edit = false;
$this->preview = false;
$this->save = false;
- $this->diff = false;
+ $this->diff = false;
$this->minoredit = false;
$this->watchthis = false;
$this->recreate = false;
@@ -575,35 +576,45 @@ class EditPage {
*/
function tokenOk( &$request ) {
global $wgUser;
- if( $wgUser->isAnon() ) {
- # Anonymous users may not have a session
- # open. Check for suffix anyway.
- $this->mTokenOk = ( EDIT_TOKEN_SUFFIX == $request->getVal( 'wpEditToken' ) );
- } else {
- $this->mTokenOk = $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
- }
+ $token = $request->getVal( 'wpEditToken' );
+ $this->mTokenOk = $wgUser->matchEditToken( $token );
+ $this->mTokenOkExceptSuffix = $wgUser->matchEditTokenNoSuffix( $token );
return $this->mTokenOk;
}
- /** */
- function showIntro() {
+ /**
+ * Show all applicable editing introductions
+ */
+ private function showIntro() {
global $wgOut, $wgUser;
- $addstandardintro=true;
- if($this->editintro) {
- $introtitle=Title::newFromText($this->editintro);
- if(isset($introtitle) && $introtitle->userCanRead()) {
- $rev=Revision::newFromTitle($introtitle);
- if($rev) {
- $wgOut->addSecondaryWikiText($rev->getText());
- $addstandardintro=false;
- }
- }
- }
- if($addstandardintro) {
- if ( $wgUser->isLoggedIn() )
+ if( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
+ if( $wgUser->isLoggedIn() ) {
$wgOut->addWikiText( wfMsg( 'newarticletext' ) );
- else
+ } else {
$wgOut->addWikiText( wfMsg( 'newarticletextanon' ) );
+ }
+ $this->showDeletionLog( $wgOut );
+ }
+ }
+
+ /**
+ * Attempt to show a custom editing introduction, if supplied
+ *
+ * @return bool
+ */
+ private function showCustomIntro() {
+ if( $this->editintro ) {
+ $title = Title::newFromText( $this->editintro );
+ if( $title instanceof Title && $title->exists() && $title->userCanRead() ) {
+ global $wgOut;
+ $revision = Revision::newFromTitle( $title );
+ $wgOut->addSecondaryWikiText( $revision->getText() );
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
}
}
@@ -762,7 +773,7 @@ class EditPage {
if ( $this->isConflict) {
wfDebug( "EditPage::editForm conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
- $this->mArticle->getTimestamp() . "'\n" );
+ $this->mArticle->getTimestamp() . "')\n" );
$text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime);
}
else {
@@ -777,7 +788,7 @@ class EditPage {
# Suppress edit conflict with self, except for section edits where merging is required.
if ( ( $this->section == '' ) && ( 0 != $userid ) && ( $this->mArticle->getUser() == $userid ) ) {
- wfDebug( "Suppressing edit conflict, same user.\n" );
+ wfDebug( "EditPage::editForm Suppressing edit conflict, same user.\n" );
$this->isConflict = false;
} else {
# switch from section editing to normal editing in edit conflict
@@ -786,11 +797,11 @@ class EditPage {
if( $this->mergeChangesInto( $text ) ){
// Successful merge! Maybe we should tell the user the good news?
$this->isConflict = false;
- wfDebug( "Suppressing edit conflict, successful merge.\n" );
+ wfDebug( "EditPage::editForm Suppressing edit conflict, successful merge.\n" );
} else {
$this->section = '';
$this->textbox1 = $text;
- wfDebug( "Keeping edit conflict, failed merge.\n" );
+ wfDebug( "EditPage::editForm Keeping edit conflict, failed merge.\n" );
}
}
}
@@ -831,6 +842,10 @@ class EditPage {
}
if( $this->summary != '' ) {
$sectionanchor = $this->sectionAnchor( $this->summary );
+ # This is a new section, so create a link to the new section
+ # in the revision summary.
+ $this->summary = wfMsgForContent('newsectionsummary') .
+ " [[{$this->mTitle->getPrefixedText()}#{$this->summary}|{$this->summary}]]";
}
} elseif( $this->section != '' ) {
# Try to get a section anchor from the section source, redirect to edited section if header found
@@ -909,6 +924,10 @@ class EditPage {
# Enabled article-related sidebar, toplinks, etc.
$wgOut->setArticleRelated( true );
+ if ( $this->formtype == 'preview' ) {
+ $wgOut->setPageTitleActionText( wfMsg( 'preview' ) );
+ }
+
if ( $this->isConflict ) {
$s = wfMsg( 'editconflict', $this->mTitle->getPrefixedText() );
$wgOut->setPageTitle( $s );
@@ -1002,13 +1021,14 @@ 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();
if ( count($cascadeSources) > 0 ) {
# Explain, and list the titles responsible
$notice = wfMsgExt( 'cascadeprotectedwarning', array('parsemag'), count($cascadeSources) ) . "\n";
- foreach( $cascadeSources as $id => $page )
+ foreach( $cascadeSources as $page ) {
$notice .= '* [[:' . $page->getPrefixedText() . "]]\n";
}
+ }
$wgOut->addWikiText( $notice );
}
@@ -1089,7 +1109,7 @@ class EditPage {
}
if ( 'diff' == $this->formtype ) {
- $wgOut->addHTML( $this->getDiff() );
+ $this->showDiff();
}
}
@@ -1115,7 +1135,7 @@ class EditPage {
if( !$this->preview && !$this->diff ) {
$wgOut->setOnloadHandler( 'document.editform.wpTextbox1.focus()' );
}
- $templates = ($this->preview || $this->section) ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates();
+ $templates = ($this->preview || $this->section != '') ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates();
$formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
global $wgUseMetadataEdit ;
@@ -1229,10 +1249,7 @@ END
* include the constant suffix to prevent editing from
* broken text-mangling proxies.
*/
- if ( $wgUser->isLoggedIn() )
- $token = htmlspecialchars( $wgUser->editToken() );
- else
- $token = EDIT_TOKEN_SUFFIX;
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "\n<input type='hidden' value=\"$token\" name=\"wpEditToken\" />\n" );
@@ -1271,7 +1288,7 @@ END
}
if ( $this->formtype == 'diff') {
- $wgOut->addHTML( $this->getDiff() );
+ $this->showDiff();
}
}
@@ -1292,6 +1309,7 @@ END
if($this->mTitle->getNamespace() == NS_CATEGORY) {
$this->mArticle->openShowCategory();
}
+ wfRunHooks( 'OutputPageBeforeHTML',array( &$wgOut, &$text ) );
$wgOut->addHTML( $text );
if($this->mTitle->getNamespace() == NS_CATEGORY) {
$this->mArticle->closeShowCategory();
@@ -1363,7 +1381,11 @@ END
wfProfileIn( $fname );
if ( $this->mTriedSave && !$this->mTokenOk ) {
- $msg = 'session_fail_preview';
+ if ( $this->mTokenOkExceptSuffix ) {
+ $msg = 'token_suffix_mismatch';
+ } else {
+ $msg = 'session_fail_preview';
+ }
} else {
$msg = 'previewnote';
}
@@ -1414,6 +1436,9 @@ END
$previewHTML = $parserOutput->getText();
$wgOut->addParserOutputNoText( $parserOutput );
+
+ # ParserOutput might have altered the page title, so reset it
+ $wgOut->setPageTitle( wfMsg( 'editing', $this->mTitle->getPrefixedText() ) );
foreach ( $parserOutput->getTemplates() as $ns => $template)
foreach ( array_keys( $template ) as $dbk)
@@ -1497,7 +1522,7 @@ END
$wgOut->setArticleRelated( false );
$wgOut->addWikiText( wfMsg( 'nosuchsectiontext', $this->section ) );
- $wgOut->returnToMain( false );
+ $wgOut->returnToMain( false, $this->mTitle->getPrefixedUrl() );
}
/**
@@ -1893,10 +1918,8 @@ END
*
* If this is a section edit, we'll replace the section as for final
* save and then make a comparison.
- *
- * @return string HTML
*/
- function getDiff() {
+ function showDiff() {
$oldtext = $this->mArticle->fetchContent();
$newtext = $this->mArticle->replaceSection(
$this->section, $this->textbox1, $this->summary, $this->edittime );
@@ -1907,11 +1930,13 @@ END
$de = new DifferenceEngine( $this->mTitle );
$de->setText( $oldtext, $newtext );
$difftext = $de->getDiff( $oldtitle, $newtitle );
+ $de->showDiffStyle();
} else {
$difftext = '';
}
- return '<div id="wikiDiff">' . $difftext . '</div>';
+ global $wgOut;
+ $wgOut->addHtml( '<div id="wikiDiff">' . $difftext . '</div>' );
}
/**
@@ -2034,7 +2059,32 @@ END
$wgOut->setPageTitle( wfMsg( 'nocreatetitle' ) );
$wgOut->addWikiText( wfMsg( 'nocreatetext' ) );
}
-
+
+ /**
+ * If there are rows in the deletion log for this page, show them,
+ * along with a nice little note for the user
+ *
+ * @param OutputPage $out
+ */
+ private function showDeletionLog( $out ) {
+ $title = $this->mArticle->getTitle();
+ $reader = new LogReader(
+ new FauxRequest(
+ array(
+ 'page' => $title->getPrefixedText(),
+ 'type' => 'delete',
+ )
+ )
+ );
+ if( $reader->hasRows() ) {
+ $out->addHtml( '<div id="mw-recreate-deleted-warn">' );
+ $out->addWikiText( wfMsg( 'recreate-deleted-warn' ) );
+ $viewer = new LogViewer( $reader );
+ $viewer->showList( $out );
+ $out->addHtml( '</div>' );
+ }
+ }
+
}
-?>
+
diff --git a/includes/EmaillingJob.php b/includes/EmaillingJob.php
new file mode 100644
index 00000000..73d71c56
--- /dev/null
+++ b/includes/EmaillingJob.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Old job used for sending single notification emails;
+ * kept for backwards-compatibility
+ */
+class EmaillingJob extends Job {
+
+ function __construct( $title, $params, $id = 0 ) {
+ parent::__construct( 'sendMail', Title::newMainPage(), $params, $id );
+ }
+
+ function run() {
+ userMailer(
+ $this->params['to'],
+ $this->params['from'],
+ $this->params['subj'],
+ $this->params['body'],
+ $this->params['replyto']
+ );
+ return true;
+ }
+
+}
+
diff --git a/includes/EnotifNotifyJob.php b/includes/EnotifNotifyJob.php
new file mode 100644
index 00000000..70d1de69
--- /dev/null
+++ b/includes/EnotifNotifyJob.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Job for email notification mails
+ */
+class EnotifNotifyJob extends Job {
+
+ function __construct( $title, $params, $id = 0 ) {
+ parent::__construct( 'enotifNotify', $title, $params, $id );
+ }
+
+ function run() {
+ $enotif = new EmailNotification();
+ $enotif->actuallyNotifyOnPageChange(
+ User::newFromName( $this->params['editor'], false ),
+ $this->title,
+ $this->params['timestamp'],
+ $this->params['summary'],
+ $this->params['minorEdit'],
+ $this->params['oldid']
+ );
+ return true;
+ }
+
+}
+
diff --git a/includes/Exception.php b/includes/Exception.php
index 4cf0b7ba..02819cc9 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -235,4 +235,4 @@ function wfExceptionHandler( $e ) {
exit( 1 );
}
-?>
+
diff --git a/includes/Exif.php b/includes/Exif.php
index 3a06ca1b..d98a8e0d 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -405,7 +405,7 @@ class Exif {
*
* @return int
*/
- function version() {
+ public static function version() {
return 1; // We don't need no bloddy constants!
}
@@ -1131,4 +1131,4 @@ define( 'MW_EXIF_UNDEFINED', Exif::UNDEFINED );
define( 'MW_EXIF_SLONG', Exif::SLONG );
define( 'MW_EXIF_SRATIONAL', Exif::SRATIONAL );
-?>
+
diff --git a/includes/Export.php b/includes/Export.php
index 9307795d..c3ef9451 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -558,7 +558,7 @@ class Dump7ZipOutput extends DumpPipeOutput {
$command = "7za a -bd -si " . wfEscapeShellArg( $file );
// Suppress annoying useless crap from p7zip
// Unfortunately this could suppress real error messages too
- $command .= " >/dev/null 2>&1";
+ $command .= ' >' . wfGetNull() . ' 2>&1';
parent::DumpPipeOutput( $command );
}
}
@@ -767,4 +767,4 @@ function xmlsafe( $string ) {
return $string;
}
-?>
+
diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php
index c8ed8bde..f3fc22e3 100644
--- a/includes/ExternalEdit.php
+++ b/includes/ExternalEdit.php
@@ -46,7 +46,7 @@ class ExternalEdit {
$extension="wiki";
} elseif($this->mMode=="file") {
$type="Edit file";
- $image = new Image( $this->mTitle );
+ $image = wfLocalFile( $this->mTitle );
$img_url = $image->getURL();
if(strpos($img_url,"://")) {
$url = $img_url;
@@ -72,4 +72,4 @@ CONTROL;
echo $control;
}
}
-?>
+
diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php
index fb66b652..5efc6e25 100644
--- a/includes/ExternalStore.php
+++ b/includes/ExternalStore.php
@@ -41,10 +41,9 @@ class ExternalStore {
return false;
$class='ExternalStore'.ucfirst($proto);
- /* Preloaded modules might exist, especially ones serving multiple protocols */
+ /* Any custom modules should be added to $wgAutoLoadClasses for on-demand loading */
if (!class_exists($class)) {
- if (!include_once($class.'.php'))
- return false;
+ return false;
}
$store=new $class();
return $store;
@@ -66,4 +65,4 @@ class ExternalStore {
}
}
}
-?>
+
diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php
index 7b4ffc2f..f9046f74 100644
--- a/includes/ExternalStoreDB.php
+++ b/includes/ExternalStoreDB.php
@@ -144,4 +144,4 @@ class ExternalStoreDB {
return "DB://$cluster/$id";
}
}
-?>
+
diff --git a/includes/ExternalStoreHttp.php b/includes/ExternalStoreHttp.php
index e6656986..cff6c4d4 100644
--- a/includes/ExternalStoreHttp.php
+++ b/includes/ExternalStoreHttp.php
@@ -19,4 +19,4 @@ class ExternalStoreHttp {
* whatever, for initial ext storage
*/
}
-?>
+
diff --git a/includes/FakeTitle.php b/includes/FakeTitle.php
index 293bdaf0..b63ae505 100644
--- a/includes/FakeTitle.php
+++ b/includes/FakeTitle.php
@@ -84,4 +84,4 @@ class FakeTitle {
function trackbackRDF() { $this->error(); }
}
-?>
+
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
new file mode 100644
index 00000000..ee165cd1
--- /dev/null
+++ b/includes/FileDeleteForm.php
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * File deletion user interface
+ *
+ * @addtogroup Media
+ * @author Rob Church <robchur@gmail.com>
+ */
+class FileDeleteForm {
+
+ private $title = null;
+ private $file = null;
+
+ private $oldfile = null;
+ private $oldimage = '';
+
+ /**
+ * Constructor
+ *
+ * @param File $file File we're deleting
+ */
+ public function __construct( $file ) {
+ $this->title = $file->getTitle();
+ $this->file = $file;
+ }
+
+ /**
+ * Fulfil the request; shows the form or deletes the file,
+ * pending authentication, confirmation, etc.
+ */
+ public function execute() {
+ global $wgOut, $wgRequest, $wgUser;
+ $this->setHeaders();
+
+ if( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ } elseif( !$wgUser->isLoggedIn() ) {
+ $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
+ return;
+ } elseif( !$wgUser->isAllowed( 'delete' ) ) {
+ $wgOut->permissionError( 'delete' );
+ return;
+ } elseif( $wgUser->isBlocked() ) {
+ $wgOut->blockedPage();
+ return;
+ }
+
+ $this->oldimage = $wgRequest->getText( 'oldimage', false );
+ $token = $wgRequest->getText( 'wpEditToken' );
+ if( $this->oldimage && !$this->isValidOldSpec() ) {
+ $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->oldimage ) );
+ return;
+ }
+ if( $this->oldimage )
+ $this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
+
+ if( !$this->haveDeletableFile() ) {
+ $wgOut->addHtml( $this->prepareMessage( 'filedelete-nofile' ) );
+ $wgOut->addReturnTo( $this->title );
+ return;
+ }
+
+ // Perform the deletion if appropriate
+ if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
+ $comment = $wgRequest->getText( 'wpReason' );
+ if( $this->oldimage ) {
+ $status = $this->file->deleteOld( $this->oldimage, $comment );
+ if( $status->ok ) {
+ // Need to do a log item
+ $log = new LogPage( 'delete' );
+ $log->addEntry( 'delete', $this->title, wfMsg( 'deletedrevision' , $this->oldimage ) );
+ }
+ } else {
+ $status = $this->file->delete( $comment );
+ if( $status->ok ) {
+ // Need to delete the associated article
+ $article = new Article( $this->title );
+ $article->doDeleteArticle( $comment );
+ }
+ }
+ if( !$status->isGood() )
+ $wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) );
+ if( $status->ok ) {
+ $wgOut->addHtml( $this->prepareMessage( 'filedelete-success' ) );
+ // Return to the main page if we just deleted all versions of the
+ // file, otherwise go back to the description page
+ $wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() );
+ }
+ return;
+ }
+
+ $this->showForm();
+ $this->showLogEntries();
+ }
+
+ /**
+ * Show the confirmation form
+ */
+ private function showForm() {
+ global $wgOut, $wgUser, $wgRequest;
+
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) );
+ $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->oldimage ) );
+ $form .= '<fieldset><legend>' . wfMsgHtml( 'filedelete-legend' ) . '</legend>';
+ $form .= $this->prepareMessage( 'filedelete-intro' );
+
+ $form .= '<p>' . Xml::inputLabel( wfMsg( 'filedelete-comment' ), 'wpReason', 'wpReason',
+ 60, $wgRequest->getText( 'wpReason' ) ) . '</p>';
+ $form .= '<p>' . Xml::submitButton( wfMsg( 'filedelete-submit' ) ) . '</p>';
+ $form .= '</fieldset>';
+ $form .= '</form>';
+
+ $wgOut->addHtml( $form );
+ }
+
+ /**
+ * Show deletion log fragments pertaining to the current file
+ */
+ private function showLogEntries() {
+ global $wgOut;
+ $wgOut->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ $reader = new LogViewer(
+ new LogReader(
+ new FauxRequest(
+ array(
+ 'type' => 'delete',
+ 'page' => $this->title->getPrefixedText(),
+ )
+ )
+ )
+ );
+ $reader->showList( $wgOut );
+ }
+
+ /**
+ * Prepare a message referring to the file being deleted,
+ * showing an appropriate message depending upon whether
+ * it's a current file or an old version
+ *
+ * @param string $message Message base
+ * @return string
+ */
+ private function prepareMessage( $message ) {
+ global $wgLang, $wgServer;
+ if( $this->oldimage ) {
+ return wfMsgExt(
+ "{$message}-old",
+ 'parse',
+ $this->title->getText(),
+ $wgLang->date( $this->getTimestamp(), true ),
+ $wgLang->time( $this->getTimestamp(), true ),
+ $wgServer . $this->file->getArchiveUrl( $this->oldimage )
+ );
+ } else {
+ return wfMsgExt(
+ $message,
+ 'parse',
+ $this->title->getText()
+ );
+ }
+ }
+
+ /**
+ * Set headers, titles and other bits
+ */
+ private function setHeaders() {
+ global $wgOut, $wgUser;
+ $wgOut->setPageTitle( wfMsg( 'filedelete', $this->title->getText() ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->setSubtitle( wfMsg( 'filedelete-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->title ) ) );
+ }
+
+ /**
+ * Is the provided `oldimage` value valid?
+ *
+ * @return bool
+ */
+ private function isValidOldSpec() {
+ return strlen( $this->oldimage ) >= 16
+ && strpos( $this->oldimage, '/' ) === false
+ && strpos( $this->oldimage, '\\' ) === false;
+ }
+
+ /**
+ * Could we delete the file specified? If an `oldimage`
+ * value was provided, does it correspond to an
+ * existing, local, old version of this file?
+ *
+ * @return bool
+ */
+ private function haveDeletableFile() {
+ return $this->oldimage
+ ? $this->oldfile && $this->oldfile->exists() && $this->oldfile->isLocal()
+ : $this->file && $this->file->exists() && $this->file->isLocal();
+ }
+
+ /**
+ * Prepare the form action
+ *
+ * @return string
+ */
+ private function getAction() {
+ $q = array();
+ $q[] = 'action=delete';
+ if( $this->oldimage )
+ $q[] = 'oldimage=' . urlencode( $this->oldimage );
+ return $this->title->getLocalUrl( implode( '&', $q ) );
+ }
+
+ /**
+ * Extract the timestamp of the old version
+ *
+ * @return string
+ */
+ private function getTimestamp() {
+ return $this->oldfile->getTimestamp();
+ }
+
+} \ No newline at end of file
diff --git a/includes/FileRevertForm.php b/includes/FileRevertForm.php
new file mode 100644
index 00000000..55f21fff
--- /dev/null
+++ b/includes/FileRevertForm.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * File reversion user interface
+ *
+ * @addtogroup Media
+ * @author Rob Church <robchur@gmail.com>
+ */
+class FileRevertForm {
+
+ private $title = null;
+ private $file = null;
+ private $oldimage = '';
+ private $timestamp = false;
+
+ /**
+ * Constructor
+ *
+ * @param File $file File we're reverting
+ */
+ public function __construct( $file ) {
+ $this->title = $file->getTitle();
+ $this->file = $file;
+ }
+
+ /**
+ * Fulfil the request; shows the form or reverts the file,
+ * pending authentication, confirmation, etc.
+ */
+ public function execute() {
+ global $wgOut, $wgRequest, $wgUser, $wgLang, $wgServer;
+ $this->setHeaders();
+
+ if( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ } elseif( !$wgUser->isLoggedIn() ) {
+ $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
+ return;
+ } elseif( !$this->title->userCan( 'edit' ) ) {
+ // The standard read-only thing doesn't make a whole lot of sense
+ // here; surely it should show the image or something? -- RC
+ $article = new Article( $this->title );
+ $wgOut->readOnlyPage( $article->getContent(), true );
+ return;
+ } elseif( $wgUser->isBlocked() ) {
+ $wgOut->blockedPage();
+ return;
+ }
+
+ $this->oldimage = $wgRequest->getText( 'oldimage' );
+ $token = $wgRequest->getText( 'wpEditToken' );
+ if( !$this->isValidOldSpec() ) {
+ $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->oldimage ) );
+ return;
+ }
+
+ if( !$this->haveOldVersion() ) {
+ $wgOut->addHtml( wfMsgExt( 'filerevert-badversion', 'parse' ) );
+ $wgOut->returnToMain( false, $this->title );
+ return;
+ }
+
+ // Perform the reversion if appropriate
+ if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
+ $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
+ $comment = $wgRequest->getText( 'wpComment' );
+ // TODO: Preserve file properties from database instead of reloading from file
+ $status = $this->file->upload( $source, $comment, $comment );
+ if( $status->isGood() ) {
+ $wgOut->addHtml( wfMsgExt( 'filerevert-success', 'parse', $this->title->getText(),
+ $wgLang->date( $this->getTimestamp(), true ),
+ $wgLang->time( $this->getTimestamp(), true ),
+ $wgServer . $this->file->getArchiveUrl( $this->oldimage ) ) );
+ $wgOut->returnToMain( false, $this->title );
+ } else {
+ $wgOut->addWikiText( $status->getWikiText() );
+ }
+ return;
+ }
+
+ // Show the form
+ $this->showForm();
+ }
+
+ /**
+ * Show the confirmation form
+ */
+ private function showForm() {
+ global $wgOut, $wgUser, $wgRequest, $wgLang, $wgContLang, $wgServer;
+ $timestamp = $this->getTimestamp();
+
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) );
+ $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->oldimage ) );
+ $form .= '<fieldset><legend>' . wfMsgHtml( 'filerevert-legend' ) . '</legend>';
+ $form .= wfMsgExt( 'filerevert-intro', 'parse', $this->title->getText(),
+ $wgLang->date( $timestamp, true ), $wgLang->time( $timestamp, true ), $wgServer . $this->file->getArchiveUrl( $this->oldimage ) );
+ $form .= '<p>' . Xml::inputLabel( wfMsg( 'filerevert-comment' ), 'wpComment', 'wpComment',
+ 60, wfMsgForContent( 'filerevert-defaultcomment',
+ $wgContLang->date( $timestamp, false, false ), $wgContLang->time( $timestamp, false, false ) ) ) . '</p>';
+ $form .= '<p>' . Xml::submitButton( wfMsg( 'filerevert-submit' ) ) . '</p>';
+ $form .= '</fieldset>';
+ $form .= '</form>';
+
+ $wgOut->addHtml( $form );
+ }
+
+ /**
+ * Set headers, titles and other bits
+ */
+ private function setHeaders() {
+ global $wgOut, $wgUser;
+ $wgOut->setPageTitle( wfMsg( 'filerevert', $this->title->getText() ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->setSubtitle( wfMsg( 'filerevert-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->title ) ) );
+ }
+
+ /**
+ * Is the provided `oldimage` value valid?
+ *
+ * @return bool
+ */
+ private function isValidOldSpec() {
+ return strlen( $this->oldimage ) >= 16
+ && strpos( $this->oldimage, '/' ) === false
+ && strpos( $this->oldimage, '\\' ) === false;
+ }
+
+ /**
+ * Does the provided `oldimage` value correspond
+ * to an existing, local, old version of this file?
+ *
+ * @return bool
+ */
+ private function haveOldVersion() {
+ $file = wfFindFile( $this->title, $this->oldimage );
+ return $file && $file->exists() && $file->isLocal();
+ }
+
+ /**
+ * Prepare the form action
+ *
+ * @return string
+ */
+ private function getAction() {
+ $q = array();
+ $q[] = 'action=revert';
+ $q[] = 'oldimage=' . urlencode( $this->oldimage );
+ return $this->title->getLocalUrl( implode( '&', $q ) );
+ }
+
+ /**
+ * Extract the timestamp of the old version
+ *
+ * @return string
+ */
+ private function getTimestamp() {
+ if( $this->timestamp === false ) {
+ $file = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
+ $this->timestamp = $file->getTimestamp();
+ }
+ return $this->timestamp;
+ }
+
+} \ No newline at end of file
diff --git a/includes/FileStore.php b/includes/FileStore.php
index dcec71c5..1554d66e 100644
--- a/includes/FileStore.php
+++ b/includes/FileStore.php
@@ -219,7 +219,7 @@ class FileStore {
* Confirm that the given file key is valid.
* Note that a valid key may refer to a file that does not exist.
*
- * Key should consist of a 32-digit base-36 SHA-1 hash and
+ * Key should consist of a 31-digit base-36 SHA-1 hash and
* an optional alphanumeric extension, all lowercase.
* The whole must not exceed 64 characters.
*
@@ -227,7 +227,7 @@ class FileStore {
* @return boolean
*/
static function validKey( $key ) {
- return preg_match( '/^[0-9a-z]{32}(\.[0-9a-z]{1,31})?$/', $key );
+ return preg_match( '/^[0-9a-z]{31,32}(\.[0-9a-z]{1,31})?$/', $key );
}
@@ -249,7 +249,7 @@ class FileStore {
return false;
}
- $base36 = wfBaseConvert( $hash, 16, 36, 32 );
+ $base36 = wfBaseConvert( $hash, 16, 36, 31 );
if( $extension == '' ) {
$key = $base36;
} else {
@@ -376,4 +376,4 @@ class FSTransaction {
*/
class FSException extends MWException { }
-?>
+
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 1ffde741..67cc1f39 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -429,18 +429,11 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform =
* @param $key String:
*/
function wfMsgWeirdKey ( $key ) {
- $subsource = str_replace ( ' ' , '_' , $key ) ;
- $source = wfMsgForContentNoTrans( $subsource ) ;
- if ( wfEmptyMsg( $subsource, $source) ) {
- # Try again with first char lower case
- $subsource = strtolower ( substr ( $subsource , 0 , 1 ) ) . substr ( $subsource , 1 ) ;
- $source = wfMsgForContentNoTrans( $subsource ) ;
- }
- if ( wfEmptyMsg( $subsource, $source ) ) {
- # Didn't work either, return blank text
- $source = "" ;
- }
- return $source ;
+ $source = wfMsgGetKey( $key, false, true, false );
+ if ( wfEmptyMsg( $key, $source ) )
+ return "";
+ else
+ return $source;
}
/**
@@ -454,6 +447,17 @@ function wfMsgWeirdKey ( $key ) {
function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
global $wgParser, $wgContLang, $wgMessageCache, $wgLang;
+ /* <Vyznev> btw, is all that code in wfMsgGetKey() that check
+ * if the message cache exists of not really necessary, or is
+ * it just paranoia?
+ * <TimStarling> Vyznev: it's probably not necessary
+ * <TimStarling> I think I wrote it in an attempt to report DB
+ * connection errors properly
+ * <TimStarling> but eventually we gave up on using the
+ * message cache for that and just hard-coded the strings
+ * <TimStarling> it may have other uses, it's not mere paranoia
+ */
+
if ( is_object( $wgMessageCache ) )
$transstat = $wgMessageCache->getTransform();
@@ -468,16 +472,18 @@ function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
$lang = &$wgLang;
}
- wfSuppressWarnings();
+ # MessageCache::get() does this already, Language::getMessage() doesn't
+ # ISSUE: Should we try to handle "message/lang" here too?
+ $key = str_replace( ' ' , '_' , $wgContLang->lcfirst( $key ) );
+ wfSuppressWarnings();
if( is_object( $lang ) ) {
$message = $lang->getMessage( $key );
} else {
$message = false;
}
wfRestoreWarnings();
- if($message === false)
- $message = Language::getMessage($key);
+
if ( $transform && strstr( $message, '{{' ) !== false ) {
$message = $wgParser->transformMsg($message, $wgMessageCache->getParserOptions() );
}
@@ -586,7 +592,7 @@ function wfMsgExt( $key, $options ) {
} elseif ( in_array('parseinline', $options) ) {
$string = $wgOut->parse( $string, true, true );
$m = array();
- if( preg_match( "~^<p>(.*)\n?</p>$~", $string, $m ) ) {
+ if( preg_match( '/^<p>(.*)\n?<\/p>$/sU', $string, $m ) ) {
$string = $m[1];
}
} elseif ( in_array('parsemag', $options) ) {
@@ -695,14 +701,14 @@ function wfHostname() {
* @return string
*/
function wfReportTime() {
- global $wgRequestTime;
+ global $wgRequestTime, $wgShowHostnames;
$now = wfTime();
$elapsed = $now - $wgRequestTime;
- $com = sprintf( "<!-- Served by %s in %01.3f secs. -->",
- wfHostname(), $elapsed );
- return $com;
+ return $wgShowHostnames
+ ? sprintf( "<!-- Served by %s in %01.3f secs. -->", wfHostname(), $elapsed )
+ : sprintf( "<!-- Served in %01.3f secs. -->", $elapsed );
}
/**
@@ -813,7 +819,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
if ( $po < 0 ) { $po = 0; }
$q = "limit={$limit}&offset={$po}";
if ( '' != $query ) { $q .= '&'.$query; }
- $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$prev}</a>";
+ $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-prevlink\">{$prev}</a>";
} else { $plink = $prev; }
$no = $offset + $limit;
@@ -823,7 +829,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
if ( $atend ) {
$nlink = $next;
} else {
- $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$next}</a>";
+ $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-nextlink\">{$next}</a>";
}
$nums = wfNumLink( $offset, 20, $title, $query ) . ' | ' .
wfNumLink( $offset, 50, $title, $query ) . ' | ' .
@@ -844,7 +850,7 @@ function wfNumLink( $offset, $limit, &$title, $query = '' ) {
$q .= 'limit='.$limit.'&offset='.$offset;
$fmtLimit = $wgLang->formatNum( $limit );
- $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$fmtLimit}</a>";
+ $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-numlink\">{$fmtLimit}</a>";
return $s;
}
@@ -1657,47 +1663,11 @@ function wfTempDir() {
* Make directory, and make all parent directories if they don't exist
*/
function wfMkdirParents( $fullDir, $mode = 0777 ) {
- if ( strval( $fullDir ) === '' ) {
+ if( strval( $fullDir ) === '' )
return true;
- }
-
- # Go back through the paths to find the first directory that exists
- $currentDir = $fullDir;
- $createList = array();
- while ( strval( $currentDir ) !== '' && !file_exists( $currentDir ) ) {
- # Strip trailing slashes
- $currentDir = rtrim( $currentDir, '/\\' );
-
- # Add to create list
- $createList[] = $currentDir;
-
- # Find next delimiter searching from the end
- $p = max( strrpos( $currentDir, '/' ), strrpos( $currentDir, '\\' ) );
- if ( $p === false ) {
- $currentDir = false;
- } else {
- $currentDir = substr( $currentDir, 0, $p );
- }
- }
-
- if ( count( $createList ) == 0 ) {
- # Directory specified already exists
+ if( file_exists( $fullDir ) )
return true;
- } elseif ( $currentDir === false ) {
- # Went all the way back to root and it apparently doesn't exist
- return false;
- }
-
- # Now go forward creating directories
- $createList = array_reverse( $createList );
- foreach ( $createList as $dir ) {
- # use chmod to override the umask, as suggested by the PHP manual
- if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
- wfDebugLog( 'mkdir', "Unable to create directory $dir\n" );
- return false;
- }
- }
- return true;
+ return mkdir( str_replace( '/', DIRECTORY_SEPARATOR, $fullDir ), $mode, true );
}
/**
@@ -1761,7 +1731,7 @@ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
* @return bool
*/
function wfEmptyMsg( $msg, $wfMsgOut ) {
- return $wfMsgOut === "&lt;$msg&gt;";
+ return $wfMsgOut === htmlspecialchars( "<$msg>" );
}
/**
@@ -1902,11 +1872,15 @@ function wfRegexReplacement( $string ) {
* We'll consider it so always, as we don't want \s in our Unix paths either.
*
* @param string $path
+ * @param string $suffix to remove if present
* @return string
*/
-function wfBaseName( $path ) {
+function wfBaseName( $path, $suffix='' ) {
+ $encSuffix = ($suffix == '')
+ ? ''
+ : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' );
$matches = array();
- if( preg_match( '#([^/\\\\]*)[/\\\\]*$#', $path, $matches ) ) {
+ if( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
return $matches[1];
} else {
return '';
@@ -2266,4 +2240,84 @@ function &wfGetDB( $db = DB_LAST, $groups = array() ) {
$ret = $wgLoadBalancer->getConnection( $db, true, $groups );
return $ret;
}
-?>
+
+/**
+ * Find a file.
+ * Shortcut for RepoGroup::singleton()->findFile()
+ * @param mixed $title Title object or string. May be interwiki.
+ * @param mixed $time Requested time for an archived image, or false for the
+ * current version. An image object will be returned which
+ * existed at or before the specified time.
+ * @return File, or false if the file does not exist
+ */
+function wfFindFile( $title, $time = false ) {
+ return RepoGroup::singleton()->findFile( $title, $time );
+}
+
+/**
+ * Get an object referring to a locally registered file.
+ * Returns a valid placeholder object if the file does not exist.
+ */
+function wfLocalFile( $title ) {
+ return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
+}
+
+/**
+ * Should low-performance queries be disabled?
+ *
+ * @return bool
+ */
+function wfQueriesMustScale() {
+ global $wgMiserMode;
+ return $wgMiserMode
+ || ( SiteStats::pages() > 100000
+ && SiteStats::edits() > 1000000
+ && SiteStats::users() > 10000 );
+}
+
+/**
+ * Get the path to a specified script file, respecting file
+ * extensions; this is a wrapper around $wgScriptExtension etc.
+ *
+ * @param string $script Script filename, sans extension
+ * @return string
+ */
+function wfScript( $script = 'index' ) {
+ global $wgScriptPath, $wgScriptExtension;
+ return "{$wgScriptPath}/{$script}{$wgScriptExtension}";
+}
+
+/**
+ * Convenience function converts boolean values into "true"
+ * or "false" (string) values
+ *
+ * @param bool $value
+ * @return string
+ */
+function wfBoolToStr( $value ) {
+ return $value ? 'true' : 'false';
+}
+
+/**
+ * Load an extension messages file
+ */
+function wfLoadExtensionMessages( $extensionName ) {
+ global $wgExtensionMessagesFiles, $wgMessageCache;
+ if ( !empty( $wgExtensionMessagesFiles[$extensionName] ) ) {
+ $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName] );
+ // Prevent double-loading
+ $wgExtensionMessagesFiles[$extensionName] = false;
+ }
+}
+
+/**
+ * Get a platform-independent path to the null file, e.g.
+ * /dev/null
+ *
+ * @return string
+ */
+function wfGetNull() {
+ return wfIsWindows()
+ ? 'NUL'
+ : '/dev/null';
+} \ No newline at end of file
diff --git a/includes/HTMLCacheUpdate.php b/includes/HTMLCacheUpdate.php
index 9a0b6a08..260439b2 100644
--- a/includes/HTMLCacheUpdate.php
+++ b/includes/HTMLCacheUpdate.php
@@ -67,13 +67,13 @@ class HTMLCacheUpdate
break;
}
}
- if ( $id !== false ) {
- // One less on the end to avoid duplicating the boundary
- $job = new HTMLCacheUpdateJob( $this->mTitle, $this->mTable, $start, $id - 1 );
- } else {
- $job = new HTMLCacheUpdateJob( $this->mTitle, $this->mTable, $start, false );
- }
- $jobs[] = $job;
+
+ $params = array(
+ 'table' => $this->mTable,
+ 'start' => $start,
+ 'end' => ( $id !== false ? $id - 1 : false ),
+ );
+ $jobs[] = new HTMLCacheUpdateJob( $this->mTitle, $params );
$start = $id;
} while ( $start );
@@ -193,20 +193,14 @@ class HTMLCacheUpdateJob extends Job {
/**
* Construct a job
* @param Title $title The title linked to
- * @param string $table The name of the link table.
- * @param integer $start Beginning page_id or false for open interval
- * @param integer $end End page_id or false for open interval
+ * @param array $params Job parameters (table, start and end page_ids)
* @param integer $id job_id
*/
- function __construct( $title, $table, $start, $end, $id = 0 ) {
- $params = array(
- 'table' => $table,
- 'start' => $start,
- 'end' => $end );
+ function __construct( $title, $params, $id = 0 ) {
parent::__construct( 'htmlCacheUpdate', $title, $params, $id );
- $this->table = $table;
- $this->start = intval( $start );
- $this->end = intval( $end );
+ $this->table = $params['table'];
+ $this->start = $params['start'];
+ $this->end = $params['end'];
}
function run() {
@@ -229,4 +223,4 @@ class HTMLCacheUpdateJob extends Job {
return true;
}
}
-?>
+
diff --git a/includes/HTMLFileCache.php b/includes/HTMLFileCache.php
index 1d3778b2..a7466814 100644
--- a/includes/HTMLFileCache.php
+++ b/includes/HTMLFileCache.php
@@ -154,4 +154,4 @@ class HTMLFileCache {
}
-?>
+
diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php
index 715c8c88..69ec1007 100644
--- a/includes/HTMLForm.php
+++ b/includes/HTMLForm.php
@@ -1,7 +1,6 @@
<?php
/**
- * This file contain a class to easily build HTML forms as well as custom
- * functions used by SpecialUserrights.php
+ * This file contain a class to easily build HTML forms
*/
/**
@@ -106,53 +105,3 @@ class HTMLForm {
"<textarea name=\"{$varname}\" rows=\"5\" cols=\"{$size}\">{$s}</textarea>\n";
}
} // end class
-
-/** Build a select with all defined groups
- *
- * used by SpecialUserrights.php
- * @todo move it to there, and don't forget to copy it for SpecialMakesysop.php
- *
- * @param $selectname String: name of this element. Name of form is automaticly prefixed.
- * @param $selectmsg String: FIXME
- * @param $selected Array: array of element selected when posted. Only multiples will show them.
- * @param $multiple Boolean: A multiple elements select.
- * @param $size Integer: number of elements to be shown ignored for non-multiple (default 6).
- * @param $reverse Boolean: if true, multiple select will hide selected elements (default false).
- * @todo Document $selectmsg
-*/
-function HTMLSelectGroups($selectname, $selectmsg, $selected=array(), $multiple=false, $size=6, $reverse=false) {
- $groups = User::getAllGroups();
- $out = htmlspecialchars( wfMsg( $selectmsg ) );
- $out .= "<br />";
-
- if( $multiple ) {
- $attribs = array(
- 'name' => $selectname . '[]',
- 'multiple'=> 'multiple',
- 'size' => $size );
- } else {
- $attribs = array( 'name' => $selectname );
- }
- $attribs['style'] = 'width: 100%';
- $out .= wfElement( 'select', $attribs, null );
-
- foreach( $groups as $group ) {
- $attribs = array( 'value' => $group );
- if( $multiple ) {
- // for multiple will only show the things we want
- if( !in_array( $group, $selected ) xor $reverse ) {
- continue;
- }
- } else {
- if( in_array( $group, $selected ) ) {
- $attribs['selected'] = 'selected';
- }
- }
- $out .= wfElement( 'option', $attribs, User::getGroupName( $group ) ) . "\n";
- }
-
- $out .= "</select>\n";
- return $out;
-}
-
-?>
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index 9dfd6d61..984ee2d4 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -310,4 +310,4 @@ class HistoryBlobCurStub {
}
-?>
+
diff --git a/includes/Hooks.php b/includes/Hooks.php
index b428b08d..20103db4 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -119,6 +119,20 @@ function wfRunHooks($event, $args = null) {
global $wgOut;
$wgOut->showFatalError($retval);
return false;
+ } elseif( $retval === null ) {
+ if( is_array( $callback ) ) {
+ if( is_object( $callback[0] ) ) {
+ $prettyClass = get_class( $callback[0] );
+ } else {
+ $prettyClass = strval( $callback[0] );
+ }
+ $prettyFunc = $prettyClass . '::' . strval( $callback[1] );
+ } else {
+ $prettyFunc = strval( $callback );
+ }
+ throw new MWException( "Detected bug in an extension! " .
+ "Hook $prettyFunc failed to return a value; " .
+ "should return true to continue hook processing or false to abort." );
} else if (!$retval) {
return false;
}
@@ -126,4 +140,4 @@ function wfRunHooks($event, $args = null) {
return true;
}
-?>
+
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index a9fb13ca..6ea3abd0 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -4,14 +4,23 @@
* Various HTTP related functions
*/
class Http {
+ static function get( $url, $timeout = 'default' ) {
+ return Http::request( "GET", $url, $timeout );
+ }
+
+ static function post( $url, $timeout = 'default' ) {
+ return Http::request( "POST", $url, $timeout );
+ }
+
/**
* Get the contents of a file by HTTP
*
* if $timeout is 'default', $wgHTTPTimeout is used
*/
- static function get( $url, $timeout = 'default' ) {
+ static function request( $method, $url, $timeout = 'default' ) {
global $wgHTTPTimeout, $wgHTTPProxy, $wgVersion, $wgTitle;
+ wfDebug( __METHOD__ . ": $method $url\n" );
# Use curl if available
if ( function_exists( 'curl_init' ) ) {
$c = curl_init( $url );
@@ -26,6 +35,10 @@ class Http {
}
curl_setopt( $c, CURLOPT_TIMEOUT, $timeout );
curl_setopt( $c, CURLOPT_USERAGENT, "MediaWiki/$wgVersion" );
+ if ( $method == 'POST' )
+ curl_setopt( $c, CURLOPT_POST, true );
+ else
+ curl_setopt( $c, CURLOPT_CUSTOMREQUEST, $method );
# Set the referer to $wgTitle, even in command-line mode
# This is useful for interwiki transclusion, where the foreign
@@ -45,12 +58,29 @@ class Http {
if ( curl_getinfo( $c, CURLINFO_HTTP_CODE ) != 200 ) {
$text = false;
}
+ # Don't return truncated output
+ if ( curl_errno( $c ) != CURLE_OK ) {
+ $text = false;
+ }
curl_close( $c );
} else {
- # Otherwise use file_get_contents, or its compatibility function from GlobalFunctions.php
+ # Otherwise use file_get_contents...
# This may take 3 minutes to time out, and doesn't have local fetch capabilities
+
+ global $wgVersion;
+ $headers = array( "User-Agent: MediaWiki/$wgVersion" );
+ if( strcasecmp( $method, 'post' ) == 0 ) {
+ // Required for HTTP 1.0 POSTs
+ $headers[] = "Content-Length: 0";
+ }
+ $opts = array(
+ 'http' => array(
+ 'method' => $method,
+ 'header' => implode( "\r\n", $headers ) ) );
+ $ctx = stream_context_create($opts);
+
$url_fopen = ini_set( 'allow_url_fopen', 1 );
- $text = file_get_contents( $url );
+ $text = file_get_contents( $url, false, $ctx );
ini_set( 'allow_url_fopen', $url_fopen );
}
return $text;
@@ -88,4 +118,4 @@ class Http {
return false;
}
}
-?>
+
diff --git a/includes/IP.php b/includes/IP.php
index 8a2756c9..db712c3b 100644
--- a/includes/IP.php
+++ b/includes/IP.php
@@ -22,7 +22,12 @@ define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)');
define( 'RE_IPV6_ADD', '(:(:' . RE_IPV6_WORD . '){1,7}|' . RE_IPV6_WORD . '(:{1,2}' . RE_IPV6_WORD . '|::$){1,7})' );
define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
// This might be useful for regexps used elsewhere, matches any IPv6 or IPv6 address or network
-define( 'IP_ADDRESS_STRING', RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)|' . RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)');
+define( 'IP_ADDRESS_STRING',
+ '(?:' .
+ RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)' .
+ '|' .
+ RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)' .
+ ')' );
/**
* A collection of public static functions to play with IP address
@@ -109,13 +114,14 @@ class IP {
* @return string
*/
public static function sanitizeIP( $ip ) {
- if ( !$ip ) return null;
+ $ip = trim( $ip );
+ if ( $ip === '' ) return null;
// Trim and return IPv4 addresses
- if ( self::isIPv4($ip) ) return trim($ip);
+ if ( self::isIPv4($ip) ) return $ip;
// Only IPv6 addresses can be expanded
if ( !self::isIPv6($ip) ) return $ip;
// Remove any whitespaces, convert to upper case
- $ip = strtoupper( trim($ip) );
+ $ip = strtoupper( $ip );
// Expand zero abbreviations
if ( strpos( $ip, '::' ) !== false ) {
$ip = str_replace('::', str_repeat(':0', 8 - substr_count($ip, ':')) . ':', $ip);
@@ -462,22 +468,29 @@ class IP {
* @return valid dotted quad IPv4 address or null
*/
public static function canonicalize( $addr ) {
- if ( self::isValid( $addr ) )
- return $addr;
+ if ( self::isValid( $addr ) )
+ return $addr;
+
+ // Annoying IPv6 representations like ::ffff:1.2.3.4
+ if ( strpos($addr,':') !==false && strpos($addr,'.') !==false ) {
+ $addr = str_replace( '.', ':', $addr );
+ if( IP::isIPv6( $addr ) )
+ return $addr;
+ }
- // IPv6 loopback address
- $m = array();
- if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) )
- return '127.0.0.1';
+ // IPv6 loopback address
+ $m = array();
+ if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) )
+ return '127.0.0.1';
- // IPv4-mapped and IPv4-compatible IPv6 addresses
- if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . '(' . RE_IP_ADD . ')$/i', $addr, $m ) )
- return $m[1];
- if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . RE_IPV6_WORD . ':' . RE_IPV6_WORD . '$/i', $addr, $m ) )
- return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
+ // IPv4-mapped and IPv4-compatible IPv6 addresses
+ if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . '(' . RE_IP_ADD . ')$/i', $addr, $m ) )
+ return $m[1];
+ if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . RE_IPV6_WORD . ':' . RE_IPV6_WORD . '$/i', $addr, $m ) )
+ return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
- return null; // give up
+ return null; // give up
}
}
-?>
+
diff --git a/includes/ImageFunctions.php b/includes/ImageFunctions.php
index d04110d4..3e87c994 100644
--- a/includes/ImageFunctions.php
+++ b/includes/ImageFunctions.php
@@ -1,113 +1,4 @@
<?php
-
-/**
- * Returns the image directory of an image
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the image file.
- * @public
- */
-function wfImageDir( $fname ) {
- global $wgUploadDirectory, $wgHashedUploadDirectory;
-
- if (!$wgHashedUploadDirectory) { return $wgUploadDirectory; }
-
- $hash = md5( $fname );
- $dest = $wgUploadDirectory . '/' . $hash{0} . '/' . substr( $hash, 0, 2 );
-
- return $dest;
-}
-
-/**
- * Returns the image directory of an image's thumbnail
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the original image file
- * @param $shared Boolean: (optional) use the shared upload directory (default: 'false').
- * @public
- */
-function wfImageThumbDir( $fname, $shared = false ) {
- $base = wfImageArchiveDir( $fname, 'thumb', $shared );
- if ( Image::isHashed( $shared ) ) {
- $dir = "$base/$fname";
- } else {
- $dir = $base;
- }
-
- return $dir;
-}
-
-/**
- * Old thumbnail directory, kept for conversion
- */
-function wfDeprecatedThumbDir( $thumbName , $subdir='thumb', $shared=false) {
- return wfImageArchiveDir( $thumbName, $subdir, $shared );
-}
-
-/**
- * Returns the image directory of an image's old version
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the thumbnail file, including file size prefix.
- * @param $subdir String: subdirectory of the image upload directory that should be used for storing the old version. Default is 'archive'.
- * @param $shared Boolean use the shared upload directory (only relevant for other functions which call this one). Default is 'false'.
- * @public
- */
-function wfImageArchiveDir( $fname , $subdir='archive', $shared=false ) {
- global $wgUploadDirectory, $wgHashedUploadDirectory;
- global $wgSharedUploadDirectory, $wgHashedSharedUploadDirectory;
- $dir = $shared ? $wgSharedUploadDirectory : $wgUploadDirectory;
- $hashdir = $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
- if (!$hashdir) { return $dir.'/'.$subdir; }
- $hash = md5( $fname );
-
- return $dir.'/'.$subdir.'/'.$hash[0].'/'.substr( $hash, 0, 2 );
-}
-
-
-/*
- * Return the hash path component of an image path (URL or filesystem),
- * e.g. "/3/3c/", or just "/" if hashing is not used.
- *
- * @param $dbkey The filesystem / database name of the file
- * @param $fromSharedDirectory Use the shared file repository? It may
- * use different hash settings from the local one.
- */
-function wfGetHashPath ( $dbkey, $fromSharedDirectory = false ) {
- if( Image::isHashed( $fromSharedDirectory ) ) {
- $hash = md5($dbkey);
- return '/' . $hash{0} . '/' . substr( $hash, 0, 2 ) . '/';
- } else {
- return '/';
- }
-}
-
-/**
- * Returns the image URL of an image's old version
- *
- * @param $name String: file name of the image file
- * @param $subdir String: (optional) subdirectory of the image upload directory that is used by the old version. Default is 'archive'
- * @public
- */
-function wfImageArchiveUrl( $name, $subdir='archive' ) {
- global $wgUploadPath, $wgHashedUploadDirectory;
-
- if ($wgHashedUploadDirectory) {
- $hash = md5( substr( $name, 15) );
- $url = $wgUploadPath.'/'.$subdir.'/' . $hash{0} . '/' .
- substr( $hash, 0, 2 ) . '/'.$name;
- } else {
- $url = $wgUploadPath.'/'.$subdir.'/'.$name;
- }
- return wfUrlencode($url);
-}
-
/**
* Return a rounded pixel equivalent for a labeled CSS/SVG length.
* http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers
@@ -256,4 +147,4 @@ function wfFitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
}
-?>
+
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
index fba7714c..64f266f6 100644
--- a/includes/ImageGallery.php
+++ b/includes/ImageGallery.php
@@ -17,11 +17,17 @@ class ImageGallery
var $mImages, $mShowBytes, $mShowFilename;
var $mCaption = false;
var $mSkin = false;
+ var $mRevisionId = 0;
/**
- * Is the gallery on a wiki page (i.e. not a special page)
+ * Hide blacklisted images?
*/
- var $mParsing;
+ var $mHideBadImages;
+
+ /**
+ * Registered parser object for output callbacks
+ */
+ var $mParser;
/**
* Contextual title, used when images are being screened
@@ -31,6 +37,8 @@ class ImageGallery
private $mPerRow = 4; // How many images wide should the gallery be?
private $mWidths = 120, $mHeights = 120; // How wide/tall each thumbnail should be
+
+ private $mAttribs = array();
/**
* Create a new image gallery object.
@@ -39,14 +47,22 @@ class ImageGallery
$this->mImages = array();
$this->mShowBytes = true;
$this->mShowFilename = true;
- $this->mParsing = false;
+ $this->mParser = false;
+ $this->mHideBadImages = false;
}
/**
- * Set the "parse" bit so we know to hide "bad" images
+ * Register a parser object
*/
- function setParsing( $val = true ) {
- $this->mParsing = $val;
+ function setParser( $parser ) {
+ $this->mParser = $parser;
+ }
+
+ /**
+ * Set bad image flag
+ */
+ function setHideBadImages( $flag = true ) {
+ $this->mHideBadImages = $flag;
}
/**
@@ -127,22 +143,30 @@ class ImageGallery
/**
* Add an image to the gallery.
*
- * @param $image Image object that is added to the gallery
+ * @param $title Title object of the image that is added to the gallery
* @param $html String: additional HTML text to be shown. The name and size of the image are always shown.
*/
- function add( $image, $html='' ) {
- $this->mImages[] = array( &$image, $html );
- wfDebug( "ImageGallery::add " . $image->getName() . "\n" );
+ function add( $title, $html='' ) {
+ if ( $title instanceof File ) {
+ // Old calling convention
+ $title = $title->getTitle();
+ }
+ $this->mImages[] = array( $title, $html );
+ wfDebug( "ImageGallery::add " . $title->getText() . "\n" );
}
/**
* Add an image at the beginning of the gallery.
*
- * @param $image Image object that is added to the gallery
+ * @param $title Title object of the image that is added to the gallery
* @param $html String: Additional HTML text to be shown. The name and size of the image are always shown.
*/
- function insert( $image, $html='' ) {
- array_unshift( $this->mImages, array( &$image, $html ) );
+ function insert( $title, $html='' ) {
+ if ( $title instanceof File ) {
+ // Old calling convention
+ $title = $title->getTitle();
+ }
+ array_unshift( $this->mImages, array( &$title, $html ) );
}
@@ -172,6 +196,19 @@ class ImageGallery
function setShowFilename( $f ) {
$this->mShowFilename = ( $f == true);
}
+
+ /**
+ * Set arbitrary attributes to go on the HTML gallery output element.
+ * Should be suitable for a &lt;table&gt; element.
+ *
+ * Note -- if taking from user input, you should probably run through
+ * Sanitizer::validateAttributes() first.
+ *
+ * @param array of HTML attribute pairs
+ */
+ function setAttributes( $attribs ) {
+ $this->mAttribs = $attribs;
+ }
/**
* Return a HTML representation of the image gallery
@@ -188,23 +225,33 @@ class ImageGallery
$sk = $this->getSkin();
- $s = '<table class="gallery" cellspacing="0" cellpadding="0">';
+ $attribs = Sanitizer::mergeAttributes(
+ array(
+ 'class' => 'gallery',
+ 'cellspacing' => '0',
+ 'cellpadding' => '0' ),
+ $this->mAttribs );
+ $s = Xml::openElement( 'table', $attribs );
if( $this->mCaption )
$s .= "\n\t<caption>{$this->mCaption}</caption>";
$params = array( 'width' => $this->mWidths, 'height' => $this->mHeights );
$i = 0;
foreach ( $this->mImages as $pair ) {
- $img =& $pair[0];
+ $nt = $pair[0];
$text = $pair[1];
+
+ # Give extensions a chance to select the file revision for us
+ $time = false;
+ wfRunHooks( 'BeforeGalleryFindFile', array( &$this, &$nt, &$time ) );
- $nt = $img->getTitle();
+ $img = wfFindFile( $nt, $time );
- if( $nt->getNamespace() != NS_IMAGE ) {
+ if( $nt->getNamespace() != NS_IMAGE || !$img ) {
# We're dealing with a non-image, spit out the name and be done with it.
$thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
. htmlspecialchars( $nt->getText() ) . '</div>';
- } elseif( $this->mParsing && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
+ } elseif( $this->mHideBadImages && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
# The image is blacklisted, just show it as a text link.
$thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
. $sk->makeKnownLinkObj( $nt, htmlspecialchars( $nt->getText() ) ) . '</div>';
@@ -214,15 +261,26 @@ class ImageGallery
. htmlspecialchars( $img->getLastError() ) . '</div>';
} else {
$vpad = floor( ( 1.25*$this->mHeights - $thumb->height ) /2 ) - 2;
- $thumbhtml = "\n\t\t\t".'<div class="thumb" style="padding: ' . $vpad . 'px 0; width: '.($this->mWidths+30).'px;">'
- . $sk->makeKnownLinkObj( $nt, $thumb->toHtml() ) . '</div>';
+
+ $thumbhtml = "\n\t\t\t".
+ '<div class="thumb" style="padding: ' . $vpad . 'px 0; width: ' .($this->mWidths+30).'px;">'
+ # Auto-margin centering for block-level elements. Needed now that we have video
+ # handlers since they may emit block-level elements as opposed to simple <img> tags.
+ # ref http://css-discuss.incutio.com/?page=CenteringBlockElement
+ . '<div style="margin-left: auto; margin-right: auto; width: ' .$this->mWidths.'px;">'
+ . $thumb->toHtml( array( 'desc-link' => true ) ) . '</div></div>';
+
+ // Call parser transform hook
+ if ( $this->mParser && $img->getHandler() ) {
+ $img->getHandler()->parserTransformHook( $this->mParser, $img );
+ }
}
//TODO
//$ul = $sk->makeLink( $wgContLang->getNsText( Namespace::getUser() ) . ":{$ut}", $ut );
if( $this->mShowBytes ) {
- if( $img->exists() ) {
+ if( $img ) {
$nb = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
$wgLang->formatNum( $img->getSize() ) );
} else {
@@ -292,4 +350,5 @@ class ImageGallery
}
} //class
-?>
+
+
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index 13f8e46a..3cf6d0ac 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -16,8 +16,18 @@ if( !defined( 'MEDIAWIKI' ) )
class ImagePage extends Article {
/* private */ var $img; // Image object this page is shown for
+ /* private */ var $repo;
var $mExtraDescription = false;
+ function __construct( $title ) {
+ parent::__construct( $title );
+ $this->img = wfFindFile( $this->mTitle );
+ if ( !$this->img ) {
+ $this->img = wfLocalFile( $this->mTitle );
+ }
+ $this->repo = $this->img->repo;
+ }
+
/**
* Handler for action=render
* Include body text only; none of the image extras
@@ -31,8 +41,6 @@ class ImagePage extends Article {
function view() {
global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
- $this->img = new Image( $this->mTitle );
-
$diff = $wgRequest->getVal( 'diff' );
$diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
@@ -40,10 +48,10 @@ class ImagePage extends Article {
return Article::view();
if ($wgShowEXIF && $this->img->exists()) {
- $exif = $this->img->getExifData();
- $showmeta = count($exif) ? true : false;
+ // FIXME: bad interface, see note on MediaHandler::formatMetadata().
+ $formattedMetadata = $this->img->formatMetadata();
+ $showmeta = $formattedMetadata !== false;
} else {
- $exif = false;
$showmeta = false;
}
@@ -76,12 +84,12 @@ class ImagePage extends Article {
$this->imageHistory();
$this->imageLinks();
- if ( $exif ) {
+ if ( $showmeta ) {
global $wgStylePath, $wgStyleVersion;
$expand = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-expand' ) ) );
$collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) );
$wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ). "\n" );
- $wgOut->addWikiText( $this->makeMetadataTable( $exif ) );
+ $wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
$wgOut->addHTML(
"<script type=\"text/javascript\" src=\"$wgStylePath/common/metadata.js?$wgStyleVersion\"></script>\n" .
"<script type=\"text/javascript\">attachMetadataToggle('mw_metadata', '$expand', '$collapse');</script>\n" );
@@ -100,9 +108,9 @@ class ImagePage extends Article {
global $wgLang;
$r = '<ul id="filetoc">
<li><a href="#file">' . $wgLang->getNsText( NS_IMAGE ) . '</a></li>
- <li><a href="#filehistory">' . wfMsgHtml( 'imghistory' ) . '</a></li>
+ <li><a href="#filehistory">' . wfMsgHtml( 'filehist' ) . '</a></li>
<li><a href="#filelinks">' . wfMsgHtml( 'imagelinks' ) . '</a></li>' .
- ($metadata ? '<li><a href="#metadata">' . wfMsgHtml( 'metadata' ) . '</a></li>' : '') . '
+ ($metadata ? ' <li><a href="#metadata">' . wfMsgHtml( 'metadata' ) . '</a></li>' : '') . '
</ul>';
return $r;
}
@@ -110,57 +118,39 @@ class ImagePage extends Article {
/**
* Make a table with metadata to be shown in the output page.
*
+ * FIXME: bad interface, see note on MediaHandler::formatMetadata().
+ *
* @access private
*
* @param array $exif The array containing the EXIF data
* @return string
*/
- function makeMetadataTable( $exif ) {
+ function makeMetadataTable( $metadata ) {
$r = wfMsg( 'metadata-help' ) . "\n\n";
$r .= "{| id=mw_metadata class=mw_metadata\n";
- $visibleFields = $this->visibleMetadataFields();
- foreach( $exif as $k => $v ) {
- $tag = strtolower( $k );
- $msg = wfMsg( "exif-$tag" );
- $class = "exif-$tag";
- if( !in_array( $tag, $visibleFields ) ) {
- $class .= ' collapsable';
+ foreach ( $metadata as $type => $stuff ) {
+ foreach ( $stuff as $v ) {
+ $class = Sanitizer::escapeId( $v['id'] );
+ if( $type == 'collapsed' ) {
+ $class .= ' collapsable';
+ }
+ $r .= "|- class=\"$class\"\n";
+ $r .= "!| {$v['name']}\n";
+ $r .= "|| {$v['value']}\n";
}
- $r .= "|- class=\"$class\"\n";
- $r .= "!| $msg\n";
- $r .= "|| $v\n";
}
$r .= '|}';
return $r;
}
/**
- * Get a list of EXIF metadata items which should be displayed when
- * the metadata table is collapsed.
- *
- * @return array of strings
- * @access private
- */
- function visibleMetadataFields() {
- $fields = array();
- $lines = explode( "\n", wfMsgForContent( 'metadata-fields' ) );
- foreach( $lines as $line ) {
- $matches = array();
- if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
- $fields[] = $matches[1];
- }
- }
- return $fields;
- }
-
- /**
* Overloading Article's getContent method.
*
* Omit noarticletext if sharedupload; text will be fetched from the
* shared upload server if possible.
*/
function getContent() {
- if( $this->img && $this->img->fromSharedDirectory && 0 == $this->getID() ) {
+ if( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
return '';
}
return Article::getContent();
@@ -203,12 +193,15 @@ class ImagePage extends Article {
$mime = $this->img->getMimeType();
$showLink = false;
$linkAttribs = array( 'href' => $full_url );
+ $longDesc = $this->img->getLongDesc();
+
+ wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) ) ;
- if ( $this->img->allowInlineDisplay() and $width and $height) {
+ if ( $this->img->allowInlineDisplay() ) {
# image
# "Download high res version" link below the image
- $msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
+ #$msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
# We'll show a thumbnail of this image
if ( $width > $maxWidth || $height > $maxHeight ) {
# Calculate the thumbnail size.
@@ -242,21 +235,20 @@ class ImagePage extends Article {
} else {
$anchorclose .=
$msgsmall .
- '<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $msgsize;
+ '<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $longDesc;
}
if ( $this->img->isMultipage() ) {
$wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
}
- $imgAttribs = array(
- 'border' => 0,
- 'alt' => $this->img->getTitle()->getPrefixedText()
- );
-
if ( $thumbnail ) {
+ $options = array(
+ 'alt' => $this->img->getTitle()->getPrefixedText(),
+ 'file-link' => true,
+ );
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
- $thumbnail->toHtml( $imgAttribs, $linkAttribs ) .
+ $thumbnail->toHtml( $options ) .
$anchorclose . '</div>' );
}
@@ -265,8 +257,8 @@ class ImagePage extends Article {
if ( $page > 1 ) {
$label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
- $link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
- $thumb1 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
+ $link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
+ $thumb1 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none',
array( 'page' => $page - 1 ) );
} else {
$thumb1 = '';
@@ -274,8 +266,8 @@ class ImagePage extends Article {
if ( $page < $count ) {
$label = wfMsg( 'imgmultipagenext' );
- $link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
- $thumb2 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
+ $link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
+ $thumb2 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none',
array( 'page' => $page + 1 ) );
} else {
$thumb2 = '';
@@ -304,9 +296,9 @@ class ImagePage extends Article {
if ($this->img->isSafeFile()) {
$icon= $this->img->iconThumb();
- $wgOut->addHTML( '<div class="fullImageLink" id="file"><a href="' . $full_url . '">' .
- $icon->toHtml() .
- '</a></div>' );
+ $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
+ $icon->toHtml( array( 'desc-link' => true ) ) .
+ '</div>' );
}
$showLink = true;
@@ -314,42 +306,32 @@ class ImagePage extends Article {
if ($showLink) {
- // Workaround for incorrect MIME type on SVGs uploaded in previous versions
- if ($mime == 'image/svg') $mime = 'image/svg+xml';
-
$filename = wfEscapeWikiText( $this->img->getName() );
- $info = wfMsg( 'file-info', $sk->formatSize( $this->img->getSize() ), $mime );
- $infores = '';
-
- // Check for MIME type. Other types may have more information in the future.
- if (substr($mime,0,9) == 'image/svg' ) {
- $infores = wfMsg('file-svg', $width_orig, $height_orig ) . '<br />';
- }
global $wgContLang;
$dirmark = $wgContLang->getDirMark();
if (!$this->img->isSafeFile()) {
$warning = wfMsg( 'mediawarning' );
- $wgOut->addWikiText( <<<END
-<div class="fullMedia">$infores
+ $wgOut->addWikiText( <<<EOT
+<div class="fullMedia">
<span class="dangerousLink">[[Media:$filename|$filename]]</span>$dirmark
-<span class="fileInfo"> $info</span>
+<span class="fileInfo"> $longDesc</span>
</div>
<div class="mediaWarning">$warning</div>
-END
+EOT
);
} else {
- $wgOut->addWikiText( <<<END
-<div class="fullMedia">$infores
-[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $info</span>
+ $wgOut->addWikiText( <<<EOT
+<div class="fullMedia">
+[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $longDesc</span>
</div>
-END
+EOT
);
}
}
- if($this->img->fromSharedDirectory) {
+ if(!$this->img->isLocal()) {
$this->printSharedImageText();
}
} else {
@@ -363,27 +345,21 @@ END
}
function printSharedImageText() {
- global $wgRepositoryBaseUrl, $wgFetchCommonsDescriptions, $wgOut, $wgUser;
-
- $url = $wgRepositoryBaseUrl . urlencode($this->mTitle->getDBkey());
- $sharedtext = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
- if ($wgRepositoryBaseUrl && !$wgFetchCommonsDescriptions) {
+ global $wgOut, $wgUser;
+ $descUrl = $this->img->getDescriptionUrl();
+ $descText = $this->img->getDescriptionText();
+ $s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
+ if ( $descUrl && !$descText) {
$sk = $wgUser->getSkin();
- $title = SpecialPage::getTitleFor( 'Upload' );
- $link = $sk->makeKnownLinkObj($title, wfMsgHtml('shareduploadwiki-linktext'),
- array( 'wpDestFile' => urlencode( $this->img->getName() )));
- $sharedtext .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
+ $link = $sk->makeExternalLink( $descUrl, wfMsg('shareduploadwiki-linktext') );
+ $s .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
}
- $sharedtext .= "</div>";
- $wgOut->addHTML($sharedtext);
-
- if ($wgRepositoryBaseUrl && $wgFetchCommonsDescriptions) {
- $renderUrl = wfAppendQuery( $url, 'action=render' );
- wfDebug( "Fetching shared description from $renderUrl\n" );
- $text = Http::get( $renderUrl );
- if ($text)
- $this->mExtraDescription = $text;
+ $s .= "</div>";
+ $wgOut->addHTML($s);
+
+ if ( $descText ) {
+ $this->mExtraDescription = $descText;
}
}
@@ -400,7 +376,7 @@ END
function uploadLinksBox() {
global $wgUser, $wgOut;
- if( $this->img->fromSharedDirectory )
+ if( !$this->img->isLocal() )
return;
$sk = $wgUser->getSkin();
@@ -408,7 +384,7 @@ END
$wgOut->addHtml( '<br /><ul>' );
# "Upload a new version of this file" link
- if( $wgUser->isAllowed( 'reupload' ) ) {
+ if( UploadForm::userCanReUpload($wgUser,$this->img->name) ) {
$ulink = $sk->makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) );
$wgOut->addHtml( "<li><div class='plainlinks'>{$ulink}</div></li>" );
}
@@ -439,25 +415,31 @@ END
$line = $this->img->nextHistoryLine();
if ( $line ) {
- $list = new ImageHistoryList( $sk );
+ $list = new ImageHistoryList( $sk, $this->img );
+ $file = $this->repo->newFileFromRow( $line );
+ $dims = $file->getDimensionsString();
$s = $list->beginImageHistoryList() .
$list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp),
$this->mTitle->getDBkey(), $line->img_user,
$line->img_user_text, $line->img_size, $line->img_description,
- $line->img_width, $line->img_height
+ $dims
);
while ( $line = $this->img->nextHistoryLine() ) {
- $s .= $list->imageHistoryLine( false, $line->img_timestamp,
- $line->oi_archive_name, $line->img_user,
- $line->img_user_text, $line->img_size, $line->img_description,
- $line->img_width, $line->img_height
+ $file = $this->repo->newFileFromRow( $line );
+ $dims = $file->getDimensionsString();
+ $s .= $list->imageHistoryLine( false, $line->oi_timestamp,
+ $line->oi_archive_name, $line->oi_user,
+ $line->oi_user_text, $line->oi_size, $line->oi_description,
+ $dims
);
}
$s .= $list->endImageHistoryList();
} else { $s=''; }
$wgOut->addHTML( $s );
+ $this->img->resetHistory(); // free db resources
+
# Exist check because we don't want to show this on pages where an image
# doesn't exist along with the noimage message, that would suck. -ævar
if( $wgUseExternalEditor && $this->img->exists() ) {
@@ -496,207 +478,31 @@ END
$wgOut->addHTML( "</ul>\n" );
}
- function delete()
- {
- global $wgUser, $wgOut, $wgRequest;
-
- $confirm = $wgRequest->wasPosted();
- $reason = $wgRequest->getVal( 'wpReason' );
- $image = $wgRequest->getVal( 'image' );
- $oldimage = $wgRequest->getVal( 'oldimage' );
-
- # Only sysops can delete images. Previously ordinary users could delete
- # old revisions, but this is no longer the case.
- if ( !$wgUser->isAllowed('delete') ) {
- $wgOut->permissionRequired( 'delete' );
- return;
- }
- if ( $wgUser->isBlocked() ) {
- return $this->blockedIPpage();
- }
- if ( wfReadOnly() ) {
- $wgOut->readOnlyPage();
- return;
- }
-
- # Better double-check that it hasn't been deleted yet!
- $wgOut->setPagetitle( wfMsg( 'confirmdelete' ) );
- if ( ( !is_null( $image ) )
- && ( '' == trim( $image ) ) ) {
- $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
- return;
- }
-
- $this->img = new Image( $this->mTitle );
-
- # Deleting old images doesn't require confirmation
- if ( !is_null( $oldimage ) || $confirm ) {
- if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
- $this->doDelete( $reason );
- } else {
- $wgOut->showFatalError( wfMsg( 'sessionfailure' ) );
- }
- return;
- }
-
- if ( !is_null( $image ) ) {
- $q = '&image=' . urlencode( $image );
- } else if ( !is_null( $oldimage ) ) {
- $q = '&oldimage=' . urlencode( $oldimage );
- } else {
- $q = '';
- }
- return $this->confirmDelete( $q, $wgRequest->getText( 'wpReason' ) );
- }
-
- /*
- * Delete an image.
- * @param $reason User provided reason for deletion.
- */
- function doDelete( $reason ) {
- global $wgOut, $wgRequest;
-
- $oldimage = $wgRequest->getVal( 'oldimage' );
-
- if ( !is_null( $oldimage ) ) {
- if ( strlen( $oldimage ) < 16 ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
- if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
- if ( !$this->doDeleteOldImage( $oldimage ) ) {
- return;
- }
- $deleted = $oldimage;
- } else {
- $ok = $this->img->delete( $reason );
- if( !$ok ) {
- # If the deletion operation actually failed, bug out:
- $wgOut->showFileDeleteError( $this->img->getName() );
- return;
- }
-
- # Image itself is now gone, and database is cleaned.
- # Now we remove the image description page.
-
- $article = new Article( $this->mTitle );
- $article->doDeleteArticle( $reason ); # ignore errors
-
- $deleted = $this->img->getName();
- }
-
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
-
- $loglink = '[[Special:Log/delete|' . wfMsg( 'deletionlog' ) . ']]';
- $text = wfMsg( 'deletedtext', $deleted, $loglink );
-
- $wgOut->addWikiText( $text );
-
- $wgOut->returnToMain( false, $this->mTitle->getPrefixedText() );
- }
-
/**
- * @return success
+ * Delete the file, or an earlier version of it
*/
- function doDeleteOldImage( $oldimage )
- {
- global $wgOut;
-
- $ok = $this->img->deleteOld( $oldimage, '' );
- if( !$ok ) {
- # If we actually have a file and can't delete it, throw an error.
- # Something went awry...
- $wgOut->showFileDeleteError( "$oldimage" );
- } else {
- # Log the deletion
- $log = new LogPage( 'delete' );
- $log->addEntry( 'delete', $this->mTitle, wfMsg('deletedrevision',$oldimage) );
- }
- return $ok;
- }
-
- function revert() {
- global $wgOut, $wgRequest, $wgUser;
-
- $oldimage = $wgRequest->getText( 'oldimage' );
- if ( strlen( $oldimage ) < 16 ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
- if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
-
- if ( wfReadOnly() ) {
- $wgOut->readOnlyPage();
- return;
- }
- if( $wgUser->isAnon() ) {
- $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
+ public function delete() {
+ if( !$this->img->exists() || !$this->img->isLocal() ) {
+ // Standard article deletion
+ Article::delete();
return;
}
- if ( ! $this->mTitle->userCan( 'edit' ) ) {
- $wgOut->readOnlyPage( $this->getContent(), true );
- return;
- }
- if ( $wgUser->isBlocked() ) {
- return $this->blockedIPpage();
- }
- if( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
- $wgOut->showErrorPage( 'internalerror', 'sessionfailure' );
- return;
- }
- $name = substr( $oldimage, 15 );
-
- $dest = wfImageDir( $name );
- $archive = wfImageArchiveDir( $name );
- $curfile = "{$dest}/{$name}";
-
- if ( !is_dir( $dest ) ) wfMkdirParents( $dest );
- if ( !is_dir( $archive ) ) wfMkdirParents( $archive );
-
- if ( ! is_file( $curfile ) ) {
- $wgOut->showFileNotFoundError( htmlspecialchars( $curfile ) );
- return;
- }
- $oldver = wfTimestampNow() . "!{$name}";
-
- if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) {
- $wgOut->showFileRenameError( $curfile, "${archive}/{$oldver}" );
- return;
- }
- if ( ! copy( "{$archive}/{$oldimage}", $curfile ) ) {
- $wgOut->showFileCopyError( "${archive}/{$oldimage}", $curfile );
- return;
- }
-
- # Record upload and update metadata cache
- $img = Image::newFromName( $name );
- $img->recordUpload( $oldver, wfMsg( "reverted" ) );
-
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
- $wgOut->addHTML( wfMsg( 'imagereverted' ) );
-
- $descTitle = $img->getTitle();
- $wgOut->returnToMain( false, $descTitle->getPrefixedText() );
+ $deleter = new FileDeleteForm( $this->img );
+ $deleter->execute();
}
- function blockedIPpage() {
- $edit = new EditPage( $this );
- return $edit->blockedIPpage();
+ /**
+ * Revert the file to an earlier version
+ */
+ public function revert() {
+ $reverter = new FileRevertForm( $this->img );
+ $reverter->execute();
}
/**
* Override handling of action=purge
*/
function doPurge() {
- $this->img = new Image( $this->mTitle );
if( $this->img->exists() ) {
wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
$update = new HTMLCacheUpdate( $this->mTitle, 'imagelinks' );
@@ -709,82 +515,120 @@ END
parent::doPurge();
}
+ /**
+ * Display an error with a wikitext description
+ */
+ function showError( $description ) {
+ global $wgOut;
+ $wgOut->setPageTitle( wfMsg( "internalerror" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setArticleRelated( false );
+ $wgOut->enableClientCache( false );
+ $wgOut->addWikiText( $description );
+ }
+
}
/**
- * @todo document
+ * Builds the image revision log shown on image pages
+ *
* @addtogroup Media
*/
class ImageHistoryList {
- function ImageHistoryList( &$skin ) {
- $this->skin =& $skin;
- }
- function beginImageHistoryList() {
- $s = "\n" .
- Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'imghistory' ) ) .
- "\n<p>" . wfMsg( 'imghistlegend' ) . "</p>\n".'<ul class="special">';
- return $s;
- }
+ protected $img, $skin, $title, $repo;
- function endImageHistoryList() {
- $s = "</ul>\n";
- return $s;
+ public function __construct( $skin, $img ) {
+ $this->skin = $skin;
+ $this->img = $img;
+ $this->title = $img->getTitle();
}
- function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $width, $height ) {
- global $wgUser, $wgLang, $wgTitle, $wgContLang;
-
- $datetime = $wgLang->timeanddate( $timestamp, true );
- $del = wfMsgHtml( 'deleteimg' );
- $delall = wfMsgHtml( 'deleteimgcompletely' );
- $cur = wfMsgHtml( 'cur' );
+ public function beginImageHistoryList() {
+ global $wgOut, $wgUser;
+ return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) )
+ . $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) )
+ . Xml::openElement( 'table', array( 'class' => 'filehistory' ) ) . "\n"
+ . '<tr><td></td>'
+ . ( $this->img->isLocal() && $wgUser->isAllowed( 'delete' ) ? '<td></td>' : '' )
+ . '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
+ . '<th class="mw-imagepage-filesize">' . wfMsgHtml( 'filehist-filesize' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
+ . "</tr>\n";
+ }
- if ( $iscur ) {
- $url = Image::imageUrl( $img );
- $rlink = $cur;
- if ( $wgUser->isAllowed('delete') ) {
- $link = $wgTitle->escapeLocalURL( 'image=' . $wgTitle->getPartialURL() .
- '&action=delete' );
- $style = $this->skin->getInternalLinkAttributes( $link, $delall );
+ public function endImageHistoryList() {
+ return "</table>\n";
+ }
- $dlink = '<a href="'.$link.'"'.$style.'>'.$delall.'</a>';
- } else {
- $dlink = $del;
- }
+ public function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $dims ) {
+ global $wgUser, $wgLang, $wgContLang;
+ $local = $this->img->isLocal();
+ $row = '';
+
+ // Deletion link
+ if( $local && $wgUser->isAllowed( 'delete' ) ) {
+ $row .= '<td>';
+ $q = array();
+ $q[] = 'action=delete';
+ if( !$iscur )
+ $q[] = 'oldimage=' . urlencode( $img );
+ $row .= '(' . $this->skin->makeKnownLinkObj(
+ $this->title,
+ wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
+ implode( '&', $q )
+ ) . ')';
+ $row .= '</td>';
+ }
+
+ // Reversion link/current indicator
+ $row .= '<td>';
+ if( $iscur ) {
+ $row .= '(' . wfMsgHtml( 'filehist-current' ) . ')';
+ } elseif( $local && $wgUser->isLoggedIn() && $this->title->userCan( 'edit' ) ) {
+ $q = array();
+ $q[] = 'action=revert';
+ $q[] = 'oldimage=' . urlencode( $img );
+ $q[] = 'wpEditToken=' . urlencode( $wgUser->editToken( $img ) );
+ $row .= '(' . $this->skin->makeKnownLinkObj(
+ $this->title,
+ wfMsgHtml( 'filehist-revert' ),
+ implode( '&', $q )
+ ) . ')';
+ }
+ $row .= '</td>';
+
+ // Date/time and image link
+ $row .= '<td>';
+ $url = $iscur ? $this->img->getUrl() : $this->img->getArchiveUrl( $img );
+ $row .= Xml::element(
+ 'a',
+ array( 'href' => $url ),
+ $wgLang->timeAndDate( $timestamp, true )
+ );
+ $row .= '</td>';
+
+ // Uploading user
+ $row .= '<td>';
+ if( $local ) {
+ $row .= $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
} else {
- $url = htmlspecialchars( wfImageArchiveUrl( $img ) );
- if( $wgUser->getID() != 0 && $wgTitle->userCan( 'edit' ) ) {
- $token = urlencode( $wgUser->editToken( $img ) );
- $rlink = $this->skin->makeKnownLinkObj( $wgTitle,
- wfMsgHtml( 'revertimg' ), 'action=revert&oldimage=' .
- urlencode( $img ) . "&wpEditToken=$token" );
- $dlink = $this->skin->makeKnownLinkObj( $wgTitle,
- $del, 'action=delete&oldimage=' . urlencode( $img ) .
- "&wpEditToken=$token" );
- } else {
- # Having live active links for non-logged in users
- # means that bots and spiders crawling our site can
- # inadvertently change content. Baaaad idea.
- $rlink = wfMsgHtml( 'revertimg' );
- $dlink = $del;
- }
+ $row .= htmlspecialchars( $usertext );
}
-
- $userlink = $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
- $nbytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
- $wgLang->formatNum( $size ) );
- $widthheight = wfMsgHtml( 'widthheight', $width, $height );
- $style = $this->skin->getInternalLinkAttributes( $url, $datetime );
+ $row .= '</td>';
- $s = "<li> ({$dlink}) ({$rlink}) <a href=\"{$url}\"{$style}>{$datetime}</a> . . {$userlink} . . {$widthheight} ({$nbytes})";
+ // Image dimensions
+ $row .= '<td>' . htmlspecialchars( $dims ) . '</td>';
- $s .= $this->skin->commentBlock( $description, $wgTitle );
- $s .= "</li>\n";
- return $s;
- }
+ // File size
+ $row .= '<td class="mw-imagepage-filesize">' . $this->skin->formatSize( $size ) . '</td>';
-}
+ // Comment
+ $row .= '<td>' . $this->skin->formatComment( $description, $this->title ) . '</td>';
+ return "<tr>{$row}</tr>\n";
+ }
-?>
+}
diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php
index 93f090a1..8948ddc6 100644
--- a/includes/ImageQueryPage.php
+++ b/includes/ImageQueryPage.php
@@ -30,8 +30,8 @@ class ImageQueryPage extends QueryPage {
# $num [should update this to use a Pager]
for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
$image = $this->prepareImage( $row );
- if( $image instanceof Image ) {
- $gallery->add( $image, $this->getCellHtml( $row ) );
+ if( $image ) {
+ $gallery->add( $image->getTitle(), $this->getCellHtml( $row ) );
}
}
@@ -49,7 +49,7 @@ class ImageQueryPage extends QueryPage {
$namespace = isset( $row->namespace ) ? $row->namespace : NS_IMAGE;
$title = Title::makeTitleSafe( $namespace, $row->title );
return ( $title instanceof Title && $title->getNamespace() == NS_IMAGE )
- ? new Image( $title )
+ ? wfFindFile( $title )
: null;
}
@@ -65,4 +65,4 @@ class ImageQueryPage extends QueryPage {
}
-?>
+
diff --git a/includes/JobQueue.php b/includes/JobQueue.php
index 140130fa..a2780bdb 100644
--- a/includes/JobQueue.php
+++ b/includes/JobQueue.php
@@ -4,6 +4,8 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point\n" );
}
+require_once('UserMailer.php');
+
/**
* Class to both describe a background job and handle jobs.
*/
@@ -37,6 +39,46 @@ abstract class Job {
*/
/**
+ * Pop a job of a certain type. This tries less hard than pop() to
+ * actually find a job; it may be adversely affected by concurrent job
+ * runners.
+ */
+ static function pop_type($type) {
+ wfProfilein( __METHOD__ );
+
+ $dbw = wfGetDB( DB_MASTER );
+
+
+ $row = $dbw->selectRow( 'job', '*', array( 'job_cmd' => $type ), __METHOD__,
+ array( 'LIMIT' => 1 ));
+
+ if ($row === false) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ /* Ensure we "own" this row */
+ $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
+ $affected = $dbw->affectedRows();
+
+ if ($affected == 0) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ $namespace = $row->job_namespace;
+ $dbkey = $row->job_title;
+ $title = Title::makeTitleSafe( $namespace, $dbkey );
+ $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
+
+ $dbw->delete( 'job', $job->insertFields(), __METHOD__ );
+ $dbw->immediateCommit();
+
+ wfProfileOut( __METHOD__ );
+ return $job;
+ }
+
+ /**
* Pop a job off the front of the queue
* @static
* @param $offset Number of jobs to skip
@@ -125,20 +167,23 @@ abstract class Job {
}
/**
- * Create an object of a subclass
+ * Create the appropriate object to handle a specific job
+ *
+ * @param string $command Job command
+ * @param Title $title Associated title
+ * @param array $params Job parameters
+ * @param int $id Job identifier
+ * @return Job
*/
static function factory( $command, $title, $params = false, $id = 0 ) {
- switch ( $command ) {
- case 'refreshLinks':
- return new RefreshLinksJob( $title, $params, $id );
- case 'htmlCacheUpdate':
- case 'html_cache_update': # BC
- return new HTMLCacheUpdateJob( $title, $params['table'], $params['start'], $params['end'], $id );
- default:
- throw new MWException( "Invalid job command \"$command\"" );
+ global $wgJobClasses;
+ if( isset( $wgJobClasses[$command] ) ) {
+ $class = $wgJobClasses[$command];
+ return new $class( $title, $params, $id );
}
+ throw new MWException( "Invalid job command `{$command}`" );
}
-
+
static function makeBlob( $params ) {
if ( $params !== false ) {
return serialize( $params );
@@ -245,50 +290,3 @@ abstract class Job {
}
}
-
-/**
- * Background job to update links for a given title.
- */
-class RefreshLinksJob extends Job {
- function __construct( $title, $params = '', $id = 0 ) {
- parent::__construct( 'refreshLinks', $title, $params, $id );
- }
-
- /**
- * Run a refreshLinks job
- * @return boolean success
- */
- function run() {
- global $wgParser;
- wfProfileIn( __METHOD__ );
-
- $linkCache =& LinkCache::singleton();
- $linkCache->clear();
-
- if ( is_null( $this->title ) ) {
- $this->error = "refreshLinks: Invalid title";
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $revision = Revision::newFromTitle( $this->title );
- if ( !$revision ) {
- $this->error = 'refreshLinks: Article not found "' . $this->title->getPrefixedDBkey() . '"';
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- wfProfileIn( __METHOD__.'-parse' );
- $options = new ParserOptions;
- $parserOutput = $wgParser->parse( $revision->getText(), $this->title, $options, true, true, $revision->getId() );
- wfProfileOut( __METHOD__.'-parse' );
- wfProfileIn( __METHOD__.'-update' );
- $update = new LinksUpdate( $this->title, $parserOutput, false );
- $update->doUpdate();
- wfProfileOut( __METHOD__.'-update' );
- wfProfileOut( __METHOD__ );
- return true;
- }
-}
-
-?>
diff --git a/includes/Licenses.php b/includes/Licenses.php
index f4586ae5..6a034468 100644
--- a/includes/Licenses.php
+++ b/includes/Licenses.php
@@ -172,4 +172,4 @@ class License {
$this->text = strrev( $text );
}
}
-?>
+
diff --git a/includes/LinkBatch.php b/includes/LinkBatch.php
index 065c540a..8ab3393e 100644
--- a/includes/LinkBatch.php
+++ b/includes/LinkBatch.php
@@ -3,7 +3,7 @@
/**
* Class representing a list of titles
* The execute() method checks them all for existence and adds them to a LinkCache object
- +
+ *
* @addtogroup Cache
*/
class LinkBatch {
@@ -156,19 +156,26 @@ class LinkBatch {
} else {
$sql .= ' OR ';
}
- $sql .= "({$prefix}_namespace=$ns AND {$prefix}_title IN (";
-
- $firstTitle = true;
- foreach( $dbkeys as $dbkey => $unused ) {
- if ( $firstTitle ) {
- $firstTitle = false;
- } else {
- $sql .= ',';
+
+ if (count($dbkeys)==1) { // avoid multiple-reference syntax if simple equality can be used
+
+ $sql .= "({$prefix}_namespace=$ns AND {$prefix}_title=".
+ $db->addQuotes(current(array_keys($dbkeys))).
+ ")";
+ } else {
+ $sql .= "({$prefix}_namespace=$ns AND {$prefix}_title IN (";
+
+ $firstTitle = true;
+ foreach( $dbkeys as $dbkey => $unused ) {
+ if ( $firstTitle ) {
+ $firstTitle = false;
+ } else {
+ $sql .= ',';
+ }
+ $sql .= $db->addQuotes( $dbkey );
}
- $sql .= $db->addQuotes( $dbkey );
+ $sql .= '))';
}
-
- $sql .= '))';
}
if ( $first && $firstTitle ) {
# No titles added
@@ -179,4 +186,4 @@ class LinkBatch {
}
}
-?>
+
diff --git a/includes/LinkCache.php b/includes/LinkCache.php
index 53fb640a..7c49d88e 100644
--- a/includes/LinkCache.php
+++ b/includes/LinkCache.php
@@ -169,4 +169,4 @@ class LinkCache {
$this->mBadLinks = array();
}
}
-?>
+
diff --git a/includes/LinkFilter.php b/includes/LinkFilter.php
index 39341d5d..ee668f08 100644
--- a/includes/LinkFilter.php
+++ b/includes/LinkFilter.php
@@ -105,4 +105,4 @@ class LinkFilter {
return $like;
}
}
-?>
+
diff --git a/includes/Linker.php b/includes/Linker.php
index b12e2ad0..9397b800 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -12,6 +12,12 @@
* @addtogroup Skins
*/
class Linker {
+
+ /**
+ * Flags for userToolLinks()
+ */
+ const TOOL_LINKS_NOBLOCK = 1;
+
function __construct() {}
/**
@@ -96,7 +102,7 @@ class Linker {
wfProfileIn( 'Linker::makeLink' );
$nt = Title::newFromText( $title );
if ($nt) {
- $result = $this->makeLinkObj( Title::newFromText( $title ), $text, $query, $trail );
+ $result = $this->makeLinkObj( $nt, $text, $query, $trail );
} else {
wfDebug( 'Invalid title passed to Linker::makeLink(): "'.$title."\"\n" );
$result = $text == "" ? $title : $text;
@@ -218,32 +224,39 @@ class Linker {
$retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
} else {
wfProfileIn( $fname.'-immediate' );
+
+ # Handles links to special pages wich do not exist in the database:
+ if( $nt->getNamespace() == NS_SPECIAL ) {
+ if( SpecialPage::exists( $nt->getDbKey() ) ) {
+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
+ } else {
+ $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
+ }
+ wfProfileOut( $fname.'-immediate' );
+ wfProfileOut( $fname );
+ return $retVal;
+ }
+
# Work out link colour immediately
$aid = $nt->getArticleID() ;
if ( 0 == $aid ) {
$retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
} else {
- $threshold = $wgUser->getOption('stubthreshold') ;
- if ( $threshold > 0 ) {
- $dbr = wfGetDB( DB_SLAVE );
- $s = $dbr->selectRow(
- array( 'page' ),
- array( 'page_len',
- 'page_namespace',
- 'page_is_redirect' ),
- array( 'page_id' => $aid ), $fname ) ;
- if ( $s !== false ) {
- $size = $s->page_len;
- if ( $s->page_is_redirect OR $s->page_namespace != NS_MAIN ) {
- $size = $threshold*2 ; # Really big
- }
- } else {
- $size = $threshold*2 ; # Really big
+ $stub = false;
+ if ( $nt->isContentPage() ) {
+ $threshold = $wgUser->getOption('stubthreshold');
+ if ( $threshold > 0 ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $s = $dbr->selectRow(
+ array( 'page' ),
+ array( 'page_len',
+ 'page_is_redirect' ),
+ array( 'page_id' => $aid ), $fname ) ;
+ $stub = ( $s !== false && !$s->page_is_redirect &&
+ $s->page_len < $threshold );
}
- } else {
- $size = 1 ;
}
- if ( $size < $threshold ) {
+ if ( $stub ) {
$retVal = $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
} else {
$retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
@@ -324,7 +337,9 @@ class Linker {
$fname = 'Linker::makeBrokenLinkObj';
wfProfileIn( $fname );
- if ( '' == $query ) {
+ if( $nt->getNamespace() == NS_SPECIAL ) {
+ $q = $query;
+ } else if ( '' == $query ) {
$q = 'action=edit';
} else {
$q = 'action=edit&'.$query;
@@ -418,42 +433,118 @@ class Linker {
return $s;
}
- /** @todo document */
- function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
- $thumb = false, $manual_thumb = '', $valign = '' )
+ /**
+ * Creates the HTML source for images
+ * @deprecated use makeImageLink2
+ *
+ * @param object $title
+ * @param string $label label text
+ * @param string $alt alt text
+ * @param string $align horizontal alignment: none, left, center, right)
+ * @param array $handlerParams Parameters to be passed to the media handler
+ * @param boolean $framed shows image in original size in a frame
+ * @param boolean $thumb shows image as thumbnail in a frame
+ * @param string $manualthumb image name for the manual thumbnail
+ * @param string $valign vertical alignment: baseline, sub, super, top, text-top, middle, bottom, text-bottom
+ * @return string
+ */
+ function makeImageLinkObj( $title, $label, $alt, $align = '', $handlerParams = array(), $framed = false,
+ $thumb = false, $manualthumb = '', $valign = '', $time = false )
{
- global $wgContLang, $wgUser, $wgThumbLimits;
-
- $img = new Image( $nt );
+ $frameParams = array( 'alt' => $alt, 'caption' => $label );
+ if ( $align ) {
+ $frameParams['align'] = $align;
+ }
+ if ( $framed ) {
+ $frameParams['framed'] = true;
+ }
+ if ( $thumb ) {
+ $frameParams['thumbnail'] = true;
+ }
+ if ( $manualthumb ) {
+ $frameParams['manualthumb'] = $manualthumb;
+ }
+ if ( $valign ) {
+ $frameParams['valign'] = $valign;
+ }
+ $file = wfFindFile( $title, $time );
+ return $this->makeImageLink2( $title, $file, $frameParams, $handlerParams );
+ }
- if ( !$img->allowInlineDisplay() && $img->exists() ) {
- return $this->makeKnownLinkObj( $nt );
+ /**
+ * Make an image link
+ * @param Title $title Title object
+ * @param File $file File object, or false if it doesn't exist
+ *
+ * @param array $frameParams Associative array of parameters external to the media handler.
+ * Boolean parameters are indicated by presence or absence, the value is arbitrary and
+ * will often be false.
+ * thumbnail If present, downscale and frame
+ * manualthumb Image name to use as a thumbnail, instead of automatic scaling
+ * framed Shows image in original size in a frame
+ * frameless Downscale but don't frame
+ * upright If present, tweak default sizes for portrait orientation
+ * upright_factor Fudge factor for "upright" tweak (default 0.75)
+ * border If present, show a border around the image
+ * align Horizontal alignment (left, right, center, none)
+ * valign Vertical alignment (baseline, sub, super, top, text-top, middle,
+ * bottom, text-bottom)
+ * alt Alternate text for image (i.e. alt attribute). Plain text.
+ * caption HTML for image caption.
+ *
+ * @param array $handlerParams Associative array of media handler parameters, to be passed
+ * to transform(). Typical keys are "width" and "page".
+ */
+ function makeImageLink2( Title $title, $file, $frameParams = array(), $handlerParams = array() ) {
+ global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright;
+ if ( $file && !$file->allowInlineDisplay() ) {
+ wfDebug( __METHOD__.': '.$title->getPrefixedDBkey()." does not allow inline display\n" );
+ return $this->makeKnownLinkObj( $title );
}
- $error = $prefix = $postfix = '';
- $page = isset( $params['page'] ) ? $params['page'] : false;
+ // Shortcuts
+ $fp =& $frameParams;
+ $hp =& $handlerParams;
- if ( 'center' == $align )
+ // Clean up parameters
+ $page = isset( $hp['page'] ) ? $hp['page'] : false;
+ if ( !isset( $fp['align'] ) ) $fp['align'] = '';
+ if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
+
+ $prefix = $postfix = '';
+
+ if ( 'center' == $fp['align'] )
{
$prefix = '<div class="center">';
$postfix = '</div>';
- $align = 'none';
+ $fp['align'] = 'none';
}
+ if ( $file && !isset( $hp['width'] ) ) {
+ $hp['width'] = $file->getWidth( $page );
- if ( !isset( $params['width'] ) ) {
- $params['width'] = $img->getWidth( $page );
- if( $thumb || $framed ) {
+ if( isset( $fp['thumbnail'] ) || isset( $fp['framed'] ) || isset( $fp['frameless'] ) || !$hp['width'] ) {
$wopt = $wgUser->getOption( 'thumbsize' );
if( !isset( $wgThumbLimits[$wopt] ) ) {
$wopt = User::getDefaultOption( 'thumbsize' );
}
- $params['width'] = min( $params['width'], $wgThumbLimits[$wopt] );
+ // Reduce width for upright images when parameter 'upright' is used
+ if ( isset( $fp['upright'] ) && $fp['upright'] == 0 ) {
+ $fp['upright'] = $wgThumbUpright;
+ }
+ // Use width which is smaller: real image width or user preference width
+ // For caching health: If width scaled down due to upright parameter, round to full __0 pixel to avoid the creation of a lot of odd thumbs
+ $prefWidth = isset( $fp['upright'] ) ?
+ round( $wgThumbLimits[$wopt] * $fp['upright'], -1 ) :
+ $wgThumbLimits[$wopt];
+ if ( $hp['width'] <= 0 || $prefWidth < $hp['width'] ) {
+ $hp['width'] = $prefWidth;
+ }
}
}
- if ( $thumb || $framed ) {
+ if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) {
# Create a thumbnail. Alignment depends on language
# writing direction, # right aligned for left-to-right-
@@ -462,159 +553,174 @@ class Linker {
#
# If thumbnail width has not been provided, it is set
# to the default user option as specified in Language*.php
- if ( $align == '' ) {
- $align = $wgContLang->isRTL() ? 'left' : 'right';
+ if ( $fp['align'] == '' ) {
+ $fp['align'] = $wgContLang->isRTL() ? 'left' : 'right';
}
- return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
+ return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp ).$postfix;
}
- if ( $params['width'] && $img->exists() ) {
+ if ( $file && $hp['width'] ) {
# Create a resized image, without the additional thumbnail features
- $thumb = $img->transform( $params );
+ $thumb = $file->transform( $hp );
} else {
$thumb = false;
}
- if ( $page ) {
- $query = 'page=' . urlencode( $page );
- } else {
- $query = '';
- }
- $u = $nt->getLocalURL( $query );
- $imgAttribs = array(
- 'alt' => $alt,
- 'longdesc' => $u
- );
- if ( $valign ) {
- $imgAttribs['style'] = "vertical-align: $valign";
- }
- $linkAttribs = array(
- 'href' => $u,
- 'class' => 'image',
- 'title' => $alt
- );
-
if ( !$thumb ) {
- $s = $this->makeBrokenImageLinkObj( $img->getTitle() );
+ $s = $this->makeBrokenImageLinkObj( $title );
} else {
- $s = $thumb->toHtml( $imgAttribs, $linkAttribs );
+ $s = $thumb->toHtml( array(
+ 'desc-link' => true,
+ 'alt' => $fp['alt'],
+ 'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false ,
+ 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ) );
}
- if ( '' != $align ) {
- $s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
+ if ( '' != $fp['align'] ) {
+ $s = "<div class=\"float{$fp['align']}\"><span>{$s}</span></div>";
}
return str_replace("\n", ' ',$prefix.$s.$postfix);
}
/**
* Make HTML for a thumbnail including image, border and caption
- * $img is an Image object
+ * @param Title $title
+ * @param File $file File object or false if it doesn't exist
*/
- function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
+ function makeThumbLinkObj( Title $title, $file, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manualthumb = "" ) {
+ $frameParams = array(
+ 'alt' => $alt,
+ 'caption' => $label,
+ 'align' => $align
+ );
+ if ( $framed ) $frameParams['framed'] = true;
+ if ( $manualthumb ) $frameParams['manualthumb'] = $manualthumb;
+ return $this->makeThumbLink2( $title, $file, $frameParams, $params );
+ }
+
+ function makeThumbLink2( Title $title, $file, $frameParams = array(), $handlerParams = array() ) {
global $wgStylePath, $wgContLang;
- $thumbUrl = '';
- $error = '';
+ $exists = $file && $file->exists();
+
+ # Shortcuts
+ $fp =& $frameParams;
+ $hp =& $handlerParams;
- $page = isset( $params['page'] ) ? $params['page'] : false;
+ $page = isset( $hp['page'] ) ? $hp['page'] : false;
+ if ( !isset( $fp['align'] ) ) $fp['align'] = 'right';
+ if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
+ if ( !isset( $fp['caption'] ) ) $fp['caption'] = '';
- if ( empty( $params['width'] ) ) {
- $params['width'] = 180;
+ if ( empty( $hp['width'] ) ) {
+ // Reduce width for upright images when parameter 'upright' is used
+ $hp['width'] = isset( $fp['upright'] ) ? 130 : 180;
}
$thumb = false;
- if ( $manual_thumb != '' ) {
- # Use manually specified thumbnail
- $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
- if( $manual_title ) {
- $manual_img = new Image( $manual_title );
- $thumb = $manual_img->getUnscaledThumb();
- }
- } elseif ( $framed ) {
- // Use image dimensions, don't scale
- $thumb = $img->getUnscaledThumb( $page );
- } else {
- $thumb = $img->transform( $params );
- }
- if ( $thumb ) {
- $outerWidth = $thumb->getWidth() + 2;
+ if ( !$exists ) {
+ $outerWidth = $hp['width'] + 2;
} else {
- $outerWidth = $params['width'] + 2;
+ if ( isset( $fp['manualthumb'] ) ) {
+ # Use manually specified thumbnail
+ $manual_title = Title::makeTitleSafe( NS_IMAGE, $fp['manualthumb'] );
+ if( $manual_title ) {
+ $manual_img = wfFindFile( $manual_title );
+ if ( $manual_img ) {
+ $thumb = $manual_img->getUnscaledThumb();
+ } else {
+ $exists = false;
+ }
+ }
+ } elseif ( isset( $fp['framed'] ) ) {
+ // Use image dimensions, don't scale
+ $thumb = $file->getUnscaledThumb( $page );
+ } else {
+ # Do not present an image bigger than the source, for bitmap-style images
+ # This is a hack to maintain compatibility with arbitrary pre-1.10 behaviour
+ $srcWidth = $file->getWidth( $page );
+ if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
+ $hp['width'] = $srcWidth;
+ }
+ $thumb = $file->transform( $hp );
+ }
+
+ if ( $thumb ) {
+ $outerWidth = $thumb->getWidth() + 2;
+ } else {
+ $outerWidth = $hp['width'] + 2;
+ }
}
$query = $page ? 'page=' . urlencode( $page ) : '';
- $u = $img->getTitle()->getLocalURL( $query );
+ $url = $title->getLocalURL( $query );
$more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
$magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
$textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
- $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
- if ( !$thumb ) {
- $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
+ $s = "<div class=\"thumb t{$fp['align']}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
+ if( !$exists ) {
+ $s .= $this->makeBrokenImageLinkObj( $title );
$zoomicon = '';
- } elseif( !$img->exists() ) {
- $s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
+ } elseif ( !$thumb ) {
+ $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
$zoomicon = '';
} else {
- $imgAttribs = array(
- 'alt' => $alt,
- 'longdesc' => $u,
- 'class' => 'thumbimage'
- );
- $linkAttribs = array(
- 'href' => $u,
- 'class' => 'internal',
- 'title' => $alt
- );
-
- $s .= $thumb->toHtml( $imgAttribs, $linkAttribs );
- if ( $framed ) {
+ $s .= $thumb->toHtml( array(
+ 'alt' => $fp['alt'],
+ 'img-class' => 'thumbimage',
+ 'desc-link' => true ) );
+ if ( isset( $fp['framed'] ) ) {
$zoomicon="";
} else {
$zoomicon = '<div class="magnify" style="float:'.$magnifyalign.'">'.
- '<a href="'.$u.'" class="internal" title="'.$more.'">'.
+ '<a href="'.$url.'" class="internal" title="'.$more.'">'.
'<img src="'.$wgStylePath.'/common/images/magnify-clip.png" ' .
'width="15" height="11" alt="" /></a></div>';
}
}
- $s .= ' <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$label."</div></div></div>";
+ $s .= ' <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$fp['caption']."</div></div></div>";
return str_replace("\n", ' ', $s);
}
/**
- * Pass a title object, not a title string
+ * Make a "broken" link to an image
+ *
+ * @param Title $title Image title
+ * @param string $text Link label
+ * @param string $query Query string
+ * @param string $trail Link trail
+ * @param string $prefix Link prefix
+ * @return string
*/
- function makeBrokenImageLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
- # Fail gracefully
- if ( ! isset($nt) ) {
- # throw new MWException();
+ public function makeBrokenImageLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' ) {
+ global $wgEnableUploads;
+ if( $title instanceof Title ) {
+ wfProfileIn( __METHOD__ );
+ if( $wgEnableUploads ) {
+ $upload = SpecialPage::getTitleFor( 'Upload' );
+ if( $text == '' )
+ $text = htmlspecialchars( $title->getPrefixedText() );
+ $q = 'wpDestFile=' . $title->getPartialUrl();
+ if( $query != '' )
+ $q .= '&' . $query;
+ list( $inside, $trail ) = self::splitTrail( $trail );
+ $style = $this->getInternalLinkAttributesObj( $title, $text, 'yes' );
+ wfProfileOut( __METHOD__ );
+ return '<a href="' . $upload->escapeLocalUrl( $q ) . '"'
+ . $style . '>' . $prefix . $text . $inside . '</a>' . $trail;
+ } else {
+ wfProfileOut( __METHOD__ );
+ return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix );
+ }
+ } else {
return "<!-- ERROR -->{$prefix}{$text}{$trail}";
}
-
- $fname = 'Linker::makeBrokenImageLinkObj';
- wfProfileIn( $fname );
-
- $q = 'wpDestFile=' . urlencode( $nt->getDBkey() );
- if ( '' != $query ) {
- $q .= "&$query";
- }
- $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
- $url = $uploadTitle->escapeLocalURL( $q );
-
- if ( '' == $text ) {
- $text = htmlspecialchars( $nt->getPrefixedText() );
- }
- $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
- list( $inside, $trail ) = Linker::splitTrail( $trail );
- $s = "<a href=\"{$url}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
-
- wfProfileOut( $fname );
- return $s;
}
- /** @todo document */
- function makeMediaLink( $name, /* wtf?! */ $url, $alt = '' ) {
+ /** @deprecated use Linker::makeMediaLinkObj() */
+ function makeMediaLink( $name, $unused = '', $text = '' ) {
$nt = Title::makeTitleSafe( NS_IMAGE, $name );
- return $this->makeMediaLinkObj( $nt, $alt );
+ return $this->makeMediaLinkObj( $nt, $text );
}
/**
@@ -632,13 +738,13 @@ class Linker {
### HOTFIX. Instead of breaking, return empty string.
return $text;
} else {
- $img = new Image( $title );
- if( $img->exists() ) {
+ $img = wfFindFile( $title );
+ if( $img ) {
$url = $img->getURL();
$class = 'internal';
} else {
$upload = SpecialPage::getTitleFor( 'Upload' );
- $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $img->getName() ) );
+ $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $title->getDbKey() ) );
$class = 'new';
}
$alt = htmlspecialchars( $title->getText() );
@@ -694,15 +800,18 @@ class Linker {
}
/**
- * @param $userId Integer: user id in database.
- * @param $userText String: user name in database.
- * @param $redContribsWhenNoEdits Bool: return a red contribs link when the user had no edits and this is true.
- * @return string HTML fragment with talk and/or block links
+ * Generate standard user tool links (talk, contributions, block link, etc.)
+ *
+ * @param int $userId User identifier
+ * @param string $userText User name or IP address
+ * @param bool $redContribsWhenNoEdits Should the contributions link be red if the user has no edits?
+ * @param int $flags Customisation flags (e.g. self::TOOL_LINKS_NOBLOCK)
+ * @return string
*/
- public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false ) {
+ public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0 ) {
global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
$talkable = !( $wgDisableAnonTalk && 0 == $userId );
- $blockable = ( $wgSysopUserBans || 0 == $userId );
+ $blockable = ( $wgSysopUserBans || 0 == $userId ) && !$flags & self::TOOL_LINKS_NOBLOCK;
$items = array();
if( $talkable ) {
@@ -711,7 +820,7 @@ class Linker {
if( $userId ) {
// check if the user has an edit
if( $redContribsWhenNoEdits && User::edits( $userId ) == 0 ) {
- $style = "class='new'";
+ $style = " class='new'";
} else {
$style = '';
}
@@ -818,16 +927,33 @@ class Linker {
function formatComment($comment, $title = NULL, $local = false) {
wfProfileIn( __METHOD__ );
- global $wgContLang;
+ # Sanitize text a bit:
$comment = str_replace( "\n", " ", $comment );
$comment = htmlspecialchars( $comment );
- # The pattern for autogen comments is / * foo * /, which makes for
- # some nasty regex.
- # We look for all comments, match any text before and after the comment,
- # add a separator where needed and format the comment itself with CSS
+ # Render autocomments and make links:
+ $comment = $this->formatAutoComments( $comment, $title, $local );
+ $comment = $this->formatLinksInComment( $comment );
+
+ wfProfileOut( __METHOD__ );
+ return $comment;
+ }
+
+ /**
+ * The pattern for autogen comments is / * foo * /, which makes for
+ * some nasty regex.
+ * We look for all comments, match any text before and after the comment,
+ * add a separator where needed and format the comment itself with CSS
+ * Called by Linker::formatComment.
+ *
+ * @param $comment Comment text
+ * @param $title An optional title object used to links to sections
+ *
+ * @todo Document the $local parameter.
+ */
+ private function formatAutocomments( $comment, $title = NULL, $local = false ) {
$match = array();
- while (preg_match('/(.*)\/\*\s*(.*?)\s*\*\/(.*)/', $comment,$match)) {
+ while (preg_match('!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment,$match)) {
$pre=$match[1];
$auto=$match[2];
$post=$match[3];
@@ -859,10 +985,23 @@ class Linker {
$comment=$pre.$auto.$post;
}
- # format regular and media links - all other wiki formatting
- # is ignored
+ return $comment;
+ }
+
+ /**
+ * Formats wiki links and media links in text; all other wiki formatting
+ * is ignored
+ *
+ * @param string $comment Text to format links in
+ * @return string
+ */
+ public function formatLinksInComment( $comment ) {
+ global $wgContLang;
+
$medians = '(?:' . preg_quote( Namespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
$medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA ), '/' ) . '):';
+
+ $match = array();
while(preg_match('/\[\[:?(.*?)(\|(.*?))*\]\](.*)$/',$comment,$match)) {
# Handle link renaming [[foo|text]] will show link as "text"
if( "" != $match[3] ) {
@@ -889,7 +1028,7 @@ class Linker {
}
$comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 );
}
- wfProfileOut( __METHOD__ );
+
return $comment;
}
@@ -914,7 +1053,7 @@ class Linker {
return " <span class=\"comment\">($formatted)</span>";
}
}
-
+
/**
* Wrap and format the given revision's comment block, if the current
* user is allowed to view it.
@@ -981,31 +1120,66 @@ class Linker {
. "</script>\n";
}
- /** @todo document */
+ /**
+ * Used to generate section edit links that point to "other" pages
+ * (sections that are really part of included pages).
+ *
+ * @param $title Title string.
+ * @param $section Integer: section number.
+ */
public function editSectionLinkForOther( $title, $section ) {
- global $wgContLang;
-
$title = Title::newFromText( $title );
- $editurl = '&section='.$section;
- $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
-
- return "<span class=\"editsection\">[".$url."]</span>";
-
+ return $this->doEditSectionLink( $title, $section, '', 'EditSectionLinkForOther' );
}
/**
- * @param $title Title object.
+ * @param $nt Title object.
* @param $section Integer: section number.
* @param $hint Link String: title, or default if omitted or empty
*/
- public function editSectionLink( $nt, $section, $hint='' ) {
- global $wgContLang;
+ public function editSectionLink( Title $nt, $section, $hint='' ) {
+ if( $hint != '' ) {
+ $hint = wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) );
+ $hint = " title=\"$hint\"";
+ }
+ return $this->doEditSectionLink( $nt, $section, $hint, 'EditSectionLink' );
+ }
+ /**
+ * Implement editSectionLink and editSectionLinkForOther.
+ *
+ * @param $nt Title object
+ * @param $section Integer, section number
+ * @param $hint String, for HTML title attribute
+ * @param $hook String, name of hook to run
+ * @return String, HTML to use for edit link
+ */
+ protected function doEditSectionLink( Title $nt, $section, $hint, $hook ) {
+ global $wgContLang;
$editurl = '&section='.$section;
- $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
- $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
+ $url = $this->makeKnownLinkObj(
+ $nt,
+ wfMsg('editsection'),
+ 'action=edit'.$editurl,
+ '', '', '', $hint
+ );
+ $result = null;
- return "<span class=\"editsection\">[".$url."]</span>";
+ // The two hooks have slightly different interfaces . . .
+ if( $hook == 'EditSectionLink' ) {
+ wfRunHooks( $hook, array( &$this, $nt, $section, $hint, $url, &$result ) );
+ } elseif( $hook == 'EditSectionLinkForOther' ) {
+ wfRunHooks( $hook, array( &$this, $nt, $section, $url, &$result ) );
+ }
+
+ // For reverse compatibility, add the brackets *after* the hook is run,
+ // and even add them to hook-provided text.
+ if( is_null( $result ) ) {
+ $result = wfMsg( 'editsection-brackets', $url );
+ } else {
+ $result = wfMsg( 'editsection-brackets', $result );
+ }
+ return "<span class=\"editsection\">$result</span>";
}
/**
@@ -1061,15 +1235,28 @@ class Linker {
* @param Revision $rev
*/
function generateRollback( $rev ) {
- global $wgUser, $wgRequest;
+ return '<span class="mw-rollback-link">['
+ . $this->buildRollbackLink( $rev )
+ . ']</span>';
+ }
+
+ /**
+ * Build a raw rollback link, useful for collections of "tool" links
+ *
+ * @param Revision $rev
+ * @return string
+ */
+ public function buildRollbackLink( $rev ) {
+ global $wgRequest, $wgUser;
$title = $rev->getTitle();
-
- $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
- $extraRollback .= '&token=' . urlencode(
- $wgUser->editToken( array( $title->getPrefixedText(), $rev->getUserText() ) ) );
- return '<span class="mw-rollback-link">['. $this->makeKnownLinkObj( $title,
- wfMsg('rollbacklink'),
- 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extraRollback ) .']</span>';
+ $extra = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
+ $extra .= '&token=' . urlencode( $wgUser->editToken( array( $title->getPrefixedText(),
+ $rev->getUserText() ) ) );
+ return $this->makeKnownLinkObj(
+ $title,
+ wfMsgHtml( 'rollbacklink' ),
+ 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extra
+ );
}
/**
@@ -1133,28 +1320,7 @@ class Linker {
*/
public function formatSize( $size ) {
global $wgLang;
- // For small sizes no decimal places necessary
- $round = 0;
- if( $size > 1024 ) {
- $size = $size / 1024;
- if( $size > 1024 ) {
- $size = $size / 1024;
- // For MB and bigger two decimal places are smarter
- $round = 2;
- if( $size > 1024 ) {
- $size = $size / 1024;
- $msg = 'size-gigabytes';
- } else {
- $msg = 'size-megabytes';
- }
- } else {
- $msg = 'size-kilobytes';
- }
- } else {
- $msg = 'size-bytes';
- }
- $size = round( $size, $round );
- return wfMsgHtml( $msg, $wgLang->formatNum( $size ) );
+ return htmlspecialchars( $wgLang->formatSize( $size ) );
}
/**
@@ -1208,4 +1374,6 @@ class Linker {
}
}
-?>
+
+
+
diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php
index 856c665d..9bcd9d67 100644
--- a/includes/LinksUpdate.php
+++ b/includes/LinksUpdate.php
@@ -9,7 +9,7 @@ class LinksUpdate {
/**@{{
* @private
*/
- var $mId, //!< Page ID of the article linked from
+ var $mId, //!< Page ID of the article linked from
$mTitle, //!< Title object of the article linked from
$mLinks, //!< Map of title strings to IDs for the links in the document
$mImages, //!< DB keys of the images used, in the array key only
@@ -24,10 +24,10 @@ class LinksUpdate {
/**
* Constructor
- * Initialize private variables
- * @param $title Integer: FIXME
- * @param $parserOutput FIXME
- * @param $recursive Boolean: FIXME, default 'true'.
+ *
+ * @param Title $title Title of the page we're updating
+ * @param ParserOutput $parserOutput Output from a full parse of this page
+ * @param bool $recursive Queue jobs for recursive updates?
*/
function LinksUpdate( $title, $parserOutput, $recursive = true ) {
global $wgAntiLockFlags;
@@ -64,6 +64,8 @@ class LinksUpdate {
}
$this->mRecursive = $recursive;
+
+ wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
}
/**
@@ -188,7 +190,7 @@ class LinksUpdate {
break;
}
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $jobs[] = Job::factory( 'refreshLinks', $title );
+ $jobs[] = new RefreshLinksJob( $title, '' );
}
Job::batchInsert( $jobs );
}
@@ -594,4 +596,4 @@ class LinksUpdate {
return $arr;
}
}
-?>
+
diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php
index 4ebe26c7..65a6d5a6 100644
--- a/includes/LoadBalancer.php
+++ b/includes/LoadBalancer.php
@@ -646,4 +646,4 @@ class LoadBalancer {
}
}
-?>
+
diff --git a/includes/LogPage.php b/includes/LogPage.php
index af03bbba..8982b59f 100644
--- a/includes/LogPage.php
+++ b/includes/LogPage.php
@@ -55,42 +55,52 @@ class LogPage {
$dbw = wfGetDB( DB_MASTER );
$uid = $wgUser->getID();
+ $log_id = $dbw->nextSequenceValue( 'log_log_id_seq' );
$this->timestamp = $now = wfTimestampNow();
- $dbw->insert( 'logging',
- array(
- 'log_type' => $this->type,
- 'log_action' => $this->action,
- 'log_timestamp' => $dbw->timestamp( $now ),
- 'log_user' => $uid,
- 'log_namespace' => $this->target->getNamespace(),
- 'log_title' => $this->target->getDBkey(),
- 'log_comment' => $this->comment,
- 'log_params' => $this->params
- ), $fname
+ $data = array(
+ 'log_type' => $this->type,
+ 'log_action' => $this->action,
+ 'log_timestamp' => $dbw->timestamp( $now ),
+ 'log_user' => $uid,
+ 'log_namespace' => $this->target->getNamespace(),
+ 'log_title' => $this->target->getDBkey(),
+ 'log_comment' => $this->comment,
+ 'log_params' => $this->params
);
+ # log_id doesn't exist on Wikimedia servers yet, and it's a tricky
+ # schema update to do. Hack it for now to ignore the field on MySQL.
+ if ( !is_null( $log_id ) ) {
+ $data['log_id'] = $log_id;
+ }
+ $dbw->insert( 'logging', $data, $fname );
+
# And update recentchanges
if ( $this->updateRecentChanges ) {
$titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
- $rcComment = $this->actionText;
- if( '' != $this->comment ) {
- if ($rcComment == '')
- $rcComment = $this->comment;
- else
- $rcComment .= ': ' . $this->comment;
- }
-
+ $rcComment = $this->getRcComment();
RecentChange::notifyLog( $now, $titleObj, $wgUser, $rcComment, '',
$this->type, $this->action, $this->target, $this->comment, $this->params );
}
return true;
}
+ public function getRcComment() {
+ $rcComment = $this->actionText;
+ if( '' != $this->comment ) {
+ if ($rcComment == '')
+ $rcComment = $this->comment;
+ else
+ $rcComment .= ': ' . $this->comment;
+ }
+ return $rcComment;
+ }
+
/**
* @static
*/
- function validTypes() {
+ public static function validTypes() {
global $wgLogTypes;
return $wgLogTypes;
}
@@ -98,7 +108,7 @@ class LogPage {
/**
* @static
*/
- function isLogType( $type ) {
+ public static function isLogType( $type ) {
return in_array( $type, LogPage::validTypes() );
}
@@ -120,7 +130,7 @@ class LogPage {
* @todo handle missing log types
* @static
*/
- function logHeader( $type ) {
+ static function logHeader( $type ) {
global $wgLogHeaders;
return wfMsg( $wgLogHeaders[$type] );
}
@@ -128,7 +138,7 @@ class LogPage {
/**
* @static
*/
- function actionText( $type, $action, $title = NULL, $skin = NULL, $params = array(), $filterWikilinks=false, $translate=false ) {
+ static function actionText( $type, $action, $title = NULL, $skin = NULL, $params = array(), $filterWikilinks=false, $translate=false ) {
global $wgLang, $wgContLang, $wgLogActions;
$key = "$type/$action";
@@ -145,14 +155,17 @@ class LogPage {
switch( $type ) {
case 'move':
$titleLink = $skin->makeLinkObj( $title, $title->getPrefixedText(), 'redirect=no' );
- $params[0] = $skin->makeLinkObj( Title::newFromText( $params[0] ), $params[0] );
+ $params[0] = $skin->makeLinkObj( Title::newFromText( $params[0] ), htmlspecialchars( $params[0] ) );
break;
case 'block':
if( substr( $title->getText(), 0, 1 ) == '#' ) {
$titleLink = $title->getText();
} else {
- $titleLink = $skin->makeLinkObj( $title, $title->getText() );
- $titleLink .= ' (' . $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Contributions', $title->getDBkey() ), wfMsg( 'contribslink' ) ) . ')';
+ // TODO: Store the user identifier in the parameters
+ // to make this faster for future log entries
+ $id = User::idFromName( $title->getText() );
+ $titleLink = $skin->userLink( $id, $title->getText() )
+ . $skin->userToolLinks( $id, $title->getText(), false, Linker::TOOL_LINKS_NOBLOCK );
}
break;
case 'rights':
@@ -233,7 +246,7 @@ class LogPage {
* Create a blob from a parameter array
* @static
*/
- function makeParamBlob( $params ) {
+ static function makeParamBlob( $params ) {
return implode( "\n", $params );
}
@@ -241,7 +254,7 @@ class LogPage {
* Extract a parameter array from a blob
* @static
*/
- function extractParams( $blob ) {
+ static function extractParams( $blob ) {
if ( $blob === '' ) {
return array();
} else {
@@ -285,4 +298,4 @@ class LogPage {
}
-?>
+
diff --git a/includes/MacBinary.php b/includes/MacBinary.php
index 2f6ad4f4..da357e52 100644
--- a/includes/MacBinary.php
+++ b/includes/MacBinary.php
@@ -100,7 +100,7 @@ class MacBinary {
fseek( $this->handle, 0 );
$head = fread( $this->handle, 128 );
- $this->hexdump( $head );
+ #$this->hexdump( $head );
if( strlen( $head ) < 128 ) {
wfDebug( "$fname: couldn't read full MacBinary header\n" );
@@ -268,4 +268,4 @@ class MacBinary {
}
}
-?>
+
diff --git a/includes/MagicWord.php b/includes/MagicWord.php
index bf72a0c8..f7a9400d 100644
--- a/includes/MagicWord.php
+++ b/includes/MagicWord.php
@@ -86,7 +86,6 @@ class MagicWord {
'subjectpagename',
'subjectpagenamee',
'numberofusers',
- 'rawsuffix',
'newsectionlink',
'numberofpages',
'currentversion',
@@ -387,6 +386,181 @@ class MagicWord {
function isCaseSensitive() {
return $this->mCaseSensitive;
}
+
+ function getId() {
+ return $this->mId;
+ }
}
-?>
+/**
+ * Class for handling an array of magic words
+ */
+class MagicWordArray {
+ var $names = array();
+ var $hash;
+ var $baseRegex, $regex;
+
+ function __construct( $names = array() ) {
+ $this->names = $names;
+ }
+
+ /**
+ * Add a magic word by name
+ */
+ public function add( $name ) {
+ global $wgContLang;
+ $this->names[] = $name;
+ $this->hash = $this->baseRegex = $this->regex = null;
+ }
+
+ /**
+ * Add a number of magic words by name
+ */
+ public function addArray( $names ) {
+ $this->names = array_merge( $this->names, array_values( $names ) );
+ $this->hash = $this->baseRegex = $this->regex = null;
+ }
+
+ /**
+ * Get a 2-d hashtable for this array
+ */
+ function getHash() {
+ if ( is_null( $this->hash ) ) {
+ global $wgContLang;
+ $this->hash = array( 0 => array(), 1 => array() );
+ foreach ( $this->names as $name ) {
+ $magic = MagicWord::get( $name );
+ $case = intval( $magic->isCaseSensitive() );
+ foreach ( $magic->getSynonyms() as $syn ) {
+ if ( !$case ) {
+ $syn = $wgContLang->lc( $syn );
+ }
+ $this->hash[$case][$syn] = $name;
+ }
+ }
+ }
+ return $this->hash;
+ }
+
+ /**
+ * Get the base regex
+ */
+ function getBaseRegex() {
+ if ( is_null( $this->baseRegex ) ) {
+ $this->baseRegex = array( 0 => '', 1 => '' );
+ foreach ( $this->names as $name ) {
+ $magic = MagicWord::get( $name );
+ $case = intval( $magic->isCaseSensitive() );
+ foreach ( $magic->getSynonyms() as $i => $syn ) {
+ $group = "(?P<{$i}_{$name}>" . preg_quote( $syn, '/' ) . ')';
+ if ( $this->baseRegex[$case] === '' ) {
+ $this->baseRegex[$case] = $group;
+ } else {
+ $this->baseRegex[$case] .= '|' . $group;
+ }
+ }
+ }
+ }
+ return $this->baseRegex;
+ }
+
+ /**
+ * Get an unanchored regex
+ */
+ function getRegex() {
+ if ( is_null( $this->regex ) ) {
+ $base = $this->getBaseRegex();
+ $this->regex = array( '', '' );
+ if ( $this->baseRegex[0] !== '' ) {
+ $this->regex[0] = "/{$base[0]}/iuS";
+ }
+ if ( $this->baseRegex[1] !== '' ) {
+ $this->regex[1] = "/{$base[1]}/S";
+ }
+ }
+ return $this->regex;
+ }
+
+ /**
+ * Get a regex for matching variables
+ */
+ function getVariableRegex() {
+ return str_replace( "\\$1", "(.*?)", $this->getRegex() );
+ }
+
+ /**
+ * Get an anchored regex for matching variables
+ */
+ function getVariableStartToEndRegex() {
+ $base = $this->getBaseRegex();
+ $newRegex = array( '', '' );
+ if ( $base[0] !== '' ) {
+ $newRegex[0] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[0]})$/iuS" );
+ }
+ if ( $base[1] !== '' ) {
+ $newRegex[1] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[1]})$/S" );
+ }
+ return $newRegex;
+ }
+
+ /**
+ * Parse a match array from preg_match
+ */
+ function parseMatch( $m ) {
+ reset( $m );
+ while ( list( $key, $value ) = each( $m ) ) {
+ if ( $key === 0 || $value === '' ) {
+ continue;
+ }
+ $parts = explode( '_', $key, 2 );
+ if ( count( $parts ) != 2 ) {
+ // This shouldn't happen
+ // continue;
+ throw new MWException( __METHOD__ . ': bad parameter name' );
+ }
+ list( /* $synIndex */, $magicName ) = $parts;
+ $paramValue = next( $m );
+ return array( $magicName, $paramValue );
+ }
+ // This shouldn't happen either
+ throw new MWException( __METHOD__.': parameter not found' );
+ return array( false, false );
+ }
+
+ /**
+ * Match some text, with parameter capture
+ * Returns an array with the magic word name in the first element and the
+ * parameter in the second element.
+ * Both elements are false if there was no match.
+ */
+ public function matchVariableStartToEnd( $text ) {
+ global $wgContLang;
+ $regexes = $this->getVariableStartToEndRegex();
+ foreach ( $regexes as $regex ) {
+ if ( $regex !== '' ) {
+ $m = false;
+ if ( preg_match( $regex, $text, $m ) ) {
+ return $this->parseMatch( $m );
+ }
+ }
+ }
+ return array( false, false );
+ }
+
+ /**
+ * Match some text, without parameter capture
+ * Returns the magic word name, or false if there was no capture
+ */
+ public function matchStartToEnd( $text ) {
+ $hash = $this->getHash();
+ if ( isset( $hash[1][$text] ) ) {
+ return $hash[1][$text];
+ }
+ global $wgContLang;
+ $lc = $wgContLang->lc( $text );
+ if ( isset( $hash[0][$lc] ) ) {
+ return $hash[0][$lc];
+ }
+ return false;
+ }
+}
diff --git a/includes/Math.php b/includes/Math.php
index 88934e5f..2771d04c 100644
--- a/includes/Math.php
+++ b/includes/Math.php
@@ -20,8 +20,9 @@ class MathRenderer {
var $mathml = '';
var $conservativeness = 0;
- function __construct( $tex ) {
+ function __construct( $tex, $params=array() ) {
$this->tex = $tex;
+ $this->params = $params;
}
function setOutputMode( $mode ) {
@@ -157,8 +158,8 @@ class MathRenderer {
$dbw = wfGetDB( DB_MASTER );
$dbw->replace( 'math', array( 'math_inputhash' ),
array(
- 'math_inputhash' => $md5_sql,
- 'math_outputhash' => $outmd5_sql,
+ 'math_inputhash' => $dbw->encodeBlob($md5_sql),
+ 'math_outputhash' => $dbw->encodeBlob($outmd5_sql),
'math_html_conservativeness' => $this->conservativeness,
'math_html' => $this->html,
'math_mathml' => $this->mathml,
@@ -186,13 +187,13 @@ class MathRenderer {
$dbr = wfGetDB( DB_SLAVE );
$rpage = $dbr->selectRow( 'math',
array( 'math_outputhash','math_html_conservativeness','math_html','math_mathml' ),
- array( 'math_inputhash' => pack("H32", $this->md5)), # Binary packed, not hex
+ array( 'math_inputhash' => $dbr->encodeBlob(pack("H32", $this->md5))), # Binary packed, not hex
$fname
);
if( $rpage !== false ) {
# Tailing 0x20s can get dropped by the database, add it back on if necessary:
- $xhash = unpack( 'H32md5', $rpage->math_outputhash . " " );
+ $xhash = unpack( 'H32md5', $dbr->decodeBlob($rpage->math_outputhash) . " " );
$this->hash = $xhash ['md5'];
$this->conservativeness = $rpage->math_html_conservativeness;
@@ -233,24 +234,44 @@ class MathRenderer {
*/
function _doRender() {
if( $this->mode == MW_MATH_MATHML && $this->mathml != '' ) {
- return "<math xmlns='http://www.w3.org/1998/Math/MathML'>{$this->mathml}</math>";
+ return Xml::tags( 'math',
+ $this->_attribs( 'math',
+ array( 'xmlns' => 'http://www.w3.org/1998/Math/MathML' ) ),
+ $this->mathml );
}
if (($this->mode == MW_MATH_PNG) || ($this->html == '') ||
(($this->mode == MW_MATH_SIMPLE) && ($this->conservativeness != 2)) ||
(($this->mode == MW_MATH_MODERN || $this->mode == MW_MATH_MATHML) && ($this->conservativeness == 0))) {
return $this->_linkToMathImage();
} else {
- return '<span class="texhtml">'.$this->html.'</span>';
+ return Xml::tags( 'span',
+ $this->_attribs( 'span',
+ array( 'class' => 'texhtml' ) ),
+ $this->html );
}
}
+
+ function _attribs( $tag, $defaults=array(), $overrides=array() ) {
+ $attribs = Sanitizer::validateTagAttributes( $this->params, $tag );
+ $attribs = Sanitizer::mergeAttributes( $defaults, $attribs );
+ $attribs = Sanitizer::mergeAttributes( $attribs, $overrides );
+ return $attribs;
+ }
function _linkToMathImage() {
global $wgMathPath;
- $url = htmlspecialchars( "$wgMathPath/" . substr($this->hash, 0, 1)
+ $url = "$wgMathPath/" . substr($this->hash, 0, 1)
.'/'. substr($this->hash, 1, 1) .'/'. substr($this->hash, 2, 1)
- . "/{$this->hash}.png" );
- $alt = trim(str_replace("\n", ' ', htmlspecialchars( $this->tex )));
- return "<img class='tex' src=\"$url\" alt=\"$alt\" />";
+ . "/{$this->hash}.png";
+
+ return Xml::element( 'img',
+ $this->_attribs(
+ 'img',
+ array(
+ 'class' => 'tex',
+ 'alt' => $this->tex ),
+ array(
+ 'src' => $url ) ) );
}
function _getHashPath() {
@@ -262,11 +283,11 @@ class MathRenderer {
return $path;
}
- public static function renderMath( $tex ) {
+ public static function renderMath( $tex, $params=array() ) {
global $wgUser;
- $math = new MathRenderer( $tex );
+ $math = new MathRenderer( $tex, $params );
$math->setOutputMode( $wgUser->getOption('math'));
return $math->render();
}
}
-?>
+
diff --git a/includes/MediaTransformOutput.php b/includes/MediaTransformOutput.php
index 60057e3a..c6cf9ac2 100644
--- a/includes/MediaTransformOutput.php
+++ b/includes/MediaTransformOutput.php
@@ -1,11 +1,13 @@
<?php
/**
- * Base class for the output of MediaHandler::doTransform() and Image::transform().
+ * Base class for the output of MediaHandler::doTransform() and File::transform().
*
* @addtogroup Media
*/
abstract class MediaTransformOutput {
+ var $file, $width, $height, $url, $page, $path;
+
/**
* Get the width of the output box
*/
@@ -36,12 +38,23 @@ abstract class MediaTransformOutput {
/**
* Fetch HTML for this transform output
- * @param array $attribs Advisory associative array of HTML attributes supplied
- * by the linker. These can be incorporated into the output in any way.
- * @param array $linkAttribs Attributes of a suggested enclosing <a> tag.
- * May be ignored.
+ *
+ * @param array $options Associative array of options. Boolean options
+ * should be indicated with a value of true for true, and false or
+ * absent for false.
+ *
+ * alt Alternate text or caption
+ * desc-link Boolean, show a description link
+ * file-link Boolean, show a file download link
+ * valign vertical-align property, if the output is an inline element
+ * img-class Class applied to the <img> tag, if there is such a tag
+ *
+ * For images, desc-link and file-link are implemented as a click-through. For
+ * sounds and videos, they may be displayed in other ways.
+ *
+ * @return string
*/
- abstract function toHtml( $attribs = array() , $linkAttribs = false );
+ abstract function toHtml( $options = array() );
/**
* This will be overridden to return true in error classes
@@ -60,6 +73,19 @@ abstract class MediaTransformOutput {
return $contents;
}
}
+
+ function getDescLinkAttribs( $alt = false ) {
+ $query = $this->page ? ( 'page=' . urlencode( $this->page ) ) : '';
+ $title = $this->file->getTitle();
+ if ( strval( $alt ) === '' ) {
+ $alt = $title->getText();
+ }
+ return array(
+ 'href' => $this->file->getTitle()->getLocalURL( $query ),
+ 'class' => 'image',
+ 'title' => $alt
+ );
+ }
}
@@ -74,7 +100,8 @@ class ThumbnailImage extends MediaTransformOutput {
* @param string $url URL path to the thumb
* @private
*/
- function ThumbnailImage( $url, $width, $height, $path = false ) {
+ function ThumbnailImage( $file, $url, $width, $height, $path = false, $page = false ) {
+ $this->file = $file;
$this->url = $url;
# These should be integers when they get here.
# If not, there's a bug somewhere. But let's at
@@ -82,28 +109,56 @@ class ThumbnailImage extends MediaTransformOutput {
$this->width = round( $width );
$this->height = round( $height );
$this->path = $path;
+ $this->page = $page;
}
/**
* Return HTML <img ... /> tag for the thumbnail, will include
* width and height attributes and a blank alt text (as required).
+ *
+ * @param array $options Associative array of options. Boolean options
+ * should be indicated with a value of true for true, and false or
+ * absent for false.
*
- * You can set or override additional attributes by passing an
- * associative array of name => data pairs. The data will be escaped
- * for HTML output, so should be in plaintext.
+ * alt Alternate text or caption
+ * desc-link Boolean, show a description link
+ * file-link Boolean, show a file download link
+ * valign vertical-align property, if the output is an inline element
+ * img-class Class applied to the <img> tag, if there is such a tag
*
- * If $linkAttribs is given, the image will be enclosed in an <a> tag.
+ * For images, desc-link and file-link are implemented as a click-through. For
+ * sounds and videos, they may be displayed in other ways.
*
- * @param array $attribs
- * @param array $linkAttribs
* @return string
* @public
*/
- function toHtml( $attribs = array(), $linkAttribs = false ) {
- $attribs['src'] = $this->url;
- $attribs['width'] = $this->width;
- $attribs['height'] = $this->height;
- if( !isset( $attribs['alt'] ) ) $attribs['alt'] = '';
+ function toHtml( $options = array() ) {
+ if ( count( func_get_args() ) == 2 ) {
+ throw new MWException( __METHOD__ .' called in the old style' );
+ }
+
+ $alt = empty( $options['alt'] ) ? '' : $options['alt'];
+ if ( !empty( $options['desc-link'] ) ) {
+ $linkAttribs = $this->getDescLinkAttribs( $alt );
+ } elseif ( !empty( $options['file-link'] ) ) {
+ $linkAttribs = array( 'href' => $this->file->getURL() );
+ } else {
+ $linkAttribs = false;
+ }
+
+ $attribs = array(
+ 'alt' => $alt,
+ 'src' => $this->url,
+ 'width' => $this->width,
+ 'height' => $this->height,
+ 'border' => 0,
+ );
+ if ( !empty( $options['valign'] ) ) {
+ $attribs['style'] = "vertical-align: {$options['valign']}";
+ }
+ if ( !empty( $options['img-class'] ) ) {
+ $attribs['class'] = $options['img-class'];
+ }
return $this->linkWrap( $linkAttribs, Xml::element( 'img', $attribs ) );
}
@@ -130,7 +185,7 @@ class MediaTransformError extends MediaTransformOutput {
$this->path = false;
}
- function toHtml( $attribs = array(), $linkAttribs = false ) {
+ function toHtml( $options = array() ) {
return "<table class=\"MediaTransformError\" style=\"" .
"width: {$this->width}px; height: {$this->height}px;\"><tr><td>" .
$this->htmlMsg .
@@ -158,9 +213,10 @@ class MediaTransformError extends MediaTransformOutput {
class TransformParameterError extends MediaTransformError {
function __construct( $params ) {
parent::__construct( 'thumbnail_error',
- max( @$params['width'], 180 ), max( @$params['height'], 180 ),
+ max( isset( $params['width'] ) ? $params['width'] : 0, 180 ),
+ max( isset( $params['height'] ) ? $params['height'] : 0, 180 ),
wfMsg( 'thumbnail_invalid_params' ) );
}
}
-?>
+
diff --git a/includes/MemcachedSessions.php b/includes/MemcachedSessions.php
index 3bcf5535..3b248cf0 100644
--- a/includes/MemcachedSessions.php
+++ b/includes/MemcachedSessions.php
@@ -69,4 +69,4 @@ function memsess_gc( $maxlifetime ) {
session_set_save_handler( 'memsess_open', 'memsess_close', 'memsess_read', 'memsess_write', 'memsess_destroy', 'memsess_gc' );
-?>
+
diff --git a/includes/MessageCache.php b/includes/MessageCache.php
index e2cbf5f6..10c95a7e 100644
--- a/includes/MessageCache.php
+++ b/includes/MessageCache.php
@@ -23,6 +23,7 @@ class MessageCache {
var $mExtensionMessages = array();
var $mInitialised = false;
var $mDeferred = true;
+ var $mAllMessagesLoaded;
function __construct( &$memCached, $useDB, $expiry, $memcPrefix) {
wfProfileIn( __METHOD__ );
@@ -627,7 +628,6 @@ class MessageCache {
/**
* Add a 2-D array of messages by lang. Useful for extensions.
- * Introduced in 1.9. Please do not use it for now, for backwards compatibility.
*
* @param array $messages The array to be added
*/
@@ -670,12 +670,39 @@ class MessageCache {
}
}
- static function loadAllMessages() {
+ function loadAllMessages() {
+ global $wgExtensionMessagesFiles;
+ if ( $this->mAllMessagesLoaded ) {
+ return;
+ }
+ $this->mAllMessagesLoaded = true;
+
# Some extensions will load their messages when you load their class file
wfLoadAllExtensions();
# Others will respond to this hook
wfRunHooks( 'LoadAllMessages' );
+ # Some register their messages in $wgExtensionMessagesFiles
+ foreach ( $wgExtensionMessagesFiles as $name => $file ) {
+ if ( $file ) {
+ $this->loadMessagesFile( $file );
+ $wgExtensionMessagesFiles[$name] = false;
+ }
+ }
# Still others will respond to neither, they are EVIL. We sometimes need to know!
}
+
+ /**
+ * Load messages from a given file
+ */
+ function loadMessagesFile( $filename ) {
+ $magicWords = false;
+ require( $filename );
+ $this->addMessagesByLang( $messages );
+
+ if ( $magicWords !== false ) {
+ global $wgContLang;
+ $wgContLang->addMagicWordsByLang( $magicWords );
+ }
+ }
}
-?>
+
diff --git a/includes/Metadata.php b/includes/Metadata.php
index b995b223..f5b0b247 100644
--- a/includes/Metadata.php
+++ b/includes/Metadata.php
@@ -365,4 +365,4 @@ function getKnownLicenses() {
return $knownLicenses;
}
-?>
+
diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php
index db35535d..264a3595 100644
--- a/includes/MimeMagic.php
+++ b/includes/MimeMagic.php
@@ -111,56 +111,67 @@ class MimeMagic {
* --- load mime.types ---
*/
- global $wgMimeTypeFile;
+ global $wgMimeTypeFile, $IP;
- $types= MM_WELL_KNOWN_MIME_TYPES;
+ $types = MM_WELL_KNOWN_MIME_TYPES;
- if ($wgMimeTypeFile) {
- if (is_file($wgMimeTypeFile) and is_readable($wgMimeTypeFile)) {
- wfDebug("MimeMagic::MimeMagic: loading mime types from $wgMimeTypeFile\n");
-
- $types.= "\n";
- $types.= file_get_contents($wgMimeTypeFile);
+ if ( $wgMimeTypeFile == 'includes/mime.types' ) {
+ $wgMimeTypeFile = "$IP/$wgMimeTypeFile";
+ }
+
+ if ( $wgMimeTypeFile ) {
+ if ( is_file( $wgMimeTypeFile ) and is_readable( $wgMimeTypeFile ) ) {
+ wfDebug( __METHOD__.": loading mime types from $wgMimeTypeFile\n" );
+ $types .= "\n";
+ $types .= file_get_contents( $wgMimeTypeFile );
+ } else {
+ wfDebug( __METHOD__.": can't load mime types from $wgMimeTypeFile\n" );
}
- else wfDebug("MimeMagic::MimeMagic: can't load mime types from $wgMimeTypeFile\n");
+ } else {
+ wfDebug( __METHOD__.": no mime types file defined, using build-ins only.\n" );
}
- else wfDebug("MimeMagic::MimeMagic: no mime types file defined, using build-ins only.\n");
- $types= str_replace(array("\r\n","\n\r","\n\n","\r\r","\r"),"\n",$types);
- $types= str_replace("\t"," ",$types);
+ $types = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $types );
+ $types = str_replace( "\t", " ", $types );
- $this->mMimeToExt= array();
- $this->mToMime= array();
+ $this->mMimeToExt = array();
+ $this->mToMime = array();
- $lines= explode("\n",$types);
- foreach ($lines as $s) {
- $s= trim($s);
- if (empty($s)) continue;
- if (strpos($s,'#')===0) continue;
+ $lines = explode( "\n",$types );
+ foreach ( $lines as $s ) {
+ $s = trim( $s );
+ if ( empty( $s ) ) continue;
+ if ( strpos( $s, '#' ) === 0 ) continue;
- $s= strtolower($s);
- $i= strpos($s,' ');
+ $s = strtolower( $s );
+ $i = strpos( $s, ' ' );
- if ($i===false) continue;
+ if ( $i === false ) continue;
#print "processing MIME line $s<br>";
- $mime= substr($s,0,$i);
- $ext= trim(substr($s,$i+1));
+ $mime = substr( $s, 0, $i );
+ $ext = trim( substr($s, $i+1 ) );
- if (empty($ext)) continue;
+ if ( empty( $ext ) ) continue;
- if ( !empty($this->mMimeToExt[$mime])) $this->mMimeToExt[$mime] .= ' '.$ext;
- else $this->mMimeToExt[$mime]= $ext;
+ if ( !empty( $this->mMimeToExt[$mime] ) ) {
+ $this->mMimeToExt[$mime] .= ' ' . $ext;
+ } else {
+ $this->mMimeToExt[$mime] = $ext;
+ }
- $extensions= explode(' ',$ext);
+ $extensions = explode( ' ', $ext );
- foreach ($extensions as $e) {
- $e= trim($e);
- if (empty($e)) continue;
+ foreach ( $extensions as $e ) {
+ $e = trim( $e );
+ if ( empty( $e ) ) continue;
- if ( !empty($this->mExtToMime[$e])) $this->mExtToMime[$e] .= ' '.$mime;
- else $this->mExtToMime[$e]= $mime;
+ if ( !empty( $this->mExtToMime[$e] ) ) {
+ $this->mExtToMime[$e] .= ' ' . $mime;
+ } else {
+ $this->mExtToMime[$e] = $mime;
+ }
}
}
@@ -169,62 +180,69 @@ class MimeMagic {
*/
global $wgMimeInfoFile;
+ if ( $wgMimeInfoFile == 'includes/mime.info' ) {
+ $wgMimeInfoFile = "$IP/$wgMimeInfoFile";
+ }
- $info= MM_WELL_KNOWN_MIME_INFO;
-
- if ($wgMimeInfoFile) {
- if (is_file($wgMimeInfoFile) and is_readable($wgMimeInfoFile)) {
- wfDebug("MimeMagic::MimeMagic: loading mime info from $wgMimeInfoFile\n");
+ $info = MM_WELL_KNOWN_MIME_INFO;
- $info.= "\n";
- $info.= file_get_contents($wgMimeInfoFile);
+ if ( $wgMimeInfoFile ) {
+ if ( is_file( $wgMimeInfoFile ) and is_readable( $wgMimeInfoFile ) ) {
+ wfDebug( __METHOD__.": loading mime info from $wgMimeInfoFile\n" );
+ $info .= "\n";
+ $info .= file_get_contents( $wgMimeInfoFile );
+ } else {
+ wfDebug(__METHOD__.": can't load mime info from $wgMimeInfoFile\n");
}
- else wfDebug("MimeMagic::MimeMagic: can't load mime info from $wgMimeInfoFile\n");
+ } else {
+ wfDebug(__METHOD__.": no mime info file defined, using build-ins only.\n");
}
- else wfDebug("MimeMagic::MimeMagic: no mime info file defined, using build-ins only.\n");
- $info= str_replace(array("\r\n","\n\r","\n\n","\r\r","\r"),"\n",$info);
- $info= str_replace("\t"," ",$info);
+ $info = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $info);
+ $info = str_replace( "\t", " ", $info );
- $this->mMimeTypeAliases= array();
- $this->mMediaTypes= array();
+ $this->mMimeTypeAliases = array();
+ $this->mMediaTypes = array();
- $lines= explode("\n",$info);
- foreach ($lines as $s) {
- $s= trim($s);
- if (empty($s)) continue;
- if (strpos($s,'#')===0) continue;
+ $lines = explode( "\n", $info );
+ foreach ( $lines as $s ) {
+ $s = trim( $s );
+ if ( empty( $s ) ) continue;
+ if ( strpos( $s, '#' ) === 0 ) continue;
- $s= strtolower($s);
- $i= strpos($s,' ');
+ $s = strtolower( $s );
+ $i = strpos( $s, ' ' );
- if ($i===false) continue;
+ if ( $i === false ) continue;
#print "processing MIME INFO line $s<br>";
- $match= array();
- if (preg_match('!\[\s*(\w+)\s*\]!',$s,$match)) {
- $s= preg_replace('!\[\s*(\w+)\s*\]!','',$s);
- $mtype= trim(strtoupper($match[1]));
+ $match = array();
+ if ( preg_match( '!\[\s*(\w+)\s*\]!', $s, $match ) ) {
+ $s = preg_replace( '!\[\s*(\w+)\s*\]!', '', $s );
+ $mtype = trim( strtoupper( $match[1] ) );
+ } else {
+ $mtype = MEDIATYPE_UNKNOWN;
}
- else $mtype= MEDIATYPE_UNKNOWN;
- $m= explode(' ',$s);
+ $m = explode( ' ', $s );
- if (!isset($this->mMediaTypes[$mtype])) $this->mMediaTypes[$mtype]= array();
+ if ( !isset( $this->mMediaTypes[$mtype] ) ) {
+ $this->mMediaTypes[$mtype] = array();
+ }
- foreach ($m as $mime) {
- $mime= trim($mime);
- if (empty($mime)) continue;
+ foreach ( $m as $mime ) {
+ $mime = trim( $mime );
+ if ( empty( $mime ) ) continue;
- $this->mMediaTypes[$mtype][]= $mime;
+ $this->mMediaTypes[$mtype][] = $mime;
}
- if (sizeof($m)>1) {
- $main= $m[0];
- for ($i=1; $i<sizeof($m); $i+= 1) {
- $mime= $m[$i];
- $this->mMimeTypeAliases[$mime]= $main;
+ if ( sizeof( $m ) > 1 ) {
+ $main = $m[0];
+ for ( $i=1; $i<sizeof($m); $i += 1 ) {
+ $mime = $m[$i];
+ $this->mMimeTypeAliases[$mime] = $main;
}
}
}
@@ -244,14 +262,14 @@ class MimeMagic {
/** returns a list of file extensions for a given mime type
* as a space separated string.
*/
- function getExtensionsForType($mime) {
-