summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2006-10-11 20:21:25 +0000
committerPierre Schmitz <pierre@archlinux.de>2006-10-11 20:21:25 +0000
commitd81f562b712f2387fa02290bf2ca86392ab356f2 (patch)
treed666cdefbe6ac320827a2c6cb473581b46e22c4c
parent183851b06bd6c52f3cae5375f433da720d410447 (diff)
Aktualisierung auf Version 1.8.1
-rw-r--r--FAQ6
-rw-r--r--HISTORY623
-rw-r--r--INSTALL2
-rw-r--r--RELEASE-NOTES1002
-rw-r--r--StartProfiler.php22
-rw-r--r--UPGRADE25
-rw-r--r--api.php108
-rwxr-xr-xbin/ulimit-tvf.sh6
-rw-r--r--config/index.php249
-rw-r--r--img_auth.php19
-rw-r--r--includes/AjaxDispatcher.php60
-rw-r--r--includes/AjaxFunctions.php43
-rw-r--r--includes/AjaxResponse.php203
-rw-r--r--includes/Article.php90
-rw-r--r--includes/AutoLoader.php18
-rw-r--r--includes/BagOStuff.php149
-rw-r--r--includes/Block.php289
-rw-r--r--includes/CategoryPage.php265
-rw-r--r--includes/ChangesList.php3
-rw-r--r--includes/CoreParserFunctions.php32
-rw-r--r--includes/Credits.php6
-rw-r--r--includes/Database.php101
-rw-r--r--includes/DatabaseOracle.php5
-rw-r--r--includes/DatabasePostgres.php336
-rw-r--r--includes/DateFormatter.php131
-rw-r--r--includes/DefaultSettings.php169
-rw-r--r--includes/Defines.php36
-rw-r--r--includes/DifferenceEngine.php24
-rw-r--r--includes/DjVuImage.php18
-rw-r--r--includes/EditPage.php33
-rw-r--r--includes/Exception.php54
-rw-r--r--includes/Exif.php2
-rw-r--r--includes/Export.php77
-rw-r--r--includes/ExternalEdit.php2
-rw-r--r--includes/ExternalStoreDB.php1
-rw-r--r--includes/FileStore.php32
-rw-r--r--includes/GlobalFunctions.php263
-rw-r--r--includes/HTMLForm.php29
-rw-r--r--includes/HistoryBlob.php1
-rw-r--r--includes/Hooks.php16
-rw-r--r--includes/IP.php211
-rw-r--r--includes/Image.php320
-rw-r--r--includes/ImageFunctions.php479
-rw-r--r--includes/ImageGallery.php8
-rw-r--r--includes/ImagePage.php72
-rw-r--r--includes/LinkBatch.php2
-rw-r--r--includes/LinkCache.php5
-rw-r--r--includes/Linker.php16
-rw-r--r--includes/LoadBalancer.php74
-rw-r--r--includes/LogPage.php4
-rw-r--r--includes/MagicWord.php322
-rw-r--r--includes/Math.php2
-rw-r--r--includes/MemcachedSessions.php3
-rw-r--r--includes/MessageCache.php140
-rw-r--r--includes/MimeMagic.php30
-rw-r--r--includes/Namespace.php16
-rw-r--r--includes/ObjectCache.php11
-rw-r--r--includes/OutputPage.php197
-rw-r--r--includes/PageHistory.php330
-rw-r--r--includes/Pager.php656
-rw-r--r--includes/Parser.php1410
-rw-r--r--includes/ParserCache.php6
-rw-r--r--includes/Profiler.php369
-rw-r--r--includes/ProfilerSimple.php19
-rw-r--r--includes/ProfilerSimpleUDP.php11
-rw-r--r--includes/ProfilerStub.php2
-rw-r--r--includes/ProtectionForm.php3
-rw-r--r--includes/ProxyTools.php81
-rw-r--r--includes/QueryPage.php4
-rw-r--r--includes/RawPage.php27
-rw-r--r--includes/RecentChange.php35
-rw-r--r--includes/Revision.php101
-rw-r--r--includes/Sanitizer.php224
-rw-r--r--includes/SearchEngine.php93
-rw-r--r--includes/SearchPostgres.php6
-rw-r--r--includes/Setup.php205
-rw-r--r--includes/SiteStatsUpdate.php10
-rw-r--r--includes/Skin.php221
-rw-r--r--includes/SkinTemplate.php124
-rw-r--r--includes/SpecialAllmessages.php22
-rw-r--r--includes/SpecialAllpages.php16
-rw-r--r--includes/SpecialBlockip.php70
-rw-r--r--includes/SpecialBrokenRedirects.php2
-rw-r--r--includes/SpecialCategories.php2
-rw-r--r--includes/SpecialConfirmemail.php6
-rw-r--r--includes/SpecialDeadendpages.php4
-rw-r--r--includes/SpecialDisambiguations.php78
-rw-r--r--includes/SpecialDoubleRedirects.php2
-rw-r--r--includes/SpecialEmailuser.php6
-rw-r--r--includes/SpecialExport.php55
-rw-r--r--includes/SpecialImagelist.php220
-rw-r--r--includes/SpecialImport.php91
-rw-r--r--includes/SpecialIpblocklist.php270
-rw-r--r--includes/SpecialListredirects.php6
-rw-r--r--includes/SpecialListusers.php21
-rw-r--r--includes/SpecialLockdb.php28
-rw-r--r--includes/SpecialLog.php4
-rw-r--r--includes/SpecialLonelypages.php3
-rw-r--r--includes/SpecialLongpages.php5
-rw-r--r--includes/SpecialMostcategories.php2
-rw-r--r--includes/SpecialMostimages.php2
-rw-r--r--includes/SpecialMostlinked.php2
-rw-r--r--includes/SpecialMostlinkedcategories.php2
-rw-r--r--includes/SpecialMostrevisions.php4
-rw-r--r--includes/SpecialMovepage.php21
-rw-r--r--includes/SpecialNewimages.php34
-rw-r--r--includes/SpecialNewpages.php60
-rw-r--r--includes/SpecialPage.php51
-rw-r--r--includes/SpecialPreferences.php70
-rw-r--r--includes/SpecialRecentchanges.php9
-rw-r--r--includes/SpecialRecentchangeslinked.php24
-rw-r--r--includes/SpecialRevisiondelete.php19
-rw-r--r--includes/SpecialSearch.php5
-rw-r--r--includes/SpecialShortpages.php3
-rw-r--r--includes/SpecialSpecialpages.php19
-rw-r--r--includes/SpecialStatistics.php26
-rw-r--r--includes/SpecialUndelete.php59
-rw-r--r--includes/SpecialUnlockdb.php11
-rw-r--r--includes/SpecialUpload.php177
-rw-r--r--includes/SpecialUploadMogile.php1
-rw-r--r--includes/SpecialUserlogin.php147
-rw-r--r--includes/SpecialUserrights.php2
-rw-r--r--includes/SpecialVersion.php66
-rw-r--r--includes/SpecialWantedcategories.php2
-rw-r--r--includes/SpecialWantedpages.php2
-rw-r--r--includes/SpecialWatchlist.php66
-rw-r--r--includes/SpecialWhatlinkshere.php6
-rw-r--r--includes/StreamFile.php8
-rw-r--r--includes/StubObject.php130
-rw-r--r--includes/Title.php91
-rw-r--r--includes/User.php366
-rw-r--r--includes/UserMailer.php17
-rw-r--r--includes/Utf8Case.php2
-rw-r--r--includes/WatchedItem.php3
-rw-r--r--includes/WebRequest.php25
-rw-r--r--includes/WebResponse.php18
-rw-r--r--includes/WebStart.php82
-rw-r--r--includes/Wiki.php17
-rw-r--r--includes/Xml.php580
-rw-r--r--includes/XmlFunctions.php14
-rw-r--r--includes/api/ApiBase.php441
-rw-r--r--includes/api/ApiFormatBase.php161
-rw-r--r--includes/api/ApiFormatJson.php56
-rw-r--r--includes/api/ApiFormatJson_json.php841
-rw-r--r--includes/api/ApiFormatXml.php161
-rw-r--r--includes/api/ApiFormatYaml.php55
-rw-r--r--includes/api/ApiFormatYaml_spyc.php854
-rw-r--r--includes/api/ApiHelp.php55
-rw-r--r--includes/api/ApiLogin.php122
-rw-r--r--includes/api/ApiMain.php226
-rw-r--r--includes/api/ApiPageSet.php514
-rw-r--r--includes/api/ApiQuery.php354
-rw-r--r--includes/api/ApiQueryAllpages.php183
-rw-r--r--includes/api/ApiQueryBase.php112
-rw-r--r--includes/api/ApiQueryInfo.php82
-rw-r--r--includes/api/ApiQueryRevisions.php320
-rw-r--r--includes/api/ApiQuerySiteinfo.php113
-rw-r--r--includes/api/ApiResult.php153
-rw-r--r--includes/memcached-client.php42
-rw-r--r--includes/normal/CleanUpTest.php2
-rw-r--r--includes/normal/RandomTest.php6
-rw-r--r--includes/normal/UtfNormal.php2
-rw-r--r--includes/templates/NoLocalSettings.php48
-rw-r--r--includes/templates/Userlogin.php24
-rw-r--r--index.php84
-rw-r--r--languages/Language.php1503
-rw-r--r--languages/LanguageConverter.php140
-rw-r--r--languages/Names.php27
-rw-r--r--languages/classes/LanguageAz.php17
-rw-r--r--languages/classes/LanguageBe.php89
-rw-r--r--languages/classes/LanguageBg.php25
-rw-r--r--languages/classes/LanguageBs.php137
-rw-r--r--languages/classes/LanguageCs.php87
-rw-r--r--languages/classes/LanguageEo.php74
-rw-r--r--languages/classes/LanguageEt.php21
-rw-r--r--languages/classes/LanguageFi.php165
-rw-r--r--languages/classes/LanguageFr.php17
-rw-r--r--languages/classes/LanguageGa.php52
-rw-r--r--languages/classes/LanguageGsw.php69
-rw-r--r--languages/classes/LanguageHe.php70
-rw-r--r--languages/classes/LanguageHr.php26
-rw-r--r--languages/classes/LanguageHu.php53
-rw-r--r--languages/classes/LanguageJa.php42
-rw-r--r--languages/classes/LanguageKo.php57
-rw-r--r--languages/classes/LanguageKsh.php35
-rw-r--r--languages/classes/LanguageLa.php37
-rw-r--r--languages/classes/LanguageLt.php22
-rw-r--r--languages/classes/LanguageLv.php57
-rw-r--r--languages/classes/LanguagePt_br.php17
-rw-r--r--languages/classes/LanguageRmy.php72
-rw-r--r--languages/classes/LanguageRu.php87
-rw-r--r--languages/classes/LanguageSk.php94
-rw-r--r--languages/classes/LanguageSl.php124
-rw-r--r--languages/classes/LanguageSr.deps.php10
-rw-r--r--languages/classes/LanguageSr.php181
-rw-r--r--languages/classes/LanguageSr_ec.php28
-rw-r--r--languages/classes/LanguageSr_el.deps.php9
-rw-r--r--languages/classes/LanguageSr_el.php28
-rw-r--r--languages/classes/LanguageTr.php18
-rw-r--r--languages/classes/LanguageTyv.php233
-rw-r--r--languages/classes/LanguageVi.php74
-rw-r--r--languages/classes/LanguageWa.php71
-rw-r--r--languages/classes/LanguageZh.deps.php10
-rw-r--r--languages/classes/LanguageZh.php101
-rw-r--r--languages/classes/LanguageZh_cn.php27
-rw-r--r--languages/classes/LanguageZh_yue.php27
-rw-r--r--languages/messages/MessagesAb.php5
-rw-r--r--languages/messages/MessagesAf.php694
-rw-r--r--languages/messages/MessagesAn.php24
-rw-r--r--languages/messages/MessagesAr.php607
-rw-r--r--languages/messages/MessagesArc.php15
-rw-r--r--languages/messages/MessagesAs.php20
-rw-r--r--languages/messages/MessagesAst.php29
-rw-r--r--languages/messages/MessagesAv.php9
-rw-r--r--languages/messages/MessagesAy.php9
-rw-r--r--languages/messages/MessagesAz.php674
-rw-r--r--languages/messages/MessagesBa.php201
-rw-r--r--languages/messages/MessagesBar.php17
-rw-r--r--languages/messages/MessagesBat_smg.php9
-rw-r--r--languages/messages/MessagesBe.php837
-rw-r--r--languages/messages/MessagesBg.php1379
-rw-r--r--languages/messages/MessagesBm.php9
-rw-r--r--languages/messages/MessagesBn.php133
-rw-r--r--languages/messages/MessagesBo.php20
-rw-r--r--languages/messages/MessagesBpy.php11
-rw-r--r--languages/messages/MessagesBr.php1096
-rw-r--r--languages/messages/MessagesBs.php1093
-rw-r--r--languages/messages/MessagesCa.php1247
-rw-r--r--languages/messages/MessagesCe.php9
-rw-r--r--languages/messages/MessagesCs.php2080
-rw-r--r--languages/messages/MessagesCsb.php326
-rw-r--r--languages/messages/MessagesCv.php244
-rw-r--r--languages/messages/MessagesCy.php845
-rw-r--r--languages/messages/MessagesDa.php1340
-rw-r--r--languages/messages/MessagesDe.php1972
-rw-r--r--languages/messages/MessagesDv.php9
-rw-r--r--languages/messages/MessagesDz.php22
-rw-r--r--languages/messages/MessagesEl.php1769
-rw-r--r--languages/messages/MessagesEn.php2457
-rw-r--r--languages/messages/MessagesEnRTL.php5
-rw-r--r--languages/messages/MessagesEo.php1250
-rw-r--r--languages/messages/MessagesEs.php1708
-rw-r--r--languages/messages/MessagesEt.php897
-rw-r--r--languages/messages/MessagesEu.php201
-rw-r--r--languages/messages/MessagesFa.php924
-rw-r--r--languages/messages/MessagesFi.php1908
-rw-r--r--languages/messages/MessagesFo.php116
-rw-r--r--languages/messages/MessagesFr.php1533
-rw-r--r--languages/messages/MessagesFur.php727
-rw-r--r--languages/messages/MessagesFy.php861
-rw-r--r--languages/messages/MessagesGa.php1785
-rw-r--r--languages/messages/MessagesGn.php9
-rw-r--r--languages/messages/MessagesGsw.php793
-rw-r--r--languages/messages/MessagesGu.php19
-rw-r--r--languages/messages/MessagesHe.php2118
-rw-r--r--languages/messages/MessagesHi.php128
-rw-r--r--languages/messages/MessagesHr.php1558
-rw-r--r--languages/messages/MessagesHu.php1052
-rw-r--r--languages/messages/MessagesIa.php801
-rw-r--r--languages/messages/MessagesId.php1919
-rw-r--r--languages/messages/MessagesIi.php9
-rw-r--r--languages/messages/MessagesIs.php705
-rw-r--r--languages/messages/MessagesIt.php1839
-rw-r--r--languages/messages/MessagesJa.php1673
-rw-r--r--languages/messages/MessagesJbo.php23
-rw-r--r--languages/messages/MessagesJv.php45
-rw-r--r--languages/messages/MessagesKa.php574
-rw-r--r--languages/messages/MessagesKk.php10
-rw-r--r--languages/messages/MessagesKk_cn.php10
-rw-r--r--languages/messages/MessagesKm.php22
-rw-r--r--languages/messages/MessagesKn.php376
-rw-r--r--languages/messages/MessagesKo.php1204
-rw-r--r--languages/messages/MessagesKs.php11
-rw-r--r--languages/messages/MessagesKsh.php2345
-rw-r--r--languages/messages/MessagesKu.php774
-rw-r--r--languages/messages/MessagesKv.php9
-rw-r--r--languages/messages/MessagesLa.php822
-rw-r--r--languages/messages/MessagesLi.php941
-rw-r--r--languages/messages/MessagesLo.php22
-rw-r--r--languages/messages/MessagesLt.php942
-rw-r--r--languages/messages/MessagesLv.php978
-rw-r--r--languages/messages/MessagesMk.php1708
-rw-r--r--languages/messages/MessagesMl.php23
-rw-r--r--languages/messages/MessagesMs.php816
-rw-r--r--languages/messages/MessagesMzn.php23
-rw-r--r--languages/messages/MessagesNah.php51
-rw-r--r--languages/messages/MessagesNap.php10
-rw-r--r--languages/messages/MessagesNds.php1170
-rw-r--r--languages/messages/MessagesNds_nl.php60
-rw-r--r--languages/messages/MessagesNl.php1900
-rw-r--r--languages/messages/MessagesNn.php1601
-rw-r--r--languages/messages/MessagesNo.php1367
-rw-r--r--languages/messages/MessagesNon.php11
-rw-r--r--languages/messages/MessagesNv.php72
-rw-r--r--languages/messages/MessagesOc.php800
-rw-r--r--languages/messages/MessagesOr.php22
-rw-r--r--languages/messages/MessagesOs.php217
-rw-r--r--languages/messages/MessagesPa.php410
-rw-r--r--languages/messages/MessagesPl.php1793
-rw-r--r--languages/messages/MessagesPms.php1699
-rw-r--r--languages/messages/MessagesPs.php17
-rw-r--r--languages/messages/MessagesPt.php1998
-rw-r--r--languages/messages/MessagesPt_br.php789
-rw-r--r--languages/messages/MessagesQu.php9
-rw-r--r--languages/messages/MessagesRmy.php337
-rw-r--r--languages/messages/MessagesRo.php1626
-rw-r--r--languages/messages/MessagesRu.php2168
-rw-r--r--languages/messages/MessagesSc.php655
-rw-r--r--languages/messages/MessagesSd.php12
-rw-r--r--languages/messages/MessagesSk.php1889
-rw-r--r--languages/messages/MessagesSl.php1586
-rw-r--r--languages/messages/MessagesSq.php1496
-rw-r--r--languages/messages/MessagesSr.php12
-rw-r--r--languages/messages/MessagesSr_ec.php2125
-rw-r--r--languages/messages/MessagesSr_el.php2128
-rw-r--r--languages/messages/MessagesSr_jc.php10
-rw-r--r--languages/messages/MessagesSr_jl.php10
-rw-r--r--languages/messages/MessagesSu.php1029
-rw-r--r--languages/messages/MessagesSv.php1524
-rw-r--r--languages/messages/MessagesTa.php730
-rw-r--r--languages/messages/MessagesTe.php836
-rw-r--r--languages/messages/MessagesTg.php34
-rw-r--r--languages/messages/MessagesTh.php214
-rw-r--r--languages/messages/MessagesTlh.php30
-rw-r--r--languages/messages/MessagesTr.php1116
-rw-r--r--languages/messages/MessagesTt.php119
-rw-r--r--languages/messages/MessagesTyv.php306
-rw-r--r--languages/messages/MessagesUdm.php51
-rw-r--r--languages/messages/MessagesUg.php10
-rw-r--r--languages/messages/MessagesUk.php826
-rw-r--r--languages/messages/MessagesUr.php33
-rw-r--r--languages/messages/MessagesUz.php11
-rw-r--r--languages/messages/MessagesVec.php1174
-rw-r--r--languages/messages/MessagesVi.php1315
-rw-r--r--languages/messages/MessagesWa.php1768
-rw-r--r--languages/messages/MessagesXal.php55
-rw-r--r--languages/messages/MessagesYi.php469
-rw-r--r--languages/messages/MessagesZa.php9
-rw-r--r--languages/messages/MessagesZh.php7
-rw-r--r--languages/messages/MessagesZh_cn.php855
-rw-r--r--languages/messages/MessagesZh_hk.php10
-rw-r--r--languages/messages/MessagesZh_sg.php10
-rw-r--r--languages/messages/MessagesZh_tw.php844
-rw-r--r--languages/messages/MessagesZh_yue.php2127
-rw-r--r--maintenance/FiveUpgrade.inc7
-rw-r--r--maintenance/InitialiseMessages.inc67
-rw-r--r--maintenance/addwiki.php56
-rw-r--r--maintenance/archives/patch-ipb_anon_only.sql44
-rw-r--r--maintenance/archives/patch-page_no_title_convert.sql0
-rw-r--r--maintenance/archives/patch-profiling.sql10
-rw-r--r--maintenance/archives/patch-recentchanges-utindex.sql4
-rw-r--r--maintenance/attachLatest.php6
-rw-r--r--maintenance/backup.inc16
-rw-r--r--maintenance/checkUsernames.php4
-rw-r--r--maintenance/cleanupDupes.inc9
-rw-r--r--maintenance/cleanupImages.php168
-rw-r--r--maintenance/cleanupTable.inc86
-rw-r--r--maintenance/cleanupTitles.php83
-rw-r--r--maintenance/cleanupWatchlist.php3
-rw-r--r--maintenance/commandLine.inc14
-rw-r--r--maintenance/convertLinks.inc4
-rw-r--r--maintenance/createAndPromote.php5
-rw-r--r--maintenance/deleteBatch.php8
-rw-r--r--maintenance/deleteImageMemcached.php6
-rw-r--r--maintenance/deleteRevision.php2
-rw-r--r--maintenance/dumpBackup.php6
-rw-r--r--maintenance/dumpHTML.inc539
-rw-r--r--maintenance/dumpHTML.php99
-rw-r--r--maintenance/dumpInterwiki.inc3
-rw-r--r--maintenance/dumpSisterSites.php49
-rw-r--r--maintenance/dumpTextPass.php34
-rw-r--r--maintenance/dumpUploads.php116
-rw-r--r--maintenance/fixSlaveDesync.php133
-rw-r--r--maintenance/fuzz-tester.php2458
-rw-r--r--maintenance/generateSitemap.php11
-rw-r--r--maintenance/importImages.php35
-rw-r--r--maintenance/installExtension.php642
-rw-r--r--maintenance/language/alltrans.php16
-rw-r--r--maintenance/language/checkLanguage.php177
-rw-r--r--maintenance/language/checktrans.php44
-rw-r--r--maintenance/language/date-formats.php45
-rw-r--r--maintenance/language/diffLanguage.php159
-rw-r--r--maintenance/language/dumpMessages.php20
-rw-r--r--maintenance/language/duplicatetrans.php43
-rw-r--r--maintenance/language/function-list.php44
-rw-r--r--maintenance/language/lang2po.php154
-rw-r--r--maintenance/language/langmemusage.php30
-rw-r--r--maintenance/language/languages.inc686
-rw-r--r--maintenance/language/splitLanguageFiles.inc1168
-rw-r--r--maintenance/language/splitLanguageFiles.php13
-rw-r--r--maintenance/language/transstat.php211
-rw-r--r--maintenance/language/unusedMessages.php42
-rw-r--r--maintenance/language/validate.php40
-rw-r--r--maintenance/mctest.php15
-rw-r--r--maintenance/mysql5/tables.sql23
-rw-r--r--maintenance/namespaceDupes.php13
-rw-r--r--maintenance/ourusers.php43
-rw-r--r--maintenance/parserTests.inc12
-rw-r--r--maintenance/parserTests.php2
-rw-r--r--maintenance/parserTests.txt413
-rw-r--r--maintenance/postgres/compare_schemas.pl181
-rw-r--r--maintenance/postgres/tables.sql156
-rw-r--r--maintenance/postgres/wp_mysql2postgres.pl400
-rw-r--r--maintenance/rebuildImages.php4
-rw-r--r--maintenance/refreshImageCount.php4
-rw-r--r--maintenance/runJobs.php12
-rw-r--r--maintenance/stats.php26
-rw-r--r--maintenance/storage/checkStorage.php936
-rw-r--r--maintenance/storage/compressOld.inc11
-rw-r--r--maintenance/tables.sql121
-rw-r--r--maintenance/update.php12
-rw-r--r--maintenance/updateSpecialPages.php4
-rw-r--r--maintenance/updaters.inc144
-rw-r--r--maintenance/userDupes.inc12
-rw-r--r--math/README25
-rw-r--r--math/render.ml11
-rw-r--r--math/texutil.ml9
-rw-r--r--opensearch_desc.php41
-rw-r--r--profileinfo.php26
-rw-r--r--redirect.php13
-rw-r--r--serialized/Makefile28
-rw-r--r--serialized/README37
-rw-r--r--serialized/Utf8Case.ser1
-rw-r--r--serialized/serialize-localisation.php35
-rw-r--r--serialized/serialize.php75
-rw-r--r--skins/CologneBlue.php2
-rw-r--r--skins/MonoBook.php20
-rw-r--r--skins/Standard.php60
-rw-r--r--skins/common/ajax.js209
-rw-r--r--skins/common/ajaxsearch.js104
-rw-r--r--skins/common/common.css48
-rw-r--r--skins/common/images/Arr_u.pngbin0 -> 1044 bytes
-rw-r--r--skins/common/images/arrow_disabled_first_25.pngbin0 -> 481 bytes
-rw-r--r--skins/common/images/arrow_disabled_last_25.pngbin0 -> 480 bytes
-rw-r--r--skins/common/images/arrow_disabled_left_25.pngbin0 -> 460 bytes
-rw-r--r--skins/common/images/arrow_disabled_right_25.pngbin0 -> 447 bytes
-rw-r--r--skins/common/images/arrow_first.svg85
-rw-r--r--skins/common/images/arrow_first_25.pngbin0 -> 482 bytes
-rw-r--r--skins/common/images/arrow_last_25.pngbin0 -> 484 bytes
-rw-r--r--skins/common/images/arrow_left.svg78
-rw-r--r--skins/common/images/arrow_left_25.pngbin0 -> 462 bytes
-rw-r--r--skins/common/images/arrow_right_25.pngbin0 -> 449 bytes
-rw-r--r--skins/common/protect.js3
-rw-r--r--skins/common/wikibits.js20
-rw-r--r--skins/disabled/HTMLDump.php16
-rw-r--r--skins/disabled/MonoBookCBT.php25
-rw-r--r--skins/monobook/main.css46
-rw-r--r--tests/DatabaseTest.php4
-rw-r--r--tests/RunTests.php2
-rw-r--r--tests/SearchEngineTest.php1
-rw-r--r--tests/SearchMySQL4Test.php6
-rw-r--r--thumb.php51
-rw-r--r--trackback.php18
453 files changed, 126189 insertions, 6661 deletions
diff --git a/FAQ b/FAQ
index e4e94edb..a4b54cf3 100644
--- a/FAQ
+++ b/FAQ
@@ -1 +1,5 @@
-The original MediaWiki FAQ can be found at http://meta.wikimedia.org/wiki/MediaWiki_FAQ. A newer version is available at http://www.mediawiki.org/wiki/Help:FAQ. \ No newline at end of file
+The original MediaWiki FAQ can be found at
+http://meta.wikimedia.org/wiki/MediaWiki_FAQ.
+
+A newer version is available at
+http://www.mediawiki.org/wiki/Help:FAQ.
diff --git a/HISTORY b/HISTORY
index 88bf58dc..06f5716c 100644
--- a/HISTORY
+++ b/HISTORY
@@ -5,6 +5,629 @@ Change notes from older releases. For current info see RELEASE-NOTES.
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.6 ==
+
+* (bug 5458) Fix double-URL encoding in block log link in contribs and contribs
+ link in block log
+* (bug 5462) Bogus missing patch warning in updater
+* (bug 5461) Use of deprecated "showhideminor" in Special:Recentchangeslinked
+* PHP warning when allow_call_time_pass_reference is off
+* Update to Finnish localization
+* (bug 5467) Link to page histories in watchlist edit mode
+* Further additions to Hebrew localisation
+* (bug 5476) Invalid xhtml in German localization
+* (bug 5479) Id translation for preferences tabs caption
+* (bug 5493) Id translation for special pages
+* Added skinname and style path parameters to CBT version of MonoBook
+* Include subversion revision number in Special:Version if available
+* (bug 5344) Fix regression that broke slashes in extension tag parameters
+* Improve Special:Log performance on big log sets
+* (bug 5507) Changed mediawiki:logouttext from plain to wikitext
+* (bug 4760) Prevent creation of entries in protection log when protection
+ levels haven't changed
+* (bug 861) Show page protection/unprotection events in histories
+* (bug 5499) Don't clear the tag strip state when asked not to clear state.
+ Fixes regression with use of <ref> in a template breaking <nowiki> etc.
+* Minor improvements to English language files
+* Display the anon talk page info message on anon talk pages again
+ (moved outside the parser cache)
+* Optional {{DISPLAYTITLE|title with markup}} magic word
+ Deactivated by default, set "$wgAllowDisplayTitle = true" in LocalSettings.php
+ to activate
+* Cleaned SpecialContributions a bit
+* Added a table to track interlanguage links
+* (bug 5544) Fix redirect arrow in Special:Listredirects for right-to-left
+ languages
+* Replace "doubleredirectsarrow" with a content language check that picks the
+ appropriate arrow
+* (bug 5537) Add stub language file for Samogitian (bat-smg); inherits
+ Lithuanian (lt)
+* Don't force edit summaries when a user is editing their own user/talk page
+* (bug 5510) Warning produced when using {{SUBPAGENAME}} in some namespaces
+* (bug 385) Installer support for PostgreSQL, fixes for PG compatibility
+* PersistentObject removed; it doesn't do anything and was broken besides.
+ All extensions using it have been corrected.
+* Propagate ISBN number for Booksources in LanguageNo.php
+* (bug 5548) Improvements to Indonesian localisation [patch: Ivan Lanin]
+* Add TALKSPACE, SUBJECTSPACE, TALKPAGENAME, SUBJECTPAGENAME (and encoded forms
+ for all) magic words
+* (bug 5403) Fix Special:Newpages RSS/Atom feeds
+* Reject malformed addresses in X-Forwarded-For entries
+* (bug 3359) Add hooks on completion of file upload
+* (bug 5559) Improve detection of ImageMagick [patch: Greg Turnquist]
+* (bug 5475) New pages feeds ignore "limit" argument
+* (bug 5184) CSS misapplied to elements in Special:Allmessages due to
+ conflicting anchor identifiers
+* (bug 5519) Allow sidebar cache to be disabled; disable it by default.
+* Maintenance script to import the contents of a text file into a wiki page
+* Add $wgReservedUsernames configuration directive to block account creation/use
+* (bug 5576) Remove debugging hack in session check
+* (bug 5426) Lowercase treatment of titles in rights log leads to broken links
+ on Special:Log
+* Minor improvements to French localisation files
+* (bug 5181) Update "nogomatch" for Slovak
+* (bug 5594) Id translation up to # Login and logout pages section
+* (bug 5536) Use content language for editing help link
+* Improvements to German localisation files
+* (bug 5570) Problems using <special page>/parameter link form for long titles
+* (bug 3884) Add $user parameter to AddNewUser hook, call it for by-email
+ registrations as well as self-registrations.
+* (bug 4327) Report age of cached data sets in query pages
+* (bug 4662) Fix Safari check in wikibits.js
+* (bug 4663) Edit toolbar enabled in compatible versions of Safari
+* (bug 5572) Edit toolbar enabled in compatible versions of Konqueror (3.5+)
+* (bug 5235) Edit toolbar tooltips no longer show JavaScript junk in Opera
+* Edit toolbar now works in pure XHTML mode (application/xhtml+xml)
+* Add watchlist clear function to allow quick purging of all items
+* (bug 5625) Additional namespace translations for Welsh
+* Add meta tag and JavaScript variables to cached special pages which provides
+ the timestamp of the last update, in YYYYMMDDHHMMSS format.
+* (bug 5628) More translations for MessagesHr.php
+* (bug 5595) Localisation for Bosnian language (bs)
+* (bug 2910) Default view preferences for watchlists
+* Add "hide bot edits from the watchlist" user preference
+* (bug 5250) Introduce Special:Unusedtemplates
+* Add user preference setting for an extended watchlist, showing all recent
+ edits up to a certain edit, and not just the latest edit..
+* Made MessageRo.php more general
+* (bug 5640) Indonesian localisation improvements
+* (bug 5592) Actions are logged with the default language for the
+ wiki, not the language of the user performing the operation.
+* (bug 5644) Error in LanguageBs.php file
+* (bug 5646) Compare for identical types in wfElement()
+* (bug 5472) Language::userAdjust()->minDiff not initialized on else condition
+* (bug 5386) LanguageMk.php: updated namespaces translations
+* (bug 5422) Stub for Romani (rmy) language which extends ro
+* Fix linktrail for LanguageSr
+* (bug 5664) Fix Bosnian linktrail
+* (bug 3825) Namespace filtering on Special:Newpages
+* (bug 1922) When Special:Wantedpages is cached, mark links to pages
+ which have since been created
+* (bug 5659) Change grammar hacks for Bosnian Wikimedia namespaces.
+ This sort of special casing should be removed and fixed properly.
+* Remove useless whitespace from Special:Brokenredirects header
+* Treat "allmessagesnotsupporteddb" as wikitext when echoing; change default
+ text
+* (bug 5497) Regression in HTML normalization in 1.6 (unclosed <li>,<dd>,<dt>)
+* (bug 5709) Allow customisation of separator for categories
+* (bug 5684) Introduce Special:Randomredirect
+* (bug 5611) Add a name attribute to the text box containing source text in
+ read-only pages
+* Indicate when a protected page is an interface message ("protectedinterface")
+* (bug 4259) Indicate when a protected page being edited is an interface message
+ ("editinginterface")
+* (bug 4834) Fix XHTML output when using $wgMaxTocLevel
+* Pass login link to "whitelistedittext" containing 'returnto' parameter
+* (bug 5728): mVersion missing from User::__sleep() leading to constant cache
+ miss
+* Updated maintenance/transstat.php so it can show duplicate messages
+* Improvements to update scripts; print out the version, check for superuser
+ credentials before attempting a connection, and produce a friendlier error if
+ the connection fails
+* (bug 5005) Fix XHTML <gallery> output.
+* (bug 5315) "Expires: -1" HTTP header made strictly valid (using 1970 date).
+* (bug 4825) note in DefaultSettings.php about 'profiling' table creation
+* Remove unneeded extra whitespace at top of Special:Categories
+* (bug 5679) time units are now using local numerals
+* (bug 5751) Updates to Portuguese localisation files
+* (bug 5741) Introduce {{NUMBEROFUSERS}} magic word
+* (bug 93) <nowiki> tags and tildes in templates
+* The returnto parameter is now actually used by SpecialUserlogin.php
+* Parser can now know that it is parsing an interface message
+* (bug 4737) MediaWiki:Viewcount supports {{PLURAL}} now
+* Fix bug in wfMsgExt under PHP 5.1.2
+* (bug 5761) Project talk namespace broken in Xal, Os, Udm and Cv
+* Rewrite reassignEdits script to be more efficient; support optional updates to
+ recent changes table; add reporting and silent modes
+* Cleaned up formatNum usage in langfiles
+* (bug 5716) Warn when a user tries to upload a file which was previously
+ deleted
+* (bug 5565) Add a class attribute to the table on Special:Allpages
+* "lang=xx" option for parser test cases to set content language
+* (bug 5764) Friulian translation updated
+* (bug 5757) Fix premature cutoff in LanguageConverter with extra end markers
+* (bug 5516) Show appropriate "return to" link on blocked page
+* (bug 5377) Do not auto-login when creating an account as another user
+* (bug 5284) Special redirect pages should remember parameters
+* Suppress 7za output on dumpBackup
+* (bug 5338) Reject extra initial colons in title
+* (bug 5487) Escape self-closed HTML pair tags
+* Add "raw suffix" magic word for some magic words, e.g. {{NUMBEROFUSERS|R}}
+ will produce a count minus formatting
+* Fix Parser::cleanSig() to use Parser::startExternalParse() and choose an
+ appropriate output format given the scope of the clean
+* (bug 5593) Change "bureaucrat log" to "rights log"
+* Show a boilerplate "(none)" in place of a blank within the log action text for
+ user rights
+* (bug 137) Commented out translations for copyrightwarning which mention GNU FDL
+* (bug 5723) Don't count pages linked to from the MediaWiki namespace as "wanted"
+* (bug 5696) Add a third parameter, $3, to "rcnote", passing the current time
+ formatted according to the current user's settings
+* (bug 5780) Thousands and decimal separators for Norwegian
+* Updated initStats maintenance script
+* (bug 5767) Fix date formats in Vietnamese locale
+* (bug 361) URL in URL, they were almost fixed. Now they are.
+* (bug 4876) Add __NEWSECTIONLINK__ magic word to force the "new section" link/tab to
+ show up on specific pages on demand
+* Bidi-aid on list pages
+* (bug 5782) Allow entries in the bad image list to use canonical namespace names
+* (bug 5789) Treat "loginreqpagetext" as wikitext
+* Sanitizer: now handles nested <li> in <ul> or <ol>
+* (bug 5796) We require MySQL >=4.0.14
+* Add 'EmailConfirmed' hook
+* New findhooks.php script to find undocumented hooks.
+* Silently ignore errors on profiling table update.
+* (bug 5801) Correct handling of underscores in Special:Listusers
+* Clean up Special:Listusers; add an "(all)" label to the group selection box
+* (bug 5812) Use appropriate link colour in Special:Mostlinked
+* (bug 5802) {{CURRENTMONTHNAME}} variable broken in Vietnamese locale
+* (bug 5817) Appropriate handling for Special:Recentchangeslinked where the target
+ page doesn't exist
+* Special:Randompage now additionally accepts English namespace name as parameter
+* (bug 2981) Really fixed linktrail for Tamil (ta)
+* Disallow substituting Special pages when included into a page
+* (bug 5587) Clean up the languages from references to the Groups special page
+* Added new group-X and group-X-member messages
+* Rewritten removeUnusedAccounts to be more efficient, print names of inactive
+ accounts
+* Redirect Special:Userlist to Special:Listusers
+* Introduce $wgAllowTitlesInSVG, which allows the <title> attribute in uploaded files
+ bearing the image/svg MIME type. Disabled by default due to the vast majority of
+ web servers being hideously misconfigured. See DefaultSettings.php for more details.
+* Changed default LocalSettings.php to append the previous include path when setting it
+* (bug 5837) Use "members" for the value descriptor in Special:Categories,
+ Special:Wantedcategories and Special:Mostlinkedcategories.
+* (bug 3309) Allow comments when undeleting pages
+* Clean up Special:Undelete a bit
+* (bug 5805) messages nbytes, ncategories can now use {{plural:}}
+* Clean up Special:Imagelist a bit
+* (bug 5838) Namespace names for Nds-NL
+* (bug 5749) Added Tyvan language files
+* (bug 5791) Fix SQL syntax in Special:BrokenRedirects, was causing incorrect data to show
+* (bug 5839) Prevent access to Special:Confirmemail for logged-out users
+* (bug 5853) Update for Portuguese messages (pt)
+* (bug 5851) Use Cyrillic for Kirghiz language name
+* (bug 5841) Allow the 'EditFilter' hook to return a non-fatal error message
+* (bug 5846) Link to individual group description pages in Special:Listusers
+* (bug 5857) Update for German localisation (de)
+* (bug 5858) Update for Russian language (ru)
+* (bug 5860) Update for Indonesian language (id)
+* (bug 1120) Update for Czech language (Cs)
+* Added many missing formatNum calls
+* Added grammar function to Belarusian (be)
+* (bug 5819) Add 'PersonalUrls' hook
+* (bug 5862) Update of Belarusian language (be)
+* (bug 5886) Update for Portuguese messages (pt)
+* (bug 5586) <gallery> treated text as links
+* (bug 5878) Update for Indonesian language (id)
+* (bug 5697) Update for Malay language (ms)
+* (bug 5890) Update for German language (de)
+* (bug 5889) Name for Sindhi language should appear as سنڌي
+* --force-normal parameter on dump scripts to force check for ICU extension
+* (bug 5895) Update for Dutch language (nl)
+* (bug 5891) Linktrail for Polish language (pl)
+* User::isBureaucrat , User::isDeveloper , User::isSysop deprecated in
+ v1.6 now die with a backtrace. They will be removed in v1.8
+* dumpTextPass now skips goes to database for entries that were blank in the
+ previous dump, as this may indicate a broken dump.
+* dumpTextPass progress includes percentage of items prefetched
+* dumpTextPass can now use 7zip files for prefetch
+* (bug 5915) Update to Indonesian localisation (id)
+* (bug 5913) Update for German localisation (de)
+* (bug 5905) Plural support for Bosnian localisation (bs)
+* Groups which won't hit the rate limiter now configurable with
+ $wgRateLimitsExcludedGroups
+* (bug 5806) {{plural:}} support instead of "twin" MediaWiki messages
+* (bug 5931) Update for Polish language (pl)
+* Ignore the user and user talk namespaces on Special:Wantedpages
+* Introduce NUMBEROFPAGES magic word
+* (bug 5833) Introduce CURRENTVERSION magic word
+* (bug 5370) Allow throttling of password reminder requests with the rate limiter
+* (bug 5683) Respect parser output marked as uncacheable when saving
+* (bug 5918) Links autonumbering now work for all defined protocols
+* (bug 5935) Improvement to German localisation (de)
+* (bug 5937) Register links from gallery captions with the parent parser output
+ object so that link tables receive those updates too
+* (bug 5845) Introduce BASEPAGENAME and BASEPAGENAMEE magic words
+* (bug 5941) Use content language when getting the administrator page title for
+ Special:Statistics
+* (bug 5949) Update to Indonesian localisation (id)
+* (bug 5862) Update of Belarusian translation (be)
+* (bug 5950) Improvements to French localisation
+* (bug 5805) {{plural:}} support for counters in some special pages
+* (bug 5952) Improvement to German localisation (de)
+* Rename conflicting metadata help message to "metadata_help" (was "metadata")
+ and treat it as wiki text
+* Improve preferences input filtering
+* Maintenance script to import multiple files into the wiki
+* (bug 5957) Update for Hebrew language (he)
+* (bug 5962) Update for Italian language (it)
+* (bug 5961) Update for Portuguese localisation (pt)
+* (bug 5849) Remove some hard-coded references to "Wikipedia" in messages
+* (bug 5967) Improvement to German localisation (de)
+* (bug 5962) Update for Italian language (it)
+* Suppress images in galleries which appear on the bad image list (when rendering
+ for a wiki page; galleries in special pages and categories are unaffected)
+* Maintenance script to remove orphaned revisions from the database
+* (bug 5991) Update for Russian language (ru)
+* (bug 6001) PAGENAMEE and FULLPAGENAMEE don't work in FULLURL and LOCALURL magic
+ words
+* (bug 5958) Switch Uzbek language name to use latin script
+* (bug 839) Add URLENCODE magic word
+* (bug 6004) Update for Polish language (pl)
+* (bug 5971) Improvement to German localisation (de)
+* (bug 4873) Don't overwrite the subtitle navigation when viewing a redirect page
+ that isn't current
+* (bug 2203) Namespace updates for Thai
+* Fix breakage in parser test suite which caused incorrect reporting of the failure of
+ {{NUMBEROFFILES}}. Now initialises the site_stats table with some dumb data. Updated
+ the expected output for {{NUMBEROFARTICLES}} to reflect this.
+* (bug 6009) Use {{ns:project}} in messages where appropriate
+* (bug 6012) Update to Indonesian localisation (id)
+* (bug 6017) Update list of bookstores in German localisation files
+* (bug 5187) Allow programmatically bypassing username validation, for scripts
+* (bug 6025) SpecialImport: wrong message when no file selected
+* (bug 6015) EditPage: add spacing in the boxes "edit is minor" and "watch this"
+* (bug 6018) Userrights: new message when no user specified ('nouserspecified')
+* (bug 2015) Add "\sim" to ~ conversion for HTML rendering
+* (bug 6029) Improvement to German localisation (de)
+* (bug 5015) Update be: magic words
+* (bug 3974) Add parameter for site URL to "passwordremindertext"
+* (bug 6039) Update for Portuguese localisation (pt)
+* (bug 764) Add CREATE TEMPORARY TABLES to default database permissions
+* Big update to Swedish localisation (sv)
+* Use appropriate HTML functions to create the tool links on image pages, so they don't
+ look garbled when tidy isn't on
+* (bug 5511) Fix URL-encoding of usernames in links on Special:Ipblocklist
+* (bug 6046) Update to Indonesian localisation (id) #15
+* (bug 5523) $wgNoFollowNsExceptions to allow disabling rel="nofollow" in
+ specially-selected namespaces.
+* (bug 6055) Fix for HTML/JS injection bug in variable handler (found by Nick Jenkins)
+* Reordered wiki table handling and __TOC__ extraction in the parser to better
+ handle some overlapping tag cases.
+* Only the first __TOC__ is now turned into a TOC
+* (bug 4610) Indicate patrolled status on watchlists and allow users to mark
+ changes as patrolled using the diff links there
+* Add 'DiffViewHeader' hook called before diff page output
+* (bug 6051) Improvement to German localisation (de)
+* (bug 6054) Update to Indonesian localisation (id) #16
+* Add {{CURRENTTIMESTAMP}} magic word
+* (bug 6061) Improper escaping in some html forms
+* (bug 6065) Remove underscore when using NAMESPACE and TALKSPACE magics.
+* (bug 6074) Correct squid purging of offsite upload URLs
+* To simplify the lives of extension developers, the logging type arrays
+ can now be appended to directly by an extension setup function. It is
+ no longer necessary to write four separate functions just to add a
+ custom log type.
+* (bug 6057) Count "licenses" as a message (and show it in Special:Allmessages)
+* Added $wgGrammarForms global
+* Fixed hardcoded 'done.' when removing watchlist entries.
+* (bug 5962) Update for Italian language (it)
+* (bug 6086) Remove vestigial attempt to call Article::validate()
+* wfHostname() function for consistent server hostname use in debug messages
+* Send thumbnailing error messages to 'thumbnail' log group
+* wfShellexec() now accepts an optional parameter to receive the exit code
+* Failed, but not zero-length, thumbnail renderings are now removed.
+ Should help clean up when rsvg fails in weird ways.
+* (bug 6081) Change description for Turkmen language
+* Increase robustness of parser placeholders; fixes some glitches when
+ adjacent to identifier-ish constructs such as URLs.
+* Shut up the parser test whining about files in a temp directory.
+* (bug 6098) Add Aragonese language support (an)
+* (bug 6101) Update for Russian language (ru)
+* Add $wgIgnoreImageErrors to suppress error messages for thumbnail rendering
+ problems. If errors are transitory, this should reduce annoying messages
+ making it into cached display.
+* (bug 6103) Wrap self-links in a CSS class ("selflink")
+* (bug 6102) For consistency with other markup, normalize all HTML-encoded
+ character entities in URLs, not just ampersands. This allows use of eg
+ &#61; when making URLs for template parameters.
+* Markup anality: escape </ as <\/ in toolbar javascript for pure correctness
+ under HTML-compatible browsers.
+* (bug 5077) Added hook 'BeforePageDisplay' to SkinTemplate::outputPage
+* Replace fatally changed 'uploadnewversion' with 'uploadnewversion-linktext'
+* (bug 472) Syndication feeds for the last few edits of page history
+* Format edit comments in Recent Changes feed
+* Switch incorrectly ordered column headers on Recent Changes feed diffs
+* (bug 6117) Use message for history feed description, add German localization
+* (bug 1017) fixed thumbnails of animated gifs.
+* Add APC as object caching option
+* Update to Albanian localization (sq)
+* (bug 6099) Introduce {{DIRECTIONMARK}} magic word (with {{DIRMARK}} as an alias)
+* Use optimized php5-only microtime()
+* Add possibility to store local message cache as PHP executable script
+* Fix profiling table definition
+* (bug 6040) Run pre-save transform before calculating the diff. when doing a
+ "show changes" operation in the editor
+* (bug 4033) Respect $wgStyleDirectory when checking available skins
+* Remove hideous backslashes from MessagesBr.php
+* Fix APC object cache issues, add functionality to installer
+* (bug 6133) Update strip state as we work. This mostly fixes extensions
+ used in Cite.php <ref> tags when Tidy is on.
+* (bug 6139) Workaround for transclusion oddities in Vietnamese upload text
+* (bug 6136) Update to Catalan language (ca)
+* Update to Japanese localization (ja)
+* Add /usr/local/bin to the diff3 search paths in the installer
+* (bug 6106) Update to Indonesian localisation (id) #17
+* (bug 6125) Add links to edit old versions to diff views
+* (bug 5127) Auto edit summary when creating/editing redirect page
+* (bug 3926) Introduce {{#language:}} magic word
+* Fix section links from edit comments for [[:Image:Bla.jpg]] in section titles
+* (bug 6126) Allow fallback to customized primary language when user language
+ message contains '-'; fixes licenses selector on Commons configuration after
+ recent addition of the message to Messages.php
+* (bug 5527) Batch up job queue insertions for, hopefully, better survivability
+ of lock contention etc. Duplicates are now removed at pop time instead of
+ at insert time.
+* When showing the "blah has been undeleted" page, make sure it's a blue link
+* parserTests.php accepts a --file parameter to run an alternate test sutie
+* parser tests can now test extensions using !!hooks sections
+* Fix oddity with open tag parameters getting stuck on </li>
+* (bug 5384) Fix <!-- comments --> in <ref> extension
+* Nesting of different tag extensions and comments should now work more
+ consistently and more safely. A cleaner, one-pass tag strip lets the
+ 'outer' tag either take source (<nowiki>-style) or pass it down to
+ further parsing (<ref>-style). There should no longer be surprise
+ expansion of foreign extensions inside HTML output, or differences
+ in behavior based on the order tags are loaded.
+* (bug 885) Pre-save transform no longer silently appends close tags
+* Pre-save transform no longer changes the case of close tags
+* (bug 6164) Fix regression with <gallery> resetting <ref> state
+* Hackaround for IE 7 wrapping bug in MonoBook footer
+* New message sp-newimages-showfrom replaces rclistfrom on special:newimages
+* Improve handling of ;: definition list construct with overlapping or
+ nested HTML tags
+* (bug 6171) Fix sanitizing of HTML-elements with an optional closing
+ tag. The sanitizer still needs to learn how to make well-formed XML
+ in this case.
+* Fix fatal error when specifying illegal name for manual thumbnail
+* (bug 6184) Use shinier Linker::userLink() to make user links in
+ Special:Undelete
+* (bug 6170) Update for Kashubian translation (csb)
+* (bug 6191) Update to Indonesian translation (id) #18
+* (bug 6114) Update to Walloon localization (wa)
+* Added $wgNamespaceRobotPolicies to allow customisation of robot policies on a
+ per-namespace basis.
+* Add <ol> to the list of block elements for doBlockLevels; avoids <p>s being
+ interspersed into your ordered lists.
+* (bug 5021) Transcluding the same special page twice now works
+* Add 'SiteNoticeBefore' and 'SiteNoticeAfter' hooks
+* (bug 6182) Date passed in "sp-newimages-showfrom" not adjusted to user time
+ preferences
+* (bug 2587) Fix for section editing with comment prefix
+* (bug 2607) Fix for section editing with mix of wiki and HTML headings
+* (bug 3342) Fix for section editing with headings wrapped in <noinclude>
+* (bug 3476) Fix for section editing with faux headings in extensions
+* (bug 5272) Fix for section editing with HTML-heading subsections
+* Fix for bogus wiki headings improperly detected with following text
+* Fix for HTML headings improperly not detected with preceding/following text
+* Section extraction and replacement functions merged into one implementation
+ on the Parser object, so they can't get out of sync with each other.
+* Edit security precautions in raw HTML mode, etc
+* (bug 6197) Update to Indonesian translation (id) #19
+* (bug 6175) Improvement to German translation (de)
+* Redirect Special:Logs to Special:Log
+* (bug 6206) Linktrail for Swedish localization (se)
+* (bug 3202) Attributes now allowed on <pre> tags
+* Sanitizer::validateTagAttributes now available to discard illegal/unsafe
+ attribute values from an array.
+* (bug 3837) Leave <center> as is instead of doing an unsafe text replacement
+ to <div class="center">. <center> is perfectly valid in the target doctype
+ (XHTML 1.0 Transitional), while the replacement didn't catch all cases and
+ could even result in invalid output from valid input.
+* (bug 4280) Use 'noindex,nofollow' instead of 'noindex,follow' for default
+ meta robots tag on diff view and special pages. Should reduce impact of
+ robots on scrolling special pages, diffs etc on sites where robots.txt
+ doesn't forbid access.
+* Regression fix: suppress warning about session failure when clicking to
+ edit with 'preview on first edit' enabled.
+* (bug 6230) Regression fix: <nowiki> in [URL link text]
+* Added AutoLoader.php, which loads classes without need of require_once()
+* (bug 5981) Add plural function Slovenian (sl)
+* (bug 5945) Introduce {{CONTENTLANGUAGE}} magic word
+* {{PLURAL}} can now take up to five forms
+* (bug 6243) Fix email for usernames containing dots when using PEAR::Mail
+* Remove a number of needless {{ns:project}}-type transforms from messages files. These
+ usages already have separate label text. Such transforms are wasteful on each page view.
+* Update to Yiddish localization (yi)
+* (bug 6254) Update to Indonesian translation (id) #20
+* (bug 6255) Fix transclusions starting with "#" or "*" in HTML attributes
+* Whitespace now normalized more or less properly in HTML attributes
+* Fix regression(?) in behavior of initial-whitespace-pre in <center>
+* (bug 6260) Update to Interlingua localization (ia)
+* Update to Vlax Romany localization (rmy)
+* Update to Latin translation (la)
+* Update to Dutch translation (nl)
+* Avoid some notices in page history with bad input
+* Use double quoted consistently on attributes in linker output; preparing
+ for new normalization code when tidy not in use
+* Replace "nogomatch" with "noexactmatch" and place the magic colon in the messages
+ themselves. Some minor tweaks to the actual message content.
+* Introduce $wgContentNamespaces which allows for articles to exist in namespaces other
+ than the main namespace, and still be counted as valid content in the site statistics.
+* (bug 5932) Introduce {{PAGESINNAMESPACE}} magic word
+* Disable $wgAllowExternalImages by default.
+* (bug 2700) Nice things like link completion and signatures now work in <gallery> tags.
+* Cancel output buffering in StreamFile; when used inside gzip buffering this
+ could cause funny timeout behavior as the Content-Length was wrong.
+* Return correct content-type header with 304 responses for StreamFile;
+ it confuses Safari if you let it return "text/html".
+* (bug 6280) Correct GRAMMAR for Slovenian localisation (sl)
+* (bug 6162) Change date format for Dutch Low Saxon (nds-nl)
+* (bug 6296) Update to Indonesian localisation (id) #21
+* Introduce EditFormPreloadText hook, see docs/hooks.txt for more information
+* (bug 4054) Add "boteditletter" to recent changes flags
+* Update to Catalan localization (ca)
+* (bug 2099) Deleted image files can now be archived and undeleted.
+ Set $wgSaveDeletedFiles on and an appropriate directory path in
+ $wgFileStore['deleted']['directory']
+* (bug 6324) Fix regression in enhanced RC alignment
+* Introduce {{NUMBEROFADMINS}} magic word
+* Update to Slovak translation (sk)
+* Update to Alemannic localization (gsw)
+* (bug 6300) Bug fixes for sr: variants
+* namespaceDupes.php can now accept an arbitrary prefix, for checking rogue
+ interwikis and such. Not yet fully automated.
+* (bug 6344) Add Special:Uncategorizedimages page
+* (bug 6357) Update to Russian translation (ru)
+* Workaround possible bug in Firefox nightlies by properly removing the
+ Content-Encoding header instead of sending explicit 'identity' value
+ in StreamFile
+* (bug 6304) Show timestamp for current revision in diff pages
+* Vertically align current version with old version header in diff display
+* (bug 6174) Remove redundant "emailforlost" message
+* (bug 6189) Show an error to an unprivilleged user trying to create account
+* (bug 6365) Show user information in the "old revision" navigation links
+* Introduce 'FetchChangesList' hook; see docs/hooks.txt for more information
+* (bug 6345) Update to Indonesian localisation (id) #22
+* (bug 6279) Add genitive month names to Slovenian localisation
+* (bug 6351) Update to German translation (de)
+* Respect language directionality when displaying arrow in Special:Brokenredirects
+* Remove unused "validation" table definitions from the schema files
+* (bug 6398) Work around apparent PCRE bug breaking section editing when
+ massively-indented preformatted text immediately followed a header
+* (bug 6392) Fix misbehaving <br /> in preferences form
+* Add translated magic words to Hebrew localization
+* (bug 6396) Change name for Chuvash language
+* Introduce optional (off by default) language selector bar for user login
+ and registration. Customisable via the "loginlanguagelinks" message, the
+ links will preserve "returnto" values. If the user creates an account while
+ using such a link, then the language in use will be saved as their language
+ preference.
+* Make sure '~~~' '~~~~' '~~~~~' are removed in Nickname preference.
+* Rename "ipusuccess" to "unblocked", change the format (now wiki text)
+* (bug 2316) Add "caption" attribute to <gallery> tag
+* Allow setting the skin object that ImageGallery will use; needed during parse
+ operations (the skin must come from the ParserOptions, not $wgUser)
+* Fix notice in MacBinary detection debug data for files of certain lengths
+* (bug 6131) Add type detection for DjVu files, allowing them to be uploaded
+ with validity checking and size detection. No inline thumbnailing yet,
+ but could be added in the future.
+* (bug 6423) Don't update newtalk flag if page content didn't change (null edits
+ were causing the newtalk flag to trigger inappropriately)
+* Parser functions are now set using magic words.
+* (bug 6428) Incorrect form action URL on Special:Newimages with hidebots = 0 set
+* (bug 4990) Show page source to blocked users on edits, or their modified version
+ if blocked during an edit
+* (bug 5903) When requesting the raw source of a non-existent message page,
+ return blank content (as opposed to the message key)
+* Improve default blank content of MediaWiki:Common.css and MediaWiki:Monobook.css
+* (bug 6434) Allow customisation of submit button text on Special:Export
+* (bug 6314) Add user tool links on page histories
+* Fix display of file-type icons in galleries when $wgIgnoreImageErrors is off
+* (bug 6438) Update to Indonesian translation (id) #23
+* Adding the language code parameter to the hook "LanguageGetMagic", to allow
+ localizble extensions magic words.
+* Update to Romanian translation (ro)
+* Update to Esperanto translation (eo)
+* Check for preg_match() existence when installing and die out whining about PCRE
+ if it's not there, instead of throwing a fatal error
+* (bug 672) Add MathAfterTexvc hook
+* Update to Piedmontese localization (pms)
+* dumpBackup can optionally compress via dbzip2
+* (bug 2483) Run link updates on change via XML import
+* (bug 2481) List imported pages during Special:Import
+* (bug 2482) Log and RC entries for Special:Import events
+* Allow fetching all revisions from transwiki Special:Import
+* Allow fetching all revisions from Special:Export GET request
+* Disable output buffering on Special:Export; should help with streaming
+ large numbers of history items.
+* Allow setting a maximum number of revisions for history Special:Export;
+ pages with more than $wgExportMaxHistory revisions are excluded from
+ export when history is requested.
+* Fix transwiki import of pages with space in name
+* Save null edit when importing pages through Special:Import
+* Update to Korean translation (ko)
+* Show a more specific message when an anonymous user tries to access Special:Watchlist
+* (bug 3278) Paging links in Special:Prefixindex
+* Added Latvian localization (lv)
+* (bug 6472) Fix regression in Special:Export with multiple pages
+* Update to Macedonian translation (mk)
+* Allow page moves over historyless self-redirects. Such are usually created
+ as part of namespace rearrangements, and it's easier to clean them up if
+ we can move over them.
+* Show some error results in moveBatch.php
+* (bug 6479) Allow specification of the skin to use during HTML dumps
+* (bug 6461) Link to page histories in Special:Newpages
+* (bug 6484) Don't do message transformations when preloading messages for editing
+* (bug 6201) Treat spaces as underscores in parameters to {{ns:}}
+* (bug 6006) Allow hiding the password change fields using an authentication plugin
+* (bug 6489) Use appropriate link colour on Special:Shortpages
+* Added formatnum magic word
+* Added Javanese localization (jv)
+* (bug 6491) Apply bad image list in category galleries
+* (bug 6488) Show relevant log fragment in Special:Movepage
+* Fix potential PHP notice in Special:Blockme when $wgBlockOpenProxies is true
+* Use mysql_real_escape_string instead of addslashes for string escaping in
+ the MySQL Database class. This may fix some rare breakage with binary fields.
+ Note that MediaWiki does not support the multibyte character sets where a
+ "dumb" byte replacement can be actively dangerous; UTF-8 is always safe
+ in this regard due to the bit patterns which make head and tail bytes
+ distinct.
+* (bug 6497) Use $wgMetaNamespaceTalk for Esperanto if set
+* (bug 6498) Use localized forms for image size in Special:Undelete
+* (bug 6485) Update to Indonesian translation (id) #24
+* Extension messages translation is now possible.
+* Add target namespace override selector for transwiki imports.
+ $wgImportTargetNamespace specifies the default, to be used for
+ Wiktionary's 'Transwiki:' namespace etc.
+* (bug 6506) Update to German localisation (de)
+* (bug 502) Avoid silly tabs on bad title by using virtual special page
+* (bug 6511) Add diff links to old revision navigation bar
+* (bug 6511) Replace 'oldrevisionnavigation' message with 'old-revision-navigation'
+* Fix regression in Polish genitive month forms
+* (bug 4037) Make input handling in Special:Allpages and Special:Prefixindex
+ more consistent: Accept just a namespace prefix and a colon, reject input
+ with interwiki prefixes, otherwise do what Title::makeTitleSafe() does.
+* (bug 6516) Update to Russian translation
+* New 'allpagesbadtitle' message for Special:Allpages, based on 'badtitletext'.
+* Rename "searchquery" to "searchsubtitle" and support wiki text in it
+* Introduce updateArticleCount maintenance script which uses a better check that
+ reflects what Article::isCountable() tests for
+* Introduce 'BadImage' hook; see docs/hooks.txt for more information
+* Add "searchsubtitleinvalid" message for searches that are not valid titles.
+* (bug 5962) Update to Italian localisation
+* (bug 6530) Update to Indonesian localisation (id) #25
+* (bug 6523) Fix SVG issue in rebuildImages.php
+* (bug 6512) Link to page-specific logs on page histories
+* (bug 6504) Allow configuring session name with $wgSessionName
+* (bug 6185) Add standard user tool links to log page views
+* Update to Venetian translation (vec)
+* Update to Slovenian translation (sl)
+* Add standard user tool links to deleted revision list
+* Separate out EditPage's getContent bits from regular Article getContent.
+ Cleans up read-only-mode warning on empty pages and neats up some code.
+* (bug 6565) Strict JavaScript writing
+* (bug 6570) Update to Indonesian localisation (id) #26
+* Added Telugu translation (te)
+* Update to Catalan translation (ca)
+* (bug 6560) Avoid PHP notice when trimming ISBN whitespace
+* Added namespace translation to Kannada (ka)
+* (bug 6566) Improve input validation on timestamp conversion
+* Implicit group "emailconfirmed" for all users whose email addresses are confirmed
+* (bug 6577) Avoid multiline parser breakage on <pre> with newline in attribute
+* (bug 6771) Make old revisions of MediaWiki pages available with action=raw
+
== Changes since 1.5 ==
* (bug 2885) More PHP 5.1 fixes: skin, search, log, undelete
diff --git a/INSTALL b/INSTALL
index 7240e417..22bf3670 100644
--- a/INSTALL
+++ b/INSTALL
@@ -8,7 +8,7 @@ the necessary prerequesites available.
Required software:
* Web server with PHP 5.x or higher.
-* A MySQL server, 4.0.14 or higher.
+* A MySQL server, 4.0.14 or higher OR a Postgres server, 8.1 or higher
MediaWiki is developed and tested mainly on Unix/Linux
platforms, but should work on Windows as well.
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 7378b5df..55582eb2 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -3,32 +3,26 @@
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.7.1 ==
+== MediaWiki 1.8.1 ==
-July 8, 2006
+October 11, 2006
-MediaWiki 1.7.1 is a security and bugfix maintenance release of the
-Summer 2006 snapshot:
+MediaWiki 1.8.1 fixes several issues in the Fall 2006 snapshot release:
-A potential HTML/JavaScript-injection vulnerability in a debugging script
-has been fixed. Only versions and configurations of PHP vulnerable to the
-$GLOBALS overwrite vulnerability are affected.
+* Fix PHP notice and estimates for dumpBackup.php and friends
+* Improved register_globals paranoia checks
+* (bug 7545) Fix PHP version check on install
+* Experimental web API disabled by default
+* Disable PHP exception backtrace printing unless $wgShowExceptionDetails
+ is set. Backtraces may contain sensitive information in function call
+ parameters.
-As a workaround for existing installs, profileinfo.php may simply be deleted
-if it's not being used.
-* Fix for 'emailconfirmed' implicit user group
-* Fix for upgrades on some versions of MySQL 4.0.x
-* Fixed potential XSS in profileinfo.php
-* Installer now shows clear error message about old PHP versions
- rather than a confusing parse error
+== MediaWiki 1.8.0 ==
+October 10, 2006
-== MediaWiki 1.7.0 ==
-
-July 6, 2006
-
-This is the quarterly release snapshot for Summer 2006. While the code
+This is the quarterly release snapshot for Fall 2006. 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.
@@ -46,26 +40,333 @@ 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
-== Compatibility ==
+== Configuration changes ==
+
+* $wgUseETag, to enable/disable sending of HTTP ETag headers
+ (default: disabled)
+* $wgLegalTitleChars now includes '+' by default for better compatibility
+ with importing data dumps from Wikipedia
+* $wgDefaultUserOptions now includes all default option settings instead
+ of only overrides.
-MediaWiki 1.7 requires PHP 5 (5.1 recommended). PHP 4 is no longer supported.
-If you are unable to run PHP 5, you may have to stick with 1.6 for now.
+== Major new features ==
+
+* (bug 7098) Add an option to disable/enable sending of HTTP ETag headers,
+ as it seems to result in broken behaviour in combination with Squid 2.6
+ (disabled by default).
+* (bug 550) Allow blocks on anonymous users only.
+* (bug 6420) Render thumbnails for DJVU images, support multipage DJVU display
+ on image pages. Added new 'page=' thumbnail option to select a page from a
+ multipage djvu for thumbnail generation.
+* Full Postgres support is now enabled. It requires version 8.1 or better, and
+ needs to have both plpgsql and tsearch2 already installed.
+* (bug 6386) fix grammatical errors in danish naming of talk namespaces.
+
+== Changes since 1.7 ==
+
+* Introduced AjaxResponse object, superceding AjaxCachePolicy
+* Changes to sajax_do_call: optionally accept an element to fill instead of a
+ callback function; take the target function or element as a third parameter;
+ pass the full XMLHttpRequest object to the handler function, instead of just
+ the resultText value; use HTTP response codes to report errors.
+* (bug 6562) Removed unmaintained ParserXml.php for now
+* History paging overlap bug fixed
+* (bug 6586) Regression in "unblocked" subtitle
+* Don't put empty-page message into view-source when page text is blank
+* (bug 6587) Remove redundant "allnonarticles" message
+* Block improvements: Allow blocks on anonymous users only. Optionally allow
+ or disallow account creation from blocked IP addresses. Prevent duplicate
+ blocks. Fixed the problem of expiry and unblocking erroneously affecting
+ multiple blocks. Fixed confusing lack of error message when a blocked user
+ attempts to create an account. Fixed inefficiency of Special:Ipblocklist in
+ the presence of large numbers of blocks; added indexes and implemented an
+ indexed pager.
+* (bug 6448) Allow filtering of Special:Newpages according to username
+* (bug 6618) Improve permissions/error detection in Special:Lockdb
+* Quick hack for extension testing: parser test doesn't create new message
+ cache object.
+* (bug 6299) Maintain parser's revision ID across recursive calls to fix
+ {{REVISIONID}} when Cite extension is used
+* (bug 6622) Removed deprecated function Image::newFromTitle
+* (bug 6627) Fix regression in Special:Ipblocklist with table prefix
+* Removed forced dereferencements (new() returns a reference in PHP5)
+* Note about $wgUploadSizeWarning using byte
+* (bug 6592) Add most viewed pages summary to Special:Statistics
+* Pre-strip characters ignored in IDNs from URLs so they can't be used
+ to break the blacklists for regular URLs
+* Fix regression in blocking of user accounts
+* (bug 6635) Fix regression searching for range blocks on Ipblocklist
+* Fix regression searching Ipblocklist with ugly URLs
+* (bug 6639) Use a consistent default for upload directories
+* Preserve entered reason when reporting unconfirmed lock on Special:Lockdb
+* (bug 6642) Don't offer to unlock the database when it isn't locked
+* cleanupTitles.php changed from --dry-run option to --fix, so default
+ behavior is now a non-invasive check as with namespaceDupes.php
+* (bug 6660) Fix behaviour of EditPage::blockedPage() when the article does
+ not exist; now doesn't show the source box if the user hasn't provided it
+ (blocked mid-edit) and the page doesn't exist
+* Improve default value of "blockedtext"
+* (bug 6680) Added localisation for Dutch bookstore list (nl)
+* Renamed maintainace script redundanttrans.php to unusedMessages.php - clearer usage
+* Fix regression which allowed some blocked users to create additional accounts
+* (bug 6657) Fix Hungarian linktrail
+* (bug 6751) Fix preview of blanked section with edit on first preview option
+* (bug 5456) Separate MediaWiki:Search into messages for both noun and verb,
+ introduced 'MediaWiki:Searchbutton'
+* Made lines from initialiseMessages() appear as list items during installation
+* Moved the bulk of the localisation data from the Language*.php files to the
+ Messages*.php files. Deleted most of the Languages*.php files.
+* Introduced "stub global" framework to provide deferred initialisation of core
+ modules.
+* Removed placeholder values for $wgTitle and $wgArticle, these variables will
+ now be null during the initialisation process, until they are set by index.php
+ or another entry point.
+* Added DBA cache type, for BDB-style caches.
+* Removed custom date format functions, replacing them with a format string in
+ the style of PHP's date(). Used string identifiers instead of integer
+ identifiers, in both the language files and user preferences. Migration should
+ be transparent in most cases.
+* Simplified the initialisation API for LoadBalancer objects.
+* Removed the broken altencoding feature.
+* Moved default user options and toggles from Language to User. Language objects
+ are still able to define default preference overrides and extra user toggles,
+ via a slightly different interface.
+* Don't include the date option in the parser cache rendering hash unless
+ $wgUseDynamicDates is enabled.
+* Merged LanguageUtf8 with Language. Removed LanguageUtf8.php.
+* Removed inclusion of language files from the bottom of Language.php. This is
+ now consistently done from Language::factory().
+* Add the name of the executing maintenance script to the debug log. Start the
+ profiler during maintenance scripts.
+* Added "serialized" directory, for storing precompiled data in serialized form.
+* Fix regression in auto-set NS_PROJECT_TALK namespace
+* Fix regression in ordering of namespaces
+* (bug 6806, 6030) Added several global JS variables for article path, user name,
+ page title, etc.
+* hooks registered with addOnloadHook are now called at the one of the html body
+ by all skins.
+* Split ajax aided search from core ajax framework. Use wgUseAjax to enable the
+ framework and wgAjaxSearch to enable the suggest feature for the search box.
+* Added experimental installer for extensions.
+ See maintenance/installExtension.php
+* Added Tajic (tg) language file.
+* (bug 6903) Added Cantonese localisation (zh-yue)
+* Fix regression in Korean and Japanese date formatting (day of week)
+* (bug 6919) Add English alias magic words for Tatar (tt) language file.
+* (bug 6753) Fixed broken Kazakh linktrail (kk)
+* (bug 6700) Added Kazakh language variants to Names.php
+* (bug 6827) some i18n specific maintenance scripts fails after merge of localisation-work branch
+* Throwed an exception for the deprecated functions OutputPage::sysopRequired and
+ OutputPage::developerRequired - use OutputPage::permissionRequired instead.
+* Removed the deprecated functions User::isSysop, User::isBureaucrat and User::isDeveloper -
+ use User::isAllowed instead.
+* (bug 769) OutputPage::permissionRequired() should suggest groups with the needed permission
+* (bug 6971) Fix regression in Special:Export history view
+* Revamped Special:Imagelist
+* (bug 7000) updated MessagesPl.php
+* (bug 6946) Fix unexpected behavior change with GET hits to Special:Export
+* (bug 1866) Improve navigation on Special:Listusers; user now a starting
+ point as with Special:Allpages, rather than a pure limit.
+* Clean up tab order on Special:Blockip
+* (bug 5969) Clean up tab order on Special:Userlogin forms
+* (bug 3512) namespaceDupes now handles spaces and initial caps properly
+* (bug 7037) Fix regression in login tab order
+* (bug 7031) Report missing email on 'email password' instead of false success
+* (bug 7010) Don't send email notifications for watched talk pages when user
+ has selected to receive only updates for their own talk page
+* Added {{CURRENTHOUR}}
+* Added [[:Image:Foo.png]] style links to the pagelinks table
+* Avoid duplicate revision imports with Special:Import
+* (bug 7054) Validate email address before sending email confirmation message
+* (bug 7061) Format title on "from (page)" links on Special:Allpages
+* (bug 7044) Introduce "padleft" and "padright" colon functions
+* Pass page title as parameters to "linkshere" and "nolinkshere" and update
+ default message text
+* Allows to upload from publicy accessible URL. Set $wgAllowCopyUploads = true ; in LocalSettings.php
+ Limited to $wgMaxUploadSize (default:100MB); URL upload is limited to sysops by default, and displayed as a second line if appropriate
+* (bug 832) Return to user page after emailing a user
+* (bug 366) Add local-system-timezone equivalents for date/time variables
+* (bug 7109) Fix Atom feed version number in header links
+* (bug 7075) List registered parser function hooks on Special:Version
+* (bug 7059) Introduce "anchorencode" colon function
+* Include SVN revision number in {{CURRENTVERSION}} output, where applicable
+* Fix bug in wfRunHooks which caused corruption of objects in the hook list
+* (bug 4979) Use simplified email addresses when running on Windows
+* (bug 4434) Show block log fragment on Special:Blockip
+* [[MediaWiki:Disambiguationspage]] may optionally contain wiki links to any number
+ of disambiguation templates.
+* [[Special:Disambiguations]] now shows pages in NS:0 that link to any pages that embed
+ any of the templates listed at [[MediaWiki:Disambiguationspage]].
+* Fix formatting of titles on Special:Undelete
+* (bug 7026) Fix action=raw&templates=expand
+* (bug 6976) Add namespace and direction classes to classic skins
+* (bug 7144) Don't "return to main" from OutputPage::loginToUse() if the the user can't
+ read the main page in the first place
+* (bug 7188) Fix minor borkage in HTMLForm
+* (bug 6675) Replaced message 'watchthis' with new message 'watchthisupload in Special:Upload
+* Add a quickie script dumpSisterSites.php for generating a page list in the
+ format for WSR-1 SisterSites support
+* (bug 7223) Monobook.js is used for site content, should not be localized
+* Set default disabled values for DjVu render options
+* Added Xml::option() for generating <option>s easily
+* Localized page numbers in drop-down for DjVu page selection
+* Fixed linktrail for vi
+* (bug 6893) "Call to a member function exists() on a non-object" on trackback.php with bad input
+* (bug 6886) PHP undefined offset on bad input to Special:Revisiondelete
+* (bug 6887) PHP error for call to getId() on bad input to Special:Revisiondelete
+* (bug 6888) PHP error for call to getTimestamp() on bad input to Special:Revisiondelete
+* (bug 7252) Use dvipng support in texvc math rastrization. dvipng is required if texvc is rebuilt.
+* (bug 7279) Use wfBaseName in place of basename() in more places
+* Clear newtalk marker on diff links with explicit current revision number
+* (bug 7064) Replace hard-coded empty message checks with wfEmptyMsg calls
+* (bug 6777) Remove some PHP 4 compat cruft
+* Add --user, --comment, and --license options to importImages.php
+* (bug 6216) The immobile namespace message does not mention the source page
+* (bug 7299) Normalize username filter on Special:Newpages
+* (bug 7306) RTL text in an LTR wiki breaks appearance of Special:Recentchanges
+* (bug 7312) Don't emit SET NAMES utf8 if connection failed
+* (bug 7305) Proper compare for bot check on RC notify, should fix overrides
+ that force edits by non-bot users to bot mode
+* Set Vary: Cookie on action=raw generated CSS and JS, to ensure that user
+ preferences don't get stuck in proxy caches for other people
+* (bug 7324) Fix error message for failure of Database::sourceFile()
+* (bug 7309) Plurals: use singular form for zero in French and Brazilian Portuguese
+* Add page_no_title_convert field to support language variant conversion
+ for page titles which shouldn't be converted on display/linking
+* Lazy extraction of text chunks in Revision objects, may reduce hits to
+ external storage when actual text content is not used
+* Added experimental $wgRevisionCacheExpiry to cache extracted revision text
+ in $wgMemc, to further reduce hits to external storage.
+ Set to 0 (disabled) by default.
+* Minor changes to the installer.
+* Remove ":" for 'youremail' and 'yourrealname' in includes/templates/Userlogin.php
+ so that ":" could be used in i18n for Special:Preferences (like 'username' and 'uid').
+* Fix layout for Special:Preferences->Date and Time (position for 'timezonetext').
+* Updates to language variant code for Serbian et al
+* (bug 6756) Enabling RTL direction for kk-cn
+* (bug 6701) Kazakh language variants in MessagesEn.php
+* (bug 7335) SVN revision check in Special:Version fails on SVN 1.4 working copy
+* (bug 6518) Replaced 'lastmodified' with 'lastmodifiedat' and 'lastmodifiedby' with 'lastmodifiedatby'
+ with seperated parameters for date and time to allow better localisation. Updated all message files
+ to display the old format for compatibility.
+* (bug 7357) Make supposedly static methods of Skin actually static
+* Added info text to Special:Deadendpages and Special:Lonelypages
+* Fix regression in cachability of generated CSS and JS for MonoBook skin,
+ while avoiding clobbering of different users' cached data
+* (bug 6849) Block @ from usernames; interferes with multi-database tools and
+ was meant to be banned years ago... For now existing accounts will not be
+ prevented fromm login.
+* (bug 6092) Introduce magic words {{REVISIONDAY}}, {{REVISIONDAY2}, {{REVISIONMONTH}},
+ {{REVISIONYEAR}} and {{REVISIONTIMESTAMP}}
+* (bug 7425) Preceeding whitespace in [[...]] breaks subpages
+* Try to reconnect after transitory database errors in dumpTextPass.php
+* (bug 6023) Fixed mismatch of 0/NULL for wl_notificationtimestamp; now notification
+ mails are working after 'Mark all pages visited' button on Special:Watchlist is clicked
+* Made {{INT:}} a core parser function instead of a special case. The syntax
+ and behaviour is largely unchanged.
+* (bug 7448) Fixing the native name for Ewe (ee)
+* (bug 6864) Replace message 'editing' with new message 'editinguser' in Special:Userrights
+ to allow better localisation
+* Add '*-summary' for special pages to MessagesEn.php to allow customizing/translation
+ directly through Special:Allmessages
+* (bug 6130, bug 5818) Replaced message 'go' with the new message 'searcharticle' in skins
+ to allow better localisation
+* Add + to $wgLegalTitleChars by default. Some sites may have occasional
+ problems with hard-to-reach pages, but it should be less trouble than
+ "I can't import dumps from Wikipedia" complaints
+* (bug 7460) Revert broken patch for bug 7226 which slows down
+ Special:Allmessages by a factor of 16
+* Committed a bunch of live hacks from Wikimedia servers
+* (bug 6889) PHP notices in thumb.php with missing params
+* Cleaner error behavior on thumb.php with invalid page selection
+* (bug 6617) Validate timestamps on Special:Undelete
+* Do fewer unnecessary full writes of user rows; only update user_touched
+ for watch/unwatch, group membership change, and login operations
+* Restructured the languages directory, to avoid problems when people
+ untar MW 1.8 over the top of a 1.7 installation.
+* (bug 6890) SQL query error on bad input to Pager lists
+ due to negative LIMIT clause, caused by integer wraparound.
+* Fixed various bugs related to table prefixes, especially the interaction
+ between table prefixes and memcached, which was formerly completely broken.
+* (bug 7004) PHP iconv() notice on bad password input to Special:Userlogin.
+* (bug 6826) Extend pre-save transform context link ("pipe trick")
+ syntax to pages with commas in title
+* Use ImageMagick -thumbnail option instead of -resize to avoid including
+ excessive metadata in thumbs (requires ImageMagick 6.0.0 or newer).
+* (bug 7499) Corrections to Swedish talk namespace names
+* (bug 7508) Added option to compress HTML pages by dumpHTML.php
+* (bug 7519) Add plural in SpecialWatchlist
+* (bug 7459) Magic word variables are always case sensitive
+* Replaced {{SERVER}}{{localurl:xxx}} with {{fullurl:xxx}} in localisation files
+* Fix regression in Special:Watchlist text header
+* (bug 7510) Update article counts etc on undelete
+* (bug 7520) Update article counts on XML import
+* (bug 7526) Make $wgDefaultUserOptions work again
+* (bug 7472) Localize Help namespace for Basque
+* (bug 7529) Including a non-existent category in an article places that article in the category
+* (bug 4528) Lack of important LaTeX functions stackrel, rightleftharpoon
+* (bug 6721) missing symbols ulcorner, urcorner, llcorner, lrcorner, twoheadrightarrow, twoheadleftarrow
+* (bug 7367) Hyphens sometimes erroneously appended to equations when not converted to PNG
+* Add "title" to the opensearch link to allow automatic adding of the search engine in Firefox 2
+* (bug 7537) Add php5 to $wgFileBlacklist
+* (bug 6929) Restore AutoAuthenticate hook
+
+
+== Languages updated ==
+
+* Albanian (sq)
+* Bashkir (ba)
+* Bavarian (bar) stub file
+* Belarusian (be)
+* Bishnupriya (bpy) stub file
+* Brazilian Portuguese (pt-br)
+* Cantonese (zh-yue)
+* Catalan (ca)
+* Czech (cs)
+* Dutch (nl)
+* English (en)
+* Finnish (fi)
+* French (fr)
+* Georgian (ka)
+* German (de)
+* Hebrew (he)
+* Hungarian (hu)
+* Indonesian (id)
+* Japanese (ja)
+* Korean (ko)
+* Latin (la)
+* Lojban (jbo)
+* Macedonian (mk)
+* Mazandarani (mzn)
+* Polish (pl)
+* Portuguese (pt)
+* Ripuarian (ksh)
+* Romani (rmy)
+* Russian (ru)
+* Slovak (sk)
+* Spanish (es)
+* Tajic (tg)
+* Tatar (tt)
+* Telugu (te)
+* Uzbek (uz)
+* Yiddish (yi)
+
+== Compatibility ==
+
+MediaWiki 1.8 requires PHP 5 (5.1 recommended). PHP 4 is no longer supported.
MySQL 3.23.x is no longer supported; some older hosts may need to upgrade.
At this time we still recommend 4.0, but 4.1/5.0 will work fine in most cases.
-Experimental Oracle support has been dropped as it is unmaintained.
-
== Upgrading ==
-Several changes to the database have been made from 1.6:
-
-* A new "langlinks" table tracks interlanguage links
-* A new "filearchive" table stores information on deleted files
-* A new "querycache_info" table stores information on query page updates
+Some minor database changes have been made since 1.7:
+* new fields and indexes on ipblocks
+* index change on recentchanges
+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.
@@ -76,649 +377,6 @@ break. Don't forget to always back up your database before upgrading!
See the file UPGRADE for more detailed upgrade instructions.
-== Configuration changes ==
-
-Some configuration options have changed:
-* $wgAllowExternalImages now defaults to off for increased security.
-* $wgLocalTZoffset was in hours, it is now using minutes.
-* Extensions may register special pages via the $wgSpecialPages array
- without forcing an early load of the SpecialPage.php class file.
-
-
-== Major new features ==
-
-* Deleted files can now be archived and undeleted, if you set up
- an appropriate non-web-accessible directory.
- Set $wgSaveDeletedFiles on and an appropriate directory path in
- $wgFileStore['deleted']['directory']
-* Experimental PostgreSQL support has been updated. It may or may
- not be in usable shape; those interested in PostgreSQL are encouraged
- to follow 1.8 development.
-
-
-
-== Changes since 1.6 ==
-
-* (bug 5458) Fix double-URL encoding in block log link in contribs and contribs
- link in block log
-* (bug 5462) Bogus missing patch warning in updater
-* (bug 5461) Use of deprecated "showhideminor" in Special:Recentchangeslinked
-* PHP warning when allow_call_time_pass_reference is off
-* Update to Finnish localization
-* (bug 5467) Link to page histories in watchlist edit mode
-* Further additions to Hebrew localisation
-* (bug 5476) Invalid xhtml in German localization
-* (bug 5479) Id translation for preferences tabs caption
-* (bug 5493) Id translation for special pages
-* Added skinname and style path parameters to CBT version of MonoBook
-* Include subversion revision number in Special:Version if available
-* (bug 5344) Fix regression that broke slashes in extension tag parameters
-* Improve Special:Log performance on big log sets
-* (bug 5507) Changed mediawiki:logouttext from plain to wikitext
-* (bug 4760) Prevent creation of entries in protection log when protection
- levels haven't changed
-* (bug 861) Show page protection/unprotection events in histories
-* (bug 5499) Don't clear the tag strip state when asked not to clear state.
- Fixes regression with use of <ref> in a template breaking <nowiki> etc.
-* Minor improvements to English language files
-* Display the anon talk page info message on anon talk pages again
- (moved outside the parser cache)
-* Optional {{DISPLAYTITLE|title with markup}} magic word
- Deactivated by default, set "$wgAllowDisplayTitle = true" in LocalSettings.php
- to activate
-* Cleaned SpecialContributions a bit
-* Added a table to track interlanguage links
-* (bug 5544) Fix redirect arrow in Special:Listredirects for right-to-left
- languages
-* Replace "doubleredirectsarrow" with a content language check that picks the
- appropriate arrow
-* (bug 5537) Add stub language file for Samogitian (bat-smg); inherits
- Lithuanian (lt)
-* Don't force edit summaries when a user is editing their own user/talk page
-* (bug 5510) Warning produced when using {{SUBPAGENAME}} in some namespaces
-* (bug 385) Installer support for PostgreSQL, fixes for PG compatibility
-* PersistentObject removed; it doesn't do anything and was broken besides.
- All extensions using it have been corrected.
-* Propagate ISBN number for Booksources in LanguageNo.php
-* (bug 5548) Improvements to Indonesian localisation [patch: Ivan Lanin]
-* Add TALKSPACE, SUBJECTSPACE, TALKPAGENAME, SUBJECTPAGENAME (and encoded forms
- for all) magic words
-* (bug 5403) Fix Special:Newpages RSS/Atom feeds
-* Reject malformed addresses in X-Forwarded-For entries
-* (bug 3359) Add hooks on completion of file upload
-* (bug 5559) Improve detection of ImageMagick [patch: Greg Turnquist]
-* (bug 5475) New pages feeds ignore "limit" argument
-* (bug 5184) CSS misapplied to elements in Special:Allmessages due to
- conflicting anchor identifiers
-* (bug 5519) Allow sidebar cache to be disabled; disable it by default.
-* Maintenance script to import the contents of a text file into a wiki page
-* Add $wgReservedUsernames configuration directive to block account creation/use
-* (bug 5576) Remove debugging hack in session check
-* (bug 5426) Lowercase treatment of titles in rights log leads to broken links
- on Special:Log
-* Minor improvements to French localisation files
-* (bug 5181) Update "nogomatch" for Slovak
-* (bug 5594) Id translation up to # Login and logout pages section
-* (bug 5536) Use content language for editing help link
-* Improvements to German localisation files
-* (bug 5570) Problems using <special page>/parameter link form for long titles
-* (bug 3884) Add $user parameter to AddNewUser hook, call it for by-email
- registrations as well as self-registrations.
-* (bug 4327) Report age of cached data sets in query pages
-* (bug 4662) Fix Safari check in wikibits.js
-* (bug 4663) Edit toolbar enabled in compatible versions of Safari
-* (bug 5572) Edit toolbar enabled in compatible versions of Konqueror (3.5+)
-* (bug 5235) Edit toolbar tooltips no longer show JavaScript junk in Opera
-* Edit toolbar now works in pure XHTML mode (application/xhtml+xml)
-* Add watchlist clear function to allow quick purging of all items
-* (bug 5625) Additional namespace translations for Welsh
-* Add meta tag and JavaScript variables to cached special pages which provides
- the timestamp of the last update, in YYYYMMDDHHMMSS format.
-* (bug 5628) More translations for MessagesHr.php
-* (bug 5595) Localisation for Bosnian language (bs)
-* (bug 2910) Default view preferences for watchlists
-* Add "hide bot edits from the watchlist" user preference
-* (bug 5250) Introduce Special:Unusedtemplates
-* Add user preference setting for an extended watchlist, showing all recent
- edits up to a certain edit, and not just the latest edit..
-* Made MessageRo.php more general
-* (bug 5640) Indonesian localisation improvements
-* (bug 5592) Actions are logged with the default language for the
- wiki, not the language of the user performing the operation.
-* (bug 5644) Error in LanguageBs.php file
-* (bug 5646) Compare for identical types in wfElement()
-* (bug 5472) Language::userAdjust()->minDiff not initialized on else condition
-* (bug 5386) LanguageMk.php: updated namespaces translations
-* (bug 5422) Stub for Romani (rmy) language which extends ro
-* Fix linktrail for LanguageSr
-* (bug 5664) Fix Bosnian linktrail
-* (bug 3825) Namespace filtering on Special:Newpages
-* (bug 1922) When Special:Wantedpages is cached, mark links to pages
- which have since been created
-* (bug 5659) Change grammar hacks for Bosnian Wikimedia namespaces.
- This sort of special casing should be removed and fixed properly.
-* Remove useless whitespace from Special:Brokenredirects header
-* Treat "allmessagesnotsupporteddb" as wikitext when echoing; change default
- text
-* (bug 5497) Regression in HTML normalization in 1.6 (unclosed <li>,<dd>,<dt>)
-* (bug 5709) Allow customisation of separator for categories
-* (bug 5684) Introduce Special:Randomredirect
-* (bug 5611) Add a name attribute to the text box containing source text in
- read-only pages
-* Indicate when a protected page is an interface message ("protectedinterface")
-* (bug 4259) Indicate when a protected page being edited is an interface message
- ("editinginterface")
-* (bug 4834) Fix XHTML output when using $wgMaxTocLevel
-* Pass login link to "whitelistedittext" containing 'returnto' parameter
-* (bug 5728): mVersion missing from User::__sleep() leading to constant cache
- miss
-* Updated maintenance/transstat.php so it can show duplicate messages
-* Improvements to update scripts; print out the version, check for superuser
- credentials before attempting a connection, and produce a friendlier error if
- the connection fails
-* (bug 5005) Fix XHTML <gallery> output.
-* (bug 5315) "Expires: -1" HTTP header made strictly valid (using 1970 date).
-* (bug 4825) note in DefaultSettings.php about 'profiling' table creation
-* Remove unneeded extra whitespace at top of Special:Categories
-* (bug 5679) time units are now using local numerals
-* (bug 5751) Updates to Portuguese localisation files
-* (bug 5741) Introduce {{NUMBEROFUSERS}} magic word
-* (bug 93) <nowiki> tags and tildes in templates
-* The returnto parameter is now actually used by SpecialUserlogin.php
-* Parser can now know that it is parsing an interface message
-* (bug 4737) MediaWiki:Viewcount supports {{PLURAL}} now
-* Fix bug in wfMsgExt under PHP 5.1.2
-* (bug 5761) Project talk namespace broken in Xal, Os, Udm and Cv
-* Rewrite reassignEdits script to be more efficient; support optional updates to
- recent changes table; add reporting and silent modes
-* Cleaned up formatNum usage in langfiles
-* (bug 5716) Warn when a user tries to upload a file which was previously
- deleted
-* (bug 5565) Add a class attribute to the table on Special:Allpages
-* "lang=xx" option for parser test cases to set content language
-* (bug 5764) Friulian translation updated
-* (bug 5757) Fix premature cutoff in LanguageConverter with extra end markers
-* (bug 5516) Show appropriate "return to" link on blocked page
-* (bug 5377) Do not auto-login when creating an account as another user
-* (bug 5284) Special redirect pages should remember parameters
-* Suppress 7za output on dumpBackup
-* (bug 5338) Reject extra initial colons in title
-* (bug 5487) Escape self-closed HTML pair tags
-* Add "raw suffix" magic word for some magic words, e.g. {{NUMBEROFUSERS|R}}
- will produce a count minus formatting
-* Fix Parser::cleanSig() to use Parser::startExternalParse() and choose an
- appropriate output format given the scope of the clean
-* (bug 5593) Change "bureaucrat log" to "rights log"
-* Show a boilerplate "(none)" in place of a blank within the log action text for
- user rights
-* (bug 137) Commented out translations for copyrightwarning which mention GNU FDL
-* (bug 5723) Don't count pages linked to from the MediaWiki namespace as "wanted"
-* (bug 5696) Add a third parameter, $3, to "rcnote", passing the current time
- formatted according to the current user's settings
-* (bug 5780) Thousands and decimal separators for Norwegian
-* Updated initStats maintenance script
-* (bug 5767) Fix date formats in Vietnamese locale
-* (bug 361) URL in URL, they were almost fixed. Now they are.
-* (bug 4876) Add __NEWSECTIONLINK__ magic word to force the "new section" link/tab to
- show up on specific pages on demand
-* Bidi-aid on list pages
-* (bug 5782) Allow entries in the bad image list to use canonical namespace names
-* (bug 5789) Treat "loginreqpagetext" as wikitext
-* Sanitizer: now handles nested <li> in <ul> or <ol>
-* (bug 5796) We require MySQL >=4.0.14
-* Add 'EmailConfirmed' hook
-* New findhooks.php script to find undocumented hooks.
-* Silently ignore errors on profiling table update.
-* (bug 5801) Correct handling of underscores in Special:Listusers
-* Clean up Special:Listusers; add an "(all)" label to the group selection box
-* (bug 5812) Use appropriate link colour in Special:Mostlinked
-* (bug 5802) {{CURRENTMONTHNAME}} variable broken in Vietnamese locale
-* (bug 5817) Appropriate handling for Special:Recentchangeslinked where the target
- page doesn't exist
-* Special:Randompage now additionally accepts English namespace name as parameter
-* (bug 2981) Really fixed linktrail for Tamil (ta)
-* Disallow substituting Special pages when included into a page
-* (bug 5587) Clean up the languages from references to the Groups special page
-* Added new group-X and group-X-member messages
-* Rewritten removeUnusedAccounts to be more efficient, print names of inactive
- accounts
-* Redirect Special:Userlist to Special:Listusers
-* Introduce $wgAllowTitlesInSVG, which allows the <title> attribute in uploaded files
- bearing the image/svg MIME type. Disabled by default due to the vast majority of
- web servers being hideously misconfigured. See DefaultSettings.php for more details.
-* Changed default LocalSettings.php to append the previous include path when setting it
-* (bug 5837) Use "members" for the value descriptor in Special:Categories,
- Special:Wantedcategories and Special:Mostlinkedcategories.
-* (bug 3309) Allow comments when undeleting pages
-* Clean up Special:Undelete a bit
-* (bug 5805) messages nbytes, ncategories can now use {{plural:}}
-* Clean up Special:Imagelist a bit
-* (bug 5838) Namespace names for Nds-NL
-* (bug 5749) Added Tyvan language files
-* (bug 5791) Fix SQL syntax in Special:BrokenRedirects, was causing incorrect data to show
-* (bug 5839) Prevent access to Special:Confirmemail for logged-out users
-* (bug 5853) Update for Portuguese messages (pt)
-* (bug 5851) Use Cyrillic for Kirghiz language name
-* (bug 5841) Allow the 'EditFilter' hook to return a non-fatal error message
-* (bug 5846) Link to individual group description pages in Special:Listusers
-* (bug 5857) Update for German localisation (de)
-* (bug 5858) Update for Russian language (ru)
-* (bug 5860) Update for Indonesian language (id)
-* (bug 1120) Update for Czech language (Cs)
-* Added many missing formatNum calls
-* Added grammar function to Belarusian (be)
-* (bug 5819) Add 'PersonalUrls' hook
-* (bug 5862) Update of Belarusian language (be)
-* (bug 5886) Update for Portuguese messages (pt)
-* (bug 5586) <gallery> treated text as links
-* (bug 5878) Update for Indonesian language (id)
-* (bug 5697) Update for Malay language (ms)
-* (bug 5890) Update for German language (de)
-* (bug 5889) Name for Sindhi language should appear as سنڌي
-* --force-normal parameter on dump scripts to force check for ICU extension
-* (bug 5895) Update for Dutch language (nl)
-* (bug 5891) Linktrail for Polish language (pl)
-* User::isBureaucrat , User::isDeveloper , User::isSysop deprecated in
- v1.6 now die with a backtrace. They will be removed in v1.8
-* dumpTextPass now skips goes to database for entries that were blank in the
- previous dump, as this may indicate a broken dump.
-* dumpTextPass progress includes percentage of items prefetched
-* dumpTextPass can now use 7zip files for prefetch
-* (bug 5915) Update to Indonesian localisation (id)
-* (bug 5913) Update for German localisation (de)
-* (bug 5905) Plural support for Bosnian localisation (bs)
-* Groups which won't hit the rate limiter now configurable with
- $wgRateLimitsExcludedGroups
-* (bug 5806) {{plural:}} support instead of "twin" MediaWiki messages
-* (bug 5931) Update for Polish language (pl)
-* Ignore the user and user talk namespaces on Special:Wantedpages
-* Introduce NUMBEROFPAGES magic word
-* (bug 5833) Introduce CURRENTVERSION magic word
-* (bug 5370) Allow throttling of password reminder requests with the rate limiter
-* (bug 5683) Respect parser output marked as uncacheable when saving
-* (bug 5918) Links autonumbering now work for all defined protocols
-* (bug 5935) Improvement to German localisation (de)
-* (bug 5937) Register links from gallery captions with the parent parser output
- object so that link tables receive those updates too
-* (bug 5845) Introduce BASEPAGENAME and BASEPAGENAMEE magic words
-* (bug 5941) Use content language when getting the administrator page title for
- Special:Statistics
-* (bug 5949) Update to Indonesian localisation (id)
-* (bug 5862) Update of Belarusian translation (be)
-* (bug 5950) Improvements to French localisation
-* (bug 5805) {{plural:}} support for counters in some special pages
-* (bug 5952) Improvement to German localisation (de)
-* Rename conflicting metadata help message to "metadata_help" (was "metadata")
- and treat it as wiki text
-* Improve preferences input filtering
-* Maintenance script to import multiple files into the wiki
-* (bug 5957) Update for Hebrew language (he)
-* (bug 5962) Update for Italian language (it)
-* (bug 5961) Update for Portuguese localisation (pt)
-* (bug 5849) Remove some hard-coded references to "Wikipedia" in messages
-* (bug 5967) Improvement to German localisation (de)
-* (bug 5962) Update for Italian language (it)
-* Suppress images in galleries which appear on the bad image list (when rendering
- for a wiki page; galleries in special pages and categories are unaffected)
-* Maintenance script to remove orphaned revisions from the database
-* (bug 5991) Update for Russian language (ru)
-* (bug 6001) PAGENAMEE and FULLPAGENAMEE don't work in FULLURL and LOCALURL magic
- words
-* (bug 5958) Switch Uzbek language name to use latin script
-* (bug 839) Add URLENCODE magic word
-* (bug 6004) Update for Polish language (pl)
-* (bug 5971) Improvement to German localisation (de)
-* (bug 4873) Don't overwrite the subtitle navigation when viewing a redirect page
- that isn't current
-* (bug 2203) Namespace updates for Thai
-* Fix breakage in parser test suite which caused incorrect reporting of the failure of
- {{NUMBEROFFILES}}. Now initialises the site_stats table with some dumb data. Updated
- the expected output for {{NUMBEROFARTICLES}} to reflect this.
-* (bug 6009) Use {{ns:project}} in messages where appropriate
-* (bug 6012) Update to Indonesian localisation (id)
-* (bug 6017) Update list of bookstores in German localisation files
-* (bug 5187) Allow programmatically bypassing username validation, for scripts
-* (bug 6025) SpecialImport: wrong message when no file selected
-* (bug 6015) EditPage: add spacing in the boxes "edit is minor" and "watch this"
-* (bug 6018) Userrights: new message when no user specified ('nouserspecified')
-* (bug 2015) Add "\sim" to ~ conversion for HTML rendering
-* (bug 6029) Improvement to German localisation (de)
-* (bug 5015) Update be: magic words
-* (bug 3974) Add parameter for site URL to "passwordremindertext"
-* (bug 6039) Update for Portuguese localisation (pt)
-* (bug 764) Add CREATE TEMPORARY TABLES to default database permissions
-* Big update to Swedish localisation (sv)
-* Use appropriate HTML functions to create the tool links on image pages, so they don't
- look garbled when tidy isn't on
-* (bug 5511) Fix URL-encoding of usernames in links on Special:Ipblocklist
-* (bug 6046) Update to Indonesian localisation (id) #15
-* (bug 5523) $wgNoFollowNsExceptions to allow disabling rel="nofollow" in
- specially-selected namespaces.
-* (bug 6055) Fix for HTML/JS injection bug in variable handler (found by Nick Jenkins)
-* Reordered wiki table handling and __TOC__ extraction in the parser to better
- handle some overlapping tag cases.
-* Only the first __TOC__ is now turned into a TOC
-* (bug 4610) Indicate patrolled status on watchlists and allow users to mark
- changes as patrolled using the diff links there
-* Add 'DiffViewHeader' hook called before diff page output
-* (bug 6051) Improvement to German localisation (de)
-* (bug 6054) Update to Indonesian localisation (id) #16
-* Add {{CURRENTTIMESTAMP}} magic word
-* (bug 6061) Improper escaping in some html forms
-* (bug 6065) Remove underscore when using NAMESPACE and TALKSPACE magics.
-* (bug 6074) Correct squid purging of offsite upload URLs
-* To simplify the lives of extension developers, the logging type arrays
- can now be appended to directly by an extension setup function. It is
- no longer necessary to write four separate functions just to add a
- custom log type.
-* (bug 6057) Count "licenses" as a message (and show it in Special:Allmessages)
-* Added $wgGrammarForms global
-* Fixed hardcoded 'done.' when removing watchlist entries.
-* (bug 5962) Update for Italian language (it)
-* (bug 6086) Remove vestigial attempt to call Article::validate()
-* wfHostname() function for consistent server hostname use in debug messages
-* Send thumbnailing error messages to 'thumbnail' log group
-* wfShellexec() now accepts an optional parameter to receive the exit code
-* Failed, but not zero-length, thumbnail renderings are now removed.
- Should help clean up when rsvg fails in weird ways.
-* (bug 6081) Change description for Turkmen language
-* Increase robustness of parser placeholders; fixes some glitches when
- adjacent to identifier-ish constructs such as URLs.
-* Shut up the parser test whining about files in a temp directory.
-* (bug 6098) Add Aragonese language support (an)
-* (bug 6101) Update for Russian language (ru)
-* Add $wgIgnoreImageErrors to suppress error messages for thumbnail rendering
- problems. If errors are transitory, this should reduce annoying messages
- making it into cached display.
-* (bug 6103) Wrap self-links in a CSS class ("selflink")
-* (bug 6102) For consistency with other markup, normalize all HTML-encoded
- character entities in URLs, not just ampersands. This allows use of eg
- &#61; when making URLs for template parameters.
-* Markup anality: escape </ as <\/ in toolbar javascript for pure correctness
- under HTML-compatible browsers.
-* (bug 5077) Added hook 'BeforePageDisplay' to SkinTemplate::outputPage
-* Replace fatally changed 'uploadnewversion' with 'uploadnewversion-linktext'
-* (bug 472) Syndication feeds for the last few edits of page history
-* Format edit comments in Recent Changes feed
-* Switch incorrectly ordered column headers on Recent Changes feed diffs
-* (bug 6117) Use message for history feed description, add German localization
-* (bug 1017) fixed thumbnails of animated gifs.
-* Add APC as object caching option
-* Update to Albanian localization (sq)
-* (bug 6099) Introduce {{DIRECTIONMARK}} magic word (with {{DIRMARK}} as an alias)
-* Use optimized php5-only microtime()
-* Add possibility to store local message cache as PHP executable script
-* Fix profiling table definition
-* (bug 6040) Run pre-save transform before calculating the diff. when doing a
- "show changes" operation in the editor
-* (bug 4033) Respect $wgStyleDirectory when checking available skins
-* Remove hideous backslashes from MessagesBr.php
-* Fix APC object cache issues, add functionality to installer
-* (bug 6133) Update strip state as we work. This mostly fixes extensions
- used in Cite.php <ref> tags when Tidy is on.
-* (bug 6139) Workaround for transclusion oddities in Vietnamese upload text
-* (bug 6136) Update to Catalan language (ca)
-* Update to Japanese localization (ja)
-* Add /usr/local/bin to the diff3 search paths in the installer
-* (bug 6106) Update to Indonesian localisation (id) #17
-* (bug 6125) Add links to edit old versions to diff views
-* (bug 5127) Auto edit summary when creating/editing redirect page
-* (bug 3926) Introduce {{#language:}} magic word
-* Fix section links from edit comments for [[:Image:Bla.jpg]] in section titles
-* (bug 6126) Allow fallback to customized primary language when user language
- message contains '-'; fixes licenses selector on Commons configuration after
- recent addition of the message to Messages.php
-* (bug 5527) Batch up job queue insertions for, hopefully, better survivability
- of lock contention etc. Duplicates are now removed at pop time instead of
- at insert time.
-* When showing the "blah has been undeleted" page, make sure it's a blue link
-* parserTests.php accepts a --file parameter to run an alternate test sutie
-* parser tests can now test extensions using !!hooks sections
-* Fix oddity with open tag parameters getting stuck on </li>
-* (bug 5384) Fix <!-- comments --> in <ref> extension
-* Nesting of different tag extensions and comments should now work more
- consistently and more safely. A cleaner, one-pass tag strip lets the
- 'outer' tag either take source (<nowiki>-style) or pass it down to
- further parsing (<ref>-style). There should no longer be surprise
- expansion of foreign extensions inside HTML output, or differences
- in behavior based on the order tags are loaded.
-* (bug 885) Pre-save transform no longer silently appends close tags
-* Pre-save transform no longer changes the case of close tags
-* (bug 6164) Fix regression with <gallery> resetting <ref> state
-* Hackaround for IE 7 wrapping bug in MonoBook footer
-* New message sp-newimages-showfrom replaces rclistfrom on special:newimages
-* Improve handling of ;: definition list construct with overlapping or
- nested HTML tags
-* (bug 6171) Fix sanitizing of HTML-elements with an optional closing
- tag. The sanitizer still needs to learn how to make well-formed XML
- in this case.
-* Fix fatal error when specifying illegal name for manual thumbnail
-* (bug 6184) Use shinier Linker::userLink() to make user links in
- Special:Undelete
-* (bug 6170) Update for Kashubian translation (csb)
-* (bug 6191) Update to Indonesian translation (id) #18
-* (bug 6114) Update to Walloon localization (wa)
-* Added $wgNamespaceRobotPolicies to allow customisation of robot policies on a
- per-namespace basis.
-* Add <ol> to the list of block elements for doBlockLevels; avoids <p>s being
- interspersed into your ordered lists.
-* (bug 5021) Transcluding the same special page twice now works
-* Add 'SiteNoticeBefore' and 'SiteNoticeAfter' hooks
-* (bug 6182) Date passed in "sp-newimages-showfrom" not adjusted to user time
- preferences
-* (bug 2587) Fix for section editing with comment prefix
-* (bug 2607) Fix for section editing with mix of wiki and HTML headings
-* (bug 3342) Fix for section editing with headings wrapped in <noinclude>
-* (bug 3476) Fix for section editing with faux headings in extensions
-* (bug 5272) Fix for section editing with HTML-heading subsections
-* Fix for bogus wiki headings improperly detected with following text
-* Fix for HTML headings improperly not detected with preceding/following text
-* Section extraction and replacement functions merged into one implementation
- on the Parser object, so they can't get out of sync with each other.
-* Edit security precautions in raw HTML mode, etc
-* (bug 6197) Update to Indonesian translation (id) #19
-* (bug 6175) Improvement to German translation (de)
-* Redirect Special:Logs to Special:Log
-* (bug 6206) Linktrail for Swedish localization (se)
-* (bug 3202) Attributes now allowed on <pre> tags
-* Sanitizer::validateTagAttributes now available to discard illegal/unsafe
- attribute values from an array.
-* (bug 3837) Leave <center> as is instead of doing an unsafe text replacement
- to <div class="center">. <center> is perfectly valid in the target doctype
- (XHTML 1.0 Transitional), while the replacement didn't catch all cases and
- could even result in invalid output from valid input.
-* (bug 4280) Use 'noindex,nofollow' instead of 'noindex,follow' for default
- meta robots tag on diff view and special pages. Should reduce impact of
- robots on scrolling special pages, diffs etc on sites where robots.txt
- doesn't forbid access.
-* Regression fix: suppress warning about session failure when clicking to
- edit with 'preview on first edit' enabled.
-* (bug 6230) Regression fix: <nowiki> in [URL link text]
-* Added AutoLoader.php, which loads classes without need of require_once()
-* (bug 5981) Add plural function Slovenian (sl)
-* (bug 5945) Introduce {{CONTENTLANGUAGE}} magic word
-* {{PLURAL}} can now take up to five forms
-* (bug 6243) Fix email for usernames containing dots when using PEAR::Mail
-* Remove a number of needless {{ns:project}}-type transforms from messages files. These
- usages already have separate label text. Such transforms are wasteful on each page view.
-* Update to Yiddish localization (yi)
-* (bug 6254) Update to Indonesian translation (id) #20
-* (bug 6255) Fix transclusions starting with "#" or "*" in HTML attributes
-* Whitespace now normalized more or less properly in HTML attributes
-* Fix regression(?) in behavior of initial-whitespace-pre in <center>
-* (bug 6260) Update to Interlingua localization (ia)
-* Update to Vlax Romany localization (rmy)
-* Update to Latin translation (la)
-* Update to Dutch translation (nl)
-* Avoid some notices in page history with bad input
-* Use double quoted consistently on attributes in linker output; preparing
- for new normalization code when tidy not in use
-* Replace "nogomatch" with "noexactmatch" and place the magic colon in the messages
- themselves. Some minor tweaks to the actual message content.
-* Introduce $wgContentNamespaces which allows for articles to exist in namespaces other
- than the main namespace, and still be counted as valid content in the site statistics.
-* (bug 5932) Introduce {{PAGESINNAMESPACE}} magic word
-* Disable $wgAllowExternalImages by default.
-* (bug 2700) Nice things like link completion and signatures now work in <gallery> tags.
-* Cancel output buffering in StreamFile; when used inside gzip buffering this
- could cause funny timeout behavior as the Content-Length was wrong.
-* Return correct content-type header with 304 responses for StreamFile;
- it confuses Safari if you let it return "text/html".
-* (bug 6280) Correct GRAMMAR for Slovenian localisation (sl)
-* (bug 6162) Change date format for Dutch Low Saxon (nds-nl)
-* (bug 6296) Update to Indonesian localisation (id) #21
-* Introduce EditFormPreloadText hook, see docs/hooks.txt for more information
-* (bug 4054) Add "boteditletter" to recent changes flags
-* Update to Catalan localization (ca)
-* (bug 2099) Deleted image files can now be archived and undeleted.
- Set $wgSaveDeletedFiles on and an appropriate directory path in
- $wgFileStore['deleted']['directory']
-* (bug 6324) Fix regression in enhanced RC alignment
-* Introduce {{NUMBEROFADMINS}} magic word
-* Update to Slovak translation (sk)
-* Update to Alemannic localization (gsw)
-* (bug 6300) Bug fixes for sr: variants
-* namespaceDupes.php can now accept an arbitrary prefix, for checking rogue
- interwikis and such. Not yet fully automated.
-* (bug 6344) Add Special:Uncategorizedimages page
-* (bug 6357) Update to Russian translation (ru)
-* Workaround possible bug in Firefox nightlies by properly removing the
- Content-Encoding header instead of sending explicit 'identity' value
- in StreamFile
-* (bug 6304) Show timestamp for current revision in diff pages
-* Vertically align current version with old version header in diff display
-* (bug 6174) Remove redundant "emailforlost" message
-* (bug 6189) Show an error to an unprivilleged user trying to create account
-* (bug 6365) Show user information in the "old revision" navigation links
-* Introduce 'FetchChangesList' hook; see docs/hooks.txt for more information
-* (bug 6345) Update to Indonesian localisation (id) #22
-* (bug 6279) Add genitive month names to Slovenian localisation
-* (bug 6351) Update to German translation (de)
-* Respect language directionality when displaying arrow in Special:Brokenredirects
-* Remove unused "validation" table definitions from the schema files
-* (bug 6398) Work around apparent PCRE bug breaking section editing when
- massively-indented preformatted text immediately followed a header
-* (bug 6392) Fix misbehaving <br /> in preferences form
-* Add translated magic words to Hebrew localization
-* (bug 6396) Change name for Chuvash language
-* Introduce optional (off by default) language selector bar for user login
- and registration. Customisable via the "loginlanguagelinks" message, the
- links will preserve "returnto" values. If the user creates an account while
- using such a link, then the language in use will be saved as their language
- preference.
-* Make sure '~~~' '~~~~' '~~~~~' are removed in Nickname preference.
-* Rename "ipusuccess" to "unblocked", change the format (now wiki text)
-* (bug 2316) Add "caption" attribute to <gallery> tag
-* Allow setting the skin object that ImageGallery will use; needed during parse
- operations (the skin must come from the ParserOptions, not $wgUser)
-* Fix notice in MacBinary detection debug data for files of certain lengths
-* (bug 6131) Add type detection for DjVu files, allowing them to be uploaded
- with validity checking and size detection. No inline thumbnailing yet,
- but could be added in the future.
-* (bug 6423) Don't update newtalk flag if page content didn't change (null edits
- were causing the newtalk flag to trigger inappropriately)
-* Parser functions are now set using magic words.
-* (bug 6428) Incorrect form action URL on Special:Newimages with hidebots = 0 set
-* (bug 4990) Show page source to blocked users on edits, or their modified version
- if blocked during an edit
-* (bug 5903) When requesting the raw source of a non-existent message page,
- return blank content (as opposed to the message key)
-* Improve default blank content of MediaWiki:Common.css and MediaWiki:Monobook.css
-* (bug 6434) Allow customisation of submit button text on Special:Export
-* (bug 6314) Add user tool links on page histories
-* Fix display of file-type icons in galleries when $wgIgnoreImageErrors is off
-* (bug 6438) Update to Indonesian translation (id) #23
-* Adding the language code parameter to the hook "LanguageGetMagic", to allow
- localizble extensions magic words.
-* Update to Romanian translation (ro)
-* Update to Esperanto translation (eo)
-* Check for preg_match() existence when installing and die out whining about PCRE
- if it's not there, instead of throwing a fatal error
-* (bug 672) Add MathAfterTexvc hook
-* Update to Piedmontese localization (pms)
-* dumpBackup can optionally compress via dbzip2
-* (bug 2483) Run link updates on change via XML import
-* (bug 2481) List imported pages during Special:Import
-* (bug 2482) Log and RC entries for Special:Import events
-* Allow fetching all revisions from transwiki Special:Import
-* Allow fetching all revisions from Special:Export GET request
-* Disable output buffering on Special:Export; should help with streaming
- large numbers of history items.
-* Allow setting a maximum number of revisions for history Special:Export;
- pages with more than $wgExportMaxHistory revisions are excluded from
- export when history is requested.
-* Fix transwiki import of pages with space in name
-* Save null edit when importing pages through Special:Import
-* Update to Korean translation (ko)
-* Show a more specific message when an anonymous user tries to access Special:Watchlist
-* (bug 3278) Paging links in Special:Prefixindex
-* Added Latvian localization (lv)
-* (bug 6472) Fix regression in Special:Export with multiple pages
-* Update to Macedonian translation (mk)
-* Allow page moves over historyless self-redirects. Such are usually created
- as part of namespace rearrangements, and it's easier to clean them up if
- we can move over them.
-* Show some error results in moveBatch.php
-* (bug 6479) Allow specification of the skin to use during HTML dumps
-* (bug 6461) Link to page histories in Special:Newpages
-* (bug 6484) Don't do message transformations when preloading messages for editing
-* (bug 6201) Treat spaces as underscores in parameters to {{ns:}}
-* (bug 6006) Allow hiding the password change fields using an authentication plugin
-* (bug 6489) Use appropriate link colour on Special:Shortpages
-* Added formatnum magic word
-* Added Javanese localization (jv)
-* (bug 6491) Apply bad image list in category galleries
-* (bug 6488) Show relevant log fragment in Special:Movepage
-* Fix potential PHP notice in Special:Blockme when $wgBlockOpenProxies is true
-* Use mysql_real_escape_string instead of addslashes for string escaping in
- the MySQL Database class. This may fix some rare breakage with binary fields.
- Note that MediaWiki does not support the multibyte character sets where a
- "dumb" byte replacement can be actively dangerous; UTF-8 is always safe
- in this regard due to the bit patterns which make head and tail bytes
- distinct.
-* (bug 6497) Use $wgMetaNamespaceTalk for Esperanto if set
-* (bug 6498) Use localized forms for image size in Special:Undelete
-* (bug 6485) Update to Indonesian translation (id) #24
-* Extension messages translation is now possible.
-* Add target namespace override selector for transwiki imports.
- $wgImportTargetNamespace specifies the default, to be used for
- Wiktionary's 'Transwiki:' namespace etc.
-* (bug 6506) Update to German localisation (de)
-* (bug 502) Avoid silly tabs on bad title by using virtual special page
-* (bug 6511) Add diff links to old revision navigation bar
-* (bug 6511) Replace 'oldrevisionnavigation' message with 'old-revision-navigation'
-* Fix regression in Polish genitive month forms
-* (bug 4037) Make input handling in Special:Allpages and Special:Prefixindex
- more consistent: Accept just a namespace prefix and a colon, reject input
- with interwiki prefixes, otherwise do what Title::makeTitleSafe() does.
-* (bug 6516) Update to Russian translation
-* New 'allpagesbadtitle' message for Special:Allpages, based on 'badtitletext'.
-* Rename "searchquery" to "searchsubtitle" and support wiki text in it
-* Introduce updateArticleCount maintenance script which uses a better check that
- reflects what Article::isCountable() tests for
-* Introduce 'BadImage' hook; see docs/hooks.txt for more information
-* Add "searchsubtitleinvalid" message for searches that are not valid titles.
-* (bug 5962) Update to Italian localisation
-* (bug 6530) Update to Indonesian localisation (id) #25
-* (bug 6523) Fix SVG issue in rebuildImages.php
-* (bug 6512) Link to page-specific logs on page histories
-* (bug 6504) Allow configuring session name with $wgSessionName
-* (bug 6185) Add standard user tool links to log page views
-* Update to Venetian translation (vec)
-* Update to Slovenian translation (sl)
-* Add standard user tool links to deleted revision list
-* Separate out EditPage's getContent bits from regular Article getContent.
- Cleans up read-only-mode warning on empty pages and neats up some code.
-* (bug 6565) Strict JavaScript writing
-* (bug 6570) Update to Indonesian localisation (id) #26
-* Added Telugu translation (te)
-* Update to Catalan translation (ca)
-* (bug 6560) Avoid PHP notice when trimming ISBN whitespace
-* Added namespace translation to Kannada (ka)
-* (bug 6566) Improve input validation on timestamp conversion
-* Implicit group "emailconfirmed" for all users whose email addresses are confirmed
-* (bug 6577) Avoid multiline parser breakage on <pre> with newline in attribute
-
=== Caveats ===
diff --git a/StartProfiler.php b/StartProfiler.php
new file mode 100644
index 00000000..8fc3ff88
--- /dev/null
+++ b/StartProfiler.php
@@ -0,0 +1,22 @@
+<?php
+
+require_once( dirname(__FILE__).'/includes/ProfilerStub.php' );
+
+/**
+ * To use a profiler, delete the line above and add something like this:
+ *
+ * require_once( dirname(__FILE__).'/includes/Profiler.php' );
+ * $wgProfiler = new Profiler;
+ *
+ * Or for a sampling profiler:
+ * if ( !mt_rand( 0, 100 ) ) {
+ * require_once( dirname(__FILE__).'/includes/Profiler.php' );
+ * $wgProfiler = new Profiler;
+ * } else {
+ * require_once( dirname(__FILE__).'/includes/ProfilerStub.php' );
+ * }
+ *
+ * Configuration of the profiler output can be done in LocalSettings.php
+ */
+
+?>
diff --git a/UPGRADE b/UPGRADE
index 70ccbadf..9862dc80 100644
--- a/UPGRADE
+++ b/UPGRADE
@@ -28,9 +28,9 @@ you take a complete backup of your wiki database and files and verify it. While
the upgrade scripts are somewhat robust, there is no guarantee that things will
not fail, leaving the database in an inconsistent state.
-Refer to the MySQL documentation for information on backing up a database. For
-information on making copies of files, consult the documentation for your
-operating system.
+Refer to the MySQL or Postgres documentation for information on backing up a
+database. For information on making copies of files, consult the documentation
+for your operating system.
=== Perform the file upgrade ===
@@ -73,6 +73,13 @@ procedure, and especially after upgrading; check that page views and edits work
normally and that special pages continue to function, etc. and correct errors
and quirks which reveal themselves.
+== Upgrading from 1.7 wikis ==
+
+$wgDefaultUserOptions now contains all the defaults, not only overrides.
+If you're setting this as a complete array(), you may need to change it
+to set only specific items as recommended in DefaultSettings.php.
+
+
== Upgrading from 1.6 wikis ==
$wgLocalTZoffset was in hours, it is now using minutes.
@@ -175,7 +182,7 @@ LocalSettings.php are not accidentally made public, as this contains
a database password.)
To back up the database, use the tools provided by your service provider
-(if applicable) or the standard mysqldump program.
+(if applicable) or the standard mysqldump or pg_dump programs.
For general help on mysqldump:
http://dev.mysql.com/doc/mysql/en/mysqldump.html
@@ -185,9 +192,19 @@ some cases damage data in your wiki. If necessary, set the charset
option to 'latin1' to avoid the conversion. Fore more info see:
http://mail.wikipedia.org/pipermail/wikitech-l/2004-November/026359.html
+For general help on pg_dump:
+http://www.postgresql.org/docs/current/static/app-pgdump.html
+
== Caveats ==
+=== Postgres ===
+
+Postgres support is new, and much of the upgrade instructions may
+not apply. The schema was changed significantly from 1.7 to 1.8,
+so you will need to at least use the update.php or web installer,
+as described above.
+
=== Upgrading from 1.4.2 or earlier ===
diff --git a/api.php b/api.php
new file mode 100644
index 00000000..63802c50
--- /dev/null
+++ b/api.php
@@ -0,0 +1,108 @@
+<?php
+
+
+/**
+* API for MediaWiki 1.8+
+*
+* Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* 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.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*/
+
+// Initialise common code
+require (dirname(__FILE__) . '/includes/WebStart.php');
+
+/**
+ * When no format parameter is given, this format will be used
+ */
+define('API_DEFAULT_FORMAT', 'xmlfm');
+
+/**
+ * Location of all api-related files (must end with a slash '/')
+ */
+define('API_DIR', 'includes/api/');
+
+/**
+ * List of classes and containing files.
+ */
+$wgApiAutoloadClasses = array (
+
+ 'ApiMain' => API_DIR . 'ApiMain.php',
+
+ // Utility classes
+ 'ApiBase' => API_DIR . 'ApiBase.php',
+ 'ApiQueryBase' => API_DIR . 'ApiQueryBase.php',
+ 'ApiResult' => API_DIR . 'ApiResult.php',
+ 'ApiPageSet' => API_DIR . 'ApiPageSet.php',
+
+ // Formats
+ 'ApiFormatBase' => API_DIR . 'ApiFormatBase.php',
+ 'ApiFormatYaml' => API_DIR . 'ApiFormatYaml.php',
+ 'ApiFormatXml' => API_DIR . 'ApiFormatXml.php',
+ 'ApiFormatJson' => API_DIR . 'ApiFormatJson.php',
+
+ // Modules (action=...) - should match the $apiModules list
+ 'ApiHelp' => API_DIR . 'ApiHelp.php',
+ 'ApiLogin' => API_DIR . 'ApiLogin.php',
+ 'ApiQuery' => API_DIR . 'ApiQuery.php',
+
+ // Query items (meta/prop/list=...)
+ 'ApiQuerySiteinfo' => API_DIR . 'ApiQuerySiteinfo.php',
+ 'ApiQueryInfo' => API_DIR . 'ApiQueryInfo.php',
+ 'ApiQueryRevisions' => API_DIR . 'ApiQueryRevisions.php',
+ 'ApiQueryAllpages' => API_DIR . 'ApiQueryAllpages.php'
+);
+
+/**
+ * List of available modules: action name => module class
+ * The class must also be listed in the $wgApiAutoloadClasses array.
+ */
+$wgApiModules = array (
+ 'help' => 'ApiHelp',
+ 'login' => 'ApiLogin',
+ 'query' => 'ApiQuery'
+);
+
+/**
+ * List of available formats: format name => format class
+ * The class must also be listed in the $wgApiAutoloadClasses array.
+ */
+$wgApiFormats = array (
+ 'json' => 'ApiFormatJson',
+ 'jsonfm' => 'ApiFormatJson',
+ 'xml' => 'ApiFormatXml',
+ 'xmlfm' => 'ApiFormatXml',
+ 'yaml' => 'ApiFormatYaml',
+ 'yamlfm' => 'ApiFormatYaml'
+);
+
+wfProfileIn('api.php');
+
+// Verify that the API has not been disabled
+if (!$wgEnableAPI) {
+ echo 'MediaWiki API is not enabled for this site. Add the following line to your LocalSettings.php';
+ echo '<pre><b>$wgEnableAPI=true;</b></pre>';
+ die(-1);
+}
+
+$wgAutoloadClasses = array_merge($wgAutoloadClasses, $wgApiAutoloadClasses);
+
+$processor = new ApiMain($wgRequestTime, $wgApiModules, $wgApiFormats, $wgEnableWriteAPI);
+$processor->execute();
+
+wfProfileOut('api.php');
+wfLogProfilingData();
+?>
diff --git a/bin/ulimit-tvf.sh b/bin/ulimit-tvf.sh
new file mode 100755
index 00000000..8a1eb81c
--- /dev/null
+++ b/bin/ulimit-tvf.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+ulimit -t $1 -v $2 -f $3
+shift 3
+"$@"
+
diff --git a/config/index.php b/config/index.php
index 8fc91f8e..5443614e 100644
--- a/config/index.php
+++ b/config/index.php
@@ -47,6 +47,7 @@ require_once( "includes/Defines.php" );
require_once( "includes/DefaultSettings.php" );
require_once( "includes/MagicWord.php" );
require_once( "includes/Namespace.php" );
+require_once( "includes/ProfilerStub.php" );
## Databases we support:
@@ -55,11 +56,13 @@ $ourdb['mysql']['fullname'] = 'MySQL';
$ourdb['mysql']['havedriver'] = 0;
$ourdb['mysql']['compile'] = 'mysql';
$ourdb['mysql']['bgcolor'] = '#ffe5a7';
+$ourdb['mysql']['rootuser'] = 'root';
$ourdb['postgres']['fullname'] = 'PostgreSQL';
$ourdb['postgres']['havedriver'] = 0;
$ourdb['postgres']['compile'] = 'pgsql';
$ourdb['postgres']['bgcolor'] = '#aaccff';
+$ourdb['postgres']['rootuser'] = 'postgres';
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -118,7 +121,7 @@ $ourdb['postgres']['bgcolor'] = '#aaccff';
.error-top {
color: red;
background-color: #FFF0F0;
- border: 2px solid red;
+ border: 2px solid red;
font-size: 130%;
font-weight: bold;
padding: 1em 1.5em;
@@ -149,15 +152,22 @@ $ourdb['postgres']['bgcolor'] = '#aaccff';
<!--
function hideall() {
<?php foreach (array_keys($ourdb) as $db) {
- echo "document.getElementById('$db').style.display='none';\n";
+ echo "\n document.getElementById('$db').style.display='none';";
}
?>
+
}
- function togglearea(id) {
+ function toggleDBarea(id,defaultroot) {
hideall();
var dbarea = document.getElementById(id).style;
- dbarea.display = dbarea.display = 'none' ? 'block' : 'none';
- }
+ dbarea.display = (dbarea.display == 'none') ? 'block' : 'none';
+ var db = document.getElementById('RootUser');
+ if (defaultroot) {
+<?php foreach (array_keys($ourdb) as $db) {
+ echo " if (id == '$db') { db.value = '".$ourdb[$db]['rootuser']."';}\n";
+}?>
+ }
+ }
// -->
</script>
</head>
@@ -175,7 +185,7 @@ $ourdb['postgres']['bgcolor'] = '#aaccff';
/* Check for existing configurations and bug out! */
if( file_exists( "../LocalSettings.php" ) ) {
- dieout( " <p><strong>Setup has completed, <a href='../index.php'>your wiki</a> is configured.</strong></p>
+ dieout( "<p><strong>Setup has completed, <a href='../index.php'>your wiki</a> is configured.</strong></p>
<p>Please delete the /config directory for extra security.</p></div></div></div></div>" );
}
@@ -199,10 +209,13 @@ if( !is_writable( "." ) ) {
<pre>
cd <i>/path/to/wiki</i>
chmod a+w config
- </pre>" );
+ </pre>
+
+ <p>Afterwards retry to start the <a href=\"\">setup</a>.</p>" );
}
+require_once( "install-utils.inc" );
require_once( "maintenance/updaters.inc" );
class ConfigData {
@@ -280,7 +293,7 @@ if( ini_get( "register_globals" ) ) {
<li>
<div style="font-size:110%">
<strong class="error">Warning:</strong>
- <strong>PHP's <tt><a href="http://php.net/register_globals">register_globals</a></tt> option is enabled. Disable it if you can.</strong>
+ <strong>PHP's <tt><a href="http://php.net/register_globals">register_globals</a></tt> option is enabled. Disable it if you can.</strong>
</div>
MediaWiki will work, but your server is more exposed to PHP-based security vulnerabilities.
</li>
@@ -407,21 +420,21 @@ if ($conf->apc ) {
$conf->eaccel = function_exists( 'eaccelerator_get' );
if ( $conf->eaccel ) {
- $conf->turck = 'eaccelerator';
- print "<li><a href=\"http://eaccelerator.sourceforge.net/\">eAccelerator</a> installed</li>\n";
+ $conf->turck = 'eaccelerator';
+ print "<li><a href=\"http://eaccelerator.sourceforge.net/\">eAccelerator</a> installed</li>\n";
}
-if (!$conf->turck && !$conf->eaccel && !$conf->apc) {
- print "<li>Neither <a href=\"http://turck-mmcache.sourceforge.net/\">Turck MMCache</a> nor ".
- "<a href=\"http://eaccelerator.sourceforge.net/\">eAccelerator</a> nor ".
- "<a href=\"http://www.php.net/apc\">APC</a> are installed, " .
- "can't use object caching functions</li>\n";
+
+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://www.php.net/apc">APC</a>. Object caching functions cannot be used.</li>' );
}
$conf->diff3 = false;
-$diff3locations = array("/usr/bin", "/usr/local/bin", "/opt/csw/bin", "/usr/gnu/bin", "/usr/sfw/bin") + explode($sep, getenv("PATH"));
-$diff3names = array("gdiff3", "diff3", "diff3.exe");
+$diff3locations = array( "/usr/bin", "/usr/local/bin", "/opt/csw/bin", "/usr/gnu/bin", "/usr/sfw/bin" ) + explode( $sep, getenv( "PATH" ) );
+$diff3names = array( "gdiff3", "diff3", "diff3.exe" );
-$diff3versioninfo = array('$1 --version 2>&1', 'diff3 (GNU diffutils)');
+$diff3versioninfo = array( '$1 --version 2>&1', 'diff3 (GNU diffutils)' );
foreach ($diff3locations as $loc) {
$exe = locate_executable($loc, $diff3names, $diff3versioninfo);
if ($exe !== false) {
@@ -487,12 +500,12 @@ print "<li style='font-weight:bold;color:green;font-size:110%'>Environment check
$conf->SysopName = importPost( "SysopName", "WikiSysop" );
$conf->SysopPass = importPost( "SysopPass" );
$conf->SysopPass2 = importPost( "SysopPass2" );
+ $conf->RootUser = importPost( "RootUser", "root" );
+ $conf->RootPW = importPost( "RootPW", "-" );
## MySQL specific:
$conf->DBprefix = importPost( "DBprefix" );
$conf->DBmysql5 = (importPost( "DBmysql5" ) == "true") ? "true" : "false";
- $conf->RootUser = importPost( "RootUser", "root" );
- $conf->RootPW = importPost( "RootPW", "-" );
$conf->LanguageCode = importPost( "LanguageCode", "en" );
## Postgres specific:
@@ -560,10 +573,10 @@ if ( $conf->Shm == 'memcached' && $conf->MCServers ) {
}
/* default values for installation */
-$conf->Email =importRequest("Email", "email_enabled");
-$conf->Emailuser=importRequest("Emailuser", "emailuser_enabled");
-$conf->Enotif =importRequest("Enotif", "enotif_allpages");
-$conf->Eauthent =importRequest("Eauthent", "eauthent_enabled");
+$conf->Email = importRequest("Email", "email_enabled");
+$conf->Emailuser = importRequest("Emailuser", "emailuser_enabled");
+$conf->Enotif = importRequest("Enotif", "enotif_allpages");
+$conf->Eauthent = importRequest("Eauthent", "eauthent_enabled");
if( $conf->posted && ( 0 == count( $errs ) ) ) {
do { /* So we can 'continue' to end prematurely */
@@ -582,6 +595,11 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
if ($conf->DBtype === $db)
$conf->DBtypename = $ourdb[$db]['fullname'];
}
+ if ( ! strlen($conf->DBtype)) {
+ $errs["DBpicktype"] = "Please choose a database type";
+ continue;
+ }
+
if (! $conf->DBtypename) {
$errs["DBtype"] = "Unknown database type '$conf->DBtype'";
continue;
@@ -601,15 +619,15 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
$wgDBts2schema = $conf->DBts2schema;
$wgCommandLineMode = true;
- $wgUseDatabaseMessages = false; /* FIXME: For database failure */
+ $wgUseDatabaseMessages = false; /* FIXME: For database failure */
require_once( "includes/Setup.php" );
chdir( "config" );
require_once( "maintenance/InitialiseMessages.inc" );
$wgTitle = Title::newFromText( "Installation script" );
-error_reporting( E_ALL );
- print "<li>Loading class: $dbclass";
+ error_reporting( E_ALL );
+ print "<li>Loading class: $dbclass";
$dbc = new $dbclass;
if( $conf->DBtype == 'mysql' ) {
@@ -680,12 +698,26 @@ error_reporting( E_ALL );
if( !$ok ) { continue; }
} else /* not mysql */ {
- echo( "<li>Attempting to connect to database server as $wgDBuser..." );
+ error_reporting( E_ALL );
+ $wgSuperUser = '';
+ ## Possible connect as a superuser
+ if( $conf->RootPW != '-' and strlen($conf->RootPW)) {
+ $wgDBsuperuser = $conf->RootUser;
+ echo( "<li>Attempting to connect to database \"postgres\" as superuser \"$wgDBsuperuser\"..." );
+ $wgDatabase = $dbc->newFromParams($wgDBserver, $wgDBsuperuser, $conf->RootPW, "postgres", 1);
+ if (!$wgDatabase->isOpen()) {
+ print " error: " . $wgDatabase->lastError() . "</li>\n";
+ $errs["DBserver"] = "Could not connect to database as superuser";
+ $errs["RootUser"] = "Check username";
+ $errs["RootPW"] = "and password";
+ continue;
+ }
+ }
+ echo( "<li>Attempting to connect to database \"$wgDBname\" as \"$wgDBuser\"..." );
$wgDatabase = $dbc->newFromParams($wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, 1);
if (!$wgDatabase->isOpen()) {
print " error: " . $wgDatabase->lastError() . "</li>\n";
} else {
- $wgDatabase->ignoreErrors(true);
$myver = $wgDatabase->getServerVersion();
}
}
@@ -698,14 +730,14 @@ error_reporting( E_ALL );
print "<li>Connected to $myver";
if ($conf->DBtype == 'mysql') {
if( version_compare( $myver, "4.0.14" ) < 0 ) {
- die( " -- mysql 4.0.14 or later required. Aborting." );
+ dieout( " -- mysql 4.0.14 or later required. Aborting." );
}
- $mysqlNewAuth = version_compare( $myver, "4.1.0", "ge" );
+ $mysqlNewAuth = version_compare( $myver, "4.1.0", "ge" );
if( $mysqlNewAuth && $mysqlOldClient ) {
print "; <b class='error'>You are using MySQL 4.1 server, but PHP is linked
- to old client libraries; if you have trouble with authentication, see
- <a href='http://dev.mysql.com/doc/mysql/en/old-client.html'
- >http://dev.mysql.com/doc/mysql/en/old-client.html</a> for help.</b>";
+ to old client libraries; if you have trouble with authentication, see
+ <a href='http://dev.mysql.com/doc/mysql/en/old-client.html'
+ >http://dev.mysql.com/doc/mysql/en/old-client.html</a> for help.</b>";
}
if( $wgDBmysql5 ) {
if( $mysqlNewAuth ) {
@@ -739,6 +771,11 @@ error_reporting( E_ALL );
}
$wgDatabase->selectDB( $wgDBname );
}
+ else if ($conf->DBtype == 'postgres') {
+ if( version_compare( $myver, "PostgreSQL 8.0" ) < 0 ) {
+ dieout( " <b>Postgres 8.0 or later is required</b>. Aborting.</li></ul>" );
+ }
+ }
if( $wgDatabase->tableExists( "cur" ) || $wgDatabase->tableExists( "revision" ) ) {
print "<li>There are already MediaWiki tables in this database. Checking if updates are needed...</li>\n";
@@ -758,14 +795,12 @@ error_reporting( E_ALL );
dbsource( "../maintenance/users.sql", $wgDatabase );
}
}
- if ( $conf->DBtype == 'mysql') {
- print "<pre>\n";
- chdir( ".." );
- flush();
- do_all_updates();
- chdir( "config" );
- print "</pre>\n";
- }
+ print "<pre>\n";
+ chdir( ".." );
+ flush();
+ do_all_updates();
+ chdir( "config" );
+ print "</pre>\n";
print "<li>Finished update checks.</li>\n";
} else {
# FIXME: Check for errors
@@ -780,11 +815,7 @@ error_reporting( E_ALL );
}
dbsource( "../maintenance/interwiki.sql", $wgDatabase );
} else if ($conf->DBtype == 'postgres') {
- dbsource( "../maintenance/postgres/tables.sql", $wgDatabase );
- $wgDatabase->update_interwiki();
- } else if ($conf->DBtype == 'oracle') {
- dbsource( "../maintenance/oracle/tables.sql", $wgDatabase );
- dbsource( "../maintenance/oracle/interwiki.sql", $wgDatabase );
+ $wgDatabase->setup_database();
}
else {
$errs["DBtype"] = "Do not know how to handle database type '$conf->DBtype'";
@@ -795,13 +826,13 @@ error_reporting( E_ALL );
print "<li>Initializing data...";
$wgDatabase->insert( 'site_stats',
- array( 'ss_row_id' => 1,
- 'ss_total_views' => 0,
- 'ss_total_edits' => 0,
- 'ss_good_articles' => 0 ) );
-
+ array ( 'ss_row_id' => 1,
+ 'ss_total_views' => 0,
+ 'ss_total_edits' => 0,
+ 'ss_good_articles' => 0 ) );
+
# Set up the "regular user" account *if we can, and if we need to*
- if( $conf->Root ) {
+ if( $conf->Root and $conf->DBtype == 'mysql') {
# See if we need to
$wgDatabase2 = $dbc->newFromParams( $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, 1 );
if( $wgDatabase2->isOpen() ) {
@@ -815,7 +846,7 @@ error_reporting( E_ALL );
echo( "success.</li>" );
}
}
-
+
if( $conf->SysopName ) {
$u = User::newFromName( $conf->getSysopName() );
if ( 0 == $u->idForName() ) {
@@ -848,23 +879,15 @@ error_reporting( E_ALL );
$revid = $revision->insertOn( $wgDatabase );
$article->updateRevisionOn( $wgDatabase, $revision );
- print "<li><pre>";
- initialiseMessages();
- print "</pre></li>\n";
+ initialiseMessages( false, false, 'printListItem' );
}
/* Write out the config file now that all is well */
print "<p>Creating LocalSettings.php...</p>\n\n";
- $localSettings = "<" . "?php$endl$local$endl?" . ">";
+ $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 );
-
- if( version_compare( phpversion(), "4.3.2" ) >= 0 ) {
- $xt = "xt"; # Refuse to overwrite an existing file
- } else {
- $xt = "wt"; # 'x' is not available prior to PHP 4.3.2. We did check above, but race conditions blah blah
- }
- $f = fopen( "LocalSettings.php", $xt );
+ $f = fopen( "LocalSettings.php", 'xt' );
if( $f == false ) {
dieout( "<p>Couldn't write out LocalSettings.php. Check that the directory permissions are correct and that there isn't already a file of that name here...</p>\n" .
@@ -978,7 +1001,7 @@ if( count( $errs ) ) {
<?php aField( $conf, "SysopPass2", "Password confirm:", "password" ) ?>
</div>
<p class="config-desc">
- An admin can lock/delete pages, block users from editing, and other maintenance tasks.<br />
+ An admin can lock/delete pages, block users from editing, and do other maintenance tasks.<br />
A new account will be added only when creating a new wiki database.
</p>
@@ -1050,7 +1073,7 @@ if( count( $errs ) ) {
</ul>
</div>
<div class="config-desc">
- <p>
+ <p>
For this feature to work, an e-mail address must be present for the user account, and the notification
options in the user's preferences must be enabled. Also note the
authentication option below. When testing the feature, keep in mind that your own changes will never trigger notifications to be sent to yourself.</p>
@@ -1077,6 +1100,7 @@ if( count( $errs ) ) {
<div class="config-section">
<div class="config-input">
<label class='column'>Database type:</label>
+<?php if (isset($errs['DBpicktype'])) print "<span class='error'>$errs[DBpicktype]</span>\n"; ?>
<ul class='plain'><?php database_picker($conf) ?></ul>
</div>
@@ -1102,13 +1126,28 @@ if( count( $errs ) ) {
<p class="config-desc">
If you only have a single user account and database available,
enter those here. If you have database root access (see below)
- you can specify new accounts/databases to be created.
- </p>
- <p>
- This account will not be created if it pre-exists. If this is the case, ensure that it
+ 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.
</p>
+ <div class="config-input">
+ <?php
+ aField( $conf, "RootUser", "Superuser account:", "superuser" );
+ ?>
+ </div>
+ <div class="config-input">
+ <?php
+ 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 provide details of a superuser account,
+ such as <strong>root</strong>, which does. Leave the password set to <strong>-</strong> if this is not needed.
+ </p>
+
<?php database_switcher('mysql'); ?>
<div class="config-input"><?php
aField( $conf, "DBprefix", "Database table prefix:" );
@@ -1148,27 +1187,10 @@ if( count( $errs ) ) {
?></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>
+ so it is recommended that you create a new user.</p>
</div>
</div>
- <div class="config-input">
- <?php
- aField( $conf, "RootUser", "Superuser account:", "superuser" );
- ?>
- </div>
- <div class="config-input">
- <?php
- 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 provide details of a superuser account,
- such as <strong>root</strong>, which does. Leave the password set to <strong>-</strong> if this is not needed.
- </p>
-
<div class="config-input" style="padding:2em 0 3em">
<label class='column'>&nbsp;</label>
<input type="submit" value="Install MediaWiki!" class="btn-install" />
@@ -1177,7 +1199,11 @@ if( count( $errs ) ) {
</div>
<script type="text/javascript">
-window.onload = togglearea('<?php echo $conf->DBtype; ?>');
+window.onload = toggleDBarea('<?php echo $conf->DBtype; ?>',
+<?php
+ ## If they passed in a root user name, don't populate it on page load
+ echo strlen(importPost('RootUser', '')) ? 0 : 1;
+?>);
</script>
</form>
@@ -1203,8 +1229,8 @@ which means that anyone on the same server can read your database password! Down
it and uploading it again will hopefully change the ownership to a user ID specific to you.</p>
EOT;
} else {
- echo "<p>Installation successful! Move the config/LocalSettings.php file into the parent directory, then follow
- <a href='../index.php'>this link</a> to your wiki.</p>\n";
+ echo "<p><span style='font-weight:bold;color:green;font-size:110%'>Installation successful!</span> Move the <tt>config/LocalSettings.php</tt> file into the parent directory, then follow
+ <strong><a href='../index.php'>this link</a></strong> to your wiki.</p>\n";
}
}
@@ -1328,7 +1354,7 @@ if ( \$wgCommandLineMode ) {
\$wgSitename = \"{$slconf['Sitename']}\";
-\$wgScriptPath = \"{$slconf['ScriptPath']}\";
+\$wgScriptPath = \"{$slconf['ScriptPath']}\";
\$wgScript = \"\$wgScriptPath/index.php\";
\$wgRedirectScript = \"\$wgScriptPath/redirect.php\";
@@ -1345,11 +1371,11 @@ if ( \$wgCommandLineMode ) {
\$wgUploadPath = \"\$wgScriptPath/images\";
\$wgUploadDirectory = \"\$IP/images\";
-\$wgEnableEmail = $enableemail;
-\$wgEnableUserEmail = $enableuseremail;
+\$wgEnableEmail = $enableemail;
+\$wgEnableUserEmail = $enableuseremail;
\$wgEmergencyContact = \"{$slconf['EmergencyContact']}\";
-\$wgPasswordSender = \"{$slconf['PasswordSender']}\";
+\$wgPasswordSender = \"{$slconf['PasswordSender']}\";
## For a detailed description of the following switches see
## http://meta.wikimedia.org/Enotif and http://meta.wikimedia.org/Eauthent
@@ -1360,13 +1386,17 @@ if ( \$wgCommandLineMode ) {
\$wgEnotifWatchlist = $enotifwatchlist; # UPO
\$wgEmailAuthentication = $eauthent;
+\$wgDBtype = \"{$slconf['DBtype']}\";
\$wgDBserver = \"{$slconf['DBserver']}\";
\$wgDBname = \"{$slconf['DBname']}\";
\$wgDBuser = \"{$slconf['DBuser']}\";
\$wgDBpassword = \"{$slconf['DBpassword']}\";
-\$wgDBprefix = \"{$slconf['DBprefix']}\";
-\$wgDBtype = \"{$slconf['DBtype']}\";
\$wgDBport = \"{$slconf['DBport']}\";
+\$wgDBprefix = \"{$slconf['DBprefix']}\";
+
+# Schemas for Postgres
+\$wgDBmwschema = \"{$slconf['DBmwschema']}\";
+\$wgDBts2schema = \"{$slconf['DBts2schema']}\";
# Experimental charset support for MySQL 4.1/5.0.
\$wgDBmysql5 = {$conf->DBmysql5};
@@ -1377,8 +1407,8 @@ if ( \$wgCommandLineMode ) {
## To enable image uploads, make sure the 'images' directory
## is writable, then set this to true:
-\$wgEnableUploads = false;
-\$wgUseImageResize = {$conf->UseImageResize};
+\$wgEnableUploads = false;
+\$wgUseImageResize = {$conf->UseImageResize};
{$magic}\$wgUseImageMagick = true;
{$magic}\$wgImageMagickConvertCommand = \"{$convert}\";
@@ -1390,7 +1420,7 @@ if ( \$wgCommandLineMode ) {
## If you have the appropriate support software installed
## you can enable inline LaTeX equations:
-\$wgUseTeX = false;
+\$wgUseTeX = false;
\$wgMathPath = \"{\$wgUploadPath}/math\";
\$wgMathDirectory = \"{\$wgUploadDirectory}/math\";
\$wgTmpDirectory = \"{\$wgUploadDirectory}/tmp\";
@@ -1421,7 +1451,8 @@ if ( \$wgCommandLineMode ) {
# sure that cached pages are cleared.
\$configdate = gmdate( 'YmdHis', @filemtime( __FILE__ ) );
\$wgCacheEpoch = max( \$wgCacheEpoch, \$configdate );
-";
+ "; ## End of setting the $localsettings string
+
// Keep things in Unix line endings internally;
// the system will write out as local text type.
return str_replace( "\r\n", "\n", $localsettings );
@@ -1480,7 +1511,7 @@ function aField( &$conf, $field, $text, $type = "text", $value = "", $onclick =
}
echo "\t\t<input $xtype name=\"$field\" id=\"$id\" class=\"iput-$type\" $checked ";
if ($onclick) {
- echo " onclick='togglearea(\"$value\")' " ;
+ echo " onclick='toggleDBarea(\"$value\",1)' " ;
}
echo "value=\"";
if( $type == "radio" ) {
@@ -1507,13 +1538,13 @@ function getLanguageList() {
$codes = array();
- $d = opendir( "../languages" );
+ $d = opendir( "../languages/messages" );
/* In case we are called from the root directory */
if (!$d)
- $d = opendir( "languages");
+ $d = opendir( "languages/messages");
while( false !== ($f = readdir( $d ) ) ) {
$m = array();
- if( preg_match( '/Language([A-Z][a-z_]+)\.php$/', $f, $m ) ) {
+ if( preg_match( '/Messages([A-Z][a-z_]+)\.php$/', $f, $m ) ) {
$code = str_replace( '_', '-', strtolower( $m[1] ) );
if( isset( $wgLanguageNames[$code] ) ) {
$name = $code . ' - ' . $wgLanguageNames[$code];
@@ -1558,7 +1589,7 @@ function testMemcachedServer( $server ) {
if ( !function_exists( 'fsockopen' ) ) {
$errstr = "Can't connect to memcached, fsockopen() not present";
}
- if ( !$errstr && count( $hostport ) != 2 ) {
+ if ( !$errstr && count( $hostport ) != 2 ) {
$errstr = 'Please specify host and port';
var_dump( $hostport );
}
@@ -1616,6 +1647,10 @@ function database_switcher($db) {
print "<h3>$full specific options:</h3>\n";
}
+function printListItem( $item ) {
+ print "<li>$item</li>";
+}
+
?>
<div class="license">
diff --git a/img_auth.php b/img_auth.php
index fb58ba28..cfe005e9 100644
--- a/img_auth.php
+++ b/img_auth.php
@@ -7,18 +7,9 @@
* 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.
*/
-# Valid web server entry point, enable includes
-define( 'MEDIAWIKI', true );
-
-if ( isset( $_REQUEST['GLOBALS'] ) ) {
- echo '<a href="http://www.hardened-php.net/index.76.html">$GLOBALS overwrite vulnerability</a>';
- die( -1 );
-}
-
-require_once( 'includes/Defines.php' );
-require_once( './LocalSettings.php' );
-require_once( 'includes/Setup.php' );
-require_once( 'includes/StreamFile.php' );
+require_once( './includes/WebStart.php' );
+wfProfileIn( 'img_auth.php' );
+require_once( './includes/StreamFile.php' );
if( !isset( $_SERVER['PATH_INFO'] ) ) {
wfForbidden();
@@ -27,7 +18,7 @@ if( !isset( $_SERVER['PATH_INFO'] ) ) {
# Get filenames/directories
$filename = realpath( $wgUploadDirectory . $_SERVER['PATH_INFO'] );
$realUploadDirectory = realpath( $wgUploadDirectory );
-$imageName = $wgLang->getNsText( NS_IMAGE ) . ":" . basename( $_SERVER['PATH_INFO'] );
+$imageName = $wgContLang->getNsText( NS_IMAGE ) . ":" . wfBaseName( $_SERVER['PATH_INFO'] );
# Check if the filename is in the correct directory
if ( substr( $filename, 0, strlen( $realUploadDirectory ) ) != $realUploadDirectory ) {
@@ -47,6 +38,7 @@ if( is_dir( $filename ) ) {
# Write file
wfStreamFile( $filename );
+wfLogProfilingData();
function wfForbidden() {
header( 'HTTP/1.0 403 Forbidden' );
@@ -55,6 +47,7 @@ function wfForbidden() {
<h1>Access denied</h1>
<p>You need to log in to access files on this server</p>
</body></html>";
+ wfLogProfilingData();
exit;
}
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index 2084c366..618c2736 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -1,36 +1,22 @@
<?php
-//$wgRequestTime = microtime();
-
-// unset( $IP );
-// @ini_set( 'allow_url_fopen', 0 ); # For security...
-
-# Valid web server entry point, enable includes.
-# Please don't move this line to includes/Defines.php. This line essentially defines
-# a valid entry point. If you put it in includes/Defines.php, then any script that includes
-# it becomes an entry point, thereby defeating its purpose.
-// define( 'MEDIAWIKI', true );
-// require_once( './includes/Defines.php' );
-// require_once( './LocalSettings.php' );
-// require_once( 'includes/Setup.php' );
-require_once( 'AjaxFunctions.php' );
+if( !defined( 'MEDIAWIKI' ) )
+ die( 1 );
if ( ! $wgUseAjax ) {
die( 1 );
}
+require_once( 'AjaxFunctions.php' );
+
class AjaxDispatcher {
var $mode;
var $func_name;
var $args;
function AjaxDispatcher() {
- global $wgAjaxCachePolicy;
-
wfProfileIn( 'AjaxDispatcher::AjaxDispatcher' );
- $wgAjaxCachePolicy = new AjaxCachePolicy();
-
$this->mode = "";
if (! empty($_GET["rs"])) {
@@ -60,23 +46,45 @@ class AjaxDispatcher {
}
function performAction() {
- global $wgAjaxCachePolicy, $wgAjaxExportList;
+ global $wgAjaxExportList, $wgOut;
+
if ( empty( $this->mode ) ) {
return;
}
wfProfileIn( 'AjaxDispatcher::performAction' );
if (! in_array( $this->func_name, $wgAjaxExportList ) ) {
- echo "-:{$this->func_name} not callable";
+ header( 'Status: 400 Bad Request', true, 400 );
+ echo "unknown function {$this->func_name}";
} else {
- echo "+:";
- $result = call_user_func_array($this->func_name, $this->args);
- header( 'Content-Type: text/html; charset=utf-8', true );
- $wgAjaxCachePolicy->writeHeader();
- echo $result;
+ try {
+ $result = call_user_func_array($this->func_name, $this->args);
+
+ if ( $result === false || $result === NULL ) {
+ header( 'Status: 500 Internal Error', true, 500 );
+ echo "{$this->func_name} returned no data";
+ }
+ else {
+ if ( is_string( $result ) ) {
+ $result= new AjaxResponse( $result );
+ }
+
+ $result->sendHeaders();
+ $result->printText();
+ }
+
+ } catch (Exception $e) {
+ if (!headers_sent()) {
+ header( 'Status: 500 Internal Error', true, 500 );
+ print $e->getMessage();
+ } else {
+ print $e->getMessage();
+ }
+ }
}
+
wfProfileOut( 'AjaxDispatcher::performAction' );
- exit;
+ $wgOut = null;
}
}
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index 4387a607..9f7a332f 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -3,8 +3,6 @@
if( !defined( 'MEDIAWIKI' ) )
die( 1 );
-require_once('WebRequest.php');
-
/**
* Function converts an Javascript escaped string back into a string with
* specified charset (default is UTF-8).
@@ -70,35 +68,8 @@ function code2utf($num){
return '';
}
-class AjaxCachePolicy {
- var $policy;
-
- function AjaxCachePolicy( $policy = null ) {
- $this->policy = $policy;
- }
-
- function setPolicy( $policy ) {
- $this->policy = $policy;
- }
-
- function writeHeader() {
- header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
- if ( is_null( $this->policy ) ) {
- // Bust cache in the head
- header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
- // always modified
- header ("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
- header ("Pragma: no-cache"); // HTTP/1.0
- } else {
- header ("Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->policy ) . " GMT");
- header ("Cache-Control: s-max-age={$this->policy},public,max-age={$this->policy}");
- }
- }
-}
-
-
function wfSajaxSearch( $term ) {
- global $wgContLang, $wgAjaxCachePolicy, $wgOut;
+ global $wgContLang, $wgOut;
$limit = 16;
$l = new Linker;
@@ -110,8 +81,6 @@ function wfSajaxSearch( $term ) {
if ( strlen( str_replace( '_', '', $term ) )<3 )
return;
- $wgAjaxCachePolicy->setPolicy( 30*60 );
-
$db =& wfGetDB( DB_SLAVE );
$res = $db->select( 'page', 'page_title',
array( 'page_namespace' => 0,
@@ -137,10 +106,10 @@ function wfSajaxSearch( $term ) {
}
$subtitlemsg = ( Title::newFromText($term) ? 'searchsubtitle' : 'searchsubtitleinvalid' );
- $subtitle = $wgOut->parse( wfMsg( $subtitlemsg, wfEscapeWikiText($term) ) );
+ $subtitle = $wgOut->parse( wfMsg( $subtitlemsg, wfEscapeWikiText($term) ) ); #FIXME: parser is missing mTitle !
$term = htmlspecialchars( $term );
- return '<div style="float:right; border:solid 1px black;background:gainsboro;padding:2px;"><a onclick="Searching_Hide_Results();">'
+ $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>'
@@ -152,6 +121,12 @@ 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;
}
?>
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
new file mode 100644
index 00000000..40f50876
--- /dev/null
+++ b/includes/AjaxResponse.php
@@ -0,0 +1,203 @@
+<?php
+
+if( !defined( 'MEDIAWIKI' ) )
+ die( 1 );
+
+class AjaxResponse {
+ var $mCacheDuration;
+ var $mVary;
+
+ var $mDisabled;
+ var $mText;
+ var $mResponseCode;
+ var $mLastModified;
+ var $mContentType;
+
+ function AjaxResponse( $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 );
+ }
+ }
+
+ function setCacheDuration( $duration ) {
+ $this->mCacheDuration = $duration;
+ }
+
+ function setVary( $vary ) {
+ $this->mVary = $vary;
+ }
+
+ 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;
+ }
+ }
+
+ function printText() {
+ if ( ! $this->mDisabled ) {
+ print $this->mText;
+ }
+ }
+
+ function sendHeaders() {
+ global $wgUseSquid, $wgUseESI, $wgSquidMaxage;
+
+ 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
+ * any future call to AjaxResponse::printText() have no effect. The method
+ * returns true iff the response code was set to 304 Not Modified.
+ */
+ function checkLastModified ( $timestamp ) {
+ global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest;
+ $fname = 'AjaxResponse::checkLastModified';
+
+ if ( !$timestamp || $timestamp == '19700101000000' ) {
+ wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP\n" );
+ return;
+ }
+ if( !$wgCachePages ) {
+ wfDebug( "$fname: CACHE DISABLED\n", false );
+ return;
+ }
+ if( $wgUser->getOption( 'nocache' ) ) {
+ wfDebug( "$fname: USER DISABLED CACHE\n", false );
+ return;
+ }
+
+ $timestamp = wfTimestamp( TS_MW, $timestamp );
+ $lastmod = wfTimestamp( TS_RFC2822, max( $timestamp, $wgUser->mTouched, $wgCacheEpoch ) );
+
+ if( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
+ # IE sends sizes after the date like this:
+ # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
+ # this breaks strtotime().
+ $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
+ $modsinceTime = strtotime( $modsince );
+ $ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 );
+ wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", false );
+ wfDebug( "$fname: -- we might send Last-Modified : $lastmod\n", false );
+ if( ($ismodsince >= $timestamp ) && $wgUser->validateCache( $ismodsince ) && $ismodsince >= $wgCacheEpoch ) {
+ $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 );
+ $this->mLastModified = $lastmod;
+ }
+ } else {
+ wfDebug( "$fname: client did not send If-Modified-Since header\n", false );
+ $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
+ if ( $touched <= $mcvalue['timestamp'] ) {
+ wfDebug( "Got $mckey from cache\n" );
+ $this->mText = $mcvalue['value'];
+ return true;
+ } else {
+ wfDebug( "$mckey has expired\n" );
+ }
+ }
+
+ return false;
+ }
+
+ function storeInMemcached( $mckey, $expiry = 86400 ) {
+ global $wgMemc;
+
+ $wgMemc->set( $mckey,
+ array(
+ 'timestamp' => wfTimestampNow(),
+ 'value' => $this->mText
+ ), $expiry
+ );
+
+ return true;
+ }
+}
+?>
diff --git a/includes/Article.php b/includes/Article.php
index b1e1f620..8c07b06c 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -5,11 +5,6 @@
*/
/**
- * Need the CacheManager to be loaded
- */
-require_once( 'CacheManager.php' );
-
-/**
* Class representing a MediaWiki article and history.
*
* See design.txt for an overview.
@@ -651,15 +646,16 @@ class Article {
# diff page instead of the article.
if ( !is_null( $diff ) ) {
- require_once( 'DifferenceEngine.php' );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
$de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid );
// DifferenceEngine directly fetched the revision:
$this->mRevIdFetched = $de->mNewid;
$de->showDiffPage();
-
- if( $diff == 0 ) {
+
+ // Needed to get the page's current revision
+ $this->loadPageData();
+ if( $diff == 0 || $diff == $this->mLatest ) {
# Run view updates for current revision only
$this->viewUpdates();
}
@@ -719,6 +715,7 @@ class Article {
$outputDone = false;
if ( $pcache ) {
if ( $wgOut->tryParserCache( $this, $wgUser ) ) {
+ wfRunHooks( 'ArticleViewHeader', array( &$this ) );
$outputDone = true;
}
}
@@ -804,13 +801,13 @@ class Article {
# Display content, don't attempt to save to parser cache
# Don't show section-edit links on old revisions... this way lies madness.
if( !$this->isCurrent() ) {
- $oldEditSectionSetting = $wgOut->mParserOptions->setEditSection( false );
+ $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
}
# Display content and don't save to parser cache
$wgOut->addPrimaryWikiText( $text, $this, false );
if( !$this->isCurrent() ) {
- $wgOut->mParserOptions->setEditSection( $oldEditSectionSetting );
+ $wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
}
}
}
@@ -886,7 +883,7 @@ class Article {
}
if ((!$wgUser->isAllowed('delete'))) {
- $wgOut->sysopRequired();
+ $wgOut->permissionRequired( 'delete' );
return;
}
@@ -1216,7 +1213,7 @@ class Article {
# Silently ignore EDIT_MINOR if not allowed
$isminor = ( $flags & EDIT_MINOR ) && $wgUser->isAllowed('minoredit');
- $bot = $wgUser->isBot() || ( $flags & EDIT_FORCE_BOT );
+ $bot = $wgUser->isAllowed( 'bot' ) || ( $flags & EDIT_FORCE_BOT );
$text = $this->preSaveTransform( $text );
@@ -1447,7 +1444,7 @@ class Article {
$wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
- $link = $this->mTitle->getPrefixedText();
+ $link = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
$text = wfMsg( 'addedwatchtext', $link );
$wgOut->addWikiText( $text );
}
@@ -1467,7 +1464,6 @@ class Article {
if (wfRunHooks('WatchArticle', array(&$wgUser, &$this))) {
$wgUser->addWatch( $this->mTitle );
- $wgUser->saveSettings();
return wfRunHooks('WatchArticleComplete', array(&$wgUser, &$this));
}
@@ -1495,7 +1491,7 @@ class Article {
$wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
- $link = $this->mTitle->getPrefixedText();
+ $link = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
$text = wfMsg( 'removedwatchtext', $link );
$wgOut->addWikiText( $text );
}
@@ -1515,7 +1511,6 @@ class Article {
if (wfRunHooks('UnwatchArticle', array(&$wgUser, &$this))) {
$wgUser->removeWatch( $this->mTitle );
- $wgUser->saveSettings();
return wfRunHooks('UnwatchArticleComplete', array(&$wgUser, &$this));
}
@@ -1527,7 +1522,6 @@ class Article {
* action=protect handler
*/
function protect() {
- require_once 'ProtectionForm.php';
$form = new ProtectionForm( $this );
$form->show();
}
@@ -1641,7 +1635,7 @@ class Article {
# Check permissions
if( $wgUser->isAllowed( 'delete' ) ) {
- if( $wgUser->isBlocked() ) {
+ if( $wgUser->isBlocked( !$confirm ) ) {
$wgOut->blockedPage();
return;
}
@@ -1842,7 +1836,7 @@ class Article {
if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
if ( $this->doDeleteArticle( $reason ) ) {
- $deleted = $this->mTitle->getPrefixedText();
+ $deleted = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -1910,29 +1904,35 @@ class Article {
);
# Now that it's safely backed up, delete it
- $dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
- if ($wgUseTrackbacks)
- $dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
+ # If using cascading deletes, we can skip some explicit deletes
+ if ( !$dbw->cascadingDeletes() ) {
+
+ $dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
+
+ if ($wgUseTrackbacks)
+ $dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
- # Clean up recentchanges entries...
- $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__ );
+ # Delete outgoing links
+ $dbw->delete( 'pagelinks', array( 'pl_from' => $id ) );
+ $dbw->delete( 'imagelinks', array( 'il_from' => $id ) );
+ $dbw->delete( 'categorylinks', array( 'cl_from' => $id ) );
+ $dbw->delete( 'templatelinks', array( 'tl_from' => $id ) );
+ $dbw->delete( 'externallinks', array( 'el_from' => $id ) );
+ $dbw->delete( 'langlinks', array( 'll_from' => $id ) );
+ }
+
+ # If using cleanup triggers, we can skip some manual deletes
+ if ( !$dbw->cleanupTriggers() ) {
- # Finally, clean up the link tables
- $t = $this->mTitle->getPrefixedDBkey();
+ # Clean up recentchanges entries...
+ $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__ );
+ }
# Clear caches
Article::onArticleDelete( $this->mTitle );
- # Delete outgoing links
- $dbw->delete( 'pagelinks', array( 'pl_from' => $id ) );
- $dbw->delete( 'imagelinks', array( 'il_from' => $id ) );
- $dbw->delete( 'categorylinks', array( 'cl_from' => $id ) );
- $dbw->delete( 'templatelinks', array( 'tl_from' => $id ) );
- $dbw->delete( 'externallinks', array( 'el_from' => $id ) );
- $dbw->delete( 'langlinks', array( 'll_from' => $id ) );
-
# Log the deletion
$log = new LogPage( 'delete' );
$log->addEntry( 'delete', $this->mTitle, $reason );
@@ -2141,7 +2141,7 @@ class Article {
# If this is another user's talk page, update newtalk
# Don't do this if $changed = false otherwise some idiot can null-edit a
# load of user talk pages and piss people off
- if( $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getName() && $changed ) {
+ if( $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getTitleKey() && $changed ) {
if (wfRunHooks('ArticleEditUpdateNewTalk', array(&$this)) ) {
$other = User::newFromName( $shortTitle );
if( is_null( $other ) && User::isIP( $shortTitle ) ) {
@@ -2161,6 +2161,22 @@ 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
+ * other shitty functions like editUpdates and such so it's not needed
+ * anymore.
+ */
+ function createUpdates( $rev ) {
+ $this->mGoodAdjustment = $this->isCountable( $rev->getText() );
+ $this->mTotalAdjustment = 1;
+ $this->editUpdates( $rev->getText(), $rev->getComment(),
+ $rev->isMinor(), wfTimestamp(), $rev->getId(), true );
+ }
/**
* Generate the navigation links when browsing through an article revisions
@@ -2174,6 +2190,10 @@ class Article {
function setOldSubtitle( $oldid=0 ) {
global $wgLang, $wgOut, $wgUser;
+ if ( !wfRunHooks( 'DisplayOldSubtitle', array(&$this, &$oldid) ) ) {
+ return;
+ }
+
$revision = Revision::newFromId( $oldid );
$current = ( $oldid == $this->mLatest );
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 7d09d5b6..810a448e 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -10,6 +10,7 @@ function __autoload($className) {
static $localClasses = array(
'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
'AjaxCachePolicy' => 'includes/AjaxFunctions.php',
+ 'AjaxResponse' => 'includes/AjaxResponse.php',
'Article' => 'includes/Article.php',
'AuthPlugin' => 'includes/AuthPlugin.php',
'BagOStuff' => 'includes/BagOStuff.php',
@@ -19,9 +20,11 @@ function __autoload($className) {
'TurckBagOStuff' => 'includes/BagOStuff.php',
'APCBagOStuff' => 'includes/BagOStuff.php',
'eAccelBagOStuff' => 'includes/BagOStuff.php',
+ 'DBABagOStuff' => 'includes/BagOStuff.php',
'Block' => 'includes/Block.php',
'CacheManager' => 'includes/CacheManager.php',
'CategoryPage' => 'includes/CategoryPage.php',
+ 'CategoryViewer' => 'includes/CategoryPage.php',
'Categoryfinder' => 'includes/Categoryfinder.php',
'RCCacheEntry' => 'includes/ChangesList.php',
'ChangesList' => 'includes/ChangesList.php',
@@ -89,6 +92,7 @@ function __autoload($className) {
'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
'Http' => 'includes/HttpFunctions.php',
'Image' => 'includes/Image.php',
+ 'IP' => 'includes/IP.php',
'ThumbnailImage' => 'includes/Image.php',
'ImageGallery' => 'includes/ImageGallery.php',
'ImagePage' => 'includes/ImagePage.php',
@@ -113,16 +117,16 @@ function __autoload($className) {
'FakeMemCachedClient' => 'includes/ObjectCache.php',
'OutputPage' => 'includes/OutputPage.php',
'PageHistory' => 'includes/PageHistory.php',
+ 'IndexPager' => 'includes/Pager.php',
+ 'ReverseChronologicalPager' => 'includes/Pager.php',
+ 'TablePager' => 'includes/Pager.php',
'Parser' => 'includes/Parser.php',
'ParserOutput' => 'includes/Parser.php',
'ParserOptions' => 'includes/Parser.php',
'ParserCache' => 'includes/ParserCache.php',
- 'element' => 'includes/ParserXML.php',
- 'xml2php' => 'includes/ParserXML.php',
- 'ParserXML' => 'includes/ParserXML.php',
'ProfilerSimple' => 'includes/ProfilerSimple.php',
'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
- 'Profiler' => 'includes/Profiling.php',
+ 'Profiler' => 'includes/Profiler.php',
'ProxyTools' => 'includes/ProxyTools.php',
'ProtectionForm' => 'includes/ProtectionForm.php',
'QueryPage' => 'includes/QueryPage.php',
@@ -213,6 +217,7 @@ function __autoload($className) {
'EmailNotification' => 'includes/UserMailer.php',
'WatchedItem' => 'includes/WatchedItem.php',
'WebRequest' => 'includes/WebRequest.php',
+ 'WebResponse' => 'includes/WebResponse.php',
'FauxRequest' => 'includes/WebRequest.php',
'MediaWiki' => 'includes/Wiki.php',
'WikiError' => 'includes/WikiError.php',
@@ -221,7 +226,10 @@ function __autoload($className) {
'Xml' => 'includes/Xml.php',
'ZhClient' => 'includes/ZhClient.php',
'memcached' => 'includes/memcached-client.php',
- 'UtfNormal' => 'includes/normal/UtfNormal.php'
+ 'UtfNormal' => 'includes/normal/UtfNormal.php',
+ 'UsercreateTemplate' => 'includes/templates/Userlogin.php',
+ 'UserloginTemplate' => 'includes/templates/Userlogin.php',
+ 'Language' => 'languages/Language.php',
);
if ( isset( $localClasses[$className] ) ) {
$filename = $localClasses[$className];
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index 182756ab..1dc93a2f 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -146,6 +146,17 @@ class BagOStuff {
if($this->debugmode)
wfDebug("BagOStuff debug: $text\n");
}
+
+ /**
+ * Convert an optionally relative time to an absolute time
+ */
+ static function convertExpiry( $exptime ) {
+ if(($exptime != 0) && ($exptime < 3600*24*30)) {
+ return time() + $exptime;
+ } else {
+ return $exptime;
+ }
+ }
}
@@ -183,9 +194,7 @@ class HashBagOStuff extends BagOStuff {
}
function set($key,$value,$exptime=0) {
- if(($exptime != 0) && ($exptime < 3600*24*30))
- $exptime = time() + $exptime;
- $this->bag[$key] = array( $value, $exptime );
+ $this->bag[$key] = array( $value, BagOStuff::convertExpiry( $exptime ) );
}
function delete($key,$time=0) {
@@ -491,7 +500,7 @@ class APCBagOStuff extends BagOStuff {
return true;
}
- function delete($key) {
+ function delete($key, $time=0) {
apc_delete($key);
return true;
}
@@ -535,4 +544,136 @@ class eAccelBagOStuff extends BagOStuff {
return true;
}
}
+
+class DBABagOStuff extends BagOStuff {
+ var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
+
+ function __construct( $handler = 'db3', $dir = false ) {
+ if ( $dir === false ) {
+ global $wgTmpDirectory;
+ $dir = $wgTmpDirectory;
+ }
+ $this->mFile = "$dir/mw-cache-" . wfWikiID();
+ $this->mFile .= '.db';
+ $this->mHandler = $handler;
+ }
+
+ /**
+ * Encode value and expiry for storage
+ */
+ function encode( $value, $expiry ) {
+ # Convert to absolute time
+ $expiry = BagOStuff::convertExpiry( $expiry );
+ return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value );
+ }
+
+ /**
+ * @return list containing value first and expiry second
+ */
+ function decode( $blob ) {
+ if ( !is_string( $blob ) ) {
+ return array( null, 0 );
+ } else {
+ return array(
+ unserialize( substr( $blob, 11 ) ),
+ intval( substr( $blob, 0, 10 ) )
+ );
+ }
+ }
+
+ function getReader() {
+ if ( file_exists( $this->mFile ) ) {
+ $handle = dba_open( $this->mFile, 'rl', $this->mHandler );
+ } else {
+ $handle = $this->getWriter();
+ }
+ if ( !$handle ) {
+ wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
+ }
+ return $handle;
+ }
+
+ function getWriter() {
+ $handle = dba_open( $this->mFile, 'cl', $this->mHandler );
+ if ( !$handle ) {
+ wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
+ }
+ return $handle;
+ }
+
+ function get( $key ) {
+ wfProfileIn( __METHOD__ );
+ wfDebug( __METHOD__."($key)\n" );
+ $handle = $this->getReader();
+ if ( !$handle ) {
+ return null;
+ }
+ $val = dba_fetch( $key, $handle );
+ list( $val, $expiry ) = $this->decode( $val );
+ # Must close ASAP because locks are held
+ dba_close( $handle );
+
+ if ( !is_null( $val ) && $expiry && $expiry < time() ) {
+ # Key is expired, delete it
+ $handle = $this->getWriter();
+ dba_delete( $key, $handle );
+ dba_close( $handle );
+ wfDebug( __METHOD__.": $key expired\n" );
+ $val = null;
+ }
+ wfProfileOut( __METHOD__ );
+ return $val;
+ }
+
+ function set( $key, $value, $exptime=0 ) {
+ wfProfileIn( __METHOD__ );
+ wfDebug( __METHOD__."($key)\n" );
+ $blob = $this->encode( $value, $exptime );
+ $handle = $this->getWriter();
+ if ( !$handle ) {
+ return false;
+ }
+ $ret = dba_replace( $key, $blob, $handle );
+ dba_close( $handle );
+ wfProfileOut( __METHOD__ );
+ return $ret;
+ }
+
+ function delete( $key, $time = 0 ) {
+ wfProfileIn( __METHOD__ );
+ $handle = $this->getWriter();
+ if ( !$handle ) {
+ return false;
+ }
+ $ret = dba_delete( $key, $handle );
+ dba_close( $handle );
+ wfProfileOut( __METHOD__ );
+ return $ret;
+ }
+
+ function add( $key, $value, $exptime = 0 ) {
+ wfProfileIn( __METHOD__ );
+ $blob = $this->encode( $value, $exptime );
+ $handle = $this->getWriter();
+ if ( !$handle ) {
+ return false;
+ }
+ $ret = dba_insert( $key, $blob, $handle );
+ # Insert failed, check to see if it failed due to an expired key
+ if ( !$ret ) {
+ list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
+ if ( $expiry < time() ) {
+ # Yes expired, delete and try again
+ dba_delete( $key, $handle );
+ $ret = dba_insert( $key, $blob, $handle );
+ # This time if it failed then it will be handled by the caller like any other race
+ }
+ }
+
+ dba_close( $handle );
+ wfProfileOut( __METHOD__ );
+ return $ret;
+ }
+}
+
?>
diff --git a/includes/Block.php b/includes/Block.php
index 26fa444d..b11df22c 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -9,7 +9,6 @@
* All the functions in this class assume the object is either explicitly
* loaded or filled. It is not load-on-demand. There are no accessors.
*
- * To use delete(), you only need to fill $mAddress
* Globals used: $wgAutoblockExpiry, $wgAntiLockFlags
*
* @todo This could be used everywhere, but it isn't.
@@ -18,27 +17,26 @@
class Block
{
/* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
- $mRangeStart, $mRangeEnd;
+ $mRangeStart, $mRangeEnd, $mAnonOnly;
/* 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 = '', $by = 0, $reason = '',
- $timestamp = '' , $auto = 0, $expiry = '' )
+ function Block( $address = '', $user = 0, $by = 0, $reason = '',
+ $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0 )
{
+ $this->mId = 0;
$this->mAddress = $address;
$this->mUser = $user;
$this->mBy = $by;
$this->mReason = $reason;
$this->mTimestamp = wfTimestamp(TS_MW,$timestamp);
$this->mAuto = $auto;
- if( empty( $expiry ) ) {
- $this->mExpiry = $expiry;
- } else {
- $this->mExpiry = wfTimestamp( TS_MW, $expiry );
- }
+ $this->mAnonOnly = $anonOnly;
+ $this->mCreateAccount = $createAccount;
+ $this->mExpiry = self::decodeExpiry( $expiry );
$this->mForUpdate = false;
$this->mFromMaster = false;
@@ -46,19 +44,36 @@ class Block
$this->initialiseRange();
}
- /*static*/ function newFromDB( $address, $user = 0, $killExpired = true )
+ static function newFromDB( $address, $user = 0, $killExpired = true )
{
- $ban = new Block();
- $ban->load( $address, $user, $killExpired );
- return $ban;
+ $block = new Block();
+ $block->load( $address, $user, $killExpired );
+ if ( $block->isValid() ) {
+ return $block;
+ } else {
+ return null;
+ }
+ }
+
+ static function newFromID( $id )
+ {
+ $dbr =& wfGetDB( DB_SLAVE );
+ $res = $dbr->resultObject( $dbr->select( 'ipblocks', '*',
+ array( 'ipb_id' => $id ), __METHOD__ ) );
+ $block = new Block;
+ if ( $block->loadFromResult( $res ) ) {
+ return $block;
+ } else {
+ return null;
+ }
}
function clear()
{
$this->mAddress = $this->mReason = $this->mTimestamp = '';
- $this->mUser = $this->mBy = 0;
+ $this->mId = $this->mAnonOnly = $this->mCreateAccount =
+ $this->mAuto = $this->mUser = $this->mBy = 0;
$this->mByName = false;
-
}
/**
@@ -70,56 +85,103 @@ class Block
if ( $this->mForUpdate || $this->mFromMaster ) {
$db =& wfGetDB( DB_MASTER );
if ( !$this->mForUpdate || ($wgAntiLockFlags & ALF_NO_BLOCK_LOCK) ) {
- $options = '';
+ $options = array();
} else {
- $options = 'FOR UPDATE';
+ $options = array( 'FOR UPDATE' );
}
} else {
$db =& wfGetDB( DB_SLAVE );
- $options = '';
+ $options = array();
}
return $db;
}
/**
* Get a ban from the DB, with either the given address or the given username
+ *
+ * @param string $address The IP address of the user, or blank to skip IP blocks
+ * @param integer $user The user ID, or zero for anonymous users
+ * @param bool $killExpired Whether to delete expired rows while loading
+ *
*/
function load( $address = '', $user = 0, $killExpired = true )
{
- $fname = 'Block::load';
wfDebug( "Block::load: '$address', '$user', $killExpired\n" );
- $options = '';
+ $options = array();
$db =& $this->getDBOptions( $options );
$ret = false;
$killed = false;
- $ipblocks = $db->tableName( 'ipblocks' );
if ( 0 == $user && $address == '' ) {
# Invalid user specification, not blocked
$this->clear();
return false;
- } elseif ( $address == '' ) {
- $sql = "SELECT * FROM $ipblocks WHERE ipb_user={$user} $options";
- } elseif ( $user == '' ) {
- $sql = "SELECT * FROM $ipblocks WHERE ipb_address=" . $db->addQuotes( $address ) . " $options";
- } elseif ( $options == '' ) {
- # If there are no options (e.g. FOR UPDATE), use a UNION
- # so that the query can make efficient use of indices
- $sql = "SELECT * FROM $ipblocks WHERE ipb_address='" . $db->strencode( $address ) .
- "' UNION SELECT * FROM $ipblocks WHERE ipb_user={$user}";
- } else {
- # If there are options, a UNION can not be used, use one
- # SELECT instead. Will do a full table scan.
- $sql = "SELECT * FROM $ipblocks WHERE (ipb_address='" . $db->strencode( $address ) .
- "' OR ipb_user={$user}) $options";
}
- $res = $db->query( $sql, $fname );
- if ( 0 != $db->numRows( $res ) ) {
+ # Try user block
+ if ( $user ) {
+ $res = $db->resultObject( $db->select( 'ipblocks', '*', array( 'ipb_user' => $user ),
+ __METHOD__, $options ) );
+ if ( $this->loadFromResult( $res, $killExpired ) ) {
+ return true;
+ }
+ }
+
+ # Try IP block
+ # TODO: improve performance by merging this query with the autoblock one
+ # Slightly tricky while handling killExpired as well
+ if ( $address ) {
+ $conds = array( 'ipb_address' => $address, 'ipb_auto' => 0 );
+ $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
+ if ( $this->loadFromResult( $res, $killExpired ) ) {
+ if ( $user && $this->mAnonOnly ) {
+ # Block is marked anon-only
+ # Whitelist this IP address against autoblocks and range blocks
+ $this->clear();
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ # Try range block
+ if ( $this->loadRange( $address, $killExpired, $user == 0 ) ) {
+ if ( $user && $this->mAnonOnly ) {
+ $this->clear();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ # Try autoblock
+ if ( $address ) {
+ $conds = array( 'ipb_address' => $address, 'ipb_auto' => 1 );
+ if ( $user ) {
+ $conds['ipb_anon_only'] = 0;
+ }
+ $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
+ if ( $this->loadFromResult( $res, $killExpired ) ) {
+ return true;
+ }
+ }
+
+ # Give up
+ $this->clear();
+ return false;
+ }
+
+ /**
+ * Fill in member variables from a result wrapper
+ */
+ function loadFromResult( ResultWrapper $res, $killExpired = true ) {
+ $ret = false;
+ if ( 0 != $res->numRows() ) {
# Get first block
- $row = $db->fetchObject( $res );
+ $row = $res->fetchObject();
$this->initFromRow( $row );
if ( $killExpired ) {
@@ -127,7 +189,7 @@ class Block
do {
$killed = $this->deleteIfExpired();
if ( $killed ) {
- $row = $db->fetchObject( $res );
+ $row = $res->fetchObject();
if ( $row ) {
$this->initFromRow( $row );
}
@@ -135,26 +197,14 @@ class Block
} while ( $killed && $row );
# If there were any left after the killing finished, return true
- if ( !$row ) {
- $ret = false;
- $this->clear();
- } else {
+ if ( $row ) {
$ret = true;
}
} else {
$ret = true;
}
}
- $db->freeResult( $res );
-
- # No blocks found yet? Try looking for range blocks
- if ( !$ret && $address != '' ) {
- $ret = $this->loadRange( $address, $killExpired );
- }
- if ( !$ret ) {
- $this->clear();
- }
-
+ $res->free();
return $ret;
}
@@ -164,9 +214,7 @@ class Block
*/
function loadRange( $address, $killExpired = true )
{
- $fname = 'Block::loadRange';
-
- $iaddr = wfIP2Hex( $address );
+ $iaddr = IP::toHex( $address );
if ( $iaddr === false ) {
# Invalid address
return false;
@@ -176,27 +224,16 @@ class Block
# Blocks should not cross a /16 boundary.
$range = substr( $iaddr, 0, 4 );
- $options = '';
+ $options = array();
$db =& $this->getDBOptions( $options );
- $ipblocks = $db->tableName( 'ipblocks' );
- $sql = "SELECT * FROM $ipblocks WHERE ipb_range_start LIKE '$range%' ".
- "AND ipb_range_start <= '$iaddr' AND ipb_range_end >= '$iaddr' $options";
- $res = $db->query( $sql, $fname );
- $row = $db->fetchObject( $res );
-
- $success = false;
- if ( $row ) {
- # Found a row, initialise this object
- $this->initFromRow( $row );
-
- # Is it expired?
- if ( !$killExpired || !$this->deleteIfExpired() ) {
- # No, return true
- $success = true;
- }
- }
+ $conds = array(
+ "ipb_range_start LIKE '$range%'",
+ "ipb_range_start <= '$iaddr'",
+ "ipb_range_end >= '$iaddr'"
+ );
- $db->freeResult( $res );
+ $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
+ $success = $this->loadFromResult( $res, $killExpired );
return $success;
}
@@ -220,10 +257,10 @@ class Block
$this->mUser = $row->ipb_user;
$this->mBy = $row->ipb_by;
$this->mAuto = $row->ipb_auto;
+ $this->mAnonOnly = $row->ipb_anon_only;
+ $this->mCreateAccount = $row->ipb_create_account;
$this->mId = $row->ipb_id;
- $this->mExpiry = $row->ipb_expiry ?
- wfTimestamp(TS_MW,$row->ipb_expiry) :
- $row->ipb_expiry;
+ $this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
if ( isset( $row->user_name ) ) {
$this->mByName = $row->user_name;
} else {
@@ -304,24 +341,32 @@ class Block
function delete()
{
- $fname = 'Block::delete';
if (wfReadOnly()) {
- return;
+ return false;
}
- $dbw =& wfGetDB( DB_MASTER );
-
- if ( $this->mAddress == '' ) {
- $condition = array( 'ipb_id' => $this->mId );
- } else {
- $condition = array( 'ipb_address' => $this->mAddress );
+ if ( !$this->mId ) {
+ throw new MWException( "Block::delete() now requires that the mId member be filled\n" );
}
- return( $dbw->delete( 'ipblocks', $condition, $fname ) > 0 ? true : false );
+
+ $dbw =& wfGetDB( DB_MASTER );
+ $dbw->delete( 'ipblocks', array( 'ipb_id' => $this->mId ), __METHOD__ );
+ return $dbw->affectedRows() > 0;
}
function insert()
{
wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
$dbw =& wfGetDB( DB_MASTER );
+ $dbw->begin();
+
+ # Unset ipb_anon_only for user blocks, makes no sense
+ if ( $this->mUser ) {
+ $this->mAnonOnly = 0;
+ }
+
+ # Don't collide with expired blocks
+ Block::purgeExpired();
+
$ipb_id = $dbw->nextSequenceValue('ipblocks_ipb_id_val');
$dbw->insert( 'ipblocks',
array(
@@ -332,13 +377,16 @@ class Block
'ipb_reason' => $this->mReason,
'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
'ipb_auto' => $this->mAuto,
- 'ipb_expiry' => $this->mExpiry ?
- $dbw->timestamp($this->mExpiry) :
- $this->mExpiry,
+ 'ipb_anon_only' => $this->mAnonOnly,
+ 'ipb_create_account' => $this->mCreateAccount,
+ 'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
'ipb_range_start' => $this->mRangeStart,
'ipb_range_end' => $this->mRangeEnd,
- ), 'Block::insert'
+ ), 'Block::insert', array( 'IGNORE' )
);
+ $affected = $dbw->affectedRows();
+ $dbw->commit();
+ return $affected;
}
function deleteIfExpired()
@@ -417,18 +465,48 @@ class Block
return wfSetVar( $this->mFromMaster, $x );
}
- /* static */ function getAutoblockExpiry( $timestamp )
+ function getRedactedName() {
+ if ( $this->mAuto ) {
+ return '#' . $this->mId;
+ } else {
+ return $this->mAddress;
+ }
+ }
+
+ /**
+ * Encode expiry for DB
+ */
+ static function encodeExpiry( $expiry, $db ) {
+ if ( $expiry == '' || $expiry == Block::infinity() ) {
+ return Block::infinity();
+ } else {
+ return $db->timestamp( $expiry );
+ }
+ }
+
+ /**
+ * Decode expiry which has come from the DB
+ */
+ static function decodeExpiry( $expiry ) {
+ if ( $expiry == '' || $expiry == Block::infinity() ) {
+ return Block::infinity();
+ } else {
+ return wfTimestamp( TS_MW, $expiry );
+ }
+ }
+
+ static function getAutoblockExpiry( $timestamp )
{
global $wgAutoblockExpiry;
return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
}
- /* static */ function normaliseRange( $range )
+ static function normaliseRange( $range )
{
$parts = explode( '/', $range );
if ( count( $parts ) == 2 ) {
$shift = 32 - $parts[1];
- $ipint = wfIP2Unsigned( $parts[0] );
+ $ipint = IP::toUnsigned( $parts[0] );
$ipint = $ipint >> $shift << $shift;
$newip = long2ip( $ipint );
$range = "$newip/{$parts[1]}";
@@ -436,5 +514,28 @@ class Block
return $range;
}
+ /**
+ * Purge expired blocks from the ipblocks table
+ */
+ static function purgeExpired() {
+ $dbw =& wfGetDB( DB_MASTER );
+ $dbw->delete( 'ipblocks', array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ );
+ }
+
+ static function infinity() {
+ # This is a special keyword for timestamps in PostgreSQL, and
+ # works with CHAR(14) as well because "i" sorts after all numbers.
+ return 'infinity';
+
+ /*
+ static $infinity;
+ if ( !isset( $infinity ) ) {
+ $dbr =& wfGetDB( DB_SLAVE );
+ $infinity = $dbr->bigTimestamp();
+ }
+ return $infinity;
+ */
+ }
+
}
?>
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 53d69971..e55d2976 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -13,7 +13,6 @@ if( !defined( 'MEDIAWIKI' ) )
* @package MediaWiki
*/
class CategoryPage extends Article {
-
function view() {
if(!wfRunHooks('CategoryPageView', array(&$this))) return;
@@ -40,10 +39,27 @@ class CategoryPage extends Article {
global $wgOut, $wgRequest;
$from = $wgRequest->getVal( 'from' );
$until = $wgRequest->getVal( 'until' );
-
- $wgOut->addHTML( $this->doCategoryMagic( $from, $until ) );
+
+ $viewer = new CategoryViewer( $this->mTitle, $from, $until );
+ $wgOut->addHTML( $viewer->getHTML() );
}
+}
+class CategoryViewer {
+ var $title, $limit, $from, $until,
+ $articles, $articles_start_char,
+ $children, $children_start_char,
+ $showGallery, $gallery,
+ $skin;
+
+ function __construct( $title, $from = '', $until = '' ) {
+ global $wgCategoryPagingLimit;
+ $this->title = $title;
+ $this->from = $from;
+ $this->until = $until;
+ $this->limit = $wgCategoryPagingLimit;
+ }
+
/**
* Format the category data list.
*
@@ -52,130 +68,205 @@ class CategoryPage extends Article {
* @return string HTML output
* @private
*/
- function doCategoryMagic( $from = '', $until = '' ) {
- global $wgOut;
- global $wgContLang,$wgUser, $wgCategoryMagicGallery, $wgCategoryPagingLimit;
- $fname = 'CategoryPage::doCategoryMagic';
- wfProfileIn( $fname );
-
- $articles = array();
- $articles_start_char = array();
- $children = array();
- $children_start_char = array();
+ function getHTML() {
+ global $wgOut, $wgCategoryMagicGallery, $wgCategoryPagingLimit;
+ wfProfileIn( __METHOD__ );
+
+ $this->showGallery = $wgCategoryMagicGallery && !$wgOut->mNoGallery;
+
+ $this->clearCategoryState();
+ $this->doCategoryQuery();
+ $this->finaliseCategoryState();
+
+ $r = $this->getCategoryTop() .
+ $this->getSubcategorySection() .
+ $this->getPagesSection() .
+ $this->getImageSection() .
+ $this->getCategoryBottom();
+
+ wfProfileOut( __METHOD__ );
+ return $r;
+ }
+
+ function clearCategoryState() {
+ $this->articles = array();
+ $this->articles_start_char = array();
+ $this->children = array();
+ $this->children_start_char = array();
+ if( $this->showGallery ) {
+ $this->gallery = new ImageGallery();
+ $this->gallery->setParsing();
+ }
+ }
+
+ function getSkin() {
+ if ( !$this->skin ) {
+ global $wgUser;
+ $this->skin = $wgUser->getSkin();
+ }
+ return $this->skin;
+ }
+
+ /**
+ * Add a subcategory to the internal lists
+ */
+ function addSubcategory( $title, $sortkey, $pageLength ) {
+ global $wgContLang;
+ // Subcategory; strip the 'Category' namespace from the link text.
+ $this->children[] = $this->getSkin()->makeKnownLinkObj(
+ $title, $wgContLang->convertHtml( $title->getText() ) );
+
+ $this->children_start_char[] = $this->getSubcategorySortChar( $title, $sortkey );
+ }
+
+ /**
+ * Get the character to be used for sorting subcategories.
+ * If there's a link from Category:A to Category:B, the sortkey of the resulting
+ * entry in the categorylinks table is Category:A, not A, which it SHOULD be.
+ * Workaround: If sortkey == "Category:".$title, than use $title for sorting,
+ * else use sortkey...
+ */
+ function getSubcategorySortChar( $title, $sortkey ) {
+ global $wgContLang;
+
+ if( $title->getPrefixedText() == $sortkey ) {
+ $firstChar = $wgContLang->firstChar( $title->getDBkey() );
+ } else {
+ $firstChar = $wgContLang->firstChar( $sortkey );
+ }
- $showGallery = $wgCategoryMagicGallery && !$wgOut->mNoGallery;
- if( $showGallery ) {
- $ig = new ImageGallery();
- $ig->setParsing();
+ return $wgContLang->convert( $firstChar );
+ }
+
+ /**
+ * Add a page in the image namespace
+ */
+ function addImage( $title, $sortkey, $pageLength ) {
+ if ( $this->showGallery ) {
+ $image = new Image( $title );
+ if( $this->flip ) {
+ $this->gallery->insert( $image );
+ } else {
+ $this->gallery->add( $image );
+ }
+ } else {
+ $this->addPage( $title, $sortkey, $pageLength );
}
+ }
+ /**
+ * Add a miscellaneous page
+ */
+ function addPage( $title, $sortkey, $pageLength ) {
+ global $wgContLang;
+ $this->articles[] = $this->getSkin()->makeSizeLinkObj(
+ $pageLength, $title, $wgContLang->convert( $title->getPrefixedText() )
+ );
+ $this->articles_start_char[] = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
+ }
+
+ function finaliseCategoryState() {
+ if( $this->flip ) {
+ $this->children = array_reverse( $this->children );
+ $this->children_start_char = array_reverse( $this->children_start_char );
+ $this->articles = array_reverse( $this->articles );
+ $this->articles_start_char = array_reverse( $this->articles_start_char );
+ }
+ }
+
+ function doCategoryQuery() {
$dbr =& wfGetDB( DB_SLAVE );
- if( $from != '' ) {
- $pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $from );
- $flip = false;
- } elseif( $until != '' ) {
- $pageCondition = 'cl_sortkey < ' . $dbr->addQuotes( $until );
- $flip = true;
+ if( $this->from != '' ) {
+ $pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from );
+ $this->flip = false;
+ } elseif( $this->until != '' ) {
+ $pageCondition = 'cl_sortkey < ' . $dbr->addQuotes( $this->until );
+ $this->flip = true;
} else {
$pageCondition = '1 = 1';
- $flip = false;
+ $this->flip = false;
}
- $limit = $wgCategoryPagingLimit;
$res = $dbr->select(
array( 'page', 'categorylinks' ),
array( 'page_title', 'page_namespace', 'page_len', 'cl_sortkey' ),
array( $pageCondition,
'cl_from = page_id',
- 'cl_to' => $this->mTitle->getDBKey()),
+ 'cl_to' => $this->title->getDBKey()),
#'page_is_redirect' => 0),
#+ $pageCondition,
- $fname,
- array( 'ORDER BY' => $flip ? 'cl_sortkey DESC' : 'cl_sortkey',
- 'LIMIT' => $limit + 1 ) );
+ __METHOD__,
+ array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : 'cl_sortkey',
+ 'LIMIT' => $this->limit + 1 ) );
- $sk =& $wgUser->getSkin();
- $r = "<br style=\"clear:both;\"/>\n";
$count = 0;
- $nextPage = null;
+ $this->nextPage = null;
while( $x = $dbr->fetchObject ( $res ) ) {
- if( ++$count > $limit ) {
+ if( ++$count > $this->limit ) {
// We've reached the one extra which shows that there are
// additional pages to be had. Stop here...
- $nextPage = $x->cl_sortkey;
+ $this->nextPage = $x->cl_sortkey;
break;
}
$title = Title::makeTitle( $x->page_namespace, $x->page_title );
if( $title->getNamespace() == NS_CATEGORY ) {
- // Subcategory; strip the 'Category' namespace from the link text.
- array_push( $children, $sk->makeKnownLinkObj( $title, $wgContLang->convertHtml( $title->getText() ) ) );
-
- // If there's a link from Category:A to Category:B, the sortkey of the resulting
- // entry in the categorylinks table is Category:A, not A, which it SHOULD be.
- // Workaround: If sortkey == "Category:".$title, than use $title for sorting,
- // else use sortkey...
- $sortkey='';
- if( $title->getPrefixedText() == $x->cl_sortkey ) {
- $sortkey=$wgContLang->firstChar( $x->page_title );
- } else {
- $sortkey=$wgContLang->firstChar( $x->cl_sortkey );
- }
- array_push( $children_start_char, $wgContLang->convert( $sortkey ) ) ;
- } elseif( $showGallery && $title->getNamespace() == NS_IMAGE ) {
- // Show thumbnails of categorized images, in a separate chunk
- if( $flip ) {
- $ig->insert( Image::newFromTitle( $title ) );
- } else {
- $ig->add( Image::newFromTitle( $title ) );
- }
+ $this->addSubcategory( $title, $x->cl_sortkey, $x->page_len );
+ } elseif( $title->getNamespace() == NS_IMAGE ) {
+ $this->addImage( $title, $x->cl_sortkey, $x->page_len );
} else {
- // Page in this category
- array_push( $articles, $sk->makeSizeLinkObj( $x->page_len, $title, $wgContLang->convert( $title->getPrefixedText() ) ) ) ;
- array_push( $articles_start_char, $wgContLang->convert( $wgContLang->firstChar( $x->cl_sortkey ) ) );
+ $this->addPage( $title, $x->cl_sortkey, $x->page_len );
}
}
$dbr->freeResult( $res );
+ }
- if( $flip ) {
- $children = array_reverse( $children );
- $children_start_char = array_reverse( $children_start_char );
- $articles = array_reverse( $articles );
- $articles_start_char = array_reverse( $articles_start_char );
- }
-
- if( $until != '' ) {
- $r .= $this->pagingLinks( $this->mTitle, $nextPage, $until, $limit );
- } elseif( $nextPage != '' || $from != '' ) {
- $r .= $this->pagingLinks( $this->mTitle, $from, $nextPage, $limit );
+ function getCategoryTop() {
+ $r = "<br style=\"clear:both;\"/>\n";
+ if( $this->until != '' ) {
+ $r .= $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit );
+ } elseif( $this->nextPage != '' || $this->from != '' ) {
+ $r .= $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit );
}
+ return $r;
+ }
+ function getSubcategorySection() {
# Don't show subcategories section if there are none.
- if( count( $children ) > 0 ) {
+ $r = '';
+ if( count( $this->children ) > 0 ) {
# Showing subcategories
$r .= '<h2>' . wfMsg( 'subcategories' ) . "</h2>\n";
- $r .= wfMsgExt( 'subcategorycount', array( 'parse' ), count( $children) );
- $r .= $this->formatList( $children, $children_start_char );
+ $r .= wfMsgExt( 'subcategorycount', array( 'parse' ), count( $this->children) );
+ $r .= $this->formatList( $this->children, $this->children_start_char );
}
+ return $r;
+ }
- # Showing articles in this category
- $ti = htmlspecialchars( $this->mTitle->getText() );
- $r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
- $r .= wfMsgExt( 'categoryarticlecount', array( 'parse' ), count( $articles) );
- $r .= $this->formatList( $articles, $articles_start_char );
+ function getPagesSection() {
+ $ti = htmlspecialchars( $this->title->getText() );
+ $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 );
+ return $r;
+ }
- if( $showGallery && ! $ig->isEmpty() ) {
- $r.= $ig->toHTML();
+ function getImageSection() {
+ if( $this->showGallery && ! $this->gallery->isEmpty() ) {
+ return $this->gallery->toHTML();
+ } else {
+ return '';
}
+ }
- if( $until != '' ) {
- $r .= $this->pagingLinks( $this->mTitle, $nextPage, $until, $limit );
- } elseif( $nextPage != '' || $from != '' ) {
- $r .= $this->pagingLinks( $this->mTitle, $from, $nextPage, $limit );
+ function getCategoryBottom() {
+ if( $this->until != '' ) {
+ return $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit );
+ } elseif( $this->nextPage != '' || $this->from != '' ) {
+ return $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit );
+ } else {
+ return '';
}
-
- wfProfileOut( $fname );
- return $r;
}
/**
@@ -293,7 +384,7 @@ class CategoryPage extends Article {
*/
function pagingLinks( $title, $first, $last, $limit, $query = array() ) {
global $wgUser, $wgLang;
- $sk =& $wgUser->getSkin();
+ $sk =& $this->getSkin();
$limitText = $wgLang->formatNum( $limit );
$prevLink = htmlspecialchars( wfMsg( 'prevn', $limitText ) );
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index b2c1abe2..6797bb41 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -394,6 +394,7 @@ class EnhancedChangesList extends ChangesList {
* Enhanced RC group
*/
function recentChangesBlockGroup( $block ) {
+ global $wgContLang;
$r = '';
# Collate list of users
@@ -423,6 +424,7 @@ class EnhancedChangesList extends ChangesList {
$users = array();
foreach( $userlinks as $userlink => $count) {
$text = $userlink;
+ $text .= $wgContLang->getDirMark();
if( $count > 1 ) {
$text .= ' ('.$count.'&times;)';
}
@@ -450,6 +452,7 @@ class EnhancedChangesList extends ChangesList {
# Article link
$r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+ $r .= $wgContLang->getDirMark();
$curIdEq = 'curid=' . $block[0]->mAttribs['rc_cur_id'];
$currentRevision = $block[0]->mAttribs['rc_this_oldid'];
diff --git a/includes/CoreParserFunctions.php b/includes/CoreParserFunctions.php
index d6578abf..2081b3f2 100644
--- a/includes/CoreParserFunctions.php
+++ b/includes/CoreParserFunctions.php
@@ -5,6 +5,15 @@
*/
class CoreParserFunctions {
+ static function intFunction( $parser, $part1 = '' /*, ... */ ) {
+ if ( strval( $part1 ) !== '' ) {
+ $args = array_slice( func_get_args(), 2 );
+ return wfMsgReal( $part1, $args, true );
+ } else {
+ return array( 'found' => false );
+ }
+ }
+
static function ns( $parser, $part1 = '' ) {
global $wgContLang;
$found = false;
@@ -106,7 +115,7 @@ class CoreParserFunctions {
function isRaw( $param ) {
static $mwRaw;
if ( !$mwRaw ) {
- $mwRaw =& MagicWord::get( MAG_RAWSUFFIX );
+ $mwRaw =& MagicWord::get( 'rawsuffix' );
}
if ( is_null( $param ) ) {
return false;
@@ -145,6 +154,27 @@ class CoreParserFunctions {
$lang = $wgContLang->getLanguageName( strtolower( $arg ) );
return $lang != '' ? $lang : $arg;
}
+
+ function pad( $string = '', $length = 0, $char = 0, $direction = STR_PAD_RIGHT ) {
+ $length = min( max( $length, 0 ), 500 );
+ $char = substr( $char, 0, 1 );
+ return ( $string && (int)$length > 0 && strlen( trim( (string)$char ) ) > 0 )
+ ? str_pad( $string, $length, (string)$char, $direction )
+ : $string;
+ }
+
+ function padleft( $parser, $string = '', $length = 0, $char = 0 ) {
+ return self::pad( $string, $length, $char, STR_PAD_LEFT );
+ }
+
+ function padright( $parser, $string = '', $length = 0, $char = 0 ) {
+ return self::pad( $string, $length, $char );
+ }
+
+ function anchorencode( $parser, $text ) {
+ return str_replace( '%', '.', str_replace('+', '_', urlencode( $text ) ) );
+ }
+
}
?>
diff --git a/includes/Credits.php b/includes/Credits.php
index ff33de74..62f0b256 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -87,11 +87,13 @@ function getAuthorCredits($article) {
$timestamp = $article->getTimestamp();
if ($timestamp) {
- $d = $wgLang->timeanddate($article->getTimestamp(), true);
+ $d = $wgLang->date($article->getTimestamp(), true);
+ $t = $wgLang->time($article->getTimestamp(), true);
} else {
$d = '';
+ $t = '';
}
- return wfMsg('lastmodifiedby', $d, $author_credit);
+ return wfMsg('lastmodifiedatby', $d, $t, $author_credit);
}
/**
diff --git a/includes/Database.php b/includes/Database.php
index f8e579b4..53e59968 100644
--- a/includes/Database.php
+++ b/includes/Database.php
@@ -5,13 +5,6 @@
* @package MediaWiki
*/
-/** See Database::makeList() */
-define( 'LIST_COMMA', 0 );
-define( 'LIST_AND', 1 );
-define( 'LIST_SET', 2 );
-define( 'LIST_NAMES', 3);
-define( 'LIST_OR', 4);
-
/** Number of times to re-try an operation in case of deadlock */
define( 'DEADLOCK_TRIES', 4 );
/** Minimum time to wait before retry, in microseconds */
@@ -86,6 +79,11 @@ class DBConnectionError extends DBError {
return $this->getMessage() . "\n";
}
+ function getLogMessage() {
+ # Don't send to the exception log
+ return false;
+ }
+
function getPageTitle() {
global $wgSitename;
return "$wgSitename has a problem";
@@ -205,6 +203,11 @@ class DBQueryError extends DBError {
}
}
+ function getLogMessage() {
+ # Don't send to the exception log
+ return false;
+ }
+
function getPageTitle() {
return $this->msg( 'databaseerror', 'Database error' );
}
@@ -244,6 +247,9 @@ class Database {
protected $mTrxLevel = 0;
protected $mErrorCount = 0;
protected $mLBInfo = array();
+ protected $mCascadingDeletes = false;
+ protected $mCleanupTriggers = false;
+ protected $mStrictIPs = false;
#------------------------------------------------------------------------------
# Accessors
@@ -334,6 +340,28 @@ class Database {
}
}
+ /**
+ * Returns true if this database supports (and uses) cascading deletes
+ */
+ function cascadingDeletes() {
+ return $this->mCascadingDeletes;
+ }
+
+ /**
+ * Returns true if this database supports (and uses) triggers (e.g. on the page table)
+ */
+ function cleanupTriggers() {
+ return $this->mCleanupTriggers;
+ }
+
+ /**
+ * Returns true if this database is strict about what can be put into an IP field.
+ * Specifically, it uses a NULL value instead of an empty string.
+ */
+ function strictIPs() {
+ return $this->mStrictIPs;
+ }
+
/**#@+
* Get function
*/
@@ -433,6 +461,7 @@ class Database {
*/
function open( $server, $user, $password, $dbName ) {
global $wguname;
+ wfProfileIn( __METHOD__ );
# Test for missing mysql.so
# First try to load it
@@ -454,12 +483,28 @@ class Database {
$success = false;
- if ( $this->mFlags & DBO_PERSISTENT ) {
- @/**/$this->mConn = mysql_pconnect( $server, $user, $password );
- } else {
- # Create a new connection...
- @/**/$this->mConn = mysql_connect( $server, $user, $password, true );
+ wfProfileIn("dbconnect-$server");
+
+ # LIVE PATCH by Tim, ask Domas for why: retry loop
+ $this->mConn = false;
+ $max = 3;
+ for ( $i = 0; $i < $max && !$this->mConn; $i++ ) {
+ if ( $i > 1 ) {
+ usleep( 1000 );
+ }
+ if ( $this->mFlags & DBO_PERSISTENT ) {
+ @/**/$this->mConn = mysql_pconnect( $server, $user, $password );
+ } else {
+ # Create a new connection...
+ @/**/$this->mConn = mysql_connect( $server, $user, $password, true );
+ }
+ if ($this->mConn === false) {
+ $iplus = $i + 1;
+ wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
+ }
}
+
+ wfProfileOut("dbconnect-$server");
if ( $dbName != '' ) {
if ( $this->mConn !== false ) {
@@ -467,6 +512,7 @@ class Database {
if ( !$success ) {
$error = "Error selecting database $dbName on server {$this->mServer} " .
"from client host {$wguname['nodename']}\n";
+ wfLogDBError(" Error selecting database $dbName on server {$this->mServer} \n");
wfDebug( $error );
}
} else {
@@ -480,18 +526,19 @@ class Database {
$success = (bool)$this->mConn;
}
- if ( !$success ) {
+ if ( $success ) {
+ global $wgDBmysql5;
+ if( $wgDBmysql5 ) {
+ // Tell the server we're communicating with it in UTF-8.
+ // This may engage various charset conversions.
+ $this->query( 'SET NAMES utf8' );
+ }
+ } else {
$this->reportConnectionError();
}
- global $wgDBmysql5;
- if( $wgDBmysql5 ) {
- // Tell the server we're communicating with it in UTF-8.
- // This may engage various charset conversions.
- $this->query( 'SET NAMES utf8' );
- }
-
$this->mOpened = $success;
+ wfProfileOut( __METHOD__ );
return $success;
}
/**@}}*/
@@ -760,8 +807,8 @@ class Database {
*/
function fetchObject( $res ) {
@/**/$row = mysql_fetch_object( $res );
- if( mysql_errno() ) {
- throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( mysql_error() ) );
+ if( $this->lastErrno() ) {
+ throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
}
return $row;
}
@@ -772,8 +819,8 @@ class Database {
*/
function fetchRow( $res ) {
@/**/$row = mysql_fetch_array( $res );
- if (mysql_errno() ) {
- throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( mysql_error() ) );
+ if ( $this->lastErrno() ) {
+ throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
}
return $row;
}
@@ -783,8 +830,8 @@ class Database {
*/
function numRows( $res ) {
@/**/$n = mysql_num_rows( $res );
- if( mysql_errno() ) {
- throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( mysql_error() ) );
+ if( $this->lastErrno() ) {
+ throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) );
}
return $n;
}
@@ -1865,7 +1912,7 @@ class Database {
function sourceFile( $filename ) {
$fp = fopen( $filename, 'r' );
if ( false === $fp ) {
- return "Could not open \"{$fname}\".\n";
+ return "Could not open \"{$filename}\".\n";
}
$cmd = "";
diff --git a/includes/DatabaseOracle.php b/includes/DatabaseOracle.php
index d5d7379d..aa1e329e 100644
--- a/includes/DatabaseOracle.php
+++ b/includes/DatabaseOracle.php
@@ -6,11 +6,6 @@
* @package MediaWiki
*/
-/**
- * Depends on database
- */
-require_once( 'Database.php' );
-
class OracleBlob extends DBObject {
function isLOB() {
return true;
diff --git a/includes/DatabasePostgres.php b/includes/DatabasePostgres.php
index 5897386f..a5e02e77 100644
--- a/includes/DatabasePostgres.php
+++ b/includes/DatabasePostgres.php
@@ -1,7 +1,7 @@
<?php
/**
- * This is PostgreSQL database abstraction layer.
+ * This is Postgres database abstraction layer.
*
* As it includes more generic version for DB functions,
* than MySQL ones, some of them should be moved to parent
@@ -10,11 +10,6 @@
* @package MediaWiki
*/
-/**
- * Depends on database
- */
-require_once( 'Database.php' );
-
class DatabasePostgres extends Database {
var $mInsertId = NULL;
var $mLastResult = NULL;
@@ -30,8 +25,10 @@ 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);
}
@@ -47,11 +44,12 @@ class DatabasePostgres extends Database {
* If the failFunction is set to a non-zero integer, returns success
*/
function open( $server, $user, $password, $dbName ) {
- # Test for PostgreSQL support, to avoid suppressed fatal error
+ # Test for Postgres support, to avoid suppressed fatal error
if ( !function_exists( 'pg_connect' ) ) {
- throw new DBConnectionError( $this, "PostgreSQL functions missing, have you compiled PHP with the --with-pgsql option?\n" );
+ 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;
$this->close();
@@ -62,7 +60,6 @@ class DatabasePostgres extends Database {
$this->mDBname = $dbName;
$success = false;
-
$hstring="";
if ($server!=false && $server!="") {
$hstring="host=$server ";
@@ -71,8 +68,11 @@ class DatabasePostgres extends Database {
$hstring .= "port=$port ";
}
- error_reporting( E_ALL );
+ 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");
if ( $this->mConn == false ) {
@@ -83,12 +83,153 @@ class DatabasePostgres extends Database {
}
$this->mOpened = true;
- ## If this is the initial connection, setup the schema stuff
- if (defined('MEDIAWIKI_INSTALL') and !defined('POSTGRES_SEARCHPATH')) {
- global $wgDBmwschema, $wgDBts2schema, $wgDBname;
+ ## If this is the initial connection, setup the schema stuff and possibly create the user
+ if (defined('MEDIAWIKI_INSTALL')) {
+ global $wgDBname, $wgDBuser, $wgDBpass, $wgDBsuperuser, $wgDBmwschema,
+ $wgDBts2schema, $wgDBts2locale;
+ print "OK</li>\n";
+
+ print "<li>Checking the version of Postgres...";
+ $version = pg_fetch_result($this->doQuery("SELECT version()"),0,0);
+ if (!preg_match("/PostgreSQL (\d+\.\d+)(\S+)/", $version, $thisver)) {
+ print "<b>FAILED</b> (could not determine the version)</li>\n";
+ dieout("</ul>");
+ }
+ $PGMINVER = "8.1";
+ if ($thisver[1] < $PGMINVER) {
+ print "<b>FAILED</b>. Required version is $PGMINVER. You have $thisver[1]$thisver[2]</li>\n";
+ dieout("</ul>");
+ }
+ print "version $thisver[1]$thisver[2] is OK.</li>\n";
+
+ $safeuser = $this->quote_ident($wgDBuser);
+ ## Are we connecting as a superuser for the first time?
+ if ($wgDBsuperuser) {
+ ## Are we really a superuser? Check out our rights
+ $SQL = "SELECT
+ CASE WHEN usesuper IS TRUE THEN
+ CASE WHEN usecreatedb IS TRUE THEN 3 ELSE 1 END
+ ELSE CASE WHEN usecreatedb IS TRUE THEN 2 ELSE 0 END
+ END AS rights
+ FROM pg_catalog.pg_user WHERE usename = " . $this->addQuotes($wgDBsuperuser);
+ $rows = $this->numRows($res = $this->doQuery($SQL));
+ if (!$rows) {
+ print "<li>ERROR: Could not read permissions for user \"$wgDBsuperuser\"</li>\n";
+ dieout('</ul>');
+ }
+ $perms = pg_fetch_result($res, 0, 0);
+
+ $SQL = "SELECT 1 FROM pg_catalog.pg_user WHERE usename = " . $this->addQuotes($wgDBuser);
+ $rows = $this->numRows($this->doQuery($SQL));
+ if ($rows) {
+ print "<li>User \"$wgDBuser\" already exists, skipping account creation.</li>";
+ }
+ else {
+ if ($perms != 1 and $perms != 3) {
+ print "<li>ERROR: the user \"$wgDBsuperuser\" cannot create other users. ";
+ print 'Please use a different Postgres user.</li>';
+ dieout('</ul>');
+ }
+ print "<li>Creating user <b>$wgDBuser</b>...";
+ $safepass = $this->addQuotes($wgDBpass);
+ $SQL = "CREATE USER $safeuser NOCREATEDB PASSWORD $safepass";
+ $this->doQuery($SQL);
+ print "OK</li>\n";
+ }
+ ## User now exists, check out the database
+ if ($dbName != $wgDBname) {
+ $SQL = "SELECT 1 FROM pg_catalog.pg_database WHERE datname = " . $this->addQuotes($wgDBname);
+ $rows = $this->numRows($this->doQuery($SQL));
+ if ($rows) {
+ print "<li>Database \"$wgDBname\" already exists, skipping database creation.</li>";
+ }
+ else {
+ if ($perms < 2) {
+ print "<li>ERROR: the user \"$wgDBsuperuser\" cannot create databases. ";
+ print 'Please use a different Postgres user.</li>';
+ dieout('</ul>');
+ }
+ print "<li>Creating database <b>$wgDBname</b>...";
+ $safename = $this->quote_ident($wgDBname);
+ $SQL = "CREATE DATABASE $safename OWNER $safeuser ";
+ $this->doQuery($SQL);
+ print "OK</li>\n";
+ ## Hopefully tsearch2 and plpgsql are in template1...
+ }
+
+ ## Reconnect to check out tsearch2 rights for this user
+ print "<li>Connecting to \"$wgDBname\" as superuser \"$wgDBsuperuser\" to check rights...";
+ @$this->mConn = pg_connect("$hstring dbname=$wgDBname user=$user password=$password");
+ if ( $this->mConn == false ) {
+ print "<b>FAILED TO CONNECT!</b></li>";
+ dieout("</ul>");
+ }
+ print "OK</li>\n";
+ }
+
+ ## Tsearch2 checks
+ print "<li>Checking that tsearch2 is installed in the database \"$wgDBname\"...";
+ if (! $this->tableExists("pg_ts_cfg", $wgDBts2schema)) {
+ print "<b>FAILED</b>. tsearch2 must be installed in the database \"$wgDBname\".";
+ print "Please see <a href='http://www.devx.com/opensource/Article/21674/0/page/2'>this article</a>";
+ print " for instructions or ask on #postgresql on irc.freenode.net</li>\n";
+ dieout("</ul>");
+ }
+ print "OK</li>\n";
+ print "<li>Ensuring that user \"$wgDBuser\" has select rights on the tsearch2 tables...";
+ foreach (array('cfg','cfgmap','dict','parser') as $table) {
+ $SQL = "GRANT SELECT ON pg_ts_$table TO $safeuser";
+ $this->doQuery($SQL);
+ }
+ print "OK</li>\n";
+
+
+ ## Setup the schema for this user if needed
+ $result = $this->schemaExists($wgDBmwschema);
+ $safeschema = $this->quote_ident($wgDBmwschema);
+ if (!$result) {
+ print "<li>Creating schema <b>$wgDBmwschema</b> ...";
+ $result = $this->doQuery("CREATE SCHEMA $safeschema AUTHORIZATION $safeuser");
+ if (!$result) {
+ print "<b>FAILED</b>.</li>\n";
+ dieout("</ul>");
+ }
+ print "OK</li>\n";
+ }
+ else {
+ print "<li>Schema already exists, explicitly granting rights...\n";
+ $safeschema2 = $this->addQuotes($wgDBmwschema);
+ $SQL = "SELECT 'GRANT ALL ON '||pg_catalog.quote_ident(relname)||' TO $safeuser;'\n".
+ "FROM pg_catalog.pg_class p, pg_catalog.pg_namespace n\n".
+ "WHERE relnamespace = n.oid AND n.nspname = $safeschema2\n".
+ "AND p.relkind IN ('r','S','v')\n";
+ $SQL .= "UNION\n";
+ $SQL .= "SELECT 'GRANT ALL ON FUNCTION '||pg_catalog.quote_ident(proname)||'('||\n".
+ "pg_catalog.oidvectortypes(p.proargtypes)||') TO $safeuser;'\n".
+ "FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n\n".
+ "WHERE p.pronamespace = n.oid AND n.nspname = $safeschema2";
+ $res = $this->doQuery($SQL);
+ if (!$res) {
+ print "<b>FAILED</b>. Could not set rights for the user.</li>\n";
+ dieout("</ul>");
+ }
+ $this->doQuery("SET search_path = $safeschema");
+ $rows = $this->numRows($res);
+ while ($rows) {
+ $rows--;
+ $this->doQuery(pg_fetch_result($res, $rows, 0));
+ }
+ print "OK</li>";
+ }
+
+ $wgDBsuperuser = '';
+ return true; ## Reconnect as regular user
+ }
+
+ if (!defined('POSTGRES_SEARCHPATH')) {
## Do we have the basic tsearch2 table?
- print "<li>Checking for tsearch2 ...";
+ print "<li>Checking for tsearch2 in the schema \"$wgDBts2schema\"...";
if (! $this->tableExists("pg_ts_dict", $wgDBts2schema)) {
print "<b>FAILED</b>. Make sure tsearch2 is installed. See <a href=";
print "'http://www.devx.com/opensource/Article/21674/0/page/2'>this article</a>";
@@ -97,15 +238,78 @@ class DatabasePostgres extends Database {
}
print "OK</li>\n";
+ ## 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...";
+ $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";
+ dieout("</ul>");
+ }
+ print "OK</li>";
+
+ ## Will the current locale work? Can we force it to?
+ print "<li>Verifying tsearch2 locale with $ctype...";
+ $rows = $this->numRows($res);
+ $resetlocale = 0;
+ if (!$rows) {
+ print "<b>not found</b></li>\n";
+ print "<li>Attempting to set default tsearch2 locale to \"$ctype\"...";
+ $resetlocale = 1;
+ }
+ else {
+ $tsname = pg_fetch_result($res, 0, 0);
+ if ($tsname != 'default') {
+ print "<b>not set to default ($tsname)</b>";
+ print "<li>Attempting to change tsearch2 default locale to \"$ctype\"...";
+ $resetlocale = 1;
+ }
+ }
+ if ($resetlocale) {
+ $SQL = "UPDATE $wgDBts2schema.pg_ts_cfg SET locale = '$ctype' WHERE ts_name = 'default'";
+ $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";
+ dieout("</ul>");
+ }
+ print "OK</li>";
+ }
+
+ ## Final test: try out a simple tsearch2 query
+ $SQL = "SELECT $wgDBts2schema.to_tsvector('default','MediaWiki tsearch2 testing')";
+ $res = $this->doQuery($SQL);
+ if (!$res) {
+ print "<b>FAILED</b>. Specifically, \"$SQL\" did not work.</li>";
+ dieout("</ul>");
+ }
+ print "OK</li>";
+
## Do we have plpgsql installed?
- print "<li>Checking for plpgsql ...";
+ print "<li>Checking for Pl/Pgsql ...";
$SQL = "SELECT 1 FROM pg_catalog.pg_language WHERE lanname = 'plpgsql'";
- $res = $this->doQuery($SQL);
$rows = $this->numRows($this->doQuery($SQL));
if ($rows < 1) {
- print "<b>FAILED</b>. Make sure the language plpgsql is installed for the database <tt>$wgDBname</tt>t</li>";
- ## XXX Better help
- dieout("</ul>");
+ // plpgsql is not installed, but if we have a pg_pltemplate table, we should be able to create it
+ print "not installed. Attempting to install Pl/Pgsql ...";
+ $SQL = "SELECT 1 FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) ".
+ "WHERE relname = 'pg_pltemplate' AND nspname='pg_catalog'";
+ $rows = $this->numRows($this->doQuery($SQL));
+ if ($rows >= 1) {
+ $result = $this->doQuery("CREATE LANGUAGE plpgsql");
+ if (!$result) {
+ print "<b>FAILED</b>. You need to install the language plpgsql in the database <tt>$wgDBname</tt></li>";
+ dieout("</ul>");
+ }
+ }
+ else {
+ print "<b>FAILED</b>. You need to install the language plpgsql in the database <tt>$wgDBname</tt></li>";
+ dieout("</ul>");
+ }
}
print "OK</li>\n";
@@ -115,40 +319,46 @@ class DatabasePostgres extends Database {
print "<li>Creating schema <b>$wgDBmwschema</b> ...";
$result = $this->doQuery("CREATE SCHEMA $wgDBmwschema");
if (!$result) {
- print "FAILED.</li>\n";
- return false;
+ print "<b>FAILED</b>.</li>\n";
+ dieout("</ul>");
}
- print "ok</li>\n";
+ print "OK</li>\n";
}
else if ($result != $user) {
- print "<li>Schema <b>$wgDBmwschema</b> exists but is not owned by <b>$user</b>. Not ideal.</li>\n";
+ print "<li>Schema \"$wgDBmwschema\" exists but is not owned by \"$user\". Not ideal.</li>\n";
}
else {
- print "<li>Schema <b>$wgDBmwschema</b> exists and is owned by <b>$user ($result)</b>. Excellent.</li>\n";
+ print "<li>Schema \"$wgDBmwschema\" exists and is owned by \"$user\". Excellent.</li>\n";
}
## Fix up the search paths if needed
- print "<li>Setting the search path for user <b>$user</b> ...";
- $path = "$wgDBmwschema";
+ print "<li>Setting the search path for user \"$user\" ...";
+ $path = $this->quote_ident($wgDBmwschema);
if ($wgDBts2schema !== $wgDBmwschema)
- $path .= ", $wgDBts2schema";
+ $path .= ", ". $this->quote_ident($wgDBts2schema);
if ($wgDBmwschema !== 'public' and $wgDBts2schema !== 'public')
$path .= ", public";
- $SQL = "ALTER USER $user SET search_path = $path";
+ $SQL = "ALTER USER $safeuser SET search_path = $path";
$result = pg_query($this->mConn, $SQL);
if (!$result) {
- print "FAILED.</li>\n";
- return false;
+ print "<b>FAILED</b>.</li>\n";
+ dieout("</ul>");
}
- print "ok</li>\n";
+ print "OK</li>\n";
## Set for the rest of this session
$SQL = "SET search_path = $path";
$result = pg_query($this->mConn, $SQL);
if (!$result) {
print "<li>Failed to set search_path</li>\n";
- return false;
+ dieout("</ul>");
}
define( "POSTGRES_SEARCHPATH", $path );
+ }}
+
+ global $wgCommandLineMode;
+ ## If called from the command-line (e.g. importDump), only show errors
+ if ($wgCommandLineMode) {
+ $this->doQuery("SET client_min_messages = 'ERROR'");
}
return $this->mConn;
@@ -177,7 +387,7 @@ class DatabasePostgres extends Database {
function freeResult( $res ) {
if ( !@pg_free_result( $res ) ) {
- throw new DBUnexpectedError($this, "Unable to free PostgreSQL result\n" );
+ throw new DBUnexpectedError($this, "Unable to free Postgres result\n" );
}
}
@@ -228,7 +438,9 @@ class DatabasePostgres extends Database {
return "No database connection";
}
}
- function lastErrno() { return 1; }
+ function lastErrno() {
+ return pg_last_error() ? 1 : 0;
+ }
function affectedRows() {
return pg_affected_rows( $this->mLastResult );
@@ -266,7 +478,7 @@ class DatabasePostgres extends Database {
}
function insert( $table, $a, $fname = 'Database::insert', $options = array() ) {
- # PostgreSQL doesn't support options
+ # Postgres doesn't support options
# We have a go at faking one of them
# TODO: DELAYED, LOW_PRIORITY
@@ -295,16 +507,12 @@ class DatabasePostgres extends Database {
}
function tableName( $name ) {
- # Replace backticks into double quotes
- $name = strtr($name,'`','"');
-
- # Now quote PG reserved keywords
+ # Replace reserved words with better ones
switch( $name ) {
case 'user':
- case 'old':
- case 'group':
- return '"' . $name . '"';
-
+ return 'mwuser';
+ case 'text':
+ return 'pagecontent';
default:
return $name;
}
@@ -323,15 +531,14 @@ class DatabasePostgres extends Database {
}
/**
- * USE INDEX clause
- * PostgreSQL doesn't have them and returns ""
+ * Postgres does not have a "USE INDEX" clause, so return an empty string
*/
function useIndexClause( $index ) {
return '';
}
# REPLACE query wrapper
- # PostgreSQL simulates this with a DELETE followed by INSERT
+ # Postgres 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
@@ -433,7 +640,7 @@ class DatabasePostgres extends Database {
/**
* Returns an SQL expression for a simple conditional.
- * Uses CASE on PostgreSQL.
+ * Uses CASE on Postgres
*
* @param string $cond SQL expression which will result in a boolean value
* @param string $trueVal SQL expression to return if true
@@ -449,9 +656,8 @@ class DatabasePostgres extends Database {
return false;
}
- # Return DB-style timestamp used for MySQL schema
function timestamp( $ts=0 ) {
- return wfTimestamp(TS_DB,$ts);
+ return wfTimestamp(TS_POSTGRES,$ts);
}
/**
@@ -499,7 +705,8 @@ class DatabasePostgres extends Database {
$etable = preg_replace("/'/", "''", $table);
$eschema = preg_replace("/'/", "''", $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'";
+ . "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
+ . "AND c.relkind IN ('r','v')";
$res = $this->query( $SQL );
$count = $res ? pg_num_rows($res) : 0;
if ($res)
@@ -563,9 +770,28 @@ class DatabasePostgres extends Database {
return $sql;
}
- function update_interwiki() {
+ function setup_database() {
+ global $wgVersion, $wgDBmwschema, $wgDBts2schema, $wgDBport;
+
+ dbsource( "../maintenance/postgres/tables.sql", $this);
+
+ ## Update version information
+ $mwv = $this->addQuotes($wgVersion);
+ $pgv = $this->addQuotes($this->getServerVersion());
+ $pgu = $this->addQuotes($this->mUser);
+ $mws = $this->addQuotes($wgDBmwschema);
+ $tss = $this->addQuotes($wgDBts2schema);
+ $pgp = $this->addQuotes($wgDBport);
+ $dbn = $this->addQuotes($this->mDBname);
+ $ctype = pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0);
+
+ $SQL = "UPDATE mediawiki_version SET mw_version=$mwv, pg_version=$pgv, pg_user=$pgu, ".
+ "mw_schema = $mws, ts2_schema = $tss, pg_port=$pgp, pg_dbname=$dbn, ".
+ "ctype = '$ctype' ".
+ "WHERE type = 'Creation'";
+ $this->query($SQL);
+
## Avoid the non-standard "REPLACE INTO" syntax
- ## Called by config/index.php
$f = fopen( "../maintenance/interwiki.sql", 'r' );
if ($f == false ) {
dieout( "<li>Could not find the interwiki.sql file");
@@ -604,6 +830,10 @@ class DatabasePostgres extends Database {
return "E'" . pg_escape_string($s) . "'";
}
+ function quote_ident( $s ) {
+ return '"' . preg_replace( '/"/', '""', $s) . '"';
+ }
+
}
?>
diff --git a/includes/DateFormatter.php b/includes/DateFormatter.php
index 02acac73..dc077fdc 100644
--- a/includes/DateFormatter.php
+++ b/includes/DateFormatter.php
@@ -1,25 +1,11 @@
<?php
/**
- * Contain things
- * @todo document
+ * Date formatter, recognises dates in plain text and formats them accoding to user preferences.
+ *
* @package MediaWiki
* @subpackage Parser
*/
-/** */
-define('DF_ALL', -1);
-define('DF_NONE', 0);
-define('DF_MDY', 1);
-define('DF_DMY', 2);
-define('DF_YMD', 3);
-define('DF_ISO1', 4);
-define('DF_LASTPREF', 4);
-define('DF_ISO2', 5);
-define('DF_YDM', 6);
-define('DF_DM', 7);
-define('DF_MD', 8);
-define('DF_LAST', 8);
-
/**
* @todo preferences, OutputPage
* @package MediaWiki
@@ -31,7 +17,20 @@ class DateFormatter
var $monthNames = '', $rxDM, $rxMD, $rxDMY, $rxYDM, $rxMDY, $rxYMD;
var $regexes, $pDays, $pMonths, $pYears;
- var $rules, $xMonths;
+ var $rules, $xMonths, $preferences;
+
+ const ALL = -1;
+ const NONE = 0;
+ const MDY = 1;
+ const DMY = 2;
+ const YMD = 3;
+ const ISO1 = 4;
+ const LASTPREF = 4;
+ const ISO2 = 5;
+ const YDM = 6;
+ const DM = 7;
+ const MD = 8;
+ const LAST = 8;
/**
* @todo document
@@ -55,75 +54,87 @@ class DateFormatter
$this->prxISO2 = '\[\[(-?\d{4})-(\d{2})-(\d{2})]]';
# Real regular expressions
- $this->regexes[DF_DMY] = "/{$this->prxDM} *,? *{$this->prxY}{$this->regexTrail}";
- $this->regexes[DF_YDM] = "/{$this->prxY} *,? *{$this->prxDM}{$this->regexTrail}";
- $this->regexes[DF_MDY] = "/{$this->prxMD} *,? *{$this->prxY}{$this->regexTrail}";
- $this->regexes[DF_YMD] = "/{$this->prxY} *,? *{$this->prxMD}{$this->regexTrail}";
- $this->regexes[DF_DM] = "/{$this->prxDM}{$this->regexTrail}";
- $this->regexes[DF_MD] = "/{$this->prxMD}{$this->regexTrail}";
- $this->regexes[DF_ISO1] = "/{$this->prxISO1}{$this->regexTrail}";
- $this->regexes[DF_ISO2] = "/{$this->prxISO2}{$this->regexTrail}";
+ $this->regexes[self::DMY] = "/{$this->prxDM} *,? *{$this->prxY}{$this->regexTrail}";
+ $this->regexes[self::YDM] = "/{$this->prxY} *,? *{$this->prxDM}{$this->regexTrail}";
+ $this->regexes[self::MDY] = "/{$this->prxMD} *,? *{$this->prxY}{$this->regexTrail}";
+ $this->regexes[self::YMD] = "/{$this->prxY} *,? *{$this->prxMD}{$this->regexTrail}";
+ $this->regexes[self::DM] = "/{$this->prxDM}{$this->regexTrail}";
+ $this->regexes[self::MD] = "/{$this->prxMD}{$this->regexTrail}";
+ $this->regexes[self::ISO1] = "/{$this->prxISO1}{$this->regexTrail}";
+ $this->regexes[self::ISO2] = "/{$this->prxISO2}{$this->regexTrail}";
# Extraction keys
# See the comments in replace() for the meaning of the letters
- $this->keys[DF_DMY] = 'jFY';
- $this->keys[DF_YDM] = 'Y jF';
- $this->keys[DF_MDY] = 'FjY';
- $this->keys[DF_YMD] = 'Y Fj';
- $this->keys[DF_DM] = 'jF';
- $this->keys[DF_MD] = 'Fj';
- $this->keys[DF_ISO1] = 'ymd'; # y means ISO year
- $this->keys[DF_ISO2] = 'ymd';
+ $this->keys[self::DMY] = 'jFY';
+ $this->keys[self::YDM] = 'Y jF';
+ $this->keys[self::MDY] = 'FjY';
+ $this->keys[self::YMD] = 'Y Fj';
+ $this->keys[self::DM] = 'jF';
+ $this->keys[self::MD] = 'Fj';
+ $this->keys[self::ISO1] = 'ymd'; # y means ISO year
+ $this->keys[self::ISO2] = 'ymd';
# Target date formats
- $this->targets[DF_DMY] = '[[F j|j F]] [[Y]]';
- $this->targets[DF_YDM] = '[[Y]], [[F j|j F]]';
- $this->targets[DF_MDY] = '[[F j]], [[Y]]';
- $this->targets[DF_YMD] = '[[Y]] [[F j]]';
- $this->targets[DF_DM] = '[[F j|j F]]';
- $this->targets[DF_MD] = '[[F j]]';
- $this->targets[DF_ISO1] = '[[Y|y]]-[[F j|m-d]]';
- $this->targets[DF_ISO2] = '[[y-m-d]]';
+ $this->targets[self::DMY] = '[[F j|j F]] [[Y]]';
+ $this->targets[self::YDM] = '[[Y]], [[F j|j F]]';
+ $this->targets[self::MDY] = '[[F j]], [[Y]]';
+ $this->targets[self::YMD] = '[[Y]] [[F j]]';
+ $this->targets[self::DM] = '[[F j|j F]]';
+ $this->targets[self::MD] = '[[F j]]';
+ $this->targets[self::ISO1] = '[[Y|y]]-[[F j|m-d]]';
+ $this->targets[self::ISO2] = '[[y-m-d]]';
# Rules
# pref source target
- $this->rules[DF_DMY][DF_MD] = DF_DM;
- $this->rules[DF_ALL][DF_MD] = DF_MD;
- $this->rules[DF_MDY][DF_DM] = DF_MD;
- $this->rules[DF_ALL][DF_DM] = DF_DM;
- $this->rules[DF_NONE][DF_ISO2] = DF_ISO1;
+ $this->rules[self::DMY][self::MD] = self::DM;
+ $this->rules[self::ALL][self::MD] = self::MD;
+ $this->rules[self::MDY][self::DM] = self::MD;
+ $this->rules[self::ALL][self::DM] = self::DM;
+ $this->rules[self::NONE][self::ISO2] = self::ISO1;
+
+ $this->preferences = array(
+ 'default' => self::NONE,
+ 'dmy' => self::DMY,
+ 'mdy' => self::MDY,
+ 'ymd' => self::YMD,
+ 'ISO 8601' => self::ISO1,
+ );
}
/**
* @static
*/
function &getInstance() {
- global $wgDBname, $wgMemc;
+ global $wgMemc;
static $dateFormatter = false;
if ( !$dateFormatter ) {
- $dateFormatter = $wgMemc->get( "$wgDBname:dateformatter" );
+ $dateFormatter = $wgMemc->get( wfMemcKey( 'dateformatter' ) );
if ( !$dateFormatter ) {
$dateFormatter = new DateFormatter;
- $wgMemc->set( "$wgDBname:dateformatter", $dateFormatter, 3600 );
+ $wgMemc->set( wfMemcKey( 'dateformatter' ), $dateFormatter, 3600 );
}
}
return $dateFormatter;
}
/**
- * @param $preference
- * @param $text
+ * @param string $preference User preference
+ * @param string $text Text to reformat
*/
function reformat( $preference, $text ) {
- if ($preference == 'ISO 8601') $preference = 4; # The ISO 8601 option used to be 4
- for ( $i=1; $i<=DF_LAST; $i++ ) {
+ if ( isset( $this->preferences[$preference] ) ) {
+ $preference = $this->preferences[$preference];
+ } else {
+ $preference = self::NONE;
+ }
+ for ( $i=1; $i<=self::LAST; $i++ ) {
$this->mSource = $i;
if ( @$this->rules[$preference][$i] ) {
# Specific rules
$this->mTarget = $this->rules[$preference][$i];
- } elseif ( @$this->rules[DF_ALL][$i] ) {
+ } elseif ( @$this->rules[self::ALL][$i] ) {
# General rules
- $this->mTarget = $this->rules[DF_ALL][$i];
+ $this->mTarget = $this->rules[self::ALL][$i];
} elseif ( $preference ) {
# User preference
$this->mTarget = $preference;
@@ -131,7 +142,7 @@ class DateFormatter
# Default
$this->mTarget = $i;
}
- $text = preg_replace_callback( $this->regexes[$i], 'wfMainDateReplace', $text );
+ $text = preg_replace_callback( $this->regexes[$i], array( &$this, 'replace' ), $text );
}
return $text;
}
@@ -277,12 +288,4 @@ class DateFormatter
}
}
-/**
- * @todo document
- */
-function wfMainDateReplace( $matches ) {
- $df =& DateFormatter::getInstance();
- return $df->replace( $matches );
-}
-
?>
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 1964aaf2..e4ce8e5e 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -32,13 +32,22 @@ require_once( 'includes/SiteConfiguration.php' );
$wgConf = new SiteConfiguration;
/** MediaWiki version number */
-$wgVersion = '1.7.1';
+$wgVersion = '1.8.1';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
-/** Will be same as you set @see $wgSitename */
-$wgMetaNamespace = FALSE;
+/**
+ * Name of the project namespace. If left set to false, $wgSitename will be
+ * used instead.
+ */
+$wgMetaNamespace = false;
+
+/**
+ * Name of the project talk namespace. If left set to false, a name derived
+ * from the name of the project namespace will be used.
+ */
+$wgMetaNamespaceTalk = false;
/** URL of the server. It will be automatically built including https mode */
@@ -115,8 +124,8 @@ $wgStylePath = "{$wgScriptPath}/skins";
$wgStyleDirectory = "{$IP}/skins";
$wgStyleSheetPath = &$wgStylePath;
$wgArticlePath = "{$wgScript}?title=$1";
-$wgUploadPath = "{$wgScriptPath}/upload";
-$wgUploadDirectory = "{$IP}/upload";
+$wgUploadPath = "{$wgScriptPath}/images";
+$wgUploadDirectory = "{$IP}/images";
$wgHashedUploadDirectory = true;
$wgLogo = "{$wgUploadPath}/wiki.png";
$wgFavicon = '/favicon.ico';
@@ -156,7 +165,7 @@ $wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
* Problematic punctuation:
* []{}|# Are needed for link syntax, never enable these
* % Enabled by default, minor problems with path to query rewrite rules, see below
- * + Doesn't work with path to query rewrite rules, corrupted by apache
+ * + Enabled by default, but doesn't work with path to query rewrite rules, corrupted by apache
* ? Enabled by default, but doesn't work with path to PATH_INFO rewrites
*
* All three of these punctuation problems can be avoided by using an alias, instead of a
@@ -169,10 +178,12 @@ $wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
* passed in the query string rather than the path. This is a minor security issue
* because articles can be created such that they are hard to view or edit.
*
+ * In some rare cases you may wish to remove + for compatibility with old links.
+ *
* Theoretically 0x80-0x9F of ISO 8859-1 should be disallowed, but
* this breaks interlanguage links
*/
-$wgLegalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
+$wgLegalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+";
/**
@@ -328,6 +339,10 @@ $wgSharedUploadDBname = false;
$wgSharedUploadDBprefix = '';
/** Cache shared metadata in memcached. Don't do this if the commons wiki is in a different memcached domain */
$wgCacheSharedUploads = true;
+/** Allow for upload to be copied from an URL. Requires Special:Upload?source=web */
+$wgAllowCopyUploads = false;
+/** Max size for uploads, in bytes */
+$wgMaxUploadSize = 1024*1024*100; # 100MB
/**
* Point the upload navigation link to an external URL
@@ -440,7 +455,6 @@ $wgDBconnection = '';
/** Database username */
$wgDBuser = 'wikiuser';
/** Database type
- * "mysql" for working code and "PostgreSQL" for development/broken code
*/
$wgDBtype = "mysql";
/** Search type
@@ -472,7 +486,7 @@ $wgSharedDB = null;
# dbname: Default database name
# user: DB user
# password: DB password
-# type: "mysql" or "pgsql"
+# type: "mysql" or "postgres"
# load: ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0
# groupLoads: array of load ratios, the key is the query group name. A query may belong
# to several groups, the most specific group defined here is used.
@@ -776,6 +790,14 @@ $wgShowSQLErrors = false;
$wgColorErrors = true;
/**
+ * If set to true, uncaught exceptions will print a complete stack trace
+ * to output. This should only be used for debugging, as it may reveal
+ * private information in function parameters due to PHP's backtrace
+ * formatting.
+ */
+$wgShowExceptionDetails = false;
+
+/**
* disable experimental dmoz-like category browsing. Output things like:
* Encyclopedia > Music > Style of Music > Jazz
*/
@@ -895,7 +917,7 @@ $wgGroupPermissions['bot' ]['autoconfirmed'] = true;
$wgGroupPermissions['sysop']['block'] = true;
$wgGroupPermissions['sysop']['createaccount'] = true;
$wgGroupPermissions['sysop']['delete'] = true;
-$wgGroupPermissions['sysop']['deletedhistory'] = true; // can view deleted history entries, but not see or restore the text
+$wgGroupPermissions['sysop']['deletedhistory'] = true; // can view deleted history entries, but not see or restore the text
$wgGroupPermissions['sysop']['editinterface'] = true;
$wgGroupPermissions['sysop']['import'] = true;
$wgGroupPermissions['sysop']['importupload'] = true;
@@ -908,8 +930,9 @@ $wgGroupPermissions['sysop']['trackback'] = true;
$wgGroupPermissions['sysop']['upload'] = true;
$wgGroupPermissions['sysop']['reupload'] = true;
$wgGroupPermissions['sysop']['reupload-shared'] = true;
-$wgGroupPermissions['sysop']['unwatchedpages'] = true;
+$wgGroupPermissions['sysop']['unwatchedpages'] = true;
$wgGroupPermissions['sysop']['autoconfirmed'] = true;
+$wgGroupPermissions['sysop']['upload_by_url'] = true;
// Permission to change users' group assignments
$wgGroupPermissions['bureaucrat']['userrights'] = true;
@@ -1022,6 +1045,9 @@ $wgFileCacheDirectory = "{$wgUploadDirectory}/cache";
*/
$wgUseGzip = false;
+/** Whether MediaWiki should send an ETag header */
+$wgUseETag = false;
+
# Email notification settings
#
@@ -1153,21 +1179,17 @@ $wgTexvc = './math/texvc';
#
# You have to create a 'profiling' table in your database before using
# profiling see maintenance/archives/patch-profiling.sql .
+#
+# To enable profiling, edit StartProfiler.php
-/** Enable for more detailed by-function times in debug log */
-$wgProfiling = false;
/** Only record profiling info for pages that took longer than this */
$wgProfileLimit = 0.0;
/** Don't put non-profiling info into log file */
$wgProfileOnly = false;
/** Log sums from profiling into "profiling" table in db. */
$wgProfileToDatabase = false;
-/** Only profile every n requests when profiling is turned on */
-$wgProfileSampleRate = 1;
/** If true, print a raw call tree instead of per-function report */
$wgProfileCallTree = false;
-/** If not empty, specifies profiler type to load */
-$wgProfilerType = '';
/** Should application server host be put into profiling table */
$wgProfilePerHost = false;
@@ -1251,7 +1273,7 @@ $wgFileBlacklist = array(
# HTML may contain cookie-stealing JavaScript and web bugs
'html', 'htm', 'js', 'jsb',
# PHP scripts may execute arbitrary code on the server
- 'php', 'phtml', 'php3', 'php4', 'phps',
+ 'php', 'phtml', 'php3', 'php4', 'php5', 'phps',
# Other types that may be interpreted by some servers
'shtml', 'jhtml', 'pl', 'py', 'cgi',
# May contain harmful executables for Windows victims
@@ -1280,7 +1302,7 @@ $wgCheckFileExtensions = true;
*/
$wgStrictFileExtensions = true;
-/** Warn if uploaded files are larger than this */
+/** Warn if uploaded files are larger than this (in bytes)*/
$wgUploadSizeWarning = 150 * 1024;
/** For compatibility with old installations set to false */
@@ -1551,21 +1573,53 @@ $wgTidyInternal = function_exists( 'tidy_load_config' );
$wgDefaultSkin = 'monobook';
/**
- * Settings added to this array will override the language globals for the user
- * preferences used by anonymous visitors and newly created accounts. (See names
- * and sample values in languages/Language.php)
+ * 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 = array();
+ *  $wgDefaultUserOptions ['editsection'] = 0;
+ *
+ */
+$wgDefaultUserOptions = array(
+ 'quickbar' => 1,
+ 'underline' => 2,
+ 'cols' => 80,
+ 'rows' => 25,
+ 'searchlimit' => 20,
+ 'contextlines' => 5,
+ 'contextchars' => 50,
+ 'skin' => false,
+ 'math' => 1,
+ 'rcdays' => 7,
+ 'rclimit' => 50,
+ 'wllimit' => 250,
+ 'highlightbroken' => 1,
+ 'stubthreshold' => 0,
+ 'previewontop' => 1,
+ 'editsection' => 1,
+ 'editsectiononrightclick'=> 0,
+ 'showtoc' => 1,
+ 'showtoolbar' => 1,
+ 'date' => 'default',
+ 'imagesize' => 2,
+ 'thumbsize' => 2,
+ 'rememberpassword' => 0,
+ 'enotifwatchlistpages' => 0,
+ 'enotifusertalkpages' => 1,
+ 'enotifminoredits' => 0,
+ 'enotifrevealaddr' => 0,
+ 'shownumberswatching' => 1,
+ 'fancysig' => 0,
+ 'externaleditor' => 0,
+ 'externaldiff' => 0,
+ 'showjumplinks' => 1,
+ 'numberheadings' => 0,
+ 'uselivepreview' => 0,
+ 'watchlistdays' => 3.0,
+);
/** Whether or not to allow and use real name fields. Defaults to true. */
$wgAllowRealName = true;
-/** Use XML parser? */
-$wgUseXMLparser = false ;
-
/*****************************************************************************
* Extensions
*/
@@ -2072,6 +2126,14 @@ $wgExternalServers = array();
$wgDefaultExternalStore = false;
/**
+ * Revision text may be cached in $wgMemc to reduce load on external storage
+ * servers and object extraction overhead for frequently-loaded revisions.
+ *
+ * Set to 0 to disable, or number of seconds before cache expiry.
+ */
+$wgRevisionCacheExpiry = 0;
+
+/**
* list of trusted media-types and mime types.
* Use the MEDIATYPE_xxx constants to represent media types.
* This list is used by Image::isSafeFile
@@ -2144,14 +2206,22 @@ $wgUpdateRowsPerJob = 500;
$wgUpdateRowsPerQuery = 10;
/**
- * Enable use of AJAX features, currently auto suggestion for the search bar
+ * Enable AJAX framework
*/
$wgUseAjax = false;
/**
- * List of Ajax-callable functions
+ * Enable auto suggestion for the search bar
+ * Requires $wgUseAjax to be true too.
+ * Causes wfSajaxSearch to be added to $wgAjaxExportList
*/
-$wgAjaxExportList = array( 'wfSajaxSearch' );
+$wgAjaxSearch = false;
+
+/**
+ * List of Ajax-callable functions.
+ * Extensions acting as Ajax callbacks must register here
+ */
+$wgAjaxExportList = array( );
/**
* Allow DISPLAYTITLE to change title display
@@ -2186,4 +2256,39 @@ $wgContentNamespaces = array( NS_MAIN );
*/
$wgMaxShellMemory = 102400;
+/**
+ * Maximum file size created by shell processes under linux, in KB
+ * ImageMagick convert for example can be fairly hungry for scratch space
+ */
+$wgMaxShellFileSize = 102400;
+
+/**
+ * DJVU settings
+ * Path of the djvutoxml executable
+ * Enable this and $wgDjvuRenderer to enable djvu rendering
+ */
+# $wgDjvuToXML = 'djvutoxml';
+$wgDjvuToXML = null;
+
+/**
+ * Path of the ddjvu DJVU renderer
+ * Enable this and $wgDjvuToXML 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
+ */
+$wgDjvuPostProcessor = 'ppmtojpeg';
+
+/**
+* Enable direct access to the data API
+* through api.php
+*/
+$wgEnableAPI = false;
+$wgEnableWriteAPI = false;
+
?>
diff --git a/includes/Defines.php b/includes/Defines.php
index 9ff8303b..40727485 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -20,6 +20,17 @@ define( 'DBO_DEFAULT', 16 );
define( 'DBO_PERSISTENT', 32 );
/**#@-*/
+# Valid database indexes
+# Operation-based indexes
+define( 'DB_SLAVE', -1 ); # Read from the slave (or only server)
+define( 'DB_MASTER', -2 ); # Write to master (or only server)
+define( 'DB_LAST', -3 ); # Whatever database was used last
+
+# Obsolete aliases
+define( 'DB_READ', -1 );
+define( 'DB_WRITE', -2 );
+
+
/**#@+
* Virtual namespaces; don't appear in the page database
*/
@@ -75,10 +86,8 @@ define( 'MW_MATH_MATHML', 5 );
/**#@-*/
/**
- * User rights management
- * a big array of string defining a right, that's how they are saved in the
- * database.
- * @todo Is this necessary?
+ * User rights list
+ * @deprecated
*/
$wgAvailableRights = array(
'block',
@@ -108,6 +117,7 @@ define( 'CACHE_NONE', 0 ); // Do not cache
define( 'CACHE_DB', 1 ); // Store cache objects in the DB
define( 'CACHE_MEMCACHED', 2 ); // MemCached, must specify servers in $wgMemCacheServers
define( 'CACHE_ACCEL', 3 ); // eAccelerator or Turck, whichever is available
+define( 'CACHE_DBA', 4 ); // Use PHP's DBA extension to store in a DBM-style database
/**#@-*/
@@ -151,10 +161,15 @@ define( 'ALF_NO_BLOCK_LOCK', 8 );
* Date format selectors; used in user preference storage and by
* Language::date() and co.
*/
-define( 'MW_DATE_DEFAULT', '0' );
+/*define( 'MW_DATE_DEFAULT', '0' );
define( 'MW_DATE_MDY', '1' );
define( 'MW_DATE_DMY', '2' );
define( 'MW_DATE_YMD', '3' );
+define( 'MW_DATE_ISO', 'ISO 8601' );*/
+define( 'MW_DATE_DEFAULT', 'default' );
+define( 'MW_DATE_MDY', 'mdy' );
+define( 'MW_DATE_DMY', 'dmy' );
+define( 'MW_DATE_YMD', 'ymd' );
define( 'MW_DATE_ISO', 'ISO 8601' );
/**#@-*/
@@ -180,4 +195,15 @@ define( 'EDIT_FORCE_BOT', 16 );
define( 'EDIT_DEFER_UPDATES', 32 );
/**#@-*/
+/**
+ * Flags for Database::makeList()
+ * These are also available as Database class constants
+ */
+define( 'LIST_COMMA', 0 );
+define( 'LIST_AND', 1 );
+define( 'LIST_SET', 2 );
+define( 'LIST_NAMES', 3);
+define( 'LIST_OR', 4);
+
+
?>
diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php
index 741b7199..448bcb5d 100644
--- a/includes/DifferenceEngine.php
+++ b/includes/DifferenceEngine.php
@@ -5,10 +5,6 @@
* @subpackage DifferenceEngine
*/
-/** */
-define( 'MAX_DIFF_LINE', 10000 );
-define( 'MAX_DIFF_XREF_LENGTH', 10000 );
-
/**
* @todo document
* @public
@@ -188,7 +184,7 @@ CONTROL;
$wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
if( !$this->mNewRev->isCurrent() ) {
- $oldEditSectionSetting = $wgOut->mParserOptions->setEditSection( false );
+ $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
}
$this->loadNewText();
@@ -198,7 +194,7 @@ CONTROL;
$wgOut->addSecondaryWikiText( $this->mNewtext );
if( !$this->mNewRev->isCurrent() ) {
- $wgOut->mParserOptions->setEditSection( $oldEditSectionSetting );
+ $wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
}
wfProfileOut( $fname );
@@ -301,7 +297,7 @@ CONTROL;
* Returns false on error
*/
function getDiffBody() {
- global $wgMemc, $wgDBname;
+ global $wgMemc;
$fname = 'DifferenceEngine::getDiffBody';
wfProfileIn( $fname );
@@ -309,7 +305,7 @@ CONTROL;
$key = false;
if ( $this->mOldid && $this->mNewid ) {
// Try cache
- $key = "$wgDBname:diff:oldid:{$this->mOldid}:newid:{$this->mNewid}";
+ $key = wfMemcKey( 'diff', 'oldid', $this->mOldid, 'newid', $this->mNewid );
$difftext = $wgMemc->get( $key );
if ( $difftext ) {
wfIncrStats( 'diff_cache_hit' );
@@ -411,8 +407,8 @@ CONTROL;
# Native PHP diff
$ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
$nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
- $diffs =& new Diff( $ota, $nta );
- $formatter =& new TableDiffFormatter();
+ $diffs = new Diff( $ota, $nta );
+ $formatter = new TableDiffFormatter();
return $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) );
}
@@ -724,6 +720,8 @@ class _DiffOp_Change extends _DiffOp {
*/
class _DiffEngine
{
+ const MAX_XREF_LENGTH = 10000;
+
function diff ($from_lines, $to_lines) {
$fname = '_DiffEngine::diff';
wfProfileIn( $fname );
@@ -821,7 +819,7 @@ class _DiffEngine
* Returns the whole line if it's small enough, or the MD5 hash otherwise
*/
function _line_hash( $line ) {
- if ( strlen( $line ) > MAX_DIFF_XREF_LENGTH ) {
+ if ( strlen( $line ) > self::MAX_XREF_LENGTH ) {
return md5( $line );
} else {
return $line;
@@ -1576,6 +1574,8 @@ class _HWLDF_WordAccumulator {
*/
class WordLevelDiff extends MappedDiff
{
+ const MAX_LINE_LENGTH = 10000;
+
function WordLevelDiff ($orig_lines, $closing_lines) {
$fname = 'WordLevelDiff::WordLevelDiff';
wfProfileIn( $fname );
@@ -1604,7 +1604,7 @@ class WordLevelDiff extends MappedDiff
$words[] = "\n";
$stripped[] = "\n";
}
- if ( strlen( $line ) > MAX_DIFF_LINE ) {
+ if ( strlen( $line ) > self::MAX_LINE_LENGTH ) {
$words[] = $line;
$stripped[] = $line;
} else {
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index b857fa66..871c563b 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -208,7 +208,23 @@ class DjVuImage {
'resolution' => $resolution,
'gamma' => $gamma / 10.0 );
}
+
+ /**
+ * Return an XML string describing the DjVu image
+ * @return string
+ */
+ function retrieveMetaData() {
+ global $wgDjvuToXML;
+ if ( isset( $wgDjvuToXML ) ) {
+ $cmd = $wgDjvuToXML . ' --without-anno --without-text ' . $this->mFilename;
+ $xml = wfShellExec( $cmd, $retval );
+ } else {
+ $xml = null;
+ }
+ return $xml;
+ }
+
}
-?> \ No newline at end of file
+?>
diff --git a/includes/EditPage.php b/includes/EditPage.php
index d43a1202..a1207d10 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -845,7 +845,7 @@ class EditPage {
$s = wfMsg('editingcomment', $this->mTitle->getPrefixedText() );
} else {
$s = wfMsg('editingsection', $this->mTitle->getPrefixedText() );
- if( !$this->preview && !$this->diff ) {
+ if( !$this->summary && !$this->preview && !$this->diff ) {
preg_match( "/^(=+)(.+)\\1/mi",
$this->textbox1,
$matches );
@@ -942,7 +942,7 @@ class EditPage {
$cancel = $sk->makeKnownLink( $this->mTitle->getPrefixedText(),
wfMsgExt('cancel', array('parseinline')) );
- $edithelpurl = $sk->makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ));
+ $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ));
$edithelp = '<a target="helpwindow" href="'.$edithelpurl.'">'.
htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '.
htmlspecialchars( wfMsg( 'newwindow' ) );
@@ -1199,7 +1199,6 @@ END
$wgOut->addHtml( wfHidden( 'wpAutoSummary', $autosumm ) );
if ( $this->isConflict ) {
- require_once( "DifferenceEngine.php" );
$wgOut->addWikiText( '==' . wfMsg( "yourdiff" ) . '==' );
$de = new DifferenceEngine( $this->mTitle );
@@ -1380,11 +1379,6 @@ END
wfProfileOut( $fname );
return $previewhead;
} else {
- # if user want to see preview when he edit an article
- if( $wgUser->getOption('previewonfirst') and ($this->textbox1 == '')) {
- $this->textbox1 = $this->getContent();
- }
-
$toparse = $this->textbox1;
# If we're adding a comment, we need to show the
@@ -1416,15 +1410,21 @@ END
# If the user made changes, preserve them when showing the markup
# (This happens when a user is blocked during edit, for instance)
$first = $this->firsttime || ( !$this->save && $this->textbox1 == '' );
- $source = $first ? $this->getContent() : $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
- $rows = $wgUser->getOption( 'rows' );
- $cols = $wgUser->getOption( 'cols' );
- $attribs = array( 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $cols, 'rows' => $rows, 'readonly' => 'readonly' );
- $wgOut->addHtml( '<hr />' );
- $wgOut->addWikiText( wfMsg( $first ? 'blockedoriginalsource' : 'blockededitsource', $this->mTitle->getPrefixedText() ) );
- $wgOut->addHtml( wfElement( 'textarea', $attribs, $source ) );
+ if( $source !== false ) {
+ $rows = $wgUser->getOption( 'rows' );
+ $cols = $wgUser->getOption( 'cols' );
+ $attribs = array( 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $cols, 'rows' => $rows, 'readonly' => 'readonly' );
+ $wgOut->addHtml( '<hr />' );
+ $wgOut->addWikiText( wfMsg( $first ? 'blockedoriginalsource' : 'blockededitsource', $this->mTitle->getPrefixedText() ) );
+ $wgOut->addHtml( wfOpenElement( 'textarea', $attribs ) . htmlspecialchars( $source ) . wfCloseElement( 'textarea' ) );
+ }
}
/**
@@ -1720,7 +1720,6 @@ END
* @return string HTML
*/
function getDiff() {
- require_once( 'DifferenceEngine.php' );
$oldtext = $this->mArticle->fetchContent();
$newtext = $this->mArticle->replaceSection(
$this->section, $this->textbox1, $this->summary, $this->edittime );
diff --git a/includes/Exception.php b/includes/Exception.php
index 1e24515b..56f18d5a 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -3,7 +3,8 @@
class MWException extends Exception
{
function useOutputPage() {
- return !empty( $GLOBALS['wgFullyInitialised'] );
+ return !empty( $GLOBALS['wgFullyInitialised'] ) &&
+ !empty( $GLOBALS['wgArticle'] ) && !empty( $GLOBALS['wgTitle'] );
}
function useMessageCache() {
@@ -19,16 +20,28 @@ class MWException extends Exception
return wfMsgReplaceArgs( $fallback, $args );
}
}
-
+
function getHTML() {
- return '<p>' . htmlspecialchars( $this->getMessage() ) .
- '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
- "</p>\n";
+ global $wgShowExceptionDetails;
+ if( $wgShowExceptionDetails ) {
+ return '<p>' . htmlspecialchars( $this->getMessage() ) .
+ '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
+ "</p>\n";
+ } else {
+ return "<p>Set <b><tt>\$wgShowExceptionDetails = true;</tt></b> " .
+ "in LocalSettings.php to show detailed debugging information.</p>";
+ }
}
function getText() {
- return $this->getMessage() .
- "\nBacktrace:\n" . $this->getTraceAsString() . "\n";
+ global $wgShowExceptionDetails;
+ if( $wgShowExceptionDetails ) {
+ return $this->getMessage() .
+ "\nBacktrace:\n" . $this->getTraceAsString() . "\n";
+ } else {
+ return "<p>Set <tt>\$wgShowExceptionDetails = true;</tt> " .
+ "in LocalSettings.php to show detailed debugging information.</p>";
+ }
}
function getPageTitle() {
@@ -40,6 +53,13 @@ class MWException extends Exception
}
}
+ function getLogMessage() {
+ $file = $this->getFile();
+ $line = $this->getLine();
+ $message = $this->getMessage();
+ return "{$_SERVER['REQUEST_URI']} Exception from line $line of $file: $message";
+ }
+
function reportHTML() {
global $wgOut;
if ( $this->useOutputPage() ) {
@@ -67,6 +87,10 @@ class MWException extends Exception
if ( $wgCommandLineMode ) {
$this->reportText();
} else {
+ $log = $this->getLogMessage();
+ if ( $log ) {
+ wfDebugLog( 'exception', $log );
+ }
$this->reportHTML();
}
}
@@ -93,11 +117,12 @@ class MWException extends Exception
function htmlFooter() {
echo "</body></html>";
- }
+ }
+
}
/**
- * Exception class which takes an HTML error message, and does not
+ * Exception class which takes an HTML error message, and does not
* produce a backtrace. Replacement for OutputPage::fatalError().
*/
class FatalError extends MWException {
@@ -145,11 +170,11 @@ function wfReportException( Exception $e ) {
$e->report();
} catch ( Exception $e2 ) {
// Exception occurred from within exception handler
- // Show a simpler error message for the original exception,
+ // Show a simpler error message for the original exception,
// don't try to invoke report()
$message = "MediaWiki internal error.\n\n" .
- "Original exception: " . $e->__toString() .
- "\n\nException caught inside exception handler: " .
+ "Original exception: " . $e->__toString() .
+ "\n\nException caught inside exception handler: " .
$e2->__toString() . "\n";
if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) {
@@ -165,7 +190,7 @@ function wfReportException( Exception $e ) {
/**
* Exception handler which simulates the appropriate catch() handling:
- *
+ *
* try {
* ...
* } catch ( MWException $e ) {
@@ -181,8 +206,7 @@ function wfExceptionHandler( $e ) {
// Final cleanup, similar to wfErrorExit()
if ( $wgFullyInitialised ) {
try {
- wfProfileClose();
- logProfilingData(); // uses $wgRequest, hence the $wgFullyInitialised condition
+ wfLogProfilingData(); // uses $wgRequest, hence the $wgFullyInitialised condition
} catch ( Exception $e ) {}
}
diff --git a/includes/Exif.php b/includes/Exif.php
index f9fb9a2c..2ab0feb1 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -293,7 +293,7 @@ class Exif {
);
$this->file = $file;
- $this->basename = basename( $this->file );
+ $this->basename = wfBaseName( $this->file );
$this->makeFlatExifTags();
diff --git a/includes/Export.php b/includes/Export.php
index da92694e..aa70e27b 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -16,46 +16,43 @@
# 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
+
/**
*
* @package MediaWiki
* @subpackage SpecialPage
*/
-/** */
-
-define( 'MW_EXPORT_FULL', 0 );
-define( 'MW_EXPORT_CURRENT', 1 );
-
-define( 'MW_EXPORT_BUFFER', 0 );
-define( 'MW_EXPORT_STREAM', 1 );
-
-define( 'MW_EXPORT_TEXT', 0 );
-define( 'MW_EXPORT_STUB', 1 );
-
-
-/**
- * @package MediaWiki
- * @subpackage 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;
+
+ const BUFFER = 0;
+ const STREAM = 1;
+
+ const TEXT = 0;
+ const STUB = 1;
+
/**
- * If using MW_EXPORT_STREAM to stream a large amount of data,
+ * If using WikiExporter::STREAM to stream a large amount of data,
* provide a database connection which is not managed by
* LoadBalancer to read from: some history blob types will
* make additional queries to pull source data while the
* main query is still running.
*
* @param Database $db
- * @param int $history one of MW_EXPORT_FULL or MW_EXPORT_CURRENT
- * @param int $buffer one of MW_EXPORT_BUFFER or MW_EXPORT_STREAM
+ * @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 = MW_EXPORT_CURRENT,
- $buffer = MW_EXPORT_BUFFER, $text = MW_EXPORT_TEXT ) {
+ function WikiExporter( &$db, $history = WikiExporter::CURRENT,
+ $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
$this->db =& $db;
$this->history = $history;
$this->buffer = $buffer;
@@ -168,20 +165,42 @@ class WikiExporter {
$revision = $this->db->tableName( 'revision' );
$text = $this->db->tableName( 'text' );
- if( $this->history == MW_EXPORT_FULL ) {
+ $order = 'ORDER BY page_id';
+ $limit = '';
+
+ if( $this->history == WikiExporter::FULL ) {
$join = 'page_id=rev_page';
- } elseif( $this->history == MW_EXPORT_CURRENT ) {
+ } elseif( $this->history == WikiExporter::CURRENT ) {
if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
$this->do_list_authors ( $page , $revision , $cond );
}
$join = 'page_id=rev_page AND page_latest=rev_id';
+ } elseif ( is_array( $this->history ) ) {
+ $join = 'page_id=rev_page';
+ if ( $this->history['dir'] == 'asc' ) {
+ $op = '>';
+ $order .= ', rev_timestamp';
+ } else {
+ $op = '<';
+ $order .= ', rev_timestamp DESC';
+ }
+ if ( !empty( $this->history['offset'] ) ) {
+ $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
+ $this->db->timestamp( $this->history['offset'] ) );
+ }
+ if ( !empty( $this->history['limit'] ) ) {
+ $limitNum = intval( $this->history['limit'] );
+ if ( $limitNum > 0 ) {
+ $limit = "LIMIT $limitNum";
+ }
+ }
} else {
wfProfileOut( $fname );
return new WikiError( "$fname given invalid history dump type." );
}
$where = ( $cond == '' ) ? '' : "$cond AND";
- if( $this->buffer == MW_EXPORT_STREAM ) {
+ if( $this->buffer == WikiExporter::STREAM ) {
$prev = $this->db->bufferResults( false );
}
if( $cond == '' ) {
@@ -193,19 +212,19 @@ class WikiExporter {
$revindex = '';
$straight = '';
}
- if( $this->text == MW_EXPORT_STUB ) {
+ if( $this->text == WikiExporter::STUB ) {
$sql = "SELECT $straight * FROM
$page $pageindex,
$revision $revindex
WHERE $where $join
- ORDER BY page_id";
+ $order $limit";
} else {
$sql = "SELECT $straight * FROM
$page $pageindex,
$revision $revindex,
$text
WHERE $where $join AND rev_text_id=old_id
- ORDER BY page_id";
+ $order $limit";
}
$result = $this->db->query( $sql, $fname );
$wrapper = $this->db->resultObject( $result );
@@ -215,7 +234,7 @@ class WikiExporter {
$this->outputStream( $wrapper );
}
- if( $this->buffer == MW_EXPORT_STREAM ) {
+ if( $this->buffer == WikiExporter::STREAM ) {
$this->db->bufferResults( $prev );
}
diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php
index 21f632ec..14b55fdb 100644
--- a/includes/ExternalEdit.php
+++ b/includes/ExternalEdit.php
@@ -48,7 +48,7 @@ class ExternalEdit {
$extension="wiki";
} elseif($this->mMode=="file") {
$type="Edit file";
- $image = Image::newFromTitle( $this->mTitle );
+ $image = new Image( $this->mTitle );
$img_url = $image->getURL();
if(strpos($img_url,"://")) {
$url = $img_url;
diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php
index f610df80..861a9939 100644
--- a/includes/ExternalStoreDB.php
+++ b/includes/ExternalStoreDB.php
@@ -6,7 +6,6 @@
* DB accessable external objects
*
*/
-require_once( 'LoadBalancer.php' );
/** @package MediaWiki */
diff --git a/includes/FileStore.php b/includes/FileStore.php
index 85aaedfe..35ebd554 100644
--- a/includes/FileStore.php
+++ b/includes/FileStore.php
@@ -36,18 +36,16 @@ class FileStore {
* @fixme Probably only works on MySQL. Abstract to the Database class?
*/
static function lock() {
- $fname = __CLASS__ . '::' . __FUNCTION__;
-
$dbw = wfGetDB( DB_MASTER );
$lockname = $dbw->addQuotes( FileStore::lockName() );
- $result = $dbw->query( "SELECT GET_LOCK($lockname, 5) AS lockstatus", $fname );
+ $result = $dbw->query( "SELECT GET_LOCK($lockname, 5) AS lockstatus", __METHOD__ );
$row = $dbw->fetchObject( $result );
$dbw->freeResult( $result );
if( $row->lockstatus == 1 ) {
return true;
} else {
- wfDebug( "$fname failed to acquire lock\n" );
+ wfDebug( __METHOD__." failed to acquire lock\n" );
return false;
}
}
@@ -56,18 +54,15 @@ class FileStore {
* Release the global file store lock.
*/
static function unlock() {
- $fname = __CLASS__ . '::' . __FUNCTION__;
-
$dbw = wfGetDB( DB_MASTER );
$lockname = $dbw->addQuotes( FileStore::lockName() );
- $result = $dbw->query( "SELECT RELEASE_LOCK($lockname)", $fname );
+ $result = $dbw->query( "SELECT RELEASE_LOCK($lockname)", __METHOD__ );
$row = $dbw->fetchObject( $result );
$dbw->freeResult( $result );
}
private static function lockName() {
- global $wgDBname, $wgDBprefix;
- return "MediaWiki.{$wgDBname}.{$wgDBprefix}FileStore";
+ return 'MediaWiki.' . wfWikiID() . '.FileStore';
}
/**
@@ -103,8 +98,6 @@ class FileStore {
}
private function copyFile( $sourcePath, $destPath, $flags=0 ) {
- $fname = __CLASS__ . '::' . __FUNCTION__;
-
if( !file_exists( $sourcePath ) ) {
// Abort! Abort!
throw new FSException( "missing source file '$sourcePath'\n" );
@@ -135,11 +128,11 @@ class FileStore {
wfRestoreWarnings();
if( $ok ) {
- wfDebug( "$fname copied '$sourcePath' to '$destPath'\n" );
+ wfDebug( __METHOD__." copied '$sourcePath' to '$destPath'\n" );
$transaction->addRollback( FSTransaction::DELETE_FILE, $destPath );
} else {
throw new FSException(
- "$fname failed to copy '$sourcePath' to '$destPath'\n" );
+ __METHOD__." failed to copy '$sourcePath' to '$destPath'\n" );
}
}
@@ -239,13 +232,11 @@ class FileStore {
* @return string or false if could not open file or bad extension
*/
static function calculateKey( $path, $extension ) {
- $fname = __CLASS__ . '::' . __FUNCTION__;
-
wfSuppressWarnings();
$hash = sha1_file( $path );
wfRestoreWarnings();
if( $hash === false ) {
- wfDebug( "$fname: couldn't hash file '$path'\n" );
+ wfDebug( __METHOD__.": couldn't hash file '$path'\n" );
return false;
}
@@ -260,7 +251,7 @@ class FileStore {
if( self::validKey( $key ) ) {
return $key;
} else {
- wfDebug( "$fname: generated bad key '$key'\n" );
+ wfDebug( __METHOD__.": generated bad key '$key'\n" );
return false;
}
}
@@ -353,7 +344,6 @@ class FSTransaction {
}
private function apply( $actions ) {
- $fname = __CLASS__ . '::' . __FUNCTION__;
$result = true;
foreach( $actions as $item ) {
list( $action, $path ) = $item;
@@ -362,9 +352,9 @@ class FSTransaction {
$ok = unlink( $path );
wfRestoreWarnings();
if( $ok )
- wfDebug( "$fname: deleting file '$path'\n" );
+ wfDebug( __METHOD__.": deleting file '$path'\n" );
else
- wfDebug( "$fname: failed to delete file '$path'\n" );
+ wfDebug( __METHOD__.": failed to delete file '$path'\n" );
$result = $result && $ok;
}
}
@@ -374,4 +364,4 @@ class FSTransaction {
class FSException extends MWException { }
-?> \ No newline at end of file
+?>
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index e2033486..623f9d3b 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -33,9 +33,10 @@ require_once( 'XmlFunctions.php' );
/**
* Compatibility functions
- * PHP <4.3.x is not actively supported; 4.1.x and 4.2.x might or might not work.
- * <4.1.x will not work, as we use a number of features introduced in 4.1.0
- * such as the new autoglobals.
+ *
+ * We more or less support PHP 5.0.x and up.
+ * Re-implementations of newer functions or functions in non-standard
+ * PHP extensions may be included here.
*/
if( !function_exists('iconv') ) {
# iconv support is not in the default configuration and so may not be present.
@@ -49,22 +50,6 @@ if( !function_exists('iconv') ) {
}
}
-if( !function_exists('file_get_contents') ) {
- # Exists in PHP 4.3.0+
- function file_get_contents( $filename ) {
- return implode( '', file( $filename ) );
- }
-}
-
-if( !function_exists('is_a') ) {
- # Exists in PHP 4.2.0+
- function is_a( $object, $class_name ) {
- return
- (strcasecmp( get_class( $object ), $class_name ) == 0) ||
- is_subclass_of( $object, $class_name );
- }
-}
-
# UTF-8 substr function based on a PHP manual comment
if ( !function_exists( 'mb_substr' ) ) {
function mb_substr( $str, $start ) {
@@ -79,17 +64,6 @@ if ( !function_exists( 'mb_substr' ) ) {
}
}
-if( !function_exists( 'floatval' ) ) {
- /**
- * First defined in PHP 4.2.0
- * @param mixed $var;
- * @return float
- */
- function floatval( $var ) {
- return (float)$var;
- }
-}
-
if ( !function_exists( 'array_diff_key' ) ) {
/**
* Exists in PHP 5.1.0+
@@ -109,39 +83,25 @@ if ( !function_exists( 'array_diff_key' ) ) {
/**
- * Wrapper for clone() for PHP 4, for the moment.
+ * Wrapper for clone(), for compatibility with PHP4-friendly extensions.
* PHP 5 won't let you declare a 'clone' function, even conditionally,
* so it has to be a wrapper with a different name.
*/
function wfClone( $object ) {
- // WARNING: clone() is not a function in PHP 5, so function_exists fails.
- if( version_compare( PHP_VERSION, '5.0' ) < 0 ) {
- return $object;
- } else {
- return clone( $object );
- }
+ return clone( $object );
}
/**
* Where as we got a random seed
- * @var bool $wgTotalViews
*/
$wgRandomSeeded = false;
/**
* Seed Mersenne Twister
- * Only necessary in PHP < 4.2.0
- *
- * @return bool
+ * No-op for compatibility; only necessary in PHP < 4.2.0
*/
function wfSeedRandom() {
- global $wgRandomSeeded;
-
- if ( ! $wgRandomSeeded && version_compare( phpversion(), '4.2.0' ) < 0 ) {
- $seed = hexdec(substr(md5(microtime()),-8)) & 0x7fffffff;
- mt_srand( $seed );
- $wgRandomSeeded = true;
- }
+ /* No-op */
}
/**
@@ -190,13 +150,25 @@ function wfUrlencode ( $s ) {
*/
function wfDebug( $text, $logonly = false ) {
global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage;
+ static $recursion = 0;
# Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) {
return;
}
- if ( isset( $wgOut ) && $wgDebugComments && !$logonly ) {
+ if ( $wgDebugComments && !$logonly ) {
+ if ( !isset( $wgOut ) ) {
+ return;
+ }
+ if ( !StubObject::isRealObject( $wgOut ) ) {
+ if ( $recursion ) {
+ return;
+ }
+ $recursion++;
+ $wgOut->_unstub();
+ $recursion--;
+ }
$wgOut->debug( $text );
}
if ( '' != $wgDebugLogFile && !$wgProfileOnly ) {
@@ -217,11 +189,12 @@ function wfDebug( $text, $logonly = false ) {
* log file is specified, (default true)
*/
function wfDebugLog( $logGroup, $text, $public = true ) {
- global $wgDebugLogGroups, $wgDBname;
+ global $wgDebugLogGroups;
if( $text{strlen( $text ) - 1} != "\n" ) $text .= "\n";
if( isset( $wgDebugLogGroups[$logGroup] ) ) {
$time = wfTimestamp( TS_DB );
- @error_log( "$time $wgDBname: $text", 3, $wgDebugLogGroups[$logGroup] );
+ $wiki = wfWikiID();
+ @error_log( "$time $wiki: $text", 3, $wgDebugLogGroups[$logGroup] );
} else if ( $public === true ) {
wfDebug( $text, true );
}
@@ -243,13 +216,12 @@ function wfLogDBError( $text ) {
/**
* @todo document
*/
-function logProfilingData() {
+function wfLogProfilingData() {
global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
global $wgProfiling, $wgUser;
- $now = wfTime();
-
- $elapsed = $now - $wgRequestTime;
if ( $wgProfiling ) {
+ $now = wfTime();
+ $elapsed = $now - $wgRequestTime;
$prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
$forward = '';
if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
@@ -260,7 +232,8 @@ function logProfilingData() {
$forward .= ' from ' . $_SERVER['HTTP_FROM'];
if( $forward )
$forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
- if( is_object($wgUser) && $wgUser->isAnon() )
+ // Don't unstub $wgUser at this late stage just for statistics purposes
+ if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() )
$forward .= ' anon';
$log = sprintf( "%s\t%04.3f\t%s\n",
gmdate( 'YmdHis' ), $elapsed,
@@ -418,12 +391,12 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform =
function wfMsgWeirdKey ( $key ) {
$subsource = str_replace ( ' ' , '_' , $key ) ;
$source = wfMsgForContentNoTrans( $subsource ) ;
- if ( $source == "&lt;{$subsource}&gt;" ) {
+ if ( wfEmptyMsg( $subsource, $source) ) {
# Try again with first char lower case
$subsource = strtolower ( substr ( $subsource , 0 , 1 ) ) . substr ( $subsource , 1 ) ;
$source = wfMsgForContentNoTrans( $subsource ) ;
}
- if ( $source == "&lt;{$subsource}&gt;" ) {
+ if ( wfEmptyMsg( $subsource, $source ) ) {
# Didn't work either, return blank text
$source = "" ;
}
@@ -439,7 +412,7 @@ function wfMsgWeirdKey ( $key ) {
* @private
*/
function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
- global $wgParser, $wgMsgParserOptions, $wgContLang, $wgMessageCache, $wgLang;
+ global $wgParser, $wgContLang, $wgMessageCache, $wgLang;
if ( is_object( $wgMessageCache ) )
$transstat = $wgMessageCache->getTransform();
@@ -466,7 +439,7 @@ function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
if($message === false)
$message = Language::getMessage($key);
if ( $transform && strstr( $message, '{{' ) !== false ) {
- $message = $wgParser->transformMsg($message, $wgMsgParserOptions);
+ $message = $wgParser->transformMsg($message, $wgMessageCache->getParserOptions() );
}
}
@@ -621,8 +594,7 @@ function wfAbruptExit( $error = false ){
wfDebug('WARNING: Abrupt exit\n');
}
- wfProfileClose();
- logProfilingData();
+ wfLogProfilingData();
if ( !$error ) {
$wgLoadBalancer->closeAll();
@@ -867,8 +839,8 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
*/
function wfEscapeWikiText( $text ) {
$text = str_replace(
- array( '[', '|', '\'', 'ISBN ' , '://' , "\n=", '{{' ),
- array( '&#91;', '&#124;', '&#39;', 'ISBN&#32;', '&#58;//' , "\n&#61;", '&#123;&#123;' ),
+ array( '[', '|', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ),
+ array( '&#91;', '&#124;', '&#39;', 'ISBN&#32;', 'RFC&#32;', '&#58;//', "\n&#61;", '&#123;&#123;' ),
htmlspecialchars($text) );
return $text;
}
@@ -1296,6 +1268,11 @@ define('TS_EXIF', 5);
define('TS_ORACLE', 6);
/**
+ * Postgres format time.
+ */
+define('TS_POSTGRES', 7);
+
+/**
* @param mixed $outputtype A timestamp in one of the supported formats, the
* function will autodetect which format is supplied
* and act accordingly.
@@ -1329,6 +1306,10 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
# TS_ISO_8601
$uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
(int)$da[2],(int)$da[3],(int)$da[1]);
+ } elseif (preg_match("/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)[\+\- ](\d\d)$/",$ts,$da)) {
+ # TS_POSTGRES
+ $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
+ (int)$da[2],(int)$da[3],(int)$da[1]);
} else {
# Bogus value; fall back to the epoch...
wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n");
@@ -1352,6 +1333,8 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
return gmdate( 'D, d M Y H:i:s', $uts ) . ' GMT';
case TS_ORACLE:
return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00';
+ case TS_POSTGRES:
+ return gmdate( 'Y-m-d H:i:s', $uts) . ' GMT';
default:
throw new MWException( 'wfTimestamp() called with illegal output type.');
}
@@ -1395,18 +1378,18 @@ function swap( &$x, &$y ) {
}
function wfGetCachedNotice( $name ) {
- global $wgOut, $parserMemc, $wgDBname;
+ global $wgOut, $parserMemc;
$fname = 'wfGetCachedNotice';
wfProfileIn( $fname );
$needParse = false;
$notice = wfMsgForContent( $name );
- if( $notice == '&lt;'. $name . ';&gt' || $notice == '-' ) {
+ if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) {
wfProfileOut( $fname );
return( false );
}
- $cachedNotice = $parserMemc->get( $wgDBname . ':' . $name );
+ $cachedNotice = $parserMemc->get( wfMemcKey( $name ) );
if( is_array( $cachedNotice ) ) {
if( md5( $notice ) == $cachedNotice['hash'] ) {
$notice = $cachedNotice['html'];
@@ -1420,7 +1403,7 @@ function wfGetCachedNotice( $name ) {
if( $needParse ) {
if( is_object( $wgOut ) ) {
$parsed = $wgOut->parse( $notice );
- $parserMemc->set( $wgDBname . ':' . $name, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
+ $parserMemc->set( wfMemcKey( $name ), array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
$notice = $parsed;
} else {
wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' );
@@ -1480,37 +1463,14 @@ function wfGetSiteNotice() {
return $siteNotice;
}
-/** Global singleton instance of MimeMagic. This is initialized on demand,
-* please always use the wfGetMimeMagic() function to get the instance.
-*
-* @private
-*/
-$wgMimeMagic= NULL;
-
-/** Factory functions for the global MimeMagic object.
-* This function always returns the same singleton instance of MimeMagic.
-* That objects will be instantiated on the first call to this function.
-* If needed, the MimeMagic.php file is automatically included by this function.
-* @return MimeMagic the global MimeMagic objects.
-*/
+/**
+ * BC wrapper for MimeMagic::singleton()
+ * @deprecated
+ */
function &wfGetMimeMagic() {
- global $wgMimeMagic;
-
- if (!is_null($wgMimeMagic)) {
- return $wgMimeMagic;
- }
-
- if (!class_exists("MimeMagic")) {
- #include on demand
- require_once("MimeMagic.php");
- }
-
- $wgMimeMagic= new MimeMagic();
-
- return $wgMimeMagic;
+ return MimeMagic::singleton();
}
-
/**
* Tries to get the system directory for temporary files.
* The TMPDIR, TMP, and TEMP environment variables are checked in sequence,
@@ -1582,8 +1542,8 @@ function wfMkdirParents( $fullDir, $mode = 0777 ) {
* Increment a statistics counter
*/
function wfIncrStats( $key ) {
- global $wgDBname, $wgMemc;
- $key = "$wgDBname:stats:$key";
+ global $wgMemc;
+ $key = wfMemcKey( 'stats', $key );
if ( is_null( $wgMemc->incr( $key ) ) ) {
$wgMemc->add( $key, 1 );
}
@@ -1689,7 +1649,7 @@ function wfUrlProtocols() {
* @return collected stdout as a string (trailing newlines stripped)
*/
function wfShellExec( $cmd, &$retval=null ) {
- global $IP, $wgMaxShellMemory;
+ global $IP, $wgMaxShellMemory, $wgMaxShellFileSize;
if( ini_get( 'safe_mode' ) ) {
wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
@@ -1700,11 +1660,12 @@ function wfShellExec( $cmd, &$retval=null ) {
if ( php_uname( 's' ) == 'Linux' ) {
$time = ini_get( 'max_execution_time' );
$mem = intval( $wgMaxShellMemory );
+ $filesize = intval( $wgMaxShellFileSize );
if ( $time > 0 && $mem > 0 ) {
- $script = "$IP/bin/ulimit.sh";
+ $script = "$IP/bin/ulimit-tvf.sh";
if ( is_executable( $script ) ) {
- $cmd = escapeshellarg( $script ) . " $time $mem $cmd";
+ $cmd = escapeshellarg( $script ) . " $time $mem $filesize $cmd";
}
}
} elseif ( php_uname( 's' ) == 'Windows NT' ) {
@@ -2002,4 +1963,104 @@ function wfIsLocalURL( $url ) {
return Http::isLocalURL( $url );
}
+/**
+ * Initialise php session
+ */
+function wfSetupSession() {
+ global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
+ if( $wgSessionsInMemcached ) {
+ require_once( 'MemcachedSessions.php' );
+ } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
+ # If it's left on 'user' or another setting from another
+ # application, it will end up failing. Try to recover.
+ ini_set ( 'session.save_handler', 'files' );
+ }
+ session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
+ session_cache_limiter( 'private, must-revalidate' );
+ @session_start();
+}
+
+/**
+ * Get an object from the precompiled serialized directory
+ *
+ * @return mixed The variable on success, false on failure
+ */
+function wfGetPrecompiledData( $name ) {
+ global $IP;
+
+ $file = "$IP/serialized/$name";
+ if ( file_exists( $file ) ) {
+ $blob = file_get_contents( $file );
+ if ( $blob ) {
+ return unserialize( $blob );
+ }
+ }
+ return false;
+}
+
+function wfGetCaller( $level = 2 ) {
+ $backtrace = debug_backtrace();
+ if ( isset( $backtrace[$level] ) ) {
+ if ( isset( $backtrace[$level]['class'] ) ) {
+ $caller = $backtrace[$level]['class'] . '::' . $backtrace[$level]['function'];
+ } else {
+ $caller = $backtrace[$level]['function'];
+ }
+ } else {
+ $caller = 'unknown';
+ }
+ return $caller;
+}
+
+/** Return a string consisting all callers in stack, somewhat useful sometimes for profiling specific points */
+function wfGetAllCallers() {
+ return implode('/', array_map(
+ create_function('$frame','
+ return isset( $frame["class"] )?
+ $frame["class"]."::".$frame["function"]:
+ $frame["function"];
+ '),
+ array_reverse(debug_backtrace())));
+}
+
+/**
+ * Get a cache key
+ */
+function wfMemcKey( /*... */ ) {
+ global $wgDBprefix, $wgDBname;
+ $args = func_get_args();
+ if ( $wgDBprefix ) {
+ $key = "$wgDBname-$wgDBprefix:" . implode( ':', $args );
+ } else {
+ $key = $wgDBname . ':' . implode( ':', $args );
+ }
+ return $key;
+}
+
+/**
+ * Get a cache key for a foreign DB
+ */
+function wfForeignMemcKey( $db, $prefix /*, ... */ ) {
+ $args = array_slice( func_get_args(), 2 );
+ if ( $prefix ) {
+ $key = "$db-$prefix:" . implode( ':', $args );
+ } else {
+ $key = $db . ':' . implode( ':', $args );
+ }
+ return $key;
+}
+
+/**
+ * Get an ASCII string identifying this wiki
+ * This is used as a prefix in memcached keys
+ */
+function wfWikiID() {
+ global $wgDBprefix, $wgDBname;
+ if ( $wgDBprefix ) {
+ return "$wgDBname-$wgDBprefix";
+ } else {
+ return $wgDBname;
+ }
+}
+
?>
diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php
index c3d74b20..3ee85859 100644
--- a/includes/HTMLForm.php
+++ b/includes/HTMLForm.php
@@ -71,7 +71,7 @@ class HTMLForm {
( $checked ? ' checked="checked"' : '' ) . " />" . wfMsg( $this->mName.'-'.$varname.'-'.$value ) .
"</label></div>\n";
}
- return $this->fieldset( $this->mName.'-'.$varname, $s );
+ return $this->fieldset( $varname, $s );
}
/**
@@ -109,10 +109,11 @@ class HTMLForm {
}
} // end class
-
-// functions used by SpecialUserrights.php
-
/** Build a select with all defined groups
+ *
+ * used by SpecialUserrights.php
+ * @todo move it to there, and don't forget to copy it for SpecialMakesysop.php
+ *
* @param $selectname String: name of this element. Name of form is automaticly prefixed.
* @param $selectmsg String: FIXME
* @param $selected Array: array of element selected when posted. Only multiples will show them.
@@ -154,24 +155,4 @@ function HTMLSelectGroups($selectname, $selectmsg, $selected=array(), $multiple=
return $out;
}
-/** Build a select with all existent rights
- * @param $selected Array: Names(?) of user rights that should be selected.
- * @return string HTML select.
- */
-function HTMLSelectRights($selected='') {
- global $wgAvailableRights;
- $out = '<select name="editgroup-getrights[]" multiple="multiple">';
- $groupRights = explode(',',$selected);
-
- foreach($wgAvailableRights as $right) {
-
- // check box when right exist
- if(in_array($right, $groupRights)) { $selected = 'selected="selected" '; }
- else { $selected = ''; }
-
- $out .= '<option value="'.$right.'" '.$selected.'>'.$right."</option>\n";
- }
- $out .= "</select>\n";
- return $out;
-}
?>
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index 8f5d3624..357c1d48 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -231,7 +231,6 @@ class HistoryBlobStub {
wfProfileOut( $fname );
return false;
}
- require_once('ExternalStore.php');
$row->old_text=ExternalStore::fetchFromUrl($url);
}
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 4daffaf3..575a28c5 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -64,7 +64,7 @@ function wfRunHooks($event, $args = null) {
if (count($hook) < 1) {
throw new MWException("Empty array in hooks for " . $event . "\n");
} else if (is_object($hook[0])) {
- $object =& $wgHooks[$event][$index][0];
+ $object = $wgHooks[$event][$index][0];
if (count($hook) < 2) {
$method = "on" . $event;
} else {
@@ -87,7 +87,7 @@ function wfRunHooks($event, $args = null) {
} else if (is_string($hook)) { # functions look like strings, too
$func = $hook;
} else if (is_object($hook)) {
- $object =& $wgHooks[$event][$index];
+ $object = $wgHooks[$event][$index];
$method = "on" . $event;
} else {
throw new MWException("Unknown datatype in hooks for " . $event . "\n");
@@ -101,18 +101,18 @@ function wfRunHooks($event, $args = null) {
$hook_args = $args;
}
-
if ( isset( $object ) ) {
$func = get_class( $object ) . '::' . $method;
+ $callback = array( $object, $method );
+ } elseif ( false !== ( $pos = strpos( '::', $func ) ) ) {
+ $callback = array( substr( $func, 0, $pos ), substr( $func, $pos + 2 ) );
+ } else {
+ $callback = $func;
}
/* Call the hook. */
wfProfileIn( $func );
- if( isset( $object ) ) {
- $retval = call_user_func_array(array(&$object, $method), $hook_args);
- } else {
- $retval = call_user_func_array($func, $hook_args);
- }
+ $retval = call_user_func_array( $callback, $hook_args );
wfProfileOut( $func );
/* String return is an error; false return means stop processing. */
diff --git a/includes/IP.php b/includes/IP.php
new file mode 100644
index 00000000..f3ff3427
--- /dev/null
+++ b/includes/IP.php
@@ -0,0 +1,211 @@
+<?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
+ */
+
+// Some regex definition to "play" with IP address and IP address blocks
+
+// 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]\d|1?\d{1,2})');
+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)
+define( 'RE_IP_PREFIX' , '(3[0-2]|[12]?\d)');
+define( 'RE_IP_BLOCK', RE_IP_ADD . '\/' . RE_IP_PREFIX);
+
+class IP {
+
+ /**
+ * Validate an IP address.
+ * @return boolean True if it is valid.
+ */
+ public static function isValid( $ip ) {
+ return preg_match( '/^' . RE_IP_ADD . '$/', $ip, $matches) ;
+ }
+
+ /**
+ * Validate an IP Block.
+ * @return boolean True if it is valid.
+ */
+ public static function isValidBlock( $ipblock ) {
+ return ( count(self::toArray($ipblock)) == 1 + 5 );
+ }
+
+ /**
+ * Determine if an IP address really is an IP address, and if it is public,
+ * i.e. not RFC 1918 or similar
+ * Comes from ProxyTools.php
+ */
+ public static function isPublic( $ip ) {
+ $n = IP::toUnsigned( $ip );
+ if ( !$n ) {
+ return false;
+ }
+
+ // ip2long accepts incomplete addresses, as well as some addresses
+ // followed by garbage characters. Check that it's really valid.
+ if( $ip != long2ip( $n ) ) {
+ return false;
+ }
+
+ static $privateRanges = false;
+ if ( !$privateRanges ) {
+ $privateRanges = array(
+ array( '10.0.0.0', '10.255.255.255' ), # RFC 1918 (private)
+ array( '172.16.0.0', '172.31.255.255' ), # "
+ array( '192.168.0.0', '192.168.255.255' ), # "
+ array( '0.0.0.0', '0.255.255.255' ), # this network
+ array( '127.0.0.0', '127.255.255.255' ), # loopback
+ );
+ }
+
+ foreach ( $privateRanges as $r ) {
+ $start = IP::toUnsigned( $r[0] );
+ $end = IP::toUnsigned( $r[1] );
+ if ( $n >= $start && $n <= $end ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Split out an IP block as an array of 4 bytes and a mask,
+ * return false if it cant be determined
+ *
+ * @parameter $ip string A quad dotted IP address
+ * @return array
+ */
+ public static function toArray( $ipblock ) {
+ if(! preg_match( '/^' . RE_IP_ADD . '(?:\/(?:'.RE_IP_PREFIX.'))?' . '$/', $ipblock, $matches ) ) {
+ return false;
+ } else {
+ return $matches;
+ }
+ }
+
+ /**
+ * Return a zero-padded hexadecimal representation of an IP address.
+ *
+ * Hexadecimal addresses are used because they can easily be extended to
+ * IPv6 support. To separate the ranges, the return value from this
+ * 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.
+ */
+ public static function toHex( $ip ) {
+ $n = self::toUnsigned( $ip );
+ if ( $n !== false ) {
+ $n = sprintf( '%08X', $n );
+ }
+ return $n;
+ }
+
+ /**
+ * Given an IP address in dotted-quad 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.
+ */
+ public static function toUnsigned( $ip ) {
+ if ( $ip == '255.255.255.255' ) {
+ $n = -1;
+ } else {
+ $n = ip2long( $ip );
+ if ( $n == -1 || $n === false ) { # Return value on error depends on PHP version
+ $n = false;
+ }
+ }
+ if ( $n < 0 ) {
+ $n += pow( 2, 32 );
+ }
+ return $n;
+ }
+
+ /**
+ * Convert a dotted-quad IP to a signed integer
+ * Returns false on failure
+ */
+ public static function toSigned( $ip ) {
+ if ( $ip == '255.255.255.255' ) {
+ $n = -1;
+ } else {
+ $n = ip2long( $ip );
+ if ( $n == -1 ) {
+ $n = false;
+ }
+ }
+ return $n;
+ }
+
+ /**
+ * Convert a network specification in CIDR notation to an integer network and a number of bits
+ */
+ public static function parseCIDR( $range ) {
+ $parts = explode( '/', $range, 2 );
+ if ( count( $parts ) != 2 ) {
+ return array( false, false );
+ }
+ $network = IP::toSigned( $parts[0] );
+ if ( $network !== false && is_numeric( $parts[1] ) && $parts[1] >= 0 && $parts[1] <= 32 ) {
+ $bits = $parts[1];
+ if ( $bits == 0 ) {
+ $network = 0;
+ } else {
+ $network &= ~((1 << (32 - $bits)) - 1);
+ }
+ # Convert to unsigned
+ if ( $network < 0 ) {
+ $network += pow( 2, 32 );
+ }
+ } 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.
+ *
+ * Formats are:
+ * 1.2.3.4/24 CIDR
+ * 1.2.3.4 - 1.2.3.5 Explicit range
+ * 1.2.3.4 Single IP
+ */
+ public static function parseRange( $range ) {
+ if ( strpos( $range, '/' ) !== false ) {
+ # CIDR
+ list( $network, $bits ) = IP::parseCIDR( $range );
+ if ( $network === false ) {
+ $start = $end = false;
+ } else {
+ $start = sprintf( '%08X', $network );
+ $end = sprintf( '%08X', $network + pow( 2, (32 - $bits) ) - 1 );
+ }
+ } elseif ( strpos( $range, '-' ) !== false ) {
+ # Explicit range
+ list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
+ if ( $start > $end ) {
+ $start = $end = false;
+ } else {
+ $start = IP::toHex( $start );
+ $end = IP::toHex( $end );
+ }
+ } else {
+ # Single IP
+ $start = $end = IP::toHex( $range );
+ }
+ if ( $start === false || $end === false ) {
+ return array( false, false );
+ } else {
+ return array( $start, $end );
+ }
+ }
+}
+?>
diff --git a/includes/Image.php b/includes/Image.php
index 185d732a..55e53e26 100644
--- a/includes/Image.php
+++ b/includes/Image.php
@@ -46,6 +46,7 @@ class Image
$size, # Size in bytes (loadFromXxx)
$metadata, # Metadata
$dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
+ $page, # Page to render when creating thumbnails
$lastError; # Error string associated with a thumbnail display error
@@ -86,6 +87,7 @@ class Image
$this->extension = Image::normalizeExtension( $n ?
substr( $this->name, $n + 1 ) : '' );
$this->historyLine = 0;
+ $this->page = 1;
$this->dataLoaded = false;
}
@@ -119,12 +121,12 @@ class Image
* Returns an array, first element is the local cache key, second is the shared cache key, if there is one
*/
function getCacheKeys( ) {
- global $wgDBname, $wgUseSharedUploads, $wgSharedUploadDBname, $wgCacheSharedUploads;
+ global $wgUseSharedUploads, $wgSharedUploadDBname, $wgCacheSharedUploads;
$hashedName = md5($this->name);
- $keys = array( "$wgDBname:Image:$hashedName" );
+ $keys = array( wfMemcKey( 'Image', $hashedName ) );
if ( $wgUseSharedUploads && $wgSharedUploadDBname && $wgCacheSharedUploads ) {
- $keys[] = "$wgSharedUploadDBname:Image:$hashedName";
+ $keys[] = wfForeignMemcKey( $wgSharedUploadDBname, false, 'Image', $hashedName );
}
return $keys;
}
@@ -142,7 +144,7 @@ class Image
// Check if the key existed and belongs to this version of MediaWiki
if (!empty($cachedValues) && is_array($cachedValues)
&& isset($cachedValues['version']) && ( $cachedValues['version'] == MW_IMAGE_VERSION )
- && $cachedValues['fileExists'] && isset( $cachedValues['mime'] ) && isset( $cachedValues['metadata'] ) )
+ && isset( $cachedValues['mime'] ) && isset( $cachedValues['metadata'] ) )
{
if ( $wgUseSharedUploads && $cachedValues['fromShared']) {
# if this is shared file, we need to check if image
@@ -200,13 +202,13 @@ class Image
* Save the image metadata to memcached
*/
function saveToCache() {
- global $wgMemc;
+ global $wgMemc, $wgUseSharedUploads;
$this->load();
$keys = $this->getCacheKeys();
- if ( $this->fileExists ) {
- // We can't cache negative metadata for non-existent files,
- // because if the file later appears in commons, the local
- // keys won't be purged.
+ // We can't cache negative metadata for non-existent files,
+ // because if the file later appears in commons, the local
+ // keys won't be purged.
+ if ( $this->fileExists || !$wgUseSharedUploads ) {
$cachedValues = array(
'version' => MW_IMAGE_VERSION,
'name' => $this->name,
@@ -258,7 +260,7 @@ class Image
if ( $this->fileExists ) {
- $magic=& wfGetMimeMagic();
+ $magic=& MimeMagic::singleton();
$this->mime = $magic->guessMimeType($this->imagePath,true);
$this->type = $magic->getMediaType($this->imagePath,$this->mime);
@@ -266,7 +268,7 @@ class Image
# Get size in bytes
$this->size = filesize( $this->imagePath );
- $magic=& wfGetMimeMagic();
+ $magic=& MimeMagic::singleton();
# Height and width
wfSuppressWarnings();
@@ -307,7 +309,11 @@ class Image
$this->dataLoaded = true;
- $this->metadata = serialize( $this->retrieveExifData( $this->imagePath ) );
+ if ( $this->mime == 'image/vnd.djvu' ) {
+ $this->metadata = $deja->retrieveMetaData();
+ } else {
+ $this->metadata = serialize( $this->retrieveExifData( $this->imagePath ) );
+ }
if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits'];
else $this->bits = 0;
@@ -323,7 +329,6 @@ class Image
wfProfileIn( __METHOD__ );
$dbr =& wfGetDB( DB_SLAVE );
-
$this->checkDBSchema($dbr);
$row = $dbr->selectRow( 'image',
@@ -374,6 +379,7 @@ class Image
$this->fileExists = false;
$this->fromSharedDirectory = false;
$this->metadata = serialize ( array() ) ;
+ $this->mime = false;
}
# Unconditionally set loaded=true, we don't want the accessors constantly rechecking
@@ -416,9 +422,12 @@ class Image
$this->loadFromDB();
if ( !$wgSharedUploadDBname && $wgUseSharedUploads ) {
$this->loadFromFile();
- } elseif ( $this->fileExists ) {
+ } elseif ( $this->fileExists || !$wgUseSharedUploads ) {
+ // We can do negative caching for local images, because the cache
+ // will be purged on upload. But we can't do it when shared images
+ // are enabled, since updates to that won't purge foreign caches.
$this->saveToCache();
- }
+ }
}
$this->dataLoaded = true;
}
@@ -601,7 +610,7 @@ class Image
* @todo remember the result of this check.
*/
function canRender() {
- global $wgUseImageMagick;
+ global $wgUseImageMagick, $wgDjvuRenderer;
if( $this->getWidth()<=0 || $this->getHeight()<=0 ) return false;
@@ -647,6 +656,7 @@ class Image
if ( $mime === 'image/vnd.wap.wbmp'
|| $mime === 'image/x-xbitmap' ) return true;
}
+ if ( $mime === 'image/vnd.djvu' && isset( $wgDjvuRenderer ) && $wgDjvuRenderer ) return true;
return false;
}
@@ -734,9 +744,16 @@ class Image
* Return the escapeLocalURL of this image
* @public
*/
- function getEscapeLocalURL() {
+ function getEscapeLocalURL( $query=false) {
$this->getTitle();
- return $this->title->escapeLocalURL();
+ if ( $query === false ) {
+ if ( $this->page != 1 ) {
+ $query = 'page=' . $this->page;
+ } else {
+ $query = '';
+ }
+ }
+ return $this->title->escapeLocalURL( $query );
}
/**
@@ -836,6 +853,9 @@ class Image
*/
function thumbName( $width ) {
$thumb = $width."px-".$this->name;
+ if ( $this->page != 1 ) {
+ $thumb = "page{$this->page}-$thumb";
+ }
if( $this->mustRender() ) {
if( $this->canRender() ) {
@@ -1123,6 +1143,7 @@ class Image
global $wgSVGConverters, $wgSVGConverter;
global $wgUseImageMagick, $wgImageMagickConvertCommand;
global $wgCustomConvertCommand;
+ global $wgDjvuRenderer, $wgDjvuPostProcessor;
$this->load();
@@ -1149,96 +1170,112 @@ class Image
$err = wfShellExec( $cmd, $retval );
wfProfileOut( 'rsvg' );
}
- } elseif ( $wgUseImageMagick ) {
- # use ImageMagick
-
- if ( $this->mime == 'image/jpeg' ) {
- $quality = "-quality 80"; // 80%
- } elseif ( $this->mime == 'image/png' ) {
- $quality = "-quality 95"; // zlib 9, adaptive filtering
- } else {
- $quality = ''; // default
- }
-
- # Specify white background color, will be used for transparent images
- # in Internet Explorer/Windows instead of default black.
-
- # Note, we specify "-size {$width}" and NOT "-size {$width}x{$height}".
- # It seems that ImageMagick has a bug wherein it produces thumbnails of
- # the wrong size in the second case.
-
- $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) .
- " {$quality} -background white -size {$width} ".
- wfEscapeShellArg($this->imagePath) .
- // Coalesce is needed to scale animated GIFs properly (bug 1017).
- ' -coalesce ' .
- // For the -resize option a "!" is needed to force exact size,
- // or ImageMagick may decide your ratio is wrong and slice off
- // a pixel.
- " -resize " . wfEscapeShellArg( "{$width}x{$height}!" ) .
- " -depth 8 " .
- wfEscapeShellArg($thumbPath) . " 2>&1";
- wfDebug("reallyRenderThumb: running ImageMagick: $cmd\n");
- wfProfileIn( 'convert' );
- $err = wfShellExec( $cmd, $retval );
- wfProfileOut( 'convert' );
- } elseif( $wgCustomConvertCommand ) {
- # Use a custom convert command
- # Variables: %s %d %w %h
- $src = wfEscapeShellArg( $this->imagePath );
- $dst = wfEscapeShellArg( $thumbPath );
- $cmd = $wgCustomConvertCommand;
- $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
- $cmd = str_replace( '%h', $height, str_replace( '%w', $width, $cmd ) ); # Size
- wfDebug( "reallyRenderThumb: Running custom convert command $cmd\n" );
- wfProfileIn( 'convert' );
- $err = wfShellExec( $cmd, $retval );
- wfProfileOut( 'convert' );
} else {
- # Use PHP's builtin GD library functions.
- #
- # First find out what kind of file this is, and select the correct
- # input routine for this.
-
- $typemap = array(
- 'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
- 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( &$this, 'imageJpegWrapper' ) ),
- 'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
- 'image/vnd.wap.wmbp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
- 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
- );
- if( !isset( $typemap[$this->mime] ) ) {
- $err = 'Image type not supported';
- wfDebug( "$err\n" );
- return $err;
- }
- list( $loader, $colorStyle, $saveType ) = $typemap[$this->mime];
+ if ( $this->mime === "image/vnd.djvu" && $wgDjvuRenderer ) {
+ // DJVU image
+ // The file contains several images. First, extract the
+ // page in hi-res, if it doesn't yet exist. Then, thumbnail
+ // it.
+
+ $cmd = "{$wgDjvuRenderer} -page={$this->page} -size=${width}x${height} " .
+ wfEscapeShellArg( $this->imagePath ) .
+ " | {$wgDjvuPostProcessor} > " . wfEscapeShellArg($thumbPath);
+ wfProfileIn( 'ddjvu' );
+ wfDebug( "reallyRenderThumb DJVU: $cmd\n" );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'ddjvu' );
- if( !function_exists( $loader ) ) {
- $err = "Incomplete GD library configuration: missing function $loader";
- wfDebug( "$err\n" );
- return $err;
- }
- if( $colorStyle == 'palette' ) {
- $truecolor = false;
- } elseif( $colorStyle == 'truecolor' ) {
- $truecolor = true;
- } elseif( $colorStyle == 'bits' ) {
- $truecolor = ( $this->bits > 8 );
- }
+ } elseif ( $wgUseImageMagick ) {
+ # use ImageMagick
+
+ if ( $this->mime == 'image/jpeg' ) {
+ $quality = "-quality 80"; // 80%
+ } elseif ( $this->mime == 'image/png' ) {
+ $quality = "-quality 95"; // zlib 9, adaptive filtering
+ } else {
+ $quality = ''; // default
+ }
- $src_image = call_user_func( $loader, $this->imagePath );
- if ( $truecolor ) {
- $dst_image = imagecreatetruecolor( $width, $height );
+ # Specify white background color, will be used for transparent images
+ # in Internet Explorer/Windows instead of default black.
+
+ # Note, we specify "-size {$width}" and NOT "-size {$width}x{$height}".
+ # It seems that ImageMagick has a bug wherein it produces thumbnails of
+ # the wrong size in the second case.
+
+ $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) .
+ " {$quality} -background white -size {$width} ".
+ wfEscapeShellArg($this->imagePath) .
+ // Coalesce is needed to scale animated GIFs properly (bug 1017).
+ ' -coalesce ' .
+ // For the -resize option a "!" is needed to force exact size,
+ // or ImageMagick may decide your ratio is wrong and slice off
+ // a pixel.
+ " -thumbnail " . wfEscapeShellArg( "{$width}x{$height}!" ) .
+ " -depth 8 " .
+ wfEscapeShellArg($thumbPath) . " 2>&1";
+ wfDebug("reallyRenderThumb: running ImageMagick: $cmd\n");
+ wfProfileIn( 'convert' );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'convert' );
+ } elseif( $wgCustomConvertCommand ) {
+ # Use a custom convert command
+ # Variables: %s %d %w %h
+ $src = wfEscapeShellArg( $this->imagePath );
+ $dst = wfEscapeShellArg( $thumbPath );
+ $cmd = $wgCustomConvertCommand;
+ $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
+ $cmd = str_replace( '%h', $height, str_replace( '%w', $width, $cmd ) ); # Size
+ wfDebug( "reallyRenderThumb: Running custom convert command $cmd\n" );
+ wfProfileIn( 'convert' );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'convert' );
} else {
- $dst_image = imagecreate( $width, $height );
+ # Use PHP's builtin GD library functions.
+ #
+ # First find out what kind of file this is, and select the correct
+ # input routine for this.
+
+ $typemap = array(
+ 'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
+ 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( &$this, 'imageJpegWrapper' ) ),
+ 'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
+ 'image/vnd.wap.wmbp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
+ 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
+ );
+ if( !isset( $typemap[$this->mime] ) ) {
+ $err = 'Image type not supported';
+ wfDebug( "$err\n" );
+ return $err;
+ }
+ list( $loader, $colorStyle, $saveType ) = $typemap[$this->mime];
+
+ if( !function_exists( $loader ) ) {
+ $err = "Incomplete GD library configuration: missing function $loader";
+ wfDebug( "$err\n" );
+ return $err;
+ }
+ if( $colorStyle == 'palette' ) {
+ $truecolor = false;
+ } elseif( $colorStyle == 'truecolor' ) {
+ $truecolor = true;
+ } elseif( $colorStyle == 'bits' ) {
+ $truecolor = ( $this->bits > 8 );
+ }
+
+ $src_image = call_user_func( $loader, $this->imagePath );
+ if ( $truecolor ) {
+ $dst_image = imagecreatetruecolor( $width, $height );
+ } else {
+ $dst_image = imagecreate( $width, $height );
+ }
+ imagecopyresampled( $dst_image, $src_image,
+ 0,0,0,0,
+ $width, $height, $this->width, $this->height );
+ call_user_func( $saveType, $dst_image, $thumbPath );
+ imagedestroy( $dst_image );
+ imagedestroy( $src_image );
}
- imagecopyresampled( $dst_image, $src_image,
- 0,0,0,0,
- $width, $height, $this->width, $this->height );
- call_user_func( $saveType, $dst_image, $thumbPath );
- imagedestroy( $dst_image );
- imagedestroy( $src_image );
}
#
@@ -1367,14 +1404,16 @@ class Image
}
function checkDBSchema(&$db) {
+ static $checkDone = false;
global $wgCheckDBSchema;
- if (!$wgCheckDBSchema) {
+ if (!$wgCheckDBSchema || $checkDone) {
return;
}
# img_name must be unique
if ( !$db->indexUnique( 'image', 'img_name' ) && !$db->indexExists('image','PRIMARY') ) {
throw new MWException( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' );
}
+ $checkDone = true;
# new fields must exist
#
@@ -1489,7 +1528,7 @@ class Image
* @return bool
* @static
*/
- function isHashed( $shared ) {
+ public static function isHashed( $shared ) {
global $wgHashedUploadDirectory, $wgHashedSharedUploadDirectory;
return $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
}
@@ -1706,7 +1745,7 @@ class Image
function getExifData() {
global $wgRequest;
- if ( $this->metadata === '0' )
+ if ( $this->metadata === '0' || $this->mime == 'image/vnd.djvu' )
return array();
$purge = $wgRequest->getVal( 'action' ) == 'purge';
@@ -2095,7 +2134,7 @@ class Image
$tempFile = $store->filePath( $row->fa_storage_key );
$metadata = serialize( $this->retrieveExifData( $tempFile ) );
- $magic = wfGetMimeMagic();
+ $magic = MimeMagic::singleton();
$mime = $magic->guessMimeType( $tempFile, true );
$media_type = $magic->getMediaType( $tempFile, $mime );
list( $major_mime, $minor_mime ) = self::splitMime( $mime );
@@ -2204,6 +2243,73 @@ class Image
return $revisions;
}
+
+ /**
+ * Select a page from a multipage document. Determines the page used for
+ * rendering thumbnails.
+ *
+ * @param $page Integer: page number, starting with 1
+ */
+ function selectPage( $page ) {
+ wfDebug( __METHOD__." selecting page $page \n" );
+ $this->page = $page;
+ if ( ! $this->dataLoaded ) {
+ $this->load();
+ }
+ if ( ! isset( $this->multiPageXML ) ) {
+ $this->initializeMultiPageXML();
+ }
+ $o = $this->multiPageXML->BODY[0]->OBJECT[$page-1];
+ $this->height = intval( $o['height'] );
+ $this->width = intval( $o['width'] );
+ }
+
+ function initializeMultiPageXML() {
+ #
+ # Check for files uploaded prior to DJVU support activation
+ # They have a '0' in their metadata field.
+ #
+ if ( $this->metadata == '0' ) {
+ $deja = new DjVuImage( $this->imagePath );
+ $this->metadata = $deja->retrieveMetaData();
+ $this->purgeMetadataCache();
+
+ # Update metadata in the database
+ $dbw =& wfGetDB( DB_MASTER );
+ $dbw->update( 'image',
+ array( 'img_metadata' => $this->metadata ),
+ array( 'img_name' => $this->name ),
+ __METHOD__
+ );
+ }
+ wfSuppressWarnings();
+ $this->multiPageXML = new SimpleXMLElement( $this->metadata );
+ wfRestoreWarnings();
+ }
+
+ /**
+ * Returns 'true' if this image is a multipage document, e.g. a DJVU
+ * document.
+ *
+ * @return Bool
+ */
+ function isMultipage() {
+ return ( $this->mime == 'image/vnd.djvu' );
+ }
+
+ /**
+ * Returns the number of pages of a multipage document, or NULL for
+ * documents which aren't multipage documents
+ */
+ function pageCount() {
+ if ( ! $this->isMultipage() ) {
+ return null;
+ }
+ if ( ! isset( $this->multiPageXML ) ) {
+ $this->initializeMultiPageXML();
+ }
+ return count( $this->multiPageXML->xpath( '//OBJECT' ) );
+ }
} //class
diff --git a/includes/ImageFunctions.php b/includes/ImageFunctions.php
index a66b4d79..d182d527 100644
--- a/includes/ImageFunctions.php
+++ b/includes/ImageFunctions.php
@@ -1,223 +1,256 @@
-<?php
-
-/**
- * Returns the image directory of an image
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the image file.
- * @public
- */
-function wfImageDir( $fname ) {
- global $wgUploadDirectory, $wgHashedUploadDirectory;
-
- if (!$wgHashedUploadDirectory) { return $wgUploadDirectory; }
-
- $hash = md5( $fname );
- $dest = $wgUploadDirectory . '/' . $hash{0} . '/' . substr( $hash, 0, 2 );
-
- return $dest;
-}
-
-/**
- * Returns the image directory of an image's thubnail
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the original image file
- * @param $shared Boolean: (optional) use the shared upload directory (default: 'false').
- * @public
- */
-function wfImageThumbDir( $fname, $shared = false ) {
- $base = wfImageArchiveDir( $fname, 'thumb', $shared );
- if ( Image::isHashed( $shared ) ) {
- $dir = "$base/$fname";
- } else {
- $dir = $base;
- }
-
- return $dir;
-}
-
-/**
- * Old thumbnail directory, kept for conversion
- */
-function wfDeprecatedThumbDir( $thumbName , $subdir='thumb', $shared=false) {
- return wfImageArchiveDir( $thumbName, $subdir, $shared );
-}
-
-/**
- * Returns the image directory of an image's old version
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the thumbnail file, including file size prefix.
- * @param $subdir String: subdirectory of the image upload directory that should be used for storing the old version. Default is 'archive'.
- * @param $shared Boolean use the shared upload directory (only relevant for other functions which call this one). Default is 'false'.
- * @public
- */
-function wfImageArchiveDir( $fname , $subdir='archive', $shared=false ) {
- global $wgUploadDirectory, $wgHashedUploadDirectory;
- global $wgSharedUploadDirectory, $wgHashedSharedUploadDirectory;
- $dir = $shared ? $wgSharedUploadDirectory : $wgUploadDirectory;
- $hashdir = $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
- if (!$hashdir) { return $dir.'/'.$subdir; }
- $hash = md5( $fname );
-
- return $dir.'/'.$subdir.'/'.$hash[0].'/'.substr( $hash, 0, 2 );
-}
-
-
-/*
- * Return the hash path component of an image path (URL or filesystem),
- * e.g. "/3/3c/", or just "/" if hashing is not used.
- *
- * @param $dbkey The filesystem / database name of the file
- * @param $fromSharedDirectory Use the shared file repository? It may
- * use different hash settings from the local one.
- */
-function wfGetHashPath ( $dbkey, $fromSharedDirectory = false ) {
- if( Image::isHashed( $fromSharedDirectory ) ) {
- $hash = md5($dbkey);
- return '/' . $hash{0} . '/' . substr( $hash, 0, 2 ) . '/';
- } else {
- return '/';
- }
-}
-
-/**
- * Returns the image URL of an image's old version
- *
- * @param $name String: file name of the image file
- * @param $subdir String: (optional) subdirectory of the image upload directory that is used by the old version. Default is 'archive'
- * @public
- */
-function wfImageArchiveUrl( $name, $subdir='archive' ) {
- global $wgUploadPath, $wgHashedUploadDirectory;
-
- if ($wgHashedUploadDirectory) {
- $hash = md5( substr( $name, 15) );
- $url = $wgUploadPath.'/'.$subdir.'/' . $hash{0} . '/' .
- substr( $hash, 0, 2 ) . '/'.$name;
- } else {
- $url = $wgUploadPath.'/'.$subdir.'/'.$name;
- }
- return wfUrlencode($url);
-}
-
-/**
- * Return a rounded pixel equivalent for a labeled CSS/SVG length.
- * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers
- *
- * @param $length String: CSS/SVG length.
- * @return Integer: length in pixels
- */
-function wfScaleSVGUnit( $length ) {
- static $unitLength = array(
- 'px' => 1.0,
- 'pt' => 1.25,
- 'pc' => 15.0,
- 'mm' => 3.543307,
- 'cm' => 35.43307,
- 'in' => 90.0,
- '' => 1.0, // "User units" pixels by default
- '%' => 2.0, // Fake it!
- );
- if( preg_match( '/^(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)$/', $length, $matches ) ) {
- $length = floatval( $matches[1] );
- $unit = $matches[2];
- return round( $length * $unitLength[$unit] );
- } else {
- // Assume pixels
- return round( floatval( $length ) );
- }
-}
-
-/**
- * Compatible with PHP getimagesize()
- * @todo support gzipped SVGZ
- * @todo check XML more carefully
- * @todo sensible defaults
- *
- * @param $filename String: full name of the file (passed to php fopen()).
- * @return array
- */
-function wfGetSVGsize( $filename ) {
- $width = 256;
- $height = 256;
-
- // Read a chunk of the file
- $f = fopen( $filename, "rt" );
- if( !$f ) return false;
- $chunk = fread( $f, 4096 );
- fclose( $f );
-
- // Uber-crappy hack! Run through a real XML parser.
- if( !preg_match( '/<svg\s*([^>]*)\s*>/s', $chunk, $matches ) ) {
- return false;
- }
- $tag = $matches[1];
- if( preg_match( '/\bwidth\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
- $width = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
- }
- if( preg_match( '/\bheight\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
- $height = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
- }
-
- return array( $width, $height, 'SVG',
- "width=\"$width\" height=\"$height\"" );
-}
-
-/**
- * Determine if an image exists on the 'bad image list'.
- *
- * @param $name String: the image name to check
- * @return bool
- */
-function wfIsBadImage( $name ) {
- static $titleList = false;
- wfProfileIn( __METHOD__ );
- $bad = false;
- if( wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) {
- if( !$titleList ) {
- # Build the list now
- $titleList = array();
- $lines = explode( "\n", wfMsgForContent( 'bad_image_list' ) );
- foreach( $lines as $line ) {
- if( preg_match( '/^\*\s*\[\[:?(.*?)\]\]/i', $line, $matches ) ) {
- $title = Title::newFromText( $matches[1] );
- if( is_object( $title ) && $title->getNamespace() == NS_IMAGE )
- $titleList[ $title->getDBkey() ] = true;
- }
- }
- }
- wfProfileOut( __METHOD__ );
- return array_key_exists( $name, $titleList );
- } else {
- wfProfileOut( __METHOD__ );
- return $bad;
- }
-}
-
-/**
- * Calculate the largest thumbnail width for a given original file size
- * such that the thumbnail's height is at most $maxHeight.
- * @param $boxWidth Integer Width of the thumbnail box.
- * @param $boxHeight Integer Height of the thumbnail box.
- * @param $maxHeight Integer Maximum height expected for the thumbnail.
- * @return Integer.
- */
-function wfFitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
- $idealWidth = $boxWidth * $maxHeight / $boxHeight;
- $roundedUp = ceil( $idealWidth );
- if( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight )
- return floor( $idealWidth );
- else
- return $roundedUp;
-}
-
-
-?>
+<?php
+
+/**
+ * Returns the image directory of an image
+ * The result is an absolute path.
+ *
+ * This function is called from thumb.php before Setup.php is included
+ *
+ * @param $fname String: file name of the image file.
+ * @public
+ */
+function wfImageDir( $fname ) {
+ global $wgUploadDirectory, $wgHashedUploadDirectory;
+
+ if (!$wgHashedUploadDirectory) { return $wgUploadDirectory; }
+
+ $hash = md5( $fname );
+ $dest = $wgUploadDirectory . '/' . $hash{0} . '/' . substr( $hash, 0, 2 );
+
+ return $dest;
+}
+
+/**
+ * Returns the image directory of an image's thubnail
+ * The result is an absolute path.
+ *
+ * This function is called from thumb.php before Setup.php is included
+ *
+ * @param $fname String: file name of the original image file
+ * @param $shared Boolean: (optional) use the shared upload directory (default: 'false').
+ * @public
+ */
+function wfImageThumbDir( $fname, $shared = false ) {
+ $base = wfImageArchiveDir( $fname, 'thumb', $shared );
+ if ( Image::isHashed( $shared ) ) {
+ $dir = "$base/$fname";
+ } else {
+ $dir = $base;
+ }
+
+ return $dir;
+}
+
+/**
+ * Old thumbnail directory, kept for conversion
+ */
+function wfDeprecatedThumbDir( $thumbName , $subdir='thumb', $shared=false) {
+ return wfImageArchiveDir( $thumbName, $subdir, $shared );
+}
+
+/**
+ * Returns the image directory of an image's old version
+ * The result is an absolute path.
+ *
+ * This function is called from thumb.php before Setup.php is included
+ *
+ * @param $fname String: file name of the thumbnail file, including file size prefix.
+ * @param $subdir String: subdirectory of the image upload directory that should be used for storing the old version. Default is 'archive'.
+ * @param $shared Boolean use the shared upload directory (only relevant for other functions which call this one). Default is 'false'.
+ * @public
+ */
+function wfImageArchiveDir( $fname , $subdir='archive', $shared=false ) {
+ global $wgUploadDirectory, $wgHashedUploadDirectory;
+ global $wgSharedUploadDirectory, $wgHashedSharedUploadDirectory;
+ $dir = $shared ? $wgSharedUploadDirectory : $wgUploadDirectory;
+ $hashdir = $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
+ if (!$hashdir) { return $dir.'/'.$subdir; }
+ $hash = md5( $fname );
+
+ return $dir.'/'.$subdir.'/'.$hash[0].'/'.substr( $hash, 0, 2 );
+}
+
+
+/*
+ * Return the hash path component of an image path (URL or filesystem),
+ * e.g. "/3/3c/", or just "/" if hashing is not used.
+ *
+ * @param $dbkey The filesystem / database name of the file
+ * @param $fromSharedDirectory Use the shared file repository? It may
+ * use different hash settings from the local one.
+ */
+function wfGetHashPath ( $dbkey, $fromSharedDirectory = false ) {
+ if( Image::isHashed( $fromSharedDirectory ) ) {
+ $hash = md5($dbkey);
+ return '/' . $hash{0} . '/' . substr( $hash, 0, 2 ) . '/';
+ } else {
+ return '/';
+ }
+}
+
+/**
+ * Returns the image URL of an image's old version
+ *
+ * @param $name String: file name of the image file
+ * @param $subdir String: (optional) subdirectory of the image upload directory that is used by the old version. Default is 'archive'
+ * @public
+ */
+function wfImageArchiveUrl( $name, $subdir='archive' ) {
+ global $wgUploadPath, $wgHashedUploadDirectory;
+
+ if ($wgHashedUploadDirectory) {
+ $hash = md5( substr( $name, 15) );
+ $url = $wgUploadPath.'/'.$subdir.'/' . $hash{0} . '/' .
+ substr( $hash, 0, 2 ) . '/'.$name;
+ } else {
+ $url = $wgUploadPath.'/'.$subdir.'/'.$name;
+ }
+ return wfUrlencode($url);
+}
+
+/**
+ * Return a rounded pixel equivalent for a labeled CSS/SVG length.
+ * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers
+ *
+ * @param $length String: CSS/SVG length.
+ * @return Integer: length in pixels
+ */
+function wfScaleSVGUnit( $length ) {
+ static $unitLength = array(
+ 'px' => 1.0,
+ 'pt' => 1.25,
+ 'pc' => 15.0,
+ 'mm' => 3.543307,
+ 'cm' => 35.43307,
+ 'in' => 90.0,
+ '' => 1.0, // "User units" pixels by default
+ '%' => 2.0, // Fake it!
+ );
+ if( preg_match( '/^(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)$/', $length, $matches ) ) {
+ $length = floatval( $matches[1] );
+ $unit = $matches[2];
+ return round( $length * $unitLength[$unit] );
+ } else {
+ // Assume pixels
+ return round( floatval( $length ) );
+ }
+}
+
+/**
+ * Compatible with PHP getimagesize()
+ * @todo support gzipped SVGZ
+ * @todo check XML more carefully
+ * @todo sensible defaults
+ *
+ * @param $filename String: full name of the file (passed to php fopen()).
+ * @return array
+ */
+function wfGetSVGsize( $filename ) {
+ $width = 256;
+ $height = 256;
+
+ // Read a chunk of the file
+ $f = fopen( $filename, "rt" );
+ if( !$f ) return false;
+ $chunk = fread( $f, 4096 );
+ fclose( $f );
+
+ // Uber-crappy hack! Run through a real XML parser.
+ if( !preg_match( '/<svg\s*([^>]*)\s*>/s', $chunk, $matches ) ) {
+ return false;
+ }
+ $tag = $matches[1];
+ if( preg_match( '/\bwidth\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
+ $width = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
+ }
+ if( preg_match( '/\bheight\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
+ $height = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
+ }
+
+ return array( $width, $height, 'SVG',
+ "width=\"$width\" height=\"$height\"" );
+}
+
+/**
+ * Determine if an image exists on the 'bad image list'.
+ *
+ * The format of MediaWiki:Bad_image_list is as follows:
+ * * Only list items (lines starting with "*") are considered
+ * * The first link on a line must be a link to a bad image
+ * * Any subsequent links on the same line are considered to be exceptions,
+ * i.e. articles where the image may occur inline.
+ *
+ * @param string $name the image name to check
+ * @param Title $contextTitle The page on which the image occurs, if known
+ * @return bool
+ */
+function wfIsBadImage( $name, $contextTitle = false ) {
+ static $badImages = false;
+ wfProfileIn( __METHOD__ );
+
+ # Run the extension hook
+ $bad = false;
+ if( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) {
+ wfProfileOut( __METHOD__ );
+ return $bad;
+ }
+
+ if( !$badImages ) {
+ # Build the list now
+ $badImages = array();
+ $lines = explode( "\n", wfMsgForContent( 'bad_image_list' ) );
+ foreach( $lines as $line ) {
+ # List items only
+ if ( substr( $line, 0, 1 ) !== '*' ) {
+ continue;
+ }
+
+ # Find all links
+ if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
+ continue;
+ }
+
+ $exceptions = array();
+ $imageDBkey = false;
+ foreach ( $m[1] as $i => $titleText ) {
+ $title = Title::newFromText( $titleText );
+ if ( !is_null( $title ) ) {
+ if ( $i == 0 ) {
+ $imageDBkey = $title->getDBkey();
+ } else {
+ $exceptions[$title->getPrefixedDBkey()] = true;
+ }
+ }
+ }
+
+ if ( $imageDBkey !== false ) {
+ $badImages[$imageDBkey] = $exceptions;
+ }
+ }
+ }
+
+ $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
+ $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
+ wfProfileOut( __METHOD__ );
+ return $bad;
+}
+
+/**
+ * Calculate the largest thumbnail width for a given original file size
+ * such that the thumbnail's height is at most $maxHeight.
+ * @param $boxWidth Integer Width of the thumbnail box.
+ * @param $boxHeight Integer Height of the thumbnail box.
+ * @param $maxHeight Integer Maximum height expected for the thumbnail.
+ * @return Integer.
+ */
+function wfFitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
+ $idealWidth = $boxWidth * $maxHeight / $boxHeight;
+ $roundedUp = ceil( $idealWidth );
+ if( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight )
+ return floor( $idealWidth );
+ else
+ return $roundedUp;
+}
+
+
+?>
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
index 0935ac30..7ff456b6 100644
--- a/includes/ImageGallery.php
+++ b/includes/ImageGallery.php
@@ -55,7 +55,7 @@ class ImageGallery
*
* @param $skin Skin object
*/
- function useSkin( &$skin ) {
+ function useSkin( $skin ) {
$this->mSkin =& $skin;
}
@@ -82,6 +82,7 @@ class ImageGallery
*/
function add( $image, $html='' ) {
$this->mImages[] = array( &$image, $html );
+ wfDebug( "ImageGallery::add " . $image->getName() . "\n" );
}
/**
@@ -135,7 +136,7 @@ class ImageGallery
function toHTML() {
global $wgLang, $wgIgnoreImageErrors, $wgGenerateThumbnailOnParse;
- $sk =& $this->getSkin();
+ $sk = $this->getSkin();
$s = '<table class="gallery" cellspacing="0" cellpadding="0">';
if( $this->mCaption )
@@ -157,8 +158,7 @@ class ImageGallery
# The image is blacklisted, just show it as a text link.
$thumbhtml = '<div style="height: 152px;">'
. $sk->makeKnownLinkObj( $nt, htmlspecialchars( $nt->getText() ) ) . '</div>';
- }
- else if( !( $thumb = $img->getThumbnail( 120, 120, $wgGenerateThumbnailOnParse ) ) ) {
+ } else if( !( $thumb = $img->getThumbnail( 120, 120, $wgGenerateThumbnailOnParse ) ) ) {
# Error generating thumbnail.
$thumbhtml = '<div style="height: 152px;">'
. htmlspecialchars( $img->getLastError() ) . '</div>';
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index dac9602d..908dd5cc 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -9,8 +9,6 @@
if( !defined( 'MEDIAWIKI' ) )
die( 1 );
-require_once( 'Image.php' );
-
/**
* Special handling for image description pages
* @package MediaWiki
@@ -165,7 +163,7 @@ class ImagePage extends Article {
}
function openShowImage() {
- global $wgOut, $wgUser, $wgImageLimits, $wgRequest;
+ global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang;
global $wgUseImageResize, $wgGenerateThumbnailOnParse;
$full_url = $this->img->getURL();
@@ -187,6 +185,12 @@ class ImagePage extends Article {
if ( $this->img->exists() ) {
# image
+ $page = $wgRequest->getIntOrNull( 'page' );
+ if ( ! is_null( $page ) ) {
+ $this->img->selectPage( $page );
+ } else {
+ $page = 1;
+ }
$width = $this->img->getWidth();
$height = $this->img->getHeight();
$showLink = false;
@@ -236,9 +240,50 @@ class ImagePage extends Article {
$url = $this->img->getViewURL();
$showLink = true;
}
+
+ if ( $this->img->isMultipage() ) {
+ $wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
+ }
+
$wgOut->addHTML( '<div class="fullImageLink" id="file">' . $anchoropen .
"<img border=\"0\" src=\"{$url}\" width=\"{$width}\" height=\"{$height}\" alt=\"" .
htmlspecialchars( $wgRequest->getVal( 'image' ) ).'" />' . $anchorclose . '</div>' );
+
+ if ( $this->img->isMultipage() ) {
+ $count = $this->img->pageCount();
+
+ if ( $page > 1 ) {
+ $label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
+ $link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
+ $this->img->selectPage( $page - 1 );
+ $thumb1 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none' );
+ } else {
+ $thumb1 = '';
+ }
+
+ if ( $page < $count ) {
+ $label = wfMsg( 'imgmultipagenext' );
+ $this->img->selectPage( $page + 1 );
+ $link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
+ $thumb2 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none' );
+ } else {
+ $thumb2 = '';
+ }
+
+ $select = '<form name="pageselector" action="' . $this->img->getEscapeLocalUrl( '' ) . '" method="GET" onchange="document.pageselector.submit();">' ;
+ $select .= $wgOut->parse( wfMsg( 'imgmultigotopre' ), false ) .
+ ' <select id="pageselector" name="page">';
+ for ( $i=1; $i <= $count; $i++ ) {
+ $select .= Xml::option( $wgLang->formatNum( $i ), $i,
+ $i == $page );
+ }
+ $select .= '</select>' . $wgOut->parse( wfMsg( 'imgmultigotopost' ), false ) .
+ '<input type="submit" value="' .
+ htmlspecialchars( wfMsg( 'imgmultigo' ) ) . '"></form>';
+
+ $wgOut->addHTML( '</td><td><div class="multipageimagenavbox">' .
+ "$select<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>" );
+ }
} else {
#if direct link is allowed but it's not a renderable image, show an icon.
if ($this->img->isSafeFile()) {
@@ -312,10 +357,7 @@ END
$wgOut->addHTML($sharedtext);
if ($wgRepositoryBaseUrl && $wgFetchCommonsDescriptions) {
- require_once("HttpFunctions.php");
- $ur = ini_set('allow_url_fopen', true);
- $text = wfGetHTTP($url . '?action=render');
- ini_set('allow_url_fopen', $ur);
+ $text = Http::get($url . '?action=render');
if ($text)
$this->mExtraDescription = $text;
}
@@ -373,7 +415,7 @@ END
$line = $this->img->nextHistoryLine();
if ( $line ) {
- $list =& new ImageHistoryList( $sk );
+ $list = new ImageHistoryList( $sk );
$s = $list->beginImageHistoryList() .
$list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp),
$this->mTitle->getDBkey(), $line->img_user,
@@ -435,13 +477,14 @@ END
global $wgUser, $wgOut, $wgRequest;
$confirm = $wgRequest->wasPosted();
+ $reason = $wgRequest->getVal( 'wpReason' );
$image = $wgRequest->getVal( 'image' );
$oldimage = $wgRequest->getVal( 'oldimage' );
# Only sysops can delete images. Previously ordinary users could delete
# old revisions, but this is no longer the case.
if ( !$wgUser->isAllowed('delete') ) {
- $wgOut->sysopRequired();
+ $wgOut->permissionRequired( 'delete' );
return;
}
if ( $wgUser->isBlocked() ) {
@@ -465,7 +508,7 @@ END
# Deleting old images doesn't require confirmation
if ( !is_null( $oldimage ) || $confirm ) {
if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
- $this->doDelete();
+ $this->doDelete( $reason );
} else {
$wgOut->showFatalError( wfMsg( 'sessionfailure' ) );
}
@@ -482,13 +525,16 @@ END
return $this->confirmDelete( $q, $wgRequest->getText( 'wpReason' ) );
}
- function doDelete() {
+ /*
+ * Delete an image.
+ * @param $reason User provided reason for deletion.
+ */
+ function doDelete( $reason ) {
global $wgOut, $wgRequest, $wgUseSquid;
global $wgPostCommitUpdateList;
$fname = 'ImagePage::doDelete';
- $reason = $wgRequest->getVal( 'wpReason' );
$oldimage = $wgRequest->getVal( 'oldimage' );
$dbw =& wfGetDB( DB_MASTER );
@@ -576,7 +622,7 @@ END
return;
}
if ( ! $this->mTitle->userCanEdit() ) {
- $wgOut->sysopRequired();
+ $wgOut->readOnlyPage( $this->getContent(), true );
return;
}
if ( $wgUser->isBlocked() ) {
diff --git a/includes/LinkBatch.php b/includes/LinkBatch.php
index e0f0f6fd..061f1b19 100644
--- a/includes/LinkBatch.php
+++ b/includes/LinkBatch.php
@@ -66,7 +66,7 @@ class LinkBatch {
*/
function execute() {
$linkCache =& LinkCache::singleton();
- $this->executeInto( $linkCache );
+ return $this->executeInto( $linkCache );
}
/**
diff --git a/includes/LinkCache.php b/includes/LinkCache.php
index 451b3f0c..8e56225b 100644
--- a/includes/LinkCache.php
+++ b/includes/LinkCache.php
@@ -21,7 +21,7 @@ class LinkCache {
/**
* Get an instance of this class
*/
- function &singleton() {
+ static function &singleton() {
static $instance;
if ( !isset( $instance ) ) {
$instance = new LinkCache;
@@ -37,8 +37,7 @@ class LinkCache {
}
/* private */ function getKey( $title ) {
- global $wgDBname;
- return $wgDBname.':lc:title:'.$title;
+ return wfMemcKey( 'lc', 'title', $title );
}
/**
diff --git a/includes/Linker.php b/includes/Linker.php
index 4a0eafbd..d34971ff 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -456,11 +456,16 @@ class Linker {
/** @todo document */
function makeImageLinkObj( $nt, $label, $alt, $align = '', $width = false, $height = false, $framed = false,
- $thumb = false, $manual_thumb = '' )
+ $thumb = false, $manual_thumb = '', $page = null )
{
global $wgContLang, $wgUser, $wgThumbLimits, $wgGenerateThumbnailOnParse;
$img = new Image( $nt );
+
+ if ( ! is_null( $page ) ) {
+ $img->selectPage( $page );
+ }
+
if ( !$img->allowInlineDisplay() && $img->exists() ) {
return $this->makeKnownLinkObj( $nt );
}
@@ -468,7 +473,7 @@ class Linker {
$url = $img->getViewURL();
$error = $prefix = $postfix = '';
- wfDebug( "makeImageLinkObj: '$width'x'$height'\n" );
+ wfDebug( "makeImageLinkObj: '$width'x'$height', \"$label\"\n" );
if ( 'center' == $align )
{
@@ -564,7 +569,6 @@ class Linker {
*/
function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $boxwidth = 180, $boxheight=false, $framed=false , $manual_thumb = "" ) {
global $wgStylePath, $wgContLang, $wgGenerateThumbnailOnParse;
- $url = $img->getViewURL();
$thumbUrl = '';
$error = '';
@@ -583,7 +587,7 @@ class Linker {
// Use image dimensions, don't scale
$boxwidth = $width;
$boxheight = $height;
- $thumbUrl = $url;
+ $thumbUrl = $img->getViewURL();
} else {
if ( $boxheight === false )
$boxheight = -1;
@@ -626,7 +630,7 @@ class Linker {
$s = "<div class=\"thumb t{$align}\"><div style=\"width:{$oboxwidth}px;\">";
if( $thumbUrl == '' ) {
// Couldn't generate thumbnail? Scale the image client-side.
- $thumbUrl = $url;
+ $thumbUrl = $img->getViewURL();
}
if ( $error ) {
$s .= htmlspecialchars( $error );
@@ -1081,7 +1085,7 @@ class Linker {
*
* @static
*/
- function splitTrail( $trail ) {
+ static function splitTrail( $trail ) {
static $regex = false;
if ( $regex === false ) {
global $wgContLang;
diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php
index f985a7b4..3e81aea9 100644
--- a/includes/LoadBalancer.php
+++ b/includes/LoadBalancer.php
@@ -4,26 +4,6 @@
* @package MediaWiki
*/
-/**
- * Depends on the database object
- */
-require_once( 'Database.php' );
-
-# Valid database indexes
-# Operation-based indexes
-define( 'DB_SLAVE', -1 ); # Read from the slave (or only server)
-define( 'DB_MASTER', -2 ); # Write to master (or only server)
-define( 'DB_LAST', -3 ); # Whatever database was used last
-
-# Obsolete aliases
-define( 'DB_READ', -1 );
-define( 'DB_WRITE', -2 );
-
-
-# Scale polling time so that under overload conditions, the database server
-# receives a SHOW STATUS query at an average interval of this many microseconds
-define( 'AVG_STATUS_POLL', 2000 );
-
/**
* Database load balancing object
@@ -38,26 +18,13 @@ class LoadBalancer {
/* private */ var $mWaitForFile, $mWaitForPos, $mWaitTimeout;
/* private */ var $mLaggedSlaveMode, $mLastError = 'Unknown error';
- function LoadBalancer()
- {
- $this->mServers = array();
- $this->mConnections = array();
- $this->mFailFunction = false;
- $this->mReadIndex = -1;
- $this->mForce = -1;
- $this->mLastIndex = -1;
- $this->mErrorConnection = false;
- $this->mAllowLag = false;
- }
-
- function newFromParams( $servers, $failFunction = false, $waitTimeout = 10 )
- {
- $lb = new LoadBalancer;
- $lb->initialise( $servers, $failFunction, $waitTimeout );
- return $lb;
- }
+ /**
+ * Scale polling time so that under overload conditions, the database server
+ * receives a SHOW STATUS query at an average interval of this many microseconds
+ */
+ const AVG_STATUS_POLL = 2000;
- function initialise( $servers, $failFunction = false, $waitTimeout = 10 )
+ function LoadBalancer( $servers, $failFunction = false, $waitTimeout = 10, $waitForMasterNow = false )
{
$this->mServers = $servers;
$this->mFailFunction = $failFunction;
@@ -71,6 +38,8 @@ class LoadBalancer {
$this->mWaitForPos = false;
$this->mWaitTimeout = $waitTimeout;
$this->mLaggedSlaveMode = false;
+ $this->mErrorConnection = false;
+ $this->mAllowLag = false;
foreach( $servers as $i => $server ) {
$this->mLoads[$i] = $server['load'];
@@ -83,6 +52,14 @@ class LoadBalancer {
}
}
}
+ if ( $waitForMasterNow ) {
+ $this->loadMasterPos();
+ }
+ }
+
+ static function newFromParams( $servers, $failFunction = false, $waitTimeout = 10 )
+ {
+ return new LoadBalancer( $servers, $failFunction, $waitTimeout );
}
/**
@@ -180,7 +157,7 @@ class LoadBalancer {
$i = $this->getRandomNonLagged( $loads );
if ( $i === false && count( $loads ) != 0 ) {
# All slaves lagged. Switch to read-only mode
- $wgReadOnly = wfMsgNoDB( 'readonly_lag' );
+ $wgReadOnly = wfMsgNoDBForContent( 'readonly_lag' );
$i = $this->pickRandom( $loads );
}
}
@@ -201,7 +178,7 @@ class LoadBalancer {
# Too much load, back off and wait for a while.
# The sleep time is scaled by the number of threads connected,
# to produce a roughly constant global poll rate.
- $sleepTime = AVG_STATUS_POLL * $status['Threads_connected'];
+ $sleepTime = self::AVG_STATUS_POLL * $status['Threads_connected'];
# If we reach the timeout and exit the loop, don't use it
$i = false;
@@ -442,9 +419,6 @@ class LoadBalancer {
extract( $server );
# Get class for this database type
$class = 'Database' . ucfirst( $type );
- if ( !class_exists( $class ) ) {
- require_once( "$class.php" );
- }
# Create object
$db = new $class( $host, $user, $password, $dbname, 1, $flags );
@@ -625,21 +599,24 @@ class LoadBalancer {
* Results are cached for a short time in memcached
*/
function getLagTimes() {
- global $wgDBname;
-
+ wfProfileIn( __METHOD__ );
$expiry = 5;
$requestRate = 10;
global $wgMemc;
- $times = $wgMemc->get( "$wgDBname:lag_times" );
+ $times = $wgMemc->get( wfMemcKey( 'lag_times' ) );
if ( $times ) {
# Randomly recache with probability rising over $expiry
$elapsed = time() - $times['timestamp'];
$chance = max( 0, ( $expiry - $elapsed ) * $requestRate );
if ( mt_rand( 0, $chance ) != 0 ) {
unset( $times['timestamp'] );
+ wfProfileOut( __METHOD__ );
return $times;
}
+ wfIncrStats( 'lag_cache_miss_expired' );
+ } else {
+ wfIncrStats( 'lag_cache_miss_absent' );
}
# Cache key missing or expired
@@ -655,10 +632,11 @@ class LoadBalancer {
# Add a timestamp key so we know when it was cached
$times['timestamp'] = time();
- $wgMemc->set( "$wgDBname:lag_times", $times, $expiry );
+ $wgMemc->set( wfMemcKey( 'lag_times' ), $times, $expiry );
# But don't give the timestamp to the caller
unset($times['timestamp']);
+ wfProfileOut( __METHOD__ );
return $times;
}
}
diff --git a/includes/LogPage.php b/includes/LogPage.php
index f588105f..954b178f 100644
--- a/includes/LogPage.php
+++ b/includes/LogPage.php
@@ -207,13 +207,13 @@ class LogPage {
* @param string $comment Description associated
* @param array $params Parameters passed later to wfMsg.* functions
*/
- function addEntry( $action, &$target, $comment, $params = array() ) {
+ function addEntry( $action, $target, $comment, $params = array() ) {
if ( !is_array( $params ) ) {
$params = array( $params );
}
$this->action = $action;
- $this->target =& $target;
+ $this->target = $target;
$this->comment = $comment;
$this->params = LogPage::makeParamBlob( $params );
diff --git a/includes/MagicWord.php b/includes/MagicWord.php
index c80d2583..68cbe345 100644
--- a/includes/MagicWord.php
+++ b/includes/MagicWord.php
@@ -6,170 +6,21 @@
*/
/**
- * private
- */
-$wgMagicFound = false;
-
-/** Actual keyword to be used is set in Language.php */
-
-$magicWords = array(
- 'MAG_REDIRECT',
- 'MAG_NOTOC',
- 'MAG_START',
- 'MAG_CURRENTMONTH',
- 'MAG_CURRENTMONTHNAME',
- 'MAG_CURRENTMONTHNAMEGEN',
- 'MAG_CURRENTMONTHABBREV',
- 'MAG_CURRENTDAY',
- 'MAG_CURRENTDAY2',
- 'MAG_CURRENTDAYNAME',
- 'MAG_CURRENTYEAR',
- 'MAG_CURRENTTIME',
- 'MAG_NUMBEROFARTICLES',
- 'MAG_SUBST',
- 'MAG_MSG',
- 'MAG_MSGNW',
- 'MAG_NOEDITSECTION',
- 'MAG_END',
- 'MAG_IMG_THUMBNAIL',
- 'MAG_IMG_RIGHT',
- 'MAG_IMG_LEFT',
- 'MAG_IMG_NONE',
- 'MAG_IMG_WIDTH',
- 'MAG_IMG_CENTER',
- 'MAG_INT',
- 'MAG_FORCETOC',
- 'MAG_SITENAME',
- 'MAG_NS',
- 'MAG_LOCALURL',
- 'MAG_LOCALURLE',
- 'MAG_SERVER',
- 'MAG_IMG_FRAMED',
- 'MAG_PAGENAME',
- 'MAG_PAGENAMEE',
- 'MAG_NAMESPACE',
- 'MAG_NAMESPACEE',
- 'MAG_TOC',
- 'MAG_GRAMMAR',
- 'MAG_NOTITLECONVERT',
- 'MAG_NOCONTENTCONVERT',
- 'MAG_CURRENTWEEK',
- 'MAG_CURRENTDOW',
- 'MAG_REVISIONID',
- 'MAG_SCRIPTPATH',
- 'MAG_SERVERNAME',
- 'MAG_NUMBEROFFILES',
- 'MAG_IMG_MANUALTHUMB',
- 'MAG_PLURAL',
- 'MAG_FULLURL',
- 'MAG_FULLURLE',
- 'MAG_LCFIRST',
- 'MAG_UCFIRST',
- 'MAG_LC',
- 'MAG_UC',
- 'MAG_FULLPAGENAME',
- 'MAG_FULLPAGENAMEE',
- 'MAG_RAW',
- 'MAG_SUBPAGENAME',
- 'MAG_SUBPAGENAMEE',
- 'MAG_DISPLAYTITLE',
- 'MAG_TALKSPACE',
- 'MAG_TALKSPACEE',
- 'MAG_SUBJECTSPACE',
- 'MAG_SUBJECTSPACEE',
- 'MAG_TALKPAGENAME',
- 'MAG_TALKPAGENAMEE',
- 'MAG_SUBJECTPAGENAME',
- 'MAG_SUBJECTPAGENAMEE',
- 'MAG_NUMBEROFUSERS',
- 'MAG_RAWSUFFIX',
- 'MAG_NEWSECTIONLINK',
- 'MAG_NUMBEROFPAGES',
- 'MAG_CURRENTVERSION',
- 'MAG_BASEPAGENAME',
- 'MAG_BASEPAGENAMEE',
- 'MAG_URLENCODE',
- 'MAG_CURRENTTIMESTAMP',
- 'MAG_DIRECTIONMARK',
- 'MAG_LANGUAGE',
- 'MAG_CONTENTLANGUAGE',
- 'MAG_PAGESINNAMESPACE',
- 'MAG_NOGALLERY',
- 'MAG_NUMBEROFADMINS',
- 'MAG_FORMATNUM',
-);
-if ( ! defined( 'MEDIAWIKI_INSTALL' ) )
- wfRunHooks( 'MagicWordMagicWords', array( &$magicWords ) );
-
-for ( $i = 0; $i < count( $magicWords ); ++$i )
- define( $magicWords[$i], $i );
-
-$wgVariableIDs = array(
- MAG_CURRENTMONTH,
- MAG_CURRENTMONTHNAME,
- MAG_CURRENTMONTHNAMEGEN,
- MAG_CURRENTMONTHABBREV,
- MAG_CURRENTDAY,
- MAG_CURRENTDAY2,
- MAG_CURRENTDAYNAME,
- MAG_CURRENTYEAR,
- MAG_CURRENTTIME,
- MAG_NUMBEROFARTICLES,
- MAG_NUMBEROFFILES,
- MAG_SITENAME,
- MAG_SERVER,
- MAG_SERVERNAME,
- MAG_SCRIPTPATH,
- MAG_PAGENAME,
- MAG_PAGENAMEE,
- MAG_FULLPAGENAME,
- MAG_FULLPAGENAMEE,
- MAG_NAMESPACE,
- MAG_NAMESPACEE,
- MAG_CURRENTWEEK,
- MAG_CURRENTDOW,
- MAG_REVISIONID,
- MAG_SUBPAGENAME,
- MAG_SUBPAGENAMEE,
- MAG_DISPLAYTITLE,
- MAG_TALKSPACE,
- MAG_TALKSPACEE,
- MAG_SUBJECTSPACE,
- MAG_SUBJECTSPACEE,
- MAG_TALKPAGENAME,
- MAG_TALKPAGENAMEE,
- MAG_SUBJECTPAGENAME,
- MAG_SUBJECTPAGENAMEE,
- MAG_NUMBEROFUSERS,
- MAG_RAWSUFFIX,
- MAG_NEWSECTIONLINK,
- MAG_NUMBEROFPAGES,
- MAG_CURRENTVERSION,
- MAG_BASEPAGENAME,
- MAG_BASEPAGENAMEE,
- MAG_URLENCODE,
- MAG_CURRENTTIMESTAMP,
- MAG_DIRECTIONMARK,
- MAG_LANGUAGE,
- MAG_CONTENTLANGUAGE,
- MAG_PAGESINNAMESPACE,
- MAG_NUMBEROFADMINS,
-);
-if ( ! defined( 'MEDIAWIKI_INSTALL' ) )
- wfRunHooks( 'MagicWordwgVariableIDs', array( &$wgVariableIDs ) );
-
-/**
* This class encapsulates "magic words" such as #redirect, __NOTOC__, etc.
* Usage:
- * if (MagicWord::get( MAG_REDIRECT )->match( $text ) )
+ * if (MagicWord::get( 'redirect' )->match( $text ) )
*
* Possible future improvements:
* * Simultaneous searching for a number of magic words
- * * $wgMagicWords in shared memory
+ * * MagicWord::$mObjects in shared memory
*
* Please avoid reading the data out of one of these objects and then writing
* special case code. If possible, add another match()-like function here.
*
+ * To add magic words in an extension, use the LanguageGetMagic hook. For
+ * magic words which are also Parser variables, add a MagicWordwgVariableIDs
+ * hook. Use string keys.
+ *
* @package MediaWiki
*/
class MagicWord {
@@ -178,7 +29,82 @@ class MagicWord {
*/
var $mId, $mSynonyms, $mCaseSensitive, $mRegex;
var $mRegexStart, $mBaseRegex, $mVariableRegex;
- var $mModified;
+ var $mModified, $mFound;
+
+ static public $mVariableIDsInitialised = false;
+ static public $mVariableIDs = array(
+ 'currentmonth',
+ 'currentmonthname',
+ 'currentmonthnamegen',
+ 'currentmonthabbrev',
+ 'currentday',
+ 'currentday2',
+ 'currentdayname',
+ 'currentyear',
+ 'currenttime',
+ 'currenthour',
+ 'localmonth',
+ 'localmonthname',
+ 'localmonthnamegen',
+ 'localmonthabbrev',
+ 'localday',
+ 'localday2',
+ 'localdayname',
+ 'localyear',
+ 'localtime',
+ 'localhour',
+ 'numberofarticles',
+ 'numberoffiles',
+ 'sitename',
+ 'server',
+ 'servername',
+ 'scriptpath',
+ 'pagename',
+ 'pagenamee',
+ 'fullpagename',
+ 'fullpagenamee',
+ 'namespace',
+ 'namespacee',
+ 'currentweek',
+ 'currentdow',
+ 'localweek',
+ 'localdow',
+ 'revisionid',
+ 'revisionday',
+ 'revisionday2',
+ 'revisionmonth',
+ 'revisionyear',
+ 'revisiontimestamp',
+ 'subpagename',
+ 'subpagenamee',
+ 'displaytitle',
+ 'talkspace',
+ 'talkspacee',
+ 'subjectspace',
+ 'subjectspacee',
+ 'talkpagename',
+ 'talkpagenamee',
+ 'subjectpagename',
+ 'subjectpagenamee',
+ 'numberofusers',
+ 'rawsuffix',
+ 'newsectionlink',
+ 'numberofpages',
+ 'currentversion',
+ 'basepagename',
+ 'basepagenamee',
+ 'urlencode',
+ 'currenttimestamp',
+ 'localtimestamp',
+ 'directionmark',
+ 'language',
+ 'contentlanguage',
+ 'pagesinnamespace',
+ 'numberofadmins',
+ );
+
+ static public $mObjects = array();
+
/**#@-*/
function MagicWord($id = 0, $syn = '', $cs = false) {
@@ -196,18 +122,32 @@ class MagicWord {
* Factory: creates an object representing an ID
* @static
*/
- function &get( $id ) {
- global $wgMagicWords;
-
- if ( !is_array( $wgMagicWords ) ) {
- throw new MWException( "Incorrect initialisation order, \$wgMagicWords does not exist\n" );
- }
- if (!array_key_exists( $id, $wgMagicWords ) ) {
+ static function &get( $id ) {
+ if (!array_key_exists( $id, self::$mObjects ) ) {
$mw = new MagicWord();
$mw->load( $id );
- $wgMagicWords[$id] = $mw;
+ self::$mObjects[$id] = $mw;
+ }
+ return self::$mObjects[$id];
+ }
+
+ /**
+ * Get an array of parser variable IDs
+ */
+ static function getVariableIDs() {
+ if ( !self::$mVariableIDsInitialised ) {
+ # Deprecated constant definition hook, available for extensions that need it
+ $magicWords = array();
+ wfRunHooks( 'MagicWordMagicWords', array( &$magicWords ) );
+ foreach ( $magicWords as $word ) {
+ define( $word, $word );
+ }
+
+ # Get variable IDs
+ wfRunHooks( 'MagicWordwgVariableIDs', array( &self::$mVariableIDs ) );
+ self::$mVariableIDsInitialised = true;
}
- return $wgMagicWords[$id];
+ return self::$mVariableIDs;
}
# Initialises this object with an ID
@@ -215,6 +155,11 @@ class MagicWord {
global $wgContLang;
$this->mId = $id;
$wgContLang->getMagic( $this );
+ if ( !$this->mSynonyms ) {
+ $this->mSynonyms = array( 'dkjsagfjsgashfajsh' );
+ #throw new MWException( "Error: invalid magic word '$id'" );
+ wfDebugLog( 'exception', "Error: invalid magic word '$id'\n" );
+ }
}
/**
@@ -233,7 +178,7 @@ class MagicWord {
$escSyn[] = preg_quote( $synonym, '/' );
$this->mBaseRegex = implode( '|', $escSyn );
- $case = $this->mCaseSensitive ? '' : 'i';
+ $case = $this->mCaseSensitive ? '' : 'iu';
$this->mRegex = "/{$this->mBaseRegex}/{$case}";
$this->mRegexStart = "/^(?:{$this->mBaseRegex})/{$case}";
$this->mVariableRegex = str_replace( "\\$1", "(.*?)", $this->mRegex );
@@ -260,7 +205,7 @@ class MagicWord {
if ( $this->mRegex === '' )
$this->initRegex();
- return $this->mCaseSensitive ? '' : 'i';
+ return $this->mCaseSensitive ? '' : 'iu';
}
/**
@@ -310,14 +255,16 @@ class MagicWord {
$matchcount = preg_match( $this->getVariableStartToEndRegex(), $text, $matches );
if ( $matchcount == 0 ) {
return NULL;
- } elseif ( count($matches) == 1 ) {
- return $matches[0];
} else {
# multiple matched parts (variable match); some will be empty because of
# synonyms. The variable will be the second non-empty one so remove any
# blank elements and re-sort the indices.
+ # See also bug 6526
+
$matches = array_values(array_filter($matches));
- return $matches[1];
+
+ if ( count($matches) == 1 ) { return $matches[0]; }
+ else { return $matches[1]; }
}
}
@@ -327,19 +274,25 @@ class MagicWord {
* input string, removing all instances of the word
*/
function matchAndRemove( &$text ) {
- global $wgMagicFound;
- $wgMagicFound = false;
- $text = preg_replace_callback( $this->getRegex(), 'pregRemoveAndRecord', $text );
- return $wgMagicFound;
+ $this->mFound = false;
+ $text = preg_replace_callback( $this->getRegex(), array( &$this, 'pregRemoveAndRecord' ), $text );
+ return $this->mFound;
}
function matchStartAndRemove( &$text ) {
- global $wgMagicFound;
- $wgMagicFound = false;
- $text = preg_replace_callback( $this->getRegexStart(), 'pregRemoveAndRecord', $text );
- return $wgMagicFound;
+ $this->mFound = false;
+ $text = preg_replace_callback( $this->getRegexStart(), array( &$this, 'pregRemoveAndRecord' ), $text );
+ return $this->mFound;
}
+ /**
+ * Used in matchAndRemove()
+ * @private
+ **/
+ function pregRemoveAndRecord( $match ) {
+ $this->mFound = true;
+ return '';
+ }
/**
* Replaces the word with something else
@@ -425,8 +378,9 @@ class MagicWord {
* lookup in a list of magic words
*/
function addToArray( &$array, $value ) {
+ global $wgContLang;
foreach ( $this->mSynonyms as $syn ) {
- $array[$syn] = $value;
+ $array[$wgContLang->lc($syn)] = $value;
}
}
@@ -435,14 +389,4 @@ class MagicWord {
}
}
-/**
- * Used in matchAndRemove()
- * @private
- **/
-function pregRemoveAndRecord( $match ) {
- global $wgMagicFound;
- $wgMagicFound = true;
- return '';
-}
-
?>
diff --git a/includes/Math.php b/includes/Math.php
index f9d6a605..a8b33984 100644
--- a/includes/Math.php
+++ b/includes/Math.php
@@ -259,7 +259,7 @@ class MathRenderer {
return $path;
}
- function renderMath( $tex ) {
+ public static function renderMath( $tex ) {
global $wgUser;
$math = new MathRenderer( $tex );
$math->setOutputMode( $wgUser->getOption('math'));
diff --git a/includes/MemcachedSessions.php b/includes/MemcachedSessions.php
index af49109c..e2dc52ca 100644
--- a/includes/MemcachedSessions.php
+++ b/includes/MemcachedSessions.php
@@ -13,8 +13,7 @@
* @todo document
*/
function memsess_key( $id ) {
- global $wgDBname;
- return "$wgDBname:session:$id";
+ return wfMemcKey( 'session', $id );
}
/**
diff --git a/includes/MessageCache.php b/includes/MessageCache.php
index c8b7124c..9cab222b 100644
--- a/includes/MessageCache.php
+++ b/includes/MessageCache.php
@@ -36,10 +36,6 @@ class MessageCache {
$this->mMemcKey = $memcPrefix.':messages';
$this->mKeys = false; # initialised on demand
$this->mInitialised = true;
-
- wfProfileIn( __METHOD__.'-parseropt' );
- $this->mParserOptions = new ParserOptions( $u=NULL );
- wfProfileOut( __METHOD__.'-parseropt' );
$this->mParser = null;
# When we first get asked for a message,
@@ -51,18 +47,25 @@ class MessageCache {
wfProfileOut( __METHOD__ );
}
+ function getParserOptions() {
+ if ( !$this->mParserOptions ) {
+ $this->mParserOptions = new ParserOptions;
+ }
+ return $this->mParserOptions;
+ }
+
/**
* Try to load the cache from a local file
*/
function loadFromLocal( $hash ) {
- global $wgLocalMessageCache, $wgDBname;
+ global $wgLocalMessageCache;
$this->mCache = false;
if ( $wgLocalMessageCache === false ) {
return;
}
- $filename = "$wgLocalMessageCache/messages-$wgDBname";
+ $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
wfSuppressWarnings();
$file = fopen( $filename, 'r' );
@@ -75,7 +78,7 @@ class MessageCache {
$localHash = fread( $file, 32 );
if ( $hash == $localHash ) {
// All good, get the rest of it
- $serialized = fread( $file, 1000000 );
+ $serialized = fread( $file, 10000000 );
$this->mCache = unserialize( $serialized );
}
fclose( $file );
@@ -85,13 +88,13 @@ class MessageCache {
* Save the cache to a local file
*/
function saveToLocal( $serialized, $hash ) {
- global $wgLocalMessageCache, $wgDBname;
+ global $wgLocalMessageCache;
if ( $wgLocalMessageCache === false ) {
return;
}
- $filename = "$wgLocalMessageCache/messages-$wgDBname";
+ $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
$oldUmask = umask( 0 );
wfMkdirParents( $wgLocalMessageCache, 0777 );
umask( $oldUmask );
@@ -108,12 +111,12 @@ class MessageCache {
}
function loadFromScript( $hash ) {
- global $wgLocalMessageCache, $wgDBname;
+ global $wgLocalMessageCache;
if ( $wgLocalMessageCache === false ) {
return;
}
- $filename = "$wgLocalMessageCache/messages-$wgDBname";
+ $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
wfSuppressWarnings();
$file = fopen( $filename, 'r' );
@@ -126,16 +129,16 @@ class MessageCache {
if ($hash!=$localHash) {
return;
}
- require("$wgLocalMessageCache/messages-$wgDBname");
+ require("$wgLocalMessageCache/messages-" . wfWikiID());
}
function saveToScript($array, $hash) {
- global $wgLocalMessageCache, $wgDBname;
+ global $wgLocalMessageCache;
if ( $wgLocalMessageCache === false ) {
return;
}
- $filename = "$wgLocalMessageCache/messages-$wgDBname";
+ $filename = "$wgLocalMessageCache/messages-" . wfWikiID();
$oldUmask = umask( 0 );
wfMkdirParents( $wgLocalMessageCache, 0777 );
umask( $oldUmask );
@@ -190,6 +193,9 @@ class MessageCache {
} else {
$this->loadFromScript( $hash );
}
+ if ( $this->mCache ) {
+ wfDebug( "MessageCache::load(): got from local cache\n" );
+ }
}
wfProfileOut( $fname.'-fromlocal' );
@@ -197,18 +203,20 @@ class MessageCache {
if ( !$this->mCache ) {
wfProfileIn( $fname.'-fromcache' );
$this->mCache = $this->mMemc->get( $this->mMemcKey );
-
- # Save to local cache
- if ( $wgLocalMessageCache !== false ) {
- $serialized = serialize( $this->mCache );
- if ( !$hash ) {
- $hash = md5( $serialized );
- $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
- }
- if ($wgLocalMessageCacheSerialized) {
- $this->saveToLocal( $serialized,$hash );
- } else {
- $this->saveToScript( $this->mCache, $hash );
+ if ( $this->mCache ) {
+ wfDebug( "MessageCache::load(): got from global cache\n" );
+ # Save to local cache
+ if ( $wgLocalMessageCache !== false ) {
+ $serialized = serialize( $this->mCache );
+ if ( !$hash ) {
+ $hash = md5( $serialized );
+ $this->mMemc->set( "{$this->mMemcKey}-hash", $hash, $this->mExpiry );
+ }
+ if ($wgLocalMessageCacheSerialized) {
+ $this->saveToLocal( $serialized,$hash );
+ } else {
+ $this->saveToScript( $this->mCache, $hash );
+ }
}
}
wfProfileOut( $fname.'-fromcache' );
@@ -283,7 +291,7 @@ class MessageCache {
* Loads all or main part of cacheable messages from the database
*/
function loadFromDB() {
- global $wgAllMessagesEn, $wgLang;
+ global $wgLang;
$fname = 'MessageCache::loadFromDB';
$dbr =& wfGetDB( DB_SLAVE );
@@ -306,7 +314,8 @@ class MessageCache {
# Negative caching
# Go through the language array and the extension array and make a note of
# any keys missing from the cache
- foreach ( $wgAllMessagesEn as $key => $value ) {
+ $allMessages = Language::getMessagesFor( 'en' );
+ foreach ( $allMessages as $key => $value ) {
$uckey = $wgLang->ucfirst( $key );
if ( !array_key_exists( $uckey, $this->mCache ) ) {
$this->mCache[$uckey] = false;
@@ -314,7 +323,7 @@ class MessageCache {
}
# Make sure all extension messages are available
- wfLoadAllExtensions();
+ MessageCache::loadAllMessages();
# Add them to the cache
foreach ( $this->mExtensionMessages as $key => $value ) {
@@ -332,10 +341,11 @@ class MessageCache {
* Not really needed anymore
*/
function getKeys() {
- global $wgAllMessagesEn, $wgContLang;
+ global $wgContLang;
if ( !$this->mKeys ) {
$this->mKeys = array();
- foreach ( $wgAllMessagesEn as $key => $value ) {
+ $allMessages = Language::getMessagesFor( 'en' );
+ foreach ( $allMessages as $key => $value ) {
$title = $wgContLang->ucfirst( $key );
array_push( $this->mKeys, $title );
}
@@ -351,11 +361,11 @@ class MessageCache {
}
function replace( $title, $text ) {
- global $wgLocalMessageCache, $wgLocalMessageCacheSerialized, $parserMemc, $wgDBname;
+ global $wgLocalMessageCache, $wgLocalMessageCacheSerialized, $parserMemc;
$this->lock();
$this->load();
- $parserMemc->delete("$wgDBname:sidebar");
+ $parserMemc->delete(wfMemcKey('sidebar'));
if ( is_array( $this->mCache ) ) {
$this->mCache[$title] = $text;
$this->mMemc->set( $this->mMemcKey, $this->mCache, $this->mExpiry );
@@ -403,17 +413,14 @@ class MessageCache {
$this->mMemc->delete( $lockKey );
}
- function get( $key, $useDB, $forcontent=true, $isfullkey = false ) {
- global $wgContLanguageCode;
+ function get( $key, $useDB = true, $forcontent = true, $isfullkey = false ) {
+ global $wgContLanguageCode, $wgContLang, $wgLang;
if( $forcontent ) {
- global $wgContLang;
$lang =& $wgContLang;
- $langcode = $wgContLanguageCode;
} else {
- global $wgLang, $wgLanguageCode;
$lang =& $wgLang;
- $langcode = $wgLanguageCode;
}
+ $langcode = $lang->getCode();
# If uninitialised, someone is trying to call this halfway through Setup.php
if( !$this->mInitialised ) {
return '&lt;' . htmlspecialchars($key) . '&gt;';
@@ -425,7 +432,7 @@ class MessageCache {
$message = false;
if( !$this->mDisable && $useDB ) {
- $title = $lang->ucfirst( $key );
+ $title = $wgContLang->ucfirst( $key );
if(!$isfullkey && ($langcode != $wgContLanguageCode) ) {
$title .= '/' . $langcode;
}
@@ -442,6 +449,7 @@ class MessageCache {
# Try the array in the language object
if( $message === false ) {
+ #wfDebug( "Trying language object for message $key\n" );
wfSuppressWarnings();
$message = $lang->getMessage( $key );
wfRestoreWarnings();
@@ -460,11 +468,26 @@ class MessageCache {
}
}
+ # Try the array of another language
+ if( $message === false && strpos( $key, '/' ) ) {
+ $message = explode( '/', $key );
+ if ( $message[1] ) {
+ wfSuppressWarnings();
+ $message = Language::getMessageFor( $message[0], $message[1] );
+ wfRestoreWarnings();
+ if ( is_null( $message ) ) {
+ $message = false;
+ }
+ } else {
+ $message = false;
+ }
+ }
+
# Is this a custom message? Try the default language in the db...
if( ($message === false || $message === '-' ) &&
!$this->mDisable && $useDB &&
!$isfullkey && ($langcode != $wgContLanguageCode) ) {
- $message = $this->getFromCache( $lang->ucfirst( $key ) );
+ $message = $this->getFromCache( $wgContLang->ucfirst( $key ) );
}
# Final fallback
@@ -489,6 +512,7 @@ class MessageCache {
if ( $this->mUseCache ) {
$message = $this->mMemc->get( $this->mMemcKey . ':' . $title );
if ( $message == '###NONEXISTENT###' ) {
+ $this->mCache[$title] = false;
return false;
} elseif( !is_null( $message ) ) {
$this->mCache[$title] = $message;
@@ -516,6 +540,7 @@ class MessageCache {
# Negative caching
# Use some special text instead of false, because false gets converted to '' somewhere
$this->mMemc->set( $this->mMemcKey . ':' . $title, '###NONEXISTENT###', $this->mExpiry );
+ $this->mCache[$title] = false;
}
return $message;
@@ -531,7 +556,7 @@ class MessageCache {
}
if ( !$this->mDisableTransform && $this->mParser ) {
if( strpos( $message, '{{' ) !== false ) {
- $message = $this->mParser->transformMsg( $message, $this->mParserOptions );
+ $message = $this->mParser->transformMsg( $message, $this->getParserOptions() );
}
}
return $message;
@@ -570,12 +595,43 @@ class MessageCache {
}
/**
+ * Get the extension messages for a specific language
+ *
+ * @param string $lang The messages language, English by default
+ */
+ function getExtensionMessagesFor( $lang = 'en' ) {
+ wfProfileIn( __METHOD__ );
+ $messages = array();
+ foreach( $this->mExtensionMessages as $key => $message ) {
+ if ( isset( $message[$lang] ) ) {
+ $messages[$key] = $message[$lang];
+ } elseif ( isset( $message['en'] ) ) {
+ $messages[$key] = $message['en'];
+ }
+ }
+ wfProfileOut( __METHOD__ );
+ return $messages;
+ }
+
+ /**
* Clear all stored messages. Mainly used after a mass rebuild.
*/
function clear() {
+ global $wgLocalMessageCache;
if( $this->mUseCache ) {
+ # Global cache
$this->mMemc->delete( $this->mMemcKey );
+ # Invalidate all local caches
+ $this->mMemc->delete( "{$this->mMemcKey}-hash" );
}
}
+
+ static function loadAllMessages() {
+ # Some extensions will load their messages when you load their class file
+ wfLoadAllExtensions();
+ # Others will respond to this hook
+ wfRunHooks( 'LoadAllMessages' );
+ # Still others will respond to neither, they are EVIL. We sometimes need to know!
+ }
}
?>
diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php
index 30861ba3..dd197c31 100644
--- a/includes/MimeMagic.php
+++ b/includes/MimeMagic.php
@@ -74,7 +74,7 @@ if ($wgLoadFileinfoExtension) {
* file extension,
*
* Instances of this class are stateles, there only needs to be one global instance
-* of MimeMagic. Please use wfGetMimeMagic to get that instance.
+* of MimeMagic. Please use MimeMagic::singleton() to get that instance.
* @package MediaWiki
*/
class MimeMagic {
@@ -97,8 +97,11 @@ class MimeMagic {
*/
var $mExtToMime= NULL;
- /** Initializes the MimeMagic object. This is called by wfGetMimeMagic when instantiation
- * the global MimeMagic singleton object.
+ /** The singleton instance
+ */
+ private static $instance;
+
+ /** Initializes the MimeMagic object. This is called by MimeMagic::singleton().
*
* This constructor parses the mime.types and mime.info files and build internal mappings.
*/
@@ -227,6 +230,16 @@ class MimeMagic {
}
+ /**
+ * Get an instance of this class
+ */
+ static function &singleton() {
+ if ( !isset( self::$instance ) ) {
+ self::$instance = new MimeMagic;
+ }
+ return self::$instance;
+ }
+
/** returns a list of file extensions for a given mime type
* as a space separated string.
*/
@@ -497,13 +510,22 @@ class MimeMagic {
# NOTE: this function is available since PHP 4.3.0, but only if
# PHP was compiled with --with-mime-magic or, before 4.3.2, with --enable-mime-magic.
#
- # On Winodws, you must set mime_magic.magicfile in php.ini to point to the mime.magic file bundeled with PHP;
+ # On Windows, you must set mime_magic.magicfile in php.ini to point to the mime.magic file bundeled with PHP;
# sometimes, this may even be needed under linus/unix.
#
# Also note that this has been DEPRECATED in favor of the fileinfo extension by PECL, see above.
# see http://www.php.net/manual/en/ref.mime-magic.php for details.
$m= mime_content_type($file);
+
+ if ( $m == 'text/plain' ) {
+ // mime_content_type sometimes considers DJVU files to be text/plain.
+ $deja = new DjVuImage( $file );
+ if( $deja->isValid() ) {
+ wfDebug("$fname: (re)detected $file as image/vnd.djvu\n");
+ $m = 'image/vnd.djvu';
+ }
+ }
}
else wfDebug("$fname: no magic mime detector found!\n");
diff --git a/includes/Namespace.php b/includes/Namespace.php
index ab7511d0..73dc2969 100644
--- a/includes/Namespace.php
+++ b/includes/Namespace.php
@@ -49,7 +49,7 @@ class Namespace {
* Check if the given namespace might be moved
* @return bool
*/
- function isMovable( $index ) {
+ static function isMovable( $index ) {
return !( $index < NS_MAIN || $index == NS_IMAGE || $index == NS_CATEGORY );
}
@@ -57,7 +57,7 @@ class Namespace {
* Check if the given namespace is not a talk page
* @return bool
*/
- function isMain( $index ) {
+ static function isMain( $index ) {
return ! Namespace::isTalk( $index );
}
@@ -65,7 +65,7 @@ class Namespace {
* Check if the give namespace is a talk page
* @return bool
*/
- function isTalk( $index ) {
+ static function isTalk( $index ) {
return ($index > NS_MAIN) // Special namespaces are negative
&& ($index % 2); // Talk namespaces are odd-numbered
}
@@ -73,7 +73,7 @@ class Namespace {
/**
* Get the talk namespace corresponding to the given index
*/
- function getTalk( $index ) {
+ static function getTalk( $index ) {
if ( Namespace::isTalk( $index ) ) {
return $index;
} else {
@@ -82,7 +82,7 @@ class Namespace {
}
}
- function getSubject( $index ) {
+ static function getSubject( $index ) {
if ( Namespace::isTalk( $index ) ) {
return $index - 1;
} else {
@@ -93,7 +93,7 @@ class Namespace {
/**
* Returns the canonical (English Wikipedia) name for a given index
*/
- function getCanonicalName( $index ) {
+ static function getCanonicalName( $index ) {
global $wgCanonicalNamespaceNames;
return $wgCanonicalNamespaceNames[$index];
}
@@ -102,7 +102,7 @@ class Namespace {
* Returns the index for a given canonical name, or NULL
* The input *must* be converted to lower case first
*/
- function getCanonicalIndex( $name ) {
+ static function getCanonicalIndex( $name ) {
global $wgCanonicalNamespaceNames;
static $xNamespaces = false;
if ( $xNamespaces === false ) {
@@ -122,7 +122,7 @@ class Namespace {
* Can this namespace ever have a talk namespace?
* @param $index Namespace index
*/
- function canTalk( $index ) {
+ static function canTalk( $index ) {
return( $index >= NS_MAIN );
}
}
diff --git a/includes/ObjectCache.php b/includes/ObjectCache.php
index fe7417d2..2b26cf4e 100644
--- a/includes/ObjectCache.php
+++ b/includes/ObjectCache.php
@@ -69,13 +69,10 @@ function &wfGetCache( $inputType ) {
} elseif ( $type == CACHE_ACCEL ) {
if ( !array_key_exists( CACHE_ACCEL, $wgCaches ) ) {
if ( function_exists( 'eaccelerator_get' ) ) {
- require_once( 'BagOStuff.php' );
$wgCaches[CACHE_ACCEL] = new eAccelBagOStuff;
} elseif ( function_exists( 'apc_fetch') ) {
- require_once( 'BagOStuff.php' );
$wgCaches[CACHE_ACCEL] = new APCBagOStuff;
} elseif ( function_exists( 'mmcache_get' ) ) {
- require_once( 'BagOStuff.php' );
$wgCaches[CACHE_ACCEL] = new TurckBagOStuff;
} else {
$wgCaches[CACHE_ACCEL] = false;
@@ -84,11 +81,15 @@ function &wfGetCache( $inputType ) {
if ( $wgCaches[CACHE_ACCEL] !== false ) {
$cache =& $wgCaches[CACHE_ACCEL];
}
+ } elseif ( $type == CACHE_DBA ) {
+ if ( !array_key_exists( CACHE_DBA, $wgCaches ) ) {
+ $wgCaches[CACHE_DBA] = new DBABagOStuff;
+ }
+ $cache =& $wgCaches[CACHE_DBA];
}
-
+
if ( $type == CACHE_DB || ( $inputType == CACHE_ANYTHING && $cache === false ) ) {
if ( !array_key_exists( CACHE_DB, $wgCaches ) ) {
- require_once( 'BagOStuff.php' );
$wgCaches[CACHE_DB] = new MediaWikiBagOStuff('objectcache');
}
$cache =& $wgCaches[CACHE_DB];
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index 31a0781a..0d55c2e0 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -22,7 +22,7 @@ class OutputPage {
var $mDoNothing;
var $mContainsOldMagic, $mContainsNewMagic;
var $mIsArticleRelated;
- var $mParserOptions;
+ protected $mParserOptions; // lazy initialised, use parserOptions()
var $mShowFeedLinks = false;
var $mEnableClientCache = true;
var $mArticleBodyOnly = false;
@@ -46,7 +46,7 @@ class OutputPage {
$this->mCategoryLinks = array();
$this->mDoNothing = false;
$this->mContainsOldMagic = $this->mContainsNewMagic = 0;
- $this->mParserOptions = ParserOptions::newFromUser( $temp = NULL );
+ $this->mParserOptions = null;
$this->mSquidMaxage = 0;
$this->mScripts = '';
$this->mETag = false;
@@ -92,7 +92,7 @@ class OutputPage {
* returns true iff cache-ok headers was sent.
*/
function checkLastModified ( $timestamp ) {
- global $wgCachePages, $wgCacheEpoch, $wgUser;
+ global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest;
$fname = 'OutputPage::checkLastModified';
if ( !$timestamp || $timestamp == '19700101000000' ) {
@@ -122,7 +122,7 @@ class OutputPage {
wfDebug( "$fname: -- we might send Last-Modified : $lastmod\n", false );
if( ($ismodsince >= $timestamp ) && $wgUser->validateCache( $ismodsince ) && $ismodsince >= $wgCacheEpoch ) {
# Make sure you're in a place you can leave when you call us!
- header( "HTTP/1.0 304 Not Modified" );
+ $wgRequest->response()->header( "HTTP/1.0 304 Not Modified" );
$this->mLastModified = $lastmod;
$this->sendCacheControl();
wfDebug( "$fname: CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false );
@@ -255,10 +255,13 @@ class OutputPage {
/* @deprecated */
function setParserOptions( $options ) {
- return $this->ParserOptions( $options );
+ return $this->parserOptions( $options );
}
- function ParserOptions( $options = null ) {
+ function parserOptions( $options = null ) {
+ if ( !$this->mParserOptions ) {
+ $this->mParserOptions = new ParserOptions;
+ }
return wfSetVar( $this->mParserOptions, $options );
}
@@ -289,9 +292,13 @@ class OutputPage {
function addWikiTextTitle($text, &$title, $linestart) {
global $wgParser;
- $parserOutput = $wgParser->parse( $text, $title, $this->mParserOptions,
+ $fname = 'OutputPage:addWikiTextTitle';
+ wfProfileIn($fname);
+ wfIncrStats('pcache_not_possible');
+ $parserOutput = $wgParser->parse( $text, $title, $this->parserOptions(),
$linestart, true, $this->mRevisionId );
$this->addParserOutput( $parserOutput );
+ wfProfileOut($fname);
}
function addParserOutputNoText( &$parserOutput ) {
@@ -304,13 +311,19 @@ class OutputPage {
}
if ( $parserOutput->mHTMLtitle != "" ) {
$this->mPagetitle = $parserOutput->mHTMLtitle ;
+ }
+ if ( $parserOutput->mSubtitle != '' ) {
$this->mSubtitle .= $parserOutput->mSubtitle ;
}
+ $this->mNoGallery = $parserOutput->getNoGallery();
+ wfRunHooks( 'OutputPageParserOutput', array( &$this, $parserOutput ) );
}
function addParserOutput( &$parserOutput ) {
$this->addParserOutputNoText( $parserOutput );
- $this->addHTML( $parserOutput->getText() );
+ $text = $parserOutput->getText();
+ wfRunHooks( 'OutputPageBeforeHTML',array( &$this, &$text ) );
+ $this->addHTML( $text );
}
/**
@@ -320,21 +333,17 @@ class OutputPage {
function addPrimaryWikiText( $text, $article, $cache = true ) {
global $wgParser, $wgUser;
- $this->mParserOptions->setTidy(true);
+ $popts = $this->parserOptions();
+ $popts->setTidy(true);
$parserOutput = $wgParser->parse( $text, $article->mTitle,
- $this->mParserOptions, true, true, $this->mRevisionId );
- $this->mParserOptions->setTidy(false);
+ $popts, true, true, $this->mRevisionId );
+ $popts->setTidy(false);
if ( $cache && $article && $parserOutput->getCacheTime() != -1 ) {
$parserCache =& ParserCache::singleton();
$parserCache->save( $parserOutput, $article, $wgUser );
}
- $this->addParserOutputNoText( $parserOutput );
- $text = $parserOutput->getText();
- $this->mNoGallery = $parserOutput->getNoGallery();
- wfRunHooks( 'OutputPageBeforeHTML',array( &$this, &$text ) );
- $parserOutput->setText( $text );
- $this->addHTML( $parserOutput->getText() );
+ $this->addParserOutput( $parserOutput );
}
/**
@@ -342,9 +351,10 @@ class OutputPage {
*/
function addSecondaryWikiText( $text, $linestart = true ) {
global $wgTitle;
- $this->mParserOptions->setTidy(true);
+ $popts = $this->parserOptions();
+ $popts->setTidy(true);
$this->addWikiTextTitle($text, $wgTitle, $linestart);
- $this->mParserOptions->setTidy(false);
+ $popts->setTidy(false);
}
@@ -364,10 +374,11 @@ class OutputPage {
*/
function parse( $text, $linestart = true, $interface = false ) {
global $wgParser, $wgTitle;
- if ( $interface) { $this->mParserOptions->setInterfaceMessage(true); }
- $parserOutput = $wgParser->parse( $text, $wgTitle, $this->mParserOptions,
+ $popts = $this->parserOptions();
+ if ( $interface) { $popts->setInterfaceMessage(true); }
+ $parserOutput = $wgParser->parse( $text, $wgTitle, $popts,
$linestart, true, $this->mRevisionId );
- if ( $interface) { $this->mParserOptions->setInterfaceMessage(false); }
+ if ( $interface) { $popts->setInterfaceMessage(false); }
return $parserOutput->getText();
}
@@ -381,18 +392,7 @@ class OutputPage {
$parserCache =& ParserCache::singleton();
$parserOutput = $parserCache->get( $article, $user );
if ( $parserOutput !== false ) {
- $this->mLanguageLinks += $parserOutput->getLanguageLinks();
- $this->addCategoryLinks( $parserOutput->getCategories() );
- $this->addKeywords( $parserOutput );
- $this->mNewSectionLink = $parserOutput->getNewSection();
- $this->mNoGallery = $parserOutput->getNoGallery();
- $text = $parserOutput->getText();
- wfRunHooks( 'OutputPageBeforeHTML', array( &$this, &$text ) );
- $this->addHTML( $text );
- $t = $parserOutput->getTitleText();
- if( !empty( $t ) ) {
- $this->setPageTitle( $t );
- }
+ $this->addParserOutput( $parserOutput );
return true;
} else {
return false;
@@ -422,15 +422,15 @@ class OutputPage {
}
function sendCacheControl() {
- global $wgUseSquid, $wgUseESI, $wgSquidMaxage;
+ global $wgUseSquid, $wgUseESI, $wgUseETag, $wgSquidMaxage, $wgRequest;
$fname = 'OutputPage::sendCacheControl';
- if ($this->mETag)
- header("ETag: $this->mETag");
+ if ($wgUseETag && $this->mETag)
+ $wgRequest->response()->header("ETag: $this->mETag");
# don't serve compressed data to clients who can't handle it
# maintain different caches for logged-in users and non-logged in ones
- header( 'Vary: Accept-Encoding, Cookie' );
+ $wgRequest->response()->header( 'Vary: Accept-Encoding, Cookie' );
if( !$this->uncacheableBecauseRequestvars() && $this->mEnableClientCache ) {
if( $wgUseSquid && ! isset( $_COOKIE[ini_get( 'session.name') ] ) &&
! $this->isPrintable() && $this->mSquidMaxage != 0 )
@@ -442,8 +442,8 @@ class OutputPage {
wfDebug( "$fname: proxy caching with ESI; {$this->mLastModified} **\n", false );
# start with a shorter timeout for initial testing
# header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"');
- header( 'Surrogate-Control: max-age='.$wgSquidMaxage.'+'.$this->mSquidMaxage.', content="ESI/1.0"');
- header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
+ $wgRequest->response()->header( 'Surrogate-Control: max-age='.$wgSquidMaxage.'+'.$this->mSquidMaxage.', content="ESI/1.0"');
+ $wgRequest->response()->header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
} else {
# We'll purge the proxy cache for anons explicitly, but require end user agents
# to revalidate against the proxy on each visit.
@@ -452,24 +452,24 @@ class OutputPage {
wfDebug( "$fname: local proxy caching; {$this->mLastModified} **\n", false );
# start with a shorter timeout for initial testing
# header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
- header( 'Cache-Control: s-maxage='.$this->mSquidMaxage.', must-revalidate, max-age=0' );
+ $wgRequest->response()->header( 'Cache-Control: s-maxage='.$this->mSquidMaxage.', must-revalidate, max-age=0' );
}
} else {
# We do want clients to cache if they can, but they *must* check for updates
# on revisiting the page.
wfDebug( "$fname: private caching; {$this->mLastModified} **\n", false );
- header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
- header( "Cache-Control: private, must-revalidate, max-age=0" );
+ $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
+ $wgRequest->response()->header( "Cache-Control: private, must-revalidate, max-age=0" );
}
- if($this->mLastModified) header( "Last-modified: {$this->mLastModified}" );
+ if($this->mLastModified) $wgRequest->response()->header( "Last-modified: {$this->mLastModified}" );
} else {
wfDebug( "$fname: no caching **\n", false );
# In general, the absence of a last modified header should be enough to prevent
# the client from using its cache. We send a few other things just to make sure.
- header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
- header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
- header( 'Pragma: no-cache' );
+ $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
+ $wgRequest->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
+ $wgRequest->response()->header( 'Pragma: no-cache' );
}
}
@@ -478,9 +478,9 @@ class OutputPage {
* the object, let's actually output it:
*/
function output() {
- global $wgUser, $wgOutputEncoding;
+ global $wgUser, $wgOutputEncoding, $wgRequest;
global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType;
- global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgScriptPath, $wgServer;
+ global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgScriptPath, $wgServer;
if( $this->mDoNothing ){
return;
@@ -490,13 +490,14 @@ class OutputPage {
$sk = $wgUser->getSkin();
if ( $wgUseAjax ) {
- $this->addScript( "<script type=\"{$wgJsMimeType}\">
- var wgScriptPath=\"{$wgScriptPath}\";
- var wgServer=\"{$wgServer}\";
- </script>" );
$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js\"></script>\n" );
}
+ if ( $wgUseAjax && $wgAjaxSearch ) {
+ $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js\"></script>\n" );
+ $this->addScript( "<script type=\"{$wgJsMimeType}\">hookEvent(\"load\", sajax_onload);</script>\n" );
+ }
+
if ( '' != $this->mRedirect ) {
if( substr( $this->mRedirect, 0, 4 ) != 'http' ) {
# Standards require redirect URLs to be absolute
@@ -505,7 +506,7 @@ class OutputPage {
}
if( $this->mRedirectCode == '301') {
if( !$wgDebugRedirects ) {
- header("HTTP/1.1 {$this->mRedirectCode} Moved Permanently");
+ $wgRequest->response()->header("HTTP/1.1 {$this->mRedirectCode} Moved Permanently");
}
$this->mLastModified = wfTimestamp( TS_RFC2822 );
}
@@ -518,7 +519,7 @@ class OutputPage {
print "<p>Location: <a href=\"$url\">$url</a></p>\n";
print "</body>\n</html>\n";
} else {
- header( 'Location: '.$this->mRedirect );
+ $wgRequest->response()->header( 'Location: '.$this->mRedirect );
}
wfProfileOut( $fname );
return;
@@ -575,7 +576,7 @@ class OutputPage {
);
if ( $statusMessage[$this->mStatusCode] )
- header( 'HTTP/1.1 ' . $this->mStatusCode . ' ' . $statusMessage[$this->mStatusCode] );
+ $wgRequest->response()->header( 'HTTP/1.1 ' . $this->mStatusCode . ' ' . $statusMessage[$this->mStatusCode] );
}
# Buffer output; final headers may depend on later processing
@@ -584,8 +585,8 @@ class OutputPage {
# Disable temporary placeholders, so that the skin produces HTML
$sk->postParseLinkColour( false );
- header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" );
- header( 'Content-language: '.$wgContLanguageCode );
+ $wgRequest->response()->header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" );
+ $wgRequest->response()->header( 'Content-language: '.$wgContLanguageCode );
if ($this->mArticleBodyOnly) {
$this->out($this->mBodytext);
@@ -617,11 +618,6 @@ class OutputPage {
$wgInputEncoding = strtolower( $wgInputEncoding );
- if( $wgUser->getOption( 'altencoding' ) ) {
- $wgContLang->setAltEncoding();
- return;
- }
-
if ( empty( $_SERVER['HTTP_ACCEPT_CHARSET'] ) ) {
$wgOutputEncoding = strtolower( $wgOutputEncoding );
return;
@@ -715,11 +711,10 @@ class OutputPage {
/**
* Display an error page noting that a given permission bit is required.
- * This should generally replace the sysopRequired, developerRequired etc.
* @param string $permission key required
*/
function permissionRequired( $permission ) {
- global $wgUser;
+ global $wgGroupPermissions, $wgUser;
$this->setPageTitle( wfMsg( 'badaccess' ) );
$this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
@@ -727,46 +722,46 @@ class OutputPage {
$this->setArticleRelated( false );
$this->mBodytext = '';
- $sk = $wgUser->getSkin();
- $ap = $sk->makeKnownLink( wfMsgForContent( 'administrators' ) );
- $this->addHTML( wfMsgHtml( 'badaccesstext', $ap, $permission ) );
- $this->returnToMain();
+ $groups = array();
+ foreach( $wgGroupPermissions as $key => $value ) {
+ if( isset( $value[$permission] ) && $value[$permission] == true ) {
+ $groupName = User::getGroupName( $key );
+ $groupPage = User::getGroupPage( $key );
+ if( $groupPage ) {
+ $skin =& $wgUser->getSkin();
+ $groups[] = '"'.$skin->makeLinkObj( $groupPage, $groupName ).'"';
+ } else {
+ $groups[] = '"'.$groupName.'"';
+ }
+ }
+ }
+ $n = count( $groups );
+ $groups = implode( ', ', $groups );
+ switch( $n ) {
+ case 0:
+ case 1:
+ case 2:
+ $message = wfMsgHtml( "badaccess-group$n", $groups );
+ break;
+ default:
+ $message = wfMsgHtml( 'badaccess-groups', $groups );
+ }
+ $this->addHtml( $message );
+ $this->returnToMain( false );
}
/**
* @deprecated
*/
function sysopRequired() {
- global $wgUser;
-
- $this->setPageTitle( wfMsg( 'sysoptitle' ) );
- $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
- $this->setRobotpolicy( 'noindex,nofollow' );
- $this->setArticleRelated( false );
- $this->mBodytext = '';
-
- $sk = $wgUser->getSkin();
- $ap = $sk->makeKnownLink( wfMsgForContent( 'administrators' ), '' );
- $this->addHTML( wfMsgHtml( 'sysoptext', $ap ) );
- $this->returnToMain();
+ throw new MWException( "Call to deprecated OutputPage::sysopRequired() method\n" );
}
/**
* @deprecated
*/
function developerRequired() {
- global $wgUser;
-
- $this->setPageTitle( wfMsg( 'developertitle' ) );
- $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
- $this->setRobotpolicy( 'noindex,nofollow' );
- $this->setArticleRelated( false );
- $this->mBodytext = '';
-
- $sk = $wgUser->getSkin();
- $ap = $sk->makeKnownLink( wfMsgForContent( 'administrators' ), '' );
- $this->addHTML( wfMsgHtml( 'developertext', $ap ) );
- $this->returnToMain();
+ throw new MWException( "Call to deprecated OutputPage::developerRequired() method\n" );
}
/**
@@ -774,6 +769,12 @@ class OutputPage {
*/
function loginToUse() {
global $wgUser, $wgTitle, $wgContLang;
+
+ if( $wgUser->isLoggedIn() ) {
+ $this->permissionRequired( 'read' );
+ return;
+ }
+
$skin = $wgUser->getSkin();
$this->setPageTitle( wfMsg( 'loginreqtitle' ) );
@@ -786,7 +787,11 @@ class OutputPage {
$this->addHtml( wfMsgWikiHtml( 'loginreqpagetext', $loginLink ) );
$this->addHtml( "\n<!--" . $wgTitle->getPrefixedUrl() . "-->" );
- $this->returnToMain();
+ # Don't return to the main page if the user can't read it
+ # otherwise we'll end up in a pointless loop
+ $mainPage = Title::newFromText( wfMsgForContent( 'mainpage' ) );
+ if( $mainPage->userCanRead() )
+ $this->returnToMain( true, $mainPage );
}
/** @obsolete */
@@ -828,7 +833,7 @@ class OutputPage {
if ( $wgTitle->getNamespace() == NS_MEDIAWIKI ) {
$source = wfMsgWeirdKey ( $wgTitle->getText() );
} else {
- $source = wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' );
+ $source = '';
}
}
$rows = $wgUser->getIntOption( 'rows' );
@@ -1045,7 +1050,7 @@ class OutputPage {
$link = $wgRequest->escapeAppendQuery( 'feed=rss' );
$ret .= "<link rel='alternate' type='application/rss+xml' title='RSS 2.0' href='$link' />\n";
$link = $wgRequest->escapeAppendQuery( 'feed=atom' );
- $ret .= "<link rel='alternate' type='application/atom+xml' title='Atom 0.3' href='$link' />\n";
+ $ret .= "<link rel='alternate' type='application/atom+xml' title='Atom 1.0' href='$link' />\n";
}
return $ret;
diff --git a/includes/PageHistory.php b/includes/PageHistory.php
index de006285..d7f426fc 100644
--- a/includes/PageHistory.php
+++ b/includes/PageHistory.php
@@ -40,8 +40,6 @@ class PageHistory {
$this->mTitle =& $article->mTitle;
$this->mNotificationTimestamp = NULL;
$this->mSkin = $wgUser->getSkin();
-
- $this->defaultLimit = 50;
}
/**
@@ -93,99 +91,30 @@ class PageHistory {
return;
}
- $dbr =& wfGetDB(DB_SLAVE);
-
- /*
- * Extract limit, the number of revisions to show, and
- * offset, the timestamp to begin at, from the URL.
- */
- $limit = $wgRequest->getInt('limit', $this->defaultLimit);
- if ( $limit <= 0 ) {
- $limit = $this->defaultLimit;
- } elseif ( $limit > 50000 ) {
- # Arbitrary maximum
- # Any more than this and we'll probably get an out of memory error
- $limit = 50000;
- }
-
- $offset = $wgRequest->getText('offset');
-
- /* Offset must be an integral. */
- if (!strlen($offset) || !preg_match("/^[0-9]+$/", $offset))
- $offset = 0;
-# $offset = $dbr->timestamp($offset);
- $dboffset = $offset === 0 ? 0 : $dbr->timestamp($offset);
+
/*
- * "go=last" means to jump to the last history page.
+ * "go=first" means to jump to the last (earliest) history page.
+ * This is deprecated, it no longer appears in the user interface
*/
- if (($gowhere = $wgRequest->getText("go")) !== NULL) {
- $gourl = null;
- switch ($gowhere) {
- case "first":
- if (($lastid = $this->getLastOffsetForPaging($this->mTitle->getArticleID(), $limit)) === NULL)
- break;
- $gourl = $wgTitle->getLocalURL("action=history&limit={$limit}&offset=".
- wfTimestamp(TS_MW, $lastid));
- break;
- }
-
- if (!is_null($gourl)) {
- $wgOut->redirect($gourl);
- return;
- }
+ if ( $wgRequest->getText("go") == 'first' ) {
+ $limit = $wgRequest->getInt( 'limit', 50 );
+ $wgOut->redirect( $wgTitle->getLocalURL( "action=history&limit={$limit}&dir=prev" ) );
+ return;
}
- /*
- * Fetch revisions.
- *
- * If the user clicked "previous", we retrieve the revisions backwards,
- * then reverse them. This is to avoid needing to know the timestamp of
- * previous revisions when generating the URL.
+ /**
+ * Do the list
*/
- $direction = $this->getDirection();
- $revisions = $this->fetchRevisions($limit, $dboffset, $direction);
- $navbar = $this->makeNavbar($revisions, $offset, $limit, $direction);
-
- /*
- * We fetch one more revision than needed to get the timestamp of the
- * one after this page (and to know if it exists).
- *
- * linesonpage stores the actual number of lines.
- */
- if (count($revisions) < $limit + 1)
- $this->linesonpage = count($revisions);
- else
- $this->linesonpage = count($revisions) - 1;
-
- /* Un-reverse revisions */
- if ($direction == PageHistory::DIR_PREV)
- $revisions = array_reverse($revisions);
-
- /*
- * Print the top navbar.
- */
- $s = $navbar;
- $s .= $this->beginHistoryList();
- $counter = 1;
-
- /*
- * Print each revision, excluding the one-past-the-end, if any.
- */
- foreach (array_slice($revisions, 0, $limit) as $i => $line) {
- $latest = !$i && $offset == 0;
- $firstInList = !$i;
- $next = isset( $revisions[$i + 1] ) ? $revisions[$i + 1 ] : null;
- $s .= $this->historyLine($line, $next, $counter, $this->getNotificationTimestamp(), $latest, $firstInList);
- $counter++;
- }
-
- /*
- * End navbar.
- */
- $s .= $this->endHistoryList();
- $s .= $navbar;
-
- $wgOut->addHTML( $s );
+ $pager = new PageHistoryPager( $this );
+ $navbar = $pager->getNavigationBar();
+ $this->linesonpage = $pager->getNumRows();
+ $wgOut->addHTML(
+ $pager->getNavigationBar() .
+ $this->beginHistoryList() .
+ $pager->getBody() .
+ $this->endHistoryList() .
+ $pager->getNavigationBar()
+ );
wfProfileOut( $fname );
}
@@ -318,16 +247,15 @@ class PageHistory {
/** @todo document */
function lastLink( $rev, $next, $counter ) {
$last = wfMsgExt( 'last', array( 'escape' ) );
- if( is_null( $next ) ) {
- if( $rev->getTimestamp() == $this->getEarliestOffset() ) {
- return $last;
- } else {
- // Cut off by paging; there are more behind us...
- return $this->mSkin->makeKnownLinkObj(
- $this->mTitle,
- $last,
- "diff=" . $rev->getId() . "&oldid=prev" );
- }
+ if ( is_null( $next ) ) {
+ # Probably no next row
+ return $last;
+ } elseif ( $next === 'unknown' ) {
+ # Next row probably exists but is unknown, use an oldid=prev link
+ return $this->mSkin->makeKnownLinkObj(
+ $this->mTitle,
+ $last,
+ "diff=" . $rev->getId() . "&oldid=prev" );
} elseif( !$rev->userCan( Revision::DELETED_TEXT ) ) {
return $last;
} else {
@@ -387,72 +315,23 @@ class PageHistory {
}
/** @todo document */
- function getLatestOffset( $id = null ) {
- if ( $id === null) $id = $this->mTitle->getArticleID();
- return $this->getExtremeOffset( $id, 'max' );
- }
-
- /** @todo document */
- function getEarliestOffset( $id = null ) {
- if ( $id === null) $id = $this->mTitle->getArticleID();
- return $this->getExtremeOffset( $id, 'min' );
- }
-
- /** @todo document */
- function getExtremeOffset( $id, $func ) {
- $db =& wfGetDB(DB_SLAVE);
- return $db->selectField( 'revision',
- "$func(rev_timestamp)",
- array( 'rev_page' => $id ),
- 'PageHistory::getExtremeOffset' );
- }
-
- /** @todo document */
function getLatestId() {
if( is_null( $this->mLatestId ) ) {
$id = $this->mTitle->getArticleID();
$db =& wfGetDB(DB_SLAVE);
- $this->mLatestId = $db->selectField( 'revision',
- "max(rev_id)",
- array( 'rev_page' => $id ),
+ $this->mLatestId = $db->selectField( 'page',
+ "page_latest",
+ array( 'page_id' => $id ),
'PageHistory::getLatestID' );
}
return $this->mLatestId;
}
- /** @todo document */
- function getLastOffsetForPaging( $id, $step ) {
- $fname = 'PageHistory::getLastOffsetForPaging';
-
- $dbr =& wfGetDB(DB_SLAVE);
- $res = $dbr->select(
- 'revision',
- 'rev_timestamp',
- "rev_page=$id",
- $fname,
- array('ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => $step));
-
- $n = $dbr->numRows( $res );
- $last = null;
- while( $obj = $dbr->fetchObject( $res ) ) {
- $last = $obj->rev_timestamp;
- }
- $dbr->freeResult( $res );
- return $last;
- }
-
/**
- * @return returns the direction of browsing watchlist
+ * Fetch an array of revisions, specified by a given limit, offset and
+ * direction. This is now only used by the feeds. It was previously
+ * used by the main UI but that's now handled by the pager.
*/
- function getDirection() {
- global $wgRequest;
- if ($wgRequest->getText("dir") == "prev")
- return PageHistory::DIR_PREV;
- else
- return PageHistory::DIR_NEXT;
- }
-
- /** @todo document */
function fetchRevisions($limit, $offset, $direction) {
$fname = 'PageHistory::fetchRevisions';
@@ -516,83 +395,6 @@ class PageHistory {
return $this->mNotificationTimestamp;
}
-
- /** @todo document */
- function makeNavbar($revisions, $offset, $limit, $direction) {
- global $wgLang;
-
- $revisions = array_slice($revisions, 0, $limit);
-
- $latestTimestamp = wfTimestamp(TS_MW, $this->getLatestOffset());
- $earliestTimestamp = wfTimestamp(TS_MW, $this->getEarliestOffset());
-
- /*
- * When we're displaying previous revisions, we need to reverse
- * the array, because it's queried in reverse order.
- */
- if ($direction == PageHistory::DIR_PREV)
- $revisions = array_reverse($revisions);
-
- /*
- * lowts is the timestamp of the first revision on this page.
- * hights is the timestamp of the last revision.
- */
-
- $lowts = $hights = 0;
-
- if( count( $revisions ) ) {
- $latestShown = wfTimestamp(TS_MW, $revisions[0]->rev_timestamp);
- $earliestShown = wfTimestamp(TS_MW, $revisions[count($revisions) - 1]->rev_timestamp);
- } else {
- $latestShown = null;
- $earliestShown = null;
- }
-
- /* Don't announce the limit everywhere if it's the default */
- $usefulLimit = $limit == $this->defaultLimit ? '' : $limit;
-
- $urls = array();
- foreach (array(20, 50, 100, 250, 500) as $num) {
- $urls[] = $this->MakeLink( $wgLang->formatNum($num),
- array('offset' => $offset == 0 ? '' : wfTimestamp(TS_MW, $offset), 'limit' => $num, ) );
- }
-
- $bits = implode($urls, ' | ');
-
- wfDebug("latestShown=$latestShown latestTimestamp=$latestTimestamp\n");
- if( $latestShown < $latestTimestamp ) {
- $prevtext = $this->MakeLink( wfMsgHtml("prevn", $limit),
- array( 'dir' => 'prev', 'offset' => $latestShown, 'limit' => $usefulLimit ) );
- $lasttext = $this->MakeLink( wfMsgHtml('histlast'),
- array( 'limit' => $usefulLimit ) );
- } else {
- $prevtext = wfMsgHtml("prevn", $limit);
- $lasttext = wfMsgHtml('histlast');
- }
-
- wfDebug("earliestShown=$earliestShown earliestTimestamp=$earliestTimestamp\n");
- if( $earliestShown > $earliestTimestamp ) {
- $nexttext = $this->MakeLink( wfMsgHtml("nextn", $limit),
- array( 'offset' => $earliestShown, 'limit' => $usefulLimit ) );
- $firsttext = $this->MakeLink( wfMsgHtml('histfirst'),
- array( 'go' => 'first', 'limit' => $usefulLimit ) );
- } else {
- $nexttext = wfMsgHtml("nextn", $limit);
- $firsttext = wfMsgHtml('histfirst');
- }
-
- $firstlast = "($lasttext | $firsttext)";
-
- return "$firstlast " . wfMsgHtml("viewprevnext", $prevtext, $nexttext, $bits);
- }
-
- function MakeLink($text, $query = NULL) {
- if ( $query === null ) return $text;
- return $this->mSkin->makeKnownLinkObj(
- $this->mTitle, $text,
- wfArrayToCGI( $query, array( 'action' => 'history' )));
- }
-
/**
* Output a subscription feed listing recent edits to this page.
@@ -678,8 +480,72 @@ class PageHistory {
function stripComment( $text ) {
return preg_replace( '/\[\[([^]]*\|)?([^]]+)\]\]/', '\2', $text );
}
+}
+
+
+class PageHistoryPager extends ReverseChronologicalPager {
+ public $mLastRow = false, $mPageHistory;
+
+ function __construct( $pageHistory ) {
+ parent::__construct();
+ $this->mPageHistory = $pageHistory;
+ }
+
+ function getQueryInfo() {
+ return array(
+ 'tables' => 'revision',
+ 'fields' => array('rev_id', 'rev_page', 'rev_text_id', 'rev_user', 'rev_comment', 'rev_user_text',
+ 'rev_timestamp', 'rev_minor_edit', 'rev_deleted'),
+ 'conds' => array('rev_page' => $this->mPageHistory->mTitle->getArticleID() ),
+ 'options' => array( 'USE INDEX' => 'page_timestamp' )
+ );
+ }
+
+ function getIndexField() {
+ return 'rev_timestamp';
+ }
+ function formatRow( $row ) {
+ if ( $this->mLastRow ) {
+ $latest = $this->mCounter == 1 && $this->mOffset == '';
+ $firstInList = $this->mCounter == 1;
+ $s = $this->mPageHistory->historyLine( $this->mLastRow, $row, $this->mCounter++,
+ $this->mPageHistory->getNotificationTimestamp(), $latest, $firstInList );
+ } else {
+ $s = '';
+ }
+ $this->mLastRow = $row;
+ return $s;
+ }
+
+ function getStartBody() {
+ $this->mLastRow = false;
+ $this->mCounter = 1;
+ return '';
+ }
+ function getEndBody() {
+ if ( $this->mLastRow ) {
+ $latest = $this->mCounter == 1 && $this->mOffset == 0;
+ $firstInList = $this->mCounter == 1;
+ if ( $this->mIsBackwards ) {
+ # Next row is unknown, but for UI reasons, probably exists if an offset has been specified
+ if ( $this->mOffset == '' ) {
+ $next = null;
+ } else {
+ $next = 'unknown';
+ }
+ } else {
+ # The next row is the past-the-end row
+ $next = $this->mPastTheEndRow;
+ }
+ $s = $this->mPageHistory->historyLine( $this->mLastRow, $next, $this->mCounter++,
+ $this->mPageHistory->getNotificationTimestamp(), $latest, $firstInList );
+ } else {
+ $s = '';
+ }
+ return $s;
+ }
}
?>
diff --git a/includes/Pager.php b/includes/Pager.php
new file mode 100644
index 00000000..b14aa8ca
--- /dev/null
+++ b/includes/Pager.php
@@ -0,0 +1,656 @@
+<?php
+
+/**
+ * Basic pager interface.
+ */
+interface Pager {
+ function getNavigationBar();
+ function getBody();
+}
+
+/**
+ * IndexPager is an efficient pager which uses a (roughly unique) index in the
+ * data set to implement paging, rather than a "LIMIT offset,limit" clause.
+ * In MySQL, such a limit/offset clause requires counting through the specified number
+ * of offset rows to find the desired data, which can be expensive for large offsets.
+ *
+ * ReverseChronologicalPager is a child class of the abstract IndexPager, and contains
+ * some formatting and display code which is specific to the use of timestamps as
+ * indexes. Here is a synopsis of its operation:
+ *
+ * * The query is specified by the offset, limit and direction (dir) parameters, in
+ * addition to any subclass-specific parameters.
+ *
+ * * The offset is the non-inclusive start of the DB query. A row with an index value
+ * equal to the offset will never be shown.
+ *
+ * * The query may either be done backwards, where the rows are returned by the database
+ * in the opposite order to which they are displayed to the user, or forwards. This is
+ * specified by the "dir" parameter, dir=prev means backwards, anything else means
+ * forwards. The offset value specifies the start of the database result set, which
+ * may be either the start or end of the displayed data set. This allows "previous"
+ * links to be implemented without knowledge of the index value at the start of the
+ * previous page.
+ *
+ * * An additional row beyond the user-specified limit is always requested. This allows
+ * us to tell whether we should display a "next" link in the case of forwards mode,
+ * or a "previous" link in the case of backwards mode. Determining whether to
+ * display the other link (the one for the page before the start of the database
+ * result set) can be done heuristically by examining the offset.
+ *
+ * * An empty offset indicates that the offset condition should be omitted from the query.
+ * This naturally produces either the first page or the last page depending on the
+ * dir parameter.
+ *
+ * Subclassing the pager to implement concrete functionality should be fairly simple,
+ * please see the examples in PageHistory.php and SpecialIpblocklist.php. You just need
+ * to override formatRow(), getQueryInfo() and getIndexField(). Don't forget to call the
+ * parent constructor if you override it.
+ */
+abstract class IndexPager implements Pager {
+ public $mRequest;
+ public $mLimitsShown = array( 20, 50, 100, 250, 500 );
+ public $mDefaultLimit = 50;
+ public $mOffset, $mLimit;
+ public $mQueryDone = false;
+ public $mDb;
+ public $mPastTheEndRow;
+
+ protected $mIndexField;
+
+ /**
+ * Default query direction. false for ascending, true for descending
+ */
+ public $mDefaultDirection = false;
+
+ /**
+ * Result object for the query. Warning: seek before use.
+ */
+ public $mResult;
+
+ function __construct() {
+ global $wgRequest;
+ $this->mRequest = $wgRequest;
+
+ # NB: the offset is quoted, not validated. It is treated as an arbitrary string
+ # to support the widest variety of index types. Be careful outputting it into
+ # HTML!
+ $this->mOffset = $this->mRequest->getText( 'offset' );
+ $this->mLimit = $this->mRequest->getInt( 'limit', $this->mDefaultLimit );
+ if ( $this->mLimit <= 0 || $this->mLimit > 50000 ) {
+ $this->mLimit = $this->mDefaultLimit;
+ }
+ $this->mIsBackwards = ( $this->mRequest->getVal( 'dir' ) == 'prev' );
+ $this->mIndexField = $this->getIndexField();
+ $this->mDb = wfGetDB( DB_SLAVE );
+ }
+
+ /**
+ * Do the query, using information from the object context. This function
+ * has been kept minimal to make it overridable if necessary, to allow for
+ * result sets formed from multiple DB queries.
+ */
+ function doQuery() {
+ # Use the child class name for profiling
+ $fname = __METHOD__ . ' (' . get_class( $this ) . ')';
+ wfProfileIn( $fname );
+
+ $descending = ( $this->mIsBackwards == $this->mDefaultDirection );
+ # Plus an extra row so that we can tell the "next" link should be shown
+ $queryLimit = $this->mLimit + 1;
+
+ $this->mResult = $this->reallyDoQuery( $this->mOffset, $queryLimit, $descending );
+ $this->extractResultInfo( $this->mOffset, $queryLimit, $this->mResult );
+ $this->mQueryDone = true;
+
+ wfProfileOut( $fname );
+ }
+
+ /**
+ * Extract some useful data from the result object for use by
+ * the navigation bar, put it into $this
+ */
+ function extractResultInfo( $offset, $limit, ResultWrapper $res ) {
+ $numRows = $res->numRows();
+ if ( $numRows ) {
+ $row = $res->fetchRow();
+ $firstIndex = $row[$this->mIndexField];
+
+ # Discard the extra result row if there is one
+ if ( $numRows > $this->mLimit && $numRows > 1 ) {
+ $res->seek( $numRows - 1 );
+ $this->mPastTheEndRow = $res->fetchObject();
+ $indexField = $this->mIndexField;
+ $this->mPastTheEndIndex = $this->mPastTheEndRow->$indexField;
+ $res->seek( $numRows - 2 );
+ $row = $res->fetchRow();
+ $lastIndex = $row[$this->mIndexField];
+ } else {
+ $this->mPastTheEndRow = null;
+ # Setting indexes to an empty string means that they will be omitted
+ # if they would otherwise appear in URLs. It just so happens that this
+ # is the right thing to do in the standard UI, in all the relevant cases.
+ $this->mPastTheEndIndex = '';
+ $res->seek( $numRows - 1 );
+ $row = $res->fetchRow();
+ $lastIndex = $row[$this->mIndexField];
+ }
+ } else {
+ $firstIndex = '';
+ $lastIndex = '';
+ $this->mPastTheEndRow = null;
+ $this->mPastTheEndIndex = '';
+ }
+
+ if ( $this->mIsBackwards ) {
+ $this->mIsFirst = ( $numRows < $limit );
+ $this->mIsLast = ( $offset == '' );
+ $this->mLastShown = $firstIndex;
+ $this->mFirstShown = $lastIndex;
+ } else {
+ $this->mIsFirst = ( $offset == '' );
+ $this->mIsLast = ( $numRows < $limit );
+ $this->mLastShown = $lastIndex;
+ $this->mFirstShown = $firstIndex;
+ }
+ }
+
+ /**
+ * Do a query with specified parameters, rather than using the object context
+ *
+ * @param string $offset Index offset, inclusive
+ * @param integer $limit Exact query limit
+ * @param boolean $descending Query direction, false for ascending, true for descending
+ * @return ResultWrapper
+ */
+ function reallyDoQuery( $offset, $limit, $ascending ) {
+ $fname = __METHOD__ . ' (' . get_class( $this ) . ')';
+ $info = $this->getQueryInfo();
+ $tables = $info['tables'];
+ $fields = $info['fields'];
+ $conds = isset( $info['conds'] ) ? $info['conds'] : array();
+ $options = isset( $info['options'] ) ? $info['options'] : array();
+ if ( $ascending ) {
+ $options['ORDER BY'] = $this->mIndexField;
+ $operator = '>';
+ } else {
+ $options['ORDER BY'] = $this->mIndexField . ' DESC';
+ $operator = '<';
+ }
+ if ( $offset != '' ) {
+ $conds[] = $this->mIndexField . $operator . $this->mDb->addQuotes( $offset );
+ }
+ $options['LIMIT'] = intval( $limit );
+ $res = $this->mDb->select( $tables, $fields, $conds, $fname, $options );
+ return new ResultWrapper( $this->mDb, $res );
+ }
+
+ /**
+ * Get the formatted result list. Calls getStartBody(), formatRow() and
+ * getEndBody(), concatenates the results and returns them.
+ */
+ function getBody() {
+ if ( !$this->mQueryDone ) {
+ $this->doQuery();
+ }
+ # Don't use any extra rows returned by the query
+ $numRows = min( $this->mResult->numRows(), $this->mLimit );
+
+ $s = $this->getStartBody();
+ if ( $numRows ) {
+ if ( $this->mIsBackwards ) {
+ for ( $i = $numRows - 1; $i >= 0; $i-- ) {
+ $this->mResult->seek( $i );
+ $row = $this->mResult->fetchObject();
+ $s .= $this->formatRow( $row );
+ }
+ } else {
+ $this->mResult->seek( 0 );
+ for ( $i = 0; $i < $numRows; $i++ ) {
+ $row = $this->mResult->fetchObject();
+ $s .= $this->formatRow( $row );
+ }
+ }
+ } else {
+ $s .= $this->getEmptyBody();
+ }
+ $s .= $this->getEndBody();
+ return $s;
+ }
+
+ /**
+ * Make a self-link
+ */
+ function makeLink($text, $query = NULL) {
+ if ( $query === null ) {
+ return $text;
+ } else {
+ return $this->getSkin()->makeKnownLinkObj( $this->getTitle(), $text,
+ wfArrayToCGI( $query, $this->getDefaultQuery() ) );
+ }
+ }
+
+ /**
+ * Hook into getBody(), allows text to be inserted at the start. This
+ * will be called even if there are no rows in the result set.
+ */
+ function getStartBody() {
+ return '';
+ }
+
+ /**
+ * Hook into getBody() for the end of the list
+ */
+ function getEndBody() {
+ return '';
+ }
+
+ /**
+ * Hook into getBody(), for the bit between the start and the
+ * end when there are no rows
+ */
+ function getEmptyBody() {
+ return '';
+ }
+
+ /**
+ * Title used for self-links. Override this if you want to be able to
+ * use a title other than $wgTitle
+ */
+ function getTitle() {
+ return $GLOBALS['wgTitle'];
+ }
+
+ /**
+ * Get the current skin. This can be overridden if necessary.
+ */
+ function getSkin() {
+ if ( !isset( $this->mSkin ) ) {
+ global $wgUser;
+ $this->mSkin = $wgUser->getSkin();
+ }
+ return $this->mSkin;
+ }
+
+ /**
+ * Get an array of query parameters that should be put into self-links.
+ * By default, all parameters passed in the URL are used, except for a
+ * short blacklist.
+ */
+ function getDefaultQuery() {
+ if ( !isset( $this->mDefaultQuery ) ) {
+ $this->mDefaultQuery = $_GET;
+ unset( $this->mDefaultQuery['title'] );
+ unset( $this->mDefaultQuery['dir'] );
+ unset( $this->mDefaultQuery['offset'] );
+ unset( $this->mDefaultQuery['limit'] );
+ }
+ return $this->mDefaultQuery;
+ }
+
+ /**
+ * Get the number of rows in the result set
+ */
+ function getNumRows() {
+ if ( !$this->mQueryDone ) {
+ $this->doQuery();
+ }
+ return $this->mResult->numRows();
+ }
+
+ /**
+ * Get a query array for the prev, next, first and last links.
+ */
+ function getPagingQueries() {
+ if ( !$this->mQueryDone ) {
+ $this->doQuery();
+ }
+
+ # Don't announce the limit everywhere if it's the default
+ $urlLimit = $this->mLimit == $this->mDefaultLimit ? '' : $this->mLimit;
+
+ if ( $this->mIsFirst ) {
+ $prev = false;
+ $first = false;
+ } else {
+ $prev = array( 'dir' => 'prev', 'offset' => $this->mFirstShown, 'limit' => $urlLimit );
+ $first = array( 'limit' => $urlLimit );
+ }
+ if ( $this->mIsLast ) {
+ $next = false;
+ $last = false;
+ } else {
+ $next = array( 'offset' => $this->mLastShown, 'limit' => $urlLimit );
+ $last = array( 'dir' => 'prev', 'limit' => $urlLimit );
+ }
+ return compact( 'prev', 'next', 'first', 'last' );
+ }
+
+ /**
+ * Get paging links. If a link is disabled, the item from $disabledTexts will
+ * be used. If there is no such item, the unlinked text from $linkTexts will
+ * be used. Both $linkTexts and $disabledTexts are arrays of HTML.
+ */
+ function getPagingLinks( $linkTexts, $disabledTexts = array() ) {
+ $queries = $this->getPagingQueries();
+ $links = array();
+ foreach ( $queries as $type => $query ) {
+ if ( $query !== false ) {
+ $links[$type] = $this->makeLink( $linkTexts[$type], $queries[$type] );
+ } elseif ( isset( $disabledTexts[$type] ) ) {
+ $links[$type] = $disabledTexts[$type];
+ } else {
+ $links[$type] = $linkTexts[$type];
+ }
+ }
+ return $links;
+ }
+
+ function getLimitLinks() {
+ global $wgLang;
+ $links = array();
+ if ( $this->mIsBackwards ) {
+ $offset = $this->mPastTheEndIndex;
+ } else {
+ $offset = $this->mOffset;
+ }
+ foreach ( $this->mLimitsShown as $limit ) {
+ $links[] = $this->makeLink( $wgLang->formatNum( $limit ),
+ array( 'offset' => $offset, 'limit' => $limit ) );
+ }
+ return $links;
+ }
+
+ /**
+ * Abstract formatting function. This should return an HTML string
+ * representing the result row $row. Rows will be concatenated and
+ * returned by getBody()
+ */
+ abstract function formatRow( $row );
+
+ /**
+ * This function should be overridden to provide all parameters
+ * needed for the main paged query. It returns an associative
+ * array with the following elements:
+ * tables => Table(s) for passing to Database::select()
+ * fields => Field(s) for passing to Database::select(), may be *
+ * conds => WHERE conditions
+ * options => option array
+ */
+ abstract function getQueryInfo();
+
+ /**
+ * This function should be overridden to return the name of the
+ * index field.
+ */
+ abstract function getIndexField();
+}
+
+/**
+ * IndexPager with a formatted navigation bar
+ */
+abstract class ReverseChronologicalPager extends IndexPager {
+ public $mDefaultDirection = true;
+
+ function __construct() {
+ parent::__construct();
+ }
+
+ function getNavigationBar() {
+ global $wgLang;
+
+ if ( isset( $this->mNavigationBar ) ) {
+ return $this->mNavigationBar;
+ }
+ $linkTexts = array(
+ 'prev' => wfMsgHtml( "prevn", $this->mLimit ),
+ 'next' => wfMsgHtml( 'nextn', $this->mLimit ),
+ 'first' => wfMsgHtml('histlast'),
+ 'last' => wfMsgHtml( 'histfirst' )
+ );
+
+ $pagingLinks = $this->getPagingLinks( $linkTexts );
+ $limitLinks = $this->getLimitLinks();
+ $limits = implode( ' | ', $limitLinks );
+
+ $this->mNavigationBar = "({$pagingLinks['first']} | {$pagingLinks['last']}) " . wfMsgHtml("viewprevnext", $pagingLinks['prev'], $pagingLinks['next'], $limits);
+ return $this->mNavigationBar;
+ }
+}
+
+/**
+ * Table-based display with a user-selectable sort order
+ */
+abstract class TablePager extends IndexPager {
+ var $mSort;
+ var $mCurrentRow;
+
+ function __construct() {
+ global $wgRequest;
+ $this->mSort = $wgRequest->getText( 'sort' );
+ if ( !array_key_exists( $this->mSort, $this->getFieldNames() ) ) {
+ $this->mSort = $this->getDefaultSort();
+ }
+ if ( $wgRequest->getBool( 'asc' ) ) {
+ $this->mDefaultDirection = false;
+ } elseif ( $wgRequest->getBool( 'desc' ) ) {
+ $this->mDefaultDirection = true;
+ } /* Else leave it at whatever the class default is */
+
+ parent::__construct();
+ }
+
+ function getStartBody() {
+ global $wgStylePath;
+ $tableClass = htmlspecialchars( $this->getTableClass() );
+ $sortClass = htmlspecialchars( $this->getSortHeaderClass() );
+
+ $s = "<table border='1' class=\"$tableClass\"><thead><tr>\n";
+ $fields = $this->getFieldNames();
+
+ # Make table header
+ foreach ( $fields as $field => $name ) {
+ if ( strval( $name ) == '' ) {
+ $s .= "<th>&nbsp;</th>\n";
+ } elseif ( $this->isFieldSortable( $field ) ) {
+ $query = array( 'sort' => $field, 'limit' => $this->mLimit );
+ if ( $field == $this->mSort ) {
+ # This is the sorted column
+ # Prepare a link that goes in the other sort order
+ if ( $this->mDefaultDirection ) {
+ # Descending
+ $image = 'Arr_u.png';
+ $query['asc'] = '1';
+ $query['desc'] = '';
+ $alt = htmlspecialchars( wfMsg( 'descending_abbrev' ) );
+ } else {
+ # Ascending
+ $image = 'Arr_d.png';
+ $query['asc'] = '';
+ $query['desc'] = '1';
+ $alt = htmlspecialchars( wfMsg( 'ascending_abbrev' ) );
+ }
+ $image = htmlspecialchars( "$wgStylePath/common/images/$image" );
+ $link = $this->makeLink(
+ "<img width=\"12\" height=\"12\" alt=\"$alt\" src=\"$image\" />" .
+ htmlspecialchars( $name ), $query );
+ $s .= "<th class=\"$sortClass\">$link</th>\n";
+ } else {
+ $s .= '<th>' . $this->makeLink( htmlspecialchars( $name ), $query ) . "</th>\n";
+ }
+ } else {
+ $s .= '<th>' . htmlspecialchars( $name ) . "</th>\n";
+ }
+ }
+ $s .= "</tr></thead><tbody>\n";
+ return $s;
+ }
+
+ function getEndBody() {
+ return '</tbody></table>';
+ }
+
+ function getEmptyBody() {
+ $colspan = count( $this->getFieldNames() );
+ $msgEmpty = wfMsgHtml( 'table_pager_empty' );
+ return "<tr><td colspan=\"$colspan\">$msgEmpty</td></tr>\n";
+ }
+
+ function formatRow( $row ) {
+ $s = "<tr>\n";
+ $fieldNames = $this->getFieldNames();
+ $this->mCurrentRow = $row; # In case formatValue needs to know
+ foreach ( $fieldNames as $field => $name ) {
+ $value = isset( $row->$field ) ? $row->$field : null;
+ $formatted = strval( $this->formatValue( $field, $value ) );
+ if ( $formatted == '' ) {
+ $formatted = '&nbsp;';
+ }
+ $class = 'TablePager_col_' . htmlspecialchars( $field );
+ $s .= "<td class=\"$class\">$formatted</td>\n";
+ }
+ $s .= "</tr>\n";
+ return $s;
+ }
+
+ function getIndexField() {
+ return $this->mSort;
+ }
+
+ function getTableClass() {
+ return 'TablePager';
+ }
+
+ function getNavClass() {
+ return 'TablePager_nav';
+ }
+
+ function getSortHeaderClass() {
+ return 'TablePager_sort';
+ }
+
+ /**
+ * A navigation bar with images
+ */
+ function getNavigationBar() {
+ global $wgStylePath, $wgContLang;
+ $path = "$wgStylePath/common/images";
+ $labels = array(
+ 'first' => 'table_pager_first',
+ 'prev' => 'table_pager_prev',
+ 'next' => 'table_pager_next',
+ 'last' => 'table_pager_last',
+ );
+ $images = array(
+ 'first' => $wgContLang->isRTL() ? 'arrow_last_25.png' : 'arrow_first_25.png',
+ 'prev' => $wgContLang->isRTL() ? 'arrow_right_25.png' : 'arrow_left_25.png',
+ 'next' => $wgContLang->isRTL() ? 'arrow_left_25.png' : 'arrow_right_25.png',
+ 'last' => $wgContLang->isRTL() ? 'arrow_first_25.png' : 'arrow_last_25.png',
+ );
+ $disabledImages = array(
+ 'first' => $wgContLang->isRTL() ? 'arrow_disabled_last_25.png' : 'arrow_disabled_first_25.png',
+ 'prev' => $wgContLang->isRTL() ? 'arrow_disabled_right_25.png' : 'arrow_disabled_left_25.png',
+ 'next' => $wgContLang->isRTL() ? 'arrow_disabled_left_25.png' : 'arrow_disabled_right_25.png',
+ 'last' => $wgContLang->isRTL() ? 'arrow_disabled_first_25.png' : 'arrow_disabled_last_25.png',
+ );
+
+ $linkTexts = array();
+ $disabledTexts = array();
+ foreach ( $labels as $type => $label ) {
+ $msgLabel = wfMsgHtml( $label );
+ $linkTexts[$type] = "<img src=\"$path/{$images[$type]}\" alt=\"$msgLabel\"/><br/>$msgLabel";
+ $disabledTexts[$type] = "<img src=\"$path/{$disabledImages[$type]}\" alt=\"$msgLabel\"/><br/>$msgLabel";
+ }
+ $links = $this->getPagingLinks( $linkTexts, $disabledTexts );
+
+ $navClass = htmlspecialchars( $this->getNavClass() );
+ $s = "<table class=\"$navClass\" align=\"center\" cellpadding=\"3\"><tr>";
+ $cellAttrs = 'valign="top" align="center" width="' . 100 / count( $links ) . '%"';
+ foreach ( $labels as $type => $label ) {
+ $s .= "<td $cellAttrs>{$links[$type]}</td>\n";
+ }
+ $s .= '</tr></table>';
+ return $s;
+ }
+
+ /**
+ * Get a <select> element which has options for each of the allowed limits
+ */
+ function getLimitSelect() {
+ global $wgLang;
+ $s = "<select name=\"limit\">";
+ foreach ( $this->mLimitsShown as $limit ) {
+ $selected = $limit == $this->mLimit ? 'selected="selected"' : '';
+ $formattedLimit = $wgLang->formatNum( $limit );
+ $s .= "<option value=\"$limit\" $selected>$formattedLimit</option>\n";
+ }
+ $s .= "</select>";
+ return $s;
+ }
+
+ /**
+ * Get <input type="hidden"> elements for use in a method="get" form.
+ * Resubmits all defined elements of the $_GET array, except for a
+ * blacklist, passed in the $blacklist parameter.
+ */
+ function getHiddenFields( $blacklist = array() ) {
+ $blacklist = (array)$blacklist;
+ $query = $_GET;
+ foreach ( $blacklist as $name ) {
+ unset( $query[$name] );
+ }
+ $s = '';
+ foreach ( $query as $name => $value ) {
+ $encName = htmlspecialchars( $name );
+ $encValue = htmlspecialchars( $value );
+ $s .= "<input type=\"hidden\" name=\"$encName\" value=\"$encValue\"/>\n";
+ }
+ return $s;
+ }
+
+ /**
+ * Get a form containing a limit selection dropdown
+ */
+ function getLimitForm() {
+ # Make the select with some explanatory text
+ $url = $this->getTitle()->escapeLocalURL();
+ $msgSubmit = wfMsgHtml( 'table_pager_limit_submit' );
+ return
+ "<form method=\"get\" action=\"$url\">" .
+ wfMsgHtml( 'table_pager_limit', $this->getLimitSelect() ) .
+ "\n<input type=\"submit\" value=\"$msgSubmit\"/>\n" .
+ $this->getHiddenFields( 'limit' ) .
+ "</form>\n";
+ }
+
+ /**
+ * Return true if the named field should be sortable by the UI, false otherwise
+ * @param string $field
+ */
+ abstract function isFieldSortable( $field );
+
+ /**
+ * Format a table cell. The return value should be HTML, but use an empty string
+ * not &nbsp; for empty cells. Do not include the <td> and </td>.
+ *
+ * @param string $name The database field name
+ * @param string $value The value retrieved from the database
+ *
+ * The current result row is available as $this->mCurrentRow, in case you need
+ * more context.
+ */
+ abstract function formatValue( $name, $value );
+
+ /**
+ * The database field name used as a default sort order
+ */
+ abstract function getDefaultSort();
+
+ /**
+ * An array mapping database field names to a textual description of the field
+ * name, for use in the table header. The description should be plain text, it
+ * will be HTML-escaped later.
+ */
+ abstract function getFieldNames();
+}
+?>
diff --git a/includes/Parser.php b/includes/Parser.php
index 31976baf..76783448 100644
--- a/includes/Parser.php
+++ b/includes/Parser.php
@@ -13,25 +13,13 @@
*/
define( 'MW_PARSER_VERSION', '1.6.1' );
-/**
- * Variable substitution O(N^2) attack
- *
- * Without countermeasures, it would be possible to attack the parser by saving
- * a page filled with a large number of inclusions of large pages. The size of
- * the generated page would be proportional to the square of the input size.
- * Hence, we limit the number of inclusions of any given page, thus bringing any
- * attack back to O(N).
- */
-
-define( 'MAX_INCLUDE_REPEAT', 100 );
-define( 'MAX_INCLUDE_SIZE', 1000000 ); // 1 Million
-
define( 'RLH_FOR_UPDATE', 1 );
# Allowed values for $mOutputType
define( 'OT_HTML', 1 );
define( 'OT_WIKI', 2 );
define( 'OT_MSG' , 3 );
+define( 'OT_PREPROCESS', 4 );
# Flags for setFunctionHook
define( 'SFH_NO_HASH', 1 );
@@ -90,7 +78,8 @@ define( 'MW_COLON_STATE_COMMENTDASHDASH', 7 );
* settings:
* $wgUseTex*, $wgUseDynamicDates*, $wgInterwikiMagic*,
* $wgNamespacesWithSubpages, $wgAllowExternalImages*,
- * $wgLocaltimezone, $wgAllowSpecialInclusion*
+ * $wgLocaltimezone, $wgAllowSpecialInclusion*,
+ * $wgMaxArticleSize*
*
* * only within ParserOptions
* </pre>
@@ -109,6 +98,7 @@ class Parser
var $mOutput, $mAutonumber, $mDTopen, $mStripState = array();
var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
var $mInterwikiLinkHolders, $mLinkHolders, $mUniqPrefix;
+ var $mIncludeSizes;
var $mTemplates, // cache of already loaded templates, avoids
// multiple SQL queries for the same string
$mTemplatePath; // stores an unsorted hash of all the templates already loaded
@@ -119,6 +109,7 @@ class Parser
var $mOptions, // ParserOptions object
$mTitle, // Title context, used for self-link rendering and similar things
$mOutputType, // Output type, one of the OT_xxx constants
+ $ot, // Shortcut alias, see setOutputType()
$mRevisionId; // ID to display in {{REVISIONID}} tags
/**#@-*/
@@ -148,31 +139,35 @@ class Parser
$this->setHook( 'pre', array( $this, 'renderPreTag' ) );
- $this->setFunctionHook( MAG_NS, array( 'CoreParserFunctions', 'ns' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_URLENCODE, array( 'CoreParserFunctions', 'urlencode' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_LCFIRST, array( 'CoreParserFunctions', 'lcfirst' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_UCFIRST, array( 'CoreParserFunctions', 'ucfirst' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_LC, array( 'CoreParserFunctions', 'lc' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_UC, array( 'CoreParserFunctions', 'uc' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_LOCALURL, array( 'CoreParserFunctions', 'localurl' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_LOCALURLE, array( 'CoreParserFunctions', 'localurle' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_FULLURL, array( 'CoreParserFunctions', 'fullurl' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_FULLURLE, array( 'CoreParserFunctions', 'fullurle' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_FORMATNUM, array( 'CoreParserFunctions', 'formatnum' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_GRAMMAR, array( 'CoreParserFunctions', 'grammar' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_PLURAL, array( 'CoreParserFunctions', 'plural' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_NUMBEROFPAGES, array( 'CoreParserFunctions', 'numberofpages' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_NUMBEROFUSERS, array( 'CoreParserFunctions', 'numberofusers' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_NUMBEROFARTICLES, array( 'CoreParserFunctions', 'numberofarticles' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_NUMBEROFFILES, array( 'CoreParserFunctions', 'numberoffiles' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_NUMBEROFADMINS, array( 'CoreParserFunctions', 'numberofadmins' ), SFH_NO_HASH );
- $this->setFunctionHook( MAG_LANGUAGE, array( 'CoreParserFunctions', 'language' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'int', array( 'CoreParserFunctions', 'intFunction' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'ns', array( 'CoreParserFunctions', 'ns' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'urlencode', array( 'CoreParserFunctions', 'urlencode' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'lcfirst', array( 'CoreParserFunctions', 'lcfirst' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'ucfirst', array( 'CoreParserFunctions', 'ucfirst' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'lc', array( 'CoreParserFunctions', 'lc' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'uc', array( 'CoreParserFunctions', 'uc' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'localurl', array( 'CoreParserFunctions', 'localurl' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'localurle', array( 'CoreParserFunctions', 'localurle' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'fullurl', array( 'CoreParserFunctions', 'fullurl' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'fullurle', array( 'CoreParserFunctions', 'fullurle' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'formatnum', array( 'CoreParserFunctions', 'formatnum' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'grammar', array( 'CoreParserFunctions', 'grammar' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'plural', array( 'CoreParserFunctions', 'plural' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofpages', array( 'CoreParserFunctions', 'numberofpages' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofusers', array( 'CoreParserFunctions', 'numberofusers' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofarticles', array( 'CoreParserFunctions', 'numberofarticles' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberoffiles', array( 'CoreParserFunctions', 'numberoffiles' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofadmins', array( 'CoreParserFunctions', 'numberofadmins' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'language', array( 'CoreParserFunctions', 'language' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'padleft', array( 'CoreParserFunctions', 'padleft' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'padright', array( 'CoreParserFunctions', 'padright' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'anchorencode', array( 'CoreParserFunctions', 'anchorencode' ), SFH_NO_HASH );
if ( $wgAllowDisplayTitle ) {
- $this->setFunctionHook( MAG_DISPLAYTITLE, array( 'CoreParserFunctions', 'displaytitle' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'displaytitle', array( 'CoreParserFunctions', 'displaytitle' ), SFH_NO_HASH );
}
if ( $wgAllowSlowParserFunctions ) {
- $this->setFunctionHook( MAG_PAGESINNAMESPACE, array( 'CoreParserFunctions', 'pagesinnamespace' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'pagesinnamespace', array( 'CoreParserFunctions', 'pagesinnamespace' ), SFH_NO_HASH );
}
$this->initialiseVariables();
@@ -187,6 +182,7 @@ class Parser
* @private
*/
function clearState() {
+ wfProfileIn( __METHOD__ );
if ( $this->mFirstCall ) {
$this->firstCallInit();
}
@@ -226,8 +222,25 @@ class Parser
$this->mShowToc = true;
$this->mForceTocPosition = false;
+ $this->mIncludeSizes = array(
+ 'pre-expand' => 0,
+ 'post-expand' => 0,
+ 'arg' => 0
+ );
wfRunHooks( 'ParserClearState', array( &$this ) );
+ wfProfileOut( __METHOD__ );
+ }
+
+ function setOutputType( $ot ) {
+ $this->mOutputType = $ot;
+ // Shortcut alias
+ $this->ot = array(
+ 'html' => $ot == OT_HTML,
+ 'wiki' => $ot == OT_WIKI,
+ 'msg' => $ot == OT_MSG,
+ 'pre' => $ot == OT_PREPROCESS,
+ );
}
/**
@@ -235,7 +248,7 @@ class Parser
*
* @public
*/
- function UniqPrefix() {
+ function uniqPrefix() {
return $this->mUniqPrefix;
}
@@ -259,7 +272,7 @@ class Parser
*/
global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang;
- $fname = 'Parser::parse';
+ $fname = 'Parser::parse-' . wfGetCaller();
wfProfileIn( $fname );
if ( $clearState ) {
@@ -268,8 +281,11 @@ class Parser
$this->mOptions = $options;
$this->mTitle =& $title;
- $this->mRevisionId = $revid;
- $this->mOutputType = OT_HTML;
+ $oldRevisionId = $this->mRevisionId;
+ if( $revid !== null ) {
+ $this->mRevisionId = $revid;
+ }
+ $this->setOutputType( OT_HTML );
//$text = $this->strip( $text, $this->mStripState );
// VOODOO MAGIC FIX! Sometimes the above segfaults in PHP5.
@@ -279,12 +295,6 @@ class Parser
$text = $this->strip( $text, $x );
wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$x ) );
- # Hook to suspend the parser in this state
- if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$x ) ) ) {
- wfProfileOut( $fname );
- return $text ;
- }
-
$text = $this->internalParse( $text );
$text = $this->unstrip( $text, $this->mStripState );
@@ -321,8 +331,8 @@ class Parser
} else {
# attempt to sanitize at least some nesting problems
# (bug #2702 and quite a few others)
- $tidyregs = array(
- # ''Something [http://www.cool.com cool''] -->
+ $tidyregs = array(
+ # ''Something [http://www.cool.com cool''] -->
# <i>Something</i><a href="http://www.cool.com"..><i>cool></i></a>
'/(<([bi])>)(<([bi])>)?([^<]*)(<\/?a[^<]*>)([^<]*)(<\/\\4>)?(<\/\\2>)/' =>
'\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9',
@@ -337,10 +347,10 @@ class Parser
'\\1\\3&lt;div\\5&gt;\\6&lt;/div&gt;\\8\\9',
# remove empty italic or bold tag pairs, some
# introduced by rules above
- '/<([bi])><\/\\1>/' => ''
+ '/<([bi])><\/\\1>/' => '',
);
- $text = preg_replace(
+ $text = preg_replace(
array_keys( $tidyregs ),
array_values( $tidyregs ),
$text );
@@ -348,13 +358,63 @@ class Parser
wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) );
+ # Information on include size limits, for the benefit of users who try to skirt them
+ if ( max( $this->mIncludeSizes ) > 1000 ) {
+ $max = $this->mOptions->getMaxIncludeSize();
+ $text .= "<!-- \n" .
+ "Pre-expand include size: {$this->mIncludeSizes['pre-expand']} bytes\n" .
+ "Post-expand include size: {$this->mIncludeSizes['post-expand']} bytes\n" .
+ "Template argument size: {$this->mIncludeSizes['arg']} bytes\n" .
+ "Maximum: $max bytes\n" .
+ "-->\n";
+ }
$this->mOutput->setText( $text );
+ $this->mRevisionId = $oldRevisionId;
wfProfileOut( $fname );
return $this->mOutput;
}
/**
+ * Recursive parser entry point that can be called from an extension tag
+ * hook.
+ */
+ function recursiveTagParse( $text ) {
+ wfProfileIn( __METHOD__ );
+ $x =& $this->mStripState;
+ wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$x ) );
+ $text = $this->strip( $text, $x );
+ wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$x ) );
+ $text = $this->internalParse( $text );
+ wfProfileOut( __METHOD__ );
+ return $text;
+ }
+
+ /**
+ * Expand templates and variables in the text, producing valid, static wikitext.
+ * Also removes comments.
+ */
+ function preprocess( $text, $title, $options ) {
+ wfProfileIn( __METHOD__ );
+ $this->clearState();
+ $this->setOutputType( OT_PREPROCESS );
+ $this->mOptions = $options;
+ $this->mTitle = $title;
+ $x =& $this->mStripState;
+ wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$x ) );
+ $text = $this->strip( $text, $x );
+ wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$x ) );
+ if ( $this->mOptions->getRemoveComments() ) {
+ $text = Sanitizer::removeHTMLcomments( $text );
+ }
+ $text = $this->replaceVariables( $text );
+ $text = $this->unstrip( $text, $x );
+ $text = $this->unstripNowiki( $text, $x );
+ wfProfileOut( __METHOD__ );
+ return $text;
+ }
+
+ /**
* Get a random string
*
* @private
@@ -391,8 +451,7 @@ class Parser
* @static
*/
function extractTagsAndParams($elements, $text, &$matches, $uniq_prefix = ''){
- $rand = Parser::getRandomString();
- $n = 1;
+ static $n = 1;
$stripped = '';
$matches = array();
@@ -419,7 +478,7 @@ class Parser
$inside = $p[4];
}
- $marker = "$uniq_prefix-$element-$rand" . sprintf('%08X', $n++) . '-QINU';
+ $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . '-QINU';
$stripped .= $marker;
if ( $close === '/>' ) {
@@ -470,11 +529,10 @@ class Parser
* @private
*/
function strip( $text, &$state, $stripcomments = false , $dontstrip = array () ) {
+ wfProfileIn( __METHOD__ );
$render = ($this->mOutputType == OT_HTML);
- # Replace any instances of the placeholders
$uniq_prefix = $this->mUniqPrefix;
- #$text = str_replace( $uniq_prefix, wfHtmlEscapeFirst( $uniq_prefix ), $text );
$commentState = array();
$elements = array_merge(
@@ -501,6 +559,7 @@ class Parser
list( $element, $content, $params, $tag ) = $data;
if( $render ) {
$tagName = strtolower( $element );
+ wfProfileIn( __METHOD__."-render-$tagName" );
switch( $tagName ) {
case '!--':
// Comment
@@ -535,17 +594,25 @@ class Parser
throw new MWException( "Invalid call hook $element" );
}
}
+ wfProfileOut( __METHOD__."-render-$tagName" );
} else {
// Just stripping tags; keep the source
$output = $tag;
}
+
+ // Unstrip the output, because unstrip() is no longer recursive so
+ // it won't do it itself
+ $output = $this->unstrip( $output, $state );
+
if( !$stripcomments && $element == '!--' ) {
$commentState[$marker] = $output;
+ } elseif ( $element == 'html' || $element == 'nowiki' ) {
+ $state['nowiki'][$marker] = $output;
} else {
- $state[$element][$marker] = $output;
+ $state['general'][$marker] = $output;
}
}
-
+
# Unstrip comments unless explicitly told otherwise.
# (The comments are always stripped prior to this point, so as to
# not invoke any extension tags / parser hooks contained within
@@ -555,6 +622,7 @@ class Parser
$text = strtr( $text, $commentState );
}
+ wfProfileOut( __METHOD__ );
return $text;
}
@@ -565,20 +633,14 @@ class Parser
* @private
*/
function unstrip( $text, &$state ) {
- if ( !is_array( $state ) ) {
+ if ( !isset( $state['general'] ) ) {
return $text;
}
- $replacements = array();
- foreach( $state as $tag => $contentDict ) {
- if( $tag != 'nowiki' && $tag != 'html' ) {
- foreach( $contentDict as $uniq => $content ) {
- $replacements[$uniq] = $content;
- }
- }
- }
- $text = strtr( $text, $replacements );
-
+ wfProfileIn( __METHOD__ );
+ # TODO: good candidate for FSS
+ $text = strtr( $text, $state['general'] );
+ wfProfileOut( __METHOD__ );
return $text;
}
@@ -588,20 +650,15 @@ class Parser
* @private
*/
function unstripNoWiki( $text, &$state ) {
- if ( !is_array( $state ) ) {
+ if ( !isset( $state['nowiki'] ) ) {
return $text;
}
- $replacements = array();
- foreach( $state as $tag => $contentDict ) {
- if( $tag == 'nowiki' || $tag == 'html' ) {
- foreach( $contentDict as $uniq => $content ) {
- $replacements[$uniq] = $content;
- }
- }
- }
- $text = strtr( $text, $replacements );
-
+ wfProfileIn( __METHOD__ );
+ # TODO: good candidate for FSS
+ $text = strtr( $text, $state['nowiki'] );
+ wfProfileOut( __METHOD__ );
+
return $text;
}
@@ -617,7 +674,7 @@ class Parser
if ( !$state ) {
$state = array();
}
- $state['item'][$rnd] = $text;
+ $state['general'][$rnd] = $text;
return $rnd;
}
@@ -797,13 +854,13 @@ class Parser
}
$after = substr ( $x , 1 ) ;
if ( $fc == '!' ) $after = str_replace ( '!!' , '||' , $after ) ;
-
+
// Split up multiple cells on the same line.
// FIXME: This can result in improper nesting of tags processed
// by earlier parser steps, but should avoid splitting up eg
// attribute values containing literal "||".
$after = wfExplodeMarkup( '||', $after );
-
+
$t[$k] = '' ;
# Loop through each table cell
@@ -877,11 +934,17 @@ class Parser
$fname = 'Parser::internalParse';
wfProfileIn( $fname );
+ # Hook to suspend the parser in this state
+ if ( !wfRunHooks( 'ParserBeforeInternalParse', array( &$this, &$text, &$x ) ) ) {
+ wfProfileOut( $fname );
+ return $text ;
+ }
+
# Remove <noinclude> tags and <includeonly> sections
$text = strtr( $text, array( '<onlyinclude>' => '' , '</onlyinclude>' => '' ) );
$text = strtr( $text, array( '<noinclude>' => '', '</noinclude>' => '') );
$text = preg_replace( '/<includeonly>.*?<\/includeonly>/s', '', $text );
-
+
$text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
$text = $this->replaceVariables( $text, $args );
@@ -923,9 +986,52 @@ class Parser
* @private
*/
function &doMagicLinks( &$text ) {
- $text = $this->magicISBN( $text );
- $text = $this->magicRFC( $text, 'RFC ', 'rfcurl' );
- $text = $this->magicRFC( $text, 'PMID ', 'pubmedurl' );
+ wfProfileIn( __METHOD__ );
+ $text = preg_replace_callback(
+ '!(?: # Start cases
+ <a.*?</a> | # Skip link text
+ <.*?> | # Skip stuff inside HTML elements
+ (?:RFC|PMID)\s+([0-9]+) | # RFC or PMID, capture number as m[1]
+ ISBN\s+([0-9Xx-]+) # ISBN, capture number as m[2]
+ )!x', array( &$this, 'magicLinkCallback' ), $text );
+ wfProfileOut( __METHOD__ );
+ return $text;
+ }
+
+ function magicLinkCallback( $m ) {
+ if ( substr( $m[0], 0, 1 ) == '<' ) {
+ # Skip HTML element
+ return $m[0];
+ } elseif ( substr( $m[0], 0, 4 ) == 'ISBN' ) {
+ $isbn = $m[2];
+ $num = strtr( $isbn, array(
+ '-' => '',
+ ' ' => '',
+ 'x' => 'X',
+ ));
+ $titleObj = Title::makeTitle( NS_SPECIAL, 'Booksources' );
+ $text = '<a href="' .
+ $titleObj->escapeLocalUrl( "isbn=$num" ) .
+ "\" class=\"internal\">ISBN $isbn</a>";
+ } else {
+ if ( substr( $m[0], 0, 3 ) == 'RFC' ) {
+ $keyword = 'RFC';
+ $urlmsg = 'rfcurl';
+ $id = $m[1];
+ } elseif ( substr( $m[0], 0, 4 ) == 'PMID' ) {
+ $keyword = 'PMID';
+ $urlmsg = 'pubmedurl';
+ $id = $m[1];
+ } else {
+ throw new MWException( __METHOD__.': unrecognised match type "' .
+ substr($m[0], 0, 20 ) . '"' );
+ }
+
+ $url = wfMsg( $urlmsg, $id);
+ $sk =& $this->mOptions->getSkin();
+ $la = $sk->getExternalLinkAttributes( $url, $keyword.$id );
+ $text = "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
+ }
return $text;
}
@@ -1193,13 +1299,8 @@ class Parser
}
$text = $wgContLang->markNoConversion($text);
-
- # Normalize any HTML entities in input. They will be
- # re-escaped by makeExternalLink().
- $url = Sanitizer::decodeCharReferences( $url );
- # Escape any control characters introduced by the above step
- $url = preg_replace( '/[\][<>"\\x00-\\x20\\x7F]/e', "urlencode('\\0')", $url );
+ $url = Sanitizer::cleanUrl( $url );
# Process the trail (i.e. everything after this link up until start of the next link),
# replacing any non-bracketed links
@@ -1280,12 +1381,7 @@ class Parser
$url = substr( $url, 0, -$numSepChars );
}
- # Normalize any HTML entities in input. They will be
- # re-escaped by makeExternalLink() or maybeMakeExternalImage()
- $url = Sanitizer::decodeCharReferences( $url );
-
- # Escape any control characters introduced by the above step
- $url = preg_replace( '/[\][<>"\\x00-\\x20\\x7F]/e', "urlencode('\\0')", $url );
+ $url = Sanitizer::cleanUrl( $url );
# Is this an external image?
$text = $this->maybeMakeExternalImage( $url );
@@ -1316,7 +1412,7 @@ class Parser
* the URL differently; as a workaround, just use the output for
* statistical records, not for actual linking/output.
*/
- function replaceUnusualEscapes( $url ) {
+ static function replaceUnusualEscapes( $url ) {
return preg_replace_callback( '/%[0-9A-Fa-f]{2}/',
array( 'Parser', 'replaceUnusualEscapesCallback' ), $url );
}
@@ -1327,7 +1423,7 @@ class Parser
* @static
* @private
*/
- function replaceUnusualEscapesCallback( $matches ) {
+ private static function replaceUnusualEscapesCallback( $matches ) {
$char = urldecode( $matches[0] );
$ord = ord( $char );
// Is it an unsafe or HTTP reserved character according to RFC 1738?
@@ -1397,7 +1493,7 @@ class Parser
$useLinkPrefixExtension = $wgContLang->linkPrefixExtension();
if( is_null( $this->mTitle ) ) {
- throw new MWException( 'nooo' );
+ throw new MWException( __METHOD__.": \$this->mTitle is null\n" );
}
$nottalk = !$this->mTitle->isTalkPage();
@@ -1412,10 +1508,8 @@ class Parser
}
$selflink = $this->mTitle->getPrefixedText();
- wfProfileOut( $fname.'-setup' );
-
- $checkVariantLink = sizeof($wgContLang->getVariants())>1;
$useSubpages = $this->areSubpagesAllowed();
+ wfProfileOut( $fname.'-setup' );
# Loop for each link
for ($k = 0; isset( $a[$k] ); $k++) {
@@ -1438,6 +1532,7 @@ class Parser
$might_be_img = false;
+ wfProfileIn( "$fname-e1" );
if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt
$text = $m[2];
# If we get a ] at the beginning of $m[3] that means we have a link that's something like:
@@ -1449,27 +1544,33 @@ class Parser
# and no image is in sight. See bug 2095.
#
if( $text !== '' &&
- preg_match( "/^\](.*)/s", $m[3], $n ) &&
+ substr( $m[3], 0, 1 ) === ']' &&
strpos($text, '[') !== false
)
{
$text .= ']'; # so that replaceExternalLinks($text) works later
- $m[3] = $n[1];
+ $m[3] = substr( $m[3], 1 );
}
# fix up urlencoded title texts
- if(preg_match('/%/', $m[1] ))
+ if( strpos( $m[1], '%' ) !== false ) {
# Should anchors '#' also be rejected?
$m[1] = str_replace( array('<', '>'), array('&lt;', '&gt;'), urldecode($m[1]) );
+ }
$trail = $m[3];
} elseif( preg_match($e1_img, $line, $m) ) { # Invalid, but might be an image with a link in its caption
$might_be_img = true;
$text = $m[2];
- if(preg_match('/%/', $m[1] )) $m[1] = urldecode($m[1]);
+ if ( strpos( $m[1], '%' ) !== false ) {
+ $m[1] = urldecode($m[1]);
+ }
$trail = "";
} else { # Invalid form; output directly
$s .= $prefix . '[[' . $line ;
+ wfProfileOut( "$fname-e1" );
continue;
}
+ wfProfileOut( "$fname-e1" );
+ wfProfileIn( "$fname-misc" );
# Don't allow internal links to pages containing
# PROTO: where PROTO is a valid URL protocol; these
@@ -1492,38 +1593,37 @@ class Parser
$link = substr($link, 1);
}
+ wfProfileOut( "$fname-misc" );
+ wfProfileIn( "$fname-title" );
$nt = Title::newFromText( $this->unstripNoWiki($link, $this->mStripState) );
if( !$nt ) {
$s .= $prefix . '[[' . $line;
+ wfProfileOut( "$fname-title" );
continue;
}
- #check other language variants of the link
- #if the article does not exist
- if( $checkVariantLink
- && $nt->getArticleID() == 0 ) {
- $wgContLang->findVariantLink($link, $nt);
- }
-
$ns = $nt->getNamespace();
$iw = $nt->getInterWiki();
-
+ wfProfileOut( "$fname-title" );
+
if ($might_be_img) { # if this is actually an invalid link
+ wfProfileIn( "$fname-might_be_img" );
if ($ns == NS_IMAGE && $noforce) { #but might be an image
$found = false;
while (isset ($a[$k+1]) ) {
#look at the next 'line' to see if we can close it there
$spliced = array_splice( $a, $k + 1, 1 );
$next_line = array_shift( $spliced );
- if( preg_match("/^(.*?]].*?)]](.*)$/sD", $next_line, $m) ) {
- # the first ]] closes the inner link, the second the image
+ $m = explode( ']]', $next_line, 3 );
+ if ( count( $m ) == 3 ) {
+ # the first ]] closes the inner link, the second the image
$found = true;
- $text .= '[[' . $m[1];
+ $text .= "[[{$m[0]}]]{$m[1]}";
$trail = $m[2];
break;
- } elseif( preg_match("/^.*?]].*$/sD", $next_line, $m) ) {
+ } elseif ( count( $m ) == 2 ) {
#if there's exactly one ]] that's fine, we'll keep looking
- $text .= '[[' . $m[0];
+ $text .= "[[{$m[0]}]]{$m[1]}";
} else {
#if $next_line is invalid too, we need look no further
$text .= '[[' . $next_line;
@@ -1534,35 +1634,40 @@ class Parser
# we couldn't find the end of this imageLink, so output it raw
#but don't ignore what might be perfectly normal links in the text we've examined
$text = $this->replaceInternalLinks($text);
- $s .= $prefix . '[[' . $link . '|' . $text;
+ $s .= "{$prefix}[[$link|$text";
# note: no $trail, because without an end, there *is* no trail
+ wfProfileOut( "$fname-might_be_img" );
continue;
}
} else { #it's not an image, so output it raw
- $s .= $prefix . '[[' . $link . '|' . $text;
+ $s .= "{$prefix}[[$link|$text";
# note: no $trail, because without an end, there *is* no trail
+ wfProfileOut( "$fname-might_be_img" );
continue;
}
+ wfProfileOut( "$fname-might_be_img" );
}
$wasblank = ( '' == $text );
if( $wasblank ) $text = $link;
-
# Link not escaped by : , create the various objects
if( $noforce ) {
# Interwikis
+ wfProfileIn( "$fname-interwiki" );
if( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
$this->mOutput->addLanguageLink( $nt->getFullText() );
$s = rtrim($s . "\n");
$s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail;
+ wfProfileOut( "$fname-interwiki" );
continue;
}
+ wfProfileOut( "$fname-interwiki" );
if ( $ns == NS_IMAGE ) {
wfProfileIn( "$fname-image" );
- if ( !wfIsBadImage( $nt->getDBkey() ) ) {
+ if ( !wfIsBadImage( $nt->getDBkey(), $this->mTitle ) ) {
# recursively parse links inside the image caption
# actually, this will parse them in any other parameters, too,
# but it might be hard to fix that, and it doesn't matter ATM
@@ -1630,12 +1735,13 @@ class Parser
$s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
continue;
} elseif( $ns == NS_IMAGE ) {
- $img = Image::newFromTitle( $nt );
+ $img = new Image( $nt );
if( $img->exists() ) {
// Force a blue link if the file exists; may be a remote
// upload on the shared repository, and we want to see its
// auto-generated page.
$s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
+ $this->mOutput->addLink( $nt );
continue;
}
}
@@ -1648,11 +1754,12 @@ class Parser
/**
* Make a link placeholder. The text returned can be later resolved to a real link with
* replaceLinkHolders(). This is done for two reasons: firstly to avoid further
- * parsing of interwiki links, and secondly to allow all extistence checks and
+ * parsing of interwiki links, and secondly to allow all existence checks and
* article length checks (for stub links) to be bundled into a single query.
*
*/
function makeLinkHolder( &$nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
+ wfProfileIn( __METHOD__ );
if ( ! is_object($nt) ) {
# Fail gracefully
$retVal = "<!-- ERROR -->{$prefix}{$text}{$trail}";
@@ -1674,6 +1781,7 @@ class Parser
$retVal = '<!--LINK '. ($nr-1) ."-->{$trail}";
}
}
+ wfProfileOut( __METHOD__ );
return $retVal;
}
@@ -1745,6 +1853,9 @@ class Parser
wfProfileIn( $fname );
$ret = $target; # default return value is no change
+ # bug 7425
+ $target = trim( $target );
+
# Some namespaces don't allow subpages,
# so only perform processing if subpages are allowed
if( $this->areSubpagesAllowed() ) {
@@ -2049,14 +2160,14 @@ class Parser
function findColonNoLinks($str, &$before, &$after) {
$fname = 'Parser::findColonNoLinks';
wfProfileIn( $fname );
-
+
$pos = strpos( $str, ':' );
if( $pos === false ) {
// Nothing to find!
wfProfileOut( $fname );
return false;
}
-
+
$lt = strpos( $str, '<' );
if( $lt === false || $lt > $pos ) {
// Easy; no tag nesting to worry about
@@ -2065,14 +2176,14 @@ class Parser
wfProfileOut( $fname );
return $pos;
}
-
+
// Ugly state machine to walk through avoiding tags.
$state = MW_COLON_STATE_TEXT;
$stack = 0;
$len = strlen( $str );
for( $i = 0; $i < $len; $i++ ) {
$c = $str{$i};
-
+
switch( $state ) {
// (Using the number is a performance hack for common cases)
case 0: // MW_COLON_STATE_TEXT:
@@ -2222,107 +2333,165 @@ class Parser
$ts = time();
wfRunHooks( 'ParserGetVariableValueTs', array( &$this, &$ts ) );
+ # Use the time zone
+ global $wgLocaltimezone;
+ if ( isset( $wgLocaltimezone ) ) {
+ $oldtz = getenv( 'TZ' );
+ putenv( 'TZ='.$wgLocaltimezone );
+ }
+ $localTimestamp = date( 'YmdHis', $ts );
+ $localMonth = date( 'm', $ts );
+ $localMonthName = date( 'n', $ts );
+ $localDay = date( 'j', $ts );
+ $localDay2 = date( 'd', $ts );
+ $localDayOfWeek = date( 'w', $ts );
+ $localWeek = date( 'W', $ts );
+ $localYear = date( 'Y', $ts );
+ $localHour = date( 'H', $ts );
+ if ( isset( $wgLocaltimezone ) ) {
+ putenv( 'TZ='.$oldtz );
+ }
+
switch ( $index ) {
- case MAG_CURRENTMONTH:
+ case 'currentmonth':
return $varCache[$index] = $wgContLang->formatNum( date( 'm', $ts ) );
- case MAG_CURRENTMONTHNAME:
+ case 'currentmonthname':
return $varCache[$index] = $wgContLang->getMonthName( date( 'n', $ts ) );
- case MAG_CURRENTMONTHNAMEGEN:
+ case 'currentmonthnamegen':
return $varCache[$index] = $wgContLang->getMonthNameGen( date( 'n', $ts ) );
- case MAG_CURRENTMONTHABBREV:
+ case 'currentmonthabbrev':
return $varCache[$index] = $wgContLang->getMonthAbbreviation( date( 'n', $ts ) );
- case MAG_CURRENTDAY:
+ case 'currentday':
return $varCache[$index] = $wgContLang->formatNum( date( 'j', $ts ) );
- case MAG_CURRENTDAY2:
+ case 'currentday2':
return $varCache[$index] = $wgContLang->formatNum( date( 'd', $ts ) );
- case MAG_PAGENAME:
+ case 'localmonth':
+ return $varCache[$index] = $wgContLang->formatNum( $localMonth );
+ case 'localmonthname':
+ return $varCache[$index] = $wgContLang->getMonthName( $localMonthName );
+ case 'localmonthnamegen':
+ return $varCache[$index] = $wgContLang->getMonthNameGen( $localMonthName );
+ case 'localmonthabbrev':
+ return $varCache[$index] = $wgContLang->getMonthAbbreviation( $localMonthName );
+ case 'localday':
+ return $varCache[$index] = $wgContLang->formatNum( $localDay );
+ case 'localday2':
+ return $varCache[$index] = $wgContLang->formatNum( $localDay2 );
+ case 'pagename':
return $this->mTitle->getText();
- case MAG_PAGENAMEE:
+ case 'pagenamee':
return $this->mTitle->getPartialURL();
- case MAG_FULLPAGENAME:
+ case 'fullpagename':
return $this->mTitle->getPrefixedText();
- case MAG_FULLPAGENAMEE:
+ case 'fullpagenamee':
return $this->mTitle->getPrefixedURL();
- case MAG_SUBPAGENAME:
+ case 'subpagename':
return $this->mTitle->getSubpageText();
- case MAG_SUBPAGENAMEE:
+ case 'subpagenamee':
return $this->mTitle->getSubpageUrlForm();
- case MAG_BASEPAGENAME:
+ case 'basepagename':
return $this->mTitle->getBaseText();
- case MAG_BASEPAGENAMEE:
+ case 'basepagenamee':
return wfUrlEncode( str_replace( ' ', '_', $this->mTitle->getBaseText() ) );
- case MAG_TALKPAGENAME:
+ case 'talkpagename':
if( $this->mTitle->canTalk() ) {
$talkPage = $this->mTitle->getTalkPage();
return $talkPage->getPrefixedText();
} else {
return '';
}
- case MAG_TALKPAGENAMEE:
+ case 'talkpagenamee':
if( $this->mTitle->canTalk() ) {
$talkPage = $this->mTitle->getTalkPage();
return $talkPage->getPrefixedUrl();
} else {
return '';
}
- case MAG_SUBJECTPAGENAME:
+ case 'subjectpagename':
$subjPage = $this->mTitle->getSubjectPage();
return $subjPage->getPrefixedText();
- case MAG_SUBJECTPAGENAMEE:
+ case 'subjectpagenamee':
$subjPage = $this->mTitle->getSubjectPage();
return $subjPage->getPrefixedUrl();
- case MAG_REVISIONID:
+ case 'revisionid':
return $this->mRevisionId;
- case MAG_NAMESPACE:
+ case 'revisionday':
+ return intval( substr( wfRevisionTimestamp( $this->mRevisionId ), 6, 2 ) );
+ case 'revisionday2':
+ return substr( wfRevisionTimestamp( $this->mRevisionId ), 6, 2 );
+ case 'revisionmonth':
+ return intval( substr( wfRevisionTimestamp( $this->mRevisionId ), 4, 2 ) );
+ case 'revisionyear':
+ return substr( wfRevisionTimestamp( $this->mRevisionId ), 0, 4 );
+ case 'revisiontimestamp':
+ return wfRevisionTimestamp( $this->mRevisionId );
+ case 'namespace':
return str_replace('_',' ',$wgContLang->getNsText( $this->mTitle->getNamespace() ) );
- case MAG_NAMESPACEE:
+ case 'namespacee':
return wfUrlencode( $wgContLang->getNsText( $this->mTitle->getNamespace() ) );
- case MAG_TALKSPACE:
+ case 'talkspace':
return $this->mTitle->canTalk() ? str_replace('_',' ',$this->mTitle->getTalkNsText()) : '';
- case MAG_TALKSPACEE:
+ case 'talkspacee':
return $this->mTitle->canTalk() ? wfUrlencode( $this->mTitle->getTalkNsText() ) : '';
- case MAG_SUBJECTSPACE:
+ case 'subjectspace':
return $this->mTitle->getSubjectNsText();
- case MAG_SUBJECTSPACEE:
+ case 'subjectspacee':
return( wfUrlencode( $this->mTitle->getSubjectNsText() ) );
- case MAG_CURRENTDAYNAME:
+ case 'currentdayname':
return $varCache[$index] = $wgContLang->getWeekdayName( date( 'w', $ts ) + 1 );
- case MAG_CURRENTYEAR:
+ case 'currentyear':
return $varCache[$index] = $wgContLang->formatNum( date( 'Y', $ts ), true );
- case MAG_CURRENTTIME:
+ case 'currenttime':
return $varCache[$index] = $wgContLang->time( wfTimestamp( TS_MW, $ts ), false, false );
- case MAG_CURRENTWEEK:
+ case 'currenthour':
+ return $varCache[$index] = $wgContLang->formatNum( date( 'H', $ts ), true );
+ case 'currentweek':
// @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
// int to remove the padding
return $varCache[$index] = $wgContLang->formatNum( (int)date( 'W', $ts ) );
- case MAG_CURRENTDOW:
+ case 'currentdow':
return $varCache[$index] = $wgContLang->formatNum( date( 'w', $ts ) );
- case MAG_NUMBEROFARTICLES:
+ case 'localdayname':
+ return $varCache[$index] = $wgContLang->getWeekdayName( $localDayOfWeek + 1 );
+ case 'localyear':
+ return $varCache[$index] = $wgContLang->formatNum( $localYear, true );
+ case 'localtime':
+ return $varCache[$index] = $wgContLang->time( $localTimestamp, false, false );
+ case 'localhour':
+ return $varCache[$index] = $wgContLang->formatNum( $localHour, true );
+ case 'localweek':
+ // @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
+ // int to remove the padding
+ return $varCache[$index] = $wgContLang->formatNum( (int)$localWeek );
+ case 'localdow':
+ return $varCache[$index] = $wgContLang->formatNum( $localDayOfWeek );
+ case 'numberofarticles':
return $varCache[$index] = $wgContLang->formatNum( wfNumberOfArticles() );
- case MAG_NUMBEROFFILES:
+ case 'numberoffiles':
return $varCache[$index] = $wgContLang->formatNum( wfNumberOfFiles() );
- case MAG_NUMBEROFUSERS:
+ case 'numberofusers':
return $varCache[$index] = $wgContLang->formatNum( wfNumberOfUsers() );
- case MAG_NUMBEROFPAGES:
+ case 'numberofpages':
return $varCache[$index] = $wgContLang->formatNum( wfNumberOfPages() );
- case MAG_NUMBEROFADMINS:
+ case 'numberofadmins':
return $varCache[$index] = $wgContLang->formatNum( wfNumberOfAdmins() );
- case MAG_CURRENTTIMESTAMP:
+ case 'currenttimestamp':
return $varCache[$index] = wfTimestampNow();
- case MAG_CURRENTVERSION:
- global $wgVersion;
- return $wgVersion;
- case MAG_SITENAME:
+ case 'localtimestamp':
+ return $varCache[$index] = $localTimestamp;
+ case 'currentversion':
+ return $varCache[$index] = SpecialVersion::getVersion();
+ case 'sitename':
return $wgSitename;
- case MAG_SERVER:
+ case 'server':
return $wgServer;
- case MAG_SERVERNAME:
+ case 'servername':
return $wgServerName;
- case MAG_SCRIPTPATH:
+ case 'scriptpath':
return $wgScriptPath;
- case MAG_DIRECTIONMARK:
+ case 'directionmark':
return $wgContLang->getDirMark();
- case MAG_CONTENTLANGUAGE:
+ case 'contentlanguage':
global $wgContLanguageCode;
return $wgContLanguageCode;
default:
@@ -2342,10 +2511,10 @@ class Parser
function initialiseVariables() {
$fname = 'Parser::initialiseVariables';
wfProfileIn( $fname );
- global $wgVariableIDs;
+ $variableIDs = MagicWord::getVariableIDs();
$this->mVariables = array();
- foreach ( $wgVariableIDs as $id ) {
+ foreach ( $variableIDs as $id ) {
$mw =& MagicWord::get( $id );
$mw->addToArray( $this->mVariables, $id );
}
@@ -2361,172 +2530,169 @@ class Parser
* '{' => array( # opening parentheses
* 'end' => '}', # closing parentheses
* 'cb' => array(2 => callback, # replacement callback to call if {{..}} is found
- * 4 => callback # replacement callback to call if {{{{..}}}} is found
+ * 3 => callback # replacement callback to call if {{{..}}} is found
* )
* )
+ * 'min' => 2, # Minimum parenthesis count in cb
+ * 'max' => 3, # Maximum parenthesis count in cb
* @private
*/
function replace_callback ($text, $callbacks) {
- wfProfileIn( __METHOD__ . '-self' );
+ wfProfileIn( __METHOD__ );
$openingBraceStack = array(); # this array will hold a stack of parentheses which are not closed yet
$lastOpeningBrace = -1; # last not closed parentheses
- for ($i = 0; $i < strlen($text); $i++) {
- # check for any opening brace
- $rule = null;
- $nextPos = -1;
- foreach ($callbacks as $key => $value) {
- $pos = strpos ($text, $key, $i);
- if (false !== $pos && (-1 == $nextPos || $pos < $nextPos)) {
- $rule = $value;
- $nextPos = $pos;
- }
+ $validOpeningBraces = implode( '', array_keys( $callbacks ) );
+
+ $i = 0;
+ while ( $i < strlen( $text ) ) {
+ # Find next opening brace, closing brace or pipe
+ if ( $lastOpeningBrace == -1 ) {
+ $currentClosing = '';
+ $search = $validOpeningBraces;
+ } else {
+ $currentClosing = $openingBraceStack[$lastOpeningBrace]['braceEnd'];
+ $search = $validOpeningBraces . '|' . $currentClosing;
}
-
- if ($lastOpeningBrace >= 0) {
- $pos = strpos ($text, $openingBraceStack[$lastOpeningBrace]['braceEnd'], $i);
-
- if (false !== $pos && (-1 == $nextPos || $pos < $nextPos)){
- $rule = null;
- $nextPos = $pos;
- }
-
- $pos = strpos ($text, '|', $i);
-
- if (false !== $pos && (-1 == $nextPos || $pos < $nextPos)){
- $rule = null;
- $nextPos = $pos;
+ $rule = null;
+ $i += strcspn( $text, $search, $i );
+ if ( $i < strlen( $text ) ) {
+ if ( $text[$i] == '|' ) {
+ $found = 'pipe';
+ } elseif ( $text[$i] == $currentClosing ) {
+ $found = 'close';
+ } elseif ( isset( $callbacks[$text[$i]] ) ) {
+ $found = 'open';
+ $rule = $callbacks[$text[$i]];
+ } else {
+ # Some versions of PHP have a strcspn which stops on null characters
+ # Ignore and continue
+ ++$i;
+ continue;
}
- }
-
- if ($nextPos == -1)
+ } else {
+ # All done
break;
+ }
- $i = $nextPos;
-
- # found openning brace, lets add it to parentheses stack
- if (null != $rule) {
+ if ( $found == 'open' ) {
+ # found opening brace, let's add it to parentheses stack
$piece = array('brace' => $text[$i],
'braceEnd' => $rule['end'],
- 'count' => 1,
'title' => '',
'parts' => null);
- # count openning brace characters
- while ($i+1 < strlen($text) && $text[$i+1] == $piece['brace']) {
- $piece['count']++;
- $i++;
- }
-
- $piece['startAt'] = $i+1;
- $piece['partStart'] = $i+1;
+ # count opening brace characters
+ $piece['count'] = strspn( $text, $piece['brace'], $i );
+ $piece['startAt'] = $piece['partStart'] = $i + $piece['count'];
+ $i += $piece['count'];
- # we need to add to stack only if openning brace count is enough for any given rule
- foreach ($rule['cb'] as $cnt => $fn) {
- if ($piece['count'] >= $cnt) {
- $lastOpeningBrace ++;
- $openingBraceStack[$lastOpeningBrace] = $piece;
- break;
+ # we need to add to stack only if opening brace count is enough for one of the rules
+ if ( $piece['count'] >= $rule['min'] ) {
+ $lastOpeningBrace ++;
+ $openingBraceStack[$lastOpeningBrace] = $piece;
+ }
+ } elseif ( $found == 'close' ) {
+ # lets check if it is enough characters for closing brace
+ $maxCount = $openingBraceStack[$lastOpeningBrace]['count'];
+ $count = strspn( $text, $text[$i], $i, $maxCount );
+
+ # check for maximum matching characters (if there are 5 closing
+ # characters, we will probably need only 3 - depending on the rules)
+ $matchingCount = 0;
+ $matchingCallback = null;
+ $cbType = $callbacks[$openingBraceStack[$lastOpeningBrace]['brace']];
+ if ( $count > $cbType['max'] ) {
+ # The specified maximum exists in the callback array, unless the caller
+ # has made an error
+ $matchingCount = $cbType['max'];
+ } else {
+ # Count is less than the maximum
+ # Skip any gaps in the callback array to find the true largest match
+ # Need to use array_key_exists not isset because the callback can be null
+ $matchingCount = $count;
+ while ( $matchingCount > 0 && !array_key_exists( $matchingCount, $cbType['cb'] ) ) {
+ --$matchingCount;
}
}
- continue;
- }
- else if ($lastOpeningBrace >= 0) {
- # first check if it is a closing brace
- if ($openingBraceStack[$lastOpeningBrace]['braceEnd'] == $text[$i]) {
- # lets check if it is enough characters for closing brace
- $count = 1;
- while ($i+$count < strlen($text) && $text[$i+$count] == $text[$i])
- $count++;
-
- # if there are more closing parentheses than opening ones, we parse less
- if ($openingBraceStack[$lastOpeningBrace]['count'] < $count)
- $count = $openingBraceStack[$lastOpeningBrace]['count'];
-
- # check for maximum matching characters (if there are 5 closing characters, we will probably need only 3 - depending on the rules)
- $matchingCount = 0;
- $matchingCallback = null;
- foreach ($callbacks[$openingBraceStack[$lastOpeningBrace]['brace']]['cb'] as $cnt => $fn) {
- if ($count >= $cnt && $matchingCount < $cnt) {
- $matchingCount = $cnt;
- $matchingCallback = $fn;
- }
- }
+ if ($matchingCount <= 0) {
+ $i += $count;
+ continue;
+ }
+ $matchingCallback = $cbType['cb'][$matchingCount];
- if ($matchingCount == 0) {
- $i += $count - 1;
- continue;
- }
+ # let's set a title or last part (if '|' was found)
+ if (null === $openingBraceStack[$lastOpeningBrace]['parts']) {
+ $openingBraceStack[$lastOpeningBrace]['title'] =
+ substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'],
+ $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
+ } else {
+ $openingBraceStack[$lastOpeningBrace]['parts'][] =
+ substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'],
+ $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
+ }
- # lets set a title or last part (if '|' was found)
- if (null === $openingBraceStack[$lastOpeningBrace]['parts'])
- $openingBraceStack[$lastOpeningBrace]['title'] = substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'], $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
- else
- $openingBraceStack[$lastOpeningBrace]['parts'][] = substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'], $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
-
- $pieceStart = $openingBraceStack[$lastOpeningBrace]['startAt'] - $matchingCount;
- $pieceEnd = $i + $matchingCount;
-
- if( is_callable( $matchingCallback ) ) {
- $cbArgs = array (
- 'text' => substr($text, $pieceStart, $pieceEnd - $pieceStart),
- 'title' => trim($openingBraceStack[$lastOpeningBrace]['title']),
- 'parts' => $openingBraceStack[$lastOpeningBrace]['parts'],
- 'lineStart' => (($pieceStart > 0) && ($text[$pieceStart-1] == "\n")),
- );
- # finally we can call a user callback and replace piece of text
- wfProfileOut( __METHOD__ . '-self' );
- $replaceWith = call_user_func( $matchingCallback, $cbArgs );
- wfProfileIn( __METHOD__ . '-self' );
- $text = substr($text, 0, $pieceStart) . $replaceWith . substr($text, $pieceEnd);
- $i = $pieceStart + strlen($replaceWith) - 1;
- }
- else {
- # null value for callback means that parentheses should be parsed, but not replaced
- $i += $matchingCount - 1;
- }
+ $pieceStart = $openingBraceStack[$lastOpeningBrace]['startAt'] - $matchingCount;
+ $pieceEnd = $i + $matchingCount;
+
+ if( is_callable( $matchingCallback ) ) {
+ $cbArgs = array (
+ 'text' => substr($text, $pieceStart, $pieceEnd - $pieceStart),
+ 'title' => trim($openingBraceStack[$lastOpeningBrace]['title']),
+ 'parts' => $openingBraceStack[$lastOpeningBrace]['parts'],
+ 'lineStart' => (($pieceStart > 0) && ($text[$pieceStart-1] == "\n")),
+ );
+ # finally we can call a user callback and replace piece of text
+ $replaceWith = call_user_func( $matchingCallback, $cbArgs );
+ $text = substr($text, 0, $pieceStart) . $replaceWith . substr($text, $pieceEnd);
+ $i = $pieceStart + strlen($replaceWith);
+ } else {
+ # null value for callback means that parentheses should be parsed, but not replaced
+ $i += $matchingCount;
+ }
- # reset last openning parentheses, but keep it in case there are unused characters
- $piece = array('brace' => $openingBraceStack[$lastOpeningBrace]['brace'],
- 'braceEnd' => $openingBraceStack[$lastOpeningBrace]['braceEnd'],
- 'count' => $openingBraceStack[$lastOpeningBrace]['count'],
- 'title' => '',
- 'parts' => null,
- 'startAt' => $openingBraceStack[$lastOpeningBrace]['startAt']);
- $openingBraceStack[$lastOpeningBrace--] = null;
-
- if ($matchingCount < $piece['count']) {
- $piece['count'] -= $matchingCount;
- $piece['startAt'] -= $matchingCount;
- $piece['partStart'] = $piece['startAt'];
- # do we still qualify for any callback with remaining count?
- foreach ($callbacks[$piece['brace']]['cb'] as $cnt => $fn) {
- if ($piece['count'] >= $cnt) {
- $lastOpeningBrace ++;
- $openingBraceStack[$lastOpeningBrace] = $piece;
- break;
- }
+ # reset last opening parentheses, but keep it in case there are unused characters
+ $piece = array('brace' => $openingBraceStack[$lastOpeningBrace]['brace'],
+ 'braceEnd' => $openingBraceStack[$lastOpeningBrace]['braceEnd'],
+ 'count' => $openingBraceStack[$lastOpeningBrace]['count'],
+ 'title' => '',
+ 'parts' => null,
+ 'startAt' => $openingBraceStack[$lastOpeningBrace]['startAt']);
+ $openingBraceStack[$lastOpeningBrace--] = null;
+
+ if ($matchingCount < $piece['count']) {
+ $piece['count'] -= $matchingCount;
+ $piece['startAt'] -= $matchingCount;
+ $piece['partStart'] = $piece['startAt'];
+ # do we still qualify for any callback with remaining count?
+ $currentCbList = $callbacks[$piece['brace']]['cb'];
+ while ( $piece['count'] ) {
+ if ( array_key_exists( $piece['count'], $currentCbList ) ) {
+ $lastOpeningBrace++;
+ $openingBraceStack[$lastOpeningBrace] = $piece;
+ break;
}
+ --$piece['count'];
}
- continue;
}
-
+ } elseif ( $found == 'pipe' ) {
# lets set a title if it is a first separator, or next part otherwise
- if ($text[$i] == '|') {
- if (null === $openingBraceStack[$lastOpeningBrace]['parts']) {
- $openingBraceStack[$lastOpeningBrace]['title'] = substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'], $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
- $openingBraceStack[$lastOpeningBrace]['parts'] = array();
- }
- else
- $openingBraceStack[$lastOpeningBrace]['parts'][] = substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'], $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
-
- $openingBraceStack[$lastOpeningBrace]['partStart'] = $i + 1;
+ if (null === $openingBraceStack[$lastOpeningBrace]['parts']) {
+ $openingBraceStack[$lastOpeningBrace]['title'] =
+ substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'],
+ $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
+ $openingBraceStack[$lastOpeningBrace]['parts'] = array();
+ } else {
+ $openingBraceStack[$lastOpeningBrace]['parts'][] =
+ substr($text, $openingBraceStack[$lastOpeningBrace]['partStart'],
+ $i - $openingBraceStack[$lastOpeningBrace]['partStart']);
}
+ $openingBraceStack[$lastOpeningBrace]['partStart'] = ++$i;
}
}
- wfProfileOut( __METHOD__ . '-self' );
+ wfProfileOut( __METHOD__ );
return $text;
}
@@ -2547,11 +2713,11 @@ class Parser
*/
function replaceVariables( $text, $args = array(), $argsOnly = false ) {
# Prevent too big inclusions
- if( strlen( $text ) > MAX_INCLUDE_SIZE ) {
+ if( strlen( $text ) > $this->mOptions->getMaxIncludeSize() ) {
return $text;
}
- $fname = 'Parser::replaceVariables';
+ $fname = __METHOD__ /*. '-L' . count( $this->mArgStack )*/;
wfProfileIn( $fname );
# This function is called recursively. To keep track of arguments we need a stack:
@@ -2561,32 +2727,45 @@ class Parser
if ( !$argsOnly ) {
$braceCallbacks[2] = array( &$this, 'braceSubstitution' );
}
- if ( $this->mOutputType == OT_HTML || $this->mOutputType == OT_WIKI ) {
+ if ( $this->mOutputType != OT_MSG ) {
$braceCallbacks[3] = array( &$this, 'argSubstitution' );
}
- $callbacks = array();
- $callbacks['{'] = array('end' => '}', 'cb' => $braceCallbacks);
- $callbacks['['] = array('end' => ']', 'cb' => array(2=>null));
- $text = $this->replace_callback ($text, $callbacks);
-
- array_pop( $this->mArgStack );
+ if ( $braceCallbacks ) {
+ $callbacks = array(
+ '{' => array(
+ 'end' => '}',
+ 'cb' => $braceCallbacks,
+ 'min' => $argsOnly ? 3 : 2,
+ 'max' => isset( $braceCallbacks[3] ) ? 3 : 2,
+ ),
+ '[' => array(
+ 'end' => ']',
+ 'cb' => array(2=>null),
+ 'min' => 2,
+ 'max' => 2,
+ )
+ );
+ $text = $this->replace_callback ($text, $callbacks);
+ array_pop( $this->mArgStack );
+ }
wfProfileOut( $fname );
return $text;
}
-
+
/**
* Replace magic variables
* @private
*/
function variableSubstitution( $matches ) {
+ global $wgContLang;
$fname = 'Parser::variableSubstitution';
- $varname = $matches[1];
+ $varname = $wgContLang->lc($matches[1]);
wfProfileIn( $fname );
$skip = false;
if ( $this->mOutputType == OT_WIKI ) {
# Do only magic variables prefixed by SUBST
- $mwSubst =& MagicWord::get( MAG_SUBST );
+ $mwSubst =& MagicWord::get( 'subst' );
if (!$mwSubst->matchStartAndRemove( $varname ))
$skip = true;
# Note that if we don't substitute the variable below,
@@ -2595,8 +2774,14 @@ class Parser
}
if ( !$skip && array_key_exists( $varname, $this->mVariables ) ) {
$id = $this->mVariables[$varname];
- $text = $this->getVariableValue( $id );
- $this->mOutput->mContainsOldMagic = true;
+ # Now check if we did really match, case sensitive or not
+ $mw =& MagicWord::get( $id );
+ if ($mw->match($matches[1])) {
+ $text = $this->getVariableValue( $id );
+ $this->mOutput->mContainsOldMagic = true;
+ } else {
+ $text = $matches[0];
+ }
} else {
$text = $matches[0];
}
@@ -2642,8 +2827,9 @@ class Parser
*/
function braceSubstitution( $piece ) {
global $wgContLang, $wgLang, $wgAllowDisplayTitle, $action;
- $fname = 'Parser::braceSubstitution';
+ $fname = __METHOD__ /*. '-L' . count( $this->mArgStack )*/;
wfProfileIn( $fname );
+ wfProfileIn( __METHOD__.'-setup' );
# Flags
$found = false; # $text has been filled
@@ -2659,10 +2845,11 @@ class Parser
$linestart = '';
+
# $part1 is the bit before the first |, and must contain only title characters
# $args is a list of arguments, starting from index 0, not including $part1
- $part1 = $piece['title'];
+ $titleText = $part1 = $piece['title'];
# If the third subpattern matched anything, it will start with |
if (null == $piece['parts']) {
@@ -2677,11 +2864,13 @@ class Parser
$args = (null == $piece['parts']) ? array() : $piece['parts'];
$argc = count( $args );
+ wfProfileOut( __METHOD__.'-setup' );
# SUBST
+ wfProfileIn( __METHOD__.'-modifiers' );
if ( !$found ) {
- $mwSubst =& MagicWord::get( MAG_SUBST );
- if ( $mwSubst->matchStartAndRemove( $part1 ) xor ($this->mOutputType == OT_WIKI) ) {
+ $mwSubst =& MagicWord::get( 'subst' );
+ if ( $mwSubst->matchStartAndRemove( $part1 ) xor $this->ot['wiki'] ) {
# One of two possibilities is true:
# 1) Found SUBST but not in the PST phase
# 2) Didn't find SUBST and in the PST phase
@@ -2693,33 +2882,25 @@ class Parser
}
}
- # MSG, MSGNW, INT and RAW
+ # MSG, MSGNW and RAW
if ( !$found ) {
# Check for MSGNW:
- $mwMsgnw =& MagicWord::get( MAG_MSGNW );
+ $mwMsgnw =& MagicWord::get( 'msgnw' );
if ( $mwMsgnw->matchStartAndRemove( $part1 ) ) {
$nowiki = true;
} else {
# Remove obsolete MSG:
- $mwMsg =& MagicWord::get( MAG_MSG );
+ $mwMsg =& MagicWord::get( 'msg' );
$mwMsg->matchStartAndRemove( $part1 );
}
# Check for RAW:
- $mwRaw =& MagicWord::get( MAG_RAW );
+ $mwRaw =& MagicWord::get( 'raw' );
if ( $mwRaw->matchStartAndRemove( $part1 ) ) {
$forceRawInterwiki = true;
}
-
- # Check if it is an internal message
- $mwInt =& MagicWord::get( MAG_INT );
- if ( $mwInt->matchStartAndRemove( $part1 ) ) {
- if ( $this->incrementIncludeCount( 'int:'.$part1 ) ) {
- $text = $linestart . wfMsgReal( $part1, $args, true );
- $found = true;
- }
- }
}
+ wfProfileOut( __METHOD__.'-modifiers' );
# Parser functions
if ( !$found ) {
@@ -2764,7 +2945,7 @@ class Parser
}
}
}
- wfProfileOut( __METHOD__ . '-pfunc' );
+ wfProfileOut( __METHOD__ . '-pfunc' );
}
# Template table test
@@ -2780,9 +2961,8 @@ class Parser
$noargs = true;
$found = true;
$text = $linestart .
- '{{' . $part1 . '}}' .
- '<!-- WARNING: template loop detected -->';
- wfDebug( "$fname: template loop broken at '$part1'\n" );
+ "[[$part1]]<!-- WARNING: template loop detected -->";
+ wfDebug( __METHOD__.": template loop broken at '$part1'\n" );
} else {
# set $text to cached message.
$text = $linestart . $this->mTemplates[$piece['title']];
@@ -2806,6 +2986,7 @@ class Parser
if ( !is_null( $title ) ) {
+ $titleText = $title->getPrefixedText();
$checkVariantLink = sizeof($wgContLang->getVariants())>1;
# Check for language variants if the template is not found
if($checkVariantLink && $title->getArticleID() == 0){
@@ -2813,36 +2994,32 @@ class Parser
}
if ( !$title->isExternal() ) {
- # Check for excessive inclusion
- $dbk = $title->getPrefixedDBkey();
- if ( $this->incrementIncludeCount( $dbk ) ) {
- if ( $title->getNamespace() == NS_SPECIAL && $this->mOptions->getAllowSpecialInclusion() && $this->mOutputType != OT_WIKI ) {
- $text = SpecialPage::capturePath( $title );
- if ( is_string( $text ) ) {
- $found = true;
- $noparse = true;
- $noargs = true;
- $isHTML = true;
- $this->disableCache();
- }
- } else {
- $articleContent = $this->fetchTemplate( $title );
- if ( $articleContent !== false ) {
- $found = true;
- $text = $articleContent;
- $replaceHeadings = true;
- }
+ if ( $title->getNamespace() == NS_SPECIAL && $this->mOptions->getAllowSpecialInclusion() && $this->ot['html'] ) {
+ $text = SpecialPage::capturePath( $title );
+ if ( is_string( $text ) ) {
+ $found = true;
+ $noparse = true;
+ $noargs = true;
+ $isHTML = true;
+ $this->disableCache();
+ }
+ } else {
+ $articleContent = $this->fetchTemplate( $title );
+ if ( $articleContent !== false ) {
+ $found = true;
+ $text = $articleContent;
+ $replaceHeadings = true;
}
}
# If the title is valid but undisplayable, make a link to it
- if ( $this->mOutputType == OT_HTML && !$found ) {
- $text = '[['.$title->getPrefixedText().']]';
+ if ( !$found && ( $this->ot['html'] || $this->ot['pre'] ) ) {
+ $text = "[[:$titleText]]";
$found = true;
}
} elseif ( $title->isTrans() ) {
// Interwiki transclusion
- if ( $this->mOutputType == OT_HTML && !$forceRawInterwiki ) {
+ if ( $this->ot['html'] && !$forceRawInterwiki ) {
$text = $this->interwikiTransclude( $title, 'render' );
$isHTML = true;
$noparse = true;
@@ -2852,7 +3029,7 @@ class Parser
}
$found = true;
}
-
+
# Template cache array insertion
# Use the original $piece['title'] not the mangled $part1, so that
# modifiers such as RAW: produce separate cache entries
@@ -2865,14 +3042,22 @@ class Parser
$text = $linestart . $text;
}
}
- wfProfileOut( __METHOD__ . '-loadtpl' );
+ wfProfileOut( __METHOD__ . '-loadtpl' );
+ }
+
+ if ( $found && !$this->incrementIncludeSize( 'pre-expand', strlen( $text ) ) ) {
+ # Error, oversize inclusion
+ $text = $linestart .
+ "[[$titleText]]<!-- WARNING: template omitted, pre-expand include size too large -->";
+ $noparse = true;
+ $noargs = true;
}
# Recursive parsing, escaping and link table handling
# Only for HTML output
- if ( $nowiki && $found && $this->mOutputType == OT_HTML ) {
+ if ( $nowiki && $found && ( $this->ot['html'] || $this->ot['pre'] ) ) {
$text = wfEscapeWikiText( $text );
- } elseif ( ($this->mOutputType == OT_HTML || $this->mOutputType == OT_WIKI) && $found ) {
+ } elseif ( !$this->ot['msg'] && $found ) {
if ( $noargs ) {
$assocArgs = array();
} else {
@@ -2911,16 +3096,20 @@ class Parser
$text = preg_replace( '/<noinclude>.*?<\/noinclude>/s', '', $text );
$text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) );
- if( $this->mOutputType == OT_HTML ) {
+ if( $this->ot['html'] || $this->ot['pre'] ) {
# Strip <nowiki>, <pre>, etc.
$text = $this->strip( $text, $this->mStripState );
- $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ), $assocArgs );
+ if ( $this->ot['html'] ) {
+ $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ), $assocArgs );
+ } elseif ( $this->ot['pre'] && $this->mOptions->getRemoveComments() ) {
+ $text = Sanitizer::removeHTMLcomments( $text );
+ }
}
$text = $this->replaceVariables( $text, $assocArgs );
# If the template begins with a table or block-level
# element, it should be treated as beginning a new line.
- if (!$piece['lineStart'] && preg_match('/^({\\||:|;|#|\*)/', $text)) {
+ if (!$piece['lineStart'] && preg_match('/^({\\||:|;|#|\*)/', $text)) /*}*/{
$text = "\n" . $text;
}
} elseif ( !$noargs ) {
@@ -2933,6 +3122,14 @@ class Parser
# Prune lower levels off the recursion check path
$this->mTemplatePath = $lastPathLevel;
+ if ( $found && !$this->incrementIncludeSize( 'post-expand', strlen( $text ) ) ) {
+ # Error, oversize inclusion
+ $text = $linestart .
+ "[[$titleText]]<!-- WARNING: template omitted, post-expand include size too large -->";
+ $noparse = true;
+ $noargs = true;
+ }
+
if ( !$found ) {
wfProfileOut( $fname );
return $piece['text'];
@@ -2946,7 +3143,7 @@ class Parser
} else {
# replace ==section headers==
# XXX this needs to go away once we have a better parser.
- if ( $this->mOutputType != OT_WIKI && $replaceHeadings ) {
+ if ( !$this->ot['wiki'] && !$this->ot['pre'] && $replaceHeadings ) {
if( !is_null( $title ) )
$encodedname = base64_encode($title->getPrefixedDBkey());
else
@@ -3070,25 +3267,31 @@ class Parser
if ( array_key_exists( $arg, $inputArgs ) ) {
$text = $inputArgs[$arg];
- } else if ($this->mOutputType == OT_HTML && null != $matches['parts'] && count($matches['parts']) > 0) {
+ } else if (($this->mOutputType == OT_HTML || $this->mOutputType == OT_PREPROCESS ) &&
+ null != $matches['parts'] && count($matches['parts']) > 0) {
$text = $matches['parts'][0];
}
+ if ( !$this->incrementIncludeSize( 'arg', strlen( $text ) ) ) {
+ $text = $matches['text'] .
+ '<!-- WARNING: argument omitted, expansion size too large -->';
+ }
return $text;
}
/**
- * Returns true if the function is allowed to include this entity
- * @private
+ * Increment an include size counter
+ *
+ * @param string $type The type of expansion
+ * @param integer $size The size of the text
+ * @return boolean False if this inclusion would take it over the maximum, true otherwise
*/
- function incrementIncludeCount( $dbk ) {
- if ( !array_key_exists( $dbk, $this->mIncludeCount ) ) {
- $this->mIncludeCount[$dbk] = 0;
- }
- if ( ++$this->mIncludeCount[$dbk] <= MAX_INCLUDE_REPEAT ) {
- return true;
- } else {
+ function incrementIncludeSize( $type, $size ) {
+ if ( $this->mIncludeSizes[$type] + $size > $this->mOptions->getMaxIncludeSize() ) {
return false;
+ } else {
+ $this->mIncludeSizes[$type] += $size;
+ return true;
}
}
@@ -3098,7 +3301,7 @@ class Parser
function stripNoGallery( &$text ) {
# if the string __NOGALLERY__ (not case-sensitive) occurs in the HTML,
# do not add TOC
- $mw = MagicWord::get( MAG_NOGALLERY );
+ $mw = MagicWord::get( 'nogallery' );
$this->mOutput->mNoGallery = $mw->matchAndRemove( $text ) ;
}
@@ -3108,19 +3311,19 @@ class Parser
function stripToc( $text ) {
# if the string __NOTOC__ (not case-sensitive) occurs in the HTML,
# do not add TOC
- $mw = MagicWord::get( MAG_NOTOC );
+ $mw = MagicWord::get( 'notoc' );
if( $mw->matchAndRemove( $text ) ) {
$this->mShowToc = false;
}
-
- $mw = MagicWord::get( MAG_TOC );
+
+ $mw = MagicWord::get( 'toc' );
if( $mw->match( $text ) ) {
$this->mShowToc = true;
$this->mForceTocPosition = true;
-
+
// Set a placeholder. At the end we'll fill it in with the TOC.
$text = $mw->replace( '<!--MWTOC-->', $text, 1 );
-
+
// Only keep the first one.
$text = $mw->replace( '', $text );
}
@@ -3152,7 +3355,7 @@ class Parser
}
# Inhibit editsection links if requested in the page
- $esw =& MagicWord::get( MAG_NOEDITSECTION );
+ $esw =& MagicWord::get( 'noeditsection' );
if( $esw->matchAndRemove( $text ) ) {
$showEditLink = 0;
}
@@ -3168,13 +3371,13 @@ class Parser
# Allow user to stipulate that a page should have a "new section"
# link added via __NEWSECTIONLINK__
- $mw =& MagicWord::get( MAG_NEWSECTIONLINK );
+ $mw =& MagicWord::get( 'newsectionlink' );
if( $mw->matchAndRemove( $text ) )
$this->mOutput->setNewSection( true );
# if the string __FORCETOC__ (not case-sensitive) occurs in the HTML,
# override above conditions and always show TOC above first header
- $mw =& MagicWord::get( MAG_FORCETOC );
+ $mw =& MagicWord::get( 'forcetoc' );
if ($mw->matchAndRemove( $text ) ) {
$this->mShowToc = true;
$enoughToc = true;
@@ -3381,137 +3584,6 @@ class Parser
}
/**
- * Return an HTML link for the "ISBN 123456" text
- * @private
- */
- function magicISBN( $text ) {
- $fname = 'Parser::magicISBN';
- wfProfileIn( $fname );
-
- $a = split( 'ISBN ', ' '.$text );
- if ( count ( $a ) < 2 ) {
- wfProfileOut( $fname );
- return $text;
- }
- $text = substr( array_shift( $a ), 1);
- $valid = '0123456789-Xx';
-
- foreach ( $a as $x ) {
- # hack: don't replace inside thumbnail title/alt
- # attributes
- if(preg_match('/<[^>]+(alt|title)="[^">]*$/', $text)) {
- $text .= "ISBN $x";
- continue;
- }
-
- $isbn = $blank = '' ;
- while ( $x !== '' && ' ' == $x{0} ) {
- $blank .= ' ';
- $x = substr( $x, 1 );
- }
- if ( $x == '' ) { # blank isbn
- $text .= "ISBN $blank";
- continue;
- }
- while ( strstr( $valid, $x{0} ) != false ) {
- $isbn .= $x{0};
- $x = substr( $x, 1 );
- }
- $num = str_replace( '-', '', $isbn );
- $num = str_replace( ' ', '', $num );
- $num = str_replace( 'x', 'X', $num );
-
- if ( '' == $num ) {
- $text .= "ISBN $blank$x";
- } else {
- $titleObj = Title::makeTitle( NS_SPECIAL, 'Booksources' );
- $text .= '<a href="' .
- $titleObj->escapeLocalUrl( 'isbn='.$num ) .
- "\" class=\"internal\">ISBN $isbn</a>";
- $text .= $x;
- }
- }
- wfProfileOut( $fname );
- return $text;
- }
-
- /**
- * Return an HTML link for the "RFC 1234" text
- *
- * @private
- * @param string $text Text to be processed
- * @param string $keyword Magic keyword to use (default RFC)
- * @param string $urlmsg Interface message to use (default rfcurl)
- * @return string
- */
- function magicRFC( $text, $keyword='RFC ', $urlmsg='rfcurl' ) {
-
- $valid = '0123456789';
- $internal = false;
-
- $a = split( $keyword, ' '.$text );
- if ( count ( $a ) < 2 ) {
- return $text;
- }
- $text = substr( array_shift( $a ), 1);
-
- /* Check if keyword is preceed by [[.
- * This test is made here cause of the array_shift above
- * that prevent the test to be done in the foreach.
- */
- if ( substr( $text, -2 ) == '[[' ) {
- $internal = true;
- }
-
- foreach ( $a as $x ) {
- /* token might be empty if we have RFC RFC 1234 */
- if ( $x=='' ) {
- $text.=$keyword;
- continue;
- }
-
- # hack: don't replace inside thumbnail title/alt
- # attributes
- if(preg_match('/<[^>]+(alt|title)="[^">]*$/', $text)) {
- $text .= $keyword . $x;
- continue;
- }
-
- $id = $blank = '' ;
-
- /** remove and save whitespaces in $blank */
- while ( $x{0} == ' ' ) {
- $blank .= ' ';
- $x = substr( $x, 1 );
- }
-
- /** remove and save the rfc number in $id */
- while ( strstr( $valid, $x{0} ) != false ) {
- $id .= $x{0};
- $x = substr( $x, 1 );
- }
-
- if ( $id == '' ) {
- /* call back stripped spaces*/
- $text .= $keyword.$blank.$x;
- } elseif( $internal ) {
- /* normal link */
- $text .= $keyword.$id.$x;
- } else {
- /* build the external link*/
- $url = wfMsg( $urlmsg, $id);
- $sk =& $this->mOptions->getSkin();
- $la = $sk->getExternalLinkAttributes( $url, $keyword.$id );
- $text .= "<a href=\"{$url}\"{$la}>{$keyword}{$id}</a>{$x}";
- }
-
- /* Check if the next RFC keyword is preceed by [[ */
- $internal = ( substr($x,-2) == '[[' );
- }
- return $text;
- }
-
- /**
* Transform wiki markup when saving a page by doing \r\n -> \n
* conversion, substitting signatures, {{subst:}} templates, etc.
*
@@ -3526,7 +3598,7 @@ class Parser
function preSaveTransform( $text, &$title, &$user, $options, $clearState = true ) {
$this->mOptions = $options;
$this->mTitle =& $title;
- $this->mOutputType = OT_WIKI;
+ $this->setOutputType( OT_WIKI );
if ( $clearState ) {
$this->clearState();
@@ -3569,10 +3641,10 @@ class Parser
# Variable replacement
# Because mOutputType is OT_WIKI, this will only process {{subst:xxx}} type tags
$text = $this->replaceVariables( $text );
-
+
# Strip out <nowiki> etc. added via replaceVariables
$text = $this->strip( $text, $stripState, false, array( 'gallery' ) );
-
+
# Signatures
$sigText = $this->getUserSig( $user );
$text = strtr( $text, array(
@@ -3585,35 +3657,31 @@ class Parser
#
global $wgLegalTitleChars;
$tc = "[$wgLegalTitleChars]";
- $np = str_replace( array( '(', ')' ), array( '', '' ), $tc ); # No parens
+ $nc = '[ _0-9A-Za-z\x80-\xff]'; # Namespaces can use non-ascii!
- $namespacechar = '[ _0-9A-Za-z\x80-\xff]'; # Namespaces can use non-ascii!
- $conpat = "/^({$np}+) \\(({$tc}+)\\)$/";
+ $p1 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\))\\|]]/"; # [[ns:page (context)|]]
+ $p3 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\)|)(, $tc+|)\\|]]/"; # [[ns:page (context), context|]]
+ $p2 = "/\[\[\\|($tc+)]]/"; # [[|page]]
- $p1 = "/\[\[({$np}+) \\(({$np}+)\\)\\|]]/"; # [[page (context)|]]
- $p2 = "/\[\[\\|({$tc}+)]]/"; # [[|page]]
- $p3 = "/\[\[(:*$namespacechar+):({$np}+)\\|]]/"; # [[namespace:page|]] and [[:namespace:page|]]
- $p4 = "/\[\[(:*$namespacechar+):({$np}+) \\(({$np}+)\\)\\|]]/"; # [[ns:page (cont)|]] and [[:ns:page (cont)|]]
- $context = '';
- $t = $this->mTitle->getText();
- if ( preg_match( $conpat, $t, $m ) ) {
- $context = $m[2];
- }
- $text = preg_replace( $p4, '[[\\1:\\2 (\\3)|\\2]]', $text );
- $text = preg_replace( $p1, '[[\\1 (\\2)|\\1]]', $text );
- $text = preg_replace( $p3, '[[\\1:\\2|\\2]]', $text );
+ # try $p1 first, to turn "[[A, B (C)|]]" into "[[A, B (C)|A, B]]"
+ $text = preg_replace( $p1, '[[\\1\\2\\3|\\2]]', $text );
+ $text = preg_replace( $p3, '[[\\1\\2\\3\\4|\\2]]', $text );
- if ( '' == $context ) {
- $text = preg_replace( $p2, '[[\\1]]', $text );
+ $t = $this->mTitle->getText();
+ if ( preg_match( "/^($nc+:|)$tc+?( \\($tc+\\))$/", $t, $m ) ) {
+ $text = preg_replace( $p2, "[[$m[1]\\1$m[2]|\\1]]", $text );
+ } elseif ( preg_match( "/^($nc+:|)$tc+?(, $tc+|)$/", $t, $m ) && '' != "$m[1]$m[2]" ) {
+ $text = preg_replace( $p2, "[[$m[1]\\1$m[2]|\\1]]", $text );
} else {
- $text = preg_replace( $p2, "[[\\1 ({$context})|\\1]]", $text );
+ # if there's no context, don't bother duplicating the title
+ $text = preg_replace( $p2, '[[\\1]]', $text );
}
# Trim trailing whitespace
- # MAG_END (__END__) tag allows for trailing
+ # __END__ tag allows for trailing
# whitespace to be deliberately included
$text = rtrim( $text );
- $mw =& MagicWord::get( MAG_END );
+ $mw =& MagicWord::get( 'end' );
$mw->matchAndRemove( $text );
return $text;
@@ -3631,7 +3699,7 @@ class Parser
$username = $user->getName();
$nickname = $user->getOption( 'nickname' );
$nickname = $nickname === '' ? $username : $nickname;
-
+
if( $user->getBoolOption( 'fancysig' ) !== false ) {
# Sig. might contain markup; validate this
if( $this->validateSig( $nickname ) !== false ) {
@@ -3661,7 +3729,7 @@ class Parser
function validateSig( $text ) {
return( wfIsWellFormedXmlFragment( $text ) ? $text : false );
}
-
+
/**
* Clean up signature text
*
@@ -3675,16 +3743,16 @@ class Parser
function cleanSig( $text, $parsing = false ) {
global $wgTitle;
$this->startExternalParse( $wgTitle, new ParserOptions(), $parsing ? OT_WIKI : OT_MSG );
-
- $substWord = MagicWord::get( MAG_SUBST );
+
+ $substWord = MagicWord::get( 'subst' );
$substRegex = '/\{\{(?!(?:' . $substWord->getBaseRegex() . '))/x' . $substWord->getRegexCase();
$substText = '{{' . $substWord->getSynonym( 0 );
$text = preg_replace( $substRegex, $substText, $text );
$text = $this->cleanSigInSig( $text );
$text = $this->replaceVariables( $text );
-
- $this->clearState();
+
+ $this->clearState();
return $text;
}
@@ -3697,7 +3765,7 @@ class Parser
$text = preg_replace( '/~{3,5}/', '', $text );
return $text;
}
-
+
/**
* Set up some variables which are usually set up in parse()
* so that an external function can call some class members with confidence
@@ -3706,7 +3774,7 @@ class Parser
function startExternalParse( &$title, $options, $outputType, $clearState = true ) {
$this->mTitle =& $title;
$this->mOptions = $options;
- $this->mOutputType = $outputType;
+ $this->setOutputType( $outputType );
if ( $clearState ) {
$this->clearState();
}
@@ -3734,9 +3802,13 @@ class Parser
wfProfileIn($fname);
- $this->mTitle = $wgTitle;
+ if ( $wgTitle ) {
+ $this->mTitle = $wgTitle;
+ } else {
+ $this->mTitle = Title::newFromText('msg');
+ }
$this->mOptions = $options;
- $this->mOutputType = OT_MSG;
+ $this->setOutputType( OT_MSG );
$this->clearState();
$text = $this->replaceVariables( $text );
@@ -3785,7 +3857,7 @@ class Parser
*
* @public
*
- * @param mixed $id The magic word ID, or (deprecated) the function name. Function names are case-insensitive.
+ * @param string $id The magic word ID
* @param mixed $callback The callback function (and object) to use
* @param integer $flags a combination of the following flags:
* SFH_NO_HASH No leading hash, i.e. {{plural:...}} instead of {{#if:...}}
@@ -3793,21 +3865,16 @@ class Parser
* @return The old callback function for this name, if any
*/
function setFunctionHook( $id, $callback, $flags = 0 ) {
- if( is_string( $id ) ) {
- $id = strtolower( $id );
- }
$oldVal = @$this->mFunctionHooks[$id];
$this->mFunctionHooks[$id] = $callback;
# Add to function cache
- if ( is_int( $id ) ) {
- $mw = MagicWord::get( $id );
- $synonyms = $mw->getSynonyms();
- $sensitive = intval( $mw->isCaseSensitive() );
- } else {
- $synonyms = array( $id );
- $sensitive = 0;
- }
+ $mw = MagicWord::get( $id );
+ if( !$mw )
+ throw new MWException( 'Parser::setFunctionHook() expecting a magic word identifier.' );
+
+ $synonyms = $mw->getSynonyms();
+ $sensitive = intval( $mw->isCaseSensitive() );
foreach ( $synonyms as $syn ) {
# Case
@@ -3828,6 +3895,15 @@ class Parser
}
/**
+ * Get all registered function hook identifiers
+ *
+ * @return array
+ */
+ function getFunctionHooks() {
+ return array_keys( $this->mFunctionHooks );
+ }
+
+ /**
* Replace <!--LINK--> link placeholders with actual links, in the buffer
* Placeholders created in Skin::makeLinkObj()
* Returns an array of links found, indexed by PDBK:
@@ -3839,6 +3915,7 @@ class Parser
function replaceLinkHolders( &$text, $options = 0 ) {
global $wgUser;
global $wgOutputReplace;
+ global $wgContLang, $wgLanguageCode;
$fname = 'Parser::replaceLinkHolders';
wfProfileIn( $fname );
@@ -3929,6 +4006,91 @@ class Parser
}
wfProfileOut( $fname.'-check' );
+ # Do a second query for different language variants of links (if needed)
+ if($wgContLang->hasVariants()){
+ $linkBatch = new LinkBatch();
+ $variantMap = array(); // maps $pdbkey_Variant => $pdbkey_original
+
+ // Add variants of links to link batch
+ foreach ( $this->mLinkHolders['namespaces'] as $key => $ns ) {
+ $title = $this->mLinkHolders['titles'][$key];
+ if ( is_null( $title ) )
+ continue;
+
+ $pdbk = $title->getPrefixedDBkey();
+
+ // generate all variants of the link title text
+ $allTextVariants = $wgContLang->convertLinkToAllVariants($title->getText());
+
+ // if link was not found (in first query), add all variants to query
+ if ( !isset($colours[$pdbk]) ){
+ foreach($allTextVariants as $textVariant){
+ $variantTitle = Title::makeTitle( $ns, $textVariant );
+ if(is_null($variantTitle)) continue;
+ $linkBatch->addObj( $variantTitle );
+ $variantMap[$variantTitle->getPrefixedDBkey()][] = $key;
+ }
+ }
+ }
+
+
+ if(!$linkBatch->isEmpty()){
+ // construct query
+ $titleClause = $linkBatch->constructSet('page', $dbr);
+
+ $variantQuery = "SELECT page_id, page_namespace, page_title";
+ if ( $threshold > 0 ) {
+ $variantQuery .= ', page_len, page_is_redirect';
+ }
+
+ $variantQuery .= " FROM $page WHERE $titleClause";
+ if ( $options & RLH_FOR_UPDATE ) {
+ $variantQuery .= ' FOR UPDATE';
+ }
+
+ $varRes = $dbr->query( $variantQuery, $fname );
+
+ // for each found variants, figure out link holders and replace
+ while ( $s = $dbr->fetchObject($varRes) ) {
+
+ $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title );
+ $varPdbk = $variantTitle->getPrefixedDBkey();
+ $linkCache->addGoodLinkObj( $s->page_id, $variantTitle );
+ $this->mOutput->addLink( $variantTitle, $s->page_id );
+
+ $holderKeys = $variantMap[$varPdbk];
+
+ // loop over link holders
+ foreach($holderKeys as $key){
+ $title = $this->mLinkHolders['titles'][$key];
+ if ( is_null( $title ) ) continue;
+
+ $pdbk = $title->getPrefixedDBkey();
+
+ if(!isset($colours[$pdbk])){
+ // found link in some of the variants, replace the link holder data
+ $this->mLinkHolders['titles'][$key] = $variantTitle;
+ $this->mLinkHolders['dbkeys'][$key] = $variantTitle->getDBkey();
+
+ // set pdbk and colour
+ $pdbks[$key] = $varPdbk;
+ if ( $threshold > 0 ) {
+ $size = $s->page_len;
+ if ( $s->page_is_redirect || $s->page_namespace != 0 || $size >= $threshold ) {
+ $colours[$varPdbk] = 1;
+ } else {
+ $colours[$varPdbk] = 2;
+ }
+ }
+ else {
+ $colours[$varPdbk] = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+
# Construct search and replace arrays
wfProfileIn( $fname.'-construct' );
$wgOutputReplace = array();
@@ -4033,13 +4195,13 @@ class Parser
function renderPreTag( $text, $attribs, $parser ) {
// Backwards-compatibility hack
$content = preg_replace( '!<nowiki>(.*?)</nowiki>!is', '\\1', $text );
-
+
$attribs = Sanitizer::validateTagAttributes( $attribs, 'pre' );
return wfOpenElement( 'pre', $attribs ) .
wfEscapeHTMLTagsOnly( $content ) .
'</pre>';
}
-
+
/**
* Renders an image gallery from a text with one line per image.
* text labels may be given by using |-style alternative text. E.g.
@@ -4058,7 +4220,7 @@ class Parser
if( isset( $params['caption'] ) )
$ig->setCaption( $params['caption'] );
-
+
$lines = explode( "\n", $text );
foreach ( $lines as $line ) {
# match lines like these:
@@ -4068,7 +4230,8 @@ class Parser
if ( count( $matches ) == 0 ) {
continue;
}
- $nt =& Title::newFromText( $matches[1] );
+ $tp = Title::newFromText( $matches[1] );
+ $nt =& $tp;
if( is_null( $nt ) ) {
# Bogus title. Ignore these so we don't bomb out later.
continue;
@@ -4101,7 +4264,7 @@ class Parser
* Parse image options text and use it to make an image
*/
function makeImage( &$nt, $options ) {
- global $wgUseImageResize;
+ global $wgUseImageResize, $wgDjvuRenderer;
$align = '';
@@ -4117,17 +4280,19 @@ class Parser
$part = explode( '|', $options);
- $mwThumb =& MagicWord::get( MAG_IMG_THUMBNAIL );
- $mwManualThumb =& MagicWord::get( MAG_IMG_MANUALTHUMB );
- $mwLeft =& MagicWord::get( MAG_IMG_LEFT );
- $mwRight =& MagicWord::get( MAG_IMG_RIGHT );
- $mwNone =& MagicWord::get( MAG_IMG_NONE );
- $mwWidth =& MagicWord::get( MAG_IMG_WIDTH );
- $mwCenter =& MagicWord::get( MAG_IMG_CENTER );
- $mwFramed =& MagicWord::get( MAG_IMG_FRAMED );
+ $mwThumb =& MagicWord::get( 'img_thumbnail' );
+ $mwManualThumb =& MagicWord::get( 'img_manualthumb' );
+ $mwLeft =& MagicWord::get( 'img_left' );
+ $mwRight =& MagicWord::get( 'img_right' );
+ $mwNone =& MagicWord::get( 'img_none' );
+ $mwWidth =& MagicWord::get( 'img_width' );
+ $mwCenter =& MagicWord::get( 'img_center' );
+ $mwFramed =& MagicWord::get( 'img_framed' );
+ $mwPage =& MagicWord::get( 'img_page' );
$caption = '';
$width = $height = $framed = $thumb = false;
+ $page = null;
$manual_thumb = '' ;
foreach( $part as $key => $val ) {
@@ -4149,8 +4314,12 @@ class Parser
} elseif ( ! is_null( $mwNone->matchVariableStartToEnd($val) ) ) {
# remember to set an alignment, don't render immediately
$align = 'none';
+ } elseif ( isset( $wgDjvuRenderer ) && $wgDjvuRenderer
+ && ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
+ # Select a page in a multipage document
+ $page = $match;
} elseif ( $wgUseImageResize && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
- wfDebug( "MAG_IMG_WIDTH match: $match\n" );
+ wfDebug( "img_width match: $match\n" );
# $match is the image width in pixels
if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
$width = intval( $m[1] );
@@ -4175,7 +4344,7 @@ class Parser
# Linker does the rest
$sk =& $this->mOptions->getSkin();
- return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb );
+ return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb, $page );
}
/**
@@ -4242,15 +4411,15 @@ class Parser
# strip NOWIKI etc. to avoid confusion (true-parameter causes HTML
# comments to be stripped as well)
$striparray = array();
-
+
$oldOutputType = $this->mOutputType;
$oldOptions = $this->mOptions;
$this->mOptions = new ParserOptions();
- $this->mOutputType = OT_WIKI;
-
+ $this->setOutputType( OT_WIKI );
+
$striptext = $this->strip( $text, $striparray, true );
-
- $this->mOutputType = $oldOutputType;
+
+ $this->setOutputType( $oldOutputType );
$this->mOptions = $oldOptions;
# now that we can be sure that no pseudo-sections are in the source,
@@ -4293,7 +4462,7 @@ class Parser
/mix",
$striptext, -1,
PREG_SPLIT_DELIM_CAPTURE);
-
+
if( $mode == "get" ) {
if( $section == 0 ) {
// "Section 0" returns the content before any other section.
@@ -4360,7 +4529,7 @@ class Parser
$rv = trim( $rv );
return $rv;
}
-
+
/**
* This function returns the text of a section, specified by a number ($section).
* A section is text under a heading like == Heading == or \<h1\>Heading\</h1\>, or
@@ -4375,7 +4544,7 @@ class Parser
function getSection( $text, $section ) {
return $this->extractSections( $text, $section, "get" );
}
-
+
function replaceSection( $oldtext, $section, $text ) {
return $this->extractSections( $oldtext, $section, "replace", $text );
}
@@ -4435,6 +4604,7 @@ class ParserOutput
function &getImages() { return $this->mImages; }
function &getExternalLinks() { return $this->mExternalLinks; }
function getNoGallery() { return $this->mNoGallery; }
+ function getSubtitle() { return $this->mSubtitle; }
function containsOldMagic() { return $this->mContainsOldMagic; }
function setText( $text ) { return wfSetVar( $this->mText, $text ); }
@@ -4442,7 +4612,8 @@ class ParserOutput
function setCategoryLinks( $cl ) { return wfSetVar( $this->mCategories, $cl ); }
function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
- function setTitleText( $t ) { return wfSetVar ($this->mTitleText, $t); }
+ function setTitleText( $t ) { return wfSetVar($this->mTitleText, $t); }
+ function setSubtitle( $st ) { return wfSetVar( $this->mSubtitle, $st ); }
function addCategory( $c, $sort ) { $this->mCategories[$c] = $sort; }
function addImage( $name ) { $this->mImages[$name] = 1; }
@@ -4456,12 +4627,15 @@ class ParserOutput
return (bool)$this->mNewSection;
}
- function addLink( $title, $id ) {
+ function addLink( $title, $id = null ) {
$ns = $title->getNamespace();
$dbk = $title->getDBkey();
if ( !isset( $this->mLinks[$ns] ) ) {
$this->mLinks[$ns] = array();
}
+ if ( is_null( $id ) ) {
+ $id = $title->getArticleID();
+ }
$this->mLinks[$ns][$dbk] = $id;
}
@@ -4513,6 +4687,8 @@ class ParserOptions
var $mAllowSpecialInclusion; # Allow inclusion of special pages
var $mTidy; # Ask for tidy cleanup
var $mInterfaceMessage; # Which lang to call for PLURAL and GRAMMAR
+ var $mMaxIncludeSize; # Maximum size of template expansions, in bytes
+ var $mRemoveComments; # Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
var $mUser; # Stored user object, just used to initialise the skin
@@ -4521,12 +4697,13 @@ class ParserOptions
function getInterwikiMagic() { return $this->mInterwikiMagic; }
function getAllowExternalImages() { return $this->mAllowExternalImages; }
function getAllowExternalImagesFrom() { return $this->mAllowExternalImagesFrom; }
- function getDateFormat() { return $this->mDateFormat; }
function getEditSection() { return $this->mEditSection; }
function getNumberHeadings() { return $this->mNumberHeadings; }
function getAllowSpecialInclusion() { return $this->mAllowSpecialInclusion; }
function getTidy() { return $this->mTidy; }
function getInterfaceMessage() { return $this->mInterfaceMessage; }
+ function getMaxIncludeSize() { return $this->mMaxIncludeSize; }
+ function getRemoveComments() { return $this->mRemoveComments; }
function &getSkin() {
if ( !isset( $this->mSkin ) ) {
@@ -4535,6 +4712,13 @@ class ParserOptions
return $this->mSkin;
}
+ function getDateFormat() {
+ if ( !isset( $this->mDateFormat ) ) {
+ $this->mDateFormat = $this->mUser->getDatePreference();
+ }
+ return $this->mDateFormat;
+ }
+
function setUseTeX( $x ) { return wfSetVar( $this->mUseTeX, $x ); }
function setUseDynamicDates( $x ) { return wfSetVar( $this->mUseDynamicDates, $x ); }
function setInterwikiMagic( $x ) { return wfSetVar( $this->mInterwikiMagic, $x ); }
@@ -4547,6 +4731,8 @@ class ParserOptions
function setTidy( $x ) { return wfSetVar( $this->mTidy, $x); }
function setSkin( &$x ) { $this->mSkin =& $x; }
function setInterfaceMessage( $x ) { return wfSetVar( $this->mInterfaceMessage, $x); }
+ function setMaxIncludeSize( $x ) { return wfSetVar( $this->mMaxIncludeSize, $x ); }
+ function setRemoveComments( $x ) { return wfSetVar( $this->mRemoveComments, $x ); }
function ParserOptions( $user = null ) {
$this->initialiseFromUser( $user );
@@ -4556,14 +4742,14 @@ class ParserOptions
* Get parser options
* @static
*/
- function newFromUser( &$user ) {
+ static function newFromUser( $user ) {
return new ParserOptions( $user );
}
/** Get user options */
- function initialiseFromUser( &$userInput ) {
+ function initialiseFromUser( $userInput ) {
global $wgUseTeX, $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages;
- global $wgAllowExternalImagesFrom, $wgAllowSpecialInclusion;
+ global $wgAllowExternalImagesFrom, $wgAllowSpecialInclusion, $wgMaxArticleSize;
$fname = 'ParserOptions::initialiseFromUser';
wfProfileIn( $fname );
if ( !$userInput ) {
@@ -4586,12 +4772,14 @@ class ParserOptions
$this->mAllowExternalImages = $wgAllowExternalImages;
$this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
$this->mSkin = null; # Deferred
- $this->mDateFormat = $user->getOption( 'date' );
+ $this->mDateFormat = null; # Deferred
$this->mEditSection = true;
$this->mNumberHeadings = $user->getOption( 'numberheadings' );
$this->mAllowSpecialInclusion = $wgAllowSpecialInclusion;
$this->mTidy = false;
$this->mInterfaceMessage = false;
+ $this->mMaxIncludeSize = $wgMaxArticleSize * 1024;
+ $this->mRemoveComments = true;
wfProfileOut( $fname );
}
}
@@ -4711,6 +4899,26 @@ function wfLoadSiteStats() {
}
/**
+ * Get revision timestamp from the database considering timecorrection
+ *
+ * @param $id Int: page revision id
+ * @return integer
+ */
+function wfRevisionTimestamp( $id ) {
+ global $wgContLang;
+ $fname = 'wfRevisionTimestamp';
+
+ wfProfileIn( $fname );
+ $dbr =& wfGetDB( DB_SLAVE );
+ $timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
+ array( 'rev_id' => $id ), __METHOD__ );
+ $timestamp = $wgContLang->userAdjust( $timestamp );
+ wfProfileOut( $fname );
+
+ return $timestamp;
+}
+
+/**
* Escape html tags
* Basically replacing " > and < with HTML entities ( &quot;, &gt;, &lt;)
*
diff --git a/includes/ParserCache.php b/includes/ParserCache.php
index 3ec7512f..1f2e2aaf 100644
--- a/includes/ParserCache.php
+++ b/includes/ParserCache.php
@@ -13,7 +13,7 @@ class ParserCache {
/**
* Get an instance of this object
*/
- function &singleton() {
+ public static function &singleton() {
static $instance;
if ( !isset( $instance ) ) {
global $parserMemc;
@@ -33,7 +33,7 @@ class ParserCache {
}
function getKey( &$article, &$user ) {
- global $wgDBname, $action;
+ global $action;
$hash = $user->getPageRenderingHash();
if( !$article->mTitle->userCanEdit() ) {
// section edit links are suppressed even if the user has them on
@@ -43,7 +43,7 @@ class ParserCache {
}
$pageid = intval( $article->getID() );
$renderkey = (int)($action == 'render');
- $key = "$wgDBname:pcache:idhash:$pageid-$renderkey!$hash$edit";
+ $key = wfMemcKey( 'pcache', 'idhash', "$pageid-$renderkey!$hash$edit" );
return $key;
}
diff --git a/includes/Profiler.php b/includes/Profiler.php
new file mode 100644
index 00000000..78003e02
--- /dev/null
+++ b/includes/Profiler.php
@@ -0,0 +1,369 @@
+<?php
+/**
+ * This file is only included if profiling is enabled
+ * @package MediaWiki
+ */
+
+$wgProfiling = true;
+
+/**
+ * @param $functioname name of the function we will profile
+ */
+function wfProfileIn($functionname) {
+ global $wgProfiler;
+ $wgProfiler->profileIn($functionname);
+}
+
+/**
+ * @param $functioname name of the function we have profiled
+ */
+function wfProfileOut($functionname = 'missing') {
+ global $wgProfiler;
+ $wgProfiler->profileOut($functionname);
+}
+
+function wfGetProfilingOutput($start, $elapsed) {
+ global $wgProfiler;
+ return $wgProfiler->getOutput($start, $elapsed);
+}
+
+function wfProfileClose() {
+ global $wgProfiler;
+ $wgProfiler->close();
+}
+
+if (!function_exists('memory_get_usage')) {
+ # Old PHP or --enable-memory-limit not compiled in
+ function memory_get_usage() {
+ return 0;
+ }
+}
+
+/**
+ * @todo document
+ * @package MediaWiki
+ */
+class Profiler {
+ var $mStack = array (), $mWorkStack = array (), $mCollated = array ();
+ var $mCalls = array (), $mTotals = array ();
+
+ function Profiler()
+ {
+ // Push an entry for the pre-profile setup time onto the stack
+ global $wgRequestTime;
+ if ( !empty( $wgRequestTime ) ) {
+ $this->mWorkStack[] = array( '-total', 0, $wgRequestTime, 0 );
+ $this->mStack[] = array( '-setup', 1, $wgRequestTime, 0, microtime(true), 0 );
+ } else {
+ $this->profileIn( '-total' );
+ }
+
+ }
+
+ function profileIn($functionname) {
+ global $wgDebugFunctionEntry;
+ if ($wgDebugFunctionEntry && function_exists('wfDebug')) {
+ wfDebug(str_repeat(' ', count($this->mWorkStack)).'Entering '.$functionname."\n");
+ }
+ $this->mWorkStack[] = array($functionname, count( $this->mWorkStack ), $this->getTime(), memory_get_usage());
+ }
+
+ function profileOut($functionname) {
+ $memory = memory_get_usage();
+ $time = $this->getTime();
+
+ global $wgDebugFunctionEntry;
+
+ if ($wgDebugFunctionEntry && function_exists('wfDebug')) {
+ wfDebug(str_repeat(' ', count($this->mWorkStack) - 1).'Exiting '.$functionname."\n");
+ }
+
+ $bit = array_pop($this->mWorkStack);
+
+ if (!$bit) {
+ wfDebug("Profiling error, !\$bit: $functionname\n");
+ } else {
+ //if ($wgDebugProfiling) {
+ if ($functionname == 'close') {
+ $message = "Profile section ended by close(): {$bit[0]}";
+ wfDebug( "$message\n" );
+ $this->mStack[] = array( $message, 0, '0 0', 0, '0 0', 0 );
+ }
+ elseif ($bit[0] != $functionname) {
+ $message = "Profiling error: in({$bit[0]}), out($functionname)";
+ wfDebug( "$message\n" );
+ $this->mStack[] = array( $message, 0, '0 0', 0, '0 0', 0 );
+ }
+ //}
+ $bit[] = $time;
+ $bit[] = $memory;
+ $this->mStack[] = $bit;
+ }
+ }
+
+ function close() {
+ while (count($this->mWorkStack)) {
+ $this->profileOut('close');
+ }
+ }
+
+ function getOutput() {
+ global $wgDebugFunctionEntry;
+ $wgDebugFunctionEntry = false;
+
+ if (!count($this->mStack) && !count($this->mCollated)) {
+ return "No profiling output\n";
+ }
+ $this->close();
+
+ global $wgProfileCallTree;
+ if ($wgProfileCallTree) {
+ return $this->getCallTree();
+ } else {
+ return $this->getFunctionReport();
+ }
+ }
+
+ function getCallTree($start = 0) {
+ return implode('', array_map(array (& $this, 'getCallTreeLine'), $this->remapCallTree($this->mStack)));
+ }
+
+ function remapCallTree($stack) {
+ if (count($stack) < 2) {
+ return $stack;
+ }
+ $outputs = array ();
+ for ($max = count($stack) - 1; $max > 0;) {
+ /* Find all items under this entry */
+ $level = $stack[$max][1];
+ $working = array ();
+ for ($i = $max -1; $i >= 0; $i --) {
+ if ($stack[$i][1] > $level) {
+ $working[] = $stack[$i];
+ } else {
+ break;
+ }
+ }
+ $working = $this->remapCallTree(array_reverse($working));
+ $output = array ();
+ foreach ($working as $item) {
+ array_push($output, $item);
+ }
+ array_unshift($output, $stack[$max]);
+ $max = $i;
+
+ array_unshift($outputs, $output);
+ }
+ $final = array ();
+ foreach ($outputs as $output) {
+ foreach ($output as $item) {
+ $final[] = $item;
+ }
+ }
+ return $final;
+ }
+
+ function getCallTreeLine($entry) {
+ list ($fname, $level, $start, $x, $end) = $entry;
+ $delta = $end - $start;
+ $space = str_repeat(' ', $level);
+
+ # The ugly double sprintf is to work around a PHP bug,
+ # which has been fixed in recent releases.
+ return sprintf( "%10s %s %s\n",
+ trim( sprintf( "%7.3f", $delta * 1000.0 ) ),
+ $space, $fname );
+ }
+
+ function getTime() {
+ return microtime(true);
+ #return $this->getUserTime();
+ }
+
+ function getUserTime() {
+ $ru = getrusage();
+ return $ru['ru_utime.tv_sec'].' '.$ru['ru_utime.tv_usec'] / 1e6;
+ }
+
+ function getFunctionReport() {
+ $width = 140;
+ $nameWidth = $width - 65;
+ $format = "%-{$nameWidth}s %6d %13.3f %13.3f %13.3f%% %9d (%13.3f -%13.3f) [%d]\n";
+ $titleFormat = "%-{$nameWidth}s %6s %13s %13s %13s %9s\n";
+ $prof = "\nProfiling data\n";
+ $prof .= sprintf($titleFormat, 'Name', 'Calls', 'Total', 'Each', '%', 'Mem');
+ $this->mCollated = array ();
+ $this->mCalls = array ();
+ $this->mMemory = array ();
+
+ # Estimate profiling overhead
+ $profileCount = count($this->mStack);
+ wfProfileIn('-overhead-total');
+ for ($i = 0; $i < $profileCount; $i ++) {
+ wfProfileIn('-overhead-internal');
+ wfProfileOut('-overhead-internal');
+ }
+ wfProfileOut('-overhead-total');
+
+ # First, subtract the overhead!
+ foreach ($this->mStack as $entry) {
+ $fname = $entry[0];
+ $thislevel = $entry[1];
+ $start = $entry[2];
+ $end = $entry[4];
+ $elapsed = $end - $start;
+ $memory = $entry[5] - $entry[3];
+
+ if ($fname == '-overhead-total') {
+ $overheadTotal[] = $elapsed;
+ $overheadMemory[] = $memory;
+ }
+ elseif ($fname == '-overhead-internal') {
+ $overheadInternal[] = $elapsed;
+ }
+ }
+ $overheadTotal = array_sum($overheadTotal) / count($overheadInternal);
+ $overheadMemory = array_sum($overheadMemory) / count($overheadInternal);
+ $overheadInternal = array_sum($overheadInternal) / count($overheadInternal);
+
+ # Collate
+ foreach ($this->mStack as $index => $entry) {
+ $fname = $entry[0];
+ $thislevel = $entry[1];
+ $start = $entry[2];
+ $end = $entry[4];
+ $elapsed = $end - $start;
+
+ $memory = $entry[5] - $entry[3];
+ $subcalls = $this->calltreeCount($this->mStack, $index);
+
+ if (!preg_match('/^-overhead/', $fname)) {
+ # Adjust for profiling overhead (except special values with elapsed=0
+ if ( $elapsed ) {
+ $elapsed -= $overheadInternal;
+ $elapsed -= ($subcalls * $overheadTotal);
+ $memory -= ($subcalls * $overheadMemory);
+ }
+ }
+
+ if (!array_key_exists($fname, $this->mCollated)) {
+ $this->mCollated[$fname] = 0;
+ $this->mCalls[$fname] = 0;
+ $this->mMemory[$fname] = 0;
+ $this->mMin[$fname] = 1 << 24;
+ $this->mMax[$fname] = 0;
+ $this->mOverhead[$fname] = 0;
+ }
+
+ $this->mCollated[$fname] += $elapsed;
+ $this->mCalls[$fname]++;
+ $this->mMemory[$fname] += $memory;
+ $this->mMin[$fname] = min($this->mMin[$fname], $elapsed);
+ $this->mMax[$fname] = max($this->mMax[$fname], $elapsed);
+ $this->mOverhead[$fname] += $subcalls;
+ }
+
+ $total = @ $this->mCollated['-total'];
+ $this->mCalls['-overhead-total'] = $profileCount;
+
+ # Output
+ arsort($this->mCollated, SORT_NUMERIC);
+ foreach ($this->mCollated as $fname => $elapsed) {
+ $calls = $this->mCalls[$fname];
+ $percent = $total ? 100. * $elapsed / $total : 0;
+ $memory = $this->mMemory[$fname];
+ $prof .= sprintf($format, substr($fname, 0, $nameWidth), $calls, (float) ($elapsed * 1000), (float) ($elapsed * 1000) / $calls, $percent, $memory, ($this->mMin[$fname] * 1000.0), ($this->mMax[$fname] * 1000.0), $this->mOverhead[$fname]);
+
+ global $wgProfileToDatabase;
+ if ($wgProfileToDatabase) {
+ Profiler :: logToDB($fname, (float) ($elapsed * 1000), $calls);
+ }
+ }
+ $prof .= "\nTotal: $total\n\n";
+
+ return $prof;
+ }
+
+ /**
+ * Counts the number of profiled function calls sitting under
+ * the given point in the call graph. Not the most efficient algo.
+ *
+ * @param $stack Array:
+ * @param $start Integer:
+ * @return Integer
+ * @private
+ */
+ function calltreeCount(& $stack, $start) {
+ $level = $stack[$start][1];
+ $count = 0;
+ for ($i = $start -1; $i >= 0 && $stack[$i][1] > $level; $i --) {
+ $count ++;
+ }
+ return $count;
+ }
+
+ /**
+ * @static
+ */
+ function logToDB($name, $timeSum, $eventCount) {
+ # Warning: $wguname is a live patch, it should be moved to Setup.php
+ global $wguname, $wgProfilePerHost;
+
+ $fname = 'Profiler::logToDB';
+ $dbw = & wfGetDB(DB_MASTER);
+ if (!is_object($dbw))
+ return false;
+ $errorState = $dbw->ignoreErrors( true );
+ $profiling = $dbw->tableName('profiling');
+
+ $name = substr($name, 0, 255);
+ $encname = $dbw->strencode($name);
+
+ if ($wgProfilePerHost) {
+ $pfhost = $wguname['nodename'];
+ } else {
+ $pfhost = '';
+ }
+
+ $sql = "UPDATE $profiling "."SET pf_count=pf_count+{$eventCount}, "."pf_time=pf_time + {$timeSum} ".
+ "WHERE pf_name='{$encname}' AND pf_server='{$pfhost}'";
+ $dbw->query($sql);
+
+ $rc = $dbw->affectedRows();
+ if ($rc == 0) {
+ $dbw->insert('profiling', array ('pf_name' => $name, 'pf_count' => $eventCount,
+ 'pf_time' => $timeSum, 'pf_server' => $pfhost ), $fname, array ('IGNORE'));
+ }
+ // When we upgrade to mysql 4.1, the insert+update
+ // can be merged into just a insert with this construct added:
+ // "ON DUPLICATE KEY UPDATE ".
+ // "pf_count=pf_count + VALUES(pf_count), ".
+ // "pf_time=pf_time + VALUES(pf_time)";
+ $dbw->ignoreErrors( $errorState );
+ }
+
+ /**
+ * Get the function name of the current profiling section
+ */
+ function getCurrentSection() {
+ $elt = end($this->mWorkStack);
+ return $elt[0];
+ }
+
+ static function getCaller( $level ) {
+ $backtrace = debug_backtrace();
+ if ( isset( $backtrace[$level] ) ) {
+ if ( isset( $backtrace[$level]['class'] ) ) {
+ $caller = $backtrace[$level]['class'] . '::' . $backtrace[$level]['function'];
+ } else {
+ $caller = $backtrace[$level]['function'];
+ }
+ } else {
+ $caller = 'unknown';
+ }
+ return $caller;
+ }
+
+}
+
+?>
diff --git a/includes/ProfilerSimple.php b/includes/ProfilerSimple.php
index ed058c65..d5bdaf94 100644
--- a/includes/ProfilerSimple.php
+++ b/includes/ProfilerSimple.php
@@ -8,9 +8,11 @@
* @todo document
* @package MediaWiki
*/
-require_once(dirname(__FILE__).'/Profiling.php');
+require_once(dirname(__FILE__).'/Profiler.php');
class ProfilerSimple extends Profiler {
+ var $mMinimumTime = 0;
+
function ProfilerSimple() {
global $wgRequestTime,$wgRUstart;
if (!empty($wgRequestTime) && !empty($wgRUstart)) {
@@ -33,6 +35,10 @@ class ProfilerSimple extends Profiler {
}
}
+ function setMinimum( $min ) {
+ $this->mMinimumTime = $min;
+ }
+
function profileIn($functionname) {
global $wgDebugFunctionEntry;
if ($wgDebugFunctionEntry) {
@@ -86,9 +92,14 @@ class ProfilerSimple extends Profiler {
}
function getCpuTime($ru=null) {
- if ($ru==null)
- $ru=getrusage();
- return ($ru['ru_utime.tv_sec']+$ru['ru_stime.tv_sec']+($ru['ru_utime.tv_usec']+$ru['ru_stime.tv_usec'])*1e-6);
+ if ( function_exists( 'getrusage' ) ) {
+ if ( $ru == null )
+ $ru = getrusage();
+ return ($ru['ru_utime.tv_sec'] + $ru['ru_stime.tv_sec'] + ($ru['ru_utime.tv_usec'] +
+ $ru['ru_stime.tv_usec']) * 1e-6);
+ } else {
+ return 0;
+ }
}
/* If argument is passed, it assumes that it is dual-format time string, returns proper float time value */
diff --git a/includes/ProfilerSimpleUDP.php b/includes/ProfilerSimpleUDP.php
index c395228b..e0490512 100644
--- a/includes/ProfilerSimpleUDP.php
+++ b/includes/ProfilerSimpleUDP.php
@@ -3,20 +3,25 @@
(the one from wikipedia/udpprofile CVS )
*/
-require_once(dirname(__FILE__).'/Profiling.php');
+require_once(dirname(__FILE__).'/Profiler.php');
require_once(dirname(__FILE__).'/ProfilerSimple.php');
class ProfilerSimpleUDP extends ProfilerSimple {
function getFunctionReport() {
global $wgUDPProfilerHost;
global $wgUDPProfilerPort;
- global $wgDBname;
+ if ( $this->mCollated['-total']['real'] < $this->mMinimumTime ) {
+ # Less than minimum, ignore
+ return;
+ }
+
+
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$plength=0;
$packet="";
foreach ($this->mCollated as $entry=>$pfdata) {
- $pfline=sprintf ("%s %s %d %f %f %f %f %s\n", $wgDBname,"-",$pfdata['count'],
+ $pfline=sprintf ("%s %s %d %f %f %f %f %s\n", wfWikiID(),"-",$pfdata['count'],
$pfdata['cpu'],$pfdata['cpu_sq'],$pfdata['real'],$pfdata['real_sq'],$entry);
$length=strlen($pfline);
/* printf("<!-- $pfline -->"); */
diff --git a/includes/ProfilerStub.php b/includes/ProfilerStub.php
index 3bcdaab2..4cf0aa44 100644
--- a/includes/ProfilerStub.php
+++ b/includes/ProfilerStub.php
@@ -21,6 +21,6 @@ function wfProfileOut( $fn = '' ) {
}
function wfGetProfilingOutput( $s, $e ) {}
function wfProfileClose() {}
-function wfLogProfilingData() {}
+$wgProfiling = false;
?>
diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php
index 2a40a376..fd1bc81e 100644
--- a/includes/ProtectionForm.php
+++ b/includes/ProtectionForm.php
@@ -79,7 +79,7 @@ class ProtectionForm {
$wgOut->addWikiText(
wfMsg( $this->disabled ? "protect-viewtext" : "protect-text",
- $this->mTitle->getPrefixedText() ) );
+ wfEscapeWikiText( $this->mTitle->getPrefixedText() ) ) );
$wgOut->addHTML( $this->buildForm() );
@@ -230,7 +230,6 @@ class ProtectionForm {
function showLogExtract( &$out ) {
# Show relevant lines from the deletion log:
$out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'protect' ) ) . "</h2>\n" );
- require_once( 'SpecialLog.php' );
$logViewer = new LogViewer(
new LogReader(
new FauxRequest(
diff --git a/includes/ProxyTools.php b/includes/ProxyTools.php
index bed79c10..7974c882 100644
--- a/includes/ProxyTools.php
+++ b/includes/ProxyTools.php
@@ -55,7 +55,7 @@ function wfGetIP() {
# Set $ip to the IP address given by that trusted server, unless the address is not sensible (e.g. private)
foreach ( $ipchain as $i => $curIP ) {
if ( array_key_exists( $curIP, $trustedProxies ) ) {
- if ( isset( $ipchain[$i + 1] ) && wfIsIPPublic( $ipchain[$i + 1] ) ) {
+ if ( isset( $ipchain[$i + 1] ) && IP::isPublic( $ipchain[$i + 1] ) ) {
$ip = $ipchain[$i + 1];
}
} else {
@@ -70,74 +70,12 @@ function wfGetIP() {
}
/**
- * Given an IP address in dotted-quad notation, returns an unsigned integer.
- * Like ip2long() except that it actually works and has a consistent error return value.
- */
-function wfIP2Unsigned( $ip ) {
- $n = ip2long( $ip );
- if ( $n == -1 || $n === false ) { # Return value on error depends on PHP version
- $n = false;
- } elseif ( $n < 0 ) {
- $n += pow( 2, 32 );
- }
- return $n;
-}
-
-/**
- * Return a zero-padded hexadecimal representation of an IP address
- */
-function wfIP2Hex( $ip ) {
- $n = wfIP2Unsigned( $ip );
- if ( $n !== false ) {
- $n = sprintf( '%08X', $n );
- }
- return $n;
-}
-
-/**
- * Determine if an IP address really is an IP address, and if it is public,
- * i.e. not RFC 1918 or similar
- */
-function wfIsIPPublic( $ip ) {
- $n = wfIP2Unsigned( $ip );
- if ( !$n ) {
- return false;
- }
-
- // ip2long accepts incomplete addresses, as well as some addresses
- // followed by garbage characters. Check that it's really valid.
- if( $ip != long2ip( $n ) ) {
- return false;
- }
-
- static $privateRanges = false;
- if ( !$privateRanges ) {
- $privateRanges = array(
- array( '10.0.0.0', '10.255.255.255' ), # RFC 1918 (private)
- array( '172.16.0.0', '172.31.255.255' ), # "
- array( '192.168.0.0', '192.168.255.255' ), # "
- array( '0.0.0.0', '0.255.255.255' ), # this network
- array( '127.0.0.0', '127.255.255.255' ), # loopback
- );
- }
-
- foreach ( $privateRanges as $r ) {
- $start = wfIP2Unsigned( $r[0] );
- $end = wfIP2Unsigned( $r[1] );
- if ( $n >= $start && $n <= $end ) {
- return false;
- }
- }
- return true;
-}
-
-/**
* Forks processes to scan the originating IP for an open proxy server
* MemCached can be used to skip IPs that have already been scanned
*/
function wfProxyCheck() {
global $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath;
- global $wgUseMemCached, $wgMemc, $wgDBname, $wgProxyMemcExpiry;
+ global $wgUseMemCached, $wgMemc, $wgProxyMemcExpiry;
global $wgProxyKey;
if ( !$wgBlockOpenProxies ) {
@@ -149,7 +87,7 @@ function wfProxyCheck() {
# Get MemCached key
$skip = false;
if ( $wgUseMemCached ) {
- $mcKey = "$wgDBname:proxy:ip:$ip";
+ $mcKey = wfMemcKey( 'proxy', 'ip', $ip );
$mcValue = $wgMemc->get( $mcKey );
if ( $mcValue ) {
$skip = true;
@@ -182,18 +120,7 @@ function wfProxyCheck() {
* Convert a network specification in CIDR notation to an integer network and a number of bits
*/
function wfParseCIDR( $range ) {