summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY691
-rw-r--r--Makefile7
-rw-r--r--RELEASE-NOTES1266
-rw-r--r--Test.php307
-rwxr-xr-xbin/svnstat2
-rwxr-xr-xbin/ulimit4.sh4
-rw-r--r--config/index.php169
-rw-r--r--docs/database.txt10
-rw-r--r--docs/deferred.txt8
-rw-r--r--docs/design.txt19
-rw-r--r--docs/hooks.txt61
-rw-r--r--docs/memcached.txt10
-rw-r--r--docs/title.txt15
-rw-r--r--extensions/FunnyDot.php16
-rw-r--r--extensions/LLAuthPlugin.php202
-rw-r--r--extensions/README17
-rw-r--r--img_auth.php1
-rw-r--r--includes/AjaxDispatcher.php23
-rw-r--r--includes/AjaxFunctions.php95
-rw-r--r--includes/AjaxResponse.php70
-rw-r--r--includes/Article.php463
-rw-r--r--includes/AuthPlugin.php8
-rw-r--r--includes/AutoLoader.php49
-rw-r--r--includes/BagOStuff.php41
-rw-r--r--includes/Block.php100
-rw-r--r--includes/CacheDependency.php21
-rw-r--r--includes/CategoryPage.php35
-rw-r--r--includes/Categoryfinder.php73
-rw-r--r--includes/ChangesList.php78
-rw-r--r--includes/CoreParserFunctions.php23
-rw-r--r--includes/Credits.php1
-rw-r--r--includes/Database.php295
-rw-r--r--includes/DatabaseFunctions.php53
-rw-r--r--includes/DatabaseOracle.php788
-rw-r--r--includes/DatabasePostgres.php332
-rw-r--r--includes/DateFormatter.php10
-rw-r--r--includes/DefaultSettings.php198
-rw-r--r--includes/Defines.php1
-rw-r--r--includes/DifferenceEngine.php127
-rw-r--r--includes/DjVuImage.php134
-rw-r--r--includes/EditPage.php615
-rw-r--r--includes/Exception.php30
-rw-r--r--includes/Exif.php30
-rw-r--r--includes/Export.php38
-rw-r--r--includes/ExternalEdit.php4
-rw-r--r--includes/ExternalStore.php1
-rw-r--r--includes/ExternalStoreDB.php2
-rw-r--r--includes/ExternalStoreHttp.php1
-rw-r--r--includes/FakeTitle.php3
-rw-r--r--includes/Feed.php18
-rw-r--r--includes/FileStore.php16
-rw-r--r--includes/GlobalFunctions.php197
-rw-r--r--includes/HTMLCacheUpdate.php9
-rw-r--r--includes/HTMLFileCache.php4
-rw-r--r--includes/HTMLForm.php4
-rw-r--r--includes/HistoryBlob.php98
-rw-r--r--includes/Hooks.php3
-rw-r--r--includes/IP.php279
-rw-r--r--includes/Image.php1279
-rw-r--r--includes/ImageFunctions.php2
-rw-r--r--includes/ImageGallery.php137
-rw-r--r--includes/ImagePage.php256
-rw-r--r--includes/ImageQueryPage.php68
-rw-r--r--includes/JobQueue.php125
-rw-r--r--includes/Licenses.php12
-rw-r--r--includes/LinkBatch.php7
-rw-r--r--includes/LinkCache.php15
-rw-r--r--includes/LinkFilter.php46
-rw-r--r--includes/Linker.php322
-rw-r--r--includes/LinksUpdate.php16
-rw-r--r--includes/LoadBalancer.php13
-rw-r--r--includes/LogPage.php56
-rw-r--r--includes/MacBinary.php7
-rw-r--r--includes/MagicWord.php7
-rw-r--r--includes/Math.php8
-rw-r--r--includes/MediaTransformOutput.php166
-rw-r--r--includes/MemcachedSessions.php1
-rw-r--r--includes/MessageCache.php10
-rw-r--r--includes/Metadata.php5
-rw-r--r--includes/MimeMagic.php39
-rw-r--r--includes/Namespace.php16
-rw-r--r--includes/ObjectCache.php6
-rw-r--r--includes/OutputHandler.php64
-rw-r--r--includes/OutputPage.php122
-rw-r--r--includes/PageHistory.php59
-rw-r--r--includes/PageQueryPage.php26
-rw-r--r--includes/Pager.php57
-rw-r--r--includes/Parser.php522
-rw-r--r--includes/ParserCache.php28
-rw-r--r--includes/ParserOptions.php119
-rw-r--r--includes/ParserOutput.php133
-rw-r--r--includes/PatrolLog.php83
-rw-r--r--includes/Profiler.php11
-rw-r--r--includes/ProfilerSimple.php19
-rw-r--r--includes/ProfilerSimpleUDP.php11
-rw-r--r--includes/ProtectionForm.php183
-rw-r--r--includes/ProxyTools.php50
-rw-r--r--includes/QueryPage.php223
-rw-r--r--includes/RawPage.php35
-rw-r--r--includes/RecentChange.php12
-rw-r--r--includes/Revision.php85
-rw-r--r--includes/Sanitizer.php184
-rw-r--r--includes/SearchEngine.php48
-rw-r--r--includes/SearchMySQL.php13
-rw-r--r--includes/SearchMySQL4.php12
-rw-r--r--includes/SearchOracle.php235
-rw-r--r--includes/SearchPostgres.php176
-rw-r--r--includes/SearchTsearch2.php20
-rw-r--r--includes/SearchUpdate.php16
-rw-r--r--includes/Setup.php4
-rw-r--r--includes/SiteConfiguration.php10
-rw-r--r--includes/SiteStats.php53
-rw-r--r--includes/Skin.php111
-rw-r--r--includes/SkinTemplate.php114
-rw-r--r--includes/SpecialAllmessages.php9
-rw-r--r--includes/SpecialAllpages.php61
-rw-r--r--includes/SpecialAncientpages.php10
-rw-r--r--includes/SpecialBlockip.php293
-rw-r--r--includes/SpecialBlockme.php3
-rw-r--r--includes/SpecialBooksources.php9
-rw-r--r--includes/SpecialBrokenRedirects.php43
-rw-r--r--includes/SpecialCategories.php103
-rw-r--r--includes/SpecialConfirmemail.php16
-rw-r--r--includes/SpecialContributions.php568
-rw-r--r--includes/SpecialDeadendpages.php18
-rw-r--r--includes/SpecialDisambiguations.php129
-rw-r--r--includes/SpecialDoubleRedirects.php30
-rw-r--r--includes/SpecialEmailuser.php23
-rw-r--r--includes/SpecialExport.php71
-rw-r--r--includes/SpecialFewestrevisions.php65
-rw-r--r--includes/SpecialImagelist.php22
-rw-r--r--includes/SpecialImport.php123
-rw-r--r--includes/SpecialIpblocklist.php61
-rw-r--r--includes/SpecialListredirects.php10
-rw-r--r--includes/SpecialListusers.php301
-rw-r--r--includes/SpecialLockdb.php10
-rw-r--r--includes/SpecialLog.php173
-rw-r--r--includes/SpecialLonelypages.php11
-rw-r--r--includes/SpecialLongpages.php15
-rw-r--r--includes/SpecialMIMEsearch.php14
-rw-r--r--includes/SpecialMostcategories.php9
-rw-r--r--includes/SpecialMostimages.php29
-rw-r--r--includes/SpecialMostlinked.php13
-rw-r--r--includes/SpecialMostlinkedcategories.php10
-rw-r--r--includes/SpecialMostrevisions.php8
-rw-r--r--includes/SpecialMovepage.php35
-rw-r--r--includes/SpecialNewimages.php33
-rw-r--r--includes/SpecialNewpages.php31
-rw-r--r--includes/SpecialPage.php41
-rw-r--r--includes/SpecialPopularpages.php34
-rw-r--r--includes/SpecialPreferences.php49
-rw-r--r--includes/SpecialPrefixindex.php13
-rw-r--r--includes/SpecialProtectedpages.php260
-rw-r--r--includes/SpecialRandompage.php142
-rw-r--r--includes/SpecialRandomredirect.php47
-rw-r--r--includes/SpecialRecentchanges.php17
-rw-r--r--includes/SpecialRecentchangeslinked.php54
-rw-r--r--includes/SpecialResetpass.php9
-rw-r--r--includes/SpecialRevisiondelete.php9
-rw-r--r--includes/SpecialSearch.php22
-rw-r--r--includes/SpecialShortpages.php8
-rw-r--r--includes/SpecialSpecialpages.php5
-rw-r--r--includes/SpecialStatistics.php17
-rw-r--r--includes/SpecialUncategorizedcategories.php8
-rw-r--r--includes/SpecialUncategorizedimages.php26
-rw-r--r--includes/SpecialUncategorizedpages.php10
-rw-r--r--includes/SpecialUndelete.php204
-rw-r--r--includes/SpecialUnlockdb.php6
-rw-r--r--includes/SpecialUnusedcategories.php8
-rw-r--r--includes/SpecialUnusedimages.php39
-rw-r--r--includes/SpecialUnusedtemplates.php13
-rw-r--r--includes/SpecialUnwatchedpages.php14
-rw-r--r--includes/SpecialUpload.php151
-rw-r--r--includes/SpecialUploadMogile.php10
-rw-r--r--includes/SpecialUserlogin.php88
-rw-r--r--includes/SpecialUserlogout.php3
-rw-r--r--includes/SpecialUserrights.php89
-rw-r--r--includes/SpecialVersion.php28
-rw-r--r--includes/SpecialWantedcategories.php12
-rw-r--r--includes/SpecialWantedpages.php10
-rw-r--r--includes/SpecialWatchlist.php46
-rw-r--r--includes/SpecialWhatlinkshere.php170
-rw-r--r--includes/SpecialWithoutinterwiki.php56
-rw-r--r--includes/SquidUpdate.php6
-rw-r--r--includes/StringUtils.php4
-rw-r--r--includes/StubObject.php7
-rw-r--r--includes/Title.php829
-rw-r--r--includes/User.php249
-rw-r--r--includes/UserMailer.php42
-rw-r--r--includes/Utf8Case.php3
-rw-r--r--includes/WatchedItem.php29
-rw-r--r--includes/WebRequest.php46
-rw-r--r--includes/WebResponse.php14
-rw-r--r--includes/WebStart.php12
-rw-r--r--includes/Wiki.php107
-rw-r--r--includes/WikiError.php9
-rw-r--r--includes/Xml.php12
-rw-r--r--includes/XmlFunctions.php2
-rw-r--r--includes/ZhClient.php2
-rw-r--r--includes/ZhConversion.php1
-rw-r--r--includes/api/ApiBase.php9
-rw-r--r--includes/api/ApiFeedWatchlist.php6
-rw-r--r--includes/api/ApiFormatBase.php9
-rw-r--r--includes/api/ApiFormatJson.php8
-rw-r--r--includes/api/ApiFormatJson_json.php51
-rw-r--r--includes/api/ApiFormatPhp.php8
-rw-r--r--includes/api/ApiFormatWddx.php8
-rw-r--r--includes/api/ApiFormatXml.php8
-rw-r--r--includes/api/ApiFormatYaml.php8
-rw-r--r--includes/api/ApiFormatYaml_spyc.php13
-rw-r--r--includes/api/ApiHelp.php8
-rw-r--r--includes/api/ApiLogin.php6
-rw-r--r--includes/api/ApiMain.php9
-rw-r--r--includes/api/ApiOpenSearch.php8
-rw-r--r--includes/api/ApiPageSet.php16
-rw-r--r--includes/api/ApiQuery.php10
-rw-r--r--includes/api/ApiQueryAllpages.php8
-rw-r--r--includes/api/ApiQueryBacklinks.php10
-rw-r--r--includes/api/ApiQueryBase.php9
-rw-r--r--includes/api/ApiQueryInfo.php6
-rw-r--r--includes/api/ApiQueryLogEvents.php8
-rw-r--r--includes/api/ApiQueryRecentChanges.php10
-rw-r--r--includes/api/ApiQueryRevisions.php8
-rw-r--r--includes/api/ApiQuerySiteinfo.php8
-rw-r--r--includes/api/ApiQueryUserContributions.php8
-rw-r--r--includes/api/ApiQueryWatchlist.php6
-rw-r--r--includes/api/ApiResult.php8
-rw-r--r--includes/media/BMP.php31
-rw-r--r--includes/media/Bitmap.php236
-rw-r--r--includes/media/DjVu.php206
-rw-r--r--includes/media/Generic.php298
-rw-r--r--includes/media/SVG.php97
-rw-r--r--includes/memcached-client.php2
-rw-r--r--includes/mime.info5
-rw-r--r--includes/mime.types1
-rw-r--r--includes/normal/CleanUpTest.php36
-rw-r--r--includes/normal/Makefile36
-rw-r--r--includes/normal/README4
-rw-r--r--includes/normal/RandomTest.php4
-rw-r--r--includes/normal/Utf8Test.php2
-rw-r--r--includes/normal/UtfNormal.php39
-rw-r--r--includes/normal/UtfNormalBench.php10
-rw-r--r--includes/normal/UtfNormalData.inc9
-rw-r--r--includes/normal/UtfNormalDataK.inc3
-rw-r--r--includes/normal/UtfNormalGenerate.php4
-rw-r--r--includes/normal/UtfNormalTest.php2
-rw-r--r--includes/normal/UtfNormalUtil.php2
-rw-r--r--includes/proxy_check.php1
-rw-r--r--includes/templates/Userlogin.php9
-rw-r--r--includes/tidy.conf18
-rw-r--r--index.php41
-rw-r--r--install-utils.inc50
-rw-r--r--languages/Language.php83
-rw-r--r--languages/LanguageConverter.php12
-rw-r--r--languages/Names.php60
-rw-r--r--languages/classes/LanguageAr.php28
-rw-r--r--languages/classes/LanguageAz.php3
-rw-r--r--languages/classes/LanguageBe.php8
-rw-r--r--languages/classes/LanguageBe_x_old.php88
-rw-r--r--languages/classes/LanguageBg.php6
-rw-r--r--languages/classes/LanguageBs.php3
-rw-r--r--languages/classes/LanguageCs.php3
-rw-r--r--languages/classes/LanguageCu.php3
-rw-r--r--languages/classes/LanguageEo.php3
-rw-r--r--languages/classes/LanguageEt.php3
-rw-r--r--languages/classes/LanguageFi.php10
-rw-r--r--languages/classes/LanguageFr.php3
-rw-r--r--languages/classes/LanguageGa.php3
-rw-r--r--languages/classes/LanguageGsw.php3
-rw-r--r--languages/classes/LanguageHe.php3
-rw-r--r--languages/classes/LanguageHr.php3
-rw-r--r--languages/classes/LanguageHsb.php3
-rw-r--r--languages/classes/LanguageHu.php3
-rw-r--r--languages/classes/LanguageHy.php73
-rw-r--r--languages/classes/LanguageJa.php3
-rw-r--r--languages/classes/LanguageKk.php48
-rw-r--r--languages/classes/LanguageKk_kz.php3
-rw-r--r--languages/classes/LanguageKo.php4
-rw-r--r--languages/classes/LanguageKsh.php3
-rw-r--r--languages/classes/LanguageLa.php57
-rw-r--r--languages/classes/LanguageLt.php3
-rw-r--r--languages/classes/LanguageLv.php3
-rw-r--r--languages/classes/LanguagePt_br.php3
-rw-r--r--languages/classes/LanguageRu.php3
-rw-r--r--languages/classes/LanguageSk.php3
-rw-r--r--languages/classes/LanguageSl.php111
-rw-r--r--languages/classes/LanguageSr.php5
-rw-r--r--languages/classes/LanguageSr_ec.php3
-rw-r--r--languages/classes/LanguageSr_el.php3
-rw-r--r--languages/classes/LanguageTr.php3
-rw-r--r--languages/classes/LanguageTyv.php3
-rw-r--r--languages/classes/LanguageUk.php88
-rw-r--r--languages/classes/LanguageWa.php3
-rw-r--r--languages/classes/LanguageZh.php3
-rw-r--r--languages/classes/LanguageZh_cn.php3
-rw-r--r--languages/classes/LanguageZh_yue.php3
-rw-r--r--languages/messages/MessagesAf.php39
-rw-r--r--languages/messages/MessagesAr.php2481
-rw-r--r--languages/messages/MessagesArc.php3
-rw-r--r--languages/messages/MessagesAs.php3
-rw-r--r--languages/messages/MessagesAst.php3
-rw-r--r--languages/messages/MessagesAv.php3
-rw-r--r--languages/messages/MessagesAy.php7
-rw-r--r--languages/messages/MessagesAz.php953
-rw-r--r--languages/messages/MessagesBa.php362
-rw-r--r--languages/messages/MessagesBar.php3
-rw-r--r--languages/messages/MessagesBat_smg.php3
-rw-r--r--languages/messages/MessagesBe.php1786
-rw-r--r--languages/messages/MessagesBe_x_old.php1028
-rw-r--r--languages/messages/MessagesBg.php3208
-rw-r--r--languages/messages/MessagesBh.php20
-rw-r--r--languages/messages/MessagesBm.php3
-rw-r--r--languages/messages/MessagesBn.php3
-rw-r--r--languages/messages/MessagesBo.php3
-rw-r--r--languages/messages/MessagesBpy.php572
-rw-r--r--languages/messages/MessagesBr.php2778
-rw-r--r--languages/messages/MessagesBs.php219
-rw-r--r--languages/messages/MessagesCa.php3015
-rw-r--r--languages/messages/MessagesCe.php3
-rw-r--r--languages/messages/MessagesCs.php3007
-rw-r--r--languages/messages/MessagesCsb.php17
-rw-r--r--languages/messages/MessagesCu.php2
-rw-r--r--languages/messages/MessagesCv.php414
-rw-r--r--languages/messages/MessagesCy.php68
-rw-r--r--languages/messages/MessagesDa.php257
-rw-r--r--languages/messages/MessagesDe.php1031
-rw-r--r--languages/messages/MessagesDv.php3
-rw-r--r--languages/messages/MessagesDz.php3
-rw-r--r--languages/messages/MessagesEl.php296
-rw-r--r--languages/messages/MessagesEn.php651
-rw-r--r--languages/messages/MessagesEo.php2096
-rw-r--r--languages/messages/MessagesEs.php235
-rw-r--r--languages/messages/MessagesEt.php49
-rw-r--r--languages/messages/MessagesEu.php229
-rw-r--r--languages/messages/MessagesFa.php26
-rw-r--r--languages/messages/MessagesFi.php3174
-rw-r--r--languages/messages/MessagesFiu_vro.php2023
-rw-r--r--languages/messages/MessagesFo.php13
-rw-r--r--languages/messages/MessagesFr.php3061
-rw-r--r--languages/messages/MessagesFur.php45
-rw-r--r--languages/messages/MessagesFy.php39
-rw-r--r--languages/messages/MessagesGa.php272
-rw-r--r--languages/messages/MessagesGn.php3
-rw-r--r--languages/messages/MessagesGsw.php168
-rw-r--r--languages/messages/MessagesGu.php3
-rw-r--r--languages/messages/MessagesHe.php851
-rw-r--r--languages/messages/MessagesHi.php158
-rw-r--r--languages/messages/MessagesHr.php225
-rw-r--r--languages/messages/MessagesHsb.php3465
-rw-r--r--languages/messages/MessagesHu.php2197
-rw-r--r--languages/messages/MessagesHy.php1008
-rw-r--r--languages/messages/MessagesIa.php25
-rw-r--r--languages/messages/MessagesId.php3428
-rw-r--r--languages/messages/MessagesIi.php3
-rw-r--r--languages/messages/MessagesIs.php172
-rw-r--r--languages/messages/MessagesIt.php3542
-rw-r--r--languages/messages/MessagesJa.php3303
-rw-r--r--languages/messages/MessagesJbo.php3
-rw-r--r--languages/messages/MessagesJv.php454
-rw-r--r--languages/messages/MessagesKa.php542
-rw-r--r--languages/messages/MessagesKaa.php3
-rw-r--r--languages/messages/MessagesKab.php1639
-rw-r--r--languages/messages/MessagesKg.php3
-rw-r--r--languages/messages/MessagesKk.php3
-rw-r--r--languages/messages/MessagesKk_cn.php3186
-rw-r--r--languages/messages/MessagesKk_kz.php1347
-rw-r--r--languages/messages/MessagesKk_tr.php1314
-rw-r--r--languages/messages/MessagesKm.php3
-rw-r--r--languages/messages/MessagesKn.php5
-rw-r--r--languages/messages/MessagesKo.php2388
-rw-r--r--languages/messages/MessagesKs.php16
-rw-r--r--languages/messages/MessagesKsh.php4047
-rw-r--r--languages/messages/MessagesKu.php245
-rw-r--r--languages/messages/MessagesKv.php3
-rw-r--r--languages/messages/MessagesLa.php1858
-rw-r--r--languages/messages/MessagesLg.php287
-rw-r--r--languages/messages/MessagesLi.php1735
-rw-r--r--languages/messages/MessagesLn.php5
-rw-r--r--languages/messages/MessagesLo.php3
-rw-r--r--languages/messages/MessagesLt.php995
-rw-r--r--languages/messages/MessagesLv.php1744
-rw-r--r--languages/messages/MessagesMi.php4
-rw-r--r--languages/messages/MessagesMk.php190
-rw-r--r--languages/messages/MessagesMl.php3
-rw-r--r--languages/messages/MessagesMr.php272
-rw-r--r--languages/messages/MessagesMs.php21
-rw-r--r--languages/messages/MessagesMt.php4
-rw-r--r--languages/messages/MessagesMzn.php3
-rw-r--r--languages/messages/MessagesNah.php3
-rw-r--r--languages/messages/MessagesNap.php3
-rw-r--r--languages/messages/MessagesNds.php2172
-rw-r--r--languages/messages/MessagesNds_nl.php8
-rw-r--r--languages/messages/MessagesNe.php20
-rw-r--r--languages/messages/MessagesNew.php39
-rw-r--r--languages/messages/MessagesNl.php3296
-rw-r--r--languages/messages/MessagesNn.php199
-rw-r--r--languages/messages/MessagesNo.php201
-rw-r--r--languages/messages/MessagesNon.php3
-rw-r--r--languages/messages/MessagesNv.php3
-rw-r--r--languages/messages/MessagesOc.php2606
-rw-r--r--languages/messages/MessagesOr.php3
-rw-r--r--languages/messages/MessagesOs.php12
-rw-r--r--languages/messages/MessagesPa.php22
-rw-r--r--languages/messages/MessagesPi.php20
-rw-r--r--languages/messages/MessagesPl.php580
-rw-r--r--languages/messages/MessagesPms.php3151
-rw-r--r--languages/messages/MessagesPs.php3
-rw-r--r--languages/messages/MessagesPt.php198
-rw-r--r--languages/messages/MessagesPt_br.php15
-rw-r--r--languages/messages/MessagesQu.php3
-rw-r--r--languages/messages/MessagesRmy.php644
-rw-r--r--languages/messages/MessagesRo.php3097
-rw-r--r--languages/messages/MessagesRu.php3065
-rw-r--r--languages/messages/MessagesSa.php84
-rw-r--r--languages/messages/MessagesSc.php23
-rw-r--r--languages/messages/MessagesScn.php85
-rw-r--r--languages/messages/MessagesSd.php3
-rw-r--r--languages/messages/MessagesSk.php3487
-rw-r--r--languages/messages/MessagesSl.php2937
-rw-r--r--languages/messages/MessagesSq.php204
-rw-r--r--languages/messages/MessagesSr.php3
-rw-r--r--languages/messages/MessagesSr_ec.php196
-rw-r--r--languages/messages/MessagesSr_el.php197
-rw-r--r--languages/messages/MessagesSr_jc.php3
-rw-r--r--languages/messages/MessagesSr_jl.php3
-rw-r--r--languages/messages/MessagesSu.php2289
-rw-r--r--languages/messages/MessagesSv.php3273
-rw-r--r--languages/messages/MessagesTa.php27
-rw-r--r--languages/messages/MessagesTe.php12
-rw-r--r--languages/messages/MessagesTg.php3
-rw-r--r--languages/messages/MessagesTh.php13
-rw-r--r--languages/messages/MessagesTlh.php3
-rw-r--r--languages/messages/MessagesTr.php171
-rw-r--r--languages/messages/MessagesTt.php177
-rw-r--r--languages/messages/MessagesTy.php8
-rw-r--r--languages/messages/MessagesTyv.php6
-rw-r--r--languages/messages/MessagesUdm.php3
-rw-r--r--languages/messages/MessagesUg.php3
-rw-r--r--languages/messages/MessagesUk.php2823
-rw-r--r--languages/messages/MessagesUr.php615
-rw-r--r--languages/messages/MessagesUz.php424
-rw-r--r--languages/messages/MessagesVec.php189
-rw-r--r--languages/messages/MessagesVi.php266
-rw-r--r--languages/messages/MessagesVls.php3
-rw-r--r--languages/messages/MessagesWa.php226
-rw-r--r--languages/messages/MessagesXal.php3
-rw-r--r--languages/messages/MessagesYi.php203
-rw-r--r--languages/messages/MessagesZa.php3
-rw-r--r--languages/messages/MessagesZea.php34
-rw-r--r--languages/messages/MessagesZh_classical.php1223
-rw-r--r--languages/messages/MessagesZh_cn.php2831
-rw-r--r--languages/messages/MessagesZh_hk.php3
-rw-r--r--languages/messages/MessagesZh_sg.php3
-rw-r--r--languages/messages/MessagesZh_tw.php2824
-rw-r--r--languages/messages/MessagesZh_yue.php719
-rw-r--r--maintenance/Doxyfile4
-rw-r--r--maintenance/FiveUpgrade.inc48
-rw-r--r--maintenance/Makefile17
-rw-r--r--maintenance/addwiki.php4
-rw-r--r--maintenance/archives/patch-ar_deleted.sql3
-rw-r--r--maintenance/archives/patch-ar_len.sql3
-rw-r--r--maintenance/archives/patch-categorylinks.sql2
-rw-r--r--maintenance/archives/patch-externallinks.sql2
-rw-r--r--maintenance/archives/patch-fa_deleted.sql3
-rw-r--r--maintenance/archives/patch-filearchive.sql2
-rw-r--r--maintenance/archives/patch-indexes.sql2
-rw-r--r--maintenance/archives/patch-interwiki.sql2
-rw-r--r--maintenance/archives/patch-ipb_anon_only.sql2
-rw-r--r--maintenance/archives/patch-ipb_deleted.sql3
-rw-r--r--maintenance/archives/patch-job.sql2
-rw-r--r--maintenance/archives/patch-langlinks.sql2
-rw-r--r--maintenance/archives/patch-linkscc.sql2
-rw-r--r--maintenance/archives/patch-linktables.sql8
-rw-r--r--maintenance/archives/patch-log_deleted.sql3
-rw-r--r--maintenance/archives/patch-log_id.sql8
-rw-r--r--maintenance/archives/patch-logging.sql2
-rw-r--r--maintenance/archives/patch-math.sql2
-rw-r--r--maintenance/archives/patch-objectcache.sql2
-rw-r--r--maintenance/archives/patch-page_restrictions.sql22
-rw-r--r--maintenance/archives/patch-page_restrictions_sortkey.sql8
-rw-r--r--maintenance/archives/patch-pagelinks.sql2
-rw-r--r--maintenance/archives/patch-parsercache.sql2
-rw-r--r--maintenance/archives/patch-querycache.sql2
-rw-r--r--maintenance/archives/patch-querycacheinfo.sql2
-rw-r--r--maintenance/archives/patch-querycachetwo.sql2
-rw-r--r--maintenance/archives/patch-rc_deleted.sql8
-rw-r--r--maintenance/archives/patch-redirect.sql2
-rw-r--r--maintenance/archives/patch-rev_len.sql3
-rw-r--r--maintenance/archives/patch-rev_parent_id.sql9
-rw-r--r--maintenance/archives/patch-templatelinks.sql2
-rw-r--r--maintenance/archives/patch-transcache.sql2
-rw-r--r--maintenance/archives/patch-user_groups.sql2
-rw-r--r--maintenance/archives/patch-user_rights.sql2
-rw-r--r--maintenance/archives/patch-userlevels.sql4
-rw-r--r--maintenance/archives/patch-validate.sql2
-rw-r--r--maintenance/archives/rebuildRecentchanges.inc3
-rw-r--r--maintenance/archives/upgradeWatchlist.php3
-rw-r--r--maintenance/attachLatest.php5
-rw-r--r--maintenance/attribute.php5
-rw-r--r--maintenance/backup.inc26
-rw-r--r--maintenance/benchmarkPurge.php3
-rw-r--r--maintenance/changePassword.php5
-rw-r--r--maintenance/checkUsernames.php2
-rw-r--r--maintenance/cleanupCaps.php6
-rw-r--r--maintenance/cleanupDupes.inc7
-rw-r--r--maintenance/cleanupDupes.php3
-rw-r--r--maintenance/cleanupImages.php3
-rw-r--r--maintenance/cleanupSpam.php9
-rw-r--r--maintenance/cleanupTable.inc2
-rw-r--r--maintenance/cleanupTitles.php9
-rw-r--r--maintenance/cleanupWatchlist.php9
-rw-r--r--maintenance/clear_interwiki_cache.php5
-rw-r--r--maintenance/commandLine.inc5
-rw-r--r--maintenance/convertLinks.inc9
-rw-r--r--maintenance/convertLinks.php3
-rw-r--r--maintenance/createAndPromote.php3
-rw-r--r--maintenance/deleteBatch.php2
-rw-r--r--maintenance/deleteDefaultMessages.php4
-rw-r--r--maintenance/deleteImageMemcached.php4
-rw-r--r--maintenance/deleteOldRevisions.inc5
-rw-r--r--maintenance/deleteOldRevisions.php3
-rw-r--r--maintenance/deleteOrphanedRevisions.inc.php3
-rw-r--r--maintenance/deleteOrphanedRevisions.php5
-rw-r--r--maintenance/deleteRevision.php2
-rw-r--r--maintenance/dumpBackup.php3
-rw-r--r--maintenance/dumpHTML.inc26
-rw-r--r--maintenance/dumpHTML.php7
-rw-r--r--maintenance/dumpInterwiki.inc27
-rw-r--r--maintenance/dumpInterwiki.php3
-rw-r--r--maintenance/dumpLinks.php7
-rw-r--r--maintenance/dumpReplayLog.php9
-rw-r--r--maintenance/dumpSisterSites.php3
-rw-r--r--maintenance/dumpTextPass.php5
-rw-r--r--maintenance/eval.php3
-rw-r--r--maintenance/findhooks.php5
-rw-r--r--maintenance/fixSlaveDesync.php19
-rw-r--r--maintenance/fixTimestamps.php2
-rw-r--r--maintenance/fixUserRegistration.php4
-rw-r--r--maintenance/fuzz-tester.php397
-rw-r--r--maintenance/generateSitemap.php40
-rw-r--r--maintenance/getLagTimes.php3
-rw-r--r--maintenance/getSlaveServer.php12
-rw-r--r--maintenance/importDump.php13
-rw-r--r--maintenance/importImages.inc.php5
-rw-r--r--maintenance/importImages.php19
-rw-r--r--maintenance/importLogs.inc11
-rw-r--r--maintenance/importLogs.php3
-rw-r--r--maintenance/importPhase2.php18
-rw-r--r--maintenance/importTextFile.php34
-rw-r--r--maintenance/importUseModWiki.php12
-rw-r--r--maintenance/initStats.inc55
-rw-r--r--maintenance/initStats.php56
-rw-r--r--maintenance/installExtension.php51
-rw-r--r--maintenance/interwiki.sql2
-rw-r--r--maintenance/language/alltrans.php3
-rw-r--r--maintenance/language/checkExtensioni18n.php279
-rw-r--r--maintenance/language/checkLanguage.inc92
-rw-r--r--maintenance/language/checkLanguage.php88
-rw-r--r--maintenance/language/date-formats.php1
-rw-r--r--maintenance/language/diffLanguage.php8
-rw-r--r--maintenance/language/dumpMessages.php3
-rw-r--r--maintenance/language/lang2po.php21
-rw-r--r--maintenance/language/languages.inc36
-rw-r--r--maintenance/language/messageTypes.inc68
-rw-r--r--maintenance/language/messages.inc400
-rw-r--r--maintenance/language/rebuildLanguage.php38
-rw-r--r--maintenance/language/transstat.php5
-rw-r--r--maintenance/language/writeMessagesArray.inc71
-rw-r--r--maintenance/mcc.php9
-rw-r--r--maintenance/mctest.php4
-rw-r--r--maintenance/moveBatch.php5
-rw-r--r--maintenance/mwdocgen.php3
-rw-r--r--maintenance/mwdoxygen.cfg946
-rw-r--r--maintenance/namespaceDupes.php2
-rw-r--r--maintenance/nextJobDB.php48
-rw-r--r--maintenance/nukeNS.php108
-rw-r--r--maintenance/nukePage.inc15
-rw-r--r--maintenance/nukePage.php3
-rw-r--r--maintenance/ora/tables.sql437
-rw-r--r--maintenance/orphans.php9
-rw-r--r--maintenance/ourusers.php3
-rw-r--r--maintenance/parserTests.inc359
-rw-r--r--maintenance/parserTests.php12
-rw-r--r--maintenance/parserTests.txt291
-rw-r--r--maintenance/parserTestsParserHook.php3
-rw-r--r--maintenance/parserTestsParserTime.php3
-rw-r--r--maintenance/parserTestsStaticParserHook.php3
-rw-r--r--maintenance/postgres/archives/patch-archive-ar_deleted.sql1
-rw-r--r--maintenance/postgres/archives/patch-archive2.sql15
-rw-r--r--maintenance/postgres/archives/patch-archive_delete.sql5
-rw-r--r--maintenance/postgres/archives/patch-archive_insert.sql6
-rw-r--r--maintenance/postgres/archives/patch-mediawiki_version.sql18
-rw-r--r--maintenance/postgres/archives/patch-mwuser.sql1
-rw-r--r--maintenance/postgres/archives/patch-page_deleted.sql11
-rw-r--r--maintenance/postgres/archives/patch-page_restrictions.sql10
-rw-r--r--maintenance/postgres/archives/patch-pagecontent.sql1
-rw-r--r--maintenance/postgres/archives/patch-profiling.sql7
-rw-r--r--maintenance/postgres/archives/patch-querycachetwo.sql12
-rw-r--r--maintenance/postgres/archives/patch-rc_cur_id-not-null.sql1
-rw-r--r--maintenance/postgres/archives/patch-redirect.sql7
-rw-r--r--maintenance/postgres/archives/patch-remove-archive2.sql3
-rw-r--r--maintenance/postgres/archives/patch-rev_text_id_idx.sql1
-rw-r--r--maintenance/postgres/archives/patch-revision_rev_user_fkey.sql4
-rw-r--r--maintenance/postgres/compare_schemas.pl30
-rw-r--r--maintenance/postgres/mediawiki_mysql2postgres.pl444
-rw-r--r--maintenance/postgres/tables.sql78
-rw-r--r--maintenance/purgeList.php3
-rw-r--r--maintenance/purgeOldText.inc8
-rw-r--r--maintenance/purgeOldText.php3
-rw-r--r--maintenance/reassignEdits.inc.php7
-rw-r--r--maintenance/reassignEdits.php3
-rw-r--r--maintenance/rebuildImages.php7
-rw-r--r--maintenance/rebuildInterwiki.inc10
-rw-r--r--maintenance/rebuildInterwiki.php3
-rw-r--r--maintenance/rebuildall.php3
-rw-r--r--maintenance/rebuildrecentchanges.inc14
-rw-r--r--maintenance/rebuildrecentchanges.php3
-rw-r--r--maintenance/rebuildtextindex.inc5
-rw-r--r--maintenance/rebuildtextindex.php3
-rw-r--r--maintenance/refreshImageCount.php2
-rw-r--r--maintenance/refreshLinks.inc9
-rw-r--r--maintenance/refreshLinks.php3
-rw-r--r--maintenance/removeUnusedAccounts.inc5
-rw-r--r--maintenance/removeUnusedAccounts.php7
-rw-r--r--maintenance/renamewiki.php60
-rw-r--r--maintenance/renderDump.php3
-rw-r--r--maintenance/runJobs.php9
-rw-r--r--maintenance/showJobs.php2
-rw-r--r--maintenance/showStats.php2
-rw-r--r--maintenance/sql.php67
-rw-r--r--maintenance/storage/blobs.sql6
-rw-r--r--maintenance/storage/checkStorage.php12
-rw-r--r--maintenance/storage/compressOld.inc38
-rw-r--r--maintenance/storage/compressOld.php7
-rw-r--r--maintenance/storage/dumpRev.php9
-rw-r--r--maintenance/storage/moveToExternal.php128
-rw-r--r--maintenance/storage/resolveStubs.php17
-rw-r--r--maintenance/tables.sql131
-rw-r--r--maintenance/update.php3
-rw-r--r--maintenance/updateArticleCount.inc.php7
-rw-r--r--maintenance/updateArticleCount.php5
-rw-r--r--maintenance/updateRestrictions.php67
-rw-r--r--maintenance/updateSearchIndex.inc11
-rw-r--r--maintenance/updateSearchIndex.php3
-rw-r--r--maintenance/updateSpecialPages.php8
-rw-r--r--maintenance/updaters.inc574
-rw-r--r--maintenance/userDupes.inc6
-rw-r--r--maintenance/userDupes.php2
-rw-r--r--maintenance/userOptions.inc237
-rw-r--r--maintenance/userOptions.php21
-rw-r--r--maintenance/waitForSlave.php16
-rw-r--r--maintenance/wikipedia-interwiki.sql53
-rw-r--r--serialized/README4
-rw-r--r--skins/Chick.deps.php2
-rw-r--r--skins/Chick.php8
-rw-r--r--skins/CologneBlue.php6
-rw-r--r--skins/MonoBook.php48
-rw-r--r--skins/MySkin.deps.php2
-rw-r--r--skins/MySkin.php8
-rw-r--r--skins/Nostalgia.php6
-rw-r--r--skins/Simple.deps.php2
-rw-r--r--skins/Simple.php8
-rw-r--r--skins/Standard.php8
-rw-r--r--skins/archlinux/handheld.css32
-rw-r--r--skins/archlinux/main.css38
-rw-r--r--skins/archlinux/rtl.css5
-rw-r--r--skins/chick/main.css22
-rw-r--r--skins/common/IEFixes.js8
-rw-r--r--skins/common/ajax.js32
-rw-r--r--skins/common/ajaxsearch.js6
-rw-r--r--skins/common/block.js47
-rw-r--r--skins/common/common.css55
-rw-r--r--skins/common/commonPrint.css8
-rw-r--r--skins/common/common_rtl.css6
-rw-r--r--skins/common/images/Checker-16x16.pngbin0 -> 81 bytes
-rw-r--r--skins/common/metadata.js18
-rw-r--r--skins/common/preview.js91
-rw-r--r--skins/common/protect.js41
-rw-r--r--skins/common/sticky.js10
-rw-r--r--skins/common/wikibits.js538
-rw-r--r--skins/disabled/HTMLDump.php8
-rw-r--r--skins/disabled/MonoBookCBT.php6
-rw-r--r--skins/monobook/handheld.css32
-rw-r--r--skins/monobook/main.css58
-rw-r--r--skins/monobook/rtl.css5
-rw-r--r--skins/simple/main.css18
-rw-r--r--t/00-test.t8
-rw-r--r--t/README54
-rw-r--r--t/inc/IP.t60
-rw-r--r--t/inc/Licenses.t29
-rw-r--r--t/inc/Sanitizer.t62
-rw-r--r--t/inc/Title.t33
-rw-r--r--t/inc/Xml.t56
-rw-r--r--t/maint/eol-style.t35
-rw-r--r--t/maint/php-lint.t33
-rw-r--r--t/maint/php-tag.t29
-rw-r--r--t/maint/unix-newlines.t28
-rw-r--r--thumb.php103
-rw-r--r--trackback.php8
699 files changed, 92173 insertions, 56022 deletions
diff --git a/HISTORY b/HISTORY
index 302f9b0f..6e710a09 100644
--- a/HISTORY
+++ b/HISTORY
@@ -5,6 +5,697 @@
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.
+
+== Changes since 1.8 ==
+
+* (bug 8200) Make category lists sorted by name when using Postgres.
+* (bug 7841) Support 'IGNORE' inserts for Postgres, fixes watchlist
+ adding problem.
+* (bug 6835) Removing the includes/Parser.php::getTemplateArgs() function,
+ because it seems to be unused.
+* (bug 7139) Increasing the visual width of the edit summary field on larger
+ screen sizes, for the default monobook skin.
+* Fix PHP notice and estimates for dumpBackup.php and friends
+* Improved register_globals paranoia checks
+* (bug 7545) Fix PHP version check on install
+* Disable PHP exception backtrace printing unless $wgShowExceptionDetails
+ is set. Backtraces may contain sensitive information in function call
+ parameters.
+* (bug 6164) Avoid smashing Cite state if message transformation triggers
+ during bad image list check, by skipping message transformation.
+ This isn't a good permanent fix.
+* (bug 6918) Stopped borders and backgrounds from showing through floated
+ tables in Monobook
+* (bug 6868) Un-hardcode section edit link style
+* (bug 3205) Stop right floats from stacking horizontally in non-Monobook skins
+* Added global $wgStyleVersion to centralize bumping CSS and JS file versions
+ for cache-friendly style and script updating
+* (bug 7562) Fix non-ASCII namespaces on Windows/XAMPP servers
+* Friendlier check for PHP 5 in command-line scripts; it's common for parallel
+ PHP 4 and 5 installations to interfere on the command-line.
+* Fix regression in autoconfirm permission check
+* (bug 3015) Add CSS ids to subcategory and page sections on category pages
+* (bug 7587) Fix erroneous id for specialpage tab, enabling informative popup
+* (bug 7599) Fix thumbnail purging, PHP notices on HTCP image page purge
+* (bug 7581) Update language name for cbk-zam
+* (bug 7444) Update namespace translations for Telugu (te), kept old values as
+ alias for compatibility
+* (bug 4525) Move section links down visually to same level as headings
+ (editsection links are now inside the heading elements)
+* Workaround for http://bugs.php.net/bug.php?id=31892 , PATH_INFO and hence
+ URLs of the style /index.php/Main_Page were broken on some CGI installations.
+* (bug 7623) Validate custom HTML id's correctly in Monobook interface
+* (bug 2241) Fix collision of 'w' and 'd' accesskeys
+* (bug 5795) CSS class added to body based on page name for page-specific
+ styling
+* (bug 6276) Stopped search field from getting too large in Cologne Blue
+* (bug 7644) User creations that are aborted by hooks shouldn't be counted
+ against account creations per day limit
+* (bug 7636) Show Firefox 2 users correct accesskey prefix
+* (bug 6427) Block blocked IPs from using the mail password function
+ to allow blocking of flooders
+* Include common.css from classic-style skins in main HTML with the bump URL
+* (bug 7607) Add Karakalpak (kaa) to Names.php and stub message file for linktrail
+* (bug 7582) Add 'tog-nolangconversion' to MessagesEn.php.
+ This key is need for languages with variants (zh, sr, kk)
+* (bug 7606) MediaWiki messages for "rss" and "atom" missing
+* (bug 7609) Add some more '*-summary' messages to MessagesEn.php with empty
+ strings to allow better localisation via Special:Allmessages. Mark this new
+ messages as optional for localisation.
+* Fix user_newpass upgrade for prefixed tables (reported by Fyren)
+* (bug 7663) Include language variant switcher links on Nostalgia skin
+* (bug 6531) Fix PHP fatal error on installation page with bad username input.
+* (bug 6977) Remove 404 link for autogenerated database documentation.
+* (bug 7369) Allow "Show Changes" without requiring edit token.
+* (bug 7687) Fix movetalk box checks itself when confirming a delete and move.
+* (bug 7684) Obey watchcreated preference for Special:Upload watch checkbox
+* (bug 7686) Include id attribute on delete form confirmation button
+* Allow compound interwiki prefixes in $wgImportSources
+* (bug 7304) Added redirect table to store redirect targets.
+* Added querycachetwo table (similar to querycache but has two titles)
+* PageArchive can now return a Revision object for more convenient processing
+ of deleted revision data
+* Added 'UndeleteShowRevision' hook in Special:Undelete
+* Error message on attempt to view invalid or missing deleted revisions
+* Remove unsightly "_" from namespace in Special:Allpages, Special:Prefixindex
+* (bug 3224) Allow minor edits by bots to skip new message notification on
+ user talk pages. This can be disabled by adjusting the 'nominornewtalk'
+ permission. Patch by Werdna.
+* (bug 7741) MATH: fixed broken syntax of underbrace etc. Fixed arrays
+* Fix purging for updated SVG files
+* (bug 7745) Add id attribute to search button in Monobook
+* (bug 7749) MATH: added some more LaTeX symbols, e.g. parallel, diamond, ast, ...
+* (bug 7304) Added code in Article.php to keep redirect table up to date.
+* Made special page names case-insensitive and localisable. Care has been taken
+ to maintain backwards compatibility.
+* Used special page subpages in a few more places, instead of query parameters.
+* (bug 7758) Added wrapper span to "templates used" explanation to allow CSS
+ styling (class="mw-templatesUsedExplanation").
+* Added {{#special:}} parser function, to give the local default title for
+ special pages
+* (bug 7766) Remove redundant / from AJAX requests, can break some servers
+* Add tab links from extensions to classic-based skins (SkinTemplateTab hook)
+ Provides better cross-skin compatibility for extensions using the modern
+ skin hooks, such as Oversight
+* Moved variant language links on Cologne Blue and Nostalgia to before the
+ login/logout link
+* Fix for parser tests with MySQL 5 in strict mode
+* Added block option "enable autoblocks"
+* Amend Special:Ipblocklist to note when a block has autoblock DISABLED.
+* (bug 7780) Fix regression in editing redirects
+* Add whitespace above "templates included on this page" using CSS, not
+ hardcoded line break.
+* Remove entries from redirect table on article deletion
+* (bug 7788) Force section headers in new section links for users who have
+ 'prompt for blank edit summaries' on.
+* (bug 1133) Special:Emailuser: add an option to send yourself a copy of your mail.
+* (bug 461) Allow "Categories:" link at bottom of pages to be customized via
+ pagecategorieslink message.
+* Sort the list of skins in "My Preferences" -> Skins by alphabetical order.
+* (bug 7785) Postgres compatibility for timestamps in RC feeds
+* (bug 7550) Normalize user parameter normally on Special:Log
+* (bug 7294) Fix PATH search for diff3 on install
+* Various fixes related to the blocking change re: autoblocks. On inserting
+ an IP block, the ipb_enable_autoblock field is now automagically blanked,
+ because it doesn't make any sense for an IP. Additionally, IP blocks
+ without the ipb_enable_autoblock option no longer show up as "autoblock
+ disabled" on Special:Ipblocklist.
+* (bug 7774) MATH: aded more amstex functions
+* (bug 1182) MATH: fixed inconsistent rendering of upper case Greek letters in TeX
+* Fix regression in streaming page dump generation
+* (bug 7801) Add support for parser function hooks in parser tests
+* checkUsernames.php now uses wfDebugLog instead of hardcoded path to log
+* (bug 7810) Update talk namespaces for Occitan
+* Allow case-sensitive URLs to be used for uploading from URLs.
+* (bug 1109) Correct fix for compressed 304 responses when additional output
+ buffers have been installed within the compression handler
+* (bug 7819) Move automatic redirect edit summary after pre-save transform
+ to work properly with subst: fun
+* (bug 7826) Fix typos in two English messages.
+* (bug 5365) Stop users being prompted to enter an edit summary for null edits,
+ if they have selected that option in preferences.
+* (bug 5936) Show an 'm' to the left of the edit summary on diff pages for minor edits.
+* (bug 7820) Improve error reporting for uploads via URL.
+* (bug 5149) When autoblocks are enabled, retroactively apply an autoblock to the most
+ recently used IP of a user when they are blocked.
+* Add an index on (rc_user_text,rc_timestamp) on the recentchanges table. This will
+ make CheckUser.php and the new retroactive autoblock functionality faster.
+* Fix regression in Special:Undelete for revisions deleted under MediaWiki 1.4
+ with compression or legacy encoding
+* (bug 6737) Fixes for MySQL 5 schema in strict mode
+* Approximate height for client-side scaling fallback instead of passing -1
+ into the HTML output.
+* Make the DNSBL to check for proxy blocking configurable via $wgSorbsUrl
+* Add experimental recording/reporting mode to parser tests runner, to
+ compare changes against the previous run.
+ Additional tables 'testrun' and 'testitem' are in maintenance/testRunner.sql,
+ source this and pass --record option to parserTests.php
+* Make the set of default parser test input files extensible via
+ $wgParserTestFiles. This can now be appended to by extensions or local
+ configuration files so that extension or custom tests can be automatically
+ run along with the main batch.
+* Run PHP install version checks on update.php so command-line updaters see
+ new version requirements
+* Do a check for the PHP 5.0.x 64-bit bug, since this is much more disruptive
+ as of MW 1.8 than it used to be. Install or upgrade now aborts with a
+ warning and a request to upgrade.
+* (bug 6440) Updated indexes to improve backlinking queries (links, templates, images)
+* Switched 'anon-only' block mode to default for IP blocks
+* (bug 3687, 7892) Add distinct heading for media files in category display,
+ with count.
+* (bug 1578) Add different icons for external links to audio, video, or PDF in
+ Monobook.
+* Made autoblocks block account creation if the user block has that option enabled.
+* Add auto-summaries to blankings and large removals without summaries.
+* (bug 7811) Allow preview of edit summaries.
+* (bug 6839) Wikibits.js minor changes to make JS-lint happier.
+* (bug 7932) Make sure that edit toolbar clears floats so it appears correctly.
+* (bug 6873) When viewing old revisions, add link to diff to current version.
+* (bug 3315) Provide rollback link directly on history page.
+* Replace 'old-revision-navigation' message with 'revision-info' and
+ 'revision-nav' messages, wrapped in divs with appropriate id's.
+* (bug 4178) MediaWiki:Common.js will now be included for all users if
+ $wgUseSiteJs is enabled, in addition to (if applicable) MediaWiki:Monobook.js
+ and user JS subpages.
+* (bug 7918) "Templates used on this page" changes during preview to reflect
+ any added or removed templates, and works as expected for section edits.
+* (bug 7919) "Templates used on this page" is now shown for read-only pages.
+* (bug 7688) When viewing diff, section anchors in autosummary jump to section
+ on current page instead of loading the latest version.
+* (bug 7970) Use current connection explicitly on Database::getServerVersion
+* (bug 2001) Tables with class="sortable" can now be dynamically sorted via
+ JavaScript.
+* Added autosummary for new pages with 500 or less characters, and refactor
+ the autosummary code so it's all done in one function. doEdit is getting too
+ big!
+* (bug 7554) The correct MIME type for SVG images is now displayed on the
+ image page (image/svg+xml, not image/svg).
+* (bug 7883) Added autoblock whitelisting feature, using which specific ranges
+ can be protected from autoblocking. These ranges are specified, in list format,
+ in the autoblock_whitelist system message.
+* Added placeholders for text injection by hooks to EditPage.php
+* (bug 8009) Automatic edit summary for redirects is not filled for edits in existing pages
+* Installer support for experimental MySQL 4.1/5.0 binary-safe schema
+* Use INSERT IGNORE for db-based BagOStuff add/insert, for more memcache-like
+ behavior when keys already exist on add (instead of dying with an error...)
+* Add a hook 'UploadForm:initial' before the upload form is generated, and two
+ member variable for text injection into the form, which can be filled by the hooks.
+* (bug 6295) Add a "revision patching" functionality, where an edit can be undone
+ (with a functionality similar to diff rev1 rev2 | patch -R rev3 -o rev3).
+ This is triggered by including &undo=revid in an edit URL. A link to a URL
+ that will undo a given edit is shown on NEW revision headers on diff pages.
+ The link leads to a "Show Changes" page showing what will be done to undo the
+ edit.
+* Fix display of link in "already rolled back" message for image/category pages
+* (bug 6016) Left-aligned images should stack vertically, like right-aligned
+ images, not horizontally.
+* Patch from LeonWP: added UploadForm:BeforeProcessing hook in SpecialUpload.php
+* Add AuthPluginSetup hook to override $wgAuth after configuration
+* Fix regression in authentication hook auto-creation on login
+* (bug 8110) Allow spaces in ISBNs
+* (bug 8024) Introduce "send me copies of emails I send to others" preference
+* Added 'EditPage::attemptSave' hook before an article is saved.
+* (bug 8083) Applied patch for sk localisation
+* Add a backslash character to the edit token, to prevent edits via certain
+ broken proxies that mangle such characters in form submissions
+* (bug 7461) Allow overwriting pages using importTextFile.php
+* (bug 7946) importTextFile.php doesn't perform pre-save transform
+* (bug 8117) {{REVISIONTIMESTAMP}} showed weird default if $wgLocalTZoffset set;
+ now uses current time for previews and if timestamp can't be loaded from DB
+* {{REVISIONTIMESTAMP}} now uses site local timezone instead of user timezone
+ to ensure consistent behavior
+* {{REVISIONTIMESTAMP}} and friends should now work on non-MySQL backends
+* (bug 7671) Observe canonical media namespace prefix in Linker::formatComment
+* Added js variable wgCurRevisionId to the output
+* (bug 8141) Cleanup of Parser::doTableStuff, patch by AzaTht
+* (bug 8042) Make miser mode caching limits settable via $wgQueryCacheLimit
+ instead of hardcoding to 1000
+* Enable QueryPage classes to override list formatting
+* (bug 5485) Show number of intervening revisions in diff view
+* (bug 8100) Fix XHTML validity in Taiwanese localization
+* Added redirect to section feature. Use it wisely.
+* Added a configuration variable allowing the "break out of framesets" feature
+ to be switched on and off ($wgBreakFrames). Off by default.
+* Allow Xml::check() $attribs parameter to override 'value' attribute
+* DB schema change: added two columns (rc_old_len and rc_new_len) to the recentchanges table to store
+ the text lengths before and after the edit
+* (bug 1085) Made Special:Recentchanges show the character difference between the changed revisions
+* Removed a redundant <strong> tag from diff pages that was causing display issues for some users
+* (bug 8203) The keyboard shortcut for "log out" was removed, because users were pressing it
+ when they intended to press the shortcut for "preview".
+* (bug 8148) Handle non-removable output buffers gracefully when cleaning
+ buffers for HTTP 304 responses, StreamFile, and Special:Export.
+ Duplicated code merged into wfResetOutputBuffers() and wfClearOutputBuffers()
+* Special:AllPages : 'next page' link now point to the first title of the next
+ chunk instead of pointing to the last title of current chunk.
+* (bug 4673) Special:AllPages : add a 'previous' link (new message 'prevpage')
+* (bug 8121) wfRandom() was not between 0 and 1
+* Add static method Parser::createAssocArgs($args), so parser functions can
+ use the same code to parse arguments as the templates do.
+* Change behavior of logins using the temporary e-mailed password (as stored
+ in user_newpassword hash field). Instead of just logging in silently and
+ leaving the previous user_password field in place indefinitely, the user
+ is now prompted to set a new password.
+
+ The password-changing form is at Special:Resetpass; currently it's only
+ usable for changing from the temporary password during login, but it
+ could perhaps be generalized, replacing the subform in preferences.
+
+ Once the new password is set successfully, the temporary password is wiped
+ so it cannot be used to login a second time, and the login process
+ is completed.
+* Suppress 'mail new password' button on login form if $wgAuth forbids
+ changing user passwords; it wouldn't work very well...
+* Consolidate password length checks and $wgAuth manipulation into
+ User::setPassword() to avoid duplicate code in different places
+ that set passwords.
+* User::setPassword() now throws PasswordError exceptions if the password
+ is illegal or cannot be set via $wgAuth. These can be caught and a human-
+ readable error message displayed by UI code.
+* Added Title::isSubpage()
+* (bug 8241) Don't consider user pages of User:Foo.css to be CSS subpages
+* Set an explicit class on framed thumbnail inner divs and images, changed some
+ CSS to use these instead of using descendent selectors.
+* Accept null parameter to User::setPassword() as indicating the password
+ field should be cleared to an unusable state. Login will only be possible
+ after the password is reset, for instance by e-mail.
+* (bug 6394) Invalidate the password set for "by e-mail" account creations
+ to avoid accidental empty password creations.
+* Made the show change size function work on page moves, page creations, and
+ log entries. Also fixed it in the javascript recentchanges.
+* (bug 8239) correctly get 50 new contributions when clicking '(50 next)'
+* (bug 2259) Fix old regression where e-mail addresses were no longer
+ confirmed on login with mailed password.
+* Add a notification about the confirmation mail sent during account
+ creation, so people don't immediately go off to request a second one.
+* Add a warning on Special:Confirmemail if a code was already sent and has
+ not yet expired.
+* Add user_editcount field to provide data for heuristics on account use.
+ Incremented on edit, with lazy initialization from past revision data.
+ Can batch-initialize with maintenance/initEditCount.php (not yet friendly
+ to replication environments, this will do all accounts in one query).
+* Allow raw SQL subsections in Database::update() SET portion as well as
+ for WHERE portion. Handy for increments and such.
+* User::getOption now accept a default value to override default user values
+ this makes it consistent with WebRequest::get* methods. Corrected code in
+ various places accordingly.
+* (bug 8264) Fix JavaScript global vars for XHTML mode
+* Make $wgSiteNotice value wikitext again, for consistency with editable
+ MediaWiki:Sitenotice and MediaWiki:Anonnotice.
+* (bug 8044) When redirecting from the canonical name of the special page
+ to the localised one, parameters/subpages are omitted
+* (bug 8164) Special:Booksources should use GET for form submission
+* Rewrite Special:Booksources to clean up interface and remove redundant code
+* (bug 7925) Change Special:Allmessages message name filter javascript to be
+ a bit more responsive and easier on the CPU
+* (bug 4488) Support watching pages on deletion; introduces new user preference
+* Minor restructuring of Special:Preferences; "watch pages I edit" and "watch
+ pages I create" options now accessible under "Watchlist" options
+* (bug 8153) <nowiki> doesn't work in site notice
+* (bug 6690) wfMsgNoTrans() transforms messages
+* (bug 8274) Wrap edit tools in a <div> with a specified class
+* Detect PHP 5.0.x 64-bit bug and abort in WebStart.php; too many things break
+ mysteriously otherwise (detection code copied from install-utils.inc)
+* (bug 8295) Change handling of <center> tags in doBlockLevels() to match that
+ of <div>
+* (bug 8110) Make magic ISBN linking stricter: only match ten-digit sequences
+ (plus optional ISBN-13 prefix) with no immediately following alphanumeric
+ character, disallow multiple consecutive internal redirects
+* (bug 2785) Accept optional colon prefix in links when formatting comments
+* Don't show "you can view and copy the source of this page" message for
+ pages which don't exist
+* (bug 8310) Blank line added to top of 'post' when page is blank
+* (bug 8109) Template parameters ignored in "recentchangestext"
+* Gracefully skip redirect-to-fragment on WebKit versions less than 420;
+ it messes up on current versions of Safari but is ok in the latest
+ nightlies. Checking the version number will allow it to automatically
+ work when new releases of Safari appear.
+* Fix regression in thumb styles; size and padding didn't match with
+ new arrangement.
+* (bug 8333) Fix quick user data update on login password change on
+ replication database setups. User data is now pulled from master
+ instead of slave in User::loadFromDatabase, ensuring that it is
+ fresh and accurate when read and then saved back into cache.
+ This was breaking with the Special:Rename operation which
+ automatically logs the user in with the new password after changing
+ it; pulling from slave meant the record was often not the updated
+ one.
+* (bug 8335) Set image width to the first valid parameter found.
+* (bug 8350) Fix watchlist viewing bug when using Postgres.
+* (bug 6603) When warning about invalid file extensions, output the bit
+ of the extension we actually checked
+* (bug 7669) Drop defaults on BLOB/TEXT columns for better compatibility
+ with MySQL's strict mode, often enabled by the Windows installer.
+ The defaults are ignored anyway when strict mode is off...
+* (bug 7685) Use explicit values for ar_text and ar_flags when deleting,
+ for better compatibility with MySQL's strict mode
+* Update default interwiki values to reflect changed location of ursine:
+* (bug 5411) Remove autopatrol preference
+* Users who have the "autopatrol" permission will have their edits marked as
+ patrolled automatically
+* Users who do not have the "autopatrol" permission will no longer be able
+ to mark their own edits as patrolled
+* Introduce 'PingLimiter' hook; see docs/hooks.txt for more information
+* (bug 532) Tweaked alt text for some interface messages
+* (bug 8231) Gave useful alt text to the main <img> on image pages
+* (bug 371) Remove alt text for "Enlarge" icon on thumbnails
+* Initialize user_editcount to 0 instead of NULL for newly created accounts
+* (bug 3696) Strip LRM and RLM characters from titles to work around the
+ problem some people have where titles cut-and-pasted from lists include
+ the bidi override characters appended to the lists.
+ A more thorough blacklist for forbidden and translatable characters would
+ be wise, though, as might a cleaner method for the lists in the first place.
+* Fix regression in email password resets on read-restricted sites
+* Set tabindex on fields in deletion form so you don't have to tab through
+ the links in the sitenotice
+* (bug 8271) Show full time and date on viewer for individual deleted
+ revisions
+* (bug 8214) Output file size limit and actual file size in appropriate units
+ on Special:Upload
+* (bug 8016) Purge objectcache table during upgrade processes - use the --nopurge
+ option to prevent this when running maintenance/update.php
+* (bug 7612) Remove superfluous link to Special:Categories from result items
+ on Special:Mostcategories
+* {{PLURAL:}} now handles formatted numbers correctly
+* (bug 8331) Added the change size value to watchlists; therefore made
+ watchlists use RecentChange::newFromRow() instead of newFromCurRow()
+* (bug 8351) Fix undo for simple reverts
+* (bug 6856) User::clearNotification() does not respect read-only mode
+* (bug 6853) Use a checkbox on the installer form to indicate that a superuser
+ account should be used; this is clearer than the old check which relied on
+ the password never being an obscure value
+* Remove old unused watchlist cache, which was a leftover from the old schema
+ where watchlists were more expensive to generate
+* Minor cosmetic changes to Special:Userrights
+* Added wgCanonicalSpecialPageName to JavaScript variables
+* Fix image deleting when using Postgres.
+* Output both source and destination titles in maintenance/moveBatch.php
+* Added basic parser tests for language variants
+* Enable selflinks and categories to be written in some of the language variants
+* Prevent conversion of JavaScript code in language variants
+* Output software version number in maintenance/parserTests.php
+* (bug 7169) Use Ajax to watch/unwatch articles if enabled
+* Make variant table caching a little more robust, using main language code
+ in cache key. Probably this is still a bit wonky, though. Was breaking
+ parser tests when Chinese tables were getting loaded into Serbian code.
+* (bug 8380) Be nicer about blank lines in deleteBatch.php
+* (bug 8401) Fix regression in SORBS lookup for some DNS setups
+* Use raw file descriptor in posix_isatty() check to avoid warning on
+ Linux systems with at least some versions of PHP
+* (bug 5908) Allow overriding the default category sort key for all items on
+ a page using {{DEFAULTSORT}}
+* (bug 6449) Throw a more definitive error message when installation fails
+ due to an invalid database name
+* (bug 5827) Use full text for option link labels on Special:Watchlist
+* (bug 8018) Allow hiding minor edits from the watchlist
+* (bug 8427) MonoBook RTL IE 7.0 tweaks failed when sidebar's navigation
+ section is renamed; no longer relies on first section name
+* Stabilize client-side table sorting even if the underlying Javascript sort()
+ implementation is unstable
+* Add hook for extensions to add user information to the panel in preferences,
+ next to the user name and ID.
+* (bug 8392) Display protection status of transcluded pages in the edit page
+ template list. Patch by Fyren, with i18n naming tweak.
+* Fix for interwiki transclusion where target wiki uses query string for title
+* Resolve namespaces on interwiki Title objects using canonical namespace names
+ if possible (should not happen, though, outside interwiki transclusion... and
+ maybe not even then, but it does)
+* (bug 8447) Fix SQL typo breaking non-default $wgHitcounterUpdateFreq
+* Do not allow previews of deleted images to be cached
+* Add global variable $wgDefaultLanguageVariant used to set the default language
+ variant of a wiki to something different than the main language code
+* Add 'variant' option to parserTests - runs test with the given variant as
+ preferred, utilize it for more parser tests of language variants code
+* (bug 6503) Fix bug that stopped certain irrelevant links from being hidden
+ for printing
+* Avoid PHP warning in Creative Commons metadata when a creative commons
+ license is not actually set up
+* (bug 8463) Don't print external link icons for Monobook
+* (bug 8461) Support watching pages on move
+* (bug 8041) Work around bug with debug_backtrace when Zend Optimizer is
+ loaded by skipping the function. Use wfDebugBacktrace() wrapper function.
+* Reduce config file clutter by setting various script and upload paths
+ based on $IP or $wgScriptPath in Setup.php. They can still be explicitly
+ overridden in LocalSettings.php if desired...
+* Attempt to detect redirect loops for the canonical title redirect, and
+ give some hints to the poor confused administrator.
+* Introduce new flag 'R' - raw output for language variant escape tags
+* Advise users when updates for a query page have been disabled using
+ $wgDisableQueryPageUpdate
+* (bug 8413) Improve comments for $wgNamespaceRobotPolicies
+* (bug 8330) Show "bytes" suffix on recent changes diff counter
+ optionally... if set in rc-changes-size message (default empty for now)
+* (bug 8489) Support basic links in <gallery> caption attribute
+* (bug 8485) Correct Lingala number formatting
+* The MediaWiki namespace is no longer pre-filled with default messages on
+ install. All default messages will be removed from the MediaWiki namespace
+ on upgrade.
+* Recentchanges RSS/Atom feeds now use a separate message for the description
+ to avoid cluttering it with useless wiki formatting
+* (bug 8417) Handle EXIF unknown dates
+* (bug 8372) Return nothing on empty <math> tags.
+* New maintenance script to show the cached statistics : showStats.php.
+* Count deleted edits when regenerating total edits in maintenance/initStats.php
+* (bug 3706) Allow users to be exempted from IP blocks. The ipblock-exempt permission
+ key has been added to enable this behaviour, by default assigned to sysops.
+* (bug 7948) importDump.php now warn that Recentchanges need to be rebuild.
+* (bug 7667) allow XHTML namespaces customization
+* (bug 8531) Correct local name of Lingála (patch by Raymond)
+* Fix regression with default lock file and cache directories; threw visible
+ warning with open_basedir
+
+
+== 1.8 Compatibility changes ==
+
+=== Zend Optimizer ===
+
+A bug in some versions of PHP 5 and Zend Optimizer which was triggered under
+MediaWiki 1.8.x has been worked around by disabling some internal debugging
+features when Zend Optimizer is loaded. This should solve some common
+"blank page" problems.
+
+=== PHP 5.0 64-bit ===
+
+MediaWiki now checks for a condition where PHP 5.0.x corrupts array data
+on 64-bit systems and warns you to upgrade PHP to solve the problem. This
+bug causes Special: pages to fail on affected systems under MediaWiki 1.8
+and higher, and subtler data corruption on earlier versions.
+
+The only known workaround is to upgrade PHP to 5.1 or later, which you
+probably should do anyway for security reasons!
+
+=== MySQL 5 ===
+
+MediaWiki should now install and run correctly on MySQL 5.0 and higher when
+MySQL's "strict mode" is enabled. (This is now the default for many Windows
+installations, though it seems to remain off by default on Unix.)
+
+This fixes errors about "cannot default default value for BLOB/TEXT fields".
+
+=== ImageMagick ===
+
+Note that ImageMagick older than 6.x may no longer work for image resizing
+due to use of the -thumbnail option.
+
+
+== 1.8 Behavior changes ==
+
+=== Localized special pages ===
+
+The names of Special: pages can now be localized, so links and URLs to them
+are more legible in languages that aren't English.
+
+Not all languages have included localized names yet.
+
+=== E-mail password ===
+
+Users are now required to set a new password for themselves when they first
+log in with a newly generated e-mailed password.
+
+Requesting passwords frequently is prevented to reduce abusive mailbombing.
+
+=== Undo revision ===
+
+An "undo" link now appears in diff view for easier reverting of older edits.
+When GNU diff3 is available for edit conflict merging, this can make it much
+easier to "undo" the changes of an older edit when there are surrounding
+changes elsewhere in the page.
+
+The changes must be manually reviewed and approved, as with conventional
+full-revision reverts.
+
+=== Blocking ===
+
+User blocks can be set to disable the automatic blocking of IP addresses the
+account logs in with.
+
+
+== 1.8 Database changes ==
+
+* new 'redirect' table stores data on page redirects
+* new 'querycachetwo' table used for some cached special pages
+* 'ipblocks' table adds 'ipb_enable_autoblock'
+* 'recentchanges' table adds 'rc_old_len', 'rc_new_len' for size tracking
+* 'user' table has added 'user_newpass_time' and 'user_editcount' fields
+* some indexes have been updated on 'recentchanges'
+
+== 1.8 Configuration changes ==
+
+Several configuration options have changed since 1.8:
+
+=== $wgEnableAPI ===
+
+The experimental machine API interface is now enabled by default, read-only.
+You can disable it by setting $wgEnableAPI = false; in LocalSettings.php.
+
+=== $wgPathInfo ===
+
+The use of PATH_INFO (the text after the script name in 'index.php/Blah')
+is controlled by the $wgUsePathInfo setting. This is now explicitly disabled
+for CGI, apache2filter, and ISAPI configurations of PHP, for more consistency
+with the autodetection from the installer.
+
+In some rarer configurations you may have to switch $wgUsePathInfo from false
+to true or, perhaps, from true to false to make things work properly if bad
+PATH_INFO data comes through the server.
+
+The wiki now tries to detect this condition and should show you an error
+message describing what to change instead of sending the browser into an
+infinite redirect loop.
+
+=== $wgScript and other path settings ===
+
+The following configuration variables are now automatically set in Setup.php
+if they are not overridden in LocalSettings.php:
+
+from $wgScriptPath:
+ + $wgScript
+ | \- $wgArticlePath
+ + $wgRedirectScript
+ + $wgStylePath
+ + $wgUploadPath
+ \- $wgLogo
+ + $wgMathPath
+
+from $IP:
+ - $wgStyleDirectory
+ + $wgUploadDirectory
+ \- $wgMathDirectory
+ + $wgTmpDirectory
+
+Newly generated configuration files will by default include only $wgScriptPath
+(hardcoded from the installer) and $IP (detected at runtime).
+
+Old configuration files which specify all these values explicitly should
+continue to work just fine, but if you use the defaults you can remove them
+to reduce clutter.
+
+=== $wgGroupPermissions ===
+
+The sysop group now holds the "autopatrol" and "ipblock-exempt" rights by
+default.
+
+"autopatrol" replaces the preference for marking ones own edits patrolled
+by default; users holding this permission will automatically have their
+edits patrolled, while others cannot mark their own edits as patrolled
+even if they have patrolling rights.
+
+"ipblock-exempt" excludes the user from IP blocks; accounts which are blocked
+explicitly by name will still be blocked, however. This is given to sysops
+to minimize annoyance from accidental "collateral damage"; remember that a
+sysop will be able to lift the block if they desire.
+
+The bot group now holds the "nominornewtalk" right. A user with this right
+will not trigger new message notifications when making minor edits to user
+talk pages. This is meant to minimize annoyance from maintenance bot
+processes.
+
+=== $wgUseWatchlistCache ===
+
+Watchlist caching has been removed. The feature was not maintained, and has
+been unnecessary since switching to the 'recentchanges' database table
+reduced server pressure for Wikipedia's watchlists.
+
+=== $wgBreakFrames ===
+
+MediaWiki in the past attempted to detect when it was embedded in a frameset
+and "break out" of it, assuming it to be hostile.
+
+This behavior is now disabled by default, but can be reenabled by setting
+$wgBreakFrames to true in LocalSettings.php.
+
+
+== 1.8 New settings ==
+
+=== $wgVariantArticlePath ===
+
+For languages with script variant support (Chinese, Serbian, and others),
+it's possible to use alternate URL paths to select the variant for article
+display, setting $wgVariantArticlePath.
+
+Documentation for this setting would be useful.
+
+=== $wgMaxMsgCacheEntrySize ===
+
+The message cache can now skip items larger than a given size; this allows
+it to better handle the primary caching case when large CSS and JS blobs are
+present.
+
+=== $wgStyleVersion ===
+
+When making significant changes to skin stylesheets and JavaScript files,
+you can append a string to this variable to tweak the generated URLs,
+forcing newly rendered pages to bring in a fresh version despite server-
+or browser-side caching.
+
+Normally this will be set in the course of MediaWiki development, but
+if doing development on a custom skin you may wish to poke it as well.
+
+=== $wgRCShowChangedSize ===
+
+Special:Recentchanges and Special:Watchlist now show the number of bytes
+added or removed to an article to give an idea of the size of the edit.
+This information was previously available only in the IRC update feeds.
+
+To disable this site-wide, set $wgRCShowChangedSize to false.
+(Individual users can suppress the data in custom CSS.)
+
+Adjust $wgRCChangedSizeThreshold to trigger highlighting of particularly
+large changes.
+
+The formatting of the size figure can be adjusted through the
+[[MediaWiki:Rc-change-size]] message.
+
+=== $wgQueryCacheLimit ===
+
+The number of rows stored for "expensive" special pages in miser mode
+can now be adjusted up or down from the default 1000.
+
+=== $wgDisableQueryPageUpdate ===
+
+Individual "expensive" special pages can be skipped in processing by
+updateSpecialPages if added to this list.
+
+=== $wgSorbsUrl ===
+
+The base hostname for the DNS-based proxy blacklist can now be overridden
+when $wgEnableSorbs is set, to use a different blacklist instead of SORBS.
+The blacklist would need to respond the same was as SORBS; any positive
+response will be taken as a proxy.
+
+=== $wgAjaxWatch ===
+
+Experimental AJAX mode for the watch/unwatch tabs to execute inline.
+Does not include the UI messages describing how to reach the watchlist,
+so you may not want it on a general-audience site just yet.
+
+=== $wgParserTestFiles ===
+
+MediaWiki's parser test suite can now be expanded with additional test
+files. Custom extensions can add their test files to this array, and
+they will be run along with the main tests by maintenance/parserTests.php
+
+
== Changes since 1.7 ==
* Introduced AjaxResponse object, superceding AjaxCachePolicy
diff --git a/Makefile b/Makefile
index 0cfba45a..b659cad2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,2 +1,9 @@
+#
+# This Makefile is used to test some MediaWiki functions. If you
+# want to install MediaWiki, point your browser to ./config/
+#
test: Test.php
prove -r t
+
+verbose:
+ prove -v -r t | egrep -v '^ok'
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 472409da..95172e56 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -3,856 +3,499 @@
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.9.3 ==
+== MediaWiki 1.10.0 ==
-February 20, 2007
+May 9, 2007
-This is a security and bug-fix update to the Winter 2007 quarterly release.
-Minor compatibility fixes for IIS and PostgreSQL are included.
+This is the quarterly release snapshot for Spring 2007. See below for a full
+list of changes since the 1.9.x series.
-An XSS injection vulnerability based on Microsoft Internet Explorer's UTF-7
-charset autodetection was located in the AJAX support module, affecting MSIE
-users on MediaWiki 1.6.x and up when the optional setting $wgUseAjax is
-enabled.
+Changes since 1.10.0rc2:
-If you are using an extension based on the optional Ajax module,
-either disable it or upgrade to a version containing the fix:
+* (bug 9808) Fix regression that ignored user 'rclimit' option for Special:Contributions
-* 1.9: fixed in 1.9.3
-* 1.8: fixed in 1.8.4
-* 1.7: fixed in 1.7.3
-* 1.6: fixed in 1.6.10
-There is no known danger in the default configuration, with $wgUseAjax off.
+== MediaWiki 1.10.0rc2 ==
-* (bug 8992) Fix a remaining raw use of REQUEST_URI in history
-* (bug 8984) Fix a database error in Special:Recentchangeslinked
- when using the PostgreSQL database.
-* 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
-
-
-== MediaWiki 1.9.2 ==
+May 4, 2007
-February 4, 2007
+THIS IS A RELEASE CANDIDATE MADE AVAILABLE FOR TESTING!
+A FINAL 1.10.0 RELEASE WILL APPEAR WITHIN A FEW DAYS.
-This is a bug-fix update that fixes some installation and other minor
-issues with the 1.9.1 release as well as a security issue which was
-introduced in the 1.9 branch.
+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
-JavaScript code which regenerated the "sortable tables" feature did
-not properly sanitize input, leading to an HTML injection vulnerability.
-
-* (bug 8774) Fix path for GNU FDL rights icon on new installs
-* (bug 8819) Fix full path disclosure with skins dependencies
-* (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.
-* Security fix for sortable tables JavaScript
-
-
-== MediaWiki 1.9.1 ==
-
-January 24, 2007
-
-This is a bug-fix update that fixes some installation and upgrade issues
-with the original 1.9.0 release.
-
-* (bug 3000) Fall back to SCRIPT_NAME plus QUERY_STRING when REQUEST_URI is
- not available, as on IIS with PHP-CGI
-* Security fix for DjVu images. (Only affects servers where .djvu file
- uploads are enabled and $wgDjvuToXML is set.)
-* (bug 8638) Fix update from 1.4 and earlier
-* (bug 8641) Fix order of updates to ipblocks table for updates from <=1.7
-* (bug 8673) Minor fix for web service API content-type header
-* Fix API revision list on PHP 5.2.1; bad reference assignment
-* Fixed up the AjaxSearch
-* Exclude settings files when generating documentation. That could
- expose the database user and password to remote users.
-* ar: fix the 'create a new page' on search page when no exact match found
-* Correct tooltip accesskey hint for Opera on the Macintosh
- (uses Shift-Esc-, not Ctrl-).
-* (bug 8719) Firefox release notes lie! Fix tooltips for Firefox 2 on x11;
- accesskeys default settings appear to be same as Windows.
+== MediaWiki 1.10.0rc1 ==
+April 30, 2007
-== MediaWiki 1.9 ==
-
-January 10, 2007
-
-This is the quarterly release snapshot for Winter 2007. While the code
-has been running on Wikipedia for some time, installation and upgrade
-bits may be less well tested. Bug fix releases may follow in the coming
-days or weeks.
-
+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 development happen
+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
-
-== Security fixes ==
-
-An XSS injection vulnerability was located in the AJAX support module,
-affecting MediaWiki 1.6.x and up when the optional setting $wgUseAjax
-is enabled.
-
-There is no danger in the default configuration, with $wgUseAjax off.
-
-If you are using an extension based on the optional Ajax module,
-either disable it or upgrade to a version containing the fix:
-
-* 1.9: fixed in 1.9.0rc2
-* 1.8: fixed in 1.8.3
-* 1.7: fixed in 1.7.2
-* 1.6: fixed in 1.6.9
-
-
-== Compatibility changes ==
-
-=== Zend Optimizer ===
-
-A bug in some versions of PHP 5 and Zend Optimizer which was triggered under
-MediaWiki 1.8.x has been worked around by disabling some internal debugging
-features when Zend Optimizer is loaded. This should solve some common
-"blank page" problems.
-
-=== PHP 5.0 64-bit ===
-
-MediaWiki now checks for a condition where PHP 5.0.x corrupts array data
-on 64-bit systems and warns you to upgrade PHP to solve the problem. This
-bug causes Special: pages to fail on affected systems under MediaWiki 1.8
-and higher, and subtler data corruption on earlier versions.
-
-The only known workaround is to upgrade PHP to 5.1 or later, which you
-probably should do anyway for security reasons!
-
-=== MySQL 5 ===
-
-MediaWiki should now install and run correctly on MySQL 5.0 and higher when
-MySQL's "strict mode" is enabled. (This is now the default for many Windows
-installations, though it seems to remain off by default on Unix.)
-
-This fixes errors about "cannot default default value for BLOB/TEXT fields".
-
-=== ImageMagick ===
-
-Note that ImageMagick older than 6.x may no longer work for image resizing
-due to use of the -thumbnail option.
-
-
-== Behavior changes ==
-
-=== Localized special pages ===
-
-The names of Special: pages can now be localized, so links and URLs to them
-are more legible in languages that aren't English.
-
-Not all languages have included localized names yet.
-
-=== E-mail password ===
-
-Users are now required to set a new password for themselves when they first
-log in with a newly generated e-mailed password.
-
-Requesting passwords frequently is prevented to reduce abusive mailbombing.
-
-=== Undo revision ===
-
-An "undo" link now appears in diff view for easier reverting of older edits.
-When GNU diff3 is available for edit conflict merging, this can make it much
-easier to "undo" the changes of an older edit when there are surrounding
-changes elsewhere in the page.
-
-The changes must be manually reviewed and approved, as with conventional
-full-revision reverts.
-
-=== Blocking ===
-
-User blocks can be set to disable the automatic blocking of IP addresses the
-account logs in with.
-
-
-== Database changes ==
-
-* new 'redirect' table stores data on page redirects
-* new 'querycachetwo' table used for some cached special pages
-* 'ipblocks' table adds 'ipb_enable_autoblock'
-* 'recentchanges' table adds 'rc_old_len', 'rc_new_len' for size tracking
-* 'user' table has added 'user_newpass_time' and 'user_editcount' fields
-* some indexes have been updated on 'recentchanges'
-
-
== Configuration changes ==
-Several configuration options have changed since 1.8:
-
-=== $wgEnableAPI ===
-
-The experimental machine API interface is now enabled by default, read-only.
-You can disable it by setting $wgEnableAPI = false; in LocalSettings.php.
-
-=== $wgPathInfo ===
-
-The use of PATH_INFO (the text after the script name in 'index.php/Blah')
-is controlled by the $wgUsePathInfo setting. This is now explicitly disabled
-for CGI, apache2filter, and ISAPI configurations of PHP, for more consistency
-with the autodetection from the installer.
-
-In some rarer configurations you may have to switch $wgUsePathInfo from false
-to true or, perhaps, from true to false to make things work properly if bad
-PATH_INFO data comes through the server.
-
-The wiki now tries to detect this condition and should show you an error
-message describing what to change instead of sending the browser into an
-infinite redirect loop.
-
-=== $wgScript and other path settings ===
-
-The following configuration variables are now automatically set in Setup.php
-if they are not overridden in LocalSettings.php:
-
-from $wgScriptPath:
- + $wgScript
- | \- $wgArticlePath
- + $wgRedirectScript
- + $wgStylePath
- + $wgUploadPath
- \- $wgLogo
- + $wgMathPath
-
-from $IP:
- - $wgStyleDirectory
- + $wgUploadDirectory
- \- $wgMathDirectory
- + $wgTmpDirectory
- + $wgReadOnlyFile
- + $wgFileCacheDirectory
-
-Newly generated configuration files will by default include only $wgScriptPath
-(hardcoded from the installer) and $IP (detected at runtime).
-
-Old configuration files which specify all these values explicitly should
-continue to work just fine, but if you use the defaults you can remove them
-to reduce clutter.
-
-=== $wgGroupPermissions ===
-
-The sysop group now holds the "autopatrol" and "ipblock-exempt" rights by
-default.
-
-"autopatrol" replaces the preference for marking ones own edits patrolled
-by default; users holding this permission will automatically have their
-edits patrolled, while others cannot mark their own edits as patrolled
-even if they have patrolling rights.
-
-"ipblock-exempt" excludes the user from IP blocks; accounts which are blocked
-explicitly by name will still be blocked, however. This is given to sysops
-to minimize annoyance from accidental "collateral damage"; remember that a
-sysop will be able to lift the block if they desire.
-
-The bot group now holds the "nominornewtalk" right. A user with this right
-will not trigger new message notifications when making minor edits to user
-talk pages. This is meant to minimize annoyance from maintenance bot
-processes.
-
-=== $wgUseWatchlistCache ===
-
-Watchlist caching has been removed. The feature was not maintained, and has
-been unnecessary since switching to the 'recentchanges' database table
-reduced server pressure for Wikipedia's watchlists.
-
-=== $wgBreakFrames ===
-
-MediaWiki in the past attempted to detect when it was embedded in a frameset
-and "break out" of it, assuming it to be hostile.
-
-This behavior is now disabled by default, but can be reenabled by setting
-$wgBreakFrames to true in LocalSettings.php.
-
-
-== New settings ==
-
-=== $wgVariantArticlePath ===
-
-For languages with script variant support (Chinese, Serbian, and others),
-it's possible to use alternate URL paths to select the variant for article
-display, setting $wgVariantArticlePath.
-
-Documentation for this setting would be useful.
-
-=== $wgMaxMsgCacheEntrySize ===
-
-The message cache can now skip items larger than a given size; this allows
-it to better handle the primary caching case when large CSS and JS blobs are
-present.
-
-=== $wgStyleVersion ===
-
-When making significant changes to skin stylesheets and JavaScript files,
-you can append a string to this variable to tweak the generated URLs,
-forcing newly rendered pages to bring in a fresh version despite server-
-or browser-side caching.
-
-Normally this will be set in the course of MediaWiki development, but
-if doing development on a custom skin you may wish to poke it as well.
-
-=== $wgRCShowChangedSize ===
-
-Special:Recentchanges and Special:Watchlist now show the number of bytes
-added or removed to an article to give an idea of the size of the edit.
-This information was previously available only in the IRC update feeds.
-
-To disable this site-wide, set $wgRCShowChangedSize to false.
-(Individual users can suppress the data in custom CSS.)
-
-Adjust $wgRCChangedSizeThreshold to trigger highlighting of particularly
-large changes.
-
-The formatting of the size figure can be adjusted through the
-[[MediaWiki:Rc-change-size]] message.
-
-=== $wgQueryCacheLimit ===
-
-The number of rows stored for "expensive" special pages in miser mode
-can now be adjusted up or down from the default 1000.
-
-=== $wgDisableQueryPageUpdate ===
-
-Individual "expensive" special pages can be skipped in processing by
-updateSpecialPages if added to this list.
-
-=== $wgSorbsUrl ===
-
-The base hostname for the DNS-based proxy blacklist can now be overridden
-when $wgEnableSorbs is set, to use a different blacklist instead of SORBS.
-The blacklist would need to respond the same was as SORBS; any positive
-response will be taken as a proxy.
-
-=== $wgAjaxWatch ===
-
-Experimental AJAX mode for the watch/unwatch tabs to execute inline.
-Does not include the UI messages describing how to reach the watchlist,
-so you may not want it on a general-audience site just yet.
-
-=== $wgParserTestFiles ===
-
-MediaWiki's parser test suite can now be expanded with additional test
-files. Custom extensions can add their test files to this array, and
-they will be run along with the main tests by maintenance/parserTests.php
-
-
-== Changes since 1.8 ==
-
-* (bug 8200) Make category lists sorted by name when using Postgres.
-* (bug 7841) Support 'IGNORE' inserts for Postgres, fixes watchlist
- adding problem.
-* (bug 6835) Removing the includes/Parser.php::getTemplateArgs() function,
- because it seems to be unused.
-* (bug 7139) Increasing the visual width of the edit summary field on larger
- screen sizes, for the default monobook skin.
-* Fix PHP notice and estimates for dumpBackup.php and friends
-* Improved register_globals paranoia checks
-* (bug 7545) Fix PHP version check on install
-* Disable PHP exception backtrace printing unless $wgShowExceptionDetails
- is set. Backtraces may contain sensitive information in function call
- parameters.
-* (bug 6164) Avoid smashing Cite state if message transformation triggers
- during bad image list check, by skipping message transformation.
- This isn't a good permanent fix.
-* (bug 6918) Stopped borders and backgrounds from showing through floated
- tables in Monobook
-* (bug 6868) Un-hardcode section edit link style
-* (bug 3205) Stop right floats from stacking horizontally in non-Monobook skins
-* Added global $wgStyleVersion to centralize bumping CSS and JS file versions
- for cache-friendly style and script updating
-* (bug 7562) Fix non-ASCII namespaces on Windows/XAMPP servers
-* Friendlier check for PHP 5 in command-line scripts; it's common for parallel
- PHP 4 and 5 installations to interfere on the command-line.
-* Fix regression in autoconfirm permission check
-* (bug 3015) Add CSS ids to subcategory and page sections on category pages
-* (bug 7587) Fix erroneous id for specialpage tab, enabling informative popup
-* (bug 7599) Fix thumbnail purging, PHP notices on HTCP image page purge
-* (bug 7581) Update language name for cbk-zam
-* (bug 7444) Update namespace translations for Telugu (te), kept old values as
- alias for compatibility
-* (bug 4525) Move section links down visually to same level as headings
- (editsection links are now inside the heading elements)
-* Workaround for http://bugs.php.net/bug.php?id=31892 , PATH_INFO and hence
- URLs of the style /index.php/Main_Page were broken on some CGI installations.
-* (bug 7623) Validate custom HTML id's correctly in Monobook interface
-* (bug 2241) Fix collision of 'w' and 'd' accesskeys
-* (bug 5795) CSS class added to body based on page name for page-specific
- styling
-* (bug 6276) Stopped search field from getting too large in Cologne Blue
-* (bug 7644) User creations that are aborted by hooks shouldn't be counted
- against account creations per day limit
-* (bug 7636) Show Firefox 2 users correct accesskey prefix
-* (bug 6427) Block blocked IPs from using the mail password function
- to allow blocking of flooders
-* Include common.css from classic-style skins in main HTML with the bump URL
-* (bug 7607) Add Karakalpak (kaa) to Names.php and stub message file for linktrail
-* (bug 7582) Add 'tog-nolangconversion' to MessagesEn.php.
- This key is need for languages with variants (zh, sr, kk)
-* (bug 7606) MediaWiki messages for "rss" and "atom" missing
-* (bug 7609) Add some more '*-summary' messages to MessagesEn.php with empty
- strings to allow better localisation via Special:Allmessages. Mark this new
- messages as optional for localisation.
-* Fix user_newpass upgrade for prefixed tables (reported by Fyren)
-* (bug 7663) Include language variant switcher links on Nostalgia skin
-* (bug 6531) Fix PHP fatal error on installation page with bad username input.
-* (bug 6977) Remove 404 link for autogenerated database documentation.
-* (bug 7369) Allow "Show Changes" without requiring edit token.
-* (bug 7687) Fix movetalk box checks itself when confirming a delete and move.
-* (bug 7684) Obey watchcreated preference for Special:Upload watch checkbox
-* (bug 7686) Include id attribute on delete form confirmation button
-* Allow compound interwiki prefixes in $wgImportSources
-* (bug 7304) Added redirect table to store redirect targets.
-* Added querycachetwo table (similar to querycache but has two titles)
-* PageArchive can now return a Revision object for more convenient processing
- of deleted revision data
-* Added 'UndeleteShowRevision' hook in Special:Undelete
-* Error message on attempt to view invalid or missing deleted revisions
-* Remove unsightly "_" from namespace in Special:Allpages, Special:Prefixindex
-* (bug 3224) Allow minor edits by bots to skip new message notification on
- user talk pages. This can be disabled by adjusting the 'nominornewtalk'
- permission. Patch by Werdna.
-* (bug 7741) MATH: fixed broken syntax of underbrace etc. Fixed arrays
-* Fix purging for updated SVG files
-* (bug 7745) Add id attribute to search button in Monobook
-* (bug 7749) MATH: added some more LaTeX symbols, e.g. parallel, diamond, ast, ...
-* (bug 7304) Added code in Article.php to keep redirect table up to date.
-* Made special page names case-insensitive and localisable. Care has been taken
- to maintain backwards compatibility.
-* Used special page subpages in a few more places, instead of query parameters.
-* (bug 7758) Added wrapper span to "templates used" explanation to allow CSS
- styling (class="mw-templatesUsedExplanation").
-* Added {{#special:}} parser function, to give the local default title for
- special pages
-* (bug 7766) Remove redundant / from AJAX requests, can break some servers
-* Add tab links from extensions to classic-based skins (SkinTemplateTab hook)
- Provides better cross-skin compatibility for extensions using the modern
- skin hooks, such as Oversight
-* Moved variant language links on Cologne Blue and Nostalgia to before the
- login/logout link
-* Fix for parser tests with MySQL 5 in strict mode
-* Added block option "enable autoblocks"
-* Amend Special:Ipblocklist to note when a block has autoblock DISABLED.
-* (bug 7780) Fix regression in editing redirects
-* Add whitespace above "templates included on this page" using CSS, not
- hardcoded line break.
-* Remove entries from redirect table on article deletion
-* (bug 7788) Force section headers in new section links for users who have
- 'prompt for blank edit summaries' on.
-* (bug 1133) Special:Emailuser: add an option to send yourself a copy of your mail.
-* (bug 461) Allow "Categories:" link at bottom of pages to be customized via
- pagecategorieslink message.
-* Sort the list of skins in "My Preferences" -> Skins by alphabetical order.
-* (bug 7785) Postgres compatibility for timestamps in RC feeds
-* (bug 7550) Normalize user parameter normally on Special:Log
-* (bug 7294) Fix PATH search for diff3 on install
-* Various fixes related to the blocking change re: autoblocks. On inserting
- an IP block, the ipb_enable_autoblock field is now automagically blanked,
- because it doesn't make any sense for an IP. Additionally, IP blocks
- without the ipb_enable_autoblock option no longer show up as "autoblock
- disabled" on Special:Ipblocklist.
-* (bug 7774) MATH: aded more amstex functions
-* (bug 1182) MATH: fixed inconsistent rendering of upper case Greek letters in TeX
-* Fix regression in streaming page dump generation
-* (bug 7801) Add support for parser function hooks in parser tests
-* checkUsernames.php now uses wfDebugLog instead of hardcoded path to log
-* (bug 7810) Update talk namespaces for Occitan
-* Allow case-sensitive URLs to be used for uploading from URLs.
-* (bug 1109) Correct fix for compressed 304 responses when additional output
- buffers have been installed within the compression handler
-* (bug 7819) Move automatic redirect edit summary after pre-save transform
- to work properly with subst: fun
-* (bug 7826) Fix typos in two English messages.
-* (bug 5365) Stop users being prompted to enter an edit summary for null edits,
- if they have selected that option in preferences.
-* (bug 5936) Show an 'm' to the left of the edit summary on diff pages for minor edits.
-* (bug 7820) Improve error reporting for uploads via URL.
-* (bug 5149) When autoblocks are enabled, retroactively apply an autoblock to the most
- recently used IP of a user when they are blocked.
-* Add an index on (rc_user_text,rc_timestamp) on the recentchanges table. This will
- make CheckUser.php and the new retroactive autoblock functionality faster.
-* Fix regression in Special:Undelete for revisions deleted under MediaWiki 1.4
- with compression or legacy encoding
-* (bug 6737) Fixes for MySQL 5 schema in strict mode
-* Approximate height for client-side scaling fallback instead of passing -1
- into the HTML output.
-* Make the DNSBL to check for proxy blocking configurable via $wgSorbsUrl
-* Add experimental recording/reporting mode to parser tests runner, to
- compare changes against the previous run.
- Additional tables 'testrun' and 'testitem' are in maintenance/testRunner.sql,
- source this and pass --record option to parserTests.php
-* Make the set of default parser test input files extensible via
- $wgParserTestFiles. This can now be appended to by extensions or local
- configuration files so that extension or custom tests can be automatically
- run along with the main batch.
-* Run PHP install version checks on update.php so command-line updaters see
- new version requirements
-* Do a check for the PHP 5.0.x 64-bit bug, since this is much more disruptive
- as of MW 1.8 than it used to be. Install or upgrade now aborts with a
- warning and a request to upgrade.
-* (bug 6440) Updated indexes to improve backlinking queries (links, templates, images)
-* Switched 'anon-only' block mode to default for IP blocks
-* (bug 3687, 7892) Add distinct heading for media files in category display,
- with count.
-* (bug 1578) Add different icons for external links to audio, video, or PDF in
- Monobook.
-* Made autoblocks block account creation if the user block has that option enabled.
-* Add auto-summaries to blankings and large removals without summaries.
-* (bug 7811) Allow preview of edit summaries.
-* (bug 6839) Wikibits.js minor changes to make JS-lint happier.
-* (bug 7932) Make sure that edit toolbar clears floats so it appears correctly.
-* (bug 6873) When viewing old revisions, add link to diff to current version.
-* (bug 3315) Provide rollback link directly on history page.
-* Replace 'old-revision-navigation' message with 'revision-info' and
- 'revision-nav' messages, wrapped in divs with appropriate id's.
-* (bug 4178) MediaWiki:Common.js will now be included for all users if
- $wgUseSiteJs is enabled, in addition to (if applicable) MediaWiki:Monobook.js
- and user JS subpages.
-* (bug 7918) "Templates used on this page" changes during preview to reflect
- any added or removed templates, and works as expected for section edits.
-* (bug 7919) "Templates used on this page" is now shown for read-only pages.
-* (bug 7688) When viewing diff, section anchors in autosummary jump to section
- on current page instead of loading the latest version.
-* (bug 7970) Use current connection explicitly on Database::getServerVersion
-* (bug 2001) Tables with class="sortable" can now be dynamically sorted via
- JavaScript.
-* Added autosummary for new pages with 500 or less characters, and refactor
- the autosummary code so it's all done in one function. doEdit is getting too
- big!
-* (bug 7554) The correct MIME type for SVG images is now displayed on the
- image page (image/svg+xml, not image/svg).
-* (bug 7883) Added autoblock whitelisting feature, using which specific ranges
- can be protected from autoblocking. These ranges are specified, in list format,
- in the autoblock_whitelist system message.
-* Added placeholders for text injection by hooks to EditPage.php
-* (bug 8009) Automatic edit summary for redirects is not filled for edits in existing pages
-* Installer support for experimental MySQL 4.1/5.0 binary-safe schema
-* Use INSERT IGNORE for db-based BagOStuff add/insert, for more memcache-like
- behavior when keys already exist on add (instead of dying with an error...)
-* Add a hook 'UploadForm:initial' before the upload form is generated, and two
- member variable for text injection into the form, which can be filled by the hooks.
-* (bug 6295) Add a "revision patching" functionality, where an edit can be undone
- (with a functionality similar to diff rev1 rev2 | patch -R rev3 -o rev3).
- This is triggered by including &undo=revid in an edit URL. A link to a URL
- that will undo a given edit is shown on NEW revision headers on diff pages.
- The link leads to a "Show Changes" page showing what will be done to undo the
- edit.
-* Fix display of link in "already rolled back" message for image/category pages
-* (bug 6016) Left-aligned images should stack vertically, like right-aligned
- images, not horizontally.
-* Patch from LeonWP: added UploadForm:BeforeProcessing hook in SpecialUpload.php
-* Add AuthPluginSetup hook to override $wgAuth after configuration
-* Fix regression in authentication hook auto-creation on login
-* (bug 8110) Allow spaces in ISBNs
-* (bug 8024) Introduce "send me copies of emails I send to others" preference
-* Added 'EditPage::attemptSave' hook before an article is saved.
-* (bug 8083) Applied patch for sk localisation
-* Add a backslash character to the edit token, to prevent edits via certain
- broken proxies that mangle such characters in form submissions
-* (bug 7461) Allow overwriting pages using importTextFile.php
-* (bug 7946) importTextFile.php doesn't perform pre-save transform
-* (bug 8117) {{REVISIONTIMESTAMP}} showed weird default if $wgLocalTZoffset set;
- now uses current time for previews and if timestamp can't be loaded from DB
-* {{REVISIONTIMESTAMP}} now uses site local timezone instead of user timezone
- to ensure consistent behavior
-* {{REVISIONTIMESTAMP}} and friends should now work on non-MySQL backends
-* (bug 7671) Observe canonical media namespace prefix in Linker::formatComment
-* Added js variable wgCurRevisionId to the output
-* (bug 8141) Cleanup of Parser::doTableStuff, patch by AzaTht
-* (bug 8042) Make miser mode caching limits settable via $wgQueryCacheLimit
- instead of hardcoding to 1000
-* Enable QueryPage classes to override list formatting
-* (bug 5485) Show number of intervening revisions in diff view
-* (bug 8100) Fix XHTML validity in Taiwanese localization
-* Added redirect to section feature. Use it wisely.
-* Added a configuration variable allowing the "break out of framesets" feature
- to be switched on and off ($wgBreakFrames). Off by default.
-* Allow Xml::check() $attribs parameter to override 'value' attribute
-* DB schema change: added two columns (rc_old_len and rc_new_len) to the recentchanges table to store
- the text lengths before and after the edit
-* (bug 1085) Made Special:Recentchanges show the character difference between the changed revisions
-* Removed a redundant <strong> tag from diff pages that was causing display issues for some users
-* (bug 8203) The keyboard shortcut for "log out" was removed, because users were pressing it
- when they intended to press the shortcut for "preview".
-* (bug 8148) Handle non-removable output buffers gracefully when cleaning
- buffers for HTTP 304 responses, StreamFile, and Special:Export.
- Duplicated code merged into wfResetOutputBuffers() and wfClearOutputBuffers()
-* Special:AllPages : 'next page' link now point to the first title of the next
- chunk instead of pointing to the last title of current chunk.
-* (bug 4673) Special:AllPages : add a 'previous' link (new message 'prevpage')
-* (bug 8121) wfRandom() was not between 0 and 1
-* Add static method Parser::createAssocArgs($args), so parser functions can
- use the same code to parse arguments as the templates do.
-* Change behavior of logins using the temporary e-mailed password (as stored
- in user_newpassword hash field). Instead of just logging in silently and
- leaving the previous user_password field in place indefinitely, the user
- is now prompted to set a new password.
-
- The password-changing form is at Special:Resetpass; currently it's only
- usable for changing from the temporary password during login, but it
- could perhaps be generalized, replacing the subform in preferences.
-
- Once the new password is set successfully, the temporary password is wiped
- so it cannot be used to login a second time, and the login process
- is completed.
-* Suppress 'mail new password' button on login form if $wgAuth forbids
- changing user passwords; it wouldn't work very well...
-* Consolidate password length checks and $wgAuth manipulation into
- User::setPassword() to avoid duplicate code in different places
- that set passwords.
-* User::setPassword() now throws PasswordError exceptions if the password
- is illegal or cannot be set via $wgAuth. These can be caught and a human-
- readable error message displayed by UI code.
-* Added Title::isSubpage()
-* (bug 8241) Don't consider user pages of User:Foo.css to be CSS subpages
-* Set an explicit class on framed thumbnail inner divs and images, changed some
- CSS to use these instead of using descendent selectors.
-* Accept null parameter to User::setPassword() as indicating the password
- field should be cleared to an unusable state. Login will only be possible
- after the password is reset, for instance by e-mail.
-* (bug 6394) Invalidate the password set for "by e-mail" account creations
- to avoid accidental empty password creations.
-* Made the show change size function work on page moves, page creations, and
- log entries. Also fixed it in the javascript recentchanges.
-* (bug 8239) correctly get 50 new contributions when clicking '(50 next)'
-* (bug 2259) Fix old regression where e-mail addresses were no longer
- confirmed on login with mailed password.
-* Add a notification about the confirmation mail sent during account
- creation, so people don't immediately go off to request a second one.
-* Add a warning on Special:Confirmemail if a code was already sent and has
- not yet expired.
-* Add user_editcount field to provide data for heuristics on account use.
- Incremented on edit, with lazy initialization from past revision data.
- Can batch-initialize with maintenance/initEditCount.php (not yet friendly
- to replication environments, this will do all accounts in one query).
-* Allow raw SQL subsections in Database::update() SET portion as well as
- for WHERE portion. Handy for increments and such.
-* User::getOption now accept a default value to override default user values
- this makes it consistent with WebRequest::get* methods. Corrected code in
- various places accordingly.
-* (bug 8264) Fix JavaScript global vars for XHTML mode
-* Make $wgSiteNotice value wikitext again, for consistency with editable
- MediaWiki:Sitenotice and MediaWiki:Anonnotice.
-* (bug 8044) When redirecting from the canonical name of the special page
- to the localised one, parameters/subpages are omitted
-* (bug 8164) Special:Booksources should use GET for form submission
-* Rewrite Special:Booksources to clean up interface and remove redundant code
-* (bug 7925) Change Special:Allmessages message name filter javascript to be
- a bit more responsive and easier on the CPU
-* (bug 4488) Support watching pages on deletion; introduces new user preference
-* Minor restructuring of Special:Preferences; "watch pages I edit" and "watch
- pages I create" options now accessible under "Watchlist" options
-* (bug 8153) <nowiki> doesn't work in site notice
-* (bug 6690) wfMsgNoTrans() transforms messages
-* (bug 8274) Wrap edit tools in a <div> with a specified class
-* Detect PHP 5.0.x 64-bit bug and abort in WebStart.php; too many things break
- mysteriously otherwise (detection code copied from install-utils.inc)
-* (bug 8295) Change handling of <center> tags in doBlockLevels() to match that
- of <div>
-* (bug 8110) Make magic ISBN linking stricter: only match ten-digit sequences
- (plus optional ISBN-13 prefix) with no immediately following alphanumeric
- character, disallow multiple consecutive internal redirects
-* (bug 2785) Accept optional colon prefix in links when formatting comments
-* Don't show "you can view and copy the source of this page" message for
- pages which don't exist
-* (bug 8310) Blank line added to top of 'post' when page is blank
-* (bug 8109) Template parameters ignored in "recentchangestext"
-* Gracefully skip redirect-to-fragment on WebKit versions less than 420;
- it messes up on current versions of Safari but is ok in the latest
- nightlies. Checking the version number will allow it to automatically
- work when new releases of Safari appear.
-* Fix regression in thumb styles; size and padding didn't match with
- new arrangement.
-* (bug 8333) Fix quick user data update on login password change on
- replication database setups. User data is now pulled from master
- instead of slave in User::loadFromDatabase, ensuring that it is
- fresh and accurate when read and then saved back into cache.
- This was breaking with the Special:Rename operation which
- automatically logs the user in with the new password after changing
- it; pulling from slave meant the record was often not the updated
- one.
-* (bug 8335) Set image width to the first valid parameter found.
-* (bug 8350) Fix watchlist viewing bug when using Postgres.
-* (bug 6603) When warning about invalid file extensions, output the bit
- of the extension we actually checked
-* (bug 7669) Drop defaults on BLOB/TEXT columns for better compatibility
- with MySQL's strict mode, often enabled by the Windows installer.
- The defaults are ignored anyway when strict mode is off...
-* (bug 7685) Use explicit values for ar_text and ar_flags when deleting,
- for better compatibility with MySQL's strict mode
-* Update default interwiki values to reflect changed location of ursine:
-* (bug 5411) Remove autopatrol preference
-* Users who have the "autopatrol" permission will have their edits marked as
- patrolled automatically
-* Users who do not have the "autopatrol" permission will no longer be able
- to mark their own edits as patrolled
-* Introduce 'PingLimiter' hook; see docs/hooks.txt for more information
-* (bug 532) Tweaked alt text for some interface messages
-* (bug 8231) Gave useful alt text to the main <img> on image pages
-* (bug 371) Remove alt text for "Enlarge" icon on thumbnails
-* Initialize user_editcount to 0 instead of NULL for newly created accounts
-* (bug 3696) Strip LRM and RLM characters from titles to work around the
- problem some people have where titles cut-and-pasted from lists include
- the bidi override characters appended to the lists.
- A more thorough blacklist for forbidden and translatable characters would
- be wise, though, as might a cleaner method for the lists in the first place.
-* Fix regression in email password resets on read-restricted sites
-* Set tabindex on fields in deletion form so you don't have to tab through
- the links in the sitenotice
-* (bug 8271) Show full time and date on viewer for individual deleted
- revisions
-* (bug 8214) Output file size limit and actual file size in appropriate units
- on Special:Upload
-* (bug 8016) Purge objectcache table during upgrade processes - use the --nopurge
- option to prevent this when running maintenance/update.php
-* (bug 7612) Remove superfluous link to Special:Categories from result items
- on Special:Mostcategories
-* {{PLURAL:}} now handles formatted numbers correctly
-* (bug 8331) Added the change size value to watchlists; therefore made
- watchlists use RecentChange::newFromRow() instead of newFromCurRow()
-* (bug 8351) Fix undo for simple reverts
-* (bug 6856) User::clearNotification() does not respect read-only mode
-* (bug 6853) Use a checkbox on the installer form to indicate that a superuser
- account should be used; this is clearer than the old check which relied on
- the password never being an obscure value
-* Remove old unused watchlist cache, which was a leftover from the old schema
- where watchlists were more expensive to generate
-* Minor cosmetic changes to Special:Userrights
-* Added wgCanonicalSpecialPageName to JavaScript variables
-* Fix image deleting when using Postgres.
-* Output both source and destination titles in maintenance/moveBatch.php
-* Added basic parser tests for language variants
-* Enable selflinks and categories to be written in some of the language variants
-* Prevent conversion of JavaScript code in language variants
-* Output software version number in maintenance/parserTests.php
-* (bug 7169) Use Ajax to watch/unwatch articles if enabled
-* Make variant table caching a little more robust, using main language code
- in cache key. Probably this is still a bit wonky, though. Was breaking
- parser tests when Chinese tables were getting loaded into Serbian code.
-* (bug 8380) Be nicer about blank lines in deleteBatch.php
-* (bug 8401) Fix regression in SORBS lookup for some DNS setups
-* Use raw file descriptor in posix_isatty() check to avoid warning on
- Linux systems with at least some versions of PHP
-* (bug 5908) Allow overriding the default category sort key for all items on
- a page using {{DEFAULTSORT}}
-* (bug 6449) Throw a more definitive error message when installation fails
- due to an invalid database name
-* (bug 5827) Use full text for option link labels on Special:Watchlist
-* (bug 8018) Allow hiding minor edits from the watchlist
-* (bug 8427) MonoBook RTL IE 7.0 tweaks failed when sidebar's navigation
- section is renamed; no longer relies on first section name
-* Stabilize client-side table sorting even if the underlying Javascript sort()
- implementation is unstable
-* Add hook for extensions to add user information to the panel in preferences,
- next to the user name and ID.
-* (bug 8392) Display protection status of transcluded pages in the edit page
- template list. Patch by Fyren, with i18n naming tweak.
-* Fix for interwiki transclusion where target wiki uses query string for title
-* Resolve namespaces on interwiki Title objects using canonical namespace names
- if possible (should not happen, though, outside interwiki transclusion... and
- maybe not even then, but it does)
-* (bug 8447) Fix SQL typo breaking non-default $wgHitcounterUpdateFreq
-* Do not allow previews of deleted images to be cached
-* Add global variable $wgDefaultLanguageVariant used to set the default language
- variant of a wiki to something different than the main language code
-* Add 'variant' option to parserTests - runs test with the given variant as
- preferred, utilize it for more parser tests of language variants code
-* (bug 6503) Fix bug that stopped certain irrelevant links from being hidden
- for printing
-* Avoid PHP warning in Creative Commons metadata when a creative commons
- license is not actually set up
-* (bug 8463) Don't print external link icons for Monobook
-* (bug 8461) Support watching pages on move
-* (bug 8041) Work around bug with debug_backtrace when Zend Optimizer is
- loaded by skipping the function. Use wfDebugBacktrace() wrapper function.
-* Reduce config file clutter by setting various script and upload paths
- based on $IP or $wgScriptPath in Setup.php. They can still be explicitly
- overridden in LocalSettings.php if desired...
-* Attempt to detect redirect loops for the canonical title redirect, and
- give some hints to the poor confused administrator.
-* Introduce new flag 'R' - raw output for language variant escape tags
-* Advise users when updates for a query page have been disabled using
- $wgDisableQueryPageUpdate
-* (bug 8413) Improve comments for $wgNamespaceRobotPolicies
-* (bug 8330) Show "bytes" suffix on recent changes diff counter
- optionally... if set in rc-changes-size message (default empty for now)
-* (bug 8489) Support basic links in <gallery> caption attribute
-* (bug 8485) Correct Lingala number formatting
-* The MediaWiki namespace is no longer pre-filled with default messages on
- install. All default messages will be removed from the MediaWiki namespace
- on upgrade.
-* Recentchanges RSS/Atom feeds now use a separate message for the description
- to avoid cluttering it with useless wiki formatting
-* (bug 8417) Handle EXIF unknown dates
-* (bug 8372) Return nothing on empty <math> tags.
-* New maintenance script to show the cached statistics : showStats.php.
-* Count deleted edits when regenerating total edits in maintenance/initStats.php
-* (bug 3706) Allow users to be exempted from IP blocks. The ipblock-exempt permission
- key has been added to enable this behaviour, by default assigned to sysops.
-* (bug 7948) importDump.php now warn that Recentchanges need to be rebuild.
-* (bug 7667) allow XHTML namespaces customization
-* (bug 8531) Correct local name of Lingála (patch by Raymond)
-* Fix regression with default lock file and cache directories; threw visible
- warning with open_basedir
+* 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 ==
+* 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)
-* Bishnupriya Manipuri (bpy)
-* Cantonese (zh-yue)
* Finnish (fi)
-* Frisian (fy)
-* German (de)
+* 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)
-* Kongo (kg)
+* Korean (ko)
+* Kashmiri (ks)
+* Ripuarian (ksh)
* Latin (la)
+* Luganda (lg)
* Limburgish (li)
-* Lingala (ln)
* Lithuanian (lt)
-* Maltese (mt)
-* Maori (mi)
-* Norwegian (no)
+* Latvian (lv)
+* Marathi (mr)
+* Low Saxon (nds)
+* Dutch Lower Saxon (nds-nl)
+* Nepali (ne)
+* Nepal Bhasa (new)
+* Dutch (nl)
* Occitan (oc)
-* Old Church Slavonic (cu)
+* Pali (pi)
* Polish (pl)
-* Portuguese (pt)
-* Ripurian (ksh)
+* Romanian (ro)
* Russian (ru)
+* Sanskrit (sa)
+* Sicilian (scn)
* Slovak (sk)
+* Sundanese (su)
* Swedish (sv)
-* Taiwanese/Holo: (bug 8217) changed language code to nan (from zh-min-nan)
- due to http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=n
-* Upper Sorbian (hsb)
+* 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.9 requires PHP 5 (5.1 recommended). PHP 4 is no longer supported.
+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
@@ -864,13 +507,11 @@ At this time we still recommend 4.0, but 4.1/5.0 will work fine in most cases.
== Upgrading ==
-Some minor database changes have been made since 1.7:
-* new fields and indexes on ipblocks
-* index change on recentchanges
+1.10 has several database changes since 1.9, and will not work without schema
+updates.
-Several changes from 1.5 and 1.6 do require updates to be run on upgrade.
-To ensure that these tables are filled with data, run refreshLinks.php after
-the upgrade.
+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
@@ -878,8 +519,6 @@ break. Don't forget to always back up your database before upgrading!
See the file UPGRADE for more detailed upgrade instructions.
-
-
=== Caveats ===
Some output, particularly involving user-supplied inline HTML, may not
@@ -889,14 +528,15 @@ cases, but this is not recommended on live sites. (This must be set for
MathML to display properly in Mozilla.)
-For notes on 1.8.x and older releases, see HISTORY.
+For notes on 1.9.x and older releases, see HISTORY.
=== Online documentation ===
Documentation for both end-users and site administrators is currently being
-built up on Meta-Wikipedia, and is covered under the GNU Free Documentation
-License:
+built up on MediaWiki.org, and is covered under the GNU Free Documentation
+License (except for pages that explicitly state that their contents are in
+the public domain) :
http://www.mediawiki.org/wiki/Documentation
diff --git a/Test.php b/Test.php
new file mode 100644
index 00000000..bad931a4
--- /dev/null
+++ b/Test.php
@@ -0,0 +1,307 @@
+<?php
+
+/*
+
+=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"
+
+ # 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 );
+
+=head1 DESCRIPTION
+
+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)>.
+
+=cut
+
+*/
+
+register_shutdown_function('test_ends');
+
+$Test = array(
+ 'run' => 0,
+ 'failed' => 0,
+ 'badpass' => 0,
+ 'planned' => null
+);
+
+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;
+ }
+}
+
+function pass( $desc = '' )
+{
+ return proclaim(true, $desc);
+}
+
+function fail( $desc = '' )
+{
+ return proclaim( false, $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 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 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" );
+}
+
+function diag($message)
+{
+ if (is_array($message))
+ {
+ $message = implode("\n", $message);
+ }
+
+ $messages = explode("\n", $message);
+
+ foreach ($messages as $msg)
+ {
+ echo "# $msg\n";
+ }
+}
+
+function include_ok( $file, $desc = '' )
+{
+ $pass = include $file;
+ return proclaim( $pass, $desc == '' ? "include $file" : $desc );
+}
+
+function require_ok( $file, $desc = '' )
+{
+ $pass = require $file;
+ return proclaim( $pass, $desc == '' ? "require $file" : $desc );
+}
+
+function is_deeply( $got, $expected, $desc = '' )
+{
+ // hack
+ $s_got = serialize( $got );
+ $s_exp = serialize( $expected );
+
+ $pass = $s_got == $s_exp;
+
+ proclaim( $pass, $desc, /* todo */ false, $got, $expected );
+}
+
+function isa_ok( $obj, $expected, $desc = '' ) {
+ $name = get_class( $obj );
+ $pass = $name == $expected;
+ proclaim( $pass, $desc, /* todo */ false, $name, $expected );
+}
+
+function proclaim(
+ $cond, // bool
+ $desc = '',
+ $todo = false,
+ $got = null,
+ $expected = null,
+ $negate = false ) {
+
+ global $Test;
+
+ $Test['run'] += 1;
+
+ # TODO: force_todo
+
+ # Everything after the first # is special, so escape user-supplied messages
+ $desc = str_replace( '#', '\\#', $desc );
+ $desc = str_replace( "\n", '\\n', $desc );
+
+ $ok = $cond ? "ok" : "not ok";
+ $directive = $todo === false ? '' : '# TODO aoeu';
+
+ printf( "%s %d %s%s\n", $ok, $Test['run'], $desc, $directive );
+
+ if ( ! $cond ) {
+ report_failure( $desc, $got, $expected, $negate, $todo );
+ }
+
+ return $cond;
+}
+
+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
+ )
+ );
+}
+
+function test_ends ()
+{
+ global $Test;
+
+ if ( $Test['planned'] === false ) {
+ printf( "1..%d\n", $Test['run'] );
+ }
+}
+
+/*
+
+=head1 TODO
+
+=over
+
+=item * Fully document this file
+
+=item *
+
+Add TODO support, maybe via C<ok(0, "foo # TODO fix this")>
+C<ok(1, "foo", array( 'todo' => 'fix this'))>.
+
+=back
+
+=head1 SEE ALSO
+
+=over
+
+=item L<TAP> - The TAP protocol
+
+=item L<Test::More>
+
+=item Pugs's Test.pm
+
+=back
+
+=head1 AUTHOR
+
+Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+
+=head1 LICENSING
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
+
+*/
+
+?>
diff --git a/bin/svnstat b/bin/svnstat
new file mode 100755
index 00000000..b134cdc2
--- /dev/null
+++ b/bin/svnstat
@@ -0,0 +1,2 @@
+#!/bin/sh
+svn stat -v $1 | sed -n 's/^[ A-Z?\*|!]\{1,15\}/r/;s/ \{1,15\}/\/r/;s/ .*//p'
diff --git a/bin/ulimit4.sh b/bin/ulimit4.sh
new file mode 100755
index 00000000..2a840d2f
--- /dev/null
+++ b/bin/ulimit4.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+ulimit -t $1 -v $2 -f $3
+eval "$4"
diff --git a/config/index.php b/config/index.php
index 69394e69..d036e8c8 100644
--- a/config/index.php
+++ b/config/index.php
@@ -226,6 +226,26 @@ class ConfigData {
function getSitename() { return $this->getEncoded( $this->Sitename ); }
function getSysopName() { return $this->getEncoded( $this->SysopName ); }
function getSysopPass() { return $this->getEncoded( $this->SysopPass ); }
+
+ function setSchema( $schema ) {
+ $this->DBschema = $schema;
+ switch ( $this->DBschema ) {
+ case 'mysql5':
+ $this->DBTableOptions = 'ENGINE=InnoDB, DEFAULT CHARSET=utf8';
+ $this->DBmysql5 = 'true';
+ break;
+ case 'mysql5-binary':
+ $this->DBTableOptions = 'ENGINE=InnoDB, DEFAULT CHARSET=binary';
+ $this->DBmysql5 = 'true';
+ break;
+ default:
+ $this->DBTableOptions = 'TYPE=InnoDB';
+ $this->DBmysql5 = 'false';
+ }
+ # Set the global for use during install
+ global $wgDBTableOptions;
+ $wgDBTableOptions = $this->DBTableOptions;
+ }
}
?>
@@ -245,7 +265,7 @@ class ConfigData {
<?php
$endl = "
";
-$wgNoOutputBuffer = true;
+define( 'MW_NO_OUTPUT_BUFFER', 1 );
$conf = new ConfigData;
install_version_checks();
@@ -326,6 +346,15 @@ if( ini_get( "mbstring.func_overload" ) ) {
<?php
}
+if( ini_get( "zend.ze1_compatibility_mode" ) ) {
+ $fatal = true;
+ ?><li class="error"><strong>Fatal: <a href="http://www.php.net/manual/en/ini.core.php">zend.ze1_compatibility_mode</a> is active!</strong>
+ This option causes horrible bugs with MediaWiki; you cannot install or use
+ MediaWiki unless this option is disabled.
+ <?php
+}
+
+
if( $fatal ) {
dieout( "</ul><p>Cannot install MediaWiki.</p>" );
}
@@ -360,20 +389,42 @@ if( $conf->xml ) {
If you're running Mandrake, install the php-xml package." );
}
-# Crude check for session support
+# Check for session support
if( !function_exists( 'session_name' ) )
dieout( "PHP's session module is missing. MediaWiki requires session support in order to function." );
-# Likewise for PCRE
+# session.save_path doesn't *have* to be set, but if it is, and it's
+# not valid/writable/etc. then it can cause problems
+$sessionSavePath = ini_get( 'session.save_path' );
+# Warn the user if it's not set, but let them proceed
+if( !$sessionSavePath ) {
+ print "<li><strong>Warning:</strong> A value for <tt>session.save_path</tt>
+ has not been set in PHP.ini. If the default value causes problems with
+ saving session data, set it to a valid path which is read/write/execute
+ for the user your web server is running under.</li>";
+} elseif ( is_dir( $sessionSavePath ) && is_writable( $sessionSavePath ) ) {
+ # All good? Let the user know
+ print "<li>Session save path appears to be valid.</li>";
+} else {
+ # Something not right? Halt the installation so the user can fix it up
+ dieout( "Your session save path appears to be invalid or is not writable.
+ PHP needs to be able to save data to this location in order for correct
+ session operation. Please check that <tt>session.save_path</tt> in
+ <tt>PHP.ini</tt> points to a valid path, and is read/write/execute for
+ the user your web server is running under." );
+}
+
+# Check for PCRE support
if( !function_exists( 'preg_match' ) )
- dieout( "The PCRE regular expression functions are missing. MediaWiki requires these in order to function." );
+ dieout( "The PCRE support module appears to be missing. MediaWiki requires the
+ Perl-compatible regular expression functions." );
$memlimit = ini_get( "memory_limit" );
$conf->raiseMemory = false;
if( empty( $memlimit ) || $memlimit == -1 ) {
print "<li>PHP is configured with no <tt>memory_limit</tt>.</li>\n";
} else {
- print "<li>PHP's <tt>memory_limit</tt> is " . htmlspecialchars( $memlimit ) . ". <strong>If this is too low, installation may fail!</strong> ";
+ print "<li>PHP's <tt>memory_limit</tt> is " . htmlspecialchars( $memlimit ) . ". ";
$n = intval( $memlimit );
if( preg_match( '/^([0-9]+)[Mm]$/', trim( $memlimit ), $m ) ) {
$n = intval( $m[1] * (1024*1024) );
@@ -381,7 +432,7 @@ if( empty( $memlimit ) || $memlimit == -1 ) {
if( $n < 20*1024*1024 ) {
print "Attempting to raise limit to 20M... ";
if( false === ini_set( "memory_limit", "20M" ) ) {
- print "failed.";
+ print "failed.<br /><b>" . htmlspecialchars( $memlimit ) . " seems too low, installation may fail!</b>";
} else {
$conf->raiseMemory = true;
print "ok.";
@@ -390,13 +441,6 @@ if( empty( $memlimit ) || $memlimit == -1 ) {
print "</li>\n";
}
-$conf->zlib = function_exists( "gzencode" );
-if( $conf->zlib ) {
- print "<li>Have zlib support; enabling output compression.</li>\n";
-} else {
- print "<li>No zlib support.</li>\n";
-}
-
$conf->turck = function_exists( 'mmcache_get' );
if ( $conf->turck ) {
print "<li><a href=\"http://turck-mmcache.sourceforge.net/\">Turck MMCache</a> installed</li>\n";
@@ -415,7 +459,7 @@ if ( $conf->eaccel ) {
if( !$conf->turck && !$conf->eaccel && !$conf->apc ) {
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://eaccelerator.sourceforge.net">eAccelerator</a>, or
<a href="http://www.php.net/apc">APC</a>. Object caching functions cannot be used.</li>' );
}
@@ -468,8 +512,6 @@ if( $conf->HaveGD ) {
}
}
-$conf->UseImageResize = $conf->HaveGD || $conf->ImageMagick;
-
$conf->IP = dirname( dirname( __FILE__ ) );
print "<li>Installation directory: <tt>" . htmlspecialchars( $conf->IP ) . "</tt></li>\n";
@@ -509,10 +551,7 @@ print "<li style='font-weight:bold;color:green;font-size:110%'>Environment check
## MySQL specific:
$conf->DBprefix = importPost( "DBprefix" );
- $conf->DBschema = importPost( "DBschema", "mysql4" );
- $conf->DBmysql5 = ($conf->DBschema == "mysql5" ||
- $conf->DBschema == "mysql5-binary")
- ? "true" : "false";
+ $conf->setSchema( importPost( "DBschema", "mysql4" ) );
$conf->LanguageCode = importPost( "LanguageCode", "en" );
## Postgres specific:
@@ -529,7 +568,10 @@ if( $conf->Sitename == "" || $conf->Sitename == "MediaWiki" || $conf->Sitename =
if( $conf->DBuser == "" ) {
$errs["DBuser"] = "Must not be blank";
}
-if( $conf->DBpassword == "" ) {
+if( ($conf->DBtype == 'mysql') && (strlen($conf->DBuser) > 16) ) {
+ $errs["DBuser"] = "Username too long";
+}
+if( $conf->DBpassword == "" && $conf->DBtype != "postgres" ) {
$errs["DBpassword"] = "Must not be blank";
}
if( $conf->DBpassword != $conf->DBpassword2 ) {
@@ -654,7 +696,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
>http://dev.mysql.com/doc/mysql/en/old-client.html</a> for help.</b></li>\n";
}
$ok = true; # Let's be optimistic
-
+
# Decide if we're going to use the superuser or the regular database user
$conf->Root = $useRoot;
if( $conf->Root ) {
@@ -664,7 +706,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
$db_user = $wgDBuser;
$db_pass = $wgDBpassword;
}
-
+
# Attempt to connect
echo( "<li>Attempting to connect to database server as $db_user..." );
$wgDatabase = Database::newFromParams( $wgDBserver, $db_user, $db_pass, '', 1 );
@@ -706,14 +748,14 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
break;
} # switch
} #conn. att.
-
+
if( !$ok ) { continue; }
} else /* not mysql */ {
error_reporting( E_ALL );
$wgSuperUser = '';
## Possible connect as a superuser
- if( $conf->Root ) {
+ if( $useRoot ) {
$wgDBsuperuser = $conf->RootUser;
echo( "<li>Attempting to connect to database \"postgres\" as superuser \"$wgDBsuperuser\"..." );
$wgDatabase = $dbc->newFromParams($wgDBserver, $wgDBsuperuser, $conf->RootPW, "postgres", 1);
@@ -797,6 +839,32 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
if( $wgDatabase->tableExists( "cur" ) || $wgDatabase->tableExists( "revision" ) ) {
print "<li>There are already MediaWiki tables in this database. Checking if updates are needed...</li>\n";
+ # Determine existing default character set
+ if ( $wgDatabase->tableExists( "revision" ) ) {
+ $revision = $wgDatabase->escapeLike( $conf->DBprefix . 'revision' );
+ $res = $wgDatabase->query( "SHOW TABLE STATUS LIKE '$revision'" );
+ $row = $wgDatabase->fetchObject( $res );
+ 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';
+ } else {
+ $existingSchema = false;
+ echo "<li><strong>Warning:</strong> Unrecognised existing collation</li>\n";
+ }
+ 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 );
+ }
+ }
+
# Create user if required (todo: other databases)
if ( $conf->Root && $conf->DBtype == 'mysql') {
$conn = $dbc->newFromParams( $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, 1 );
@@ -812,33 +880,18 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
dbsource( "../maintenance/users.sql", $wgDatabase );
}
}
- print "<pre>\n";
+ print "</ul><pre>\n";
chdir( ".." );
flush();
do_all_updates();
chdir( "config" );
print "</pre>\n";
- print "<li>Finished update checks.</li>\n";
+ print "<ul><li>Finished update checks.</li>\n";
} else {
# FIXME: Check for errors
print "<li>Creating tables...";
if ($conf->DBtype == 'mysql') {
- switch( $conf->DBschema ) {
- case "mysql4":
- print " using MySQL 4 table defs...";
- dbsource( "../maintenance/tables.sql", $wgDatabase );
- break;
- case "mysql5":
- print " using MySQL 5 UTF-8 table defs...";
- dbsource( "../maintenance/mysql5/tables.sql", $wgDatabase );
- break;
- case "mysql5-binary":
- print " using MySQL 5 binary table defs...";
- dbsource( "../maintenance/mysql5/tables-binary.sql", $wgDatabase );
- break;
- default:
- dieout( " <b>invalid schema selection!</b></li>" );
- }
+ dbsource( "../maintenance/tables.sql", $wgDatabase );
dbsource( "../maintenance/interwiki.sql", $wgDatabase );
} else if ($conf->DBtype == 'postgres') {
$wgDatabase->setup_database();
@@ -900,7 +953,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
$newid = $article->insertOn( $wgDatabase );
$revision = new Revision( array(
'page' => $newid,
- 'text' => wfMsg( 'mainpagetext' ) . "\n\n" . wfMsg( 'mainpagedocfooter' ),
+ 'text' => wfMsg( 'mainpagetext' ) . "\n\n" . wfMsgNoTrans( 'mainpagedocfooter' ),
'comment' => '',
'user' => 0,
'user_text' => 'MediaWiki default',
@@ -912,7 +965,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$endl\r\n";
// Fix up a common line-ending problem (due to CVS on Windows)
$localSettings = str_replace( "\r\n", "\n", $localSettings );
$f = fopen( "LocalSettings.php", 'xt' );
@@ -1063,7 +1116,7 @@ if( count( $errs ) ) {
<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-separted list. Only
+ 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.
</p>
</div>
@@ -1079,7 +1132,7 @@ if( count( $errs ) ) {
</ul>
</div>
<p class="config-desc">
- Use this to disable all e-mail functions (password reminders, user-to-user e-mail and e-mail notifications)
+ Use this to disable all e-mail functions (password reminders, user-to-user e-mail, and e-mail notifications)
if sending mail doesn't work on your server.
</p>
@@ -1157,7 +1210,7 @@ if( count( $errs ) ) {
enter those here. If you have database root access (see below)
you can specify new accounts/databases to be created. This account
will not be created if it pre-exists. If this is the case, ensure that it
- has SELECT, INSERT, UPDATE and DELETE permissions on the MediaWiki database.
+ has SELECT, INSERT, UPDATE, and DELETE permissions on the MediaWiki database.
</p>
<div class="config-input">
@@ -1175,7 +1228,7 @@ if( count( $errs ) ) {
aField( $conf, "RootPW", "Superuser password:", "password" );
?>
</div>
-
+
<p class="config-desc">
If the database user specified above does not exist, or does not have access to create
the database (if needed) or tables within it, please check the box and provide details
@@ -1188,7 +1241,7 @@ if( count( $errs ) ) {
?></div>
<div class="config-desc">
<p>If you need to share one database between multiple wikis, or
- MediaWiki and another web application, you may choose to
+ between MediaWiki and another web application, you may choose to
add a prefix to all the table names to avoid conflicts.</p>
<p>Avoid exotic characters; something like <tt>mw_</tt> is good.</p>
@@ -1221,8 +1274,9 @@ if( count( $errs ) ) {
aField( $conf, "DBts2schema", "Schema for tsearch2:" );
?></div>
<div class="config-desc">
- <p>The username specified above will have it's search path set to the above schemas,
- so it is recommended that you create a new user.</p>
+ <p>The username specified above (at "DB username") will have its search path set to the above schemas,
+ 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>
@@ -1283,9 +1337,7 @@ function escapePhpString( $string ) {
}
function writeLocalSettings( $conf ) {
- $conf->UseImageResize = $conf->UseImageResize ? 'true' : 'false';
$conf->PasswordSender = $conf->EmergencyContact;
- $zlib = ($conf->zlib ? "" : "# ");
$magic = ($conf->ImageMagick ? "" : "# ");
$convert = ($conf->ImageMagick ? $conf->ImageMagick : "/usr/bin/convert" );
$rights = ($conf->RightsUrl) ? "" : "# ";
@@ -1380,10 +1432,9 @@ if ( \$wgCommandLineMode ) {
if ( isset( \$_SERVER ) && array_key_exists( 'REQUEST_METHOD', \$_SERVER ) ) {
die( \"This script must be run from the command line\\n\" );
}
-} elseif ( empty( \$wgNoOutputBuffer ) ) {
- ## Compress output if the browser supports it
- {$zlib}if( !ini_get( 'zlib.output_compression' ) ) @ob_start( 'ob_gzhandler' );
}
+## Uncomment this to disable output compression
+# \$wgDisableOutputCompression = true;
\$wgSitename = \"{$slconf['Sitename']}\";
@@ -1417,6 +1468,9 @@ if ( \$wgCommandLineMode ) {
\$wgDBport = \"{$slconf['DBport']}\";
\$wgDBprefix = \"{$slconf['DBprefix']}\";
+# MySQL table options to use during installation or update
+\$wgDBTableOptions = \"{$slconf['DBTableOptions']}\";
+
# Schemas for Postgres
\$wgDBmwschema = \"{$slconf['DBmwschema']}\";
\$wgDBts2schema = \"{$slconf['DBts2schema']}\";
@@ -1431,7 +1485,6 @@ if ( \$wgCommandLineMode ) {
## To enable image uploads, make sure the 'images' directory
## is writable, then set this to true:
\$wgEnableUploads = false;
-\$wgUseImageResize = {$conf->UseImageResize};
{$magic}\$wgUseImageMagick = true;
{$magic}\$wgImageMagickConvertCommand = \"{$convert}\";
diff --git a/docs/database.txt b/docs/database.txt
index 25dce8b7..80044734 100644
--- a/docs/database.txt
+++ b/docs/database.txt
@@ -7,7 +7,7 @@ By Tim Starling, January 2006.
For information about the MediaWiki database layout, such as a
description of the tables and their contents, please see:
- http://meta.wikimedia.org/wiki/Help:Database_layout
+ http://www.mediawiki.org/wiki/Manual:Database_layout
http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/maintenance/tables.sql?view=markup
@@ -17,7 +17,7 @@ description of the tables and their contents, please see:
To make a read query, something like this usually suffices:
-$dbr =& wfGetDB( DB_SLAVE );
+$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( /* ...see docs... */ );
while ( $row = $dbr->fetchObject( $res ) ) {
...
@@ -28,7 +28,7 @@ Note the assignment operator in the while condition.
For a write query, use something like:
-$dbw =& wfGetDB( DB_MASTER );
+$dbw = wfGetDB( DB_MASTER );
$dbw->insert( /* ...see docs... */ );
We use the convention $dbr for read and $dbw for write to help you keep
@@ -103,7 +103,7 @@ regularly lag by several minutes, making review of recent edits
difficult.
In addition to this, MediaWiki attempts to ensure that the user sees
-events occuring on the wiki in chronological order. A few seconds of lag
+events occurring on the wiki in chronological order. A few seconds of lag
can be tolerated, as long as the user sees a consistent picture from
subsequent requests. This is done by saving the master binlog position
in the session, and then at the start of each request, waiting for the
@@ -157,7 +157,7 @@ Often this approach is not good enough, and it becomes necessary to
enclose small groups of queries in their own transaction. Use the
following syntax:
-$dbw =& wfGetDB( DB_MASTER );
+$dbw = wfGetDB( DB_MASTER );
$dbw->immediateBegin();
/* Do queries */
$dbw->immediateCommit();
diff --git a/docs/deferred.txt b/docs/deferred.txt
index 445eb0e4..06155c56 100644
--- a/docs/deferred.txt
+++ b/docs/deferred.txt
@@ -17,3 +17,11 @@ smart like collating updates to the same table or such because
the list is almost always going to have just one item on it, if
that, so it's not worth the trouble.
+
+Since 1.6 there is a 'job queue' in the jobs table, which is used
+to update link tables of transcluding pages after edits; this
+may be extended in the future to more general background tasks.
+
+Job queue items are fetched out of the queue and run either
+at a random rate during regular page views (by default) or by
+a batch process which can be run via maintenance/runJobs.php.
diff --git a/docs/design.txt b/docs/design.txt
index 5fff9fd0..8f24d0d8 100644
--- a/docs/design.txt
+++ b/docs/design.txt
@@ -1,5 +1,8 @@
This is a brief overview of the new design.
+More thorough and up-to-date information is available on the documentation
+wiki at http://www.mediawiki.org/
+
Primary source files/objects:
index.php
@@ -42,10 +45,15 @@ Primary source files/objects:
don't involve their text, such as access rights.
Article
- Encapsulates access to the "cur" table of the database. The
+ Encapsulates access to the "page" table of the database. The
object represents a an article, and maintains state such as
text (in Wikitext format), flags, etc.
+ Revision
+ Encapsulates individual page revision data and access to the
+ revision/text/blobs storage system. Higher-level code should
+ never touch text storage directly; this class mediates it.
+
Skin
Encapsulates a "look and feel" for the wiki. All of the
functions that render HTML, and make choices about how to
@@ -117,12 +125,3 @@ Naming/coding conventions:
of session variables are wsName, cookies wcName, and form field
values wpName ("p" for "POST").
- - Be kind to your release manager and don't use CVS keywords (Id,
- Revision, etc.) to mark file versions. They make merging code
- between different branches a pain for CVS, and are kind of sketchy
- for versions after that. (Yes, you can use the '-kk' flag so that
- merges ignore keywords, but that messes up binary files. See
- https://www.cvshome.org/docs/manual/cvs-1.11.18/cvs_5.html#SEC64).
-
-
-
diff --git a/docs/hooks.txt b/docs/hooks.txt
index d5a17660..9f5d289f 100644
--- a/docs/hooks.txt
+++ b/docs/hooks.txt
@@ -286,6 +286,11 @@ $isminor: minor flag
$iswatch: watch flag
$section: section #
+'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)
+
'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
@@ -308,6 +313,18 @@ $user: the user _doing_ the block (not the one being blocked)
$block: the Block object that was saved
$user: the user who did the block (not the one being blocked)
+'BookInformation': Before information output on Special:Booksources
+$isbn: ISBN to show information for
+$output: OutputPage object in use
+
+'CustomEditor': When invoking the page editor
+$article: Article being edited
+$user: User performing the edit
+
+Return true to allow the normal editor to be used, or false
+if implementing a custom editor, e.g. for a special namespace,
+etc.
+
'DiffViewHeader': called before diff display
$diff: DifferenceEngine object that's calling
$oldRev: Revision object of the "old" revision (may be null/invalid)
@@ -321,6 +338,11 @@ saved, that is before insertNewArticle() is called
&$text: Text to preload with
&$title: Title object representing the page being created
+'EditPage::showEditForm:fields': allows injection of form field into edit form
+&$editor: the EditPage instance for reference
+&$out: an OutputPage instance to write to
+return value is ignored (should always return true)
+
'EditFilter': Perform checks on an edit
$editor: Edit form (see includes/EditPage.php)
$text: Contents of the edit box
@@ -356,6 +378,9 @@ $text: text of the mail
&$list: List object (defaults to NULL, change it to an object instance and return
false override the list derivative used)
+'FileUpload': When a file upload occurs
+$file : Image object representing the file that was uploaded
+
'GetInternalURL': modify fully-qualified URLs used for squid cache purging
$title: Title object of page
$url: string value as output (out parameter, can modify)
@@ -371,6 +396,12 @@ $title: Title object of page
$url: string value as output (out parameter, can modify)
$query: query options passed to Title::getFullURL()
+'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
+
'LogPageValidTypes': action being logged. DEPRECATED: Use $wgLogTypes
&$type: array of strings
@@ -405,11 +436,23 @@ the resulting HTML is about to be displayed.
$parserOutput: the parserOutput (object) that corresponds to the page
$text: the text that will be displayed, in HTML (string)
+'PageHistoryBeforeList': When a history page list is about to be constructed.
+$article: the article that the history is loading for
+
+'PageHistoryLineEnding' : right before the end <li> is added to a history line
+$row: the revision row for this line
+$s: the string representing this parsed line
+
'PageRenderingHash': alter the parser cache option hash key
A parser extension which depends on user options should install
this hook and append its values to the key.
$hash: reference to a hash key string which can be modified
+'ParserTestTables': alter the list of tables to duplicate when parser tests
+are run. Use when page save hooks require the presence of custom tables
+to ensure that tests continue to run properly.
+&$tables: array of table names
+
'PersonalUrls': Alter the user-specific navigation links (e.g. "my page,
my talk page, my contributions" etc).
@@ -427,6 +470,16 @@ the built-in rate limiting checks are used, if enabled.
$form : PreferencesForm object
&$html : HTML to append to
+'RawPageViewBeforeOutput': Right before the text is blown out in action=raw
+&$obj: RawPage object
+&$text: The text that's going to be the output
+
+'SearchUpdate': Prior to search update completion
+$id : Page id
+$namespace : Page namespace
+$title : Page title
+$text : Current text being indexed
+
'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,
@@ -436,6 +489,10 @@ 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.
+'SkinTemplateOutputPageBeforeExec': Before SkinTemplate::outputPage() starts page output
+&$sktemplate: SkinTemplate object
+&$tpl: Template engine object
+
'TitleMoveComplete': after moving an article (title)
$old: old title
$nt: new title
@@ -529,5 +586,9 @@ $content_actions: The array of content actions
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.
diff --git a/docs/memcached.txt b/docs/memcached.txt
index 6752e9c8..d4e2915f 100644
--- a/docs/memcached.txt
+++ b/docs/memcached.txt
@@ -29,7 +29,6 @@ can run multiple servers on one machine or on multiple machines on
a network; storage can be distributed across multiple servers, and
multiple web servers can use the same cache cluster.
-
********************* W A R N I N G ! ! ! ! ! ***********************
Memcached has no security or authentication. Please ensure that your
server is appropriately firewalled, and that the port(s) used for
@@ -54,8 +53,8 @@ on port 11000, using up to 64MB of memory)
In your LocalSettings.php file, set:
- $wgUseMemCached = true;
- $wgMemCachedServers = array( "127.0.0.1:11000" );
+ $wgMainCacheType = CACHE_MEMCACHED;;
+ $wgMemCachedServers = array( "127.0.0.1:11000" );
The wiki should then use memcached to cache various data. To use
multiple servers (physically separate boxes or multiple caches
@@ -66,10 +65,9 @@ usage evenly), make its entry a subarray:
$wgMemCachedServers = array(
"127.0.0.1:11000", # one gig on this box
- array("192.168.0.1:11000", 2) # two gigs on the other box
+ array("192.168.0.1:11000", 2 ) # two gigs on the other box
);
-
== PHP client for memcached ==
As of this writing, MediaWiki includes version 1.0.10 of the PHP
@@ -129,4 +127,4 @@ IP blocks:
stores: array of arrays, for the BlockCache class
cleared by: BlockCache:clear()
-... more to come ...
+... more to come ... \ No newline at end of file
diff --git a/docs/title.txt b/docs/title.txt
index b404bd3c..5d9bd417 100644
--- a/docs/title.txt
+++ b/docs/title.txt
@@ -35,9 +35,14 @@ and the colon is just removed. Note that because of these
rules, it is possible to have articles with colons in their
names. "E. Coli 0157:H7" is a valid title, as is "2001: A Space
Odyssey", because "E. Coli 0157" and "2001" are not valid
-interwikis or namespaces. Likewise, ":de:name" is a link to
-the article "de:name"--even though "de" is a valid interwiki,
-the initial colon stops all prefix matching.
+interwikis or namespaces.
+
+It is not possible to have an article whose bare name includes
+a namespace or interwiki prefix.
+
+An initial colon in a title listed in wiki text may however
+suppress special handling for interlanguage links, image links,
+and category links.
Character mapping rules: Once prefixes have been stripped, the
rest of the title processed this way: spaces and underscores are
@@ -64,9 +69,9 @@ lowercase. The namespace will use underscores when returned
alone; it will use spaces only when attached to the text title.
getArticleID() needs some explanation: for "internal" articles,
-it should return the "cur_id" field if the article exists, else
+it should return the "page_id" field if the article exists, else
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 wuickly while rendering wiki
+cached, so they can be looked up quickly while rendering wiki
text with lots of internal links.
diff --git a/extensions/FunnyDot.php b/extensions/FunnyDot.php
index 418575e2..d9d932ae 100644
--- a/extensions/FunnyDot.php
+++ b/extensions/FunnyDot.php
@@ -1,9 +1,6 @@
<?php
-if ( defined( 'MEDIAWIKI' ) ) {
-
-global $wgHooks;
-$wgHooks['ArticleSave'][] = 'checkAntiSpamHash';
+$wgHooks['ArticleSave'][] = 'FunnyDot::checkAntiSpamHash';
$wgExtensionCredits['other'][] = array(
'name' => 'FunnyDot',
@@ -12,13 +9,15 @@ $wgExtensionCredits['other'][] = array(
'url' => 'http://www.laber-land.de',
);
-function hexVal($in)
+class FunnyDot {
+
+private static function hexVal($in)
{
$result = preg_replace('/[^0-9a-fA-F]/', '', $in);
return (empty($result) ? 0 : $result);
}
-function checkAntiSpamHash()
+public static function checkAntiSpamHash()
{
global $wgAntiSpamHash, $wgAntiSpamTimeout, $wgAntiSpamWait;
@@ -27,7 +26,7 @@ function checkAntiSpamHash()
if (!empty($_COOKIE['AntiSpamTime']) && !empty($_COOKIE['AntiSpamHash']))
{
$time = intval($_COOKIE['AntiSpamTime']);
- $hash = hexVal($_COOKIE['AntiSpamHash']);
+ $hash = self::hexVal($_COOKIE['AntiSpamHash']);
if ($hash != sha1($time.$wgAntiSpamHash))
{
@@ -51,5 +50,6 @@ function checkAntiSpamHash()
return true;
}
-} # End invocation guard
+}
+
?> \ No newline at end of file
diff --git a/extensions/LLAuthPlugin.php b/extensions/LLAuthPlugin.php
index 2d903b28..f5bb718f 100644
--- a/extensions/LLAuthPlugin.php
+++ b/extensions/LLAuthPlugin.php
@@ -1,40 +1,6 @@
<?php
-/**
- * @package MediaWiki
- */
-# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
-/**
- * Authentication plugin interface. Instantiate a subclass of AuthPlugin
- * and set $wgAuth to it to authenticate against some external tool.
- *
- * The default behavior is not to do anything, and use the local user
- * database for all authentication. A subclass can require that all
- * accounts authenticate externally, or use it only as a fallback; also
- * you can transparently create internal wiki accounts the first time
- * someone logs in who can be authenticated externally.
- *
- * This interface is new, and might change a bit before 1.4.0 final is
- * done...
- *
- * @package MediaWiki
- */
+$wgHooks['isValidPassword'][] = 'LLAuthPlugin::isValidPassword';
$wgExtensionCredits['other'][] = array(
'name' => 'LLAuthPlugin',
@@ -47,218 +13,92 @@ require_once('includes/AuthPlugin.php');
class LLAuthPlugin extends AuthPlugin {
+ public static function isValidPassword($password) {
+ $length = strlen($password);
+ return ($length >= 6 && $length <= 25);
+ }
+
private $dbLink = null;
- function __construct()
- {
+ function __construct() {
global $wgDBuser, $wgDBpassword;
$this->dbLink = mysqli_connect('localhost', $wgDBuser, $wgDBpassword, 'current');
- }
+ }
- function __destruct()
- {
+ function __destruct() {
mysqli_close($this->dbLink);
- }
+ }
- function getUserData($username)
- {
+ function getUserData($username) {
$result = mysqli_query($this->dbLink, 'SELECT id, email, realname FROM users WHERE name = \''.mysqli_escape_string($this->dbLink, $username).'\'');
$data = mysqli_fetch_assoc($result);
mysqli_free_result($result);
return $data;
- }
- /**
- * Check whether there exists a user account with the given name.
- * The name will be normalized to MediaWiki's requirements, so
- * you might need to munge it (for instance, for lowercase initial
- * letters).
- *
- * @param $username String: username.
- * @return bool
- * @public
- */
+ }
+
function userExists( $username ) {
$result = mysqli_query($this->dbLink, 'SELECT id FROM users WHERE name = \''.mysqli_escape_string($this->dbLink, $username).'\'');
$exists = mysqli_num_rows($result) > 0;
mysqli_free_result($result);
- return $exists;
+ return $exists;
}
- /**
- * Check if a username+password pair is a valid login.
- * The name will be normalized to MediaWiki's requirements, so
- * you might need to munge it (for instance, for lowercase initial
- * letters).
- *
- * @param $username String: username.
- * @param $password String: user password.
- * @return bool
- * @public
- */
function authenticate( $username, $password ) {
$result = mysqli_query($this->dbLink, 'SELECT id FROM users WHERE name = \''.mysqli_escape_string($this->dbLink, $username).'\' AND password = \''.mysqli_escape_string($this->dbLink, sha1($password)).'\' ');
$authenticated = mysqli_num_rows($result) > 0;
mysqli_free_result($result);
- return $authenticated;
+ return $authenticated;
}
- /**
- * Modify options in the login template.
- *
- * @param $template UserLoginTemplate object.
- * @public
- */
function modifyUITemplate( &$template ) {
- # Override this!
$template->set( 'usedomain', false );
$template->set('link', 'Um Dich hier anzumelden, nutze Deine Konto-Daten aus dem <a href="http://www.laber-land.de/?page=Forums;id=20">archlinux.de-Forum</a>.');
}
- /**
- * Set the domain this plugin is supposed to use when authenticating.
- *
- * @param $domain String: authentication domain.
- * @public
- */
function setDomain( $domain ) {
$this->domain = $domain;
}
- /**
- * Check to see if the specific domain is a valid domain.
- *
- * @param $domain String: authentication domain.
- * @return bool
- * @public
- */
function validDomain( $domain ) {
- # Override this!
return true;
}
- /**
- * When a user logs in, optionally fill in preferences and such.
- * For instance, you might pull the email address or real name from the
- * external user database.
- *
- * The User object is passed by reference so it can be modified; don't
- * forget the & on your function declaration.
- *
- * @param User $user
- * @public
- */
function updateUser( &$user ) {
return $this->initUser($user);
}
-
- /**
- * Return true if the wiki should create a new local account automatically
- * when asked to login a user who doesn't exist locally but does in the
- * external auth database.
- *
- * If you don't automatically create accounts, you must still create
- * accounts in some way. It's not possible to authenticate without
- * a local account.
- *
- * This is just a question, and shouldn't perform any actions.
- *
- * @return bool
- * @public
- */
function autoCreate() {
return true;
}
- /**
- * Can users change their passwords?
- *
- * @return bool
- */
function allowPasswordChange() {
- return true;
+ return false;
}
- /**
- * Set the given password in the authentication database.
- * As a special case, the password may be set to null to request
- * locking the password to an unusable value, with the expectation
- * that it will be set later through a mail reset or other method.
- *
- * Return true if successful.
- *
- * @param $user User object.
- * @param $password String: password.
- * @return bool
- * @public
- */
function setPassword( $user, $password ) {
- return true;
+ return false;
}
- /**
- * Update user information in the external authentication database.
- * Return true if successful.
- *
- * @param $user User object.
- * @return bool
- * @public
- */
function updateExternalDB( $user ) {
+ // this way userdata is allways overwritten by external db
return $this->initUser($user);
}
- /**
- * Check to see if external accounts can be created.
- * Return true if external accounts can be created.
- * @return bool
- * @public
- */
function canCreateAccounts() {
return false;
}
- /**
- * Add a user to the external authentication database.
- * Return true if successful.
- *
- * @param User $user
- * @param string $password
- * @return bool
- * @public
- */
- function addUser( $user, $password ) {
+ function addUser( $user, $password, $email = '', $realname = '' ) {
return false;
}
-
- /**
- * Return true to prevent logins that don't authenticate here from being
- * checked against the local database's password fields.
- *
- * This is just a question, and shouldn't perform any actions.
- *
- * @return bool
- * @public
- */
function strict() {
return true;
}
- /**
- * When creating a user account, optionally fill in preferences and such.
- * For instance, you might pull the email address or real name from the
- * external user database.
- *
- * The User object is passed by reference so it can be modified; don't
- * forget the & on your function declaration.
- *
- * @param $user User object.
- * @public
- */
function initUser( &$user ) {
$data = $this->getUserData($user->getName());
$user->setEmail($data['email']);
@@ -267,10 +107,6 @@ class LLAuthPlugin extends AuthPlugin {
return true;
}
- /**
- * If you want to munge the case of an account name before the final
- * check, now is your chance.
- */
function getCanonicalName( $username ) {
return $username;
}
diff --git a/extensions/README b/extensions/README
index cc931160..85f4943b 100644
--- a/extensions/README
+++ b/extensions/README
@@ -1,3 +1,14 @@
-Some extensions (such as the hieroglyphic module WikiHiero) are
-distributed separately. Drop them into this extensions directory
-and enable as per the extension's directions.
+Extensions (such as the hieroglyphic module WikiHiero) are distributed
+separately. Drop them into this extensions directory and enable as
+per the extension's directions.
+
+If you are a developer, you want to fetch the extension tree in another
+directory and make a symbolic link:
+
+ mediawiki/extensions$ ln -s ../../extensions-trunk/FooBarExt
+
+The extensions are available through svn at:
+ http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/
+
+You can find documentation and additional extensions on MediaWiki website:
+ http://www.mediawiki.org/wiki/Category:Extensions
diff --git a/img_auth.php b/img_auth.php
index e0a6459f..11684b37 100644
--- a/img_auth.php
+++ b/img_auth.php
@@ -7,6 +7,7 @@
* 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.
*/
+define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
require_once( './includes/WebStart.php' );
wfProfileIn( 'img_auth.php' );
require_once( './includes/StreamFile.php' );
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index 39ec19f8..ca129029 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -1,7 +1,8 @@
<?php
-if( !defined( 'MEDIAWIKI' ) )
- die( 1 );
+if( !defined( 'MEDIAWIKI' ) ) {
+ die( 1 );
+}
if ( ! $wgUseAjax ) {
die( 1 );
@@ -9,12 +10,16 @@ if ( ! $wgUseAjax ) {
require_once( 'AjaxFunctions.php' );
+/**
+ * Object-Oriented Ajax functions.
+ * @addtogroup Ajax
+ */
class AjaxDispatcher {
var $mode;
var $func_name;
var $args;
- function AjaxDispatcher() {
+ function __construct() {
wfProfileIn( __METHOD__ );
$this->mode = "";
@@ -28,14 +33,14 @@ class AjaxDispatcher {
}
if ($this->mode == "get") {
- $this->func_name = $_GET["rs"];
+ $this->func_name = isset( $_GET["rs"] ) ? $_GET["rs"] : '';
if (! empty($_GET["rsargs"])) {
$this->args = $_GET["rsargs"];
} else {
$this->args = array();
}
} else {
- $this->func_name = $_POST["rs"];
+ $this->func_name = isset( $_POST["rs"] ) ? $_POST["rs"] : '';
if (! empty($_POST["rsargs"])) {
$this->args = $_POST["rsargs"];
} else {
@@ -47,7 +52,7 @@ class AjaxDispatcher {
function performAction() {
global $wgAjaxExportList, $wgOut;
-
+
if ( empty( $this->mode ) ) {
return;
}
@@ -59,7 +64,7 @@ class AjaxDispatcher {
} else {
try {
$result = call_user_func_array($this->func_name, $this->args);
-
+
if ( $result === false || $result === NULL ) {
wfHttpError( 500, 'Internal Error',
"{$this->func_name} returned no data" );
@@ -68,7 +73,7 @@ class AjaxDispatcher {
if ( is_string( $result ) ) {
$result= new AjaxResponse( $result );
}
-
+
$result->sendHeaders();
$result->printText();
}
@@ -82,7 +87,7 @@ class AjaxDispatcher {
}
}
}
-
+
wfProfileOut( __METHOD__ );
$wgOut = null;
}
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index eee2a1a4..86f853db 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -1,7 +1,13 @@
<?php
-if( !defined( 'MEDIAWIKI' ) )
- die( 1 );
+/**
+ * @package MediaWiki
+ * @addtogroup Ajax
+ */
+
+if( !defined( 'MEDIAWIKI' ) ) {
+ die( 1 );
+}
/**
* Function converts an Javascript escaped string back into a string with
@@ -13,40 +19,39 @@ if( !defined( 'MEDIAWIKI' ) )
* @return string
*/
function js_unescape($source, $iconv_to = 'UTF-8') {
- $decodedStr = '';
- $pos = 0;
- $len = strlen ($source);
- while ($pos < $len) {
- $charAt = substr ($source, $pos, 1);
- if ($charAt == '%') {
- $pos++;
- $charAt = substr ($source, $pos, 1);
- if ($charAt == 'u') {
- // we got a unicode character
- $pos++;
- $unicodeHexVal = substr ($source, $pos, 4);
- $unicode = hexdec ($unicodeHexVal);
- $decodedStr .= code2utf($unicode);
- $pos += 4;
- }
- else {
- // we have an escaped ascii character
- $hexVal = substr ($source, $pos, 2);
- $decodedStr .= chr (hexdec ($hexVal));
- $pos += 2;
- }
- }
- else {
- $decodedStr .= $charAt;
- $pos++;
- }
- }
-
- if ($iconv_to != "UTF-8") {
- $decodedStr = iconv("UTF-8", $iconv_to, $decodedStr);
- }
-
- return $decodedStr;
+ $decodedStr = '';
+ $pos = 0;
+ $len = strlen ($source);
+
+ while ($pos < $len) {
+ $charAt = substr ($source, $pos, 1);
+ if ($charAt == '%') {
+ $pos++;
+ $charAt = substr ($source, $pos, 1);
+ if ($charAt == 'u') {
+ // we got a unicode character
+ $pos++;
+ $unicodeHexVal = substr ($source, $pos, 4);
+ $unicode = hexdec ($unicodeHexVal);
+ $decodedStr .= code2utf($unicode);
+ $pos += 4;
+ } else {
+ // we have an escaped ascii character
+ $hexVal = substr ($source, $pos, 2);
+ $decodedStr .= chr (hexdec ($hexVal));
+ $pos += 2;
+ }
+ } else {
+ $decodedStr .= $charAt;
+ $pos++;
+ }
+ }
+
+ if ($iconv_to != "UTF-8") {
+ $decodedStr = iconv("UTF-8", $iconv_to, $decodedStr);
+ }
+
+ return $decodedStr;
}
/**
@@ -71,7 +76,7 @@ function code2utf($num){
function wfSajaxSearch( $term ) {
global $wgContLang, $wgOut;
$limit = 16;
-
+
$l = new Linker;
$term = str_replace( ' ', '_', $wgContLang->ucfirst(
@@ -81,7 +86,7 @@ function wfSajaxSearch( $term ) {
if ( strlen( str_replace( '_', '', $term ) )<3 )
return;
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$res = $db->select( 'page', 'page_title',
array( 'page_namespace' => 0,
"page_title LIKE '". $db->strencode( $term) ."%'" ),
@@ -108,8 +113,8 @@ function wfSajaxSearch( $term ) {
$subtitlemsg = ( Title::newFromText($term) ? 'searchsubtitle' : 'searchsubtitleinvalid' );
$subtitle = $wgOut->parse( wfMsg( $subtitlemsg, wfEscapeWikiText($term) ) ); #FIXME: parser is missing mTitle !
- $term = htmlspecialchars( $term );
- $html = '<div style="float:right; border:solid 1px black;background:gainsboro;padding:2px;"><a onclick="Searching_Hide_Results();">'
+ $term = urlencode( $term );
+ $html = '<div style="float:right; border:solid 1px black;background:gainsboro;padding:2px;"><a onclick="Searching_Hide_Results();">'
. wfMsg( 'hideresults' ) . '</a></div>'
. '<h1 class="firstHeading">'.wfMsg('search')
. '</h1><div id="contentSub">'. $subtitle . '</div><ul><li>'
@@ -121,11 +126,11 @@ function wfSajaxSearch( $term ) {
"search=$term&go=Go" )
. "</li></ul><h2>" . wfMsg( 'articletitles', $term ) . "</h2>"
. '<ul>' .$r .'</ul>'.$more;
-
+
$response = new AjaxResponse( $html );
-
+
$response->setCacheDuration( 30*60 );
-
+
return $response;
}
@@ -152,14 +157,14 @@ function wfAjaxWatch($pageID = "", $watch = "") {
if($watch) {
if(!$watching) {
- $dbw =& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
$dbw->begin();
$article->doWatch();
$dbw->commit();
}
} else {
if($watching) {
- $dbw =& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
$dbw->begin();
$article->doUnwatch();
$dbw->commit();
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index a59c73bb..cb4af1b5 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -1,28 +1,32 @@
<?php
+if( !defined( 'MEDIAWIKI' ) ) {
+ die( 1 );
+}
-if( !defined( 'MEDIAWIKI' ) )
- die( 1 );
-
+/**
+ * @todo document
+ * @addtogroup Ajax
+ */
class AjaxResponse {
var $mCacheDuration;
var $mVary;
-
+
var $mDisabled;
var $mText;
var $mResponseCode;
var $mLastModified;
var $mContentType;
- function AjaxResponse( $text = NULL ) {
+ function __construct( $text = NULL ) {
$this->mCacheDuration = NULL;
$this->mVary = NULL;
-
+
$this->mDisabled = false;
$this->mText = '';
$this->mResponseCode = '200 OK';
$this->mLastModified = false;
$this->mContentType= 'text/html; charset=utf-8';
-
+
if ( $text ) {
$this->addText( $text );
}
@@ -39,15 +43,15 @@ class AjaxResponse {
function setResponseCode( $code ) {
$this->mResponseCode = $code;
}
-
+
function setContentType( $type ) {
$this->mContentType = $type;
}
-
+
function disable() {
$this->mDisabled = true;
}
-
+
function addText( $text ) {
if ( ! $this->mDisabled && $text ) {
$this->mText .= $text;
@@ -59,62 +63,62 @@ class AjaxResponse {
print $this->mText;
}
}
-
+
function sendHeaders() {
global $wgUseSquid, $wgUseESI;
-
+
if ( $this->mResponseCode ) {
$n = preg_replace( '/^ *(\d+)/', '\1', $this->mResponseCode );
header( "Status: " . $this->mResponseCode, true, (int)$n );
}
-
+
header ("Content-Type: " . $this->mContentType );
-
+
if ( $this->mLastModified ) {
header ("Last-Modified: " . $this->mLastModified );
}
else {
header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
}
-
+
if ( $this->mCacheDuration ) {
-
+
# If squid caches are configured, tell them to cache the response,
# and tell the client to always check with the squid. Otherwise,
# tell the client to use a cached copy, without a way to purge it.
-
+
if( $wgUseSquid ) {
-
+
# Expect explicite purge of the proxy cache, but require end user agents
# to revalidate against the proxy on each visit.
# Surrogate-Control controls our Squid, Cache-Control downstream caches
-
+
if ( $wgUseESI ) {
header( 'Surrogate-Control: max-age='.$this->mCacheDuration.', content="ESI/1.0"');
header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
} else {
header( 'Cache-Control: s-maxage='.$this->mCacheDuration.', must-revalidate, max-age=0' );
}
-
+
} else {
-
+
# Let the client do the caching. Cache is not purged.
header ("Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT");
header ("Cache-Control: s-max-age={$this->mCacheDuration},public,max-age={$this->mCacheDuration}");
}
-
+
} else {
# always expired, always modified
header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header ("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header ("Pragma: no-cache"); // HTTP/1.0
}
-
+
if ( $this->mVary ) {
header ( "Vary: " . $this->mVary );
}
}
-
+
/**
* checkLastModified tells the client to use the client-cached response if
* possible. If sucessful, the AjaxResponse is disabled so that
@@ -154,9 +158,9 @@ class AjaxResponse {
$this->setResponseCode( "304 Not Modified" );
$this->disable();
$this->mLastModified = $lastmod;
-
+
wfDebug( "$fname: CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false );
-
+
return true;
} else {
wfDebug( "$fname: READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false );
@@ -167,11 +171,11 @@ class AjaxResponse {
$this->mLastModified = $lastmod;
}
}
-
+
function loadFromMemcached( $mckey, $touched ) {
global $wgMemc;
if ( !$touched ) return false;
-
+
$mcvalue = $wgMemc->get( $mckey );
if ( $mcvalue ) {
# Check to see if the value has been invalidated
@@ -183,20 +187,20 @@ class AjaxResponse {
wfDebug( "$mckey has expired\n" );
}
}
-
+
return false;
}
-
+
function storeInMemcached( $mckey, $expiry = 86400 ) {
global $wgMemc;
-
- $wgMemc->set( $mckey,
+
+ $wgMemc->set( $mckey,
array(
'timestamp' => wfTimestampNow(),
'value' => $this->mText
), $expiry
);
-
+
return true;
}
}
diff --git a/includes/Article.php b/includes/Article.php
index 6b4f5270..0130ceba 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -1,7 +1,6 @@
<?php
/**
* File for articles
- * @package MediaWiki
*/
/**
@@ -11,7 +10,6 @@
* Note: edit user interface and cache support functions have been
* moved to separate EditPage and HTMLFileCache classes.
*
- * @package MediaWiki
*/
class Article {
/**@{{
@@ -43,7 +41,7 @@ class Article {
* @param $title Reference to a Title object.
* @param $oldId Integer revision ID, null to fetch from request, zero for current
*/
- function Article( &$title, $oldId = null ) {
+ function __construct( &$title, $oldId = null ) {
$this->mTitle =& $title;
$this->mOldId = $oldId;
$this->clear();
@@ -57,14 +55,14 @@ class Article {
function setRedirectedFrom( $from ) {
$this->mRedirectedFrom = $from;
}
-
+
/**
* @return mixed false, Title of in-wiki target, or string with URL
*/
function followRedirect() {
$text = $this->getContent();
$rt = Title::newFromRedirect( $text );
-
+
# process if title object is valid and not special:userlogout
if( $rt ) {
if( $rt->getInterwiki() != '' ) {
@@ -73,7 +71,7 @@ class Article {
//
// This can be hard to reverse and may produce loops,
// so they may be disabled in the site configuration.
-
+
$source = $this->mTitle->getFullURL( 'redirect=no' );
return $rt->getFullURL( 'rdfrom=' . urlencode( $source ) );
}
@@ -84,7 +82,7 @@ class Article {
// the rest of the page we're on.
//
// This can be hard to reverse, so they may be disabled.
-
+
if( $rt->isSpecial( 'Userlogout' ) ) {
// rolleyes
} else {
@@ -94,7 +92,7 @@ class Article {
return $rt;
}
}
-
+
// No or invalid redirect
return false;
}
@@ -247,7 +245,7 @@ class Article {
* @param array $conditions
* @private
*/
- function pageData( &$dbr, $conditions ) {
+ function pageData( $dbr, $conditions ) {
$fields = array(
'page_id',
'page_namespace',
@@ -273,7 +271,7 @@ class Article {
* @param Database $dbr
* @param Title $title
*/
- function pageDataFromTitle( &$dbr, $title ) {
+ function pageDataFromTitle( $dbr, $title ) {
return $this->pageData( $dbr, array(
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey() ) );
@@ -283,7 +281,7 @@ class Article {
* @param Database $dbr
* @param int $id
*/
- function pageDataFromId( &$dbr, $id ) {
+ function pageDataFromId( $dbr, $id ) {
return $this->pageData( $dbr, array( 'page_id' => $id ) );
}
@@ -296,17 +294,18 @@ class Article {
*/
function loadPageData( $data = 'fromdb' ) {
if ( $data === 'fromdb' ) {
- $dbr =& $this->getDB();
+ $dbr = $this->getDB();
$data = $this->pageDataFromId( $dbr, $this->getId() );
}
-
+
$lc =& LinkCache::singleton();
if ( $data ) {
$lc->addGoodLinkObj( $data->page_id, $this->mTitle );
$this->mTitle->mArticleID = $data->page_id;
+
+ # Old-fashioned restrictions.
$this->mTitle->loadRestrictions( $data->page_restrictions );
- $this->mTitle->mRestrictionsLoaded = true;
$this->mCounter = $data->page_counter;
$this->mTouched = wfTimestamp( TS_MW, $data->page_touched );
@@ -333,7 +332,7 @@ class Article {
return $this->mContent;
}
- $dbr =& $this->getDB();
+ $dbr = $this->getDB();
# Pre-fill content with error message so that if something
# fails we'll have something telling us what we intended.
@@ -405,9 +404,8 @@ class Article {
*
* @return Database
*/
- function &getDB() {
- $ret =& wfGetDB( DB_MASTER );
- return $ret;
+ function getDB() {
+ return wfGetDB( DB_MASTER );
}
/**
@@ -455,7 +453,7 @@ class Article {
if ( $id == 0 ) {
$this->mCounter = 0;
} else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$this->mCounter = $dbr->selectField( 'page', 'page_counter', array( 'page_id' => $id ),
'Article::getCount', $this->getSelectOptions() );
}
@@ -471,12 +469,12 @@ class Article {
* @return bool
*/
function isCountable( $text ) {
- global $wgUseCommaCount, $wgContentNamespaces;
+ global $wgUseCommaCount;
$token = $wgUseCommaCount ? ',' : '[[';
return
- array_search( $this->mTitle->getNamespace(), $wgContentNamespaces ) !== false
- && ! $this->isRedirect( $text )
+ $this->mTitle->isContentPage()
+ && !$this->isRedirect( $text )
&& in_string( $token, $text );
}
@@ -573,7 +571,7 @@ class Article {
# XXX: this is expensive; cache this info somewhere.
$contribs = array();
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$revTable = $dbr->tableName( 'revision' );
$userTable = $dbr->tableName( 'user' );
$user = $this->getUser();
@@ -613,7 +611,7 @@ class Article {
$parserCache =& ParserCache::singleton();
$ns = $this->mTitle->getNamespace(); # shortcut
-
+
# Get variables from query string
$oldid = $this->getOldID();
@@ -627,16 +625,21 @@ class Article {
$diff = $wgRequest->getVal( 'diff' );
$rcid = $wgRequest->getVal( 'rcid' );
$rdfrom = $wgRequest->getVal( 'rdfrom' );
+ $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
$wgOut->setArticleFlag( true );
- if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
+
+ # Discourage indexing of printable versions, but encourage following
+ if( $wgOut->isPrintable() ) {
+ $policy = 'noindex,follow';
+ } elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
+ # Honour customised robot policies for this namespace
$policy = $wgNamespaceRobotPolicies[$ns];
} else {
- # The default policy. Dev note: make sure you change the documentation
- # in DefaultSettings.php before changing it.
+ # Default to encourage indexing and following links
$policy = 'index,follow';
}
- $wgOut->setRobotpolicy( $policy );
+ $wgOut->setRobotPolicy( $policy );
# If we got diff and oldid in the query, we want to see a
# diff page instead of the article.
@@ -647,8 +650,8 @@ class Article {
$de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid );
// DifferenceEngine directly fetched the revision:
$this->mRevIdFetched = $de->mNewid;
- $de->showDiffPage();
-
+ $de->showDiffPage( $diffOnly );
+
// Needed to get the page's current revision
$this->loadPageData();
if( $diff == 0 || $diff == $this->mLatest ) {
@@ -658,7 +661,7 @@ class Article {
wfProfileOut( __METHOD__ );
return;
}
-
+
if ( empty( $oldid ) && $this->checkTouched() ) {
$wgOut->setETag($parserCache->getETag($this, $wgUser));
@@ -713,11 +716,11 @@ class Article {
$wasRedirected = true;
}
}
-
+
$outputDone = false;
+ wfRunHooks( 'ArticleViewHeader', array( &$this ) );
if ( $pcache ) {
if ( $wgOut->tryParserCache( $this, $wgUser ) ) {
- wfRunHooks( 'ArticleViewHeader', array( &$this ) );
$outputDone = true;
}
}
@@ -764,17 +767,12 @@ class Article {
}
}
if( !$outputDone ) {
- /**
- * @fixme: this hook doesn't work most of the time, as it doesn't
- * trigger when the parser cache is used.
- */
- wfRunHooks( 'ArticleViewHeader', array( &$this ) ) ;
$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())
+ preg_match('/\\/[\\w]+\\.(?:css|js)$/', $this->mTitle->getDBkey())
) {
$wgOut->addWikiText( wfMsg('clearyourcache'));
$wgOut->addHTML( '<pre>'.htmlspecialchars($this->mContent)."\n</pre>" );
@@ -795,7 +793,7 @@ class Article {
$wgOut->addParserOutputNoText( $parseout );
} else if ( $pcache ) {
# Display content and save to parser cache
- $wgOut->addPrimaryWikiText( $text, $this );
+ $this->outputWikiText( $text );
} else {
# Display content, don't attempt to save to parser cache
# Don't show section-edit links on old revisions... this way lies madness.
@@ -803,11 +801,21 @@ class Article {
$oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
}
# Display content and don't save to parser cache
- $wgOut->addPrimaryWikiText( $text, $this, false );
+ # With timing hack -- TS 2006-07-26
+ $time = -wfTime();
+ $this->outputWikiText( $text, false );
+ $time += wfTime();
+
+ # Timing hack
+ if ( $time > 3 ) {
+ wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
+ $this->mTitle->getPrefixedDBkey()));
+ }
if( !$this->isCurrent() ) {
$wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
}
+
}
}
/* title may have been set from the cache */
@@ -827,8 +835,9 @@ class Article {
if ( $wgUseRCPatrol && !is_null( $rcid ) && $rcid != 0 && $wgUser->isAllowed( 'patrol' ) ) {
$wgOut->addHTML(
"<div class='patrollink'>" .
- wfMsg ( 'markaspatrolledlink',
- $sk->makeKnownLinkObj( $this->mTitle, wfMsg('markaspatrolledtext'), "action=markpatrolled&rcid=$rcid" )
+ wfMsgHtml( 'markaspatrolledlink',
+ $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml('markaspatrolledtext'),
+ "action=markpatrolled&rcid=$rcid" )
) .
'</div>'
);
@@ -845,7 +854,7 @@ class Article {
function addTrackbacks() {
global $wgOut, $wgUser;
- $dbr =& wfGetDB(DB_SLAVE);
+ $dbr = wfGetDB(DB_SLAVE);
$tbs = $dbr->select(
/* FROM */ 'trackbacks',
/* SELECT */ array('tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name'),
@@ -891,7 +900,7 @@ class Article {
return;
}
- $db =& wfGetDB(DB_MASTER);
+ $db = wfGetDB(DB_MASTER);
$db->delete('trackbacks', array('tb_id' => $wgRequest->getInt('tbid')));
$wgTitle->invalidateCache();
$wgOut->addWikiText(wfMsg('trackbackdeleteok'));
@@ -910,7 +919,7 @@ class Article {
function purge() {
global $wgUser, $wgRequest, $wgOut;
- if ( $wgUser->isLoggedIn() || $wgRequest->wasPosted() ) {
+ if ( $wgUser->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) {
if( wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
$this->doPurge();
}
@@ -928,7 +937,7 @@ class Article {
$wgOut->addHTML( $msg );
}
}
-
+
/**
* Perform the actions of a page purging
*/
@@ -957,11 +966,10 @@ class Article {
* Best if all done inside a transaction.
*
* @param Database $dbw
- * @param string $restrictions
* @return int The newly created page_id key
* @private
*/
- function insertOn( &$dbw, $restrictions = '' ) {
+ function insertOn( $dbw ) {
wfProfileIn( __METHOD__ );
$page_id = $dbw->nextSequenceValue( 'page_page_id_seq' );
@@ -970,7 +978,7 @@ class Article {
'page_namespace' => $this->mTitle->getNamespace(),
'page_title' => $this->mTitle->getDBkey(),
'page_counter' => 0,
- 'page_restrictions' => $restrictions,
+ 'page_restrictions' => '',
'page_is_redirect' => 0, # Will set this shortly...
'page_is_new' => 1,
'page_random' => wfRandom(),
@@ -996,7 +1004,7 @@ class Article {
* when different from the currently set value.
* Giving 0 indicates the new page flag should
* be set on.
- * @param bool $lastRevIsRedirect If given, will optimize adding and
+ * @param bool $lastRevIsRedirect If given, will optimize adding and
* removing rows in redirect table.
* @return bool true on success, false on failure
* @private
@@ -1006,7 +1014,7 @@ class Article {
$text = $revision->getText();
$rt = Title::newFromRedirect( $text );
-
+
$conditions = array( 'page_id' => $this->getId() );
if( !is_null( $lastRevision ) ) {
# An extra check against threads stepping on each other
@@ -1028,20 +1036,20 @@ class Article {
if ($result) {
// FIXME: Should the result from updateRedirectOn() be returned instead?
- $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
+ $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
}
-
+
wfProfileOut( __METHOD__ );
return $result;
}
/**
- * Add row to the redirect table if this is a redirect, remove otherwise.
+ * Add row to the redirect table if this is a redirect, remove otherwise.
*
* @param Database $dbw
* @param $redirectTitle a title object pointing to the redirect target,
- * or NULL if this is not a redirect
- * @param bool $lastRevIsRedirect If given, will optimize adding and
+ * or NULL if this is not a redirect
+ * @param bool $lastRevIsRedirect If given, will optimize adding and
* removing rows in redirect table.
* @return bool true on success, false on failure
* @private
@@ -1067,7 +1075,7 @@ class Article {
$dbw->replace( 'redirect', array( 'rd_from' ), $set, __METHOD__ );
} else {
- // This is not a redirect, remove row from redirect table
+ // This is not a redirect, remove row from redirect table
$where = array( 'rd_from' => $this->getId() );
$dbw->delete( 'redirect', $where, __METHOD__);
}
@@ -1075,7 +1083,7 @@ class Article {
wfProfileOut( __METHOD__ );
return ( $dbw->affectedRows() != 0 );
}
-
+
return true;
}
@@ -1119,14 +1127,14 @@ class Article {
*/
function replaceSection($section, $text, $summary = '', $edittime = NULL) {
wfProfileIn( __METHOD__ );
-
+
if( $section == '' ) {
// Whole-page edit; let the text through unmolested.
} else {
if( is_null( $edittime ) ) {
$rev = Revision::newFromTitle( $this->mTitle );
} else {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
}
if( is_null( $rev ) ) {
@@ -1166,10 +1174,10 @@ class Article {
if ( $comment && $summary != "" ) {
$text = "== {$summary} ==\n\n".$text;
}
-
+
$this->doEdit( $text, $summary, $flags );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
if ($watchthis) {
if (!$this->mTitle->userIsWatching()) {
$dbw->begin();
@@ -1196,7 +1204,7 @@ class Article {
$good = $this->doEdit( $text, $summary, $flags );
if ( $good ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
if ($watchthis) {
if (!$this->mTitle->userIsWatching()) {
$dbw->begin();
@@ -1219,7 +1227,7 @@ class Article {
/**
* Article::doEdit()
*
- * Change an existing article or create a new article. Updates RC and all necessary caches,
+ * Change an existing article or create a new article. Updates RC and all necessary caches,
* optionally via the deferred update array.
*
* $wgUser must be set before calling this function.
@@ -1241,9 +1249,9 @@ class Article {
* Defer some of the updates until the end of index.php
* EDIT_AUTOSUMMARY
* Fill in blank summaries with generated text where possible
- *
- * If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
- * If EDIT_UPDATE is specified and the article doesn't exist, the function will return false. If
+ *
+ * If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
+ * If EDIT_UPDATE is specified and the article doesn't exist, the function will return false. If
* EDIT_NEW is specified and the article does exist, a duplicate key error will cause an exception
* to be thrown from the Database. These two conditions are also possible with auto-detection due
* to MediaWiki's performance-optimised locking strategy.
@@ -1267,7 +1275,7 @@ class Article {
if( !wfRunHooks( 'ArticleSave', array( &$this, &$wgUser, &$text,
&$summary, $flags & EDIT_MINOR,
- null, null, &$flags ) ) )
+ null, null, &$flags ) ) )
{
wfDebug( __METHOD__ . ": ArticleSave hook aborted save!\n" );
wfProfileOut( __METHOD__ );
@@ -1288,9 +1296,9 @@ class Article {
$text = $this->preSaveTransform( $text );
$newsize = strlen( $text );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$now = wfTimestampNow();
-
+
if ( $flags & EDIT_UPDATE ) {
# Update article, but only if changed.
@@ -1316,7 +1324,7 @@ class Article {
wfProfileOut( __METHOD__ );
return false;
}
-
+
$revision = new Revision( array(
'page' => $this->getId(),
'comment' => $summary,
@@ -1340,10 +1348,11 @@ class Article {
$rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary,
$lastRevision, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
$revisionId );
-
+
# Mark as patrolled if the user can do so
- if( $wgUser->isAllowed( 'autopatrol' ) ) {
+ if( $GLOBALS['wgUseRCPatrol'] && $wgUser->isAllowed( 'autopatrol' ) ) {
RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid, true );
}
}
$wgUser->incEditCount();
@@ -1362,19 +1371,19 @@ class Article {
}
if ( $good ) {
- # Invalidate cache of this article and all pages using this article
+ # Invalidate cache of this article and all pages using this article
# as a template. Partly deferred.
Article::onArticleEdit( $this->mTitle );
-
+
# Update links tables, site stats, etc.
$changed = ( strcmp( $oldtext, $text ) != 0 );
$this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
}
} else {
# Create new article
-
+
# Set statistics members
- # We work out if it's countable after PST to avoid counter drift
+ # We work out if it's countable after PST to avoid counter drift
# when articles are created with {{subst:}}
$this->mGoodAdjustment = (int)$this->isCountable( $text );
$this->mTotalAdjustment = 1;
@@ -1403,8 +1412,9 @@ class Article {
$rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot,
'', strlen( $text ), $revisionId );
# Mark as patrolled if the user can
- if( $wgUser->isAllowed( 'autopatrol' ) ) {
+ if( $GLOBALS['wgUseRCPatrol'] && $wgUser->isAllowed( 'autopatrol' ) ) {
RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid, true );
}
}
$wgUser->incEditCount();
@@ -1429,7 +1439,7 @@ class Article {
array( &$this, &$wgUser, $text,
$summary, $flags & EDIT_MINOR,
null, null, &$flags ) );
-
+
wfProfileOut( __METHOD__ );
return $good;
}
@@ -1457,7 +1467,7 @@ class Article {
}
$wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
}
-
+
/**
* Mark this particular edit as patrolled
*/
@@ -1470,25 +1480,25 @@ class Article {
$wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
-
+
# Check permissions
if( !$wgUser->isAllowed( 'patrol' ) ) {
$wgOut->permissionRequired( 'patrol' );
return;
}
-
+
# If we haven't been given an rc_id value, we can't do anything
$rcid = $wgRequest->getVal( 'rcid' );
if( !$rcid ) {
$wgOut->errorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
return;
}
-
+
# Handle the 'MarkPatrolled' hook
if( !wfRunHooks( 'MarkPatrolled', array( $rcid, &$wgUser, false ) ) ) {
return;
}
-
+
$return = SpecialPage::getTitleFor( 'Recentchanges' );
# If it's left up to us, check that the user is allowed to patrol this edit
# If the user has the "autopatrol" right, then we'll assume there are no
@@ -1507,11 +1517,12 @@ class Article {
return;
}
}
-
+
# Mark the edit as patrolled
RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid );
wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
-
+
# Inform the user
$wgOut->setPageTitle( wfMsg( 'markedaspatrolled' ) );
$wgOut->addWikiText( wfMsgNoTrans( 'markedaspatrolledtext' ) );
@@ -1534,7 +1545,7 @@ class Article {
$wgOut->readOnlyPage();
return;
}
-
+
if( $this->doWatch() ) {
$wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -1546,7 +1557,7 @@ class Article {
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
}
-
+
/**
* Add this page to $wgUser's watchlist
* @return bool true on successful watch operation
@@ -1556,13 +1567,13 @@ class Article {
if( $wgUser->isAnon() ) {
return false;
}
-
+
if (wfRunHooks('WatchArticle', array(&$wgUser, &$this))) {
$wgUser->addWatch( $this->mTitle );
return wfRunHooks('WatchArticleComplete', array(&$wgUser, &$this));
}
-
+
return false;
}
@@ -1581,7 +1592,7 @@ class Article {
$wgOut->readOnlyPage();
return;
}
-
+
if( $this->doUnwatch() ) {
$wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -1593,7 +1604,7 @@ class Article {
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
}
-
+
/**
* Stop watching a page
* @return bool true on successful unwatch
@@ -1609,7 +1620,7 @@ class Article {
return wfRunHooks('UnwatchArticleComplete', array(&$wgUser, &$this));
}
-
+
return false;
}
@@ -1618,7 +1629,7 @@ class Article {
*/
function protect() {
$form = new ProtectionForm( $this );
- $form->show();
+ $form->execute();
}
/**
@@ -1635,14 +1646,21 @@ class Article {
* @param string $reason
* @return bool true on success
*/
- function updateRestrictions( $limit = array(), $reason = '' ) {
+ function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = null ) {
global $wgUser, $wgRestrictionTypes, $wgContLang;
-
+
$id = $this->mTitle->getArticleID();
if( !$wgUser->isAllowed( 'protect' ) || wfReadOnly() || $id == 0 ) {
return false;
}
+ if (!$cascade) {
+ $cascade = false;
+ }
+
+ // Take this opportunity to purge out expired restrictions
+ Title::purgeExpiredRestrictions();
+
# FIXME: Same limitations as described in ProtectionForm.php (line 37);
# we expect a single selection, but the schema allows otherwise.
$current = array();
@@ -1651,48 +1669,89 @@ class Article {
$current = Article::flattenRestrictions( $current );
$updated = Article::flattenRestrictions( $limit );
-
+
$changed = ( $current != $updated );
+ $changed = $changed || ($this->mTitle->areRestrictionsCascading() != $cascade);
+ $changed = $changed || ($this->mTitle->mRestrictionsExpiry != $expiry);
$protect = ( $updated != '' );
-
+
# If nothing's changed, do nothing
if( $changed ) {
+ global $wgGroupPermissions;
if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
- $dbw =& wfGetDB( DB_MASTER );
-
+ $dbw = wfGetDB( DB_MASTER );
+
+ $encodedExpiry = Block::encodeExpiry($expiry, $dbw );
+
+ $expiry_description = '';
+ if ( $encodedExpiry != 'infinity' ) {
+ $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ) ).')';
+ }
+
# Prepare a null revision to be added to the history
$comment = $wgContLang->ucfirst( wfMsgForContent( $protect ? 'protectedarticle' : 'unprotectedarticle', $this->mTitle->getPrefixedText() ) );
+
+ foreach( $limit as $action => $restrictions ) {
+ # Check if the group level required to edit also can protect pages
+ # Otherwise, people who cannot normally protect can "protect" pages via transclusion
+ $cascade = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) && $wgGroupPermissions[$restrictions]['protect'] );
+ }
+
+ $cascade_description = '';
+ if ($cascade) {
+ $cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
+ }
+
if( $reason )
$comment .= ": $reason";
if( $protect )
$comment .= " [$updated]";
+ if ( $expiry_description && $protect )
+ $comment .= "$expiry_description";
+ if ( $cascade )
+ $comment .= "$cascade_description";
+
$nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
$nullRevId = $nullRevision->insertOn( $dbw );
-
+
+ # Update restrictions table
+ foreach( $limit as $action => $restrictions ) {
+ if ($restrictions != '' ) {
+ $dbw->replace( 'page_restrictions', array(array('pr_page', 'pr_type')),
+ array( 'pr_page' => $id, 'pr_type' => $action
+ , 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0
+ , 'pr_expiry' => $encodedExpiry ), __METHOD__ );
+ } else {
+ $dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
+ 'pr_type' => $action ), __METHOD__ );
+ }
+ }
+
# Update page record
$dbw->update( 'page',
array( /* SET */
'page_touched' => $dbw->timestamp(),
- 'page_restrictions' => $updated,
+ 'page_restrictions' => '',
'page_latest' => $nullRevId
), array( /* WHERE */
'page_id' => $id
), 'Article::protect'
);
wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, $limit, $reason ) );
-
+
# Update the protection log
$log = new LogPage( 'protect' );
+
if( $protect ) {
- $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]" ) );
+ $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
-
+
} # End hook
} # End "changed" check
-
+
return true;
}
@@ -1745,9 +1804,9 @@ class Article {
}
$wgOut->setPagetitle( wfMsg( 'confirmdelete' ) );
-
+
# Better double-check that it hasn't been deleted yet!
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$conds = $this->mTitle->pageCond();
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
if ( $latest === false ) {
@@ -1769,7 +1828,7 @@ class Article {
# and insert a warning if it does
$maxRevisions = 20;
$authors = $this->getLastNAuthors( $maxRevisions, $latest );
-
+
if( count( $authors ) > 1 && !$confirm ) {
$skin=$wgUser->getSkin();
$wgOut->addHTML( '<strong>' . wfMsg( 'historywarning' ) . ' ' . $skin->historyLink() . '</strong>' );
@@ -1813,7 +1872,7 @@ class Article {
$reason = wfMsgForContent( 'exblank' );
}
- if( $length < 500 && $reason === '' ) {
+ if( $reason === '' ) {
# comment field=255, let's grep the first 150 to have some user
# space left
global $wgContLang;
@@ -1849,7 +1908,7 @@ class Article {
// First try the slave
// If that doesn't have the latest revision, try the master
$continue = 2;
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
do {
$res = $db->select( array( 'page', 'revision' ),
array( 'rev_id', 'rev_user_text' ),
@@ -1868,7 +1927,7 @@ class Article {
}
$row = $db->fetchObject( $res );
if ( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
$continue--;
} else {
$continue = 0;
@@ -1882,7 +1941,7 @@ class Article {
wfProfileOut( __METHOD__ );
return $authors;
}
-
+
/**
* Output deletion confirmation dialog
*/
@@ -1929,6 +1988,23 @@ class Article {
</form>\n" );
$wgOut->returnToMain( false );
+
+ $this->showLogExtract( $wgOut );
+ }
+
+
+ /**
+ * Fetch deletion log
+ */
+ function showLogExtract( &$out ) {
+ # Show relevant lines from the deletion log:
+ $out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ $logViewer = new LogViewer(
+ new LogReader(
+ new FauxRequest(
+ array( 'page' => $this->mTitle->getPrefixedText(),
+ 'type' => 'delete' ) ) ) );
+ $logViewer->showList( $out );
}
@@ -1969,7 +2045,7 @@ class Article {
wfDebug( __METHOD__."\n" );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$ns = $this->mTitle->getNamespace();
$t = $this->mTitle->getDBkey();
$id = $this->mTitle->getArticleID();
@@ -2004,12 +2080,16 @@ class Article {
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
+ 'ar_len' => 'rev_len'
), array(
'page_id' => $id,
'page_id = rev_page'
), __METHOD__
);
+ # Delete restrictions for it
+ $dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
+
# Now that it's safely backed up, delete it
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
@@ -2078,7 +2158,7 @@ class Article {
$wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
return;
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
# Enhanced rollback, marks edits rc_bot=1
$bot = $wgRequest->getBool( 'bot' );
@@ -2103,7 +2183,7 @@ class Article {
if( $current->getComment() != '') {
$wgOut->addHTML(
wfMsg( 'editcomment',
- htmlspecialchars( $current->getComment() ) ) );
+ $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
}
return;
}
@@ -2189,7 +2269,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.
- *
+ *
* @private
* @param $text New text of the article
* @param $summary Edit summary
@@ -2222,7 +2302,7 @@ class Article {
# Periodically flush old entries from the recentchanges table.
global $wgRCMaxAge;
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
$recentchanges = $dbw->tableName( 'recentchanges' );
$sql = "DELETE FROM $recentchanges WHERE rc_timestamp < '{$cutoff}'";
@@ -2269,13 +2349,13 @@ class Article {
wfProfileOut( __METHOD__ );
}
-
+
/**
* Perform article updates on a special page creation.
*
* @param Revision $rev
*
- * @fixme This is a shitty interface function. Kill it and replace the
+ * @todo This is a shitty interface function. Kill it and replace the
* other shitty functions like editUpdates and such so it's not needed
* anymore.
*/
@@ -2299,8 +2379,8 @@ class Article {
global $wgLang, $wgOut, $wgUser;
if ( !wfRunHooks( 'DisplayOldSubtitle', array(&$this, &$oldid) ) ) {
- return;
- }
+ return;
+ }
$revision = Revision::newFromId( $oldid );
@@ -2326,10 +2406,10 @@ class Article {
$nextdiff = $current
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
-
+
$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" .
"\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 );
@@ -2381,25 +2461,40 @@ class Article {
* @return bool
*/
function isFileCacheable() {
- global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest;
+ global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest, $wgLang, $wgContLang;
$action = $wgRequest->getVal( 'action' );
$oldid = $wgRequest->getVal( 'oldid' );
$diff = $wgRequest->getVal( 'diff' );
$redirect = $wgRequest->getVal( 'redirect' );
$printable = $wgRequest->getVal( 'printable' );
+ $page = $wgRequest->getVal( 'page' );
+
+ //check for non-standard user language; this covers uselang,
+ //and extensions for auto-detecting user language.
+ $ulang = $wgLang->getCode();
+ $clang = $wgContLang->getCode();
- return $wgUseFileCache
- and (!$wgShowIPinHeader)
- and ($this->getID() != 0)
- and ($wgUser->isAnon())
- and (!$wgUser->getNewtalk())
- and ($this->mTitle->getNamespace() != NS_SPECIAL )
- and (empty( $action ) || $action == 'view')
- and (!isset($oldid))
- and (!isset($diff))
- and (!isset($redirect))
- and (!isset($printable))
- and (!$this->mRedirectedFrom);
+ $cacheable = $wgUseFileCache
+ && (!$wgShowIPinHeader)
+ && ($this->getID() != 0)
+ && ($wgUser->isAnon())
+ && (!$wgUser->getNewtalk())
+ && ($this->mTitle->getNamespace() != NS_SPECIAL )
+ && (empty( $action ) || $action == 'view')
+ && (!isset($oldid))
+ && (!isset($diff))
+ && (!isset($redirect))
+ && (!isset($printable))
+ && !isset($page)
+ && (!$this->mRedirectedFrom)
+ && ($ulang === $clang);
+
+ if ( $cacheable ) {
+ //extension may have reason to disable file caching on some pages.
+ $cacheable = wfRunHooks( 'IsFileCacheable', array( $this ) );
+ }
+
+ return $cacheable;
}
/**
@@ -2446,7 +2541,7 @@ class Article {
function quickEdit( $text, $comment = '', $minor = 0 ) {
wfProfileIn( __METHOD__ );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$revision = new Revision( array(
'page' => $this->getId(),
@@ -2471,7 +2566,7 @@ class Article {
$id = intval( $id );
global $wgHitcounterUpdateFreq, $wgDBtype;
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$pageTable = $dbw->tableName( 'page' );
$hitcounterTable = $dbw->tableName( 'hitcounter' );
$acchitsTable = $dbw->tableName( 'acchits' );
@@ -2555,7 +2650,7 @@ class Article {
$title->touchLinks();
$title->purgeSquid();
-
+
# File cache
if ( $wgUseFileCache ) {
$cm = new HTMLFileCache( $title );
@@ -2617,7 +2712,7 @@ class Article {
$wgOut->addHTML(wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' ) );
}
} else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$wl_clause = array(
'wl_title' => $page->getDBkey(),
'wl_namespace' => $page->getNamespace() );
@@ -2659,7 +2754,7 @@ class Article {
return false;
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$rev_clause = array( 'rev_page' => $id );
@@ -2693,7 +2788,7 @@ class Article {
return array();
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'templatelinks' ),
array( 'tl_namespace', 'tl_title' ),
array( 'tl_from' => $id ),
@@ -2708,7 +2803,7 @@ class Article {
$dbr->freeResult( $res );
return $result;
}
-
+
/**
* Return an auto-generated summary if the text provided is a redirect.
*
@@ -2785,6 +2880,84 @@ class Article {
return $summary;
}
+
+ /**
+ * Add the primary page-view wikitext to the output buffer
+ * Saves the text into the parser cache if possible.
+ * Updates templatelinks if it is out of date.
+ *
+ * @param string $text
+ * @param bool $cache
+ */
+ public function outputWikiText( $text, $cache = true ) {
+ global $wgParser, $wgUser, $wgOut;
+
+ $popts = $wgOut->parserOptions();
+ $popts->setTidy(true);
+ $parserOutput = $wgParser->parse( $text, $this->mTitle,
+ $popts, true, true, $this->getRevIdFetched() );
+ $popts->setTidy(false);
+ if ( $cache && $this && $parserOutput->getCacheTime() != -1 ) {
+ $parserCache =& ParserCache::singleton();
+ $parserCache->save( $parserOutput, $this, $wgUser );
+ }
+
+ if ( !wfReadOnly() && $this->mTitle->areRestrictionsCascading() ) {
+ // templatelinks table may have become out of sync,
+ // especially if using variable-based transclusions.
+ // For paranoia, check if things have changed and if
+ // so apply updates to the database. This will ensure
+ // that cascaded protections apply as soon as the changes
+ // are visible.
+
+ # Get templates from templatelinks
+ $id = $this->mTitle->getArticleID();
+
+ $tlTemplates = array();
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( array( 'templatelinks' ),
+ array( 'tl_namespace', 'tl_title' ),
+ array( 'tl_from' => $id ),
+ 'Article:getUsedTemplates' );
+
+ global $wgContLang;
+
+ if ( false !== $res ) {
+ if ( $dbr->numRows( $res ) ) {
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $tlTemplates[] = $wgContLang->getNsText( $row->tl_namespace ) . ':' . $row->tl_title ;
+ }
+ }
+ }
+
+ # Get templates from parser output.
+ $poTemplates_allns = $parserOutput->getTemplates();
+
+ $poTemplates = array ();
+ foreach ( $poTemplates_allns as $ns_templates ) {
+ $poTemplates = array_merge( $poTemplates, $ns_templates );
+ }
+
+ # Get the diff
+ $templates_diff = array_diff( $poTemplates, $tlTemplates );
+
+ if ( count( $templates_diff ) > 0 ) {
+ # Whee, link updates time.
+ $u = new LinksUpdate( $this->mTitle, $parserOutput );
+
+ $dbw = wfGetDb( DB_MASTER );
+ $dbw->begin();
+
+ $u->doUpdate();
+
+ $dbw->commit();
+ }
+ }
+
+ $wgOut->addParserOutput( $parserOutput );
+ }
+
}
?>
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index e33ef1bf..9395032f 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -1,6 +1,5 @@
<?php
/**
- * @package MediaWiki
*/
# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
# http://www.mediawiki.org/
@@ -33,7 +32,6 @@
* This interface is new, and might change a bit before 1.4.0 final is
* done...
*
- * @package MediaWiki
*/
class AuthPlugin {
/**
@@ -187,12 +185,14 @@ class AuthPlugin {
* Add a user to the external authentication database.
* Return true if successful.
*
- * @param User $user
+ * @param User $user - only the name should be assumed valid at this point
* @param string $password
+ * @param string $email
+ * @param string $realname
* @return bool
* @public
*/
- function addUser( $user, $password ) {
+ function addUser( $user, $password, $email='', $realname='' ) {
return true;
}
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 8de5608f..72a71c71 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -8,9 +8,11 @@ function __autoload($className) {
global $wgAutoloadClasses;
static $localClasses = array(
+ # Includes
'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
'AjaxCachePolicy' => 'includes/AjaxFunctions.php',
'AjaxResponse' => 'includes/AjaxResponse.php',
+ 'AlphabeticPager' => 'includes/Pager.php',
'Article' => 'includes/Article.php',
'AuthPlugin' => 'includes/AuthPlugin.php',
'BagOStuff' => 'includes/BagOStuff.php',
@@ -39,9 +41,8 @@ function __autoload($className) {
'Database' => 'includes/Database.php',
'DatabaseMysql' => 'includes/Database.php',
'ResultWrapper' => 'includes/Database.php',
- 'OracleBlob' => 'includes/DatabaseOracle.php',
- 'DatabaseOracle' => 'includes/DatabaseOracle.php',
'DatabasePostgres' => 'includes/DatabasePostgres.php',
+ 'DatabaseOracle' => 'includes/DatabaseOracle.php',
'DateFormatter' => 'includes/DateFormatter.php',
'DifferenceEngine' => 'includes/DifferenceEngine.php',
'_DiffOp' => 'includes/DifferenceEngine.php',
@@ -95,6 +96,7 @@ function __autoload($className) {
'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',
@@ -114,6 +116,10 @@ function __autoload($className) {
'MacBinary' => 'includes/MacBinary.php',
'MagicWord' => 'includes/MagicWord.php',
'MathRenderer' => 'includes/Math.php',
+ 'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
+ 'ThumbnailImage' => 'includes/MediaTransformOutput.php',
+ 'MediaTransformError' => 'includes/MediaTransformOutput.php',
+ 'TransformParameterError' => 'includes/MediaTransformOutput.php',
'MessageCache' => 'includes/MessageCache.php',
'MimeMagic' => 'includes/MimeMagic.php',
'Namespace' => 'includes/Namespace.php',
@@ -124,16 +130,18 @@ function __autoload($className) {
'ReverseChronologicalPager' => 'includes/Pager.php',
'TablePager' => 'includes/Pager.php',
'Parser' => 'includes/Parser.php',
- 'ParserOutput' => 'includes/Parser.php',
- 'ParserOptions' => 'includes/Parser.php',
+ 'ParserOutput' => 'includes/ParserOutput.php',
+ 'ParserOptions' => 'includes/ParserOptions.php',
'ParserCache' => 'includes/ParserCache.php',
+ 'PatrolLog' => 'includes/PatrolLog.php',
'ProfilerSimple' => 'includes/ProfilerSimple.php',
'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
'Profiler' => 'includes/Profiler.php',
'ProxyTools' => 'includes/ProxyTools.php',
'ProtectionForm' => 'includes/ProtectionForm.php',
'QueryPage' => 'includes/QueryPage.php',
- 'PageQueryPage' => 'includes/QueryPage.php',
+ 'PageQueryPage' => 'includes/PageQueryPage.php',
+ 'ImageQueryPage' => 'includes/ImageQueryPage.php',
'RawPage' => 'includes/RawPage.php',
'RecentChange' => 'includes/RecentChange.php',
'Revision' => 'includes/Revision.php',
@@ -148,6 +156,7 @@ function __autoload($className) {
'SearchPostgres' => 'includes/SearchPostgres.php',
'SearchUpdate' => 'includes/SearchUpdate.php',
'SearchUpdateMyISAM' => 'includes/SearchUpdate.php',
+ 'SearchOracle' => 'includes/SearchOracle.php',
'SiteConfiguration' => 'includes/SiteConfiguration.php',
'SiteStats' => 'includes/SiteStats.php',
'SiteStatsUpdate' => 'includes/SiteStats.php',
@@ -160,7 +169,6 @@ function __autoload($className) {
'IPBlockForm' => 'includes/SpecialBlockip.php',
'SpecialBookSources' => 'includes/SpecialBooksources.php',
'BrokenRedirectsPage' => 'includes/SpecialBrokenRedirects.php',
- 'CategoriesPage' => 'includes/SpecialCategories.php',
'EmailConfirmation' => 'includes/SpecialConfirmemail.php',
'ContributionsPage' => 'includes/SpecialContributions.php',
'DeadendPagesPage' => 'includes/SpecialDeadendpages.php',
@@ -173,7 +181,6 @@ function __autoload($className) {
'ImportStreamSource' => 'includes/SpecialImport.php',
'IPUnblockForm' => 'includes/SpecialIpblocklist.php',
'ListredirectsPage' => 'includes/SpecialListredirects.php',
- 'ListUsersPage' => 'includes/SpecialListusers.php',
'DBLockForm' => 'includes/SpecialLockdb.php',
'LogReader' => 'includes/SpecialLog.php',
'LogViewer' => 'includes/SpecialLog.php',
@@ -185,6 +192,7 @@ function __autoload($className) {
'MostlinkedPage' => 'includes/SpecialMostlinked.php',
'MostlinkedCategoriesPage' => 'includes/SpecialMostlinkedcategories.php',
'MostrevisionsPage' => 'includes/SpecialMostrevisions.php',
+ 'FewestrevisionsPage' => 'includes/SpecialFewestrevisions.php',
'MovePageForm' => 'includes/SpecialMovepage.php',
'NewbieContributionsPage' => 'includes/SpecialNewbieContributions.php',
'NewPagesPage' => 'includes/SpecialNewpages.php',
@@ -194,6 +202,7 @@ function __autoload($className) {
'PopularPagesPage' => 'includes/SpecialPopularpages.php',
'PreferencesForm' => 'includes/SpecialPreferences.php',
'SpecialPrefixindex' => 'includes/SpecialPrefixindex.php',
+ 'PasswordResetForm' => 'includes/SpecialResetpass.php',
'RevisionDeleteForm' => 'includes/SpecialRevisiondelete.php',
'RevisionDeleter' => 'includes/SpecialRevisiondelete.php',
'SpecialSearch' => 'includes/SpecialSearch.php',
@@ -215,6 +224,7 @@ function __autoload($className) {
'WantedCategoriesPage' => 'includes/SpecialWantedcategories.php',
'WantedPagesPage' => 'includes/SpecialWantedpages.php',
'WhatLinksHerePage' => 'includes/SpecialWhatlinkshere.php',
+ 'WithoutInterwikiPage' => 'includes/SpecialWithoutinterwiki.php',
'SquidUpdate' => 'includes/SquidUpdate.php',
'ReplacementArray' => 'includes/StringUtils.php',
'Replacer' => 'includes/StringUtils.php',
@@ -237,13 +247,27 @@ function __autoload($className) {
'Xml' => 'includes/Xml.php',
'ZhClient' => 'includes/ZhClient.php',
'memcached' => 'includes/memcached-client.php',
+
+ # Media
+ 'BitmapHandler' => 'includes/media/Bitmap.php',
+ 'BmpHandler' => 'includes/media/BMP.php',
+ 'DjVuHandler' => 'includes/media/DjVu.php',
+ 'MediaHandler' => 'includes/media/Generic.php',
+ 'ImageHandler' => 'includes/media/Generic.php',
+ 'SvgHandler' => 'includes/media/SVG.php',
+
+ # Normal
'UtfNormal' => 'includes/normal/UtfNormal.php',
+
+ # Templates
'UsercreateTemplate' => 'includes/templates/Userlogin.php',
'UserloginTemplate' => 'includes/templates/Userlogin.php',
+
+ # Languages
'Language' => 'languages/Language.php',
- 'PasswordResetForm' => 'includes/SpecialResetpass.php',
+ 'RandomPage' => 'includes/SpecialRandompage.php',
- // API classes
+ # API
'ApiBase' => 'includes/api/ApiBase.php',
'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php',
'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
@@ -274,6 +298,7 @@ function __autoload($className) {
'ApiResult' => 'includes/api/ApiResult.php',
);
+ wfProfileIn( __METHOD__ );
if ( isset( $localClasses[$className] ) ) {
$filename = $localClasses[$className];
} elseif ( isset( $wgAutoloadClasses[$className] ) ) {
@@ -290,6 +315,7 @@ function __autoload($className) {
}
if ( !$filename ) {
# Give up
+ wfProfileOut( __METHOD__ );
return;
}
}
@@ -300,6 +326,7 @@ function __autoload($className) {
$filename = "$IP/$filename";
}
require( $filename );
+ wfProfileOut( __METHOD__ );
}
function wfLoadAllExtensions() {
@@ -311,10 +338,10 @@ function wfLoadAllExtensions() {
# guaranteed by entering special pages via SpecialPage members such as
# executePath(), but here we have to take a more explicit measure.
- require_once( 'SpecialPage.php' );
+ require_once( dirname(__FILE__) . '/SpecialPage.php' );
foreach( $wgAutoloadClasses as $class => $file ) {
- if ( ! class_exists( $class ) ) {
+ if( !( class_exists( $class ) || interface_exists( $class ) ) ) {
require( $file );
}
}
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index c720807d..2a04b9dd 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -19,7 +19,6 @@
# http://www.gnu.org/copyleft/gpl.html
/**
*
- * @package MediaWiki
*/
/**
@@ -29,15 +28,16 @@
* the PHP memcached client.
*
* backends for local hash array and SQL table included:
- * $bag = new HashBagOStuff();
- * $bag = new MysqlBagOStuff($tablename); # connect to db first
+ * <code>
+ * $bag = new HashBagOStuff();
+ * $bag = new MysqlBagOStuff($tablename); # connect to db first
+ * </code>
*
- * @package MediaWiki
*/
class BagOStuff {
var $debugmode;
- function BagOStuff() {
+ function __construct() {
$this->set_debug( false );
}
@@ -163,7 +163,6 @@ class BagOStuff {
/**
* Functional versions!
* @todo document
- * @package MediaWiki
*/
class HashBagOStuff extends BagOStuff {
/*
@@ -218,7 +217,6 @@ CREATE TABLE objectcache (
/**
* @todo document
* @abstract
- * @package MediaWiki
*/
abstract class SqlBagOStuff extends BagOStuff {
var $table;
@@ -386,34 +384,32 @@ abstract class SqlBagOStuff extends BagOStuff {
/**
* @todo document
- * @package MediaWiki
*/
class MediaWikiBagOStuff extends SqlBagOStuff {
var $tableInitialised = false;
function _doquery($sql) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->query($sql, 'MediaWikiBagOStuff::_doquery');
}
function _doinsert($t, $v) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->insert($t, $v, 'MediaWikiBagOStuff::_doinsert',
array( 'IGNORE' ) );
}
function _fetchobject($result) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->fetchObject($result);
}
function _freeresult($result) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->freeResult($result);
}
function _dberror($result) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->lastError();
}
function _maxdatetime() {
- $dbw =& wfGetDB(DB_MASTER);
if ( time() > 0x7fffffff ) {
return $this->_fromunixtime( 1<<62 );
} else {
@@ -421,24 +417,24 @@ class MediaWikiBagOStuff extends SqlBagOStuff {
}
}
function _fromunixtime($ts) {
- $dbw =& wfGetDB(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
return $dbw->timestamp($ts);
}
function _strencode($s) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->strencode($s);
}
function _blobencode($s) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->encodeBlob($s);
}
function _blobdecode($s) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
return $dbw->decodeBlob($s);
}
function getTableName() {
if ( !$this->tableInitialised ) {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
/* This is actually a hack, we should be able
to use Language classes here... or not */
if (!$dbw)
@@ -463,7 +459,6 @@ class MediaWikiBagOStuff extends SqlBagOStuff {
* that Turck's serializer is faster, so a possible future extension would be
* to use it for arrays but not for objects.
*
- * @package MediaWiki
*/
class TurckBagOStuff extends BagOStuff {
function get($key) {
@@ -498,9 +493,7 @@ class TurckBagOStuff extends BagOStuff {
/**
* This is a wrapper for APC's shared memory functions
*
- * @package MediaWiki
*/
-
class APCBagOStuff extends BagOStuff {
function get($key) {
$val = apc_fetch($key);
@@ -528,7 +521,6 @@ class APCBagOStuff extends BagOStuff {
* This is basically identical to the Turck MMCache version,
* mostly because eAccelerator is based on Turck MMCache.
*
- * @package MediaWiki
*/
class eAccelBagOStuff extends BagOStuff {
function get($key) {
@@ -560,6 +552,9 @@ class eAccelBagOStuff extends BagOStuff {
}
}
+/**
+ * @todo document
+ */
class DBABagOStuff extends BagOStuff {
var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
diff --git a/includes/Block.php b/includes/Block.php
index ff813ba3..94bfa5b4 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -1,7 +1,6 @@
<?php
/**
* Blocks and bans object
- * @package MediaWiki
*/
/**
@@ -12,22 +11,24 @@
* Globals used: $wgAutoblockExpiry, $wgAntiLockFlags
*
* @todo This could be used everywhere, but it isn't.
- * @package MediaWiki
*/
class Block
{
/* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
- $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock;
+ $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName;
/* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster, $mByName;
const EB_KEEP_EXPIRED = 1;
const EB_FOR_UPDATE = 2;
const EB_RANGE_ONLY = 4;
- function Block( $address = '', $user = 0, $by = 0, $reason = '',
- $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0 )
+ function __construct( $address = '', $user = 0, $by = 0, $reason = '',
+ $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
+ $hideName = 0 )
{
$this->mId = 0;
+ # Expand valid IPv6 addresses
+ $address = IP::sanitizeIP( $address );
$this->mAddress = $address;
$this->mUser = $user;
$this->mBy = $by;
@@ -38,6 +39,7 @@ class Block
$this->mCreateAccount = $createAccount;
$this->mExpiry = self::decodeExpiry( $expiry );
$this->mEnableAutoblock = $enableAutoblock;
+ $this->mHideName = $hideName;
$this->mForUpdate = false;
$this->mFromMaster = false;
@@ -58,7 +60,7 @@ class Block
static function newFromID( $id )
{
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->resultObject( $dbr->select( 'ipblocks', '*',
array( 'ipb_id' => $id ), __METHOD__ ) );
$block = new Block;
@@ -74,7 +76,7 @@ class Block
$this->mAddress = $this->mReason = $this->mTimestamp = '';
$this->mId = $this->mAnonOnly = $this->mCreateAccount =
$this->mEnableAutoblock = $this->mAuto = $this->mUser =
- $this->mBy = 0;
+ $this->mBy = $this->mHideName = 0;
$this->mByName = false;
}
@@ -85,14 +87,14 @@ class Block
{
global $wgAntiLockFlags;
if ( $this->mForUpdate || $this->mFromMaster ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
if ( !$this->mForUpdate || ($wgAntiLockFlags & ALF_NO_BLOCK_LOCK) ) {
$options = array();
} else {
$options = array( 'FOR UPDATE' );
}
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$options = array();
}
return $db;
@@ -147,7 +149,7 @@ class Block
}
# Try range block
- if ( $this->loadRange( $address, $killExpired, $user == 0 ) ) {
+ if ( $this->loadRange( $address, $killExpired, $user ) ) {
if ( $user && $this->mAnonOnly ) {
$this->clear();
return false;
@@ -176,7 +178,8 @@ class Block
/**
* Fill in member variables from a result wrapper
*/
- function loadFromResult( ResultWrapper $res, $killExpired = true ) {
+ function loadFromResult( ResultWrapper $res, $killExpired = true )
+ {
$ret = false;
if ( 0 != $res->numRows() ) {
# Get first block
@@ -211,7 +214,7 @@ class Block
* Search the database for any range blocks matching the given address, and
* load the row if one is found.
*/
- function loadRange( $address, $killExpired = true )
+ function loadRange( $address, $killExpired = true, $user = 0 )
{
$iaddr = IP::toHex( $address );
if ( $iaddr === false ) {
@@ -230,6 +233,10 @@ class Block
"ipb_range_start <= '$iaddr'",
"ipb_range_end >= '$iaddr'"
);
+
+ if ( $user ) {
+ $conds['ipb_anon_only'] = 0;
+ }
$res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
$success = $this->loadFromResult( $res, $killExpired );
@@ -255,6 +262,7 @@ class Block
$this->mAnonOnly = $row->ipb_anon_only;
$this->mCreateAccount = $row->ipb_create_account;
$this->mEnableAutoblock = $row->ipb_enable_autoblock;
+ $this->mHideName = $row->ipb_deleted;
$this->mId = $row->ipb_id;
$this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
if ( isset( $row->user_name ) ) {
@@ -286,7 +294,7 @@ class Block
$block = new Block();
if ( $flags & Block::EB_FOR_UPDATE ) {
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
if ( $wgAntiLockFlags & ALF_NO_BLOCK_LOCK ) {
$options = '';
} else {
@@ -294,7 +302,7 @@ class Block
}
$block->forUpdate( true );
} else {
- $db =& wfGetDB( DB_SLAVE );
+ $db = wfGetDB( DB_SLAVE );
$options = '';
}
if ( $flags & Block::EB_RANGE_ONLY ) {
@@ -341,7 +349,7 @@ class Block
throw new MWException( "Block::delete() now requires that the mId member be filled\n" );
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'ipblocks', array( 'ipb_id' => $this->mId ), __METHOD__ );
return $dbw->affectedRows() > 0;
}
@@ -353,8 +361,7 @@ class Block
function insert()
{
wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
- $dbw =& wfGetDB( DB_MASTER );
- $dbw->begin();
+ $dbw = wfGetDB( DB_MASTER );
# Unset ipb_anon_only for user blocks, makes no sense
if ( $this->mUser ) {
@@ -385,6 +392,7 @@ class Block
'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
'ipb_range_start' => $this->mRangeStart,
'ipb_range_end' => $this->mRangeEnd,
+ 'ipb_deleted' => $this->mHideName
), 'Block::insert', array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
@@ -418,20 +426,20 @@ class Block
} else {
#Limit is 1, so no loop needed.
$retroblockip = $row->rc_ip;
- return $this->doAutoblock($retroblockip);
+ return $this->doAutoblock( $retroblockip, true );
}
}
}
/**
* Autoblocks the given IP, referring to this Block.
- * @param $autoblockip The IP to autoblock.
+ * @param string $autoblockip The IP to autoblock.
+ * @param bool $justInserted The main block was just inserted
* @return bool Whether or not an autoblock was inserted.
*/
- function doAutoblock( $autoblockip ) {
+ function doAutoblock( $autoblockip, $justInserted = false ) {
# Check if this IP address is already blocked
- $dbw =& wfGetDB( DB_MASTER );
- $dbw->begin();
+ $dbw = wfGetDB( DB_MASTER );
# If autoblocks are disabled, go away.
if ( !$this->mEnableAutoblock ) {
@@ -480,7 +488,9 @@ class Block
return;
}
# Just update the timestamp
- $ipblock->updateTimestamp();
+ if ( !$justInserted ) {
+ $ipblock->updateTimestamp();
+ }
return;
} else {
$ipblock = new Block;
@@ -495,6 +505,8 @@ class Block
$ipblock->mTimestamp = wfTimestampNow();
$ipblock->mAuto = 1;
$ipblock->mCreateAccount = $this->mCreateAccount;
+ # Continue suppressing the name if needed
+ $ipblock->mHideName = $this->mHideName;
# If the user is already blocked with an expiry date, we don't
# want to pile on top of that!
@@ -544,7 +556,7 @@ class Block
$this->mTimestamp = wfTimestamp();
$this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->update( 'ipblocks',
array( /* SET */
'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
@@ -628,16 +640,36 @@ class Block
global $wgAutoblockExpiry;
return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
}
-
- static function normaliseRange( $range )
- {
+
+ /**
+ * Gets rid of uneeded numbers in quad-dotted/octet IP strings
+ * For example, 127.111.113.151/24 -> 127.111.113.0/24
+ */
+ static function normaliseRange( $range ) {
$parts = explode( '/', $range );
if ( count( $parts ) == 2 ) {
- $shift = 32 - $parts[1];
- $ipint = IP::toUnsigned( $parts[0] );
- $ipint = $ipint >> $shift << $shift;
- $newip = long2ip( $ipint );
- $range = "$newip/{$parts[1]}";
+ // IPv6
+ if ( IP::isIPv6($range) && $parts[1] >= 64 && $parts[1] <= 128 ) {
+ $bits = $parts[1];
+ $ipint = IP::toUnsigned6( $parts[0] );
+ # Native 32 bit functions WONT work here!!!
+ # Convert to a padded binary number
+ $network = wfBaseConvert( $ipint, 10, 2, 128 );
+ # Truncate the last (128-$bits) bits and replace them with zeros
+ $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
+ # Convert back to an integer
+ $network = wfBaseConvert( $network, 2, 10 );
+ # Reform octet address
+ $newip = IP::toOctet( $network );
+ $range = "$newip/{$parts[1]}";
+ } // IPv4
+ else if ( IP::isIPv4($range) && $parts[1] >= 16 && $parts[1] <= 32 ) {
+ $shift = 32 - $parts[1];
+ $ipint = IP::toUnsigned( $parts[0] );
+ $ipint = $ipint >> $shift << $shift;
+ $newip = long2ip( $ipint );
+ $range = "$newip/{$parts[1]}";
+ }
}
return $range;
}
@@ -646,7 +678,7 @@ class Block
* Purge expired blocks from the ipblocks table
*/
static function purgeExpired() {
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'ipblocks', array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ );
}
@@ -658,7 +690,7 @@ class Block
/*
static $infinity;
if ( !isset( $infinity ) ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$infinity = $dbr->bigTimestamp();
}
return $infinity;
diff --git a/includes/CacheDependency.php b/includes/CacheDependency.php
index 4bb3d328..bb5c5437 100644
--- a/includes/CacheDependency.php
+++ b/includes/CacheDependency.php
@@ -4,6 +4,7 @@
* This class stores an arbitrary value along with its dependencies.
* Users should typically only use DependencyWrapper::getFromCache(), rather
* than instantiating one of these objects directly.
+ * @addtogroup Cache
*/
class DependencyWrapper {
var $value;
@@ -95,6 +96,9 @@ class DependencyWrapper {
}
}
+/**
+ * @addtogroup Cache
+ */
abstract class CacheDependency {
/**
* Returns true if the dependency is expired, false otherwise
@@ -107,6 +111,9 @@ abstract class CacheDependency {
function loadDependencyValues() {}
}
+/**
+ * @addtogroup Cache
+ */
class FileDependency extends CacheDependency {
var $filename, $timestamp;
@@ -163,6 +170,9 @@ class FileDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class TitleDependency extends CacheDependency {
var $titleObj;
var $ns, $dbk;
@@ -219,6 +229,9 @@ class TitleDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class TitleListDependency extends CacheDependency {
var $linkBatch;
var $timestamps;
@@ -244,7 +257,7 @@ class TitleListDependency extends CacheDependency {
# Do the query
if ( count( $timestamps ) ) {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$where = $this->getLinkBatch()->constructSet( 'page', $dbr );
$res = $dbr->select( 'page',
array( 'page_namespace', 'page_title', 'page_touched' ),
@@ -299,6 +312,9 @@ class TitleListDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class GlobalDependency extends CacheDependency {
var $name, $value;
@@ -312,6 +328,9 @@ class GlobalDependency extends CacheDependency {
}
}
+/**
+ * @addtogroup Cache
+ */
class ConstantDependency extends CacheDependency {
var $name, $value;
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 0086a2f9..356f9ea2 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -3,17 +3,23 @@
* Special handling for category description pages
* Modelled after ImagePage.php
*
- * @package MediaWiki
*/
if( !defined( 'MEDIAWIKI' ) )
die( 1 );
/**
- * @package MediaWiki
*/
class CategoryPage extends Article {
function view() {
+ global $wgRequest, $wgUser;
+
+ $diff = $wgRequest->getVal( 'diff' );
+ $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
+
+ if ( isset( $diff ) && $diffOnly )
+ return Article::view();
+
if(!wfRunHooks('CategoryPageView', array(&$this))) return;
if ( NS_CATEGORY == $this->mTitle->getNamespace() ) {
@@ -175,7 +181,7 @@ class CategoryViewer {
}
function doCategoryQuery() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
if( $this->from != '' ) {
$pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from );
$this->flip = false;
@@ -196,6 +202,7 @@ class CategoryViewer {
#+ $pageCondition,
__METHOD__,
array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : 'cl_sortkey',
+ 'USE INDEX' => 'cl_sortkey',
'LIMIT' => $this->limit + 1 ) );
$count = 0;
@@ -234,11 +241,12 @@ class CategoryViewer {
function getSubcategorySection() {
# Don't show subcategories section if there are none.
$r = '';
- if( count( $this->children ) > 0 ) {
+ $c = count( $this->children );
+ if( $c > 0 ) {
# Showing subcategories
$r .= "<div id=\"mw-subcategories\">\n";
$r .= '<h2>' . wfMsg( 'subcategories' ) . "</h2>\n";
- $r .= wfMsgExt( 'subcategorycount', array( 'parse' ), count( $this->children) );
+ $r .= wfMsgExt( 'subcategorycount', array( 'parse' ), $c );
$r .= $this->formatList( $this->children, $this->children_start_char );
$r .= "\n</div>";
}
@@ -247,11 +255,16 @@ class CategoryViewer {
function getPagesSection() {
$ti = htmlspecialchars( $this->title->getText() );
- $r = "<div id=\"mw-pages\">\n";
- $r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
- $r .= wfMsgExt( 'categoryarticlecount', array( 'parse' ), count( $this->articles) );
- $r .= $this->formatList( $this->articles, $this->articles_start_char );
- $r .= "\n</div>";
+ # Don't show articles section if there are none.
+ $r = '';
+ $c = count( $this->articles );
+ if( $c > 0 ) {
+ $r = "<div id=\"mw-pages\">\n";
+ $r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
+ $r .= wfMsgExt( 'categoryarticlecount', array( 'parse' ), $c );
+ $r .= $this->formatList( $this->articles, $this->articles_start_char );
+ $r .= "\n</div>";
+ }
return $r;
}
@@ -391,7 +404,7 @@ class CategoryViewer {
*/
function pagingLinks( $title, $first, $last, $limit, $query = array() ) {
global $wgUser, $wgLang;
- $sk =& $this->getSkin();
+ $sk = $this->getSkin();
$limitText = $wgLang->formatNum( $limit );
$prevLink = htmlspecialchars( wfMsg( 'prevn', $limitText ) );
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index a8cdf3ce..7faae935 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -1,26 +1,27 @@
<?php
-/*
-The "Categoryfinder" class takes a list of articles, creates an internal representation of all their parent
-categories (as well as parents of parents etc.). From this representation, it determines which of these articles
-are in one or all of a given subset of categories.
-
-Example use :
-
- # Determines wether the article with the page_id 12345 is in both
- # "Category 1" and "Category 2" or their subcategories, respectively
-
- $cf = new Categoryfinder ;
- $cf->seed (
- array ( 12345 ) ,
- array ( "Category 1","Category 2" ) ,
- "AND"
- ) ;
- $a = $cf->run() ;
- print implode ( "," , $a ) ;
-
-*/
-
+/**
+ * The "Categoryfinder" class takes a list of articles, creates an internal
+ * representation of all their parent categories (as well as parents of
+ * parents etc.). From this representation, it determines which of these
+ * articles are in one or all of a given subset of categories.
+ *
+ * Example use :
+ * <code>
+ * # Determines whether the article with the page_id 12345 is in both
+ * # "Category 1" and "Category 2" or their subcategories, respectively
+ *
+ * $cf = new Categoryfinder ;
+ * $cf->seed (
+ * array ( 12345 ) ,
+ * array ( "Category 1","Category 2" ) ,
+ * "AND"
+ * ) ;
+ * $a = $cf->run() ;
+ * print implode ( "," , $a ) ;
+ * </code>
+ *
+ */
class Categoryfinder {
var $articles = array () ; # The original article IDs passed to the seed function
@@ -34,8 +35,8 @@ class Categoryfinder {
/**
* Constructor (currently empty).
- */
- function Categoryfinder () {
+ */
+ function __construct() {
}
/**
@@ -61,10 +62,10 @@ class Categoryfinder {
/**
* Iterates through the parent tree starting with the seed values,
* then checks the articles if they match the conditions
- @return array of page_ids (those given to seed() that match the conditions)
- */
+ * @return array of page_ids (those given to seed() that match the conditions)
+ */
function run () {
- $this->dbr =& wfGetDB( DB_SLAVE );
+ $this->dbr = wfGetDB( DB_SLAVE );
while ( count ( $this->next ) > 0 ) {
$this->scan_next_layer () ;
}
@@ -83,20 +84,20 @@ class Categoryfinder {
/**
* This functions recurses through the parent representation, trying to match the conditions
- @param $id The article/category to check
- @param $conds The array of categories to match
- @return bool Does this match the conditions?
- */
+ * @param $id The article/category to check
+ * @param $conds The array of categories to match
+ * @return bool Does this match the conditions?
+ */
function check ( $id , &$conds ) {
# Shortcut (runtime paranoia): No contitions=all matched
if ( count ( $conds ) == 0 ) return true ;
-
+
if ( !isset ( $this->parents[$id] ) ) return false ;
# iterate through the parents
foreach ( $this->parents[$id] AS $p ) {
$pname = $p->cl_to ;
-
+
# Is this a condition?
if ( isset ( $conds[$pname] ) ) {
# This key is in the category list!
@@ -113,7 +114,7 @@ class Categoryfinder {
}
}
}
-
+
# Not done yet, try sub-parents
if ( !isset ( $this->name2id[$pname] ) ) {
# No sub-parent
@@ -130,10 +131,10 @@ class Categoryfinder {
/**
* Scans a "parent layer" of the articles/categories in $this->next
- */
+ */
function scan_next_layer () {
$fname = "Categoryfinder::scan_next_layer" ;
-
+
# Find all parents of the article currently in $this->next
$layer = array () ;
$res = $this->dbr->select(
@@ -161,7 +162,7 @@ class Categoryfinder {
$this->dbr->freeResult( $res ) ;
$this->next = array() ;
-
+
# Find the IDs of all category pages in $layer, if they exist
if ( count ( $layer ) > 0 ) {
$res = $this->dbr->select(
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index a2c1a265..bc141579 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -1,15 +1,7 @@
<?php
-/**
- * @package MediaWiki
- * Contain class to show various lists of change:
- * - what's link here
- * - related changes
- * - recent changes
- */
/**
* @todo document
- * @package MediaWiki
*/
class RCCacheEntry extends RecentChange
{
@@ -17,8 +9,7 @@ class RCCacheEntry extends RecentChange
var $curlink , $difflink, $lastlink , $usertalklink , $versionlink ;
var $userlink, $timestamp, $watched;
- function newFromParent( $rc )
- {
+ function newFromParent( $rc ) {
$rc2 = new RCCacheEntry;
$rc2->mAttribs = $rc->mAttribs;
$rc2->mExtra = $rc->mExtra;
@@ -27,14 +18,17 @@ class RCCacheEntry extends RecentChange
} ;
/**
- * @package MediaWiki
+ * Class to show various lists of changes:
+ * - what links here
+ * - related changes
+ * - recent changes
*/
class ChangesList {
# Called by history lists and recent changes
#
/** @todo document */
- function ChangesList( &$skin ) {
+ function __construct( &$skin ) {
$this->skin =& $skin;
$this->preCacheMessages();
}
@@ -47,7 +41,7 @@ class ChangesList {
* @return ChangesList derivative
*/
public static function newFromUser( &$user ) {
- $sk =& $user->getSkin();
+ $sk = $user->getSkin();
$list = NULL;
if( wfRunHooks( 'FetchChangesList', array( &$user, &$sk, &$list ) ) ) {
return $user->getOption( 'usenewrc' ) ? new EnhancedChangesList( $sk ) : new OldChangesList( $sk );
@@ -64,7 +58,7 @@ class ChangesList {
// Precache various messages
if( !isset( $this->message ) ) {
foreach( explode(' ', 'cur diff hist minoreditletter newpageletter last '.
- 'blocklink changes history boteditletter' ) as $msg ) {
+ 'blocklink history boteditletter' ) as $msg ) {
$this->message[$msg] = wfMsgExt( $msg, array( 'escape') );
}
}
@@ -212,6 +206,23 @@ class ChangesList {
global $wgUseRCPatrol, $wgUser;
return( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) );
}
+
+ /**
+ * Returns the string which indicates the number of watching users
+ */
+ function numberofWatchingusers( $count ) {
+ global $wgLang;
+ static $cache = array();
+ if ( $count > 0 ) {
+ if ( !isset( $cache[$count] ) ) {
+ $cache[$count] = wfMsgExt('number_of_watching_users_RCview',
+ array('parsemag', 'escape'), $wgLang->formatNum($count));
+ }
+ return $cache[$count];
+ } else {
+ return '';
+ }
+ }
}
@@ -229,6 +240,7 @@ class OldChangesList extends ChangesList {
wfProfileIn( $fname );
# Extract DB fields into local scope
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rc->mAttribs );
# Should patrol-related stuff be shown?
@@ -273,9 +285,7 @@ class OldChangesList extends ChangesList {
$this->insertUserRelatedLinks($s,$rc);
$this->insertComment($s, $rc);
- if($rc->numberofWatchingusers > 0) {
- $s .= ' ' . wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rc->numberofWatchingusers));
- }
+ $s .= rtrim(' ' . $this->numberofWatchingusers($rc->numberofWatchingusers));
$s .= "</li>\n";
@@ -301,6 +311,7 @@ class EnhancedChangesList extends ChangesList {
$rc = RCCacheEntry::newFromParent( $baseRC );
# Extract fields from DB into the function scope (rc_xxxx variables)
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rc->mAttribs );
$curIdEq = 'curid=' . $rc_cur_id;
@@ -405,7 +416,7 @@ class EnhancedChangesList extends ChangesList {
* Enhanced RC group
*/
function recentChangesBlockGroup( $block ) {
- global $wgContLang, $wgRCShowChangedSize;
+ global $wgLang, $wgContLang, $wgRCShowChangedSize;
$r = '';
# Collate list of users
@@ -467,22 +478,32 @@ class EnhancedChangesList extends ChangesList {
$currentRevision = $block[0]->mAttribs['rc_this_oldid'];
if( $block[0]->mAttribs['rc_type'] != RC_LOG ) {
# Changes
- $r .= ' ('.count($block).' ';
+
+ $n = count($block);
+ static $nchanges = array();
+ if ( !isset( $nchanges[$n] ) ) {
+ $nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape'),
+ $wgLang->formatNum( $n ) );
+ }
+
+ $r .= ' (';
if( $isnew ) {
- $r .= $this->message['changes'];
+ $r .= $nchanges[$n];
} else {
$r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
- $this->message['changes'], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
+ $nchanges[$n], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
}
+ $r .= ') . . ';
+
# Character difference
$chardiff = $rcObj->getCharacterDifference( $block[ count( $block ) - 1 ]->mAttribs['rc_old_len'],
$block[0]->mAttribs['rc_new_len'] );
if( $chardiff == '' ) {
- $r .= '; ';
+ $r .= ' (';
} else {
- $r .= '; ' . $chardiff . ' ';
+ $r .= ' ' . $chardiff. ' . . (';
}
@@ -494,16 +515,14 @@ class EnhancedChangesList extends ChangesList {
$r .= $users;
- if($block[0]->numberofWatchingusers > 0) {
- global $wgContLang;
- $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($block[0]->numberofWatchingusers));
- }
+ $r .= $this->numberofWatchingusers($block[0]->numberofWatchingusers);
$r .= "<br />\n";
# Sub-entries
$r .= '<div id="'.$rci.'" style="display:none">';
foreach( $block as $rcObj ) {
# Get rc_xxxx variables
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rcObj->mAttribs );
$r .= $this->spacerArrow();
@@ -607,6 +626,7 @@ class EnhancedChangesList extends ChangesList {
global $wgContLang, $wgRCShowChangedSize;
# Get rc_xxxx variables
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rcObj->mAttribs );
$curIdEq = 'curid='.$rc_cur_id;
@@ -647,9 +667,7 @@ class EnhancedChangesList extends ChangesList {
$r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
}
- if( $rcObj->numberofWatchingusers > 0 ) {
- $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rcObj->numberofWatchingusers));
- }
+ $r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers);
$r .= "<br />\n";
return $r;
diff --git a/includes/CoreParserFunctions.php b/includes/CoreParserFunctions.php
index 402a3ba9..72ceb45f 100644
--- a/includes/CoreParserFunctions.php
+++ b/includes/CoreParserFunctions.php
@@ -2,8 +2,8 @@
/**
* Various core parser functions, registered in Parser::firstCallInit()
+ * @addtogroup Parser
*/
-
class CoreParserFunctions {
static function intFunction( $parser, $part1 = '' /*, ... */ ) {
if ( strval( $part1 ) !== '' ) {
@@ -87,7 +87,7 @@ class CoreParserFunctions {
static function formatNum( $parser, $num = '' ) {
return $parser->getFunctionLang()->formatNum( $num );
}
-
+
static function grammar( $parser, $case = '', $word = '' ) {
return $parser->getFunctionLang()->convertGrammar( $word, $case );
}
@@ -135,6 +135,7 @@ class CoreParserFunctions {
static function numberofarticles( $parser, $raw = null ) { return self::statisticsFunction( 'articles', $raw ); }
static function numberoffiles( $parser, $raw = null ) { return self::statisticsFunction( 'images', $raw ); }
static function numberofadmins( $parser, $raw = null ) { return self::statisticsFunction( 'admins', $raw ); }
+ static function numberofedits( $parser, $raw = null ) { return self::statisticsFunction( 'edits', $raw ); }
static function pagesinnamespace( $parser, $namespace = 0, $raw = null ) {
$count = SiteStats::pagesInNs( intval( $namespace ) );
@@ -151,7 +152,7 @@ class CoreParserFunctions {
$lang = $wgContLang->getLanguageName( strtolower( $arg ) );
return $lang != '' ? $lang : $arg;
}
-
+
static function pad( $string = '', $length = 0, $char = 0, $direction = STR_PAD_RIGHT ) {
$length = min( max( $length, 0 ), 500 );
$char = substr( $char, 0, 1 );
@@ -159,17 +160,21 @@ class CoreParserFunctions {
? str_pad( $string, $length, (string)$char, $direction )
: $string;
}
-
+
static function padleft( $parser, $string = '', $length = 0, $char = 0 ) {
return self::pad( $string, $length, $char, STR_PAD_LEFT );
}
-
+
static function padright( $parser, $string = '', $length = 0, $char = 0 ) {
return self::pad( $string, $length, $char );
}
-
+
static function anchorencode( $parser, $text ) {
- return strtr( urlencode( $text ) , array( '%' => '.' , '+' => '_' ) );
+ $a = urlencode( $text );
+ $a = strtr( $a, array( '%' => '.', '+' => '_' ) );
+ # leave colons alone, however
+ $a = str_replace( '.3A', ':', $a );
+ return $a;
}
static function special( $parser, $text ) {
@@ -180,14 +185,12 @@ class CoreParserFunctions {
return wfMsgForContent( 'nosuchspecialpage' );
}
}
-
+
public static function defaultsort( $parser, $text ) {
$text = trim( $text );
if( strlen( $text ) > 0 )
$parser->setDefaultSort( $text );
return '';
}
-
}
-
?>
diff --git a/includes/Credits.php b/includes/Credits.php
index 62f0b256..87382a86 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -18,7 +18,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* @author <evan@wikitravel.org>
- * @package MediaWiki
*/
/**
diff --git a/includes/Database.php b/includes/Database.php
index eb1ee135..3fd6ad16 100644
--- a/includes/Database.php
+++ b/includes/Database.php
@@ -2,7 +2,6 @@
/**
* This file deals with MySQL interface functions
* and query specifics/optimisations
- * @package MediaWiki
*/
/** Number of times to re-try an operation in case of deadlock */
@@ -16,6 +15,10 @@ define( 'DEADLOCK_DELAY_MAX', 1500000 );
* Utility classes
*****************************************************************************/
+/**
+ * Utility class.
+ * @addtogroup Database
+ */
class DBObject {
public $mData;
@@ -32,12 +35,66 @@ class DBObject {
}
};
+/**
+ * Utility class.
+ * @addtogroup Database
+ */
+class MySQLField {
+ private $name, $tablename, $default, $max_length, $nullable,
+ $is_pk, $is_unique, $is_key, $type;
+ function __construct ($info) {
+ $this->name = $info->name;
+ $this->tablename = $info->table;
+ $this->default = $info->def;
+ $this->max_length = $info->max_length;
+ $this->nullable = !$info->not_null;
+ $this->is_pk = $info->primary_key;
+ $this->is_unique = $info->unique_key;
+ $this->is_multiple = $info->multiple_key;
+ $this->is_key = ($this->is_pk || $this->is_unique || $this->is_multiple);
+ $this->type = $info->type;
+ }
+
+ function name() {
+ return $this->name;
+ }
+
+ function tableName() {
+ return $this->tableName;
+ }
+
+ function defaultValue() {
+ return $this->default;
+ }
+
+ function maxLength() {
+ return $this->max_length;
+ }
+
+ function nullable() {
+ return $this->nullable;
+ }
+
+ function isKey() {
+ return $this->is_key;
+ }
+
+ function isMultipleKey() {
+ return $this->is_multiple;
+ }
+
+ function type() {
+ return $this->type;
+ }
+}
+
/******************************************************************************
* Error classes
*****************************************************************************/
/**
* Database error base class
+ * @addtogroup Database
*/
class DBError extends MWException {
public $db;
@@ -53,6 +110,9 @@ class DBError extends MWException {
}
}
+/**
+ * @addtogroup Database
+ */
class DBConnectionError extends DBError {
public $error;
@@ -154,6 +214,7 @@ border=\"0\" ALT=\"Google\"></A>
$cache = new HTMLFileCache( $t );
if( $cache->isFileCached() ) {
+ // FIXME: $msg is not defined on the next line.
$msg = '<p style="color: red"><b>'.$msg."<br />\n" .
$cachederror . "</b></p>\n";
@@ -169,6 +230,9 @@ border=\"0\" ALT=\"Google\"></A>
}
}
+/**
+ * @addtogroup Database
+ */
class DBQueryError extends DBError {
public $error, $errno, $sql, $fname;
@@ -222,13 +286,16 @@ class DBQueryError extends DBError {
}
}
+/**
+ * @addtogroup Database
+ */
class DBUnexpectedError extends DBError {}
/******************************************************************************/
/**
* Database abstraction object
- * @package MediaWiki
+ * @addtogroup Database
*/
class Database {
@@ -247,9 +314,6 @@ class Database {
protected $mTrxLevel = 0;
protected $mErrorCount = 0;
protected $mLBInfo = array();
- protected $mCascadingDeletes = false;
- protected $mCleanupTriggers = false;
- protected $mStrictIPs = false;
#------------------------------------------------------------------------------
# Accessors
@@ -344,14 +408,14 @@ class Database {
* Returns true if this database supports (and uses) cascading deletes
*/
function cascadingDeletes() {
- return $this->mCascadingDeletes;
+ return false;
}
/**
* Returns true if this database supports (and uses) triggers (e.g. on the page table)
*/
function cleanupTriggers() {
- return $this->mCleanupTriggers;
+ return false;
}
/**
@@ -359,7 +423,7 @@ class Database {
* Specifically, it uses a NULL value instead of an empty string.
*/
function strictIPs() {
- return $this->mStrictIPs;
+ return false;
}
/**
@@ -376,6 +440,14 @@ class Database {
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';
+ */
+ function searchableIPs() {
+ return false;
+ }
+
/**#@+
* Get function
*/
@@ -407,13 +479,11 @@ class Database {
#------------------------------------------------------------------------------
/**@{{
+ * Constructor.
* @param string $server database server host
* @param string $user database user name
* @param string $password database user password
* @param string $dbname database name
- */
-
- /**
* @param failFunction
* @param $flags
* @param $tablePrefix String: database table prefixes. By default use the prefix gave in LocalSettings.php
@@ -463,8 +533,7 @@ class Database {
* @param failFunction
* @param $flags
*/
- static function newFromParams( $server, $user, $password, $dbName,
- $failFunction = false, $flags = 0 )
+ static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0 )
{
return new Database( $server, $user, $password, $dbName, $failFunction, $flags );
}
@@ -514,7 +583,7 @@ class Database {
}
if ($this->mConn === false) {
$iplus = $i + 1;
- wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
+ #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
}
}
@@ -541,12 +610,19 @@ class Database {
}
if ( $success ) {
- global $wgDBmysql5;
- if( $wgDBmysql5 ) {
+ $version = $this->getServerVersion();
+ if ( version_compare( $version, '4.1' ) >= 0 ) {
// Tell the server we're communicating with it in UTF-8.
// This may engage various charset conversions.
- $this->query( 'SET NAMES utf8' );
+ global $wgDBmysql5;
+ if( $wgDBmysql5 ) {
+ $this->query( 'SET NAMES utf8', __METHOD__ );
+ }
+ // Turn off strict mode
+ $this->query( "SET sql_mode = ''", __METHOD__ );
}
+
+ // Turn off strict mode if it is on
} else {
$this->reportConnectionError();
}
@@ -599,10 +675,15 @@ class Database {
}
/**
- * Usually aborts on failure
- * If errors are explicitly ignored, returns success
+ * 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
+ * @throws DBQueryError Thrown when the database returns an error of any kind
*/
- function query( $sql, $fname = '', $tempIgnore = false ) {
+ public function query( $sql, $fname = '', $tempIgnore = false ) {
global $wgProfiling;
if ( $wgProfiling ) {
@@ -626,11 +707,21 @@ class Database {
$this->mLastQuery = $sql;
# Add a comment for easy SHOW PROCESSLIST interpretation
- if ( $fname ) {
- $commentedSql = preg_replace('/\s/', " /* $fname */ ", $sql, 1);
- } else {
- $commentedSql = $sql;
- }
+ #if ( $fname ) {
+ global $wgUser;
+ if ( is_object( $wgUser ) && !($wgUser instanceof StubObject) ) {
+ $userName = $wgUser->getName();
+ if ( strlen( $userName ) > 15 ) {
+ $userName = substr( $userName, 0, 15 ) . '...';
+ }
+ $userName = str_replace( '/', '', $userName );
+ } else {
+ $userName = '';
+ }
+ $commentedSql = preg_replace('/\s/', " /* $fname $userName */ ", $sql, 1);
+ #} else {
+ # $commentedSql = $sql;
+ #}
# If DBO_TRX is set, start a transaction
if ( ( $this->mFlags & DBO_TRX ) && !$this->trxLevel() &&
@@ -655,6 +746,11 @@ class Database {
wfDebug( "Connection lost, reconnecting...\n" );
if ( $this->ping() ) {
wfDebug( "Reconnected\n" );
+ $sqlx = substr( $commentedSql, 0, 500 );
+ $sqlx = strtr( $sqlx, "\t\n", ' ' );
+ global $wgRequestTime;
+ $elapsed = round( microtime(true) - $wgRequestTime, 3 );
+ wfLogDBError( "Connection lost and reconnected after {$elapsed}s, query: $sqlx\n" );
$ret = $this->doQuery( $commentedSql );
} else {
wfDebug( "Failed\n" );
@@ -674,9 +770,11 @@ class Database {
/**
* The DBMS-dependent part of query()
- * @param string $sql SQL query.
+ * @param $sql String: SQL query.
+ * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure
+ * @access private
*/
- function doQuery( $sql ) {
+ /*private*/ function doQuery( $sql ) {
if( $this->bufferResults() ) {
$ret = mysql_query( $sql, $this->mConn );
} else {
@@ -817,7 +915,13 @@ class Database {
}
/**
- * Fetch the next row from the given result object, in object form
+ * 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( $res ) {
@/**/$row = mysql_fetch_object( $res );
@@ -828,8 +932,12 @@ class Database {
}
/**
- * Fetch the next row from the given result object
- * Returns an array
+ * 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( $res ) {
@/**/$row = mysql_fetch_array( $res );
@@ -972,7 +1080,7 @@ class Database {
* @return array
*/
function makeSelectOptions( $options ) {
- $tailOpts = '';
+ $preLimitTail = $postLimitTail = '';
$startOpts = '';
$noKeyOptions = array();
@@ -982,16 +1090,17 @@ class Database {
}
}
- if ( isset( $options['GROUP BY'] ) ) $tailOpts .= " GROUP BY {$options['GROUP BY']}";
- if ( isset( $options['ORDER BY'] ) ) $tailOpts .= " ORDER BY {$options['ORDER BY']}";
+ if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
+ if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
- if (isset($options['LIMIT'])) {
- $tailOpts .= $this->limitResult('', $options['LIMIT'],
- isset($options['OFFSET']) ? $options['OFFSET'] : false);
- }
-
- if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
- if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
+ //if (isset($options['LIMIT'])) {
+ // $tailOpts .= $this->limitResult('', $options['LIMIT'],
+ // isset($options['OFFSET']) ? $options['OFFSET']
+ // : false);
+ //}
+
+ 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';
# Various MySQL extensions
@@ -1010,7 +1119,7 @@ class Database {
$useIndex = '';
}
- return array( $startOpts, $useIndex, $tailOpts );
+ return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
}
/**
@@ -1038,20 +1147,33 @@ class Database {
else
$from = ' FROM ' . implode( ',', array_map( array( &$this, 'tableName' ), $table ) );
} elseif ($table!='') {
- $from = ' FROM ' . $this->tableName( $table );
+ if ($table{0}==' ') {
+ $from = ' FROM ' . $table;
+ } else {
+ $from = ' FROM ' . $this->tableName( $table );
+ }
} else {
$from = '';
}
- list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $options );
+ list( $startOpts, $useIndex, $preLimitTail, $postLimitTail ) = $this->makeSelectOptions( $options );
if( !empty( $conds ) ) {
if ( is_array( $conds ) ) {
$conds = $this->makeList( $conds, LIST_AND );
}
- $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $tailOpts";
+ $sql = "SELECT $startOpts $vars $from $useIndex WHERE $conds $preLimitTail";
} else {
- $sql = "SELECT $startOpts $vars $from $useIndex $tailOpts";
+ $sql = "SELECT $startOpts $vars $from $useIndex $preLimitTail";
+ }
+
+ if (isset($options['LIMIT']))
+ $sql = $this->limitResult($sql, $options['LIMIT'],
+ isset($options['OFFSET']) ? $options['OFFSET'] : false);
+ $sql = "$sql $postLimitTail";
+
+ if (isset($options['EXPLAIN'])) {
+ $sql = 'EXPLAIN ' . $sql;
}
return $this->query( $sql, $fname );
@@ -1085,6 +1207,33 @@ class Database {
return $obj;
}
+
+ /**
+ * Estimate rows in dataset
+ * Returns estimated count, based on EXPLAIN output
+ * Takes same arguments as Database::select()
+ */
+
+ function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+ $options['EXPLAIN']=true;
+ $res = $this->select ($table, $vars, $conds, $fname, $options );
+ if ( $res === false )
+ return false;
+ if (!$this->numRows($res)) {
+ $this->freeResult($res);
+ return 0;
+ }
+
+ $rows=1;
+
+ while( $plan = $this->fetchObject( $res ) ) {
+ $rows *= ($plan->rows > 0)?$plan->rows:1; // avoid resetting to zero
+ }
+
+ $this->freeResult($res);
+ return $rows;
+ }
+
/**
* Removes most variables from an SQL query and replaces them with X or N for numbers.
@@ -1207,7 +1356,7 @@ class Database {
for( $i = 0; $i < $n; $i++ ) {
$meta = mysql_fetch_field( $res, $i );
if( $field == $meta->name ) {
- return $meta;
+ return new MySQLField($meta);
}
}
return false;
@@ -1417,7 +1566,7 @@ class Database {
}
/**
- * @desc: Fetch a number of table names into an zero-indexed numerical array
+ * Fetch a number of table names into an zero-indexed numerical array
* This is handy when you need to construct SQL for joins
*
* Example:
@@ -1952,20 +2101,50 @@ class Database {
}
/**
+ * Override database's default connection timeout.
+ * May be useful for very long batch queries such as
+ * full-wiki dumps, where a single query reads out
+ * over hours or days.
+ * @param int $timeout in seconds
+ */
+ public function setTimeout( $timeout ) {
+ $this->query( "SET net_read_timeout=$timeout" );
+ $this->query( "SET net_write_timeout=$timeout" );
+ }
+
+ /**
* Read and execute SQL commands from a file.
* Returns true on success, error string on failure
+ * @param string $filename File name to open
+ * @param callback $lineCallback Optional function called before reading each line
+ * @param callback $resultCallback Optional function called for each MySQL result
*/
- function sourceFile( $filename ) {
+ function sourceFile( $filename, $lineCallback = false, $resultCallback = false ) {
$fp = fopen( $filename, 'r' );
if ( false === $fp ) {
return "Could not open \"{$filename}\".\n";
}
+ $error = $this->sourceStream( $fp, $lineCallback, $resultCallback );
+ fclose( $fp );
+ return $error;
+ }
+ /**
+ * Read and execute commands from an open file handle
+ * Returns true on success, error string on failure
+ * @param string $fp File handle
+ * @param callback $lineCallback Optional function called before reading each line
+ * @param callback $resultCallback Optional function called for each MySQL result
+ */
+ function sourceStream( $fp, $lineCallback = false, $resultCallback = false ) {
$cmd = "";
$done = false;
$dollarquote = false;
while ( ! feof( $fp ) ) {
+ if ( $lineCallback ) {
+ call_user_func( $lineCallback );
+ }
$line = trim( fgets( $fp, 1024 ) );
$sl = strlen( $line ) - 1;
@@ -1995,7 +2174,10 @@ class Database {
if ( $done ) {
$cmd = str_replace(';;', ";", $cmd);
$cmd = $this->replaceVars( $cmd );
- $res = $this->query( $cmd, 'dbsource', true );
+ $res = $this->query( $cmd, __METHOD__, true );
+ if ( $resultCallback ) {
+ call_user_func( $resultCallback, $this->resultObject( $res ) );
+ }
if ( false === $res ) {
$err = $this->lastError();
@@ -2006,10 +2188,10 @@ class Database {
$done = false;
}
}
- fclose( $fp );
return true;
}
+
/**
* Replace variables in sourced SQL
*/
@@ -2017,7 +2199,7 @@ class Database {
$varnames = array(
'wgDBserver', 'wgDBname', 'wgDBintlname', 'wgDBuser',
'wgDBpassword', 'wgDBsqluser', 'wgDBsqlpassword',
- 'wgDBadminuser', 'wgDBadminpassword',
+ 'wgDBadminuser', 'wgDBadminpassword', 'wgDBTableOptions',
);
// Ordinary variables
@@ -2050,7 +2232,7 @@ class Database {
* Database abstraction object for mySQL
* Inherit all methods and properties of Database::Database()
*
- * @package MediaWiki
+ * @addtogroup Database
* @see Database
*/
class DatabaseMysql extends Database {
@@ -2060,8 +2242,7 @@ class DatabaseMysql extends Database {
/**
* Result wrapper for grabbing data queried by someone else
- *
- * @package MediaWiki
+ * @addtogroup Database
*/
class ResultWrapper {
var $db, $result;
@@ -2107,6 +2288,12 @@ class ResultWrapper {
function seek( $row ) {
$this->db->dataSeek( $this->result, $row );
}
+
+ function rewind() {
+ if ($this->numRows()) {
+ $this->db->dataSeek($this->result, 0);
+ }
+ }
}
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
index ca83b9e5..4b31b4f0 100644
--- a/includes/DatabaseFunctions.php
+++ b/includes/DatabaseFunctions.php
@@ -3,7 +3,6 @@
* Legacy database functions, for compatibility with pre-1.3 code
* NOTE: this file is no longer loaded by default.
*
- * @package MediaWiki
*/
/**
@@ -18,7 +17,7 @@ function wfQuery( $sql, $db, $fname = '' ) {
# Someone has tried to call this the old way
throw new FatalError( wfMsgNoDB( 'wrong_wfQuery_params', $db, $sql ) );
}
- $c =& wfGetDB( $db );
+ $c = wfGetDB( $db );
if ( $c !== false ) {
return $c->query( $sql, $fname );
} else {
@@ -34,7 +33,7 @@ function wfQuery( $sql, $db, $fname = '' ) {
* @return Array: first row from the database
*/
function wfSingleQuery( $sql, $dbi, $fname = '' ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
$res = $db->query($sql, $fname );
$row = $db->fetchRow( $res );
$ret = $row[0];
@@ -54,7 +53,7 @@ function wfSingleQuery( $sql, $dbi, $fname = '' ) {
* @return Returns the previous state.
*/
function wfIgnoreSQLErrors( $newstate, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->ignoreErrors( $newstate );
} else {
@@ -73,7 +72,7 @@ function wfIgnoreSQLErrors( $newstate, $dbi = DB_LAST ) {
*/
function wfFreeResult( $res, $dbi = DB_LAST )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
$db->freeResult( $res );
return true;
@@ -87,7 +86,7 @@ function wfFreeResult( $res, $dbi = DB_LAST )
* @return object|false object we requested
*/
function wfFetchObject( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fetchObject( $res, $dbi = DB_LAST );
} else {
@@ -100,7 +99,7 @@ function wfFetchObject( $res, $dbi = DB_LAST ) {
* @return object|false row we requested
*/
function wfFetchRow( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fetchRow ( $res, $dbi = DB_LAST );
} else {
@@ -113,7 +112,7 @@ function wfFetchRow( $res, $dbi = DB_LAST ) {
* @return integer|false number of rows
*/
function wfNumRows( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->numRows( $res, $dbi = DB_LAST );
} else {
@@ -126,7 +125,7 @@ function wfNumRows( $res, $dbi = DB_LAST ) {
* @return integer|false number of fields
*/
function wfNumFields( $res, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->numFields( $res );
} else {
@@ -143,7 +142,7 @@ function wfNumFields( $res, $dbi = DB_LAST ) {
*/
function wfFieldName( $res, $n, $dbi = DB_LAST )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fieldName( $res, $n, $dbi = DB_LAST );
} else {
@@ -156,7 +155,7 @@ function wfFieldName( $res, $n, $dbi = DB_LAST )
* @todo document function
*/
function wfInsertId( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->insertId();
} else {
@@ -168,7 +167,7 @@ function wfInsertId( $dbi = DB_LAST ) {
* @todo document function
*/
function wfDataSeek( $res, $row, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->dataSeek( $res, $row );
} else {
@@ -180,7 +179,7 @@ function wfDataSeek( $res, $row, $dbi = DB_LAST ) {
* @todo document function
*/
function wfLastErrno( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->lastErrno();
} else {
@@ -192,7 +191,7 @@ function wfLastErrno( $dbi = DB_LAST ) {
* @todo document function
*/
function wfLastError( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->lastError();
} else {
@@ -204,7 +203,7 @@ function wfLastError( $dbi = DB_LAST ) {
* @todo document function
*/
function wfAffectedRows( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->affectedRows();
} else {
@@ -216,7 +215,7 @@ function wfAffectedRows( $dbi = DB_LAST ) {
* @todo document function
*/
function wfLastDBquery( $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->lastQuery();
} else {
@@ -235,7 +234,7 @@ function wfLastDBquery( $dbi = DB_LAST ) {
*/
function wfSetSQL( $table, $var, $value, $cond, $dbi = DB_MASTER )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->set( $table, $var, $value, $cond );
} else {
@@ -254,7 +253,7 @@ function wfSetSQL( $table, $var, $value, $cond, $dbi = DB_MASTER )
*/
function wfGetSQL( $table, $var, $cond='', $dbi = DB_LAST )
{
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->selectField( $table, $var, $cond );
} else {
@@ -271,7 +270,7 @@ function wfGetSQL( $table, $var, $cond='', $dbi = DB_LAST )
* @return Result of Database::fieldExists() or false.
*/
function wfFieldExists( $table, $field, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->fieldExists( $table, $field );
} else {
@@ -288,7 +287,7 @@ function wfFieldExists( $table, $field, $dbi = DB_LAST ) {
* @return Result of Database::indexExists() or false.
*/
function wfIndexExists( $table, $index, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->indexExists( $table, $index );
} else {
@@ -306,7 +305,7 @@ function wfIndexExists( $table, $index, $dbi = DB_LAST ) {
* @return result of Database::insert() or false.
*/
function wfInsertArray( $table, $array, $fname = 'wfInsertArray', $dbi = DB_MASTER ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->insert( $table, $array, $fname );
} else {
@@ -325,7 +324,7 @@ function wfInsertArray( $table, $array, $fname = 'wfInsertArray', $dbi = DB_MAST
* @return result of Database::getArray() or false.
*/
function wfGetArray( $table, $vars, $conds, $fname = 'wfGetArray', $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->getArray( $table, $vars, $conds, $fname );
} else {
@@ -344,7 +343,7 @@ function wfGetArray( $table, $vars, $conds, $fname = 'wfGetArray', $dbi = DB_LAS
* @todo document function
*/
function wfUpdateArray( $table, $values, $conds, $fname = 'wfUpdateArray', $dbi = DB_MASTER ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
$db->update( $table, $values, $conds, $fname );
return true;
@@ -357,7 +356,7 @@ function wfUpdateArray( $table, $values, $conds, $fname = 'wfUpdateArray', $dbi
* @todo document function
*/
function wfTableName( $name, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->tableName( $name );
} else {
@@ -369,7 +368,7 @@ function wfTableName( $name, $dbi = DB_LAST ) {
* @todo document function
*/
function wfStrencode( $s, $dbi = DB_LAST ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->strencode( $s );
} else {
@@ -381,7 +380,7 @@ function wfStrencode( $s, $dbi = DB_LAST ) {
* @todo document function
*/
function wfNextSequenceValue( $seqName, $dbi = DB_MASTER ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->nextSequenceValue( $seqName );
} else {
@@ -393,7 +392,7 @@ function wfNextSequenceValue( $seqName, $dbi = DB_MASTER ) {
* @todo document function
*/
function wfUseIndexClause( $index, $dbi = DB_SLAVE ) {
- $db =& wfGetDB( $dbi );
+ $db = wfGetDB( $dbi );
if ( $db !== false ) {
return $db->useIndexClause( $index );
} else {
diff --git a/includes/DatabaseOracle.php b/includes/DatabaseOracle.php
index 1a6f62f2..2b720df7 100644
--- a/includes/DatabaseOracle.php
+++ b/includes/DatabaseOracle.php
@@ -1,44 +1,141 @@
<?php
/**
- * Oracle.
- *
- * @package MediaWiki
+ * This is the Oracle database abstraction layer.
+ * @addtogroup Database
*/
+class ORABlob {
+ var $mData;
-class OracleBlob extends DBObject {
- function isLOB() {
- return true;
+ function __construct($data) {
+ $this->mData = $data;
}
- function data() {
+
+ function getData() {
return $this->mData;
}
-};
+}
+
+/**
+ * The oci8 extension is fairly weak and doesn't support oci_num_rows, among
+ * other things. We use a wrapper class to handle that and other
+ * Oracle-specific bits, like converting column names back to lowercase.
+ * @addtogroup Database
+ */
+class ORAResult {
+ private $rows;
+ private $cursor;
+ private $stmt;
+ private $nrows;
+ private $db;
+
+ function __construct(&$db, $stmt) {
+ $this->db =& $db;
+ if (($this->nrows = oci_fetch_all($stmt, $this->rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM)) === false) {
+ $e = oci_error($stmt);
+ $db->reportQueryError($e['message'], $e['code'], '', __FUNCTION__);
+ return;
+ }
+
+ $this->cursor = 0;
+ $this->stmt = $stmt;
+ }
+
+ function free() {
+ oci_free_statement($this->stmt);
+ }
+
+ function seek($row) {
+ $this->cursor = min($row, $this->nrows);
+ }
+
+ function numRows() {
+ return $this->nrows;
+ }
+
+ function numFields() {
+ return oci_num_fields($this->stmt);
+ }
+
+ function fetchObject() {
+ if ($this->cursor >= $this->nrows)
+ return false;
+
+ $row = $this->rows[$this->cursor++];
+ $ret = new stdClass();
+ foreach ($row as $k => $v) {
+ $lc = strtolower(oci_field_name($this->stmt, $k + 1));
+ $ret->$lc = $v;
+ }
+
+ return $ret;
+ }
+
+ function fetchAssoc() {
+ if ($this->cursor >= $this->nrows)
+ return false;
+
+ $row = $this->rows[$this->cursor++];
+ $ret = array();
+ foreach ($row as $k => $v) {
+ $lc = strtolower(oci_field_name($this->stmt, $k + 1));
+ $ret[$lc] = $v;
+ $ret[$k] = $v;
+ }
+ return $ret;
+ }
+}
/**
- *
- * @package MediaWiki
+ * @addtogroup Database
*/
class DatabaseOracle extends Database {
var $mInsertId = NULL;
var $mLastResult = NULL;
- var $mFetchCache = array();
- var $mFetchID = array();
- var $mNcols = array();
- var $mFieldNames = array(), $mFieldTypes = array();
- var $mAffectedRows = array();
- var $mErr;
+ var $numeric_version = NULL;
+ var $lastResult = null;
+ var $cursor = 0;
+ var $mAffectedRows;
function DatabaseOracle($server = false, $user = false, $password = false, $dbName = false,
- $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
+ $failFunction = false, $flags = 0 )
{
- Database::Database( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix );
+
+ global $wgOut;
+ # Can't get a reference if it hasn't been set yet
+ if ( !isset( $wgOut ) ) {
+ $wgOut = NULL;
+ }
+ $this->mOut =& $wgOut;
+ $this->mFailFunction = $failFunction;
+ $this->mFlags = $flags;
+ $this->open( $server, $user, $password, $dbName);
+
+ }
+
+ function cascadingDeletes() {
+ return true;
+ }
+ function cleanupTriggers() {
+ return true;
+ }
+ function strictIPs() {
+ return true;
+ }
+ function realTimestamps() {
+ return true;
+ }
+ function implicitGroupby() {
+ return false;
+ }
+ function searchableIPs() {
+ return true;
}
- /* static */ function newFromParams( $server = false, $user = false, $password = false, $dbName = false,
- $failFunction = false, $flags = 0, $tablePrefix = 'get from global' )
+ static function newFromParams( $server = false, $user = false, $password = false, $dbName = false,
+ $failFunction = false, $flags = 0)
{
- return new DatabaseOracle( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix );
+ return new DatabaseOracle( $server, $user, $password, $dbName, $failFunction, $flags );
}
/**
@@ -47,23 +144,33 @@ class DatabaseOracle extends Database {
*/
function open( $server, $user, $password, $dbName ) {
if ( !function_exists( 'oci_connect' ) ) {
- throw new DBConnectionError( $this, "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n" );
+ throw new DBConnectionError( $this, "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n (Note: if you recently installed PHP, you may need to restart your webserver and database)\n" );
}
+
+ # Needed for proper UTF-8 functionality
+ putenv("NLS_LANG=AMERICAN_AMERICA.AL32UTF8");
+
$this->close();
$this->mServer = $server;
$this->mUser = $user;
$this->mPassword = $password;
$this->mDBname = $dbName;
- $this->mConn = oci_new_connect($user, $password, $dbName, "AL32UTF8");
- if ( $this->mConn === false ) {
- wfDebug( "DB connection error\n" );
- wfDebug( "Server: $server, Database: $dbName, User: $user, Password: "
- . substr( $password, 0, 3 ) . "...\n" );
- wfDebug( $this->lastError()."\n" );
- } else {
- $this->mOpened = true;
+ if (!strlen($user)) { ## e.g. the class is being loaded
+ return;
+ }
+
+ error_reporting( E_ALL );
+ $this->mConn = oci_connect($user, $password, $dbName);
+
+ if ($this->mConn == false) {
+ wfDebug("DB connection error\n");
+ wfDebug("Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n");
+ wfDebug($this->lastError()."\n");
+ return false;
}
+
+ $this->mOpened = true;
return $this->mConn;
}
@@ -73,116 +180,67 @@ class DatabaseOracle extends Database {
*/
function close() {
$this->mOpened = false;
- if ($this->mConn) {
- return oci_close($this->mConn);
+ if ( $this->mConn ) {
+ return oci_close( $this->mConn );
} else {
return true;
}
}
- function parseStatement($sql) {
- $this->mErr = $this->mLastResult = false;
- if (($stmt = oci_parse($this->mConn, $sql)) === false) {
- $this->lastError();
- return $this->mLastResult = false;
- }
- $this->mAffectedRows[$stmt] = 0;
- return $this->mLastResult = $stmt;
+ function execFlags() {
+ return $this->mTrxLevel ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
}
function doQuery($sql) {
- if (($stmt = $this->parseStatement($sql)) === false)
- return false;
- return $this->executeStatement($stmt);
- }
+ wfDebug("SQL: [$sql]\n");
+ if (!mb_check_encoding($sql)) {
+ throw new MWException("SQL encoding is invalid");
+ }
- function executeStatement($stmt) {
- if (!oci_execute($stmt, OCI_DEFAULT)) {
- $this->lastError();
- oci_free_statement($stmt);
- return false;
+ if (($this->mLastResult = $stmt = oci_parse($this->mConn, $sql)) === false) {
+ $e = oci_error($this->mConn);
+ $this->reportQueryError($e['message'], $e['code'], $sql, __FUNCTION__);
}
- $this->mAffectedRows[$stmt] = oci_num_rows($stmt);
- $this->mFetchCache[$stmt] = array();
- $this->mFetchID[$stmt] = 0;
- $this->mNcols[$stmt] = oci_num_fields($stmt);
- if ($this->mNcols[$stmt] == 0)
- return $this->mLastResult;
- for ($i = 1; $i <= $this->mNcols[$stmt]; $i++) {
- $this->mFieldNames[$stmt][$i] = oci_field_name($stmt, $i);
- $this->mFieldTypes[$stmt][$i] = oci_field_type($stmt, $i);
+
+ if (oci_execute($stmt, $this->execFlags()) == false) {
+ $e = oci_error($stmt);
+ $this->reportQueryError($e['message'], $e['code'], $sql, __FUNCTION__);
}
- while (($o = oci_fetch_array($stmt)) !== false) {
- foreach ($o as $key => $value) {
- if (is_object($value)) {
- $o[$key] = $value->load();
- }
- }
- $this->mFetchCache[$stmt][] = $o;
+ if (oci_statement_type($stmt) == "SELECT")
+ return new ORAResult($this, $stmt);
+ else {
+ $this->mAffectedRows = oci_num_rows($stmt);
+ return true;
}
- return $this->mLastResult;
}
- function queryIgnore( $sql, $fname = '' ) {
- return $this->query( $sql, $fname, true );
+ function queryIgnore($sql, $fname = '') {
+ return $this->query($sql, $fname, true);
}
- function freeResult( $res ) {
- if (!oci_free_statement($res)) {
- throw new DBUnexpectedError( $this, "Unable to free Oracle result\n" );
- }
- unset($this->mFetchID[$res]);
- unset($this->mFetchCache[$res]);
- unset($this->mNcols[$res]);
- unset($this->mFieldNames[$res]);
- unset($this->mFieldTypes[$res]);
+ function freeResult($res) {
+ $res->free();
}
- function fetchAssoc($res) {
- if ($this->mFetchID[$res] >= count($this->mFetchCache[$res]))
- return false;
-
- for ($i = 1; $i <= $this->mNcols[$res]; $i++) {
- $name = $this->mFieldNames[$res][$i];
- if (isset($this->mFetchCache[$res][$this->mFetchID[$res]][$name]))
- $value = $this->mFetchCache[$res][$this->mFetchID[$res]][$name];
- else $value = NULL;
- $key = strtolower($name);
- wfdebug("'$key' => '$value'\n");
- $ret[$key] = $value;
- }
- $this->mFetchID[$res]++;
- return $ret;
+ function fetchObject($res) {
+ return $res->fetchObject();
}
function fetchRow($res) {
- $r = $this->fetchAssoc($res);
- if (!$r)
- return false;
- $i = 0;
- $ret = array();
- foreach ($r as $value) {
- wfdebug("ret[$i]=[$value]\n");
- $ret[$i++] = $value;
- }
- return $ret;
+ return $res->fetchAssoc();
}
- function fetchObject($res) {
- $row = $this->fetchAssoc($res);
- if (!$row)
- return false;
- $ret = new stdClass;
- foreach ($row as $key => $value)
- $ret->$key = $value;
- return $ret;
+ function numRows($res) {
+ return $res->numRows();
}
- function numRows($res) {
- return count($this->mFetchCache[$res]);
+ function numFields($res) {
+ return $res->numFields();
+ }
+
+ function fieldName($stmt, $n) {
+ return pg_field_name($stmt, $n);
}
- function numFields( $res ) { return pg_num_fields( $res ); }
- function fieldName( $res, $n ) { return pg_field_name( $res, $n ); }
/**
* This must be called after nextSequenceVal
@@ -192,139 +250,153 @@ class DatabaseOracle extends Database {
}
function dataSeek($res, $row) {
- $this->mFetchID[$res] = $row;
+ $res->seek($row);
}
function lastError() {
- if ($this->mErr === false) {
- if ($this->mLastResult !== false) {
- $what = $this->mLastResult;
- } else if ($this->mConn !== false) {
- $what = $this->mConn;
- } else {
- $what = false;
- }
- $err = ($what !== false) ? oci_error($what) : oci_error();
- if ($err === false) {
- $this->mErr = 'no error';
- } else {
- $this->mErr = $err['message'];
- }
- }
- return str_replace("\n", '<br />', $this->mErr);
+ if ($this->mConn === false)
+ $e = oci_error();
+ else
+ $e = oci_error($this->mConn);
+ return $e['message'];
}
+
function lastErrno() {
- return 0;
+ if ($this->mConn === false)
+ $e = oci_error();
+ else
+ $e = oci_error($this->mConn);
+ return $e['code'];
}
function affectedRows() {
- return $this->mAffectedRows[$this->mLastResult];
+ return $this->mAffectedRows;
}
/**
* Returns information about an index
* If errors are explicitly ignored, returns NULL on failure
*/
- function indexInfo ($table, $index, $fname = 'Database::indexInfo' ) {
- $table = $this->tableName($table, true);
- if ($index == 'PRIMARY')
- $index = "${table}_pk";
- $sql = "SELECT uniqueness FROM all_indexes WHERE table_name='" .
- $table . "' AND index_name='" .
- $this->strencode(strtoupper($index)) . "'";
- $res = $this->query($sql, $fname);
- if (!$res)
- return NULL;
- if (($row = $this->fetchObject($res)) == NULL)
- return false;
- $this->freeResult($res);
- $row->Non_unique = !$row->uniqueness;
- return $row;
-
- // BUG: !!!! This code needs to be synced up with database.php
-
+ function indexInfo( $table, $index, $fname = 'Database::indexExists' ) {
+ return false;
}
- function indexUnique ($table, $index, $fname = 'indexUnique') {
- if (!($i = $this->indexInfo($table, $index, $fname)))
- return $i;
- return $i->uniqueness == 'UNIQUE';
+ function indexUnique ($table, $index, $fname = 'Database::indexUnique' ) {
+ return false;
}
- function fieldInfo( $table, $field ) {
- $o = new stdClass;
- $o->multiple_key = true; /* XXX */
- return $o;
- }
+ function insert( $table, $a, $fname = 'Database::insert', $options = array() ) {
+ if (!is_array($options))
+ $options = array($options);
- function getColumnInformation($table, $field) {
- $table = $this->tableName($table, true);
- $field = strtoupper($field);
+ #if (in_array('IGNORE', $options))
+ # $oldIgnore = $this->ignoreErrors(true);
- $res = $this->doQuery("SELECT * FROM all_tab_columns " .
- "WHERE table_name='".$table."' " .
- "AND column_name='".$field."'");
- if (!$res)
- return false;
- $o = $this->fetchObject($res);
- $this->freeResult($res);
- return $o;
- }
+ # IGNORE is performed using single-row inserts, ignoring errors in each
+ # FIXME: need some way to distiguish between key collision and other types of error
+ //$oldIgnore = $this->ignoreErrors(true);
+ if (!is_array(reset($a))) {
+ $a = array($a);
+ }
+ foreach ($a as $row) {
+ $this->insertOneRow($table, $row, $fname);
+ }
+ //$this->ignoreErrors($oldIgnore);
+ $retVal = true;
- function fieldExists( $table, $field, $fname = 'Database::fieldExists' ) {
- $column = $this->getColumnInformation($table, $field);
- if (!$column)
- return false;
- return true;
+ //if (in_array('IGNORE', $options))
+ // $this->ignoreErrors($oldIgnore);
+
+ return $retVal;
}
- function tableName($name, $forddl = false) {
- # First run any transformations from the parent object
- $name = parent::tableName( $name );
+ function insertOneRow($table, $row, $fname) {
+ // "INSERT INTO tables (a, b, c)"
+ $sql = "INSERT INTO " . $this->tableName($table) . " (" . join(',', array_keys($row)) . ')';
+ $sql .= " VALUES (";
+
+ // for each value, append ":key"
+ $first = true;
+ $returning = '';
+ foreach ($row as $col => $val) {
+ if (is_object($val)) {
+ $what = "EMPTY_BLOB()";
+ assert($returning === '');
+ $returning = " RETURNING $col INTO :bval";
+ $blobcol = $col;
+ } else
+ $what = ":$col";
+
+ if ($first)
+ $sql .= "$what";
+ else
+ $sql.= ", $what";
+ $first = false;
+ }
+ $sql .= ") $returning";
+
+ $stmt = oci_parse($this->mConn, $sql);
+ foreach ($row as $col => $val) {
+ if (!is_object($val)) {
+ if (oci_bind_by_name($stmt, ":$col", $row[$col]) === false)
+ $this->reportQueryError($this->lastErrno(), $this->lastError(), $sql, __METHOD__);
+ }
+ }
+
+ if (($bval = oci_new_descriptor($this->mConn, OCI_D_LOB)) === false) {
+ $e = oci_error($stmt);
+ throw new DBUnexpectedError($this, "Cannot create LOB descriptor: " . $e['message']);
+ }
+
+ if (strlen($returning))
+ oci_bind_by_name($stmt, ":bval", $bval, -1, SQLT_BLOB);
- # Replace backticks into empty
- # Note: "foo" and foo are not the same in Oracle!
- $name = str_replace('`', '', $name);
+ if (oci_execute($stmt, OCI_DEFAULT) === false) {
+ $e = oci_error($stmt);
+ $this->reportQueryError($e['message'], $e['code'], $sql, __METHOD__);
+ }
+ if (strlen($returning)) {
+ $bval->save($row[$blobcol]->getData());
+ $bval->free();
+ }
+ if (!$this->mTrxLevel)
+ oci_commit($this->mConn);
- # Now quote Oracle reserved keywords
+ oci_free_statement($stmt);
+ }
+
+ function tableName( $name ) {
+ # Replace reserved words with better ones
switch( $name ) {
case 'user':
- case 'group':
- case 'validate':
- if ($forddl)
- return $name;
- else
- return '"' . $name . '"';
-
+ return 'mwuser';
+ case 'text':
+ return 'pagecontent';
default:
- return strtoupper($name);
+ return $name;
}
}
- function strencode( $s ) {
- return str_replace("'", "''", $s);
- }
-
/**
* Return the next in a sequence, save the value for retrieval via insertId()
*/
- function nextSequenceValue( $seqName ) {
- $r = $this->doQuery("SELECT $seqName.nextval AS val FROM dual");
- $o = $this->fetchObject($r);
- $this->freeResult($r);
- return $this->mInsertId = (int)$o->val;
+ function nextSequenceValue($seqName) {
+ $res = $this->query("SELECT $seqName.nextval FROM dual");
+ $row = $this->fetchRow($res);
+ $this->mInsertId = $row[0];
+ $this->freeResult($res);
+ return $this->mInsertId;
}
/**
- * USE INDEX clause
- * PostgreSQL doesn't have them and returns ""
+ * Oracle does not have a "USE INDEX" clause, so return an empty string
*/
- function useIndexClause( $index ) {
+ function useIndexClause($index) {
return '';
}
# REPLACE query wrapper
- # PostgreSQL simulates this with a DELETE followed by INSERT
+ # Oracle simulates this with a DELETE followed by INSERT
# $row is the row to insert, an associative array
# $uniqueIndexes is an array of indexes. Each element may be either a
# field name or an array of field names
@@ -333,15 +405,15 @@ class DatabaseOracle extends Database {
# However if you do this, you run the risk of encountering errors which wouldn't have
# occurred in MySQL
function replace( $table, $uniqueIndexes, $rows, $fname = 'Database::replace' ) {
- $table = $this->tableName( $table );
+ $table = $this->tableName($table);
if (count($rows)==0) {
return;
}
# Single row case
- if ( !is_array( reset( $rows ) ) ) {
- $rows = array( $rows );
+ if (!is_array(reset($rows))) {
+ $rows = array($rows);
}
foreach( $rows as $row ) {
@@ -377,14 +449,14 @@ class DatabaseOracle extends Database {
# Now insert the row
$sql = "INSERT INTO $table (" . $this->makeList( array_keys( $row ), LIST_NAMES ) .') VALUES (' .
$this->makeList( $row, LIST_COMMA ) . ')';
- $this->query( $sql, $fname );
+ $this->query($sql, $fname);
}
}
# DELETE where the condition is a join
function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) {
if ( !$conds ) {
- throw new DBUnexpectedError( $this, 'Database::deleteJoin() called with empty $conds' );
+ throw new DBUnexpectedError($this, 'Database::deleteJoin() called with empty $conds' );
}
$delTable = $this->tableName( $delTable );
@@ -421,17 +493,14 @@ class DatabaseOracle extends Database {
}
function limitResult($sql, $limit, $offset) {
- $ret = "SELECT * FROM ($sql) WHERE ROWNUM < " . ((int)$limit + (int)($offset+1));
- if (is_numeric($offset))
- $ret .= " AND ROWNUM >= " . (int)$offset;
- return $ret;
- }
- function limitResultForUpdate($sql, $limit) {
- return $sql;
+ if ($offset === false)
+ $offset = 0;
+ return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < 1 + $limit + $offset";
}
+
/**
* Returns an SQL expression for a simple conditional.
- * Uses CASE on PostgreSQL.
+ * Uses CASE on Oracle
*
* @param string $cond SQL expression which will result in a boolean value
* @param string $trueVal SQL expression to return if true
@@ -442,15 +511,12 @@ class DatabaseOracle extends Database {
return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
}
- # FIXME: actually detecting deadlocks might be nice
function wasDeadlock() {
- return false;
+ return $this->lastErrno() == 'OCI-00060';
}
- # Return DB-style timestamp used for MySQL schema
function timestamp($ts = 0) {
- return $this->strencode(wfTimestamp(TS_ORACLE, $ts));
-# return "TO_TIMESTAMP('" . $this->strencode(wfTimestamp(TS_DB, $ts)) . "', 'RRRR-MM-DD HH24:MI:SS')";
+ return wfTimestamp(TS_ORACLE, $ts);
}
/**
@@ -460,13 +526,25 @@ class DatabaseOracle extends Database {
return $valuedata;
}
+ function reportQueryError($error, $errno, $sql, $fname, $tempIgnore = false) {
+ # Ignore errors during error handling to avoid infinite
+ # recursion
+ $ignore = $this->ignoreErrors(true);
+ ++$this->mErrorCount;
- function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
- $message = "A database error has occurred\n" .
- "Query: $sql\n" .
- "Function: $fname\n" .
- "Error: $errno $error\n";
- throw new DBUnexpectedError($this, $message);
+ if ($ignore || $tempIgnore) {
+echo "error ignored! query = [$sql]\n";
+ wfDebug("SQL ERROR (ignored): $error\n");
+ $this->ignoreErrors( $ignore );
+ }
+ else {
+echo "error!\n";
+ $message = "A database error has occurred\n" .
+ "Query: $sql\n" .
+ "Function: $fname\n" .
+ "Error: $errno $error\n";
+ throw new DBUnexpectedError($this, $message);
+ }
}
/**
@@ -483,209 +561,125 @@ class DatabaseOracle extends Database {
return oci_server_version($this->mConn);
}
- function setSchema($schema=false) {
- $schemas=$this->mSchemas;
- if ($schema) { array_unshift($schemas,$schema); }
- $searchpath=$this->makeList($schemas,LIST_NAMES);
- $this->query("SET search_path = $searchpath");
+ /**
+ * Query whether a given table exists (in the given schema, or the default mw one if not given)
+ */
+ function tableExists($table) {
+ $etable= $this->addQuotes($table);
+ $SQL = "SELECT 1 FROM user_tables WHERE table_name='$etable'";
+ $res = $this->query($SQL);
+ $count = $res ? oci_num_rows($res) : 0;
+ if ($res)
+ $this->freeResult($res);
+ return $count;
}
- function begin() {
+ /**
+ * Query whether a given column exists in the mediawiki schema
+ */
+ function fieldExists( $table, $field ) {
+ return true; // XXX
}
- function immediateCommit( $fname = 'Database::immediateCommit' ) {
- oci_commit($this->mConn);
- $this->mTrxLevel = 0;
+ function fieldInfo( $table, $field ) {
+ return false; // XXX
}
- function rollback( $fname = 'Database::rollback' ) {
- oci_rollback($this->mConn);
- $this->mTrxLevel = 0;
+
+ function begin( $fname = '' ) {
+ $this->mTrxLevel = 1;
}
- function getLag() {
- return false;
+ function immediateCommit( $fname = '' ) {
+ return true;
}
- function getStatus($which=null) {
- $result = array('Threads_running' => 0, 'Threads_connected' => 0);
- return $result;
+ function commit( $fname = '' ) {
+ oci_commit($this->mConn);
+ $this->mTrxLevel = 0;
}
- /**
- * Returns an optional USE INDEX clause to go after the table, and a
- * string to go at the end of the query
- *
- * @access private
- *
- * @param array $options an associative array of options to be turned into
- * an SQL query, valid keys are listed in the function.
- * @return array
- */
- function makeSelectOptions($options) {
- $tailOpts = '';
-
- if (isset( $options['ORDER BY'])) {
- $tailOpts .= " ORDER BY {$options['ORDER BY']}";
- }
+ /* Not even sure why this is used in the main codebase... */
+ function limitResultForUpdate($sql, $num) {
+ return $sql;
+ }
- return array('', $tailOpts);
+ function strencode($s) {
+ return str_replace("'", "''", $s);
}
- function maxListLen() {
- return 1000;
+ function encodeBlob($b) {
+ return new ORABlob($b);
+ }
+ function decodeBlob($b) {
+ return $b; //return $b->load();
}
- /**
- * Query whether a given table exists
- */
- function tableExists( $table ) {
- $table = $this->tableName($table, true);
- $res = $this->query( "SELECT COUNT(*) as NUM FROM user_tables WHERE table_name='"
- . $table . "'" );
- if (!$res)
- return false;
- $row = $this->fetchObject($res);
- $this->freeResult($res);
- return $row->num >= 1;
+ function addQuotes( $s ) {
+ global $wgLang;
+ $s = $wgLang->checkTitleEncoding($s);
+ return "'" . $this->strencode($s) . "'";
}
- /**
- * UPDATE wrapper, takes a condition array and a SET array
- */
- function update( $table, $values, $conds, $fname = 'Database::update' ) {
- $table = $this->tableName( $table );
+ function quote_ident( $s ) {
+ return $s;
+ }
- $sql = "UPDATE $table SET ";
- $first = true;
- foreach ($values as $field => $v) {
- if ($first)
- $first = false;
- else
- $sql .= ", ";
- $sql .= "$field = :n$field ";
- }
- if ( $conds != '*' ) {
- $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
- }
- $stmt = $this->parseStatement($sql);
- if ($stmt === false) {
- $this->reportQueryError( $this->lastError(), $this->lastErrno(), $stmt );
- return false;
- }
- if ($this->debug())
- wfDebug("SQL: $sql\n");
- $s = '';
- foreach ($values as $field => $v) {
- oci_bind_by_name($stmt, ":n$field", $values[$field]);
- if ($this->debug())
- $s .= " [$field] = [$v]\n";
- }
- if ($this->debug())
- wfdebug(" PH: $s\n");
- $ret = $this->executeStatement($stmt);
- return $ret;
+ /* For now, does nothing */
+ function selectDB( $db ) {
+ return true;
}
/**
- * INSERT wrapper, inserts an array into a table
+ * Returns an optional USE INDEX clause to go after the table, and a
+ * string to go at the end of the query
*
- * $a may be a single associative array, or an array of these with numeric keys, for
- * multi-row insert.
+ * @private
*
- * Usually aborts on failure
- * If errors are explicitly ignored, returns success
+ * @param array $options an associative array of options to be turned into
+ * an SQL query, valid keys are listed in the function.
+ * @return array
*/
- function insert( $table, $a, $fname = 'Database::insert', $options = array() ) {
- # No rows to insert, easy just return now
- if ( !count( $a ) ) {
- return true;
- }
-
- $table = $this->tableName( $table );
- if (!is_array($options))
- $options = array($options);
-
- $oldIgnore = false;
- if (in_array('IGNORE', $options))
- $oldIgnore = $this->ignoreErrors( true );
-
- if ( isset( $a[0] ) && is_array( $a[0] ) ) {
- $multi = true;
- $keys = array_keys( $a[0] );
- } else {
- $multi = false;
- $keys = array_keys( $a );
+ function makeSelectOptions( $options ) {
+ $preLimitTail = $postLimitTail = '';
+ $startOpts = '';
+
+ $noKeyOptions = array();
+ foreach ( $options as $key => $option ) {
+ if ( is_numeric( $key ) ) {
+ $noKeyOptions[$option] = true;
+ }
}
- $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES (';
- $return = '';
- $first = true;
- foreach ($a as $key => $value) {
- if ($first)
- $first = false;
- else
- $sql .= ", ";
- if (is_object($value) && $value->isLOB()) {
- $sql .= "EMPTY_BLOB()";
- $return = "RETURNING $key INTO :bobj";
- } else
- $sql .= ":$key";
+ if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
+ if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
+
+ if (isset($options['LIMIT'])) {
+ // $tailOpts .= $this->limitResult('', $options['LIMIT'],
+ // isset($options['OFFSET']) ? $options['OFFSET']
+ // : false);
}
- $sql .= ") $return";
- if ($this->debug()) {
- wfDebug("SQL: $sql\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 (($stmt = $this->parseStatement($sql)) === false) {
- $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname);
- $this->ignoreErrors($oldIgnore);
- return false;
+ if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
+ $useIndex = $this->useIndexClause( $options['USE INDEX'] );
+ } else {
+ $useIndex = '';
}
+
+ return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
+ }
- /*
- * If we're inserting multiple rows, parse the statement once and
- * execute it for each set of values. Otherwise, convert it into an
- * array and pretend.
- */
- if (!$multi)
- $a = array($a);
-
- foreach ($a as $key => $row) {
- $blob = false;
- $bdata = false;
- $s = '';
- foreach ($row as $k => $value) {
- if (is_object($value) && $value->isLOB()) {
- $blob = oci_new_descriptor($this->mConn, OCI_D_LOB);
- $bdata = $value->data();
- oci_bind_by_name($stmt, ":bobj", $blob, -1, OCI_B_BLOB);
- } else
- oci_bind_by_name($stmt, ":$k", $a[$key][$k], -1);
- if ($this->debug())
- $s .= " [$k] = {$row[$k]}";
- }
- if ($this->debug())
- wfDebug(" PH: $s\n");
- if (($s = $this->executeStatement($stmt)) === false) {
- $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname);
- $this->ignoreErrors($oldIgnore);
- return false;
- }
-
- if ($blob) {
- $blob->save($bdata);
- }
- }
- $this->ignoreErrors($oldIgnore);
- return $this->mLastResult = $s;
+ public function setTimeout( $timeout ) {
+ // @todo fixme no-op
}
function ping() {
+ wfDebug( "Function ping() not written for DatabasePostgres.php yet");
return true;
}
- function encodeBlob($b) {
- return new OracleBlob($b);
- }
-}
+
+} // end DatabaseOracle class
?>
diff --git a/includes/DatabasePostgres.php b/includes/DatabasePostgres.php
index 803c0e26..7158e2d1 100644
--- a/includes/DatabasePostgres.php
+++ b/includes/DatabasePostgres.php
@@ -7,12 +7,69 @@
* than MySQL ones, some of them should be moved to parent
* Database class.
*
- * @package MediaWiki
+ * @addtogroup Database
*/
+class PostgresField {
+ private $name, $tablename, $type, $nullable, $max_length;
+
+ static function fromText($db, $table, $field) {
+ global $wgDBmwschema;
+
+ $q = <<<END
+SELECT typname, attnotnull, attlen
+FROM pg_class, pg_namespace, pg_attribute, pg_type
+WHERE relnamespace=pg_namespace.oid
+AND relkind='r'
+AND attrelid=pg_class.oid
+AND atttypid=pg_type.oid
+AND nspname=%s
+AND relname=%s
+AND attname=%s;
+END;
+ $res = $db->query(sprintf($q,
+ $db->addQuotes($wgDBmwschema),
+ $db->addQuotes($table),
+ $db->addQuotes($field)));
+ $row = $db->fetchObject($res);
+ if (!$row)
+ return null;
+ $n = new PostgresField;
+ $n->type = $row->typname;
+ $n->nullable = ($row->attnotnull == 'f');
+ $n->name = $field;
+ $n->tablename = $table;
+ $n->max_length = $row->attlen;
+ return $n;
+ }
+
+ function name() {
+ return $this->name;
+ }
+
+ function tableName() {
+ return $this->tablename;
+ }
+
+ function type() {
+ return $this->type;
+ }
+
+ function nullable() {
+ return $this->nullable;
+ }
+ function maxLength() {
+ return $this->max_length;
+ }
+}
+
+/**
+ * @addtogroup Database
+ */
class DatabasePostgres extends Database {
var $mInsertId = NULL;
var $mLastResult = NULL;
+ var $numeric_version = NULL;
function DatabasePostgres($server = false, $user = false, $password = false, $dbName = false,
$failFunction = false, $flags = 0 )
@@ -25,24 +82,31 @@ class DatabasePostgres extends Database {
}
$this->mOut =& $wgOut;
$this->mFailFunction = $failFunction;
- $this->mCascadingDeletes = true;
- $this->mCleanupTriggers = true;
- $this->mStrictIPs = true;
$this->mFlags = $flags;
$this->open( $server, $user, $password, $dbName);
}
+ function cascadingDeletes() {
+ return true;
+ }
+ function cleanupTriggers() {
+ return true;
+ }
+ function strictIPs() {
+ return true;
+ }
function realTimestamps() {
return true;
}
-
function implicitGroupby() {
return false;
}
+ function searchableIPs() {
+ return true;
+ }
- static function newFromParams( $server = false, $user = false, $password = false, $dbName = false,
- $failFunction = false, $flags = 0)
+ static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0)
{
return new DatabasePostgres( $server, $user, $password, $dbName, $failFunction, $flags );
}
@@ -57,9 +121,12 @@ class DatabasePostgres extends Database {
throw new DBConnectionError( $this, "Postgres functions missing, have you compiled PHP with the --with-pgsql option?\n (Note: if you recently installed PHP, you may need to restart your webserver and database)\n" );
}
-
global $wgDBport;
+ if (!strlen($user)) { ## e.g. the class is being loaded
+ return;
+ }
+
$this->close();
$this->mServer = $server;
$port = $wgDBport;
@@ -75,9 +142,6 @@ class DatabasePostgres extends Database {
$hstring .= "port=$port ";
}
- if (!strlen($user)) { ## e.g. the class is being loaded
- return;
- }
error_reporting( E_ALL );
@$this->mConn = pg_connect("$hstring dbname=$dbName user=$user password=$password");
@@ -94,21 +158,15 @@ class DatabasePostgres extends Database {
if (defined('MEDIAWIKI_INSTALL')) {
global $wgDBname, $wgDBuser, $wgDBpassword, $wgDBsuperuser, $wgDBmwschema,
$wgDBts2schema;
- print "OK</li>\n";
print "<li>Checking the version of Postgres...";
- $version = pg_fetch_result($this->doQuery("SELECT version()"),0,0);
- $thisver = array();
- if (!preg_match('/PostgreSQL (\d+\.\d+)(\S+)/', $version, $thisver)) {
- print "<b>FAILED</b> (could not determine the version)</li>\n";
- dieout("</ul>");
- }
+ $version = $this->getServerVersion();
$PGMINVER = "8.1";
- if ($thisver[1] < $PGMINVER) {
- print "<b>FAILED</b>. Required version is $PGMINVER. You have $thisver[1]$thisver[2]</li>\n";
+ if ($this->numeric_version < $PGMINVER) {
+ print "<b>FAILED</b>. Required version is $PGMINVER. You have $this->numeric_version ($version)</li>\n";
dieout("</ul>");
}
- print "version $thisver[1]$thisver[2] is OK.</li>\n";
+ print "version $this->numeric_version is OK.</li>\n";
$safeuser = $this->quote_ident($wgDBuser);
## Are we connecting as a superuser for the first time?
@@ -232,7 +290,8 @@ class DatabasePostgres extends Database {
$wgDBsuperuser = '';
return true; ## Reconnect as regular user
- }
+
+ } ## end superuser
if (!defined('POSTGRES_SEARCHPATH')) {
@@ -249,13 +308,24 @@ class DatabasePostgres extends Database {
## Does this user have the rights to the tsearch2 tables?
$ctype = pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0);
print "<li>Checking tsearch2 permissions...";
+ ## Let's check all four, just to be safe
+ error_reporting( 0 );
+ $ts2tables = array('cfg','cfgmap','dict','parser');
+ foreach ( $ts2tables AS $tname ) {
+ $SQL = "SELECT count(*) FROM $wgDBts2schema.pg_ts_$tname";
+ $res = $this->doQuery($SQL);
+ if (!$res) {
+ print "<b>FAILED</b> to access pg_ts_$tname. Make sure that the user ".
+ "\"$wgDBuser\" has SELECT access to all four tsearch2 tables</li>\n";
+ dieout("</ul>");
+ }
+ }
$SQL = "SELECT ts_name FROM $wgDBts2schema.pg_ts_cfg WHERE locale = '$ctype'";
$SQL .= " ORDER BY CASE WHEN ts_name <> 'default' THEN 1 ELSE 0 END";
- error_reporting( 0 );
$res = $this->doQuery($SQL);
error_reporting( E_ALL );
if (!$res) {
- print "<b>FAILED</b>. Make sure that the user \"$wgDBuser\" has SELECT access to the tsearch2 tables</li>\n";
+ print "<b>FAILED</b>. Could not determine the tsearch2 locale information</li>\n";
dieout("</ul>");
}
print "OK</li>";
@@ -282,7 +352,7 @@ class DatabasePostgres extends Database {
$res = $this->doQuery($SQL);
if (!$res) {
print "<b>FAILED</b>. ";
- print "Please make sure that the locale in pg_ts_cfg for \"default\" is set to \"ctype\"</li>\n";
+ print "Please make sure that the locale in pg_ts_cfg for \"default\" is set to \"$ctype\"</li>\n";
dieout("</ul>");
}
print "OK</li>";
@@ -325,9 +395,13 @@ class DatabasePostgres extends Database {
$result = $this->schemaExists($wgDBmwschema);
if (!$result) {
print "<li>Creating schema <b>$wgDBmwschema</b> ...";
+ error_reporting( 0 );
$result = $this->doQuery("CREATE SCHEMA $wgDBmwschema");
+ error_reporting( E_ALL );
if (!$result) {
- print "<b>FAILED</b>.</li>\n";
+ print "<b>FAILED</b>. The user \"$wgDBuser\" must be able to access the schema. ".
+ "You can try making them the owner of the database, or try creating the schema with a ".
+ "different user, and then grant access to the \"$wgDBuser\" user.</li>\n";
dieout("</ul>");
}
print "OK</li>\n";
@@ -339,6 +413,39 @@ class DatabasePostgres extends Database {
print "<li>Schema \"$wgDBmwschema\" exists and is owned by \"$user\". Excellent.</li>\n";
}
+ ## Always return GMT time to accomodate the existing integer-based timestamp assumption
+ print "<li>Setting the timezone to GMT for user \"$user\" ...";
+ $SQL = "ALTER USER $safeuser SET timezone = 'GMT'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<b>FAILED</b>.</li>\n";
+ dieout("</ul>");
+ }
+ print "OK</li>\n";
+ ## Set for the rest of this session
+ $SQL = "SET timezone = 'GMT'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<li>Failed to set timezone</li>\n";
+ dieout("</ul>");
+ }
+
+ print "<li>Setting the datestyle to ISO, YMD for user \"$user\" ...";
+ $SQL = "ALTER USER $safeuser SET datestyle = 'ISO, YMD'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<b>FAILED</b>.</li>\n";
+ dieout("</ul>");
+ }
+ print "OK</li>\n";
+ ## Set for the rest of this session
+ $SQL = "SET datestyle = 'ISO, YMD'";
+ $result = pg_query($this->mConn, $SQL);
+ if (!$result) {
+ print "<li>Failed to set datestyle</li>\n";
+ dieout("</ul>");
+ }
+
## Fix up the search paths if needed
print "<li>Setting the search path for user \"$user\" ...";
$path = $this->quote_ident($wgDBmwschema);
@@ -455,6 +562,30 @@ class DatabasePostgres extends Database {
}
/**
+ * Estimate rows in dataset
+ * Returns estimated count, based on EXPLAIN output
+ * This is not necessarily an accurate estimate, so use sparingly
+ * Returns -1 if count cannot be found
+ * Takes same arguments as Database::select()
+ */
+
+ function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+ $options['EXPLAIN'] = true;
+ $res = $this->select( $table, $vars, $conds, $fname, $options );
+ $rows = -1;
+ if ( $res ) {
+ $row = $this->fetchRow( $res );
+ $count = array();
+ if( preg_match( '/rows=(\d+)/', $row[0], $count ) ) {
+ $rows = $count[1];
+ }
+ $this->freeResult($res);
+ }
+ return $rows;
+ }
+
+
+ /**
* Returns information about an index
* If errors are explicitly ignored, returns NULL on failure
*/
@@ -645,7 +776,7 @@ class DatabasePostgres extends Database {
return '';
}
- function limitResult($sql, $limit,$offset) {
+ function limitResult($sql, $limit,$offset=false) {
return "$sql LIMIT $limit ".(is_numeric($offset)?" OFFSET {$offset} ":"");
}
@@ -707,26 +838,31 @@ class DatabasePostgres extends Database {
* @return string Version information from the database
*/
function getServerVersion() {
- $res = $this->query( "SELECT version()" );
- $row = $this->fetchRow( $res );
- $version = $row[0];
- $this->freeResult( $res );
+ $version = pg_fetch_result($this->doQuery("SELECT version()"),0,0);
+ $thisver = array();
+ if (!preg_match('/PostgreSQL (\d+\.\d+)(\S+)/', $version, $thisver)) {
+ die("Could not determine the numeric version from $version!");
+ }
+ $this->numeric_version = $thisver[1];
return $version;
}
/**
- * Query whether a given table exists (in the given schema, or the default mw one if not given)
+ * Query whether a given relation exists (in the given schema, or the
+ * default mw one if not given)
*/
- function tableExists( $table, $schema = false ) {
+ function relationExists( $table, $types, $schema = false ) {
global $wgDBmwschema;
+ if (!is_array($types))
+ $types = array($types);
if (! $schema )
$schema = $wgDBmwschema;
- $etable = preg_replace("/'/", "''", $table);
- $eschema = preg_replace("/'/", "''", $schema);
+ $etable = $this->addQuotes($table);
+ $eschema = $this->addQuotes($schema);
$SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
- . "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
- . "AND c.relkind IN ('r','v')";
+ . "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;
if ($res)
@@ -734,6 +870,61 @@ class DatabasePostgres extends Database {
return $count;
}
+ /*
+ * For backward compatibility, this function checks both tables and
+ * views.
+ */
+ function tableExists ($table, $schema = false) {
+ return $this->relationExists($table, array('r', 'v'), $schema);
+ }
+
+ function sequenceExists ($sequence, $schema = false) {
+ return $this->relationExists($sequence, 'S', $schema);
+ }
+
+ function triggerExists($table, $trigger) {
+ global $wgDBmwschema;
+
+ $q = <<<END
+ SELECT 1 FROM pg_class, pg_namespace, pg_trigger
+ WHERE relnamespace=pg_namespace.oid AND relkind='r'
+ AND tgrelid=pg_class.oid
+ AND nspname=%s AND relname=%s AND tgname=%s
+END;
+ $res = $this->query(sprintf($q,
+ $this->addQuotes($wgDBmwschema),
+ $this->addQuotes($table),
+ $this->addQuotes($trigger)));
+ if (!$res)
+ return NULL;
+ $rows = pg_num_rows($res);
+ $this->freeResult($res);
+ return $rows;
+ }
+
+ function ruleExists($table, $rule) {
+ global $wgDBmwschema;
+ $exists = $this->selectField("pg_rules", "rulename",
+ array( "rulename" => $rule,
+ "tablename" => $table,
+ "schemaname" => $wgDBmwschema));
+ return $exists === $rule;
+ }
+
+ function constraintExists($table, $constraint) {
+ global $wgDBmwschema;
+ $SQL = sprintf("SELECT 1 FROM information_schema.table_constraints ".
+ "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s",
+ $this->addQuotes($wgDBmwschema),
+ $this->addQuotes($table),
+ $this->addQuotes($constraint));
+ $res = $this->query($SQL);
+ if (!$res)
+ return NULL;
+ $rows = pg_num_rows($res);
+ $this->freeResult($res);
+ return $rows;
+ }
/**
* Query whether a given schema exists. Returns the name of the owner
@@ -752,7 +943,7 @@ class DatabasePostgres extends Database {
/**
* Query whether a given column exists in the mediawiki schema
*/
- function fieldExists( $table, $field ) {
+ function fieldExists( $table, $field, $fname = 'DatabasePostgres::fieldExists' ) {
global $wgDBmwschema;
$etable = preg_replace("/'/", "''", $table);
$eschema = preg_replace("/'/", "''", $wgDBmwschema);
@@ -760,7 +951,7 @@ class DatabasePostgres extends Database {
$SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n, pg_catalog.pg_attribute a "
. "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 );
+ $res = $this->query( $SQL, $fname );
$count = $res ? pg_num_rows($res) : 0;
if ($res)
$this->freeResult( $res );
@@ -768,12 +959,10 @@ class DatabasePostgres extends Database {
}
function fieldInfo( $table, $field ) {
- $res = $this->query( "SELECT $field FROM $table LIMIT 1" );
- $type = pg_field_type( $res, 0 );
- return $type;
+ return PostgresField::fromText($this, $table, $field);
}
- function begin( $fname = 'DatabasePostgrs::begin' ) {
+ function begin( $fname = 'DatabasePostgres::begin' ) {
$this->query( 'BEGIN', $fname );
$this->mTrxLevel = 1;
}
@@ -791,10 +980,36 @@ class DatabasePostgres extends Database {
}
function setup_database() {
- global $wgVersion, $wgDBmwschema, $wgDBts2schema, $wgDBport;
+ global $wgVersion, $wgDBmwschema, $wgDBts2schema, $wgDBport, $wgDBuser;
+
+ ## Make sure that we can write to the correct schema
+ ## If not, Postgres will happily and silently go to the next search_path item
+ $ctest = "mw_test_table";
+ if ($this->tableExists($ctest, $wgDBmwschema)) {
+ $this->doQuery("DROP TABLE $wgDBmwschema.$ctest");
+ }
+ $SQL = "CREATE TABLE $wgDBmwschema.$ctest(a int)";
+ error_reporting( 0 );
+ $res = $this->doQuery($SQL);
+ error_reporting( E_ALL );
+ if (!$res) {
+ print "<b>FAILED</b>. Make sure that the user \"$wgDBuser\" can write to the schema \"$wgDBmwschema\"</li>\n";
+ dieout("</ul>");
+ }
+ $this->doQuery("DROP TABLE $wgDBmwschema.mw_test_table");
dbsource( "../maintenance/postgres/tables.sql", $this);
+ ## Version-specific stuff
+ if ($this->numeric_version == 8.1) {
+ $this->doQuery("CREATE INDEX ts2_page_text ON pagecontent USING gist(textvector)");
+ $this->doQuery("CREATE INDEX ts2_page_title ON page USING gist(titlevector)");
+ }
+ else {
+ $this->doQuery("CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector)");
+ $this->doQuery("CREATE INDEX ts2_page_title ON page USING gin(titlevector)");
+ }
+
## Update version information
$mwv = $this->addQuotes($wgVersion);
$pgv = $this->addQuotes($this->getServerVersion());
@@ -827,6 +1042,8 @@ class DatabasePostgres extends Database {
$this->query("$SQL $matches[1],$matches[2])");
}
print " (table interwiki successfully populated)...\n";
+
+ $this->doQuery("COMMIT");
}
function encodeBlob($b) {
@@ -870,7 +1087,7 @@ class DatabasePostgres extends Database {
* @return array
*/
function makeSelectOptions( $options ) {
- $tailOpts = '';
+ $preLimitTail = $postLimitTail = '';
$startOpts = '';
$noKeyOptions = array();
@@ -880,16 +1097,17 @@ class DatabasePostgres extends Database {
}
}
- if ( isset( $options['GROUP BY'] ) ) $tailOpts .= " GROUP BY {$options['GROUP BY']}";
- if ( isset( $options['ORDER BY'] ) ) $tailOpts .= " ORDER BY {$options['ORDER BY']}";
+ if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY " . $options['GROUP BY'];
+ if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY " . $options['ORDER BY'];
- if (isset($options['LIMIT'])) {
- $tailOpts .= $this->limitResult('', $options['LIMIT'],
- isset($options['OFFSET']) ? $options['OFFSET'] : false);
- }
-
- if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
- if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
+ //if (isset($options['LIMIT'])) {
+ // $tailOpts .= $this->limitResult('', $options['LIMIT'],
+ // isset($options['OFFSET']) ? $options['OFFSET']
+ // : false);
+ //}
+
+ 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'] ) ) {
@@ -898,7 +1116,11 @@ class DatabasePostgres extends Database {
$useIndex = '';
}
- return array( $startOpts, $useIndex, $tailOpts );
+ return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
+ }
+
+ public function setTimeout( $timeout ) {
+ // @todo fixme no-op
}
function ping() {
diff --git a/includes/DateFormatter.php b/includes/DateFormatter.php
index c795618a..88a64453 100644
--- a/includes/DateFormatter.php
+++ b/includes/DateFormatter.php
@@ -1,15 +1,9 @@
<?php
-/**
- * Date formatter, recognises dates in plain text and formats them accoding to user preferences.
- *
- * @package MediaWiki
- * @subpackage Parser
- */
/**
+ * Date formatter, recognises dates in plain text and formats them accoding to user preferences.
* @todo preferences, OutputPage
- * @package MediaWiki
- * @subpackage Parser
+ * @addtogroup Parser
*/
class DateFormatter
{
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 0692401d..169d67c9 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -15,7 +15,6 @@
* Documentation is in the source and on:
* http://www.mediawiki.org/wiki/Help:Configuration_settings
*
- * @package MediaWiki
*/
# This is not a valid entry point, perform no further processing unless MEDIAWIKI is defined
@@ -32,7 +31,7 @@ require_once( 'includes/SiteConfiguration.php' );
$wgConf = new SiteConfiguration;
/** MediaWiki version number */
-$wgVersion = '1.9.3';
+$wgVersion = '1.10.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
@@ -163,7 +162,6 @@ $wgTmpDirectory = false; /// defaults to "{$wgUploadDirectory}/tmp"
$wgUploadBaseUrl = "";
/**#@-*/
-
/**
* By default deleted files are simply discarded; to save them and
* make it possible to undelete images, create a directory which
@@ -192,6 +190,7 @@ $wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
*
* Problematic punctuation:
* []{}|# Are needed for link syntax, never enable these
+ * <> Causes problems with HTML escaping, don't use
* % Enabled by default, minor problems with path to query rewrite rules, see below
* + Enabled by default, but doesn't work with path to query rewrite rules, corrupted by apache
* ? Enabled by default, but doesn't work with path to PATH_INFO rewrites
@@ -307,8 +306,8 @@ $wgVerifyMimeType= true;
/** Sets the mime type definition file to use by MimeMagic.php.
* @global string $wgMimeTypeFile
*/
-#$wgMimeTypeFile= "/etc/mime.types";
$wgMimeTypeFile= "includes/mime.types";
+#$wgMimeTypeFile= "/etc/mime.types";
#$wgMimeTypeFile= NULL; #use built-in defaults only.
/** Sets the mime type info file to use by MimeMagic.php.
@@ -372,7 +371,11 @@ $wgSharedUploadDBprefix = '';
$wgCacheSharedUploads = true;
/** Allow for upload to be copied from an URL. Requires Special:Upload?source=web */
$wgAllowCopyUploads = false;
-/** Max size for uploads, in bytes */
+/**
+ * Max size for uploads, in bytes. Currently only works for uploads from URL
+ * via CURL (see $wgAllowCopyUploads). The only way to impose limits on
+ * normal uploads is currently to edit php.ini.
+ */
$wgMaxUploadSize = 1024*1024*100; # 100MB
/**
@@ -502,8 +505,12 @@ $wgDBtype = "mysql";
$wgSearchType = null;
/** Table name prefix */
$wgDBprefix = '';
+/** MySQL table options to use during installation or update */
+$wgDBTableOptions = 'TYPE=InnoDB';
+
/**#@-*/
+
/** Live high performance sites should disable this - some checks acquire giant mysql locks */
$wgCheckDBSchema = true;
@@ -964,6 +971,7 @@ $wgGroupPermissions['user' ]['upload'] = true;
$wgGroupPermissions['user' ]['reupload'] = true;
$wgGroupPermissions['user' ]['reupload-shared'] = true;
$wgGroupPermissions['user' ]['minoredit'] = true;
+$wgGroupPermissions['user' ]['purge'] = true; // can use ?action=purge without clicking "ok"
// Implicit group for accounts that pass $wgAutoConfirmAge
$wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
@@ -977,6 +985,7 @@ $wgGroupPermissions['emailconfirmed']['emailconfirmed'] = true;
$wgGroupPermissions['bot' ]['bot'] = true;
$wgGroupPermissions['bot' ]['autoconfirmed'] = true;
$wgGroupPermissions['bot' ]['nominornewtalk'] = true;
+$wgGroupPermissions['bot' ]['autopatrol'] = true;
// Most extra permission abilities go to this group
$wgGroupPermissions['sysop']['block'] = true;
@@ -988,7 +997,7 @@ $wgGroupPermissions['sysop']['import'] = true;
$wgGroupPermissions['sysop']['importupload'] = true;
$wgGroupPermissions['sysop']['move'] = true;
$wgGroupPermissions['sysop']['patrol'] = true;
-$wgGroupPermissions['sysop']['autopatrol'] = true;
+$wgGroupPermissions['sysop']['autopatrol'] = true;
$wgGroupPermissions['sysop']['protect'] = true;
$wgGroupPermissions['sysop']['proxyunbannable'] = true;
$wgGroupPermissions['sysop']['rollback'] = true;
@@ -1029,6 +1038,21 @@ $wgRestrictionTypes = array( 'edit', 'move' );
*/
$wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
+/**
+ * Set the minimum permissions required to edit pages in each
+ * namespace. If you list more than one permission, a user must
+ * have all of them to edit pages in that namespace.
+ */
+$wgNamespaceProtection = array();
+$wgNamespaceProtection[ NS_MEDIAWIKI ] = array( 'editinterface' );
+
+/**
+* Pages in namespaces in this array can not be used as templates.
+* Elements must be numeric namespace ids.
+* Among other things, this may be useful to enforce read-restrictions
+* which may otherwise be bypassed by using the template machanism.
+*/
+$wgNonincludableNamespaces = array();
/**
* Number of seconds an account is required to age before
@@ -1045,6 +1069,11 @@ $wgAutoConfirmAge = 0;
//$wgAutoConfirmAge = 600; // ten minutes
//$wgAutoConfirmAge = 3600*24; // one day
+# Number of edits an account requires before it is autoconfirmed
+# Passing both this AND the time requirement is needed
+$wgAutoConfirmCount = 0;
+//$wgAutoConfirmCount = 50;
+
# Proxy scanner settings
@@ -1096,7 +1125,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '42b';
+$wgStyleVersion = '63';
# Server-side caching:
@@ -1145,6 +1174,11 @@ $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)
+/**
+ * Array of usernames who will be sent a notification email for every change which occurs on a wiki
+ */
+$wgUsersNotifedOnAllChanges = array();
+
/** Show watching users in recent changes, watchlist and page history views */
$wgRCShowWatchingUsers = false; # UPO
/** Show watching users in Page views */
@@ -1419,8 +1453,19 @@ $wgSiteNotice = '';
# Images settings
#
-/** dynamic server side image resizing ("Thumbnails") */
-$wgUseImageResize = false;
+/**
+ * Plugins for media file type handling.
+ * Each entry in the array maps a MIME type to a class name
+ */
+$wgMediaHandlers = array(
+ 'image/jpeg' => 'BitmapHandler',
+ 'image/png' => 'BitmapHandler',
+ 'image/gif' => 'BitmapHandler',
+ 'image/x-ms-bmp' => 'BmpHandler',
+ 'image/svg+xml' => 'SvgHandler',
+ 'image/vnd.djvu' => 'DjVuHandler',
+);
+
/**
* Resizing can be done using PHP's internal image libraries or using
@@ -1434,6 +1479,12 @@ $wgUseImageMagick = false;
/** The convert command shipped with ImageMagick */
$wgImageMagickConvertCommand = '/usr/bin/convert';
+/** Sharpening parameter to ImageMagick */
+$wgSharpenParameter = '0x0.4';
+
+/** Reduction in linear dimensions below which sharpening will be enabled */
+$wgSharpenReductionThreshold = 0.85;
+
/**
* Use another resizing converter, e.g. GraphicMagick
* %s will be replaced with the source path, %d with the destination
@@ -1451,7 +1502,7 @@ $wgCustomConvertCommand = false;
#
# An external program is required to perform this conversion:
$wgSVGConverters = array(
- 'ImageMagick' => '$path/convert -background white -geometry $width $input $output',
+ 'ImageMagick' => '$path/convert -background white -geometry $width $input PNG:$output',
'sodipodi' => '$path/sodipodi -z -w $width -f $input -e $output',
'inkscape' => '$path/inkscape -z -w $width -f $input -e $output',
'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input',
@@ -1499,11 +1550,17 @@ $wgIgnoreImageErrors = false;
*/
$wgGenerateThumbnailOnParse = true;
+/** Obsolete, always true, kept for compatibility with extensions */
+$wgUseImageResize = true;
+
+
/** Set $wgCommandLineMode if it's not set already, to avoid notices */
if( !isset( $wgCommandLineMode ) ) {
$wgCommandLineMode = false;
}
+/** For colorized maintenance script output, is your terminal background dark ? */
+$wgCommandLineDarkBg = false;
#
# Recent changes settings
@@ -1613,18 +1670,22 @@ $wgExportAllowListContributors = false ;
/** Text matching this regular expression will be recognised as spam
* See http://en.wikipedia.org/wiki/Regular_expression */
$wgSpamRegex = false;
-/** Similarly if this function returns true */
+/** Similarly you can get a function to do the job. The function will be given
+ * the following args:
+ * - a Title object for the article the edit is made on
+ * - the text submitted in the textarea (wpTextbox1)
+ * - the section number.
+ * The return should be boolean indicating whether the edit matched some evilness:
+ * - true : block it
+ * - false : let it through
+ *
+ * For a complete example, have a look at the SpamBlacklist extension.
+ */
$wgFilterCallback = false;
/** Go button goes straight to the edit screen if the article doesn't exist. */
$wgGoToEdit = false;
-/** Allow limited user-specified HTML in wiki pages?
- * It will be run through a whitelist for security. Set this to false if you
- * want wiki pages to consist only of wiki markup. Note that replacements do not
- * yet exist for all HTML constructs.*/
-$wgUserHtml = true;
-
/** Allow raw, unchecked HTML in <html>...</html> sections.
* THIS IS VERY DANGEROUS on a publically editable site, so USE wgGroupPermissions
* TO RESTRICT EDITING to only those that you trust
@@ -1633,8 +1694,7 @@ $wgRawHtml = false;
/**
* $wgUseTidy: use tidy to make sure HTML output is sane.
- * This should only be enabled if $wgUserHtml is true.
- * tidy is a free tool that fixes broken HTML.
+ * Tidy is a free tool that fixes broken HTML.
* See http://www.w3.org/People/Raggett/tidy/
* $wgTidyBin should be set to the path of the binary and
* $wgTidyConf to the path of the configuration file.
@@ -1649,7 +1709,7 @@ $wgRawHtml = false;
$wgUseTidy = false;
$wgAlwaysUseTidy = false;
$wgTidyBin = 'tidy';
-$wgTidyConf = $IP.'/extensions/tidy/tidy.conf';
+$wgTidyConf = $IP.'/includes/tidy.conf';
$wgTidyOpts = '';
$wgTidyInternal = function_exists( 'tidy_load_config' );
@@ -1660,7 +1720,7 @@ $wgDefaultSkin = 'monobook';
* Settings added to this array will override the default globals for the user
* preferences used by anonymous visitors and newly created accounts.
* For instance, to disable section editing links:
- *  $wgDefaultUserOptions ['editsection'] = 0;
+ * $wgDefaultUserOptions ['editsection'] = 0;
*
*/
$wgDefaultUserOptions = array(
@@ -1831,9 +1891,28 @@ $wgFeedDiffCutoff = 32768;
$wgExtraNamespaces = NULL;
/**
+ * Namespace aliases
+ * These are alternate names for the primary localised namespace names, which
+ * are defined by $wgExtraNamespaces and the language file. If a page is
+ * requested with such a prefix, the request will be redirected to the primary
+ * name.
+ *
+ * Set this to a map from namespace names to IDs.
+ * Example:
+ * $wgNamespaceAliases = array(
+ * 'Wikipedian' => NS_USER,
+ * 'Help' => 100,
+ * );
+ */
+$wgNamespaceAliases = array();
+
+/**
* Limit images on image description pages to a user-selectable limit. In order
- * to reduce disk usage, limits can only be selected from a list. This is the
- * list of settings the user can choose from:
+ * to reduce disk usage, limits can only be selected from a list.
+ * The user preference is saved as an array offset in the database, by default
+ * the offset is set with $wgDefaultUserOptions['imagesize']. Make sure you
+ * change it if you alter the array (see bug 8858).
+ * This is the list of settings the user can choose from:
*/
$wgImageLimits = array (
array(320,240),
@@ -1883,9 +1962,9 @@ $wgBrowserBlackList = array(
*
* Reference: http://www.psychedelix.com/agents/index.shtml
*/
- '/^Mozilla\/2\.[^ ]+ .*?\((?!compatible).*; [UIN]/',
- '/^Mozilla\/3\.[^ ]+ .*?\((?!compatible).*; [UIN]/',
- '/^Mozilla\/4\.[^ ]+ .*?\((?!compatible).*; [UIN]/',
+ '/^Mozilla\/2\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
+ '/^Mozilla\/3\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
+ '/^Mozilla\/4\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
/**
* MSIE on Mac OS 9 is teh sux0r, converts þ to <thorn>, ð to <eth>, Þ to <THORN> and Ð to <ETH>
@@ -1985,7 +2064,9 @@ $wgLogTypes = array( '',
'delete',
'upload',
'move',
- 'import' );
+ 'import',
+ 'patrol',
+);
/**
* Lists the message key string for each log type. The localized messages
@@ -2001,7 +2082,9 @@ $wgLogNames = array(
'delete' => 'dellogpage',
'upload' => 'uploadlogpage',
'move' => 'movelogpage',
- 'import' => 'importlogpage' );
+ 'import' => 'importlogpage',
+ 'patrol' => 'patrol-log-page',
+);
/**
* Lists the message key string for descriptive text to be shown at the
@@ -2017,7 +2100,9 @@ $wgLogHeaders = array(
'delete' => 'dellogpagetext',
'upload' => 'uploadlogpagetext',
'move' => 'movelogpagetext',
- 'import' => 'importlogpagetext', );
+ 'import' => 'importlogpagetext',
+ 'patrol' => 'patrol-log-header',
+);
/**
* Lists the message key string for formatting individual events of each
@@ -2039,7 +2124,8 @@ $wgLogActions = array(
'move/move' => '1movedto2',
'move/move_redir' => '1movedto2_redir',
'import/upload' => 'import-logentry-upload',
- 'import/interwiki' => 'import-logentry-interwiki' );
+ 'import/interwiki' => 'import-logentry-interwiki',
+);
/**
* Experimental preview feature to fetch rendered text
@@ -2166,6 +2252,9 @@ $wgRateLimits = array(
'mailpassword' => array(
'anon' => NULL,
),
+ 'emailuser' => array(
+ 'user' => null,
+ ),
);
/**
@@ -2235,7 +2324,7 @@ $wgTrustedMediaFormats= array(
MEDIATYPE_BITMAP, //all bitmap formats
MEDIATYPE_AUDIO, //all audio formats
MEDIATYPE_VIDEO, //all plain video formats
- "image/svg", //svg (only needed if inline rendering of svg is not supported)
+ "image/svg+xml", //svg (only needed if inline rendering of svg is not supported)
"application/pdf", //PDF files
#"application/x-shockwave-flash", //flash/shockwave movie
);
@@ -2330,7 +2419,7 @@ $wgAllowDisplayTitle = false ;
$wgReservedUsernames = array(
'MediaWiki default', // Default 'Main Page' and MediaWiki: message pages
'Conversion script', // Used for the old Wikipedia software upgrade
- 'Maintenance script', // ... maintenance/edit.php uses this?
+ 'Maintenance script', // Maintenance scripts which perform editing, image import script
'Template namespace initialisation script', // Used in 1.2->1.3 upgrade
);
@@ -2338,7 +2427,7 @@ $wgReservedUsernames = array(
* MediaWiki will reject HTMLesque tags in uploaded files due to idiotic browsers which can't
* perform basic stuff like MIME detection and which are vulnerable to further idiots uploading
* crap files as images. When this directive is on, <title> will be allowed in files with
- * an "image/svg" MIME type. You should leave this disabled if your web server is misconfigured
+ * an "image/svg+xml" MIME type. You should leave this disabled if your web server is misconfigured
* and doesn't send appropriate MIME types for SVG images.
*/
$wgAllowTitlesInSVG = false;
@@ -2364,25 +2453,42 @@ $wgMaxShellFileSize = 102400;
/**
* DJVU settings
- * Path of the djvutoxml executable
+ * Path of the djvudump executable
* Enable this and $wgDjvuRenderer to enable djvu rendering
*/
-# $wgDjvuToXML = 'djvutoxml';
-$wgDjvuToXML = null;
+# $wgDjvuDump = 'djvudump';
+$wgDjvuDump = null;
/**
* Path of the ddjvu DJVU renderer
- * Enable this and $wgDjvuToXML to enable djvu rendering
+ * Enable this and $wgDjvuDump to enable djvu rendering
*/
# $wgDjvuRenderer = 'ddjvu';
$wgDjvuRenderer = null;
/**
- * Path of the DJVU post processor
- * May include command line options
- * Default: ppmtojpeg, since ddjvu generates ppm output
+ * Path of the djvutoxml executable
+ * This works like djvudump except much, much slower as of version 3.5.
+ *
+ * For now I recommend you use djvudump instead. The djvuxml output is
+ * probably more stable, so we'll switch back to it as soon as they fix
+ * the efficiency problem.
+ * http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
+ */
+# $wgDjvuToXML = 'djvutoxml';
+$wgDjvuToXML = null;
+
+
+/**
+ * Shell command for the DJVU post processor
+ * Default: pnmtopng, since ddjvu generates ppm output
+ * Set this to false to output the ppm file directly.
+ */
+$wgDjvuPostProcessor = 'pnmtojpeg';
+/**
+ * File extension for the DJVU post processor output
*/
-$wgDjvuPostProcessor = 'ppmtojpeg';
+$wgDjvuOutputExtension = 'jpg';
/**
* Enable direct access to the data API
@@ -2416,4 +2522,14 @@ $wgBreakFrames = false;
*/
$wgDisableQueryPageUpdate = false;
+/**
+ * Set this to false to disable cascading protection
+ */
+$wgEnableCascadingProtection = true;
+
+/**
+ * Disable output compression (enabled by default if zlib is available)
+ */
+$wgDisableOutputCompression = false;
+
?>
diff --git a/includes/Defines.php b/includes/Defines.php
index 84bc4495..98e76277 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -1,7 +1,6 @@
<?php
/**
* A few constants that might be needed during LocalSettings.php
- * @package MediaWiki
*/
/**
diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php
index a72f0153..af65ce3a 100644
--- a/includes/DifferenceEngine.php
+++ b/includes/DifferenceEngine.php
@@ -1,15 +1,14 @@
<?php
/**
* See diff.doc
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @todo indicate where diff.doc can be found.
+ * @addtogroup DifferenceEngine
*/
/**
* @todo document
* @public
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class DifferenceEngine {
/**#@+
@@ -63,8 +62,8 @@ class DifferenceEngine {
$this->mRcidMarkPatrolled = intval($rcid); # force it to be an integer
}
- function showDiffPage() {
- global $wgUser, $wgOut, $wgContLang, $wgUseExternalEditor, $wgUseRCPatrol;
+ function showDiffPage( $diffOnly = false ) {
+ global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol;
$fname = 'DifferenceEngine::showDiffPage';
wfProfileIn( $fname );
@@ -118,6 +117,7 @@ CONTROL;
# is the first version of that article. In that case, V' does not exist.
if ( $this->mOldid === false ) {
$this->showFirstRevision();
+ $this->renderNewRevision(); // should we respect $diffOnly here or not?
wfProfileOut( $fname );
return;
}
@@ -178,15 +178,34 @@ CONTROL;
$oldHeader = "<strong>{$this->mOldtitle}</strong><br />" .
$sk->revUserTools( $this->mOldRev ) . "<br />" .
- $oldminor . $sk->revComment( $this->mOldRev, true ) . "<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, true ) . "<br />" .
+ $newminor . $sk->revComment( $this->mNewRev, !$diffOnly ) . "<br />" .
$nextlink . $patrol;
$this->showDiff( $oldHeader, $newHeader );
+
+ if ( !$diffOnly )
+ $this->renderNewRevision();
+
+ wfProfileOut( $fname );
+ }
+
+ /**
+ * Show the new revision of the page.
+ */
+ function renderNewRevision() {
+ global $wgOut;
+ $fname = 'DifferenceEngine::renderNewRevision';
+ wfProfileIn( $fname );
+
$wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
+ #add deleted rev tag if needed
+ if ( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
+ }
if( !$this->mNewRev->isCurrent() ) {
$oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
@@ -196,7 +215,8 @@ CONTROL;
if( is_object( $this->mNewRev ) ) {
$wgOut->setRevisionId( $this->mNewRev->getId() );
}
- $wgOut->addSecondaryWikiText( $this->mNewtext );
+
+ $wgOut->addWikiTextTidy( $this->mNewtext );
if( !$this->mNewRev->isCurrent() ) {
$wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
@@ -254,15 +274,6 @@ CONTROL;
$wgOut->setSubtitle( wfMsg( 'difference' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
-
- # Show current revision
- #
- $wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
- if( is_object( $this->mNewRev ) ) {
- $wgOut->setRevisionId( $this->mNewRev->getId() );
- }
- $wgOut->addSecondaryWikiText( $this->mNewtext );
-
wfProfileOut( $fname );
}
@@ -322,9 +333,14 @@ CONTROL;
}
}
+ #loadtext is permission safe, this just clears out the diff
if ( !$this->loadText() ) {
wfProfileOut( $fname );
return false;
+ } else if ( $this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
+ return '';
+ } else if ( $this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
+ return '';
}
$difftext = $this->generateDiffBody( $this->mOldtext, $this->mNewtext );
@@ -463,6 +479,14 @@ CONTROL;
* Add the header to a diff body
*/
function addHeader( $diff, $otitle, $ntitle, $multi = '' ) {
+ global $wgOut;
+
+ if ( $this->mOldRev && $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
+ $otitle = '<span class="history-deleted">'.$otitle.'</span>';
+ }
+ if ( $this->mNewRev && $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
+ $ntitle = '<span class="history-deleted">'.$ntitle.'</span>';
+ }
$header = "
<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>
<tr>
@@ -523,21 +547,17 @@ CONTROL;
$newLink = $this->mNewPage->escapeLocalUrl();
$this->mPagetitle = htmlspecialchars( wfMsg( 'currentrev' ) );
$newEdit = $this->mNewPage->escapeLocalUrl( 'action=edit' );
- $newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undo=' . $this->mNewid );
$this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a> ($timestamp)"
- . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)"
- . " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
+ . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
} else {
$newLink = $this->mNewPage->escapeLocalUrl( 'oldid=' . $this->mNewid );
$newEdit = $this->mNewPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mNewid );
- $newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undo=' . $this->mNewid );
$this->mPagetitle = htmlspecialchars( wfMsg( 'revisionasof', $timestamp ) );
$this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>"
- . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)"
- . " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
+ . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
}
// Load the old revision object
@@ -568,6 +588,9 @@ 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)
+ $newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undoafter=' . $this->mOldid . '&undo=' . $this->mNewid);
+ $this->mNewtitle .= " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
}
return true;
@@ -589,13 +612,13 @@ CONTROL;
}
if ( $this->mOldRev ) {
// FIXME: permission tests
- $this->mOldtext = $this->mOldRev->getText();
+ $this->mOldtext = $this->mOldRev->revText();
if ( $this->mOldtext === false ) {
return false;
}
}
if ( $this->mNewRev ) {
- $this->mNewtext = $this->mNewRev->getText();
+ $this->mNewtext = $this->mNewRev->revText();
if ( $this->mNewtext === false ) {
return false;
}
@@ -633,8 +656,7 @@ define('USE_ASSERTS', function_exists('assert'));
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp {
var $type;
@@ -657,8 +679,7 @@ class _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Copy extends _DiffOp {
var $type = 'copy';
@@ -678,8 +699,7 @@ class _DiffOp_Copy extends _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Delete extends _DiffOp {
var $type = 'delete';
@@ -697,8 +717,7 @@ class _DiffOp_Delete extends _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Add extends _DiffOp {
var $type = 'add';
@@ -716,8 +735,7 @@ class _DiffOp_Add extends _DiffOp {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffOp_Change extends _DiffOp {
var $type = 'change';
@@ -754,8 +772,7 @@ class _DiffOp_Change extends _DiffOp {
*
* @author Geoffrey T. Dairiki, Tim Starling
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _DiffEngine
{
@@ -1176,8 +1193,7 @@ class _DiffEngine
* Class representing a 'diff' between two sequences of strings.
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class Diff
{
@@ -1315,11 +1331,9 @@ class Diff
}
/**
- * FIXME: bad name.
- * @todo document
+ * @todo document, bad name.
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class MappedDiff extends Diff
{
@@ -1382,8 +1396,7 @@ class MappedDiff extends Diff
* to obtain fancier outputs.
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class DiffFormatter
{
@@ -1549,8 +1562,7 @@ define('NBSP', '&#160;'); // iso-8859-x non-breaking space.
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class _HWLDF_WordAccumulator {
function _HWLDF_WordAccumulator () {
@@ -1562,9 +1574,12 @@ class _HWLDF_WordAccumulator {
function _flushGroup ($new_tag) {
if ($this->_group !== '') {
- if ($this->_tag == 'mark')
- $this->_line .= '<span class="diffchange">' .
- htmlspecialchars ( $this->_group ) . '</span>';
+ if ($this->_tag == 'ins')
+ $this->_line .= '<ins class="diffchange">' .
+ htmlspecialchars ( $this->_group ) . '</ins>';
+ elseif ($this->_tag == 'del')
+ $this->_line .= '<del class="diffchange">' .
+ htmlspecialchars ( $this->_group ) . '</del>';
else
$this->_line .= htmlspecialchars ( $this->_group );
}
@@ -1608,8 +1623,7 @@ class _HWLDF_WordAccumulator {
/**
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class WordLevelDiff extends MappedDiff
{
@@ -1669,7 +1683,7 @@ class WordLevelDiff extends MappedDiff
if ($edit->type == 'copy')
$orig->addWords($edit->orig);
elseif ($edit->orig)
- $orig->addWords($edit->orig, 'mark');
+ $orig->addWords($edit->orig, 'del');
}
$lines = $orig->getLines();
wfProfileOut( $fname );
@@ -1685,7 +1699,7 @@ class WordLevelDiff extends MappedDiff
if ($edit->type == 'copy')
$closing->addWords($edit->closing);
elseif ($edit->closing)
- $closing->addWords($edit->closing, 'mark');
+ $closing->addWords($edit->closing, 'ins');
}
$lines = $closing->getLines();
wfProfileOut( $fname );
@@ -1697,8 +1711,7 @@ class WordLevelDiff extends MappedDiff
* Wikipedia Table style diff formatter.
* @todo document
* @private
- * @package MediaWiki
- * @subpackage DifferenceEngine
+ * @addtogroup DifferenceEngine
*/
class TableDiffFormatter extends DiffFormatter
{
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index 3b8a68ba..1e423565 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -1,11 +1,6 @@
<?php
+
/**
- * Support for detecting/validating DjVu image files and getting
- * some basic file metadata (resolution etc)
- *
- * File format docs are available in source package for DjVuLibre:
- * http://djvulibre.djvuzone.org/
- *
*
* Copyright (C) 2006 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
@@ -25,9 +20,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @package MediaWiki
*/
+/**
+ * Support for detecting/validating DjVu image files and getting
+ * some basic file metadata (resolution etc)
+ *
+ * File format docs are available in source package for DjVuLibre:
+ * http://djvulibre.djvuzone.org/
+ *
+ * @addtogroup Media
+ */
class DjVuImage {
function __construct( $filename ) {
$this->mFilename = $filename;
@@ -68,6 +71,7 @@ class DjVuImage {
function dump() {
$file = fopen( $this->mFilename, 'rb' );
$header = fread( $file, 12 );
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4magic/a4chunk/NchunkLength', $header ) );
echo "$chunk $chunkLength\n";
$this->dumpForm( $file, $chunkLength, 1 );
@@ -83,6 +87,7 @@ class DjVuImage {
if( $chunkHeader == '' ) {
break;
}
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4chunk/NchunkLength', $chunkHeader ) );
echo str_repeat( ' ', $indent * 4 ) . "$chunk $chunkLength\n";
@@ -111,6 +116,7 @@ class DjVuImage {
if( strlen( $header ) < 16 ) {
wfDebug( __METHOD__ . ": too short file header\n" );
} else {
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4magic/a4form/NformLength/a4subtype', $header ) );
if( $magic != 'AT&T' ) {
@@ -134,6 +140,7 @@ class DjVuImage {
if( strlen( $header ) < 8 ) {
return array( false, 0 );
} else {
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4chunk/Nlength', $header ) );
return array( $chunk, $length );
}
@@ -192,6 +199,7 @@ class DjVuImage {
return false;
}
+ // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack(
'nwidth/' .
'nheight/' .
@@ -214,17 +222,121 @@ class DjVuImage {
* @return string
*/
function retrieveMetaData() {
- global $wgDjvuToXML;
- if ( isset( $wgDjvuToXML ) ) {
- $cmd = $wgDjvuToXML . ' --without-anno --without-text ' .
+ global $wgDjvuToXML, $wgDjvuDump;
+ if ( isset( $wgDjvuDump ) ) {
+ # djvudump is faster as of version 3.5
+ # http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
+ wfProfileIn( 'djvudump' );
+ $cmd = wfEscapeShellArg( $wgDjvuDump ) . ' ' . wfEscapeShellArg( $this->mFilename );
+ $dump = wfShellExec( $cmd );
+ $xml = $this->convertDumpToXML( $dump );
+ wfProfileOut( 'djvudump' );
+ } elseif ( isset( $wgDjvuToXML ) ) {
+ wfProfileIn( 'djvutoxml' );
+ $cmd = wfEscapeShellArg( $wgDjvuToXML ) . ' --without-anno --without-text ' .
wfEscapeShellArg( $this->mFilename );
$xml = wfShellExec( $cmd );
+ wfProfileOut( 'djvutoxml' );
} else {
$xml = null;
}
return $xml;
}
-
+
+ /**
+ * Hack to temporarily work around djvutoxml bug
+ */
+ function convertDumpToXML( $dump ) {
+ if ( strval( $dump ) == '' ) {
+ return false;
+ }
+
+ $xml = <<<EOT
+<?xml version="1.0" ?>
+<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
+<DjVuXML>
+<HEAD></HEAD>
+<BODY>
+EOT;
+
+ $dump = str_replace( "\r", '', $dump );
+ $line = strtok( $dump, "\n" );
+ $m = false;
+ $good = false;
+ if ( preg_match( '/^( *)FORM:DJVU/', $line, $m ) ) {
+ # Single-page
+ if ( $this->parseFormDjvu( $line, $xml ) ) {
+ $good = true;
+ } else {
+ return false;
+ }
+ } elseif ( preg_match( '/^( *)FORM:DJVM/', $line, $m ) ) {
+ # Multi-page
+ $parentLevel = strlen( $m[1] );
+ # Find DIRM
+ $line = strtok( "\n" );
+ while ( $line !== false ) {
+ $childLevel = strspn( $line, ' ' );
+ if ( $childLevel <= $parentLevel ) {
+ # End of chunk
+ break;
+ }
+
+ if ( preg_match( '/^ *DIRM.*indirect/', $line ) ) {
+ wfDebug( "Indirect multi-page DjVu document, bad for server!\n" );
+ return false;
+ }
+ if ( preg_match( '/^ *FORM:DJVU/', $line ) ) {
+ # Found page
+ if ( $this->parseFormDjvu( $line, $xml ) ) {
+ $good = true;
+ } else {
+ return false;
+ }
+ }
+ $line = strtok( "\n" );
+ }
+ }
+ if ( !$good ) {
+ return false;
+ }
+
+ $xml .= "</BODY>\n</DjVuXML>\n";
+ return $xml;
+ }
+
+ function parseFormDjvu( $line, &$xml ) {
+ $parentLevel = strspn( $line, ' ' );
+ $line = strtok( "\n" );
+
+ # Find INFO
+ while ( $line !== false ) {
+ $childLevel = strspn( $line, ' ' );
+ if ( $childLevel <= $parentLevel ) {
+ # End of chunk
+ break;
+ }
+
+ if ( preg_match( '/^ *INFO *\[\d*\] *DjVu *(\d+)x(\d+), *\w*, *(\d+) *dpi, *gamma=([0-9.-]+)/', $line, $m ) ) {
+ $xml .= Xml::tags( 'OBJECT',
+ array(
+ #'data' => '',
+ #'type' => 'image/x.djvu',
+ 'height' => $m[2],
+ 'width' => $m[1],
+ #'usemap' => '',
+ ),
+ "\n" .
+ Xml::element( 'PARAM', array( 'name' => 'DPI', 'value' => $m[3] ) ) . "\n" .
+ Xml::element( 'PARAM', array( 'name' => 'GAMMA', 'value' => $m[4] ) ) . "\n"
+ ) . "\n";
+ return true;
+ }
+ $line = strtok( "\n" );
+ }
+ # Not found
+ return false;
+ }
}
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 7688a64a..bec6e300 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -1,18 +1,14 @@
<?php
/**
- * Contain the EditPage class
- * @package MediaWiki
+ * Contains the EditPage class
*/
/**
- * Splitting edit page/HTML interface from Article...
+ * The edit page/HTML interface (split from Article)
* The actual database and text munging is still in Article,
* but it should get easier to call those from alternate
* interfaces.
- *
- * @package MediaWiki
*/
-
class EditPage {
var $mArticle;
var $mTitle;
@@ -69,22 +65,26 @@ class EditPage {
/**
* Fetch initial editing page content.
*/
- private function getContent() {
+ private function getContent( $def_text = '' ) {
global $wgOut, $wgRequest, $wgParser;
# Get variables from query string :P
$section = $wgRequest->getVal( 'section' );
$preload = $wgRequest->getVal( 'preload' );
+ $undoafter = $wgRequest->getVal( 'undoafter' );
$undo = $wgRequest->getVal( 'undo' );
wfProfileIn( __METHOD__ );
$text = '';
if( !$this->mTitle->exists() ) {
-
- # If requested, preload some text.
- $text = $this->getPreloadedText( $preload );
-
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ # If this is a system message, get the default text.
+ $text = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
+ } else {
+ # If requested, preload some text.
+ $text = $this->getPreloadedText( $preload );
+ }
# We used to put MediaWiki:Newarticletext here if
# $text was empty at this point.
# This is now shown above the edit box instead.
@@ -94,51 +94,63 @@ class EditPage {
// fetch the page record from the high-priority server,
// which is needed to guarantee we don't pick up lagged
// information.
-
+
$text = $this->mArticle->getContent();
- if ( $undo > 0 ) {
- #Undoing a specific edit overrides section editing; section-editing
+ if ( $undo > 0 && $undo > $undoafter ) {
+ # Undoing a specific edit overrides section editing; section-editing
# doesn't work with undoing.
- $undorev = Revision::newFromId($undo);
+ if ( $undoafter ) {
+ $undorev = Revision::newFromId($undo);
+ $oldrev = Revision::newFromId($undoafter);
+ } else {
+ $undorev = Revision::newFromId($undo);
+ $oldrev = $undorev ? $undorev->getPrevious() : null;
+ }
#Sanity check, make sure it's the right page.
# Otherwise, $text will be left as-is.
- if (!is_null($undorev) && $undorev->getPage() == $this->mArticle->getID()) {
- $oldrev = $undorev->getPrevious();
+ if ( !is_null($undorev) && !is_null($oldrev) && $undorev->getPage()==$oldrev->getPage() && $undorev->getPage()==$this->mArticle->getID() ) {
$undorev_text = $undorev->getText();
$oldrev_text = $oldrev->getText();
$currev_text = $text;
#No use doing a merge if it's just a straight revert.
- if ($currev_text != $undorev_text) {
+ if ( $currev_text != $undorev_text ) {
$result = wfMerge($undorev_text, $oldrev_text, $currev_text, $text);
} else {
$text = $oldrev_text;
$result = true;
}
-
- if( $result ) {
- # Inform the user of our success and set an automatic edit summary
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-success' ) );
- $this->summary = wfMsgForContent( 'undo-summary', $undo, $undorev->getUserText() );
- $this->formtype = 'diff';
- } else {
- # Warn the user that something went wrong
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-failure' ) );
+ } else {
+ // Failed basic sanity checks.
+ // Older revisions may have been removed since the link
+ // was created, or we may simply have got bogus input.
+ $result = false;
+ }
+
+ if( $result ) {
+ # Inform the user of our success and set an automatic edit summary
+ $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-success' ) );
+ $firstrev = $oldrev->getNext();
+ # If we just undid one rev, use an autosummary
+ if ( $firstrev->mId == $undo ) {
+ $this->summary = wfMsgForContent('undo-summary', $undo, $undorev->getUserText());
}
-
+ $this->formtype = 'diff';
+ } else {
+ # Warn the user that something went wrong
+ $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-failure' ) );
}
- }
- else if( $section != '' ) {
+ } else if( $section != '' ) {
if( $section == 'new' ) {
$text = $this->getPreloadedText( $preload );
} else {
- $text = $wgParser->getSection( $text, $section );
+ $text = $wgParser->getSection( $text, $section, $def_text );
}
}
}
-
+
wfProfileOut( __METHOD__ );
return $text;
}
@@ -282,7 +294,7 @@ class EditPage {
global $wgOut, $wgUser, $wgRequest, $wgTitle;
global $wgEmailConfirmToEdit;
- if ( ! wfRunHooks( 'AlternateEdit', array( &$this ) ) )
+ if ( ! wfRunHooks( 'AlternateEdit', array( &$this ) ) )
return;
$fname = 'EditPage::edit';
@@ -301,7 +313,7 @@ class EditPage {
return;
}
- if ( ! $this->mTitle->userCanEdit() ) {
+ if ( ! $this->mTitle->userCan( 'edit' ) ) {
wfDebug( "$fname: user can't edit\n" );
$wgOut->readOnlyPage( $this->getContent(), true );
wfProfileOut( $fname );
@@ -335,7 +347,7 @@ class EditPage {
wfProfileOut($fname);
return;
}
- if ( !$this->mTitle->userCanCreate() && !$this->mTitle->exists() ) {
+ if ( !$this->mTitle->userCan( 'create' ) && !$this->mTitle->exists() ) {
wfDebug( "$fname: no create permission\n" );
$this->noCreatePermission();
wfProfileOut( $fname );
@@ -421,7 +433,12 @@ class EditPage {
# First time through: get contents, set time for conflict
# checking, etc.
if ( 'initial' == $this->formtype || $this->firsttime ) {
- $this->initialiseForm();
+ if ($this->initialiseForm() === false) {
+ $this->noSuchSectionPage();
+ wfProfileOut( "$fname-business-end" );
+ wfProfileOut( $fname );
+ return;
+ }
if( !$this->mTitle->getArticleId() )
wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
}
@@ -482,7 +499,7 @@ class EditPage {
// Remember whether a save was requested, so we can indicate
// if we forced preview due to session failure.
$this->mTriedSave = !$this->preview;
-
+
if ( $this->tokenOk( $request ) ) {
# Some browsers will not report any submit button
# if the user hits enter in the comment box.
@@ -519,8 +536,8 @@ class EditPage {
} else {
$this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' );
}
-
- $this->autoSumm = $request->getText( 'wpAutoSummary' );
+
+ $this->autoSumm = $request->getText( 'wpAutoSummary' );
} else {
# Not a posted form? Start with nothing.
wfDebug( "$fname: Not a posted form.\n" );
@@ -652,7 +669,7 @@ class EditPage {
wfProfileOut( $fname );
return true;
}
-
+
if ( !$wgUser->isAllowed('edit') ) {
if ( $wgUser->isAnon() ) {
$this->userNotLoggedInPage();
@@ -696,7 +713,7 @@ class EditPage {
if ( 0 == $aid ) {
// Late check for create permission, just in case *PARANOIA*
- if ( !$this->mTitle->userCanCreate() ) {
+ if ( !$this->mTitle->userCan( 'create' ) ) {
wfDebug( "$fname: no create permission\n" );
$this->noCreatePermission();
wfProfileOut( $fname );
@@ -723,6 +740,8 @@ class EditPage {
$this->mArticle->clear(); # Force reload of dates, etc.
$this->mArticle->forUpdate( true ); # Lock the article
+ wfDebug("timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n");
+
if( $this->mArticle->getTimestamp() != $this->edittime ) {
$this->isConflict = true;
if( $this->section == 'new' ) {
@@ -794,7 +813,7 @@ class EditPage {
}
#And a similar thing for new sections
- if( $this->section == 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary' ) ) {
+ if( $this->section == 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary' ) ) {
if (trim($this->summary) == '') {
$this->missingSummary = true;
wfProfileOut( $fname );
@@ -860,10 +879,13 @@ class EditPage {
function initialiseForm() {
$this->edittime = $this->mArticle->getTimestamp();
$this->summary = '';
- $this->textbox1 = $this->getContent();
+ $this->textbox1 = $this->getContent(false);
+ if ($this->textbox1 === false) return false;
+
if ( !$this->mArticle->exists() && $this->mArticle->mTitle->getNamespace() == NS_MEDIAWIKI )
- $this->textbox1 = wfMsgWeirdKey( $this->mArticle->mTitle->getText() ) ;
+ $this->textbox1 = wfMsgWeirdKey( $this->mArticle->mTitle->getText() );
wfProxyCheck();
+ return true;
}
/**
@@ -878,7 +900,7 @@ class EditPage {
$fname = 'EditPage::showEditForm';
wfProfileIn( $fname );
- $sk =& $wgUser->getSkin();
+ $sk = $wgUser->getSkin();
wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) ) ;
@@ -920,15 +942,15 @@ class EditPage {
if ( $this->missingComment ) {
$wgOut->addWikiText( wfMsg( 'missingcommenttext' ) );
}
-
+
if( $this->missingSummary && $this->section != 'new' ) {
$wgOut->addWikiText( wfMsg( 'missingsummary' ) );
}
- if( $this->missingSummary && $this->section == 'new' ) {
- $wgOut->addWikiText( wfMsg( 'missingcommentheader' ) );
- }
-
+ if( $this->missingSummary && $this->section == 'new' ) {
+ $wgOut->addWikiText( wfMsg( 'missingcommentheader' ) );
+ }
+
if( !$this->hookError == '' ) {
$wgOut->addWikiText( $this->hookError );
}
@@ -936,11 +958,15 @@ class EditPage {
if ( !$this->checkUnicodeCompliantBrowser() ) {
$wgOut->addWikiText( wfMsg( 'nonunicodebrowser') );
}
- if ( isset( $this->mArticle )
- && isset( $this->mArticle->mRevision )
- && !$this->mArticle->mRevision->isCurrent() ) {
- $this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() );
- $wgOut->addWikiText( wfMsg( 'editingold' ) );
+ if ( isset( $this->mArticle ) && isset( $this->mArticle->mRevision ) ) {
+ // Let sysop know that this will make private content public if saved
+ if( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
+ }
+ if( !$this->mArticle->mRevision->isCurrent() ) {
+ $this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() );
+ $wgOut->addWikiText( wfMsg( 'editingold' ) );
+ }
}
}
@@ -958,24 +984,33 @@ class EditPage {
}
}
}
-
- if( $this->mTitle->isProtected( 'edit' ) ) {
- # Is the protection due to the namespace, e.g. interface text?
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- # Yes; remind the user
- $notice = wfMsg( 'editinginterface' );
- } elseif( $this->mTitle->isSemiProtected() ) {
- # No; semi protected
+
+ if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ # Show a warning if editing an interface message
+ $wgOut->addWikiText( wfMsg( 'editinginterface' ) );
+ } elseif( $this->mTitle->isProtected( 'edit' ) ) {
+ # Is the title semi-protected?
+ if( $this->mTitle->isSemiProtected() ) {
$notice = wfMsg( 'semiprotectedpagewarning' );
- if( wfEmptyMsg( 'semiprotectedpagewarning', $notice ) || $notice == '-' ) {
+ if( wfEmptyMsg( 'semiprotectedpagewarning', $notice ) || $notice == '-' )
$notice = '';
- }
} else {
- # No; regular protection
+ # Then it must be protected based on static groups (regular)
$notice = wfMsg( 'protectedpagewarning' );
}
$wgOut->addWikiText( $notice );
}
+ if ( $this->mTitle->isCascadeProtected() ) {
+ # Is this page under cascading protection from some source pages?
+ 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 )
+ $notice .= '* [[:' . $page->getPrefixedText() . "]]\n";
+ }
+ $wgOut->addWikiText( $notice );
+ }
if ( $this->kblength === false ) {
$this->kblength = (int)(strlen( $this->textbox1 ) / 1024);
@@ -1005,8 +1040,6 @@ class EditPage {
$summary = wfMsg('summary');
$subject = wfMsg('subject');
- $minor = wfMsgExt('minoredit', array('parseinline'));
- $watchthis = wfMsgExt('watchthis', array('parseinline'));
$cancel = $sk->makeKnownLink( $this->mTitle->getPrefixedText(),
wfMsgExt('cancel', array('parseinline')) );
@@ -1041,31 +1074,10 @@ class EditPage {
# Already watched
$this->watchthis = true;
}
-
- if( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
- }
-
- $minoredithtml = '';
-
- if ( $wgUser->isAllowed('minoredit') ) {
- $minoredithtml =
- "<input tabindex='3' type='checkbox' value='1' name='wpMinoredit'".($this->minoredit?" checked='checked'":"").
- " accesskey='".wfMsg('accesskey-minoredit')."' id='wpMinoredit' />\n".
- "<label for='wpMinoredit' title='".wfMsg('tooltip-minoredit')."'>{$minor}</label>\n";
- }
- $watchhtml = '';
-
- if ( $wgUser->isLoggedIn() ) {
- $watchhtml = "<input tabindex='4' type='checkbox' name='wpWatchthis'".
- ($this->watchthis?" checked='checked'":"").
- " accesskey=\"".htmlspecialchars(wfMsg('accesskey-watch'))."\" id='wpWatchthis' />\n".
- "<label for='wpWatchthis' title=\"" .
- htmlspecialchars(wfMsg('tooltip-watch'))."\">{$watchthis}</label>\n";
+ if( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
}
- $checkboxhtml = $minoredithtml . $watchhtml;
-
$wgOut->addHTML( $this->editFormPageTop );
if ( $wgUser->getOption( 'previewontop' ) ) {
@@ -1132,68 +1144,18 @@ class EditPage {
}
}
- $temp = array(
- 'id' => 'wpSave',
- 'name' => 'wpSave',
- 'type' => 'submit',
- 'tabindex' => '5',
- 'value' => wfMsg('savearticle'),
- 'accesskey' => wfMsg('accesskey-save'),
- 'title' => wfMsg('tooltip-save'),
- );
- $buttons['save'] = wfElement('input', $temp, '');
- $temp = array(
- 'id' => 'wpDiff',
- 'name' => 'wpDiff',
- 'type' => 'submit',
- 'tabindex' => '7',
- 'value' => wfMsg('showdiff'),
- 'accesskey' => wfMsg('accesskey-diff'),
- 'title' => wfMsg('tooltip-diff'),
- );
- $buttons['diff'] = wfElement('input', $temp, '');
+ $tabindex = 2;
- global $wgLivePreview;
- if ( $wgLivePreview && $wgUser->getOption( 'uselivepreview' ) ) {
- $temp = array(
- 'id' => 'wpPreview',
- 'name' => 'wpPreview',
- 'type' => 'submit',
- 'tabindex' => '6',
- 'value' => wfMsg('showpreview'),
- 'accesskey' => '',
- 'title' => wfMsg('tooltip-preview'),
- 'style' => 'display: none;',
- );
- $buttons['preview'] = wfElement('input', $temp, '');
- $temp = array(
- 'id' => 'wpLivePreview',
- 'name' => 'wpLivePreview',
- 'type' => 'submit',
- 'tabindex' => '6',
- 'value' => wfMsg('showlivepreview'),
- 'accesskey' => wfMsg('accesskey-preview'),
- 'title' => '',
- 'onclick' => $this->doLivePreviewScript(),
- );
- $buttons['live'] = wfElement('input', $temp, '');
- } else {
- $temp = array(
- 'id' => 'wpPreview',
- 'name' => 'wpPreview',
- 'type' => 'submit',
- 'tabindex' => '6',
- 'value' => wfMsg('showpreview'),
- 'accesskey' => wfMsg('accesskey-preview'),
- 'title' => wfMsg('tooltip-preview'),
- );
- $buttons['preview'] = wfElement('input', $temp, '');
- $buttons['live'] = '';
- }
+ $checkboxes = self::getCheckboxes( $tabindex, $sk,
+ array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) );
+
+ $checkboxhtml = implode( $checkboxes, "\n" );
+
+ $buttons = $this->getEditButtons( $tabindex );
+ $buttonshtml = implode( $buttons, "\n" );
$safemodehtml = $this->checkUnicodeCompliantBrowser()
- ? ""
- : "<input type='hidden' name=\"safemode\" value='1' />\n";
+ ? '' : Xml::hidden( 'safemode', '1' );
$wgOut->addHTML( <<<END
{$toolbar}
@@ -1205,6 +1167,8 @@ END
call_user_func_array( $formCallback, array( &$wgOut ) );
}
+ wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
+
// Put these up at the top to ensure they aren't lost on early form submission
$wgOut->addHTML( "
<input type='hidden' value=\"" . htmlspecialchars( $this->section ) . "\" name=\"wpSection\" />
@@ -1236,10 +1200,7 @@ END
$wgOut->addHTML(
"<div class='editButtons'>
- {$buttons['save']}
- {$buttons['preview']}
- {$buttons['live']}
- {$buttons['diff']}
+{$buttonshtml}
<span class='editHelp'>{$cancel} | {$edithelp}</span>
</div><!-- editButtons -->
</div><!-- editOptions -->");
@@ -1282,7 +1243,7 @@ END
if( $this->missingSummary ) {
$wgOut->addHTML( "<input type=\"hidden\" name=\"wpIgnoreBlankSummary\" value=\"1\" />\n" );
}
-
+
# For a bit more sophisticated detection of blank summaries, hash the
# automatic one and pass that in a hidden field.
$autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
@@ -1308,7 +1269,7 @@ END
} else {
$wgOut->addHTML( '<div id="wikiPreview"></div>' );
}
-
+
if ( $this->formtype == 'diff') {
$wgOut->addHTML( $this->getDiff() );
}
@@ -1361,7 +1322,7 @@ END
}
function getLastDelete() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$fname = 'EditPage::getLastDelete';
$res = $dbr->select(
array( 'logging', 'user' ),
@@ -1425,7 +1386,7 @@ END
# don't parse user css/js, show message about preview
# XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here
-
+
if ( $this->isCssJsSubpage ) {
if(preg_match("/\\.css$/", $wgTitle->getText() ) ) {
$previewtext = wfMsg('usercsspreview');
@@ -1469,16 +1430,16 @@ END
function blockedPage() {
global $wgOut, $wgUser;
$wgOut->blockedPage( false ); # Standard block notice on the top, don't 'return'
-
+
# If the user made changes, preserve them when showing the markup
- # (This happens when a user is blocked during edit, for instance)
+ # (This happens when a user is blocked during edit, for instance)
$first = $this->firsttime || ( !$this->save && $this->textbox1 == '' );
if( $first ) {
$source = $this->mTitle->exists() ? $this->getContent() : false;
} else {
$source = $this->textbox1;
}
-
+
# Spit out the source or the user's modified version
if( $source !== false ) {
$rows = $wgUser->getOption( 'rows' );
@@ -1496,14 +1457,14 @@ END
function userNotLoggedInPage() {
global $wgUser, $wgOut;
$skin = $wgUser->getSkin();
-
+
$loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
$loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $this->mTitle->getPrefixedUrl() );
-
+
$wgOut->setPageTitle( wfMsg( 'whitelistedittitle' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
-
+
$wgOut->addHtml( wfMsgWikiHtml( 'whitelistedittext', $loginLink ) );
$wgOut->returnToMain( false, $this->mTitle->getPrefixedUrl() );
}
@@ -1519,12 +1480,27 @@ END
$wgOut->setPageTitle( wfMsg( 'confirmedittitle' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
-
+
$wgOut->addWikiText( wfMsg( 'confirmedittext' ) );
$wgOut->returnToMain( false );
}
/**
+ * Creates a basic error page which informs the user that
+ * they have attempted to edit a nonexistant section.
+ */
+ function noSuchSectionPage() {
+ global $wgOut;
+
+ $wgOut->setPageTitle( wfMsg( 'nosuchsectiontitle' ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->setArticleRelated( false );
+
+ $wgOut->addWikiText( wfMsg( 'nosuchsectiontext', $this->section ) );
+ $wgOut->returnToMain( false );
+ }
+
+ /**
* Produce the stock "your edit contains spam" page
*
* @param $match Text which triggered one or more filters
@@ -1539,7 +1515,7 @@ END
$wgOut->addWikiText( wfMsg( 'spamprotectiontext' ) );
if ( $match )
$wgOut->addWikiText( wfMsg( 'spamprotectionmatch', "<nowiki>{$match}</nowiki>" ) );
-
+
$wgOut->returnToMain( false );
}
@@ -1551,7 +1527,7 @@ END
$fname = 'EditPage::mergeChangesInto';
wfProfileIn( $fname );
- $db =& wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_MASTER );
// This is the revision the editor started from
$baseRevision = Revision::loadFromTimestamp(
@@ -1645,90 +1621,102 @@ END
* can figure out a way to make them work in IE. However, we should make
* sure these keys are not defined on the edit page.
*/
- $toolarray=array(
- array( 'image'=>'button_bold.png',
- 'open' => '\\\'\\\'\\\'',
- 'close' => '\\\'\\\'\\\'',
- 'sample'=> wfMsg('bold_sample'),
- 'tip' => wfMsg('bold_tip'),
- 'key' => 'B'
- ),
- array( 'image'=>'button_italic.png',
- 'open' => '\\\'\\\'',
- 'close' => '\\\'\\\'',
- 'sample'=> wfMsg('italic_sample'),
- 'tip' => wfMsg('italic_tip'),
- 'key' => 'I'
- ),
- array( 'image'=>'button_link.png',
- 'open' => '[[',
- 'close' => ']]',
- 'sample'=> wfMsg('link_sample'),
- 'tip' => wfMsg('link_tip'),
- 'key' => 'L'
- ),
- array( 'image'=>'button_extlink.png',
- 'open' => '[',
- 'close' => ']',
- 'sample'=> wfMsg('extlink_sample'),
- 'tip' => wfMsg('extlink_tip'),
- 'key' => 'X'
- ),
- array( 'image'=>'button_headline.png',
- 'open' => "\\n== ",
- 'close' => " ==\\n",
- 'sample'=> wfMsg('headline_sample'),
- 'tip' => wfMsg('headline_tip'),
- 'key' => 'H'
- ),
- array( 'image'=>'button_image.png',
- 'open' => '[['.$wgContLang->getNsText(NS_IMAGE).":",
- 'close' => ']]',
- 'sample'=> wfMsg('image_sample'),
- 'tip' => wfMsg('image_tip'),
- 'key' => 'D'
- ),
- array( 'image' =>'button_media.png',
- 'open' => '[['.$wgContLang->getNsText(NS_MEDIA).':',
- 'close' => ']]',
- 'sample'=> wfMsg('media_sample'),
- 'tip' => wfMsg('media_tip'),
- 'key' => 'M'
- ),
- array( 'image' =>'button_math.png',
- 'open' => "<math>",
- 'close' => "<\\/math>",
- 'sample'=> wfMsg('math_sample'),
- 'tip' => wfMsg('math_tip'),
- 'key' => 'C'
- ),
- array( 'image' =>'button_nowiki.png',
- 'open' => "<nowiki>",
- 'close' => "<\\/nowiki>",
- 'sample'=> wfMsg('nowiki_sample'),
- 'tip' => wfMsg('nowiki_tip'),
- 'key' => 'N'
- ),
- array( 'image' =>'button_sig.png',
- 'open' => '--~~~~',
- 'close' => '',
- 'sample'=> '',
- 'tip' => wfMsg('sig_tip'),
- 'key' => 'Y'
- ),
- array( 'image' =>'button_hr.png',
- 'open' => "\\n----\\n",
- 'close' => '',
- 'sample'=> '',
- 'tip' => wfMsg('hr_tip'),
- 'key' => 'R'
- )
+ $toolarray = array(
+ array( 'image' => 'button_bold.png',
+ 'id' => 'mw-editbutton-bold',
+ 'open' => '\\\'\\\'\\\'',
+ 'close' => '\\\'\\\'\\\'',
+ 'sample'=> wfMsg('bold_sample'),
+ 'tip' => wfMsg('bold_tip'),
+ 'key' => 'B'
+ ),
+ array( 'image' => 'button_italic.png',
+ 'id' => 'mw-editbutton-italic',
+ 'open' => '\\\'\\\'',
+ 'close' => '\\\'\\\'',
+ 'sample'=> wfMsg('italic_sample'),
+ 'tip' => wfMsg('italic_tip'),
+ 'key' => 'I'
+ ),
+ array( 'image' => 'button_link.png',
+ 'id' => 'mw-editbutton-link',
+ 'open' => '[[',
+ 'close' => ']]',
+ 'sample'=> wfMsg('link_sample'),
+ 'tip' => wfMsg('link_tip'),
+ 'key' => 'L'
+ ),
+ array( 'image' => 'button_extlink.png',
+ 'id' => 'mw-editbutton-extlink',
+ 'open' => '[',
+ 'close' => ']',
+ 'sample'=> wfMsg('extlink_sample'),
+ 'tip' => wfMsg('extlink_tip'),
+ 'key' => 'X'
+ ),
+ array( 'image' => 'button_headline.png',
+ 'id' => 'mw-editbutton-headline',
+ 'open' => "\\n== ",
+ 'close' => " ==\\n",
+ 'sample'=> wfMsg('headline_sample'),
+ 'tip' => wfMsg('headline_tip'),
+ 'key' => 'H'
+ ),
+ array( 'image' => 'button_image.png',
+ 'id' => 'mw-editbutton-image',
+ 'open' => '[['.$wgContLang->getNsText(NS_IMAGE).":",
+ 'close' => ']]',
+ 'sample'=> wfMsg('image_sample'),
+ 'tip' => wfMsg('image_tip'),
+ 'key' => 'D'
+ ),
+ array( 'image' => 'button_media.png',
+ 'id' => 'mw-editbutton-media',
+ 'open' => '[['.$wgContLang->getNsText(NS_MEDIA).':',
+ 'close' => ']]',
+ 'sample'=> wfMsg('media_sample'),
+ 'tip' => wfMsg('media_tip'),
+ 'key' => 'M'
+ ),
+ array( 'image' => 'button_math.png',
+ 'id' => 'mw-editbutton-math',
+ 'open' => "<math>",
+ 'close' => "<\\/math>",
+ 'sample'=> wfMsg('math_sample'),
+ 'tip' => wfMsg('math_tip'),
+ 'key' => 'C'
+ ),
+ array( 'image' => 'button_nowiki.png',
+ 'id' => 'mw-editbutton-nowiki',
+ 'open' => "<nowiki>",
+ 'close' => "<\\/nowiki>",
+ 'sample'=> wfMsg('nowiki_sample'),
+ 'tip' => wfMsg('nowiki_tip'),
+ 'key' => 'N'
+ ),
+ array( 'image' => 'button_sig.png',
+ 'id' => 'mw-editbutton-signature',
+ 'open' => '--~~~~',
+ 'close' => '',
+ 'sample'=> '',
+ 'tip' => wfMsg('sig_tip'),
+ 'key' => 'Y'
+ ),
+ array( 'image' => 'button_hr.png',
+ 'id' => 'mw-editbutton-hr',
+ 'open' => "\\n----\\n",
+ 'close' => '',
+ 'sample'=> '',
+ 'tip' => wfMsg('hr_tip'),
+ 'key' => 'R'
+ )
);
$toolbar = "<div id='toolbar'>\n";
$toolbar.="<script type='$wgJsMimeType'>\n/*<![CDATA[*/\n";
foreach($toolarray as $tool) {
+ $cssId = $tool['id'];
$image=$wgStylePath.'/common/images/'.$tool['image'];
$open=$tool['open'];
$close=$tool['close'];
@@ -1742,7 +1730,7 @@ END
#$key = $tool["key"];
- $toolbar.="addButton('$image','$tip','$open','$close','$sample');\n";
+ $toolbar.="addButton('$image','$tip','$open','$close','$sample','$cssId');\n";
}
$toolbar.="/*]]>*/\n</script>";
@@ -1751,6 +1739,127 @@ END
}
/**
+ * Returns an array of html code of the following checkboxes:
+ * minor and watch
+ *
+ * @param $tabindex Current tabindex
+ * @param $skin Skin object
+ * @param $checked Array of checkbox => bool, where bool indicates the checked
+ * status of the checkbox
+ *
+ * @return array
+ */
+ public static function getCheckboxes( &$tabindex, $skin, $checked ) {
+ global $wgUser;
+
+ $checkboxes = array();
+
+ $checkboxes['minor'] = '';
+ $minorLabel = wfMsgExt('minoredit', array('parseinline'));
+ if ( $wgUser->isAllowed('minoredit') ) {
+ $attribs = array(
+ 'tabindex' => ++$tabindex,
+ 'accesskey' => wfMsg( 'accesskey-minoredit' ),
+ 'id' => 'wpMinoredit',
+ );
+ $checkboxes['minor'] =
+ Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
+ "&nbsp;<label for='wpMinoredit'".$skin->tooltipAndAccesskey('minoredit').">{$minorLabel}</label>";
+ }
+
+ $watchLabel = wfMsgExt('watchthis', array('parseinline'));
+ $checkboxes['watch'] = '';
+ if ( $wgUser->isLoggedIn() ) {
+ $attribs = array(
+ 'tabindex' => ++$tabindex,
+ 'accesskey' => wfMsg( 'accesskey-watch' ),
+ 'id' => 'wpWatchthis',
+ );
+ $checkboxes['watch'] =
+ Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
+ "&nbsp;<label for='wpWatchthis'".$skin->tooltipAndAccesskey('watch').">{$watchLabel}</label>";
+ }
+ return $checkboxes;
+ }
+
+ /**
+ * Returns an array of html code of the following buttons:
+ * save, diff, preview and live
+ *
+ * @param $tabindex Current tabindex
+ *
+ * @return array
+ */
+ public function getEditButtons(&$tabindex) {
+ global $wgLivePreview, $wgUser;
+
+ $buttons = array();
+
+ $temp = array(
+ 'id' => 'wpSave',
+ 'name' => 'wpSave',
+ 'type' => 'submit',
+ 'tabindex' => ++$tabindex,
+ 'value' => wfMsg('savearticle'),
+ 'accesskey' => wfMsg('accesskey-save'),
+ 'title' => wfMsg( 'tooltip-save' ).' ['.wfMsg( 'accesskey-save' ).']',
+ );
+ $buttons['save'] = wfElement('input', $temp, '');
+
+ ++$tabindex; // use the same for preview and live preview
+ if ( $wgLivePreview && $wgUser->getOption( 'uselivepreview' ) ) {
+ $temp = array(
+ 'id' => 'wpPreview',
+ 'name' => 'wpPreview',
+ 'type' => 'submit',
+ 'tabindex' => $tabindex,
+ 'value' => wfMsg('showpreview'),
+ 'accesskey' => '',
+ 'title' => wfMsg( 'tooltip-preview' ).' ['.wfMsg( 'accesskey-preview' ).']',
+ 'style' => 'display: none;',
+ );
+ $buttons['preview'] = wfElement('input', $temp, '');
+
+ $temp = array(
+ 'id' => 'wpLivePreview',
+ 'name' => 'wpLivePreview',
+ 'type' => 'submit',
+ 'tabindex' => $tabindex,
+ 'value' => wfMsg('showlivepreview'),
+ 'accesskey' => wfMsg('accesskey-preview'),
+ 'title' => '',
+ 'onclick' => $this->doLivePreviewScript(),
+ );
+ $buttons['live'] = wfElement('input', $temp, '');
+ } else {
+ $temp = array(
+ 'id' => 'wpPreview',
+ 'name' => 'wpPreview',
+ 'type' => 'submit',
+ 'tabindex' => $tabindex,
+ 'value' => wfMsg('showpreview'),
+ 'accesskey' => wfMsg('accesskey-preview'),
+ 'title' => wfMsg( 'tooltip-preview' ).' ['.wfMsg( 'accesskey-preview' ).']',
+ );
+ $buttons['preview'] = wfElement('input', $temp, '');
+ $buttons['live'] = '';
+ }
+
+ $temp = array(
+ 'id' => 'wpDiff',
+ 'name' => 'wpDiff',
+ 'type' => 'submit',
+ 'tabindex' => ++$tabindex,
+ 'value' => wfMsg('showdiff'),
+ 'accesskey' => wfMsg('accesskey-diff'),
+ 'title' => wfMsg( 'tooltip-diff' ).' ['.wfMsg( 'accesskey-diff' ).']',
+ );
+ $buttons['diff'] = wfElement('input', $temp, '');
+
+ return $buttons;
+ }
+
+ /**
* Output preview text only. This can be sucked into the edit page
* via JavaScript, and saves the server time rendering the skin as
* well as theoretically being more robust on the client (doesn't
@@ -1758,8 +1867,8 @@ END
* failure, etc).
*
* @todo This doesn't include category or interlanguage links.
- * Would need to enhance it a bit, maybe wrap them in XML
- * or something... that might also require more skin
+ * Would need to enhance it a bit, <s>maybe wrap them in XML
+ * or something...</s> that might also require more skin
* initialization, so check whether that's a problem.
*/
function livePreview() {
@@ -1767,10 +1876,14 @@ END
$wgOut->disable();
header( 'Content-type: text/xml; charset=utf-8' );
header( 'Cache-control: no-cache' );
- # FIXME
- echo $this->getPreviewText( );
- /* To not shake screen up and down between preview and live-preview */
- echo "<br style=\"clear:both;\" />\n";
+
+ $s =
+ '<?xml version="1.0" encoding="UTF-8" ?>' . "\n" .
+ Xml::openElement( 'livepreview' ) .
+ Xml::element( 'preview', null, $this->getPreviewText() ) .
+ Xml::element( 'br', array( 'style' => 'clear: both;' ) ) .
+ Xml::closeElement( 'livepreview' );
+ echo $s;
}
diff --git a/includes/Exception.php b/includes/Exception.php
index ad7ec14a..4cf0b7ba 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -1,5 +1,9 @@
<?php
+/**
+ * MediaWiki exception
+ * @addtogroup Exception
+ */
class MWException extends Exception
{
function useOutputPage() {
@@ -12,6 +16,7 @@ class MWException extends Exception
return is_object( $wgLang );
}
+ /** Get a message from i18n */
function msg( $key, $fallback /*[, params...] */ ) {
$args = array_slice( func_get_args(), 2 );
if ( $this->useMessageCache() ) {
@@ -21,6 +26,7 @@ class MWException extends Exception
}
}
+ /* If wgShowExceptionDetails, return a HTML message with a backtrace to the error. */
function getHTML() {
global $wgShowExceptionDetails;
if( $wgShowExceptionDetails ) {
@@ -33,6 +39,7 @@ class MWException extends Exception
}
}
+ /* If wgShowExceptionDetails, return a text message with a backtrace to the error */
function getText() {
global $wgShowExceptionDetails;
if( $wgShowExceptionDetails ) {
@@ -43,7 +50,8 @@ class MWException extends Exception
"in LocalSettings.php to show detailed debugging information.</p>";
}
}
-
+
+ /* Return titles of this error page */
function getPageTitle() {
if ( $this->useMessageCache() ) {
return wfMsg( 'internalerror' );
@@ -52,7 +60,10 @@ class MWException extends Exception
return "$wgSitename error";
}
}
-
+
+ /** Return the requested URL and point to file and line number from which the
+ * exception occured
+ */
function getLogMessage() {
global $wgRequest;
$file = $this->getFile();
@@ -60,7 +71,8 @@ class MWException extends Exception
$message = $this->getMessage();
return $wgRequest->getRequestURL() . " Exception from line $line of $file: $message";
}
-
+
+ /** Output the exception report using HTML */
function reportHTML() {
global $wgOut;
if ( $this->useOutputPage() ) {
@@ -78,11 +90,15 @@ class MWException extends Exception
echo $this->htmlFooter();
}
}
-
+
+ /** Print the exception report using text */
function reportText() {
echo $this->getText();
}
+ /* Output a report about the exception and takes care of formatting.
+ * It will be either HTML or plain text based on $wgCommandLineMode.
+ */
function report() {
global $wgCommandLineMode;
if ( $wgCommandLineMode ) {
@@ -125,6 +141,7 @@ class MWException extends Exception
/**
* Exception class which takes an HTML error message, and does not
* produce a backtrace. Replacement for OutputPage::fatalError().
+ * @addtogroup Exception
*/
class FatalError extends MWException {
function getHTML() {
@@ -136,6 +153,9 @@ class FatalError extends MWException {
}
}
+/**
+ * @addtogroup Exception
+ */
class ErrorPageError extends MWException {
public $title, $msg;
@@ -203,7 +223,7 @@ function wfReportException( Exception $e ) {
function wfExceptionHandler( $e ) {
global $wgFullyInitialised;
wfReportException( $e );
-
+
// Final cleanup, similar to wfErrorExit()
if ( $wgFullyInitialised ) {
try {
diff --git a/includes/Exif.php b/includes/Exif.php
index 0860d5f7..3a06ca1b 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -1,7 +1,6 @@
<?php
/**
- * @package MediaWiki
- * @subpackage Metadata
+ * @addtogroup Media
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -22,13 +21,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @link http://exif.org/Exif2-2.PDF The Exif 2.2 specification
- * @bug 1555, 1947
+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 specification
*/
/**
- * @package MediaWiki
- * @subpackage Metadata
+ * @todo document (e.g. one-sentence class-overview description)
+ * @addtogroup Media
*/
class Exif {
//@{
@@ -95,9 +93,9 @@ class Exif {
var $basename;
/**
- * The private log to log to
+ * The private log to log to, e.g. 'exif'
*/
- var $log = 'exif';
+ var $log = false;
//@}
@@ -106,7 +104,7 @@ class Exif {
*
* @param $file String: filename.
*/
- function Exif( $file ) {
+ function __construct( $file ) {
/**
* Page numbers here refer to pages in the EXIF 2.2 standard
*
@@ -563,7 +561,10 @@ class Exif {
* @param $fname String:
* @param $action Mixed: , default NULL.
*/
- function debug( $in, $fname, $action = NULL ) {
+ function debug( $in, $fname, $action = NULL ) {
+ if ( !$this->log ) {
+ return;
+ }
$type = gettype( $in );
$class = ucfirst( __CLASS__ );
if ( $type === 'array' )
@@ -588,6 +589,9 @@ class Exif {
* @param $io Boolean: Specify whether we're beginning or ending
*/
function debugFile( $fname, $io ) {
+ if ( !$this->log ) {
+ return;
+ }
$class = ucfirst( __CLASS__ );
if ( $io ) {
wfDebugLog( $this->log, "$class::$fname: begin processing: '{$this->basename}'\n" );
@@ -599,8 +603,8 @@ class Exif {
}
/**
- * @package MediaWiki
- * @subpackage Metadata
+ * @todo document (e.g. one-sentence class-overview description)
+ * @addtogroup Media
*/
class FormatExif {
/**
@@ -733,7 +737,7 @@ class FormatExif {
case 'DateTimeDigitized':
if( $val == '0000:00:00 00:00:00' ) {
$tags[$tag] = wfMsg('exif-unknowndate');
- } elseif( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/', $val ) ) {
+ } elseif( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/', $val ) ) {
$tags[$tag] = $wgLang->timeanddate( wfTimestamp(TS_MW, $val) );
}
break;
diff --git a/includes/Export.php b/includes/Export.php
index b7e0f9a1..9307795d 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -17,16 +17,15 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
+
/**
*
- * @package MediaWiki
- * @subpackage SpecialPage
+ * @addtogroup SpecialPage
*/
-
class WikiExporter {
var $list_authors = false ; # Return distinct author list (when not returning full history)
var $author_list = "" ;
-
+
const FULL = 0;
const CURRENT = 1;
@@ -44,14 +43,14 @@ class WikiExporter {
* main query is still running.
*
* @param Database $db
- * @param mixed $history one of WikiExporter::FULL or WikiExporter::CURRENT, or an
+ * @param mixed $history one of WikiExporter::FULL or WikiExporter::CURRENT, or an
* associative array:
* offset: non-inclusive offset at which to start the query
* limit: maximum number of rows to return
* dir: "asc" or "desc" timestamp order
* @param int $buffer one of WikiExporter::BUFFER or WikiExporter::STREAM
*/
- function WikiExporter( &$db, $history = WikiExporter::CURRENT,
+ function __construct( &$db, $history = WikiExporter::CURRENT,
$buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
$this->db =& $db;
$this->history = $history;
@@ -140,7 +139,10 @@ class WikiExporter {
$fname = "do_list_authors" ;
wfProfileIn( $fname );
$this->author_list = "<contributors>";
- $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND " . $cond ;
+ //rev_deleted
+ $nothidden = '(rev_deleted & '.Revision::DELETED_USER.') = 0';
+
+ $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND $nothidden AND " . $cond ;
$result = $this->db->query( $sql, $fname );
$resultset = $this->db->resultObject( $result );
while( $row = $resultset->fetchObject() ) {
@@ -164,10 +166,10 @@ class WikiExporter {
$page = $this->db->tableName( 'page' );
$revision = $this->db->tableName( 'revision' );
$text = $this->db->tableName( 'text' );
-
+
$order = 'ORDER BY page_id';
$limit = '';
-
+
if( $this->history == WikiExporter::FULL ) {
$join = 'page_id=rev_page';
} elseif( $this->history == WikiExporter::CURRENT ) {
@@ -185,7 +187,7 @@ class WikiExporter {
$order .= ', rev_timestamp DESC';
}
if ( !empty( $this->history['offset'] ) ) {
- $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
+ $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
$this->db->timestamp( $this->history['offset'] ) );
}
if ( !empty( $this->history['limit'] ) ) {
@@ -229,7 +231,7 @@ class WikiExporter {
$result = $this->db->query( $sql, $fname );
$wrapper = $this->db->resultObject( $result );
$this->outputStream( $wrapper );
-
+
if ( $this->list_authors ) {
$this->outputStream( $wrapper );
}
@@ -279,6 +281,9 @@ class WikiExporter {
}
}
+/**
+ * @addtogroup Dump
+ */
class XmlDumpWriter {
/**
@@ -461,6 +466,7 @@ class XmlDumpWriter {
/**
* Base class for output stream; prints to stdout or buffer or whereever.
+ * @addtogroup Dump
*/
class DumpOutput {
function writeOpenStream( $string ) {
@@ -494,6 +500,7 @@ class DumpOutput {
/**
* Stream outputter to send data to a file.
+ * @addtogroup Dump
*/
class DumpFileOutput extends DumpOutput {
var $handle;
@@ -511,6 +518,7 @@ class DumpFileOutput extends DumpOutput {
* Stream outputter to send data to a file via some filter program.
* Even if compression is available in a library, using a separate
* program can allow us to make use of a multi-processor system.
+ * @addtogroup Dump
*/
class DumpPipeOutput extends DumpFileOutput {
function DumpPipeOutput( $command, $file = null ) {
@@ -523,6 +531,7 @@ class DumpPipeOutput extends DumpFileOutput {
/**
* Sends dump output via the gzip compressor.
+ * @addtogroup Dump
*/
class DumpGZipOutput extends DumpPipeOutput {
function DumpGZipOutput( $file ) {
@@ -532,6 +541,7 @@ class DumpGZipOutput extends DumpPipeOutput {
/**
* Sends dump output via the bgzip2 compressor.
+ * @addtogroup Dump
*/
class DumpBZip2Output extends DumpPipeOutput {
function DumpBZip2Output( $file ) {
@@ -541,6 +551,7 @@ class DumpBZip2Output extends DumpPipeOutput {
/**
* Sends dump output via the p7zip compressor.
+ * @addtogroup Dump
*/
class Dump7ZipOutput extends DumpPipeOutput {
function Dump7ZipOutput( $file ) {
@@ -558,6 +569,7 @@ class Dump7ZipOutput extends DumpPipeOutput {
* Dump output filter class.
* This just does output filtering and streaming; XML formatting is done
* higher up, so be careful in what you do.
+ * @addtogroup Dump
*/
class DumpFilter {
function DumpFilter( &$sink ) {
@@ -603,6 +615,7 @@ class DumpFilter {
/**
* Simple dump output filter to exclude all talk pages.
+ * @addtogroup Dump
*/
class DumpNotalkFilter extends DumpFilter {
function pass( $page ) {
@@ -612,6 +625,7 @@ class DumpNotalkFilter extends DumpFilter {
/**
* Dump output filter to include or exclude pages in a given set of namespaces.
+ * @addtogroup Dump
*/
class DumpNamespaceFilter extends DumpFilter {
var $invert = false;
@@ -666,6 +680,7 @@ class DumpNamespaceFilter extends DumpFilter {
/**
* Dump output filter to include only the last revision in each page sequence.
+ * @addtogroup Dump
*/
class DumpLatestFilter extends DumpFilter {
var $page, $pageString, $rev, $revString;
@@ -697,6 +712,7 @@ class DumpLatestFilter extends DumpFilter {
/**
* Base class for output stream; prints to stdout or buffer or whereever.
+ * @addtogroup Dump
*/
class DumpMultiWriter {
function DumpMultiWriter( $sinks ) {
diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php
index 14b55fdb..c8ed8bde 100644
--- a/includes/ExternalEdit.php
+++ b/includes/ExternalEdit.php
@@ -3,12 +3,10 @@
* License: Public domain
*
* @author Erik Moeller <moeller@scireview.de>
- * @package MediaWiki
*/
/**
*
- * @package MediaWiki
*
* Support for external editors to modify both text and files
* in external applications. It works as follows: MediaWiki
@@ -22,7 +20,7 @@
class ExternalEdit {
- function ExternalEdit ( $article, $mode ) {
+ function __construct( $article, $mode ) {
global $wgInputEncoding;
$this->mArticle =& $article;
$this->mTitle =& $article->mTitle;
diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php
index 79f1a528..fb66b652 100644
--- a/includes/ExternalStore.php
+++ b/includes/ExternalStore.php
@@ -1,7 +1,6 @@
<?php
/**
*
- * @package MediaWiki
*
* Constructor class for data kept in external repositories
*
diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php
index 861a9939..7b4ffc2f 100644
--- a/includes/ExternalStoreDB.php
+++ b/includes/ExternalStoreDB.php
@@ -1,14 +1,12 @@
<?php
/**
*
- * @package MediaWiki
*
* DB accessable external objects
*
*/
-/** @package MediaWiki */
/**
* External database storage will use one (or more) separate connection pools
diff --git a/includes/ExternalStoreHttp.php b/includes/ExternalStoreHttp.php
index daf62cc4..e6656986 100644
--- a/includes/ExternalStoreHttp.php
+++ b/includes/ExternalStoreHttp.php
@@ -1,7 +1,6 @@
<?php
/**
*
- * @package MediaWiki
*
* Example class for HTTP accessable external objects
*
diff --git a/includes/FakeTitle.php b/includes/FakeTitle.php
index ae05385a..293bdaf0 100644
--- a/includes/FakeTitle.php
+++ b/includes/FakeTitle.php
@@ -14,7 +14,6 @@ class FakeTitle {
function getInterwikiCached() { $this->error(); }
function isLocal() { $this->error(); }
function isTrans() { $this->error(); }
- function touchArray( $titles, $timestamp = '' ) { $this->error(); }
function getText() { $this->error(); }
function getPartialURL() { $this->error(); }
function getDBkey() { $this->error(); }
@@ -41,6 +40,7 @@ class FakeTitle {
function isProtected() { $this->error(); }
function userIsWatching() { $this->error(); }
function userCan() { $this->error(); }
+ function userCanCreate() { $this->error(); }
function userCanEdit() { $this->error(); }
function userCanMove() { $this->error(); }
function isMovable() { $this->error(); }
@@ -71,7 +71,6 @@ class FakeTitle {
function moveOverExistingRedirect() { $this->error(); }
function moveToNewTitle() { $this->error(); }
function isValidMoveTarget() { $this->error(); }
- function createRedirect() { $this->error(); }
function getParentCategories() { $this->error(); }
function getParentCategoryTree() { $this->error(); }
function pageCond() { $this->error(); }
diff --git a/includes/Feed.php b/includes/Feed.php
index 5c14865d..ed4343c3 100644
--- a/includes/Feed.php
+++ b/includes/Feed.php
@@ -1,6 +1,5 @@
<?php
-# Basic support for outputting syndication feeds in RSS, other formats
-#
+
# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
# http://www.mediawiki.org/
#
@@ -20,15 +19,13 @@
# http://www.gnu.org/copyleft/gpl.html
/**
+ * Basic support for outputting syndication feeds in RSS, other formats.
* Contain a feed class as well as classes to build rss / atom ... feeds
* Available feeds are defined in Defines.php
- * @package MediaWiki
*/
-
/**
- * @todo document
- * @package MediaWiki
+ * A base class for basic support for outputting syndication feeds in RSS and other formats.
*/
class FeedItem {
/**#@+
@@ -45,7 +42,7 @@ class FeedItem {
/**#@+
* @todo document
*/
- function FeedItem( $Title, $Description, $Url, $Date = '', $Author = '', $Comments = '' ) {
+ function __construct( $Title, $Description, $Url, $Date = '', $Author = '', $Comments = '' ) {
$this->Title = $Title;
$this->Description = $Description;
$this->Url = $Url;
@@ -77,8 +74,7 @@ class FeedItem {
}
/**
- * @todo document
- * @package MediaWiki
+ * @todo document (needs one-sentence top-level class description).
*/
class ChannelFeed extends FeedItem {
/**#@+
@@ -160,8 +156,6 @@ class ChannelFeed extends FeedItem {
/**
* Generate a RSS feed
- * @todo document
- * @package MediaWiki
*/
class RSSFeed extends ChannelFeed {
@@ -221,8 +215,6 @@ class RSSFeed extends ChannelFeed {
/**
* Generate an Atom feed
- * @todo document
- * @package MediaWiki
*/
class AtomFeed extends ChannelFeed {
/**
diff --git a/includes/FileStore.php b/includes/FileStore.php
index 1fd35b01..dcec71c5 100644
--- a/includes/FileStore.php
+++ b/includes/FileStore.php
@@ -1,5 +1,8 @@
<?php
+/**
+ * @todo document (needs one-sentence top-level class description).
+ */
class FileStore {
const DELETE_ORIGINAL = 1;
@@ -33,7 +36,7 @@ class FileStore {
* suffer an uncaught error the lock will be released when the
* connection is closed.
*
- * @fixme Probably only works on MySQL. Abstract to the Database class?
+ * @todo Probably only works on MySQL. Abstract to the Database class?
*/
static function lock() {
global $wgDBtype;
@@ -106,7 +109,7 @@ class FileStore {
private function copyFile( $sourcePath, $destPath, $flags=0 ) {
if( !file_exists( $sourcePath ) ) {
// Abort! Abort!
- throw new FSException( "missing source file '$sourcePath'\n" );
+ throw new FSException( "missing source file '$sourcePath'" );
}
$transaction = new FSTransaction();
@@ -125,7 +128,7 @@ class FileStore {
if( !$ok ) {
throw new FSException(
- "failed to create directory for '$destPath'\n" );
+ "failed to create directory for '$destPath'" );
}
}
@@ -138,7 +141,7 @@ class FileStore {
$transaction->addRollback( FSTransaction::DELETE_FILE, $destPath );
} else {
throw new FSException(
- __METHOD__." failed to copy '$sourcePath' to '$destPath'\n" );
+ __METHOD__." failed to copy '$sourcePath' to '$destPath'" );
}
}
@@ -175,7 +178,7 @@ class FileStore {
* @throws FSException if file can't be deleted
* @return FSTransaction
*
- * @fixme Might be worth preliminary permissions check
+ * @todo Might be worth preliminary permissions check
*/
static function deleteFile( $path ) {
if( file_exists( $path ) ) {
@@ -368,6 +371,9 @@ class FSTransaction {
}
}
+/**
+ * @addtogroup Exception
+ */
class FSException extends MWException { }
?>
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index de07b321..1ffde741 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -1,8 +1,11 @@
<?php
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( "This file is part of MediaWiki, it is not a valid entry point" );
+}
+
/**
* Global functions used everywhere
- * @package MediaWiki
*/
/**
@@ -19,9 +22,9 @@ $wgTotalViews = -1;
$wgTotalEdits = -1;
-require_once( 'LogPage.php' );
-require_once( 'normal/UtfNormalUtil.php' );
-require_once( 'XmlFunctions.php' );
+require_once dirname(__FILE__) . '/LogPage.php';
+require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php';
+require_once dirname(__FILE__) . '/XmlFunctions.php';
/**
* Compatibility functions
@@ -57,6 +60,30 @@ if ( !function_exists( 'mb_substr' ) ) {
}
}
+if ( !function_exists( 'mb_strlen' ) ) {
+ /**
+ * Fallback implementation of mb_strlen, hardcoded to UTF-8.
+ * @param string $str
+ * @param string $enc optional encoding; ignored
+ * @return int
+ */
+ function mb_strlen( $str, $enc="" ) {
+ $counts = count_chars( $str );
+ $total = 0;
+
+ // Count ASCII bytes
+ for( $i = 0; $i < 0x80; $i++ ) {
+ $total += $counts[$i];
+ }
+
+ // Count multibyte sequence heads
+ for( $i = 0xc0; $i < 0xff; $i++ ) {
+ $total += $counts[$i];
+ }
+ return $total;
+ }
+}
+
if ( !function_exists( 'array_diff_key' ) ) {
/**
* Exists in PHP 5.1.0+
@@ -168,7 +195,7 @@ function wfDebug( $text, $logonly = false ) {
# Strip unprintables; they can switch terminal modes when binary data
# gets dumped, which is pretty annoying.
$text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
- @error_log( $text, 3, $wgDebugLogFile );
+ wfErrorLog( $text, $wgDebugLogFile );
}
}
@@ -187,7 +214,7 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
if( isset( $wgDebugLogGroups[$logGroup] ) ) {
$time = wfTimestamp( TS_DB );
$wiki = wfWikiID();
- @error_log( "$time $wiki: $text", 3, $wgDebugLogGroups[$logGroup] );
+ wfErrorLog( "$time $wiki: $text", $wgDebugLogGroups[$logGroup] );
} else if ( $public === true ) {
wfDebug( $text, true );
}
@@ -198,15 +225,28 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
* @param $text String: database error message.
*/
function wfLogDBError( $text ) {
- global $wgDBerrorLog;
+ global $wgDBerrorLog, $wgDBname;
if ( $wgDBerrorLog ) {
$host = trim(`hostname`);
- $text = date('D M j G:i:s T Y') . "\t$host\t".$text;
- error_log( $text, 3, $wgDBerrorLog );
+ $text = date('D M j G:i:s T Y') . "\t$host\t$wgDBname\t$text";
+ wfErrorLog( $text, $wgDBerrorLog );
}
}
/**
+ * Log to a file without getting "file size exceeded" signals
+ */
+function wfErrorLog( $text, $file ) {
+ wfSuppressWarnings();
+ $exists = file_exists( $file );
+ $size = $exists ? filesize( $file ) : false;
+ if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
+ error_log( $text, 3, $file );
+ }
+ wfRestoreWarnings();
+}
+
+/**
* @todo document
*/
function wfLogProfilingData() {
@@ -232,7 +272,7 @@ function wfLogProfilingData() {
gmdate( 'YmdHis' ), $elapsed,
urldecode( $wgRequest->getRequestURL() . $forward ) );
if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
- error_log( $log . $prof, 3, $wgDebugLogFile );
+ wfErrorLog( $log . $prof, $wgDebugLogFile );
}
}
}
@@ -376,8 +416,11 @@ function wfMsgNoDBForContent( $key ) {
* @return String: the requested message.
*/
function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) {
+ $fname = 'wfMsgReal';
+ wfProfileIn( $fname );
$message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
$message = wfMsgReplaceArgs( $message, $args );
+ wfProfileOut( $fname );
return $message;
}
@@ -515,11 +558,11 @@ function wfMsgWikiHtml( $key ) {
* Returns message in the requested format
* @param string $key Key of the message
* @param array $options Processing rules:
- * <i>parse<i>: parses wikitext to html
- * <i>parseinline<i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
- * <i>escape<i>: filters message trough htmlspecialchars
- * <i>replaceafter<i>: parameters are substituted after parsing or escaping
- * <i>parsemag<i>: ??
+ * <i>parse</i>: parses wikitext to html
+ * <i>parseinline</i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
+ * <i>escape</i>: filters message trough htmlspecialchars
+ * <i>replaceafter</i>: parameters are substituted after parsing or escaping
+ * <i>parsemag</i>: transform the message using magic phrases
*/
function wfMsgExt( $key, $options ) {
global $wgOut, $wgParser;
@@ -569,7 +612,7 @@ function wfMsgExt( $key, $options ) {
* Just like exit() but makes a note of it.
* Commits open transactions except if the error parameter is set
*
- * @obsolete Please return control to the caller or throw an exception
+ * @deprecated Please return control to the caller or throw an exception
*/
function wfAbruptExit( $error = false ){
global $wgLoadBalancer;
@@ -599,7 +642,7 @@ function wfAbruptExit( $error = false ){
}
/**
- * @obsolete Please return control the caller or throw an exception
+ * @deprecated Please return control the caller or throw an exception
*/
function wfErrorExit() {
wfAbruptExit( true );
@@ -736,7 +779,7 @@ function wfBacktrace() {
*/
function wfShowingResults( $offset, $limit ) {
global $wgLang;
- return wfMsg( 'showingresults', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
+ return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
}
/**
@@ -744,7 +787,7 @@ function wfShowingResults( $offset, $limit ) {
*/
function wfShowingResultsNum( $offset, $limit, $num ) {
global $wgLang;
- return wfMsg( 'showingresultsnum', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
+ return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
}
/**
@@ -941,6 +984,26 @@ function wfArrayToCGI( $array1, $array2 = NULL )
}
/**
+ * Append a query string to an existing URL, which may or may not already
+ * have query string parameters already. If so, they will be combined.
+ *
+ * @param string $url
+ * @param string $query
+ * @return string
+ */
+function wfAppendQuery( $url, $query ) {
+ if( $query != '' ) {
+ if( false === strpos( $url, '?' ) ) {
+ $url .= '?';
+ } else {
+ $url .= '&';
+ }
+ $url .= $query;
+ }
+ return $url;
+}
+
+/**
* This is obsolete, use SquidUpdate::purge()
* @deprecated
*/
@@ -1104,9 +1167,15 @@ function wfHttpError( $code, $label, $desc ) {
* Note that some PHP configuration options may add output buffer
* layers which cannot be removed; these are left in place.
*
- * @parameter bool $resetGzipEncoding
+ * @param bool $resetGzipEncoding
*/
function wfResetOutputBuffers( $resetGzipEncoding=true ) {
+ if( $resetGzipEncoding ) {
+ // Suppress Content-Encoding and Content-Length
+ // headers from 1.10+s wfOutputHandler
+ global $wgDisableOutputCompression;
+ $wgDisableOutputCompression = true;
+ }
while( $status = ob_get_status() ) {
if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
// Probably from zlib.output_compression or other
@@ -1332,7 +1401,7 @@ define('TS_ISO_8601', 4);
/**
* An Exif timestamp (YYYY:MM:DD HH:MM:SS)
*
- * @url http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
* DateTime tag and page 36 for the DateTimeOriginal and
* DateTimeDigitized tags.
*/
@@ -1624,6 +1693,7 @@ function wfMkdirParents( $fullDir, $mode = 0777 ) {
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;
}
}
@@ -1750,14 +1820,14 @@ function wfShellExec( $cmd, &$retval=null ) {
}
if ( php_uname( 's' ) == 'Linux' ) {
- $time = ini_get( 'max_execution_time' );
+ $time = intval( ini_get( 'max_execution_time' ) );
$mem = intval( $wgMaxShellMemory );
$filesize = intval( $wgMaxShellFileSize );
if ( $time > 0 && $mem > 0 ) {
- $script = "$IP/bin/ulimit-tvf.sh";
+ $script = "$IP/bin/ulimit4.sh";
if ( is_executable( $script ) ) {
- $cmd = escapeshellarg( $script ) . " $time $mem $filesize $cmd";
+ $cmd = escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd );
}
}
} elseif ( php_uname( 's' ) == 'Windows NT' ) {
@@ -1844,23 +1914,83 @@ function wfBaseName( $path ) {
}
/**
+ * Generate a relative path name to the given file.
+ * May explode on non-matching case-insensitive paths,
+ * funky symlinks, etc.
+ *
+ * @param string $path Absolute destination path including target filename
+ * @param string $from Absolute source path, directory only
+ * @return string
+ */
+function wfRelativePath( $path, $from ) {
+ // Normalize mixed input on Windows...
+ $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
+ $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
+
+ $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
+ $against = explode( DIRECTORY_SEPARATOR, $from );
+
+ // Trim off common prefix
+ while( count( $pieces ) && count( $against )
+ && $pieces[0] == $against[0] ) {
+ array_shift( $pieces );
+ array_shift( $against );
+ }
+
+ // relative dots to bump us to the parent
+ while( count( $against ) ) {
+ array_unshift( $pieces, '..' );
+ array_shift( $against );
+ }
+
+ array_push( $pieces, wfBaseName( $path ) );
+
+ return implode( DIRECTORY_SEPARATOR, $pieces );
+}
+
+/**
* Make a URL index, appropriate for the el_index field of externallinks.
*/
function wfMakeUrlIndex( $url ) {
- wfSuppressWarnings();
+ global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
$bits = parse_url( $url );
+ wfSuppressWarnings();
wfRestoreWarnings();
- if ( !$bits || $bits['scheme'] !== 'http' ) {
+ if ( !$bits ) {
return false;
}
+ // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
+ $delimiter = '';
+ if ( in_array( $bits['scheme'] . '://' , $wgUrlProtocols ) ) {
+ $delimiter = '://';
+ } elseif ( in_array( $bits['scheme'] .':' , $wgUrlProtocols ) ) {
+ $delimiter = ':';
+ // parse_url detects for news: and mailto: the host part of an url as path
+ // We have to correct this wrong detection
+ if ( isset ( $bits['path'] ) ) {
+ $bits['host'] = $bits['path'];
+ $bits['path'] = '';
+ }
+ } else {
+ return false;
+ }
+
// Reverse the labels in the hostname, convert to lower case
- $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+ // For emails reverse domainpart only
+ if ( $bits['scheme'] == 'mailto' ) {
+ $mailparts = explode( '@', $bits['host'] );
+ $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
+ $reversedHost = $domainpart . '@' . $mailparts[0];
+ } else {
+ $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+ }
// Add an extra dot to the end
if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
$reversedHost .= '.';
}
// Reconstruct the pseudo-URL
- $index = "http://$reversedHost";
+ $prot = $bits['scheme'];
+ $index = "$prot$delimiter$reversedHost";
// Leave out user and password. Add the port, path, query and fragment
if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port'];
if ( isset( $bits['path'] ) ) {
@@ -1908,9 +2038,11 @@ function wfExplodeMarkup( $separator, $text ) {
* @param $sourceBase int 2-36
* @param $destBase int 2-36
* @param $pad int 1 or greater
+ * @param $lowercase bool
* @return string or false on invalid input
*/
-function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
+function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) {
+ $input = strval( $input );
if( $sourceBase < 2 ||
$sourceBase > 36 ||
$destBase < 2 ||
@@ -1923,8 +2055,7 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
$input == '' ) {
return false;
}
-
- $digitChars = '0123456789abcdefghijklmnopqrstuvwxyz';
+ $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$inDigits = array();
$outChars = '';
@@ -2024,7 +2155,7 @@ function wfIsLocalURL( $url ) {
* Initialise php session
*/
function wfSetupSession() {
- global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
+ global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure;
if( $wgSessionsInMemcached ) {
require_once( 'MemcachedSessions.php' );
} elseif( 'files' != ini_get( 'session.save_handler' ) ) {
@@ -2032,7 +2163,7 @@ function wfSetupSession() {
# application, it will end up failing. Try to recover.
ini_set ( 'session.save_handler', 'files' );
}
- session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
+ session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
session_cache_limiter( 'private, must-revalidate' );
@session_start();
}
diff --git a/includes/HTMLCacheUpdate.php b/includes/HTMLCacheUpdate.php
index bda4720d..9a0b6a08 100644
--- a/includes/HTMLCacheUpdate.php
+++ b/includes/HTMLCacheUpdate.php
@@ -38,7 +38,7 @@ class HTMLCacheUpdate
function doUpdate() {
# Fetch the IDs
$cond = $this->getToCondition();
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( $this->mTable, $this->getFromField(), $cond, __METHOD__ );
$resWrap = new ResultWrapper( $dbr, $res );
if ( $dbr->numRows( $res ) != 0 ) {
@@ -136,7 +136,7 @@ class HTMLCacheUpdate
return;
}
- $dbw =& wfGetDB( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$timestamp = $dbw->timestamp();
$done = false;
@@ -184,6 +184,9 @@ class HTMLCacheUpdate
}
}
+/**
+ * @todo document (e.g. one-sentence top-level class description).
+ */
class HTMLCacheUpdateJob extends Job {
var $table, $start, $end;
@@ -218,7 +221,7 @@ class HTMLCacheUpdateJob extends Job {
$conds[] = "$fromField <= {$this->end}";
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( $this->table, $fromField, $conds, __METHOD__ );
$update->invalidateIDs( new ResultWrapper( $dbr, $res ) );
$dbr->freeResult( $res );
diff --git a/includes/HTMLFileCache.php b/includes/HTMLFileCache.php
index d85a4411..1d3778b2 100644
--- a/includes/HTMLFileCache.php
+++ b/includes/HTMLFileCache.php
@@ -1,8 +1,7 @@
<?php
/**
* Contain the HTMLFileCache class
- * @package MediaWiki
- * @subpackage Cache
+ * @addtogroup Cache
*/
/**
@@ -16,7 +15,6 @@
* $wgUseFileCache
* $wgFileCacheDirectory
* $wgUseGzip
- * @package MediaWiki
*/
class HTMLFileCache {
var $mTitle, $mFileCache;
diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php
index 189e5c79..715c8c88 100644
--- a/includes/HTMLForm.php
+++ b/includes/HTMLForm.php
@@ -2,13 +2,11 @@
/**
* This file contain a class to easily build HTML forms as well as custom
* functions used by SpecialUserrights.php
- * @package MediaWiki
*/
/**
* Class to build various forms
*
- * @package MediaWiki
* @author jeluf, hashar
*/
class HTMLForm {
@@ -125,6 +123,7 @@ class HTMLForm {
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(
@@ -134,6 +133,7 @@ function HTMLSelectGroups($selectname, $selectmsg, $selected=array(), $multiple=
} else {
$attribs = array( 'name' => $selectname );
}
+ $attribs['style'] = 'width: 100%';
$out .= wfElement( 'select', $attribs, null );
foreach( $groups as $group ) {
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index a06b620d..9dfd6d61 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -1,82 +1,86 @@
<?php
/**
*
- * @package MediaWiki
*/
/**
* Pure virtual parent
- * @package MediaWiki
+ * @todo document (needs a one-sentence top-level class description, that answers the question: "what is a HistoryBlob?")
*/
-class HistoryBlob
+interface HistoryBlob
{
/**
* setMeta and getMeta currently aren't used for anything, I just thought
* they might be useful in the future.
* @param $meta String: a single string.
*/
- function setMeta( $meta ) {}
+ public function setMeta( $meta );
/**
* setMeta and getMeta currently aren't used for anything, I just thought
* they might be useful in the future.
* Gets the meta-value
*/
- function getMeta() {}
+ public function getMeta();
/**
* Adds an item of text, returns a stub object which points to the item.
* You must call setLocation() on the stub object before storing it to the
* database
*/
- function addItem() {}
+ public function addItem( $text );
/**
* Get item by hash
*/
- function getItem( $hash ) {}
+ public function getItem( $hash );
# Set the "default text"
# This concept is an odd property of the current DB schema, whereby each text item has a revision
# associated with it. The default text is the text of the associated revision. There may, however,
# be other revisions in the same object
- function setText() {}
+ public function setText( $text );
/**
* Get default text. This is called from Revision::getRevisionText()
*/
- function getText() {}
+ function getText();
}
/**
* The real object
- * @package MediaWiki
+ * @todo document (needs one-sentence top-level class description + function descriptions).
*/
-class ConcatenatedGzipHistoryBlob extends HistoryBlob
+class ConcatenatedGzipHistoryBlob implements HistoryBlob
{
- /* private */ var $mVersion = 0, $mCompressed = false, $mItems = array(), $mDefaultHash = '';
- /* private */ var $mFast = 0, $mSize = 0;
+ public $mVersion = 0, $mCompressed = false, $mItems = array(), $mDefaultHash = '';
+ public $mFast = 0, $mSize = 0;
- function ConcatenatedGzipHistoryBlob() {
+ /** Constructor */
+ public function ConcatenatedGzipHistoryBlob() {
if ( !function_exists( 'gzdeflate' ) ) {
throw new MWException( "Need zlib support to read or write this kind of history object (ConcatenatedGzipHistoryBlob)\n" );
}
}
+ #
+ # HistoryBlob implementation:
+ #
+
/** @todo document */
- function setMeta( $metaData ) {
+ public function setMeta( $metaData ) {
$this->uncompress();
$this->mItems['meta'] = $metaData;
}
/** @todo document */
- function getMeta() {
+ public function getMeta() {
$this->uncompress();
return $this->mItems['meta'];
}
/** @todo document */
- function addItem( $text ) {
+ public function addItem( $text ) {
$this->uncompress();
$hash = md5( $text );
$this->mItems[$hash] = $text;
@@ -87,7 +91,7 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
}
/** @todo document */
- function getItem( $hash ) {
+ public function getItem( $hash ) {
$this->uncompress();
if ( array_key_exists( $hash, $this->mItems ) ) {
return $this->mItems[$hash];
@@ -97,13 +101,29 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
}
/** @todo document */
- function removeItem( $hash ) {
+ public function setText( $text ) {
+ $this->uncompress();
+ $stub = $this->addItem( $text );
+ $this->mDefaultHash = $stub->mHash;
+ }
+
+ /** @todo document */
+ public function getText() {
+ $this->uncompress();
+ return $this->getItem( $this->mDefaultHash );
+ }
+
+ # HistoryBlob implemented.
+
+
+ /** @todo document */
+ public function removeItem( $hash ) {
$this->mSize -= strlen( $this->mItems[$hash] );
unset( $this->mItems[$hash] );
}
/** @todo document */
- function compress() {
+ public function compress() {
if ( !$this->mCompressed ) {
$this->mItems = gzdeflate( serialize( $this->mItems ) );
$this->mCompressed = true;
@@ -111,25 +131,13 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
}
/** @todo document */
- function uncompress() {
+ public function uncompress() {
if ( $this->mCompressed ) {
$this->mItems = unserialize( gzinflate( $this->mItems ) );
$this->mCompressed = false;
}
}
- /** @todo document */
- function getText() {
- $this->uncompress();
- return $this->getItem( $this->mDefaultHash );
- }
-
- /** @todo document */
- function setText( $text ) {
- $this->uncompress();
- $stub = $this->addItem( $text );
- $this->mDefaultHash = $stub->mHash;
- }
/** @todo document */
function __sleep() {
@@ -145,7 +153,7 @@ class ConcatenatedGzipHistoryBlob extends HistoryBlob
/**
* Determines if this object is happy
*/
- function isHappy( $maxFactor, $factorThreshold ) {
+ public function isHappy( $maxFactor, $factorThreshold ) {
if ( count( $this->mItems ) == 0 ) {
return true;
}
@@ -179,7 +187,7 @@ $wgBlobCache = array();
/**
- * @package MediaWiki
+ * @todo document (needs one-sentence top-level class description + some function descriptions).
*/
class HistoryBlobStub {
var $mOldId, $mHash, $mRef;
@@ -197,28 +205,28 @@ class HistoryBlobStub {
$this->mOldId = $id;
}
- /**
- * Sets the location (old_id) of the referring object
- */
+ /**
+ * Sets the location (old_id) of the referring object
+ */
function setReferrer( $id ) {
$this->mRef = $id;
}
- /**
- * Gets the location of the referring object
- */
+ /**
+ * Gets the location of the referring object
+ */
function getReferrer() {
return $this->mRef;
}
/** @todo document */
function getText() {
- $fname = 'HistoryBlob::getText';
+ $fname = 'HistoryBlobStub::getText';
global $wgBlobCache;
if( isset( $wgBlobCache[$this->mOldId] ) ) {
$obj = $wgBlobCache[$this->mOldId];
} else {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'text', array( 'old_flags', 'old_text' ), array( 'old_id' => $this->mOldId ) );
if( !$row ) {
return false;
@@ -273,8 +281,6 @@ class HistoryBlobStub {
*
* Serialized HistoryBlobCurStub objects will be inserted into the text table
* on conversion if $wgFastSchemaUpgrades is set to true.
- *
- * @package MediaWiki
*/
class HistoryBlobCurStub {
var $mCurId;
@@ -294,7 +300,7 @@ class HistoryBlobCurStub {
/** @todo document */
function getText() {
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mCurId ) );
if( !$row ) {
return false;
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 2eecfd72..b428b08d 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -18,7 +18,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* @author Evan Prodromou <evan@wikitravel.org>
- * @package MediaWiki
* @see hooks.txt
*/
@@ -103,7 +102,7 @@ function wfRunHooks($event, $args = null) {
if ( isset( $object ) ) {
$func = get_class( $object ) . '::' . $method;
$callback = array( $object, $method );
- } elseif ( false !== ( $pos = strpos( '::', $func ) ) ) {
+ } elseif ( false !== ( $pos = strpos( $func, '::' ) ) ) {
$callback = array( substr( $func, 0, $pos ), substr( $func, $pos + 2 ) );
} else {
$callback = $func;
diff --git a/includes/IP.php b/includes/IP.php
index edf4af7a..8a2756c9 100644
--- a/includes/IP.php
+++ b/includes/IP.php
@@ -1,8 +1,5 @@
<?php
/*
- * Collection of public static functions to play with IP address
- * and IP blocks.
- *
* @Author "Ashar Voultoiz" <hashar@altern.org>
* @License GPL v2 or later
*/
@@ -12,22 +9,231 @@
// An IP is made of 4 bytes from x00 to xFF which is d0 to d255
define( 'RE_IP_BYTE', '(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])');
define( 'RE_IP_ADD' , RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE );
-// An IP block is an IP address and a prefix (d1 to d32)
+// An IPv4 block is an IP address and a prefix (d1 to d32)
define( 'RE_IP_PREFIX', '(3[0-2]|[12]?\d)');
define( 'RE_IP_BLOCK', RE_IP_ADD . '\/' . RE_IP_PREFIX);
// For IPv6 canonicalization (NOT for strict validation; these are quite lax!)
define( 'RE_IPV6_WORD', '([0-9A-Fa-f]{1,4})' );
define( 'RE_IPV6_GAP', ':(?:0+:)*(?::(?:0+:)*)?' );
define( 'RE_IPV6_V4_PREFIX', '0*' . RE_IPV6_GAP . '(?:ffff:)?' );
+// An IPv6 block is an IP address and a prefix (d1 to d128)
+define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)');
+// An IPv6 IP is made up of 8 octets. However abbreviations like "::" can be used. This is lax!
+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 . '|)');
+/**
+ * A collection of public static functions to play with IP address
+ * and IP blocks.
+ */
class IP {
+ /**
+ * Given a string, determine if it as valid IP
+ * Unlike isValid(), this looks for networks too
+ * @param $ip IP address.
+ * @return string
+ */
+ public static function isIPAddress( $ip ) {
+ if ( !$ip ) return false;
+ if ( is_array( $ip ) ) {
+ throw new MWException( "invalid value passed to " . __METHOD__ );
+ }
+ // IPv6 IPs with two "::" strings are ambiguous and thus invalid
+ return preg_match( '/^' . IP_ADDRESS_STRING . '$/', $ip) && ( substr_count($ip, '::') < 2 );
+ }
+
+ public static function isIPv6( $ip ) {
+ if ( !$ip ) return false;
+ if( is_array( $ip ) ) {
+ throw new MWException( "invalid value passed to " . __METHOD__ );
+ }
+ // IPv6 IPs with two "::" strings are ambiguous and thus invalid
+ return preg_match( '/^' . RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)$/', $ip) && ( substr_count($ip, '::') < 2);
+ }
+
+ public static function isIPv4( $ip ) {
+ if ( !$ip ) return false;
+ return preg_match( '/^' . RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)$/', $ip);
+ }
+
+ /**
+ * Given an IP address in dotted-quad notation, returns an IPv6 octet.
+ * See http://www.answers.com/topic/ipv4-compatible-address
+ * IPs with the first 92 bits as zeros are reserved from IPv6
+ * @param $ip quad-dotted IP address.
+ * @return string
+ */
+ public static function IPv4toIPv6( $ip ) {
+ if ( !$ip ) return null;
+ // Convert only if needed
+ if ( self::isIPv6( $ip ) ) return $ip;
+ // IPv4 CIDRs
+ if ( strpos( $ip, '/' ) !== false ) {
+ $parts = explode( '/', $ip, 2 );
+ if ( count( $parts ) != 2 ) {
+ return false;
+ }
+ $network = self::toUnsigned( $parts[0] );
+ if ( $network !== false && is_numeric( $parts[1] ) && $parts[1] >= 0 && $parts[1] <= 32 ) {
+ $bits = $parts[1] + 96;
+ return self::toOctet( $network ) . "/$bits";
+ } else {
+ return false;
+ }
+ }
+ return self::toOctet( self::toUnsigned( $ip ) );
+ }
/**
+ * Given an IPv6 address in octet notation, returns an unsigned integer.
+ * @param $ip octet ipv6 IP address.
+ * @return string
+ */
+ public static function toUnsigned6( $ip ) {
+ if ( !$ip ) return null;
+ $ip = explode(':', self::sanitizeIP( $ip ) );
+ $r_ip = '';
+ foreach ($ip as $v) {
+ $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
+ }
+ $r_ip = wfBaseConvert( $r_ip, 16, 10 );
+ return $r_ip;
+ }
+
+ /**
+ * Given an IPv6 address in octet notation, returns the expanded octet.
+ * IPv4 IPs will be trimmed, thats it...
+ * @param $ip octet ipv6 IP address.
+ * @return string
+ */
+ public static function sanitizeIP( $ip ) {
+ if ( !$ip ) return null;
+ // Trim and return IPv4 addresses
+ if ( self::isIPv4($ip) ) return trim($ip);
+ // Only IPv6 addresses can be expanded
+ if ( !self::isIPv6($ip) ) return $ip;
+ // Remove any whitespaces, convert to upper case
+ $ip = strtoupper( trim($ip) );
+ // Expand zero abbreviations
+ if ( strpos( $ip, '::' ) !== false ) {
+ $ip = str_replace('::', str_repeat(':0', 8 - substr_count($ip, ':')) . ':', $ip);
+ }
+ // For IPs that start with "::", correct the final IP so that it starts with '0' and not ':'
+ if ( $ip[0] == ':' ) $ip = "0$ip";
+ // Remove leading zereos from each bloc as needed
+ $ip = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip );
+ return $ip;
+ }
+
+ /**
+ * Given an unsigned integer, returns an IPv6 address in octet notation
+ * @param $ip integer IP address.
+ * @return string
+ */
+ public static function toOctet( $ip_int ) {
+ // Convert to padded uppercase hex
+ $ip_hex = wfBaseConvert($ip_int, 10, 16, 32, false);
+ // Seperate into 8 octets
+ $ip_oct = substr( $ip_hex, 0, 4 );
+ for ($n=1; $n < 8; $n++) {
+ $ip_oct .= ':' . substr($ip_hex, 4*$n, 4);
+ }
+ // NO leading zeroes
+ $ip_oct = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip_oct );
+ return $ip_oct;
+ }
+
+ /**
+ * Convert a network specification in IPv6 CIDR notation to an integer network and a number of bits
+ * @return array(string, int)
+ */
+ public static function parseCIDR6( $range ) {
+ # Expand any IPv6 IP
+ $parts = explode( '/', IP::sanitizeIP( $range ), 2 );
+ if ( count( $parts ) != 2 ) {
+ return array( false, false );
+ }
+ $network = self::toUnsigned6( $parts[0] );
+ if ( $network !== false && is_numeric( $parts[1] ) && $parts[1] >= 0 && $parts[1] <= 128 ) {
+ $bits = $parts[1];
+ if ( $bits == 0 ) {
+ $network = 0;
+ } else {
+ # Native 32 bit functions WONT work here!!!
+ # Convert to a padded binary number
+ $network = wfBaseConvert( $network, 10, 2, 128 );
+ # Truncate the last (128-$bits) bits and replace them with zeros
+ $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
+ # Convert back to an integer
+ $network = wfBaseConvert( $network, 2, 10 );
+ }
+ } else {
+ $network = false;
+ $bits = false;
+ }
+ return array( $network, $bits );
+ }
+
+ /**
+ * Given a string range in a number of formats, return the start and end of
+ * the range in hexadecimal. For IPv6.
+ *
+ * Formats are:
+ * 2001:0db8:85a3::7344/96 CIDR
+ * 2001:0db8:85a3::7344 - 2001:0db8:85a3::7344 Explicit range
+ * 2001:0db8:85a3::7344/96 Single IP
+ * @return array(string, int)
+ */
+ public static function parseRange6( $range ) {
+ # Expand any IPv6 IP
+ $range = IP::sanitizeIP( $range );
+ if ( strpos( $range, '/' ) !== false ) {
+ # CIDR
+ list( $network, $bits ) = self::parseCIDR6( $range );
+ if ( $network === false ) {
+ $start = $end = false;
+ } else {
+ $start = wfBaseConvert( $network, 10, 16, 32, false );
+ # Turn network to binary (again)
+ $end = wfBaseConvert( $network, 10, 2, 128 );
+ # Truncate the last (128-$bits) bits and replace them with ones
+ $end = str_pad( substr( $end, 0, $bits ), 128, 1, STR_PAD_RIGHT );
+ # Convert to hex
+ $end = wfBaseConvert( $end, 2, 16, 32, false );
+ # see toHex() comment
+ $start = "v6-$start"; $end = "v6-$end";
+ }
+ } elseif ( strpos( $range, '-' ) !== false ) {
+ # Explicit range
+ list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
+ $start = self::toUnsigned6( $start ); $end = self::toUnsigned6( $end );
+ if ( $start > $end ) {
+ $start = $end = false;
+ } else {
+ $start = wfBaseConvert( $start, 10, 16, 32, false );
+ $end = wfBaseConvert( $end, 10, 16, 32, false );
+ }
+ # see toHex() comment
+ $start = "v6-$start"; $end = "v6-$end";
+ } else {
+ # Single IP
+ $start = $end = self::toHex( $range );
+ }
+ if ( $start === false || $end === false ) {
+ return array( false, false );
+ } else {
+ return array( $start, $end );
+ }
+ }
+
+ /**
* Validate an IP address.
* @return boolean True if it is valid.
*/
public static function isValid( $ip ) {
- return preg_match( '/^' . RE_IP_ADD . '$/', $ip) ;
+ return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip) || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip) );
}
/**
@@ -44,7 +250,7 @@ class IP {
* Comes from ProxyTools.php
*/
public static function isPublic( $ip ) {
- $n = IP::toUnsigned( $ip );
+ $n = self::toUnsigned( $ip );
if ( !$n ) {
return false;
}
@@ -67,8 +273,8 @@ class IP {
}
foreach ( $privateRanges as $r ) {
- $start = IP::toUnsigned( $r[0] );
- $end = IP::toUnsigned( $r[1] );
+ $start = self::toUnsigned( $r[0] );
+ $end = self::toUnsigned( $r[1] );
if ( $n >= $start && $n <= $end ) {
return false;
}
@@ -80,15 +286,17 @@ class IP {
* Split out an IP block as an array of 4 bytes and a mask,
* return false if it can't be determined
*
- * @parameter $ip string A quad dotted IP address
+ * @param $ip string A quad dotted/octet IP address
* @return array
*/
public static function toArray( $ipblock ) {
$matches = array();
- if(! preg_match( '/^' . RE_IP_ADD . '(?:\/(?:'.RE_IP_PREFIX.'))?' . '$/', $ipblock, $matches ) ) {
- return false;
- } else {
+ if( preg_match( '/^' . RE_IP_ADD . '(?:\/(?:'.RE_IP_PREFIX.'))?' . '$/', $ipblock, $matches ) ) {
+ return $matches;
+ } else if ( preg_match( '/^' . RE_IPV6_ADD . '(?:\/(?:'.RE_IPV6_PREFIX.'))?' . '$/', $ipblock, $matches ) ) {
return $matches;
+ } else {
+ return false;
}
}
@@ -100,23 +308,29 @@ class IP {
* function for an IPv6 address will be prefixed with "v6-", a non-
* hexadecimal string which sorts after the IPv4 addresses.
*
- * @param $ip Quad dotted IP address.
+ * @param $ip Quad dotted/octet IP address.
+ * @return hexidecimal
*/
public static function toHex( $ip ) {
$n = self::toUnsigned( $ip );
if ( $n !== false ) {
- $n = sprintf( '%08X', $n );
+ $n = ( self::isIPv6($ip) ) ? "v6-" . wfBaseConvert( $n, 10, 16, 32, false ) : wfBaseConvert( $n, 10, 16, 8, false );
}
return $n;
}
/**
- * Given an IP address in dotted-quad notation, returns an unsigned integer.
+ * Given an IP address in dotted-quad/octet notation, returns an unsigned integer.
* Like ip2long() except that it actually works and has a consistent error return value.
* Comes from ProxyTools.php
* @param $ip Quad dotted IP address.
+ * @return integer
*/
public static function toUnsigned( $ip ) {
+ // Use IPv6 functions if needed
+ if ( self::isIPv6( $ip ) ) {
+ return self::toUnsigned6( $ip );
+ }
if ( $ip == '255.255.255.255' ) {
$n = -1;
} else {
@@ -149,13 +363,14 @@ class IP {
/**
* Convert a network specification in CIDR notation to an integer network and a number of bits
+ * @return array(string, int)
*/
public static function parseCIDR( $range ) {
$parts = explode( '/', $range, 2 );
if ( count( $parts ) != 2 ) {
return array( false, false );
}
- $network = IP::toSigned( $parts[0] );
+ $network = self::toSigned( $parts[0] );
if ( $network !== false && is_numeric( $parts[1] ) && $parts[1] >= 0 && $parts[1] <= 32 ) {
$bits = $parts[1];
if ( $bits == 0 ) {
@@ -182,11 +397,20 @@ class IP {
* 1.2.3.4/24 CIDR
* 1.2.3.4 - 1.2.3.5 Explicit range
* 1.2.3.4 Single IP
+ *
+ * 2001:0db8:85a3::7344/96 CIDR
+ * 2001:0db8:85a3::7344 - 2001:0db8:85a3::7344 Explicit range
+ * 2001:0db8:85a3::7344 Single IP
+ * @return array(string, int)
*/
public static function parseRange( $range ) {
+ // Use IPv6 functions if needed
+ if ( self::isIPv6( $range ) ) {
+ return self::parseRange6( $range );
+ }
if ( strpos( $range, '/' ) !== false ) {
# CIDR
- list( $network, $bits ) = IP::parseCIDR( $range );
+ list( $network, $bits ) = self::parseCIDR( $range );
if ( $network === false ) {
$start = $end = false;
} else {
@@ -196,15 +420,16 @@ class IP {
} elseif ( strpos( $range, '-' ) !== false ) {
# Explicit range
list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
+ $start = self::toUnsigned( $start ); $end = self::toUnsigned( $end );
if ( $start > $end ) {
$start = $end = false;
} else {
- $start = IP::toHex( $start );
- $end = IP::toHex( $end );
+ $start = sprintf( '%08X', $start );
+ $end = sprintf( '%08X', $end );
}
} else {
# Single IP
- $start = $end = IP::toHex( $range );
+ $start = $end = self::toHex( $range );
}
if ( $start === false || $end === false ) {
return array( false, false );
@@ -214,18 +439,15 @@ class IP {
}
/**
- * Determine if a given integer IPv4 address is in a given CIDR network
+ * Determine if a given IPv4/IPv6 address is in a given CIDR network
* @param $addr The address to check against the given range.
* @param $range The range to check the given address against.
* @return bool Whether or not the given address is in the given range.
*/
public static function isInRange( $addr, $range ) {
- $unsignedIP = IP::toUnsigned($addr);
- list( $start, $end ) = IP::parseRange($range);
-
- $start = hexdec($start);
- $end = hexdec($end);
-
+ // Convert to IPv6 if needed
+ $unsignedIP = self::toHex( $addr );
+ list( $start, $end ) = self::parseRange( $range );
return (($unsignedIP >= $start) && ($unsignedIP <= $end));
}
@@ -240,10 +462,11 @@ class IP {
* @return valid dotted quad IPv4 address or null
*/
public static function canonicalize( $addr ) {
- if ( IP::isValid( $addr ) )
+ if ( self::isValid( $addr ) )
return $addr;
// IPv6 loopback address
+ $m = array();
if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) )
return '127.0.0.1';
diff --git a/includes/Image.php b/includes/Image.php
index 7a6442c3..09c2286e 100644
--- a/includes/Image.php
+++ b/includes/Image.php
@@ -1,6 +1,5 @@
<?php
/**