summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxDispatcher.php35
-rw-r--r--includes/AjaxFunctions.php96
-rw-r--r--includes/AjaxResponse.php44
-rw-r--r--includes/Article.php2217
-rw-r--r--includes/AuthPlugin.php47
-rw-r--r--includes/AutoLoader.php217
-rw-r--r--includes/Autopromote.php7
-rw-r--r--includes/BacklinkCache.php73
-rw-r--r--includes/BagOStuff.php660
-rw-r--r--includes/Block.php185
-rw-r--r--includes/CacheDependency.php43
-rw-r--r--includes/Category.php80
-rw-r--r--includes/CategoryPage.php233
-rw-r--r--includes/Categoryfinder.php142
-rw-r--r--includes/Cdb.php149
-rw-r--r--includes/Cdb_PHP.php374
-rw-r--r--includes/ChangeTags.php98
-rw-r--r--includes/ChangesFeed.php68
-rw-r--r--includes/ChangesList.php519
-rw-r--r--includes/ConfEditor.php1058
-rw-r--r--includes/Credits.php70
-rw-r--r--includes/DatabaseFunctions.php2
-rw-r--r--includes/DefaultSettings.php850
-rw-r--r--includes/Defines.php5
-rw-r--r--includes/DjVuImage.php26
-rw-r--r--includes/DoubleRedirectJob.php12
-rw-r--r--includes/EditPage.php1575
-rw-r--r--includes/Exception.php18
-rw-r--r--includes/Exif.php18
-rw-r--r--includes/Export.php73
-rw-r--r--includes/ExternalStore.php48
-rw-r--r--includes/ExternalStoreDB.php49
-rw-r--r--includes/ExternalStoreHttp.php18
-rw-r--r--includes/ExternalUser.php304
-rw-r--r--includes/FakeTitle.php83
-rw-r--r--includes/Feed.php111
-rw-r--r--includes/FeedUtils.php76
-rw-r--r--includes/FileDeleteForm.php60
-rw-r--r--includes/FileRevertForm.php13
-rw-r--r--includes/FileStore.php360
-rw-r--r--includes/ForkController.php24
-rw-r--r--includes/GlobalFunctions.php847
-rw-r--r--includes/HTMLCacheUpdate.php143
-rw-r--r--includes/HTMLFileCache.php21
-rw-r--r--includes/HTMLForm.php1391
-rw-r--r--includes/HistoryPage.php730
-rw-r--r--includes/Hooks.php80
-rw-r--r--includes/Html.php539
-rw-r--r--includes/HttpFunctions.php929
-rw-r--r--includes/IP.php160
-rw-r--r--includes/ImageFunctions.php6
-rw-r--r--includes/ImageGallery.php37
-rw-r--r--includes/ImagePage.php361
-rw-r--r--includes/ImageQueryPage.php18
-rw-r--r--includes/Import.php63
-rw-r--r--includes/Interwiki.php133
-rw-r--r--includes/JSMin.php290
-rw-r--r--includes/JobQueue.php42
-rw-r--r--includes/Licenses.php79
-rw-r--r--includes/LinkCache.php22
-rw-r--r--includes/LinkFilter.php84
-rw-r--r--includes/Linker.php1135
-rw-r--r--includes/LinksUpdate.php7
-rw-r--r--includes/LocalisationCache.php999
-rw-r--r--includes/LogEventsList.php628
-rw-r--r--includes/LogPage.php115
-rw-r--r--includes/MagicWord.php91
-rw-r--r--includes/Math.php39
-rw-r--r--includes/MediaTransformOutput.php30
-rw-r--r--includes/MessageCache.php295
-rw-r--r--includes/MimeMagic.php40
-rw-r--r--includes/Namespace.php47
-rw-r--r--includes/ObjectCache.php13
-rw-r--r--includes/OutputHandler.php22
-rw-r--r--includes/OutputPage.php1738
-rw-r--r--includes/PageHistory.php630
-rw-r--r--includes/Pager.php213
-rw-r--r--includes/PatrolLog.php36
-rw-r--r--includes/PoolCounter.php64
-rw-r--r--includes/Preferences.php1389
-rw-r--r--includes/PrefixSearch.php53
-rw-r--r--includes/Profiler.php15
-rw-r--r--includes/ProfilerSimpleText.php4
-rw-r--r--includes/ProtectionForm.php104
-rw-r--r--includes/ProxyTools.php29
-rw-r--r--includes/QueryPage.php174
-rw-r--r--includes/RawPage.php35
-rw-r--r--includes/RecentChange.php273
-rw-r--r--includes/RefreshLinksJob.php3
-rw-r--r--includes/Revision.php356
-rw-r--r--includes/Sanitizer.php392
-rw-r--r--includes/SearchMySQL.php270
-rw-r--r--includes/Setup.php100
-rw-r--r--includes/SiteConfiguration.php17
-rw-r--r--includes/SiteStats.php168
-rw-r--r--includes/Skin.php1064
-rw-r--r--includes/SkinTemplate.php593
-rw-r--r--includes/SpecialPage.php104
-rw-r--r--includes/SquidPurgeClient.php380
-rw-r--r--includes/SquidUpdate.php137
-rw-r--r--includes/Status.php16
-rw-r--r--includes/StreamFile.php7
-rw-r--r--includes/StubObject.php16
-rw-r--r--includes/Title.php740
-rw-r--r--includes/User.php829
-rw-r--r--includes/UserMailer.php19
-rw-r--r--includes/UserRightsProxy.php73
-rw-r--r--includes/WatchedItem.php4
-rw-r--r--includes/WatchlistEditor.php37
-rw-r--r--includes/WebRequest.php190
-rw-r--r--includes/WebResponse.php30
-rw-r--r--includes/WebStart.php7
-rw-r--r--includes/Wiki.php96
-rw-r--r--includes/WikiMap.php161
-rw-r--r--includes/Xml.php75
-rw-r--r--includes/ZhConversion.php1781
-rw-r--r--includes/api/ApiBase.php955
-rw-r--r--includes/api/ApiBlock.php105
-rw-r--r--includes/api/ApiDelete.php203
-rw-r--r--includes/api/ApiDisabled.php23
-rw-r--r--includes/api/ApiEditPage.php385
-rw-r--r--includes/api/ApiEmailUser.php34
-rw-r--r--includes/api/ApiExpandTemplates.php17
-rw-r--r--includes/api/ApiFeedWatchlist.php80
-rw-r--r--includes/api/ApiFormatBase.php115
-rw-r--r--includes/api/ApiFormatDbg.php18
-rw-r--r--includes/api/ApiFormatJson.php44
-rw-r--r--includes/api/ApiFormatPhp.php12
-rw-r--r--includes/api/ApiFormatRaw.php28
-rw-r--r--includes/api/ApiFormatTxt.php18
-rw-r--r--includes/api/ApiFormatWddx.php77
-rw-r--r--includes/api/ApiFormatXml.php117
-rw-r--r--includes/api/ApiFormatYaml.php12
-rw-r--r--includes/api/ApiFormatYaml_spyc.php98
-rw-r--r--includes/api/ApiHelp.php12
-rw-r--r--includes/api/ApiImport.php98
-rw-r--r--includes/api/ApiLogin.php99
-rw-r--r--includes/api/ApiLogout.php12
-rw-r--r--includes/api/ApiMain.php347
-rw-r--r--includes/api/ApiMove.php159
-rw-r--r--includes/api/ApiOpenSearch.php38
-rw-r--r--includes/api/ApiPageSet.php257
-rw-r--r--includes/api/ApiParamInfo.php134
-rw-r--r--includes/api/ApiParse.php194
-rw-r--r--includes/api/ApiPatrol.php50
-rw-r--r--includes/api/ApiProtect.php146
-rw-r--r--includes/api/ApiPurge.php41
-rw-r--r--includes/api/ApiQuery.php243
-rw-r--r--includes/api/ApiQueryAllCategories.php92
-rw-r--r--includes/api/ApiQueryAllLinks.php135
-rw-r--r--includes/api/ApiQueryAllUsers.php148
-rw-r--r--includes/api/ApiQueryAllimages.php121
-rw-r--r--includes/api/ApiQueryAllmessages.php118
-rw-r--r--includes/api/ApiQueryAllpages.php162
-rw-r--r--includes/api/ApiQueryBacklinks.php338
-rw-r--r--includes/api/ApiQueryBase.php196
-rw-r--r--includes/api/ApiQueryBlocks.php205
-rw-r--r--includes/api/ApiQueryCategories.php179
-rw-r--r--includes/api/ApiQueryCategoryInfo.php66
-rw-r--r--includes/api/ApiQueryCategoryMembers.php186
-rw-r--r--includes/api/ApiQueryDeletedrevs.php257
-rw-r--r--includes/api/ApiQueryDisabled.php12
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php97
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php125
-rw-r--r--includes/api/ApiQueryExternalLinks.php50
-rw-r--r--includes/api/ApiQueryImageInfo.php229
-rw-r--r--includes/api/ApiQueryImages.php101
-rw-r--r--includes/api/ApiQueryInfo.php447
-rw-r--r--includes/api/ApiQueryLangLinks.php75
-rw-r--r--includes/api/ApiQueryLinks.php120
-rw-r--r--includes/api/ApiQueryLogEvents.php274
-rw-r--r--includes/api/ApiQueryProtectedTitles.php112
-rw-r--r--includes/api/ApiQueryRandom.php82
-rw-r--r--includes/api/ApiQueryRecentChanges.php383
-rw-r--r--includes/api/ApiQueryRevisions.php435
-rw-r--r--includes/api/ApiQuerySearch.php129
-rw-r--r--includes/api/ApiQuerySiteinfo.php198
-rw-r--r--includes/api/ApiQueryTags.php181
-rw-r--r--includes/api/ApiQueryUserContributions.php305
-rw-r--r--includes/api/ApiQueryUserInfo.php99
-rw-r--r--includes/api/ApiQueryUsers.php202
-rw-r--r--includes/api/ApiQueryWatchlist.php316
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php118
-rw-r--r--includes/api/ApiResult.php168
-rw-r--r--includes/api/ApiRollback.php76
-rw-r--r--includes/api/ApiUnblock.php59
-rw-r--r--includes/api/ApiUndelete.php87
-rw-r--r--includes/api/ApiUpload.php325
-rw-r--r--includes/api/ApiUserrights.php128
-rw-r--r--includes/api/ApiWatch.php46
-rw-r--r--includes/cbt/CBTCompiler.php366
-rw-r--r--includes/cbt/CBTProcessor.php539
-rw-r--r--includes/cbt/README108
-rw-r--r--includes/db/Database.php812
-rw-r--r--includes/db/DatabaseIbm_db2.php908
-rw-r--r--includes/db/DatabaseMssql.php86
-rw-r--r--includes/db/DatabaseMysql.php453
-rw-r--r--includes/db/DatabaseOracle.php1049
-rw-r--r--includes/db/DatabasePostgres.php184
-rw-r--r--includes/db/DatabaseSqlite.php432
-rw-r--r--includes/db/LBFactory.php14
-rw-r--r--includes/db/LoadBalancer.php21
-rw-r--r--includes/diff/DifferenceEngine.php933
-rw-r--r--includes/diff/DifferenceInterface.php1021
-rw-r--r--includes/diff/HTMLDiff.php1009
-rw-r--r--includes/diff/Nodes.php439
-rw-r--r--includes/extauth/Hardcoded.php79
-rw-r--r--includes/extauth/MediaWiki.php141
-rw-r--r--includes/extauth/vB.php140
-rw-r--r--includes/filerepo/ArchivedFile.php68
-rw-r--r--includes/filerepo/FSRepo.php103
-rw-r--r--includes/filerepo/File.php76
-rw-r--r--includes/filerepo/FileCache.php156
-rw-r--r--includes/filerepo/FileRepo.php175
-rw-r--r--includes/filerepo/ForeignAPIFile.php11
-rw-r--r--includes/filerepo/ForeignAPIRepo.php114
-rw-r--r--includes/filerepo/ForeignDBFile.php10
-rw-r--r--includes/filerepo/ForeignDBRepo.php15
-rw-r--r--includes/filerepo/ForeignDBViaLBRepo.php15
-rw-r--r--includes/filerepo/Image.php4
-rw-r--r--includes/filerepo/LocalFile.php224
-rw-r--r--includes/filerepo/LocalRepo.php97
-rw-r--r--includes/filerepo/NullRepo.php8
-rw-r--r--includes/filerepo/OldLocalFile.php24
-rw-r--r--includes/filerepo/RepoGroup.php124
-rw-r--r--includes/json/FormatJson.php32
-rw-r--r--includes/json/Services_JSON.php (renamed from includes/api/ApiFormatJson_json.php)20
-rw-r--r--includes/media/Bitmap.php121
-rw-r--r--includes/media/DjVu.php48
-rw-r--r--includes/media/GIF.php72
-rw-r--r--includes/media/GIFMetadataExtractor.php175
-rw-r--r--includes/media/Generic.php44
-rw-r--r--includes/media/SVG.php2
-rw-r--r--includes/memcached-client.php1990
-rw-r--r--includes/mime.types6
-rw-r--r--includes/normal/RandomTest.php2
-rw-r--r--includes/normal/Utf8CaseGenerate.php2
-rw-r--r--includes/normal/Utf8Test.php2
-rw-r--r--includes/normal/UtfNormal.php8
-rw-r--r--includes/normal/UtfNormalData.inc10
-rw-r--r--includes/normal/UtfNormalDataK.inc4
-rw-r--r--includes/normal/UtfNormalGenerate.php8
-rw-r--r--includes/parser/CoreParserFunctions.php148
-rw-r--r--includes/parser/CoreTagHooks.php49
-rw-r--r--includes/parser/DateFormatter.php10
-rw-r--r--includes/parser/LinkHolderArray.php3
-rw-r--r--includes/parser/Parser.php1121
-rw-r--r--includes/parser/ParserCache.php75
-rw-r--r--includes/parser/ParserOptions.php27
-rw-r--r--includes/parser/ParserOutput.php31
-rw-r--r--includes/parser/Preprocessor.php15
-rw-r--r--includes/parser/Preprocessor_DOM.php31
-rw-r--r--includes/parser/Preprocessor_Hash.php18
-rw-r--r--includes/search/SearchEngine.php (renamed from includes/SearchEngine.php)325
-rw-r--r--includes/search/SearchIBM_DB2.php (renamed from includes/SearchIBM_DB2.php)89
-rw-r--r--includes/search/SearchMySQL.php412
-rw-r--r--includes/search/SearchMySQL4.php (renamed from includes/SearchMySQL4.php)0
-rw-r--r--includes/search/SearchOracle.php (renamed from includes/SearchOracle.php)136
-rw-r--r--includes/search/SearchPostgres.php (renamed from includes/SearchPostgres.php)21
-rw-r--r--includes/search/SearchSqlite.php344
-rw-r--r--includes/search/SearchUpdate.php (renamed from includes/SearchUpdate.php)6
-rw-r--r--includes/specials/SpecialActiveusers.php195
-rw-r--r--includes/specials/SpecialAllmessages.php581
-rw-r--r--includes/specials/SpecialAllpages.php180
-rw-r--r--includes/specials/SpecialAncientpages.php28
-rw-r--r--includes/specials/SpecialBlankpage.php19
-rw-r--r--includes/specials/SpecialBlockip.php433
-rw-r--r--includes/specials/SpecialBooksources.php4
-rw-r--r--includes/specials/SpecialBrokenRedirects.php42
-rw-r--r--includes/specials/SpecialCategories.php13
-rw-r--r--includes/specials/SpecialConfirmemail.php14
-rw-r--r--includes/specials/SpecialContributions.php318
-rw-r--r--includes/specials/SpecialDeletedContributions.php243
-rw-r--r--includes/specials/SpecialDisambiguations.php2
-rw-r--r--includes/specials/SpecialDoubleRedirects.php28
-rw-r--r--includes/specials/SpecialEmailuser.php23
-rw-r--r--includes/specials/SpecialExport.php69
-rw-r--r--includes/specials/SpecialFewestrevisions.php21
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php23
-rw-r--r--includes/specials/SpecialFilepath.php4
-rw-r--r--includes/specials/SpecialImport.php15
-rw-r--r--includes/specials/SpecialIpblocklist.php64
-rw-r--r--includes/specials/SpecialLinkSearch.php18
-rw-r--r--includes/specials/SpecialListUserRestrictions.php162
-rw-r--r--includes/specials/SpecialListfiles.php22
-rw-r--r--includes/specials/SpecialListgrouprights.php74
-rw-r--r--includes/specials/SpecialListredirects.php9
-rw-r--r--includes/specials/SpecialListusers.php34
-rw-r--r--includes/specials/SpecialLockdb.php6
-rw-r--r--includes/specials/SpecialLog.php12
-rw-r--r--includes/specials/SpecialMIMEsearch.php20
-rw-r--r--includes/specials/SpecialMergeHistory.php38
-rw-r--r--includes/specials/SpecialMostlinked.php37
-rw-r--r--includes/specials/SpecialMostlinkedcategories.php2
-rw-r--r--includes/specials/SpecialMostlinkedtemplates.php33
-rw-r--r--includes/specials/SpecialMostrevisions.php9
-rw-r--r--includes/specials/SpecialMovepage.php138
-rw-r--r--includes/specials/SpecialNewimages.php81
-rw-r--r--includes/specials/SpecialNewpages.php38
-rw-r--r--includes/specials/SpecialPopularpages.php12
-rw-r--r--includes/specials/SpecialPreferences.php1315
-rw-r--r--includes/specials/SpecialPrefixindex.php28
-rw-r--r--includes/specials/SpecialProtectedpages.php47
-rw-r--r--includes/specials/SpecialProtectedtitles.php6
-rw-r--r--includes/specials/SpecialRandompage.php61
-rw-r--r--includes/specials/SpecialRandomredirect.php5
-rw-r--r--includes/specials/SpecialRecentchanges.php112
-rw-r--r--includes/specials/SpecialRecentchangeslinked.php72
-rw-r--r--includes/specials/SpecialRemoveRestrictions.php10
-rw-r--r--includes/specials/SpecialResetpass.php76
-rw-r--r--includes/specials/SpecialRestrictUser.php190
-rw-r--r--includes/specials/SpecialRevisiondelete.php2801
-rw-r--r--includes/specials/SpecialSearch.php1417
-rw-r--r--includes/specials/SpecialShortpages.php11
-rw-r--r--includes/specials/SpecialSpecialpages.php6
-rw-r--r--includes/specials/SpecialStatistics.php70
-rw-r--r--includes/specials/SpecialTags.php12
-rw-r--r--includes/specials/SpecialUncategorizedtemplates.php2
-rw-r--r--includes/specials/SpecialUndelete.php366
-rw-r--r--includes/specials/SpecialUnlockdb.php6
-rw-r--r--includes/specials/SpecialUnusedcategories.php2
-rw-r--r--includes/specials/SpecialUnusedimages.php16
-rw-r--r--includes/specials/SpecialUnusedtemplates.php13
-rw-r--r--includes/specials/SpecialUnwatchedpages.php12
-rw-r--r--includes/specials/SpecialUpload.php2374
-rw-r--r--includes/specials/SpecialUploadMogile.php135
-rw-r--r--includes/specials/SpecialUserlogin.php215
-rw-r--r--includes/specials/SpecialUserlogout.php10
-rw-r--r--includes/specials/SpecialUserrights.php390
-rw-r--r--includes/specials/SpecialVersion.php312
-rw-r--r--includes/specials/SpecialWantedcategories.php39
-rw-r--r--includes/specials/SpecialWantedfiles.php59
-rw-r--r--includes/specials/SpecialWantedpages.php93
-rw-r--r--includes/specials/SpecialWantedtemplates.php59
-rw-r--r--includes/specials/SpecialWatchlist.php120
-rw-r--r--includes/specials/SpecialWhatlinkshere.php84
-rw-r--r--includes/specials/SpecialWithoutinterwiki.php11
-rw-r--r--includes/templates/NoLocalSettings.php4
-rw-r--r--includes/templates/PHP4.php4
-rw-r--r--includes/templates/Userlogin.php111
-rw-r--r--includes/upload/UploadBase.php1091
-rw-r--r--includes/upload/UploadFromFile.php32
-rw-r--r--includes/upload/UploadFromStash.php84
-rw-r--r--includes/upload/UploadFromUrl.php137
-rw-r--r--includes/zhtable/Makefile2
-rw-r--r--includes/zhtable/Makefile.py83
-rw-r--r--includes/zhtable/simp2trad.manual459
-rw-r--r--includes/zhtable/simpphrases.manual122
-rw-r--r--includes/zhtable/simpphrases_exclude.manual4
-rw-r--r--includes/zhtable/toCN.manual12
-rw-r--r--includes/zhtable/toHK.manual206
-rw-r--r--includes/zhtable/toSimp.manual25
-rw-r--r--includes/zhtable/toTW.manual59
-rw-r--r--includes/zhtable/toTrad.manual77
-rw-r--r--includes/zhtable/trad2simp.manual265
-rw-r--r--includes/zhtable/tradphrases.manual972
-rw-r--r--includes/zhtable/tradphrases_exclude.manual53
357 files changed, 47259 insertions, 28880 deletions
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index c489cf1c..5bd7cfa4 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -7,7 +7,7 @@
* Handle ajax requests and send them to the proper handler.
*/
-if( !(defined( 'MEDIAWIKI' ) && $wgUseAjax ) ) {
+if ( !( defined( 'MEDIAWIKI' ) && $wgUseAjax ) ) {
die( 1 );
}
@@ -33,11 +33,11 @@ class AjaxDispatcher {
$this->mode = "";
- if (! empty($_GET["rs"])) {
+ if ( ! empty( $_GET["rs"] ) ) {
$this->mode = "get";
}
- if (!empty($_POST["rs"])) {
+ if ( !empty( $_POST["rs"] ) ) {
$this->mode = "post";
}
@@ -45,7 +45,7 @@ class AjaxDispatcher {
case 'get':
$this->func_name = isset( $_GET["rs"] ) ? $_GET["rs"] : '';
- if (! empty($_GET["rsargs"])) {
+ if ( ! empty( $_GET["rsargs"] ) ) {
$this->args = $_GET["rsargs"];
} else {
$this->args = array();
@@ -54,7 +54,7 @@ class AjaxDispatcher {
case 'post':
$this->func_name = isset( $_POST["rs"] ) ? $_POST["rs"] : '';
- if (! empty($_POST["rsargs"])) {
+ if ( ! empty( $_POST["rsargs"] ) ) {
$this->args = $_POST["rsargs"];
} else {
$this->args = array();
@@ -65,7 +65,7 @@ class AjaxDispatcher {
wfProfileOut( __METHOD__ );
return;
# Or we could throw an exception:
- #throw new MWException( __METHOD__ . ' called without any data (mode empty).' );
+ # throw new MWException( __METHOD__ . ' called without any data (mode empty).' );
}
@@ -83,9 +83,10 @@ class AjaxDispatcher {
if ( empty( $this->mode ) ) {
return;
}
+
wfProfileIn( __METHOD__ );
- if (! in_array( $this->func_name, $wgAjaxExportList ) ) {
+ if ( ! in_array( $this->func_name, $wgAjaxExportList ) ) {
wfDebug( __METHOD__ . ' Bad Request for unknown function ' . $this->func_name . "\n" );
wfHttpError( 400, 'Bad Request',
@@ -99,11 +100,11 @@ class AjaxDispatcher {
$func = $this->func_name;
}
try {
- $result = call_user_func_array($func, $this->args);
+ $result = call_user_func_array( $func, $this->args );
- if ( $result === false || $result === NULL ) {
- wfDebug( __METHOD__ . ' ERROR while dispatching '
- . $this->func_name . "(" . var_export( $this->args, true ) . "): "
+ if ( $result === false || $result === null ) {
+ wfDebug( __METHOD__ . ' ERROR while dispatching '
+ . $this->func_name . "(" . var_export( $this->args, true ) . "): "
. "no data returned\n" );
wfHttpError( 500, 'Internal Error',
@@ -111,7 +112,7 @@ class AjaxDispatcher {
}
else {
if ( is_string( $result ) ) {
- $result= new AjaxResponse( $result );
+ $result = new AjaxResponse( $result );
}
$result->sendHeaders();
@@ -120,12 +121,12 @@ class AjaxDispatcher {
wfDebug( __METHOD__ . ' dispatch complete for ' . $this->func_name . "\n" );
}
- } catch (Exception $e) {
- wfDebug( __METHOD__ . ' ERROR while dispatching '
- . $this->func_name . "(" . var_export( $this->args, true ) . "): "
- . get_class($e) . ": " . $e->getMessage() . "\n" );
+ } catch ( Exception $e ) {
+ wfDebug( __METHOD__ . ' ERROR while dispatching '
+ . $this->func_name . "(" . var_export( $this->args, true ) . "): "
+ . get_class( $e ) . ": " . $e->getMessage() . "\n" );
- if (!headers_sent()) {
+ if ( !headers_sent() ) {
wfHttpError( 500, 'Internal Error',
$e->getMessage() );
} else {
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index 1a9adbca..e3180e0a 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -4,7 +4,7 @@
* @ingroup Ajax
*/
-if( !defined( 'MEDIAWIKI' ) ) {
+if ( !defined( 'MEDIAWIKI' ) ) {
die( 1 );
}
@@ -14,31 +14,31 @@ if( !defined( 'MEDIAWIKI' ) ) {
* Modified function from http://pure-essence.net/stuff/code/utf8RawUrlDecode.phps
*
* @param $source String escaped with Javascript's escape() function
- * @param $iconv_to String destination character set will be used as second parameter
+ * @param $iconv_to String destination character set will be used as second parameter
* in the iconv function. Default is UTF-8.
* @return string
*/
-function js_unescape($source, $iconv_to = 'UTF-8') {
+function js_unescape( $source, $iconv_to = 'UTF-8' ) {
$decodedStr = '';
$pos = 0;
- $len = strlen ($source);
+ $len = strlen ( $source );
- while ($pos < $len) {
- $charAt = substr ($source, $pos, 1);
- if ($charAt == '%') {
+ while ( $pos < $len ) {
+ $charAt = substr ( $source, $pos, 1 );
+ if ( $charAt == '%' ) {
$pos++;
- $charAt = substr ($source, $pos, 1);
- if ($charAt == 'u') {
+ $charAt = substr ( $source, $pos, 1 );
+ if ( $charAt == 'u' ) {
// we got a unicode character
$pos++;
- $unicodeHexVal = substr ($source, $pos, 4);
- $unicode = hexdec ($unicodeHexVal);
- $decodedStr .= code2utf($unicode);
+ $unicodeHexVal = substr ( $source, $pos, 4 );
+ $unicode = hexdec ( $unicodeHexVal );
+ $decodedStr .= code2utf( $unicode );
$pos += 4;
} else {
// we have an escaped ascii character
- $hexVal = substr ($source, $pos, 2);
- $decodedStr .= chr (hexdec ($hexVal));
+ $hexVal = substr ( $source, $pos, 2 );
+ $decodedStr .= chr ( hexdec ( $hexVal ) );
$pos += 2;
}
} else {
@@ -47,8 +47,8 @@ function js_unescape($source, $iconv_to = 'UTF-8') {
}
}
- if ($iconv_to != "UTF-8") {
- $decodedStr = iconv("UTF-8", $iconv_to, $decodedStr);
+ if ( $iconv_to != "UTF-8" ) {
+ $decodedStr = iconv( "UTF-8", $iconv_to, $decodedStr );
}
return $decodedStr;
@@ -61,16 +61,16 @@ function js_unescape($source, $iconv_to = 'UTF-8') {
* @param $num Integer
* @return utf8char
*/
-function code2utf($num){
- if ( $num<128 )
- return chr($num);
- if ( $num<2048 )
- return chr(($num>>6)+192).chr(($num&63)+128);
- if ( $num<65536 )
- return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128);
- if ( $num<2097152 )
- return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr((($num>>6)&63)+128) .chr(($num&63)+128);
- return '';
+function code2utf( $num ) {
+ if ( $num < 128 )
+ return chr( $num );
+ if ( $num < 2048 )
+ return chr( ( $num >> 6 ) + 192 ) . chr( ( $num&63 ) + 128 );
+ if ( $num < 65536 )
+ return chr( ( $num >> 12 ) + 224 ) . chr( ( ( $num >> 6 )&63 ) + 128 ) . chr( ( $num&63 ) + 128 );
+ if ( $num < 2097152 )
+ return chr( ( $num >> 18 ) + 240 ) . chr( ( ( $num >> 12 )&63 ) + 128 ) . chr( ( ( $num >> 6 )&63 ) + 128 ) . chr( ( $num&63 ) + 128 );
+ return '';
}
/**
@@ -81,49 +81,49 @@ function code2utf($num){
* respectively, followed by an HTML message to display in the alert box; or
* '<err#>' on error
*/
-function wfAjaxWatch($pagename = "", $watch = "") {
- if(wfReadOnly()) {
+function wfAjaxWatch( $pagename = "", $watch = "" ) {
+ if ( wfReadOnly() ) {
// redirect to action=(un)watch, which will display the database lock
// message
return '<err#>';
}
- if('w' !== $watch && 'u' !== $watch) {
+ if ( 'w' !== $watch && 'u' !== $watch ) {
return '<err#>';
}
$watch = 'w' === $watch;
- $title = Title::newFromDBkey($pagename);
- if(!$title) {
+ $title = Title::newFromDBkey( $pagename );
+ if ( !$title ) {
// Invalid title
return '<err#>';
}
- $article = new Article($title);
+ $article = new Article( $title );
$watching = $title->userIsWatching();
- if($watch) {
- if(!$watching) {
- $dbw = wfGetDB(DB_MASTER);
+ if ( $watch ) {
+ if ( !$watching ) {
+ $dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$ok = $article->doWatch();
$dbw->commit();
}
} else {
- if($watching) {
- $dbw = wfGetDB(DB_MASTER);
+ if ( $watching ) {
+ $dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$ok = $article->doUnwatch();
$dbw->commit();
}
}
// Something stopped the change
- if( isset($ok) && !$ok ) {
+ if ( isset( $ok ) && !$ok ) {
return '<err#>';
}
- if( $watch ) {
- return '<w#>'.wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+ if ( $watch ) {
+ return '<w#>' . wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
} else {
- return '<u#>'.wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+ return '<u#>' . wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
}
}
@@ -133,12 +133,12 @@ function wfAjaxWatch($pagename = "", $watch = "") {
*/
function wfAjaxGetThumbnailUrl( $file, $width, $height ) {
$file = wfFindFile( $file );
-
+
if ( !$file || !$file->exists() )
return null;
-
+
$url = $file->getThumbnail( $width, $height )->url;
-
+
return $url;
}
@@ -148,11 +148,11 @@ function wfAjaxGetThumbnailUrl( $file, $width, $height ) {
*/
function wfAjaxGetFileUrl( $file ) {
$file = wfFindFile( $file );
-
+
if ( !$file || !$file->exists() )
return null;
-
+
$url = $file->getUrl();
-
+
return $url;
-} \ No newline at end of file
+}
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index 26b6f443..f7495666 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -4,14 +4,14 @@
* @ingroup Ajax
*/
-if( !defined( 'MEDIAWIKI' ) ) {
+if ( !defined( 'MEDIAWIKI' ) ) {
die( 1 );
}
/**
* Handle responses for Ajax requests (send headers, print
* content, that sort of thing)
- *
+ *
* @ingroup Ajax
*/
class AjaxResponse {
@@ -37,15 +37,15 @@ class AjaxResponse {
/** Content of our HTTP response */
private $mText;
- function __construct( $text = NULL ) {
- $this->mCacheDuration = NULL;
- $this->mVary = NULL;
+ function __construct( $text = null ) {
+ $this->mCacheDuration = null;
+ $this->mVary = null;
$this->mDisabled = false;
$this->mText = '';
$this->mResponseCode = '200 OK';
$this->mLastModified = false;
- $this->mContentType= 'application/x-wiki';
+ $this->mContentType = 'application/x-wiki';
if ( $text ) {
$this->addText( $text );
@@ -95,13 +95,13 @@ class AjaxResponse {
header( "Status: " . $this->mResponseCode, true, (int)$n );
}
- header ("Content-Type: " . $this->mContentType );
+ header ( "Content-Type: " . $this->mContentType );
if ( $this->mLastModified ) {
- header ("Last-Modified: " . $this->mLastModified );
+ header ( "Last-Modified: " . $this->mLastModified );
}
else {
- header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+ header ( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );
}
if ( $this->mCacheDuration ) {
@@ -110,31 +110,31 @@ class AjaxResponse {
# 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 ) {
+ 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( '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' );
+ 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}");
+ 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
+ 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 ) {
@@ -156,11 +156,11 @@ class AjaxResponse {
wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP\n" );
return;
}
- if( !$wgCachePages ) {
+ if ( !$wgCachePages ) {
wfDebug( "$fname: CACHE DISABLED\n", false );
return;
}
- if( $wgUser->getOption( 'nocache' ) ) {
+ if ( $wgUser->getOption( 'nocache' ) ) {
wfDebug( "$fname: USER DISABLED CACHE\n", false );
return;
}
@@ -168,7 +168,7 @@ class AjaxResponse {
$timestamp = wfTimestamp( TS_MW, $timestamp );
$lastmod = wfTimestamp( TS_RFC2822, max( $timestamp, $wgUser->mTouched, $wgCacheEpoch ) );
- if( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
+ 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().
@@ -177,8 +177,8 @@ class AjaxResponse {
$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 ) {
- ini_set('zlib.output_compression', 0);
+ if ( ( $ismodsince >= $timestamp ) && $wgUser->validateCache( $ismodsince ) && $ismodsince >= $wgCacheEpoch ) {
+ ini_set( 'zlib.output_compression', 0 );
$this->setResponseCode( "304 Not Modified" );
$this->disable();
$this->mLastModified = $lastmod;
diff --git a/includes/Article.php b/includes/Article.php
index ef219ea3..d3863c77 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -16,30 +16,32 @@ class Article {
/**@{{
* @private
*/
- var $mComment = ''; //!<
- var $mContent; //!<
- var $mContentLoaded = false; //!<
- var $mCounter = -1; //!< Not loaded
- var $mCurID = -1; //!< Not loaded
- var $mDataLoaded = false; //!<
- var $mForUpdate = false; //!<
- var $mGoodAdjustment = 0; //!<
- var $mIsRedirect = false; //!<
- var $mLatest = false; //!<
- var $mMinorEdit; //!<
- var $mOldId; //!<
- var $mPreparedEdit = false; //!< Title object if set
- var $mRedirectedFrom = null; //!< Title object if set
- var $mRedirectTarget = null; //!< Title object if set
- var $mRedirectUrl = false; //!<
- var $mRevIdFetched = 0; //!<
- var $mRevision; //!<
- var $mTimestamp = ''; //!<
- var $mTitle; //!<
- var $mTotalAdjustment = 0; //!<
- var $mTouched = '19700101000000'; //!<
- var $mUser = -1; //!< Not loaded
- var $mUserText = ''; //!<
+ var $mComment = ''; // !<
+ var $mContent; // !<
+ var $mContentLoaded = false; // !<
+ var $mCounter = -1; // !< Not loaded
+ var $mCurID = -1; // !< Not loaded
+ var $mDataLoaded = false; // !<
+ var $mForUpdate = false; // !<
+ var $mGoodAdjustment = 0; // !<
+ var $mIsRedirect = false; // !<
+ var $mLatest = false; // !<
+ var $mMinorEdit; // !<
+ var $mOldId; // !<
+ var $mPreparedEdit = false; // !< Title object if set
+ var $mRedirectedFrom = null; // !< Title object if set
+ var $mRedirectTarget = null; // !< Title object if set
+ var $mRedirectUrl = false; // !<
+ var $mRevIdFetched = 0; // !<
+ var $mRevision; // !<
+ var $mTimestamp = ''; // !<
+ var $mTitle; // !<
+ var $mTotalAdjustment = 0; // !<
+ var $mTouched = '19700101000000'; // !<
+ var $mUser = -1; // !< Not loaded
+ var $mUserText = ''; // !<
+ var $mParserOptions; // !<
+ var $mParserOutput; // !<
/**@}}*/
/**
@@ -58,7 +60,9 @@ class Article {
*/
public static function newFromID( $id ) {
$t = Title::newFromID( $id );
- return $t == null ? null : new Article( $t );
+ # FIXME: doesn't inherit right
+ return $t == null ? null : new self( $t );
+ # return $t == null ? null : new static( $t ); // PHP 5.3
}
/**
@@ -78,19 +82,19 @@ class Article {
* @return mixed Title object, or null if this page is not a redirect
*/
public function getRedirectTarget() {
- if( !$this->mTitle || !$this->mTitle->isRedirect() )
+ if ( !$this->mTitle || !$this->mTitle->isRedirect() )
return null;
- if( !is_null($this->mRedirectTarget) )
+ if ( !is_null( $this->mRedirectTarget ) )
return $this->mRedirectTarget;
# Query the redirect table
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'redirect',
- array('rd_namespace', 'rd_title'),
- array('rd_from' => $this->getID() ),
+ array( 'rd_namespace', 'rd_title' ),
+ array( 'rd_from' => $this->getID() ),
__METHOD__
);
- if( $row ) {
- return $this->mRedirectTarget = Title::makeTitle($row->rd_namespace, $row->rd_title);
+ if ( $row ) {
+ return $this->mRedirectTarget = Title::makeTitle( $row->rd_namespace, $row->rd_title );
}
# This page doesn't have an entry in the redirect table
return $this->mRedirectTarget = $this->insertRedirect();
@@ -104,15 +108,15 @@ class Article {
*/
public function insertRedirect() {
$retval = Title::newFromRedirect( $this->getContent() );
- if( !$retval ) {
+ if ( !$retval ) {
return null;
}
$dbw = wfGetDB( DB_MASTER );
- $dbw->replace( 'redirect', array('rd_from'),
+ $dbw->replace( 'redirect', array( 'rd_from' ),
array(
'rd_from' => $this->getID(),
'rd_namespace' => $retval->getNamespace(),
- 'rd_title' => $retval->getDBKey()
+ 'rd_title' => $retval->getDBkey()
),
__METHOD__
);
@@ -137,9 +141,9 @@ class Article {
public function followRedirectText( $text ) {
$rt = Title::newFromRedirectRecurse( $text ); // recurse through to only get the final target
# process if title object is valid and not special:userlogout
- if( $rt ) {
- if( $rt->getInterwiki() != '' ) {
- if( $rt->isLocal() ) {
+ if ( $rt ) {
+ if ( $rt->getInterwiki() != '' ) {
+ if ( $rt->isLocal() ) {
// Offsite wikis need an HTTP redirect.
//
// This can be hard to reverse and may produce loops,
@@ -148,13 +152,13 @@ class Article {
return $rt->getFullURL( 'rdfrom=' . urlencode( $source ) );
}
} else {
- if( $rt->getNamespace() == NS_SPECIAL ) {
+ if ( $rt->getNamespace() == NS_SPECIAL ) {
// Gotta handle redirects to special pages differently:
// Fill the HTTP response "Location" header and ignore
// the rest of the page we're on.
//
// This can be hard to reverse, so they may be disabled.
- if( $rt->isSpecial( 'Userlogout' ) ) {
+ if ( $rt->isSpecial( 'Userlogout' ) ) {
// rolleyes
} else {
return $rt->getFullURL();
@@ -203,19 +207,19 @@ class Article {
* the shortcut in Article::followContent()
*
* @return Return the text of this revision
- */
+ */
public function getContent() {
global $wgUser, $wgContLang, $wgOut, $wgMessageCache;
wfProfileIn( __METHOD__ );
- if( $this->getID() === 0 ) {
+ if ( $this->getID() === 0 ) {
# If this is a MediaWiki:x message, then load the messages
# and return the message value for x.
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
# If this is a system message, get the default text.
list( $message, $lang ) = $wgMessageCache->figureMessage( $wgContLang->lcfirst( $this->mTitle->getText() ) );
$wgMessageCache->loadAllMessages( $lang );
$text = wfMsgGetKey( $message, false, $lang, false );
- if( wfEmptyMsg( $message, $text ) )
+ if ( wfEmptyMsg( $message, $text ) )
$text = '';
} else {
$text = wfMsgExt( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon', 'parsemag' );
@@ -228,15 +232,15 @@ class Article {
return $this->mContent;
}
}
-
+
/**
* Get the text of the current revision. No side-effects...
*
* @return Return the text of the current revision
- */
+ */
public function getRawText() {
// Check process cache for current revision
- if( $this->mContentLoaded && $this->mOldId == 0 ) {
+ if ( $this->mContentLoaded && $this->mOldId == 0 ) {
return $this->mContent;
}
$rev = Revision::newFromTitle( $this->mTitle );
@@ -260,12 +264,12 @@ class Article {
global $wgParser;
return $wgParser->getSection( $text, $section );
}
-
+
/**
* Get the text that needs to be saved in order to undo all revisions
* between $undo and $undoafter. Revisions must belong to the same page,
* must exist and must not be deleted
- * @param $undo Revision
+ * @param $undo Revision
* @param $undoafter Revision Must be an earlier revision than $undo
* @return mixed string on success, false on failure
*/
@@ -288,7 +292,7 @@ class Article {
* current revision
*/
public function getOldID() {
- if( is_null( $this->mOldId ) ) {
+ if ( is_null( $this->mOldId ) ) {
$this->mOldId = $this->getOldIDFromRequest();
}
return $this->mOldId;
@@ -303,23 +307,23 @@ class Article {
global $wgRequest;
$this->mRedirectUrl = false;
$oldid = $wgRequest->getVal( 'oldid' );
- if( isset( $oldid ) ) {
+ if ( isset( $oldid ) ) {
$oldid = intval( $oldid );
- if( $wgRequest->getVal( 'direction' ) == 'next' ) {
+ if ( $wgRequest->getVal( 'direction' ) == 'next' ) {
$nextid = $this->mTitle->getNextRevisionID( $oldid );
- if( $nextid ) {
+ if ( $nextid ) {
$oldid = $nextid;
} else {
$this->mRedirectUrl = $this->mTitle->getFullURL( 'redirect=no' );
}
- } elseif( $wgRequest->getVal( 'direction' ) == 'prev' ) {
+ } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) {
$previd = $this->mTitle->getPreviousRevisionID( $oldid );
- if( $previd ) {
+ if ( $previd ) {
$oldid = $previd;
}
}
}
- if( !$oldid ) {
+ if ( !$oldid ) {
$oldid = 0;
}
return $oldid;
@@ -329,7 +333,7 @@ class Article {
* Load the revision (including text) into this object
*/
function loadContent() {
- if( $this->mContentLoaded ) return;
+ if ( $this->mContentLoaded ) return;
wfProfileIn( __METHOD__ );
# Query variables :P
$oldid = $this->getOldID();
@@ -396,26 +400,26 @@ class Article {
* @param $data Database row object or "fromdb"
*/
public function loadPageData( $data = 'fromdb' ) {
- if( $data === 'fromdb' ) {
+ if ( $data === 'fromdb' ) {
$dbr = wfGetDB( DB_MASTER );
$data = $this->pageDataFromId( $dbr, $this->getId() );
}
$lc = LinkCache::singleton();
- if( $data ) {
+ if ( $data ) {
$lc->addGoodLinkObj( $data->page_id, $this->mTitle, $data->page_len, $data->page_is_redirect );
- $this->mTitle->mArticleID = $data->page_id;
+ $this->mTitle->mArticleID = intval( $data->page_id );
# Old-fashioned restrictions
$this->mTitle->loadRestrictions( $data->page_restrictions );
- $this->mCounter = $data->page_counter;
+ $this->mCounter = intval( $data->page_counter );
$this->mTouched = wfTimestamp( TS_MW, $data->page_touched );
- $this->mIsRedirect = $data->page_is_redirect;
- $this->mLatest = $data->page_latest;
+ $this->mIsRedirect = intval( $data->page_is_redirect );
+ $this->mLatest = intval( $data->page_latest );
} else {
- if( is_object( $this->mTitle ) ) {
+ if ( is_object( $this->mTitle ) ) {
$lc->addBadLinkObj( $this->mTitle );
}
$this->mTitle->mArticleID = 0;
@@ -431,7 +435,7 @@ class Article {
* @return string
*/
function fetchContent( $oldid = 0 ) {
- if( $this->mContentLoaded ) {
+ if ( $this->mContentLoaded ) {
return $this->mContent;
}
@@ -441,33 +445,33 @@ class Article {
# fails we'll have something telling us what we intended.
$t = $this->mTitle->getPrefixedText();
$d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
- $this->mContent = wfMsg( 'missing-article', $t, $d ) ;
+ $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ;
- if( $oldid ) {
+ if ( $oldid ) {
$revision = Revision::newFromId( $oldid );
- if( is_null( $revision ) ) {
- wfDebug( __METHOD__." failed to retrieve specified revision, id $oldid\n" );
+ if ( is_null( $revision ) ) {
+ wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
return false;
}
$data = $this->pageDataFromId( $dbr, $revision->getPage() );
- if( !$data ) {
- wfDebug( __METHOD__." failed to get page data linked to revision id $oldid\n" );
+ if ( !$data ) {
+ wfDebug( __METHOD__ . " failed to get page data linked to revision id $oldid\n" );
return false;
}
$this->mTitle = Title::makeTitle( $data->page_namespace, $data->page_title );
$this->loadPageData( $data );
} else {
- if( !$this->mDataLoaded ) {
+ if ( !$this->mDataLoaded ) {
$data = $this->pageDataFromTitle( $dbr, $this->mTitle );
- if( !$data ) {
- wfDebug( __METHOD__." failed to find page data for title " . $this->mTitle->getPrefixedText() . "\n" );
+ if ( !$data ) {
+ wfDebug( __METHOD__ . " failed to find page data for title " . $this->mTitle->getPrefixedText() . "\n" );
return false;
}
$this->loadPageData( $data );
}
$revision = Revision::newFromId( $this->mLatest );
- if( is_null( $revision ) ) {
- wfDebug( __METHOD__." failed to retrieve current page, rev_id {$this->mLatest}\n" );
+ if ( is_null( $revision ) ) {
+ wfDebug( __METHOD__ . " failed to retrieve current page, rev_id {$this->mLatest}\n" );
return false;
}
}
@@ -495,7 +499,7 @@ class Article {
*
* @param $x Mixed: FIXME
*/
- public function forUpdate( $x = NULL ) {
+ public function forUpdate( $x = null ) {
return wfSetVar( $this->mForUpdate, $x );
}
@@ -518,8 +522,8 @@ class Article {
* @return Array: options
*/
protected function getSelectOptions( $options = '' ) {
- if( $this->mForUpdate ) {
- if( is_array( $options ) ) {
+ if ( $this->mForUpdate ) {
+ if ( is_array( $options ) ) {
$options[] = 'FOR UPDATE';
} else {
$options = 'FOR UPDATE';
@@ -532,7 +536,7 @@ class Article {
* @return int Page ID
*/
public function getID() {
- if( $this->mTitle ) {
+ if ( $this->mTitle ) {
return $this->mTitle->getArticleID();
} else {
return 0;
@@ -545,7 +549,7 @@ class Article {
public function exists() {
return $this->getId() > 0;
}
-
+
/**
* Check if this page is something we're going to be showing
* some sort of sensible content for. If we return false, page
@@ -562,16 +566,16 @@ class Article {
* @return int The view count for the page
*/
public function getCount() {
- if( -1 == $this->mCounter ) {
+ if ( -1 == $this->mCounter ) {
$id = $this->getID();
- if( $id == 0 ) {
+ if ( $id == 0 ) {
$this->mCounter = 0;
} else {
$dbr = wfGetDB( DB_SLAVE );
- $this->mCounter = $dbr->selectField( 'page',
- 'page_counter',
- array( 'page_id' => $id ),
- __METHOD__,
+ $this->mCounter = $dbr->selectField( 'page',
+ 'page_counter',
+ array( 'page_id' => $id ),
+ __METHOD__,
$this->getSelectOptions()
);
}
@@ -590,7 +594,7 @@ class Article {
global $wgUseCommaCount;
$token = $wgUseCommaCount ? ',' : '[[';
- return $this->mTitle->isContentPage() && !$this->isRedirect($text) && in_string($token,$text);
+ return $this->mTitle->isContentPage() && !$this->isRedirect( $text ) && in_string( $token, $text );
}
/**
@@ -600,8 +604,8 @@ class Article {
* @return bool
*/
public function isRedirect( $text = false ) {
- if( $text === false ) {
- if( $this->mDataLoaded ) {
+ if ( $text === false ) {
+ if ( $this->mDataLoaded ) {
return $this->mIsRedirect;
}
// Apparently loadPageData was never called
@@ -610,7 +614,7 @@ class Article {
} else {
$titleObj = Title::newFromRedirect( $text );
}
- return $titleObj !== NULL;
+ return $titleObj !== null;
}
/**
@@ -620,10 +624,10 @@ class Article {
*/
public function isCurrent() {
# If no oldid, this is the current version.
- if( $this->getOldID() == 0 ) {
+ if ( $this->getOldID() == 0 ) {
return true;
}
- return $this->exists() && isset($this->mRevision) && $this->mRevision->isCurrent();
+ return $this->exists() && isset( $this->mRevision ) && $this->mRevision->isCurrent();
}
/**
@@ -631,15 +635,15 @@ class Article {
* This isn't necessary for all uses, so it's only done if needed.
*/
protected function loadLastEdit() {
- if( -1 != $this->mUser )
+ if ( -1 != $this->mUser )
return;
# New or non-existent articles have no user information
$id = $this->getID();
- if( 0 == $id ) return;
+ if ( 0 == $id ) return;
$this->mLastRevision = Revision::loadFromPageId( wfGetDB( DB_MASTER ), $id );
- if( !is_null( $this->mLastRevision ) ) {
+ if ( !is_null( $this->mLastRevision ) ) {
$this->mUser = $this->mLastRevision->getUser();
$this->mUserText = $this->mLastRevision->getUserText();
$this->mTimestamp = $this->mLastRevision->getTimestamp();
@@ -651,10 +655,10 @@ class Article {
public function getTimestamp() {
// Check if the field has been filled by ParserCache::get()
- if( !$this->mTimestamp ) {
+ if ( !$this->mTimestamp ) {
$this->loadLastEdit();
}
- return wfTimestamp(TS_MW, $this->mTimestamp);
+ return wfTimestamp( TS_MW, $this->mTimestamp );
}
public function getUser() {
@@ -687,69 +691,77 @@ class Article {
* @param $limit Integer: default 0.
* @param $offset Integer: default 0.
*/
- public function getContributors($limit = 0, $offset = 0) {
+ public function getContributors( $limit = 0, $offset = 0 ) {
# XXX: this is expensive; cache this info somewhere.
- $contribs = array();
$dbr = wfGetDB( DB_SLAVE );
$revTable = $dbr->tableName( 'revision' );
$userTable = $dbr->tableName( 'user' );
- $user = $this->getUser();
+
$pageId = $this->getId();
- $hideBit = Revision::DELETED_USER; // username hidden?
+ $user = $this->getUser();
+ if ( $user ) {
+ $excludeCond = "AND rev_user != $user";
+ } else {
+ $userText = $dbr->addQuotes( $this->getUserText() );
+ $excludeCond = "AND rev_user_text != $userText";
+ }
- $sql = "SELECT {$userTable}.*, MAX(rev_timestamp) as timestamp
+ $deletedBit = $dbr->bitAnd( 'rev_deleted', Revision::DELETED_USER ); // username hidden?
+
+ $sql = "SELECT {$userTable}.*, rev_user_text as user_name, MAX(rev_timestamp) as timestamp
FROM $revTable LEFT JOIN $userTable ON rev_user = user_id
WHERE rev_page = $pageId
- AND rev_user != $user
- AND rev_deleted & $hideBit = 0
- GROUP BY rev_user, rev_user_text, user_real_name
+ $excludeCond
+ AND $deletedBit = 0
+ GROUP BY rev_user, rev_user_text
ORDER BY timestamp DESC";
- if($limit > 0) { $sql .= ' LIMIT '.$limit; }
- if($offset > 0) { $sql .= ' OFFSET '.$offset; }
-
- $sql .= ' '. $this->getSelectOptions();
+ if ( $limit > 0 )
+ $sql = $dbr->limitResult( $sql, $limit, $offset );
- $res = $dbr->query($sql, __METHOD__ );
+ $sql .= ' ' . $this->getSelectOptions();
+ $res = $dbr->query( $sql, __METHOD__ );
return new UserArrayFromResult( $res );
}
/**
- * This is the default action of the script: just view the page of
- * the given title.
- */
+ * This is the default action of the index.php entry point: just view the
+ * page of the given title.
+ */
public function view() {
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgParser;
- global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
- global $wgDefaultRobotPolicy;
+ global $wgUseTrackbacks, $wgUseFileCache;
- # Let the parser know if this is the printable version
- if( $wgOut->isPrintable() ) {
- $wgOut->parserOptions()->setIsPrintable( true );
- }
-
wfProfileIn( __METHOD__ );
# Get variables from query string
$oldid = $this->getOldID();
+ $parserCache = ParserCache::singleton();
+
+ $parserOptions = clone $this->getParserOptions();
+ # Render printable version, use printable version cache
+ if ( $wgOut->isPrintable() ) {
+ $parserOptions->setIsPrintable( true );
+ }
# Try client and file cache
- if( $oldid === 0 && $this->checkTouched() ) {
+ if ( $oldid === 0 && $this->checkTouched() ) {
global $wgUseETag;
- if( $wgUseETag ) {
- $parserCache = ParserCache::singleton();
- $wgOut->setETag( $parserCache->getETag($this, $wgOut->parserOptions()) );
+ if ( $wgUseETag ) {
+ $wgOut->setETag( $parserCache->getETag( $this, $parserOptions ) );
}
# Is is client cached?
- if( $wgOut->checkLastModified( $this->getTouched() ) ) {
+ if ( $wgOut->checkLastModified( $this->getTouched() ) ) {
+ wfDebug( __METHOD__ . ": done 304\n" );
wfProfileOut( __METHOD__ );
return;
# Try file cache
- } else if( $this->tryFileCache() ) {
+ } else if ( $wgUseFileCache && $this->tryFileCache() ) {
+ wfDebug( __METHOD__ . ": done file cache\n" );
# tell wgOut that output is taken care of
$wgOut->disable();
$this->viewUpdates();
@@ -758,91 +770,355 @@ class Article {
}
}
- $ns = $this->mTitle->getNamespace(); # shortcut
$sk = $wgUser->getSkin();
# getOldID may want us to redirect somewhere else
- if( $this->mRedirectUrl ) {
+ if ( $this->mRedirectUrl ) {
$wgOut->redirect( $this->mRedirectUrl );
+ wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
wfProfileOut( __METHOD__ );
return;
}
+ $wgOut->setArticleFlag( true );
+ # Set page title (may be overridden by DISPLAYTITLE)
+ $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+
+ # If we got diff in the query, we want to see a diff page instead of the article.
+ if ( !is_null( $wgRequest->getVal( 'diff' ) ) ) {
+ wfDebug( __METHOD__ . ": showing diff page\n" );
+ $this->showDiffPage();
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ # Should the parser cache be used?
+ $useParserCache = $this->useParserCache( $oldid );
+ wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
+ if ( $wgUser->getOption( 'stubthreshold' ) ) {
+ wfIncrStats( 'pcache_miss_stub' );
+ }
+
+ $wasRedirected = $this->showRedirectedFromHeader();
+ $this->showNamespaceHeader();
+
+ # Iterate through the possible ways of constructing the output text.
+ # Keep going until $outputDone is set, or we run out of things to do.
+ $pass = 0;
+ $outputDone = false;
+ $this->mParserOutput = false;
+ while ( !$outputDone && ++$pass ) {
+ switch( $pass ) {
+ case 1:
+ wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
+ break;
+
+ case 2:
+ # Try the parser cache
+ if ( $useParserCache ) {
+ $this->mParserOutput = $parserCache->get( $this, $parserOptions );
+ if ( $this->mParserOutput !== false ) {
+ wfDebug( __METHOD__ . ": showing parser cache contents\n" );
+ $wgOut->addParserOutput( $this->mParserOutput );
+ # Ensure that UI elements requiring revision ID have
+ # the correct version information.
+ $wgOut->setRevisionId( $this->mLatest );
+ $outputDone = true;
+ }
+ }
+ break;
+
+ case 3:
+ $text = $this->getContent();
+ if ( $text === false || $this->getID() == 0 ) {
+ wfDebug( __METHOD__ . ": showing missing article\n" );
+ $this->showMissingArticle();
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ # Another whitelist check in case oldid is altering the title
+ if ( !$this->mTitle->userCanRead() ) {
+ wfDebug( __METHOD__ . ": denied on secondary read check\n" );
+ $wgOut->loginToUse();
+ $wgOut->output();
+ $wgOut->disable();
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ # Are we looking at an old revision
+ if ( $oldid && !is_null( $this->mRevision ) ) {
+ $this->setOldSubtitle( $oldid );
+ if ( !$this->showDeletedRevisionHeader() ) {
+ wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+ # If this "old" version is the current, then try the parser cache...
+ if ( $oldid === $this->getLatest() && $this->useParserCache( false ) ) {
+ $this->mParserOutput = $parserCache->get( $this, $parserOptions );
+ if ( $this->mParserOutput ) {
+ wfDebug( __METHOD__ . ": showing parser cache for current rev permalink\n" );
+ $wgOut->addParserOutput( $this->mParserOutput );
+ $wgOut->setRevisionId( $this->mLatest );
+ $this->showViewFooter();
+ $this->viewUpdates();
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+ }
+ }
+
+ # Ensure that UI elements requiring revision ID have
+ # the correct version information.
+ $wgOut->setRevisionId( $this->getRevIdFetched() );
+
+ # Pages containing custom CSS or JavaScript get special treatment
+ if ( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
+ wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
+ $this->showCssOrJsPage();
+ $outputDone = true;
+ } else if ( $rt = Title::newFromRedirectArray( $text ) ) {
+ wfDebug( __METHOD__ . ": showing redirect=no page\n" );
+ # Viewing a redirect page (e.g. with parameter redirect=no)
+ # Don't append the subtitle if this was an old revision
+ $wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
+ # Parse just to get categories, displaytitle, etc.
+ $this->mParserOutput = $wgParser->parse( $text, $this->mTitle, $parserOptions );
+ $wgOut->addParserOutputNoText( $this->mParserOutput );
+ $outputDone = true;
+ }
+ break;
+
+ case 4:
+ # Run the parse, protected by a pool counter
+ wfDebug( __METHOD__ . ": doing uncached parse\n" );
+ $key = $parserCache->getKey( $this, $parserOptions );
+ $poolCounter = PoolCounter::factory( 'Article::view', $key );
+ $dirtyCallback = $useParserCache ? array( $this, 'tryDirtyCache' ) : false;
+ $status = $poolCounter->executeProtected( array( $this, 'doViewParse' ), $dirtyCallback );
+
+ if ( !$status->isOK() ) {
+ # Connection or timeout error
+ $this->showPoolError( $status );
+ wfProfileOut( __METHOD__ );
+ return;
+ } else {
+ $outputDone = true;
+ }
+ break;
+
+ # Should be unreachable, but just in case...
+ default:
+ break 2;
+ }
+ }
+
+ # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
+ if ( $this->mParserOutput ) {
+ $titleText = $this->mParserOutput->getTitleText();
+ if ( strval( $titleText ) !== '' ) {
+ $wgOut->setPageTitle( $titleText );
+ }
+ }
+
+ # For the main page, overwrite the <title> element with the con-
+ # tents of 'pagetitle-view-mainpage' instead of the default (if
+ # that's not empty).
+ if ( $this->mTitle->equals( Title::newMainPage() )
+ && ( $m = wfMsgForContent( 'pagetitle-view-mainpage' ) ) !== '' )
+ {
+ $wgOut->setHTMLTitle( $m );
+ }
+
+ # Now that we've filled $this->mParserOutput, we know whether
+ # there are any __NOINDEX__ tags on the page
+ $policy = $this->getRobotPolicy( 'view' );
+ $wgOut->setIndexPolicy( $policy['index'] );
+ $wgOut->setFollowPolicy( $policy['follow'] );
+
+ $this->showViewFooter();
+ $this->viewUpdates();
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * Show a diff page according to current request variables. For use within
+ * Article::view() only, other callers should use the DifferenceEngine class.
+ */
+ public function showDiffPage() {
+ global $wgOut, $wgRequest, $wgUser;
+
$diff = $wgRequest->getVal( 'diff' );
$rcid = $wgRequest->getVal( 'rcid' );
- $rdfrom = $wgRequest->getVal( 'rdfrom' );
$diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
$purge = $wgRequest->getVal( 'action' ) == 'purge';
- $return404 = false;
+ $unhide = $wgRequest->getInt( 'unhide' ) == 1;
+ $oldid = $this->getOldID();
- $wgOut->setArticleFlag( true );
+ $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $unhide );
+ // DifferenceEngine directly fetched the revision:
+ $this->mRevIdFetched = $de->mNewid;
+ $de->showDiffPage( $diffOnly );
- # Discourage indexing of printable versions, but encourage following
- if( $wgOut->isPrintable() ) {
- $policy = 'noindex,follow';
- } elseif( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
- $policy = $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()];
- } elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
- # Honour customised robot policies for this namespace
- $policy = $wgNamespaceRobotPolicies[$ns];
- } else {
- $policy = $wgDefaultRobotPolicy;
+ // 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();
}
- $wgOut->setRobotPolicy( $policy );
+ }
- # Allow admins to see deleted content if explicitly requested
- $delId = $diff ? $diff : $oldid;
- $unhide = $wgRequest->getInt('unhide') == 1
- && $wgUser->matchEditToken( $wgRequest->getVal('token'), $delId );
- # If we got diff and oldid in the query, we want to see a
- # diff page instead of the article.
- if( !is_null( $diff ) ) {
- $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ /**
+ * Show a page view for a page formatted as CSS or JavaScript. To be called by
+ * Article::view() only.
+ *
+ * This is hooked by SyntaxHighlight_GeSHi to do syntax highlighting of these
+ * page views.
+ */
+ public function showCssOrJsPage() {
+ global $wgOut;
+ $wgOut->addHTML( wfMsgExt( 'clearyourcache', 'parse' ) );
+ // Give hooks a chance to customise the output
+ if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
+ // Wrap the whole lot in a <pre> and don't parse
+ $m = array();
+ preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
+ $wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
+ $wgOut->addHTML( htmlspecialchars( $this->mContent ) );
+ $wgOut->addHTML( "\n</pre>\n" );
+ }
+ }
- $htmldiff = $wgRequest->getVal( 'htmldiff' , false);
- $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $htmldiff, $unhide );
- // DifferenceEngine directly fetched the revision:
- $this->mRevIdFetched = $de->mNewid;
- $de->showDiffPage( $diffOnly );
+ /**
+ * Get the robot policy to be used for the current action=view request.
+ * @return String the policy that should be set
+ * @deprecated use getRobotPolicy() instead, which returns an associative
+ * array
+ */
+ public function getRobotPolicyForView() {
+ wfDeprecated( __FUNC__ );
+ $policy = $this->getRobotPolicy( 'view' );
+ return $policy['index'] . ',' . $policy['follow'];
+ }
- // 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();
- }
- wfProfileOut( __METHOD__ );
- return;
- }
-
- if( $ns == NS_USER || $ns == NS_USER_TALK ) {
- # User/User_talk subpages are not modified. (bug 11443)
- if( !$this->mTitle->isSubpage() ) {
+ /**
+ * Get the robot policy to be used for the current view
+ * @param $action String the action= GET parameter
+ * @return Array the policy that should be set
+ * TODO: actions other than 'view'
+ */
+ public function getRobotPolicy( $action ) {
+
+ global $wgOut, $wgArticleRobotPolicies, $wgNamespaceRobotPolicies;
+ global $wgDefaultRobotPolicy, $wgRequest;
+
+ $ns = $this->mTitle->getNamespace();
+ if ( $ns == NS_USER || $ns == NS_USER_TALK ) {
+ # Don't index user and user talk pages for blocked users (bug 11443)
+ if ( !$this->mTitle->isSubpage() ) {
$block = new Block();
- if( $block->load( $this->mTitle->getBaseText() ) ) {
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ if ( $block->load( $this->mTitle->getText() ) ) {
+ return array( 'index' => 'noindex',
+ 'follow' => 'nofollow' );
}
}
}
- # Should the parser cache be used?
- $pcache = $this->useParserCache( $oldid );
- wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" );
- if( $wgUser->getOption( 'stubthreshold' ) ) {
- wfIncrStats( 'pcache_miss_stub' );
+ if ( $this->getID() === 0 || $this->getOldID() ) {
+ # Non-articles (special pages etc), and old revisions
+ return array( 'index' => 'noindex',
+ 'follow' => 'nofollow' );
+ } elseif ( $wgOut->isPrintable() ) {
+ # Discourage indexing of printable versions, but encourage following
+ return array( 'index' => 'noindex',
+ 'follow' => 'follow' );
+ } elseif ( $wgRequest->getInt( 'curid' ) ) {
+ # For ?curid=x urls, disallow indexing
+ return array( 'index' => 'noindex',
+ 'follow' => 'follow' );
}
- $wasRedirected = false;
- if( isset( $this->mRedirectedFrom ) ) {
+ # Otherwise, construct the policy based on the various config variables.
+ $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
+
+ if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
+ # Honour customised robot policies for this namespace
+ $policy = array_merge( $policy,
+ self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] ) );
+ }
+ if ( $this->mTitle->canUseNoindex() && is_object( $this->mParserOutput ) && $this->mParserOutput->getIndexPolicy() ) {
+ # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
+ # a final sanity check that we have really got the parser output.
+ $policy = array_merge( $policy,
+ array( 'index' => $this->mParserOutput->getIndexPolicy() ) );
+ }
+
+ if ( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
+ # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
+ $policy = array_merge( $policy,
+ self::formatRobotPolicy( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) );
+ }
+
+ return $policy;
+
+ }
+
+ /**
+ * Converts a String robot policy into an associative array, to allow
+ * merging of several policies using array_merge().
+ * @param $policy Mixed, returns empty array on null/false/'', transparent
+ * to already-converted arrays, converts String.
+ * @return associative Array: 'index' => <indexpolicy>, 'follow' => <followpolicy>
+ */
+ public static function formatRobotPolicy( $policy ) {
+ if ( is_array( $policy ) ) {
+ return $policy;
+ } elseif ( !$policy ) {
+ return array();
+ }
+
+ $policy = explode( ',', $policy );
+ $policy = array_map( 'trim', $policy );
+
+ $arr = array();
+ foreach ( $policy as $var ) {
+ if ( in_array( $var, array( 'index', 'noindex' ) ) ) {
+ $arr['index'] = $var;
+ } elseif ( in_array( $var, array( 'follow', 'nofollow' ) ) ) {
+ $arr['follow'] = $var;
+ }
+ }
+ return $arr;
+ }
+
+ /**
+ * If this request is a redirect view, send "redirected from" subtitle to
+ * $wgOut. Returns true if the header was needed, false if this is not a
+ * redirect view. Handles both local and remote redirects.
+ */
+ public function showRedirectedFromHeader() {
+ global $wgOut, $wgUser, $wgRequest, $wgRedirectSources;
+
+ $rdfrom = $wgRequest->getVal( 'rdfrom' );
+ $sk = $wgUser->getSkin();
+ if ( isset( $this->mRedirectedFrom ) ) {
// This is an internally redirected page view.
// We'll need a backlink to the source page for navigation.
- if( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
- $redir = $sk->makeKnownLinkObj( $this->mRedirectedFrom, '', 'redirect=no' );
+ if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
+ $redir = $sk->link(
+ $this->mRedirectedFrom,
+ null,
+ array(),
+ array( 'redirect' => 'no' ),
+ array( 'known', 'noclasses' )
+ );
$s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
$wgOut->setSubtitle( $s );
// Set the fragment if one was specified in the redirect
- if( strval( $this->mTitle->getFragment() ) != '' ) {
+ if ( strval( $this->mTitle->getFragment() ) != '' ) {
$fragment = Xml::escapeJsString( $this->mTitle->getFragmentForURL() );
$wgOut->addInlineScript( "redirectToFragment(\"$fragment\");" );
}
@@ -851,225 +1127,198 @@ class Article {
$wgOut->addLink( array( 'rel' => 'canonical',
'href' => $this->mTitle->getLocalURL() )
);
- $wasRedirected = true;
+ return true;
}
- } elseif( !empty( $rdfrom ) ) {
+ } elseif ( $rdfrom ) {
// This is an externally redirected view, from some other wiki.
// If it was reported from a trusted site, supply a backlink.
- global $wgRedirectSources;
- if( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
+ if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
$redir = $sk->makeExternalLink( $rdfrom, $rdfrom );
$s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
$wgOut->setSubtitle( $s );
- $wasRedirected = true;
- }
- }
-
- $outputDone = false;
- wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$pcache ) );
- if( $pcache && $wgOut->tryParserCache( $this ) ) {
- // Ensure that UI elements requiring revision ID have
- // the correct version information.
- $wgOut->setRevisionId( $this->mLatest );
- $outputDone = true;
- }
- # Fetch content and check for errors
- if( !$outputDone ) {
- # If the article does not exist and was deleted, show the log
- if( $this->getID() == 0 ) {
- $this->showDeletionLog();
- }
- $text = $this->getContent();
- // For now, check also for ID until getContent actually returns
- // false for pages that do not exists
- if( $text === false || $this->getID() === 0 ) {
- # Failed to load, replace text with error message
- $t = $this->mTitle->getPrefixedText();
- if( $oldid ) {
- $d = wfMsgExt( 'missingarticle-rev', 'escape', $oldid );
- $text = wfMsgExt( 'missing-article', 'parsemag', $t, $d );
- // Always use page content for pages in the MediaWiki namespace
- // since it contains the default message
- } elseif ( $this->mTitle->getNamespace() != NS_MEDIAWIKI ) {
- $text = wfMsgExt( 'noarticletext', 'parsemag' );
- }
- }
-
- # Non-existent pages
- if( $this->getID() === 0 ) {
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
- $text = "<div class='noarticletext'>\n$text\n</div>";
- if( !$this->hasViewableContent() ) {
- // If there's no backing content, send a 404 Not Found
- // for better machine handling of broken links.
- $return404 = true;
- }
- }
-
- if( $return404 ) {
- $wgRequest->response()->header( "HTTP/1.x 404 Not Found" );
- }
-
- # Another whitelist check in case oldid is altering the title
- if( !$this->mTitle->userCanRead() ) {
- $wgOut->loginToUse();
- $wgOut->output();
- $wgOut->disable();
- wfProfileOut( __METHOD__ );
- return;
- }
-
- # For ?curid=x urls, disallow indexing
- if( $wgRequest->getInt('curid') )
- $wgOut->setRobotPolicy( 'noindex,follow' );
-
- # We're looking at an old revision
- if( !empty( $oldid ) ) {
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
- if( is_null( $this->mRevision ) ) {
- // FIXME: This would be a nice place to load the 'no such page' text.
- } else {
- $this->setOldSubtitle( isset($this->mOldId) ? $this->mOldId : $oldid );
- # Allow admins to see deleted content if explicitly requested
- if( $this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
- if( !$unhide || !$this->mRevision->userCan(Revision::DELETED_TEXT) ) {
- $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
- $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
- wfProfileOut( __METHOD__ );
- return;
- } else {
- $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
- // and we are allowed to see...
- }
- }
- // Is this the current revision and otherwise cacheable? Try the parser cache...
- if( $oldid === $this->getLatest() && $this->useParserCache( false )
- && $wgOut->tryParserCache( $this ) )
- {
- $outputDone = true;
- }
- }
- }
-
- // Ensure that UI elements requiring revision ID have
- // the correct version information.
- $wgOut->setRevisionId( $this->getRevIdFetched() );
-
- if( $outputDone ) {
- // do nothing...
- // Pages containing custom CSS or JavaScript get special treatment
- } else if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
- $wgOut->addHTML( wfMsgExt( 'clearyourcache', 'parse' ) );
- // Give hooks a chance to customise the output
- if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
- // Wrap the whole lot in a <pre> and don't parse
- $m = array();
- preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
- $wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
- $wgOut->addHTML( htmlspecialchars( $this->mContent ) );
- $wgOut->addHTML( "\n</pre>\n" );
- }
- } else if( $rt = Title::newFromRedirectArray( $text ) ) { # get an array of redirect targets
- # Don't append the subtitle if this was an old revision
- $wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
- $parseout = $wgParser->parse($text, $this->mTitle, ParserOptions::newFromUser($wgUser));
- $wgOut->addParserOutputNoText( $parseout );
- } else if( $pcache ) {
- # Display content and save to parser cache
- $this->outputWikiText( $text );
- } else {
- # Display content, don't attempt to save to parser cache
- # Don't show section-edit links on old revisions... this way lies madness.
- if( !$this->isCurrent() ) {
- $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
- }
- # Display content and don't save to parser cache
- # With timing hack -- TS 2006-07-26
- $time = -wfTime();
- $this->outputWikiText( $text, false );
- $time += wfTime();
-
- # Timing hack
- if( $time > 3 ) {
- wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
- $this->mTitle->getPrefixedDBkey()));
- }
-
- if( !$this->isCurrent() ) {
- $wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
- }
+ return true;
}
}
- /* title may have been set from the cache */
- $t = $wgOut->getPageTitle();
- if( empty( $t ) ) {
- $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ return false;
+ }
- # For the main page, overwrite the <title> element with the con-
- # tents of 'pagetitle-view-mainpage' instead of the default (if
- # that's not empty).
- if( $this->mTitle->equals( Title::newMainPage() ) &&
- wfMsgForContent( 'pagetitle-view-mainpage' ) !== '' ) {
- $wgOut->setHTMLTitle( wfMsgForContent( 'pagetitle-view-mainpage' ) );
+ /**
+ * Show a header specific to the namespace currently being viewed, like
+ * [[MediaWiki:Talkpagetext]]. For Article::view().
+ */
+ public function showNamespaceHeader() {
+ global $wgOut;
+ if ( $this->mTitle->isTalkPage() ) {
+ $msg = wfMsgNoTrans( 'talkpageheader' );
+ if ( $msg !== '-' && !wfEmptyMsg( 'talkpageheader', $msg ) ) {
+ $wgOut->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1</div>", array( 'talkpageheader' ) );
}
}
+ }
+ /**
+ * Show the footer section of an ordinary page view
+ */
+ public function showViewFooter() {
+ global $wgOut, $wgUseTrackbacks, $wgRequest;
# check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
- if( $ns == NS_USER_TALK && IP::isValid( $this->mTitle->getText() ) ) {
- $wgOut->addWikiMsg('anontalkpagetext');
+ if ( $this->mTitle->getNamespace() == NS_USER_TALK && IP::isValid( $this->mTitle->getText() ) ) {
+ $wgOut->addWikiMsg( 'anontalkpagetext' );
}
# If we have been passed an &rcid= parameter, we want to give the user a
# chance to mark this new article as patrolled.
- if( !empty($rcid) && $this->mTitle->exists() && $this->mTitle->quickUserCan('patrol') ) {
- $wgOut->addHTML(
- "<div class='patrollink'>" .
- wfMsgHtml( 'markaspatrolledlink',
- $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml('markaspatrolledtext'),
- "action=markpatrolled&rcid=$rcid" )
- ) .
- '</div>'
- );
- }
+ $this->showPatrolFooter();
# Trackbacks
- if( $wgUseTrackbacks ) {
+ if ( $wgUseTrackbacks ) {
$this->addTrackbacks();
}
+ }
- $this->viewUpdates();
- wfProfileOut( __METHOD__ );
+ /**
+ * If patrol is possible, output a patrol UI box. This is called from the
+ * footer section of ordinary page views. If patrol is not possible or not
+ * desired, does nothing.
+ */
+ public function showPatrolFooter() {
+ global $wgOut, $wgRequest, $wgUser;
+ $rcid = $wgRequest->getVal( 'rcid' );
+
+ if ( !$rcid || !$this->mTitle->exists() || !$this->mTitle->quickUserCan( 'patrol' ) ) {
+ return;
+ }
+
+ $sk = $wgUser->getSkin();
+
+ $wgOut->addHTML(
+ "<div class='patrollink'>" .
+ wfMsgHtml(
+ 'markaspatrolledlink',
+ $sk->link(
+ $this->mTitle,
+ wfMsgHtml( 'markaspatrolledtext' ),
+ array(),
+ array(
+ 'action' => 'markpatrolled',
+ 'rcid' => $rcid
+ ),
+ array( 'known', 'noclasses' )
+ )
+ ) .
+ '</div>'
+ );
}
-
- protected function showDeletionLog() {
- global $wgUser, $wgOut;
- $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut );
- $pager = new LogPager( $loglist, 'delete', false, $this->mTitle->getPrefixedText() );
- if( $pager->getNumRows() > 0 ) {
- $pager->mLimit = 10;
- $wgOut->addHTML( '<div class="mw-warning-with-logexcerpt">' );
- $wgOut->addWikiMsg( 'deleted-notice' );
- $wgOut->addHTML(
- $loglist->beginLogEventsList() .
- $pager->getBody() .
- $loglist->endLogEventsList()
- );
- if( $pager->getNumRows() > 10 ) {
- $wgOut->addHTML( $wgUser->getSkin()->link(
- SpecialPage::getTitleFor( 'Log' ),
- wfMsgHtml( 'deletelog-fulllog' ),
- array(),
- array( 'type' => 'delete', 'page' => $this->mTitle->getPrefixedText() )
- ) );
+
+ /**
+ * Show the error text for a missing article. For articles in the MediaWiki
+ * namespace, show the default message text. To be called from Article::view().
+ */
+ public function showMissingArticle() {
+ global $wgOut, $wgRequest, $wgUser;
+
+ # Show info in user (talk) namespace. Does the user exist? Is he blocked?
+ if ( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK ) {
+ $parts = explode( '/', $this->mTitle->getText() );
+ $rootPart = $parts[0];
+ $user = User::newFromName( $rootPart, false /* allow IP users*/ );
+ $ip = User::isIP( $rootPart );
+ if ( !$user->isLoggedIn() && !$ip ) { # User does not exist
+ $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1</div>",
+ array( 'userpage-userdoesnotexist-view', $rootPart ) );
+ } else if ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
+ LogEventsList::showLogExtract(
+ $wgOut,
+ 'block',
+ $user->getUserPage()->getPrefixedText(),
+ '',
+ array(
+ 'lim' => 1,
+ 'showIfEmpty' => false,
+ 'msgKey' => array(
+ 'blocked-notice-logextract',
+ $user->getName() # Support GENDER in notice
+ )
+ )
+ );
}
- $wgOut->addHTML( '</div>' );
+ }
+ wfRunHooks( 'ShowMissingArticle', array( $this ) );
+ # Show delete and move logs
+ LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle->getPrefixedText(), '',
+ array( 'lim' => 10,
+ 'conds' => array( "log_action != 'revision'" ),
+ 'showIfEmpty' => false,
+ 'msgKey' => array( 'moveddeleted-notice' ) )
+ );
+
+ # Show error message
+ $oldid = $this->getOldID();
+ if ( $oldid ) {
+ $text = wfMsgNoTrans( 'missing-article',
+ $this->mTitle->getPrefixedText(),
+ wfMsgNoTrans( 'missingarticle-rev', $oldid ) );
+ } elseif ( $this->mTitle->getNamespace() === NS_MEDIAWIKI ) {
+ // Use the default message text
+ $text = $this->getContent();
+ } else {
+ $createErrors = $this->mTitle->getUserPermissionsErrors( 'create', $wgUser );
+ $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
+ $errors = array_merge( $createErrors, $editErrors );
+
+ if ( !count( $errors ) )
+ $text = wfMsgNoTrans( 'noarticletext' );
+ else
+ $text = wfMsgNoTrans( 'noarticletext-nopermission' );
+ }
+ $text = "<div class='noarticletext'>\n$text\n</div>";
+ if ( !$this->hasViewableContent() ) {
+ // If there's no backing content, send a 404 Not Found
+ // for better machine handling of broken links.
+ $wgRequest->response()->header( "HTTP/1.x 404 Not Found" );
+ }
+ $wgOut->addWikiText( $text );
+ }
+
+ /**
+ * If the revision requested for view is deleted, check permissions.
+ * Send either an error message or a warning header to $wgOut.
+ * Returns true if the view is allowed, false if not.
+ */
+ public function showDeletedRevisionHeader() {
+ global $wgOut, $wgRequest;
+ if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
+ // Not deleted
+ return true;
+ }
+ // If the user is not allowed to see it...
+ if ( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n",
+ 'rev-deleted-text-permission' );
+ return false;
+ // If the user needs to confirm that they want to see it...
+ } else if ( $wgRequest->getInt( 'unhide' ) != 1 ) {
+ # Give explanation and add a link to view the revision...
+ $oldid = intval( $this->getOldID() );
+ $link = $this->mTitle->getFullUrl( "oldid={$oldid}&unhide=1" );
+ $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
+ 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n",
+ array( $msg, $link ) );
+ return false;
+ // We are allowed to see...
+ } else {
+ $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
+ 'rev-suppressed-text-view' : 'rev-deleted-text-view';
+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", $msg );
+ return true;
}
}
/*
* Should the parser cache be used?
*/
- protected function useParserCache( $oldid ) {
+ public function useParserCache( $oldid ) {
global $wgUser, $wgEnableParserCache;
return $wgEnableParserCache
@@ -1081,45 +1330,116 @@ class Article {
}
/**
+ * Execute the uncached parse for action=view
+ */
+ public function doViewParse() {
+ global $wgOut;
+ $oldid = $this->getOldID();
+ $useParserCache = $this->useParserCache( $oldid );
+ $parserOptions = clone $this->getParserOptions();
+ # Render printable version, use printable version cache
+ $parserOptions->setIsPrintable( $wgOut->isPrintable() );
+ # Don't show section-edit links on old revisions... this way lies madness.
+ $parserOptions->setEditSection( $this->isCurrent() );
+ $useParserCache = $this->useParserCache( $oldid );
+ $this->outputWikiText( $this->getContent(), $useParserCache, $parserOptions );
+ }
+
+ /**
+ * Try to fetch an expired entry from the parser cache. If it is present,
+ * output it and return true. If it is not present, output nothing and
+ * return false. This is used as a callback function for
+ * PoolCounter::executeProtected().
+ */
+ public function tryDirtyCache() {
+ global $wgOut;
+ $parserCache = ParserCache::singleton();
+ $options = $this->getParserOptions();
+ $options->setIsPrintable( $wgOut->isPrintable() );
+ $output = $parserCache->getDirty( $this, $options );
+ if ( $output ) {
+ wfDebug( __METHOD__ . ": sending dirty output\n" );
+ wfDebugLog( 'dirty', "dirty output " . $parserCache->getKey( $this, $options ) . "\n" );
+ $wgOut->setSquidMaxage( 0 );
+ $this->mParserOutput = $output;
+ $wgOut->addParserOutput( $output );
+ $wgOut->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" );
+ return true;
+ } else {
+ wfDebugLog( 'dirty', "dirty missing\n" );
+ wfDebug( __METHOD__ . ": no dirty cache\n" );
+ return false;
+ }
+ }
+
+ /**
+ * Show an error page for an error from the pool counter.
+ * @param $status Status
+ */
+ public function showPoolError( $status ) {
+ global $wgOut;
+ $wgOut->clearHTML(); // for release() errors
+ $wgOut->enableClientCache( false );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->addWikiText(
+ '<div class="errorbox">' .
+ $status->getWikiText( false, 'view-pool-error' ) .
+ '</div>'
+ );
+ }
+
+ /**
* View redirect
* @param $target Title object or Array of destination(s) to redirect
* @param $appendSubtitle Boolean [optional]
* @param $forceKnown Boolean: should the image be shown as a bluelink regardless of existence?
*/
public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
- global $wgParser, $wgOut, $wgContLang, $wgStylePath, $wgUser;
+ global $wgOut, $wgContLang, $wgStylePath, $wgUser;
# Display redirect
- if( !is_array( $target ) ) {
+ if ( !is_array( $target ) ) {
$target = array( $target );
}
- $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
+ $imageDir = $wgContLang->getDir();
$imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
$imageUrl2 = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
$alt2 = $wgContLang->isRTL() ? '&larr;' : '&rarr;'; // should -> and <- be used instead of entities?
-
- if( $appendSubtitle ) {
+
+ if ( $appendSubtitle ) {
$wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) );
}
$sk = $wgUser->getSkin();
// the loop prepends the arrow image before the link, so the first case needs to be outside
$title = array_shift( $target );
- if( $forceKnown ) {
- $link = $sk->makeKnownLinkObj( $title, htmlspecialchars( $title->getFullText() ) );
+ if ( $forceKnown ) {
+ $link = $sk->link(
+ $title,
+ htmlspecialchars( $title->getFullText() ),
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ );
} else {
- $link = $sk->makeLinkObj( $title, htmlspecialchars( $title->getFullText() ) );
+ $link = $sk->link( $title, htmlspecialchars( $title->getFullText() ) );
}
// automatically append redirect=no to each link, since most of them are redirect pages themselves
- foreach( $target as $rt ) {
- if( $forceKnown ) {
- $link .= '<img src="'.$imageUrl2.'" alt="'.$alt2.' " />'
- . $sk->makeKnownLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) );
+ foreach ( $target as $rt ) {
+ if ( $forceKnown ) {
+ $link .= '<img src="' . $imageUrl2 . '" alt="' . $alt2 . ' " />'
+ . $sk->link(
+ $rt,
+ htmlspecialchars( $rt->getFullText() ),
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ );
} else {
- $link .= '<img src="'.$imageUrl2.'" alt="'.$alt2.' " />'
- . $sk->makeLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) );
+ $link .= '<img src="' . $imageUrl2 . '" alt="' . $alt2 . ' " />'
+ . $sk->link( $rt, htmlspecialchars( $rt->getFullText() ) );
}
}
- return '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
- '<span class="redirectText">'.$link.'</span>';
+ return '<img src="' . $imageUrl . '" alt="#REDIRECT " />' .
+ '<span class="redirectText">' . $link . '</span>';
}
@@ -1127,46 +1447,46 @@ class Article {
global $wgOut, $wgUser;
$dbr = wfGetDB( DB_SLAVE );
$tbs = $dbr->select( 'trackbacks',
- array('tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name'),
- array('tb_page' => $this->getID() )
+ array( 'tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name' ),
+ array( 'tb_page' => $this->getID() )
);
- if( !$dbr->numRows($tbs) ) return;
+ if ( !$dbr->numRows( $tbs ) ) return;
$tbtext = "";
- while( $o = $dbr->fetchObject($tbs) ) {
+ while ( $o = $dbr->fetchObject( $tbs ) ) {
$rmvtxt = "";
- if( $wgUser->isAllowed( 'trackback' ) ) {
- $delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid=" .
+ if ( $wgUser->isAllowed( 'trackback' ) ) {
+ $delurl = $this->mTitle->getFullURL( "action=deletetrackback&tbid=" .
$o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
$rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
$tbtext .= "\n";
- $tbtext .= wfMsg(strlen($o->tb_ex) ? 'trackbackexcerpt' : 'trackback',
+ $tbtext .= wfMsg( strlen( $o->tb_ex ) ? 'trackbackexcerpt' : 'trackback',
$o->tb_title,
$o->tb_url,
$o->tb_ex,
$o->tb_name,
- $rmvtxt);
+ $rmvtxt );
}
$wgOut->wrapWikiMsg( "<div id='mw_trackbacks'>$1</div>\n", array( 'trackbackbox', $tbtext ) );
$this->mTitle->invalidateCache();
}
public function deletetrackback() {
- global $wgUser, $wgRequest, $wgOut, $wgTitle;
- if( !$wgUser->matchEditToken($wgRequest->getVal('token')) ) {
+ global $wgUser, $wgRequest, $wgOut;
+ if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ) ) ) {
$wgOut->addWikiMsg( 'sessionfailure' );
return;
}
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
- if( count($permission_errors) ) {
+ if ( count( $permission_errors ) ) {
$wgOut->showPermissionsErrorPage( $permission_errors );
return;
}
$db = wfGetDB( DB_MASTER );
- $db->delete( 'trackbacks', array('tb_id' => $wgRequest->getInt('tbid')) );
+ $db->delete( 'trackbacks', array( 'tb_id' => $wgRequest->getInt( 'tbid' ) ) );
$wgOut->addWikiMsg( 'trackbackdeleteok' );
$this->mTitle->invalidateCache();
@@ -1174,7 +1494,7 @@ class Article {
public function render() {
global $wgOut;
- $wgOut->setArticleBodyOnly(true);
+ $wgOut->setArticleBodyOnly( true );
$this->view();
}
@@ -1183,19 +1503,19 @@ class Article {
*/
public function purge() {
global $wgUser, $wgRequest, $wgOut;
- if( $wgUser->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) {
- if( wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
+ if ( $wgUser->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) {
+ if ( wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
$this->doPurge();
$this->view();
}
} else {
$action = htmlspecialchars( $wgRequest->getRequestURL() );
- $button = wfMsgExt( 'confirm_purge_button', array('escapenoentities') );
+ $button = wfMsgExt( 'confirm_purge_button', array( 'escapenoentities' ) );
$form = "<form method=\"post\" action=\"$action\">\n" .
"<input type=\"submit\" name=\"submit\" value=\"$button\" />\n" .
"</form>\n";
- $top = wfMsgExt( 'confirm-purge-top', array('parse') );
- $bottom = wfMsgExt( 'confirm-purge-bottom', array('parse') );
+ $top = wfMsgExt( 'confirm-purge-top', array( 'parse' ) );
+ $bottom = wfMsgExt( 'confirm-purge-bottom', array( 'parse' ) );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addHTML( $top . $form . $bottom );
@@ -1210,18 +1530,18 @@ class Article {
// Invalidate the cache
$this->mTitle->invalidateCache();
- if( $wgUseSquid ) {
+ if ( $wgUseSquid ) {
// Commit the transaction before the purge is sent
$dbw = wfGetDB( DB_MASTER );
- $dbw->immediateCommit();
+ $dbw->commit();
// Send purge
$update = SquidUpdate::newSimplePurge( $this->mTitle );
$update->doUpdate();
}
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
global $wgMessageCache;
- if( $this->getID() == 0 ) {
+ if ( $this->getID() == 0 ) {
$text = false;
} else {
$text = $this->getRawText();
@@ -1260,7 +1580,7 @@ class Article {
), __METHOD__, 'IGNORE' );
$affected = $dbw->affectedRows();
- if( $affected ) {
+ if ( $affected ) {
$newid = $dbw->insertId();
$this->mTitle->resetArticleId( $newid );
}
@@ -1279,7 +1599,7 @@ class Article {
* Giving 0 indicates the new page flag should be set
* on.
* @param $lastRevIsRedirect Boolean: if given, will optimize adding and
- * removing rows in redirect table.
+ * removing rows in redirect table.
* @return bool true on success, false on failure
* @private
*/
@@ -1290,7 +1610,7 @@ class Article {
$rt = Title::newFromRedirect( $text );
$conditions = array( 'page_id' => $this->getId() );
- if( !is_null( $lastRevision ) ) {
+ if ( !is_null( $lastRevision ) ) {
# An extra check against threads stepping on each other
$conditions['page_latest'] = $lastRevision;
}
@@ -1299,15 +1619,15 @@ class Article {
array( /* SET */
'page_latest' => $revision->getId(),
'page_touched' => $dbw->timestamp(),
- 'page_is_new' => ($lastRevision === 0) ? 1 : 0,
- 'page_is_redirect' => $rt !== NULL ? 1 : 0,
+ 'page_is_new' => ( $lastRevision === 0 ) ? 1 : 0,
+ 'page_is_redirect' => $rt !== null ? 1 : 0,
'page_len' => strlen( $text ),
),
$conditions,
__METHOD__ );
$result = $dbw->affectedRows() != 0;
- if( $result ) {
+ if ( $result ) {
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
}
@@ -1320,9 +1640,9 @@ class Article {
*
* @param $dbw Database
* @param $redirectTitle a title object pointing to the redirect target,
- * or NULL if this is not a redirect
+ * or NULL if this is not a redirect
* @param $lastRevIsRedirect If given, will optimize adding and
- * removing rows in redirect table.
+ * removing rows in redirect table.
* @return bool true on success, false on failure
* @private
*/
@@ -1330,10 +1650,10 @@ class Article {
// Always update redirects (target link might have changed)
// Update/Insert if we don't know if the last revision was a redirect or not
// Delete if changing from redirect to non-redirect
- $isRedirect = !is_null($redirectTitle);
- if($isRedirect || is_null($lastRevIsRedirect) || $lastRevIsRedirect !== $isRedirect) {
+ $isRedirect = !is_null( $redirectTitle );
+ if ( $isRedirect || is_null( $lastRevIsRedirect ) || $lastRevIsRedirect !== $isRedirect ) {
wfProfileIn( __METHOD__ );
- if( $isRedirect ) {
+ if ( $isRedirect ) {
// This title is a redirect, Add/Update row in the redirect table
$set = array( /* SET */
'rd_namespace' => $redirectTitle->getNamespace(),
@@ -1344,9 +1664,9 @@ class Article {
} else {
// This is not a redirect, remove row from redirect table
$where = array( 'rd_from' => $this->getId() );
- $dbw->delete( 'redirect', $where, __METHOD__);
+ $dbw->delete( 'redirect', $where, __METHOD__ );
}
- if( $this->getTitle()->getNamespace() == NS_FILE ) {
+ if ( $this->getTitle()->getNamespace() == NS_FILE ) {
RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() );
}
wfProfileOut( __METHOD__ );
@@ -1371,8 +1691,8 @@ class Article {
'page_id' => $this->getId(),
'page_latest=rev_id' ),
__METHOD__ );
- if( $row ) {
- if( wfTimestamp(TS_MW, $row->rev_timestamp) >= $revision->getTimestamp() ) {
+ if ( $row ) {
+ if ( wfTimestamp( TS_MW, $row->rev_timestamp ) >= $revision->getTimestamp() ) {
wfProfileOut( __METHOD__ );
return false;
}
@@ -1392,27 +1712,27 @@ class Article {
* @param $section empty/null/false or a section number (0, 1, 2, T1, T2...)
* @return string Complete article text, or null if error
*/
- public function replaceSection( $section, $text, $summary = '', $edittime = NULL ) {
+ public function replaceSection( $section, $text, $summary = '', $edittime = null ) {
wfProfileIn( __METHOD__ );
- if( strval( $section ) == '' ) {
+ if ( strval( $section ) == '' ) {
// Whole-page edit; let the whole text through
} else {
- if( is_null($edittime) ) {
+ if ( is_null( $edittime ) ) {
$rev = Revision::newFromTitle( $this->mTitle );
} else {
$dbw = wfGetDB( DB_MASTER );
$rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
}
- if( !$rev ) {
+ if ( !$rev ) {
wfDebug( "Article::replaceSection asked for bogus section (page: " .
$this->getId() . "; section: $section; edittime: $edittime)\n" );
return null;
}
$oldtext = $rev->getText();
- if( $section == 'new' ) {
+ if ( $section == 'new' ) {
# Inserting a new section
- $subject = $summary ? wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n" : '';
+ $subject = $summary ? wfMsgForContent( 'newsectionheaderdefaultlevel', $summary ) . "\n\n" : '';
$text = strlen( trim( $oldtext ) ) > 0
? "{$oldtext}\n\n{$subject}{$text}"
: "{$subject}{$text}";
@@ -1427,31 +1747,31 @@ class Article {
}
/**
- * @deprecated use Article::doEdit()
+ * This function is not deprecated until somebody fixes the core not to use
+ * it. Nevertheless, use Article::doEdit() instead.
*/
- function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC=false, $comment=false, $bot=false ) {
- wfDeprecated( __METHOD__ );
+ function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC = false, $comment = false, $bot = false ) {
$flags = EDIT_NEW | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
( $isminor ? EDIT_MINOR : 0 ) |
( $suppressRC ? EDIT_SUPPRESS_RC : 0 ) |
( $bot ? EDIT_FORCE_BOT : 0 );
# If this is a comment, add the summary as headline
- if( $comment && $summary != "" ) {
- $text = wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n".$text;
+ if ( $comment && $summary != "" ) {
+ $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $summary ) . "\n\n" . $text;
}
$this->doEdit( $text, $summary, $flags );
$dbw = wfGetDB( DB_MASTER );
- if($watchthis) {
- if(!$this->mTitle->userIsWatching()) {
+ if ( $watchthis ) {
+ if ( !$this->mTitle->userIsWatching() ) {
$dbw->begin();
$this->doWatch();
$dbw->commit();
}
} else {
- if( $this->mTitle->userIsWatching() ) {
+ if ( $this->mTitle->userIsWatching() ) {
$dbw->begin();
$this->doUnwatch();
$dbw->commit();
@@ -1464,25 +1784,24 @@ class Article {
* @deprecated use Article::doEdit()
*/
function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) {
- wfDeprecated( __METHOD__ );
$flags = EDIT_UPDATE | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
( $minor ? EDIT_MINOR : 0 ) |
( $forceBot ? EDIT_FORCE_BOT : 0 );
$status = $this->doEdit( $text, $summary, $flags );
- if( !$status->isOK() ) {
+ if ( !$status->isOK() ) {
return false;
}
$dbw = wfGetDB( DB_MASTER );
- if( $watchthis ) {
- if(!$this->mTitle->userIsWatching()) {
+ if ( $watchthis ) {
+ if ( !$this->mTitle->userIsWatching() ) {
$dbw->begin();
$this->doWatch();
$dbw->commit();
}
} else {
- if( $this->mTitle->userIsWatching() ) {
+ if ( $this->mTitle->userIsWatching() ) {
$dbw->begin();
$this->doUnwatch();
$dbw->commit();
@@ -1523,9 +1842,9 @@ class Article {
* Fill in blank summaries with generated text where possible
*
* If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
- * If EDIT_UPDATE is specified and the article doesn't exist, the function will an
- * edit-gone-missing error. If EDIT_NEW is specified and the article does exist, an
- * edit-already-exists error will be returned. These two conditions are also possible with
+ * If EDIT_UPDATE is specified and the article doesn't exist, the function will an
+ * edit-gone-missing error. If EDIT_NEW is specified and the article does exist, an
+ * edit-already-exists error will be returned. These two conditions are also possible with
* auto-detection due to MediaWiki's performance-optimised locking strategy.
*
* @param $baseRevId the revision ID this edit was based off, if any
@@ -1550,47 +1869,47 @@ class Article {
global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
# Low-level sanity check
- if( $this->mTitle->getText() == '' ) {
+ if ( $this->mTitle->getText() == '' ) {
throw new MWException( 'Something is trying to edit an article with an empty title' );
}
wfProfileIn( __METHOD__ );
- $user = is_null($user) ? $wgUser : $user;
+ $user = is_null( $user ) ? $wgUser : $user;
$status = Status::newGood( array() );
# Load $this->mTitle->getArticleID() and $this->mLatest if it's not already
- $this->loadPageData();
+ $this->loadPageData();
- if( !($flags & EDIT_NEW) && !($flags & EDIT_UPDATE) ) {
+ if ( !( $flags & EDIT_NEW ) && !( $flags & EDIT_UPDATE ) ) {
$aid = $this->mTitle->getArticleID();
- if( $aid ) {
+ if ( $aid ) {
$flags |= EDIT_UPDATE;
} else {
$flags |= EDIT_NEW;
}
}
- if( !wfRunHooks( 'ArticleSave', array( &$this, &$user, &$text, &$summary,
+ if ( !wfRunHooks( 'ArticleSave', array( &$this, &$user, &$text, &$summary,
$flags & EDIT_MINOR, null, null, &$flags, &$status ) ) )
{
wfDebug( __METHOD__ . ": ArticleSave hook aborted save!\n" );
wfProfileOut( __METHOD__ );
- if( $status->isOK() ) {
- $status->fatal( 'edit-hook-aborted');
+ if ( $status->isOK() ) {
+ $status->fatal( 'edit-hook-aborted' );
}
return $status;
}
# Silently ignore EDIT_MINOR if not allowed
- $isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed('minoredit');
+ $isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' );
$bot = $flags & EDIT_FORCE_BOT;
$oldtext = $this->getRawText(); // current revision
$oldsize = strlen( $oldtext );
# Provide autosummaries if one is not provided and autosummaries are enabled.
- if( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
+ if ( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
$summary = $this->getAutosummary( $oldtext, $text, $flags );
}
@@ -1600,12 +1919,13 @@ class Article {
$dbw = wfGetDB( DB_MASTER );
$now = wfTimestampNow();
+ $this->mTimestamp = $now;
- if( $flags & EDIT_UPDATE ) {
+ if ( $flags & EDIT_UPDATE ) {
# Update article, but only if changed.
$status->value['new'] = false;
# Make sure the revision is either completely inserted or not inserted at all
- if( !$wgDBtransactions ) {
+ if ( !$wgDBtransactions ) {
$userAbort = ignore_user_abort( true );
}
@@ -1613,14 +1933,14 @@ class Article {
$changed = ( strcmp( $text, $oldtext ) != 0 );
- if( $changed ) {
+ if ( $changed ) {
$this->mGoodAdjustment = (int)$this->isCountable( $text )
- (int)$this->isCountable( $oldtext );
$this->mTotalAdjustment = 0;
- if( !$this->mLatest ) {
+ if ( !$this->mLatest ) {
# Article gone missing
- wfDebug( __METHOD__.": EDIT_UPDATE specified but article doesn't exist\n" );
+ wfDebug( __METHOD__ . ": EDIT_UPDATE specified but article doesn't exist\n" );
$status->fatal( 'edit-gone-missing' );
wfProfileOut( __METHOD__ );
return $status;
@@ -1641,36 +1961,36 @@ class Article {
# Update page
#
- # Note that we use $this->mLatest instead of fetching a value from the master DB
- # during the course of this function. This makes sure that EditPage can detect
- # edit conflicts reliably, either by $ok here, or by $article->getTimestamp()
+ # Note that we use $this->mLatest instead of fetching a value from the master DB
+ # during the course of this function. This makes sure that EditPage can detect
+ # edit conflicts reliably, either by $ok here, or by $article->getTimestamp()
# before this function is called. A previous function used a separate query, this
# creates a window where concurrent edits can cause an ignored edit conflict.
$ok = $this->updateRevisionOn( $dbw, $revision, $this->mLatest );
- if( !$ok ) {
+ if ( !$ok ) {
/* Belated edit conflict! Run away!! */
$status->fatal( 'edit-conflict' );
# Delete the invalid revision if the DB is not transactional
- if( !$wgDBtransactions ) {
+ if ( !$wgDBtransactions ) {
$dbw->delete( 'revision', array( 'rev_id' => $revisionId ), __METHOD__ );
}
$revisionId = 0;
$dbw->rollback();
} else {
global $wgUseRCPatrol;
- wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, $baseRevId, $user) );
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
# Update recentchanges
- if( !( $flags & EDIT_SUPPRESS_RC ) ) {
+ if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
# Mark as patrolled if the user can do so
- $patrolled = $wgUseRCPatrol && $this->mTitle->userCan('autopatrol');
+ $patrolled = $wgUseRCPatrol && $this->mTitle->userCan( 'autopatrol' );
# Add RC row to the DB
$rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
$this->mLatest, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
$revisionId, $patrolled
);
# Log auto-patrolled edits
- if( $patrolled ) {
+ if ( $patrolled ) {
PatrolLog::record( $rc, true );
}
}
@@ -1687,11 +2007,11 @@ class Article {
$this->mTitle->invalidateCache();
}
- if( !$wgDBtransactions ) {
+ if ( !$wgDBtransactions ) {
ignore_user_abort( $userAbort );
}
// Now that ignore_user_abort is restored, we can respond to fatal errors
- if( !$status->isOK() ) {
+ if ( !$status->isOK() ) {
wfProfileOut( __METHOD__ );
return $status;
}
@@ -1717,7 +2037,7 @@ class Article {
# This will return false if the article already exists
$newid = $this->insertOn( $dbw );
- if( $newid === false ) {
+ if ( $newid === false ) {
$dbw->rollback();
$status->fatal( 'edit-already-exists' );
wfProfileOut( __METHOD__ );
@@ -1740,17 +2060,17 @@ class Article {
# Update the page record with revision data
$this->updateRevisionOn( $dbw, $revision, 0 );
- wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false, $user) );
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
# Update recentchanges
- if( !( $flags & EDIT_SUPPRESS_RC ) ) {
+ if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
global $wgUseRCPatrol, $wgUseNPPatrol;
# Mark as patrolled if the user can do so
- $patrolled = ($wgUseRCPatrol || $wgUseNPPatrol) && $this->mTitle->userCan('autopatrol');
+ $patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) && $this->mTitle->userCan( 'autopatrol' );
# Add RC row to the DB
$rc = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot,
- '', strlen($text), $revisionId, $patrolled );
+ '', strlen( $text ), $revisionId, $patrolled );
# Log auto-patrolled edits
- if( $patrolled ) {
+ if ( $patrolled ) {
PatrolLog::record( $rc, true );
}
}
@@ -1768,7 +2088,7 @@ class Article {
}
# Do updates right now unless deferral was requested
- if( !( $flags & EDIT_DEFER_UPDATES ) ) {
+ if ( !( $flags & EDIT_DEFER_UPDATES ) ) {
wfDoUpdates();
}
@@ -1800,9 +2120,9 @@ class Article {
*/
public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
global $wgOut;
- if( $noRedir ) {
+ if ( $noRedir ) {
$query = 'redirect=no';
- if( $extraQuery )
+ if ( $extraQuery )
$query .= "&$query";
} else {
$query = $extraQuery;
@@ -1818,63 +2138,62 @@ class Article {
$wgOut->setRobotPolicy( 'noindex,nofollow' );
# If we haven't been given an rc_id value, we can't do anything
- $rcid = (int) $wgRequest->getVal('rcid');
- $rc = RecentChange::newFromId($rcid);
- if( is_null($rc) ) {
+ $rcid = (int) $wgRequest->getVal( 'rcid' );
+ $rc = RecentChange::newFromId( $rcid );
+ if ( is_null( $rc ) ) {
$wgOut->showErrorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
return;
}
- #It would be nice to see where the user had actually come from, but for now just guess
+ # It would be nice to see where the user had actually come from, but for now just guess
$returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
$return = SpecialPage::getTitleFor( $returnto );
$dbw = wfGetDB( DB_MASTER );
$errors = $rc->doMarkPatrolled();
- if( in_array(array('rcpatroldisabled'), $errors) ) {
+ if ( in_array( array( 'rcpatroldisabled' ), $errors ) ) {
$wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
-
- if( in_array(array('hookaborted'), $errors) ) {
+
+ if ( in_array( array( 'hookaborted' ), $errors ) ) {
// The hook itself has handled any output
return;
}
-
- if( in_array(array('markedaspatrollederror-noautopatrol'), $errors) ) {
+
+ if ( in_array( array( 'markedaspatrollederror-noautopatrol' ), $errors ) ) {
$wgOut->setPageTitle( wfMsg( 'markedaspatrollederror' ) );
$wgOut->addWikiMsg( 'markedaspatrollederror-noautopatrol' );
$wgOut->returnToMain( false, $return );
return;
}
- if( !empty($errors) ) {
+ if ( !empty( $errors ) ) {
$wgOut->showPermissionsErrorPage( $errors );
return;
}
# Inform the user
$wgOut->setPageTitle( wfMsg( 'markedaspatrolled' ) );
- $wgOut->addWikiMsg( 'markedaspatrolledtext' );
+ $wgOut->addWikiMsg( 'markedaspatrolledtext', $rc->getTitle()->getPrefixedText() );
$wgOut->returnToMain( false, $return );
}
/**
* User-interface handler for the "watch" action
*/
-
public function watch() {
global $wgUser, $wgOut;
- if( $wgUser->isAnon() ) {
+ if ( $wgUser->isAnon() ) {
$wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
return;
}
- if( wfReadOnly() ) {
+ if ( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
- if( $this->doWatch() ) {
+ if ( $this->doWatch() ) {
$wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'addedwatchtext', $this->mTitle->getPrefixedText() );
@@ -1888,12 +2207,12 @@ class Article {
*/
public function doWatch() {
global $wgUser;
- if( $wgUser->isAnon() ) {
+ if ( $wgUser->isAnon() ) {
return false;
}
- if( wfRunHooks('WatchArticle', array(&$wgUser, &$this)) ) {
+ if ( wfRunHooks( 'WatchArticle', array( &$wgUser, &$this ) ) ) {
$wgUser->addWatch( $this->mTitle );
- return wfRunHooks('WatchArticleComplete', array(&$wgUser, &$this));
+ return wfRunHooks( 'WatchArticleComplete', array( &$wgUser, &$this ) );
}
return false;
}
@@ -1903,15 +2222,15 @@ class Article {
*/
public function unwatch() {
global $wgUser, $wgOut;
- if( $wgUser->isAnon() ) {
+ if ( $wgUser->isAnon() ) {
$wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
return;
}
- if( wfReadOnly() ) {
+ if ( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
- if( $this->doUnwatch() ) {
+ if ( $this->doUnwatch() ) {
$wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'removedwatchtext', $this->mTitle->getPrefixedText() );
@@ -1925,12 +2244,12 @@ class Article {
*/
public function doUnwatch() {
global $wgUser;
- if( $wgUser->isAnon() ) {
+ if ( $wgUser->isAnon() ) {
return false;
}
- if( wfRunHooks('UnwatchArticle', array(&$wgUser, &$this)) ) {
+ if ( wfRunHooks( 'UnwatchArticle', array( &$wgUser, &$this ) ) ) {
$wgUser->removeWatch( $this->mTitle );
- return wfRunHooks('UnwatchArticleComplete', array(&$wgUser, &$this));
+ return wfRunHooks( 'UnwatchArticleComplete', array( &$wgUser, &$this ) );
}
return false;
}
@@ -1960,25 +2279,27 @@ class Article {
* @return bool true on success
*/
public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
- global $wgUser, $wgRestrictionTypes, $wgContLang;
+ global $wgUser, $wgContLang;
+
+ $restrictionTypes = $this->mTitle->getRestrictionTypes();
$id = $this->mTitle->getArticleID();
if ( $id <= 0 ) {
wfDebug( "updateRestrictions failed: $id <= 0\n" );
return false;
}
-
+
if ( wfReadOnly() ) {
wfDebug( "updateRestrictions failed: read-only\n" );
return false;
}
-
+
if ( !$this->mTitle->userCan( 'protect' ) ) {
wfDebug( "updateRestrictions failed: insufficient permissions\n" );
return false;
}
- if( !$cascade ) {
+ if ( !$cascade ) {
$cascade = false;
}
@@ -1990,17 +2311,17 @@ class Article {
$current = array();
$updated = Article::flattenRestrictions( $limit );
$changed = false;
- foreach( $wgRestrictionTypes as $action ) {
- if( isset( $expiry[$action] ) ) {
+ foreach ( $restrictionTypes as $action ) {
+ if ( isset( $expiry[$action] ) ) {
# Get current restrictions on $action
$aLimits = $this->mTitle->getRestrictions( $action );
$current[$action] = implode( '', $aLimits );
# Are any actual restrictions being dealt with here?
- $aRChanged = count($aLimits) || !empty($limit[$action]);
+ $aRChanged = count( $aLimits ) || !empty( $limit[$action] );
# If something changed, we need to log it. Checking $aRChanged
# assures that "unprotecting" a page that is not protected does
# not log just because the expiry was "changed".
- if( $aRChanged && $this->mTitle->mRestrictionsExpiry[$action] != $expiry[$action] ) {
+ if ( $aRChanged && $this->mTitle->mRestrictionsExpiry[$action] != $expiry[$action] ) {
$changed = true;
}
}
@@ -2008,19 +2329,19 @@ class Article {
$current = Article::flattenRestrictions( $current );
- $changed = ($changed || $current != $updated );
- $changed = $changed || ($updated && $this->mTitle->areRestrictionsCascading() != $cascade);
+ $changed = ( $changed || $current != $updated );
+ $changed = $changed || ( $updated && $this->mTitle->areRestrictionsCascading() != $cascade );
$protect = ( $updated != '' );
# If nothing's changed, do nothing
- if( $changed ) {
- if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
+ if ( $changed ) {
+ if ( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
$dbw = wfGetDB( DB_MASTER );
-
+
# Prepare a null revision to be added to the history
$modified = $current != '' && $protect;
- if( $protect ) {
+ if ( $protect ) {
$comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle';
} else {
$comment_type = 'unprotectedarticle';
@@ -2031,51 +2352,51 @@ class Article {
# Otherwise, people who cannot normally protect can "protect" pages via transclusion
$editrestriction = isset( $limit['edit'] ) ? array( $limit['edit'] ) : $this->mTitle->getRestrictions( 'edit' );
# The schema allows multiple restrictions
- if(!in_array('protect', $editrestriction) && !in_array('sysop', $editrestriction))
+ if ( !in_array( 'protect', $editrestriction ) && !in_array( 'sysop', $editrestriction ) )
$cascade = false;
- $cascade_description = '';
- if( $cascade ) {
- $cascade_description = ' ['.wfMsgForContent('protect-summary-cascade').']';
+ $cascade_description = '';
+ if ( $cascade ) {
+ $cascade_description = ' [' . wfMsgForContent( 'protect-summary-cascade' ) . ']';
}
- if( $reason )
+ if ( $reason )
$comment .= ": $reason";
$editComment = $comment;
$encodedExpiry = array();
$protect_description = '';
- foreach( $limit as $action => $restrictions ) {
- if ( !isset($expiry[$action]) )
+ foreach ( $limit as $action => $restrictions ) {
+ if ( !isset( $expiry[$action] ) )
$expiry[$action] = 'infinite';
-
- $encodedExpiry[$action] = Block::encodeExpiry($expiry[$action], $dbw );
- if( $restrictions != '' ) {
+
+ $encodedExpiry[$action] = Block::encodeExpiry( $expiry[$action], $dbw );
+ if ( $restrictions != '' ) {
$protect_description .= "[$action=$restrictions] (";
- if( $encodedExpiry[$action] != 'infinity' ) {
- $protect_description .= wfMsgForContent( 'protect-expiring',
+ if ( $encodedExpiry[$action] != 'infinity' ) {
+ $protect_description .= wfMsgForContent( 'protect-expiring',
$wgContLang->timeanddate( $expiry[$action], false, false ) ,
$wgContLang->date( $expiry[$action], false, false ) ,
- $wgContLang->time( $expiry[$action], false, false ) );
+ $wgContLang->time( $expiry[$action], false, false ) );
} else {
$protect_description .= wfMsgForContent( 'protect-expiry-indefinite' );
}
$protect_description .= ') ';
}
}
- $protect_description = trim($protect_description);
-
- if( $protect_description && $protect )
+ $protect_description = trim( $protect_description );
+
+ if ( $protect_description && $protect )
$editComment .= " ($protect_description)";
- if( $cascade )
+ if ( $cascade )
$editComment .= "$cascade_description";
# Update restrictions table
- foreach( $limit as $action => $restrictions ) {
- if($restrictions != '' ) {
- $dbw->replace( 'page_restrictions', array(array('pr_page', 'pr_type')),
- array( 'pr_page' => $id,
- 'pr_type' => $action,
- 'pr_level' => $restrictions,
- 'pr_cascade' => ($cascade && $action == 'edit') ? 1 : 0,
+ foreach ( $limit as $action => $restrictions ) {
+ if ( $restrictions != '' ) {
+ $dbw->replace( 'page_restrictions', array( array( 'pr_page', 'pr_type' ) ),
+ array( 'pr_page' => $id,
+ 'pr_type' => $action,
+ 'pr_level' => $restrictions,
+ 'pr_cascade' => ( $cascade && $action == 'edit' ) ? 1 : 0,
'pr_expiry' => $encodedExpiry[$action] ), __METHOD__ );
} else {
$dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
@@ -2099,14 +2420,14 @@ class Article {
), 'Article::protect'
);
- wfRunHooks( 'NewRevisionFromEditComplete', array($this, $nullRevision, $latest, $wgUser) );
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $nullRevision, $latest, $wgUser ) );
wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, $limit, $reason ) );
# Update the protection log
$log = new LogPage( 'protect' );
- if( $protect ) {
- $params = array($protect_description,$cascade ? 'cascade' : '');
- $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason), $params );
+ if ( $protect ) {
+ $params = array( $protect_description, $cascade ? 'cascade' : '' );
+ $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason ), $params );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
@@ -2124,13 +2445,13 @@ class Article {
* @return String
*/
protected static function flattenRestrictions( $limit ) {
- if( !is_array( $limit ) ) {
+ if ( !is_array( $limit ) ) {
throw new MWException( 'Article::flattenRestrictions given non-array restriction set' );
}
$bits = array();
ksort( $limit );
- foreach( $limit as $action => $restrictions ) {
- if( $restrictions != '' ) {
+ foreach ( $limit as $action => $restrictions ) {
+ if ( $restrictions != '' ) {
$bits[] = "$action=$restrictions";
}
}
@@ -2146,7 +2467,7 @@ class Article {
$dbw = wfGetDB( DB_MASTER );
// Get the last revision
$rev = Revision::newFromTitle( $this->mTitle );
- if( is_null( $rev ) )
+ if ( is_null( $rev ) )
return false;
// Get the article's contents
@@ -2154,9 +2475,9 @@ class Article {
$blank = false;
// If the page is blank, use the text from the previous revision,
// which can only be blank if there's a move/import/protect dummy revision involved
- if( $contents == '' ) {
+ if ( $contents == '' ) {
$prev = $rev->getPrevious();
- if( $prev ) {
+ if ( $prev ) {
$contents = $prev->getText();
$blank = true;
}
@@ -2164,23 +2485,21 @@ class Article {
// Find out if there was only one contributor
// Only scan the last 20 revisions
- $limit = 20;
$res = $dbw->select( 'revision', 'rev_user_text',
- array( 'rev_page' => $this->getID() ), __METHOD__,
- array( 'LIMIT' => $limit )
+ array( 'rev_page' => $this->getID(), $dbw->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' ),
+ __METHOD__,
+ array( 'LIMIT' => 20 )
);
- if( $res === false )
+ if ( $res === false )
// This page has no revisions, which is very weird
return false;
- if( $res->numRows() > 1 )
- $hasHistory = true;
- else
- $hasHistory = false;
+
+ $hasHistory = ( $res->numRows() > 1 );
$row = $dbw->fetchObject( $res );
$onlyAuthor = $row->rev_user_text;
// Try to find a second contributor
- foreach( $res as $row ) {
- if( $row->rev_user_text != $onlyAuthor ) {
+ foreach ( $res as $row ) {
+ if ( $row->rev_user_text != $onlyAuthor ) {
$onlyAuthor = false;
break;
}
@@ -2188,18 +2507,18 @@ class Article {
$dbw->freeResult( $res );
// Generate the summary with a '$1' placeholder
- if( $blank ) {
+ if ( $blank ) {
// The current revision is blank and the one before is also
// blank. It's just not our lucky day
$reason = wfMsgForContent( 'exbeforeblank', '$1' );
} else {
- if( $onlyAuthor )
+ if ( $onlyAuthor )
$reason = wfMsgForContent( 'excontentauthor', '$1', $onlyAuthor );
else
$reason = wfMsgForContent( 'excontent', '$1' );
}
-
- if( $reason == '-' ) {
+
+ if ( $reason == '-' ) {
// Allow these UI messages to be blanked out cleanly
return '';
}
@@ -2208,7 +2527,7 @@ class Article {
$contents = preg_replace( "/[\n\r]/", ' ', $contents );
// Calculate the maximum amount of chars to get
// Max content length = max comment length - length of the comment (excl. $1) - '...'
- $maxLength = 255 - (strlen( $reason ) - 2) - 3;
+ $maxLength = 255 - ( strlen( $reason ) - 2 ) - 3;
$contents = $wgContLang->truncate( $contents, $maxLength );
// Remove possible unfinished links
$contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
@@ -2232,10 +2551,10 @@ class Article {
$reason = $this->DeleteReasonList;
- if( $reason != 'other' && $this->DeleteReason != '' ) {
+ if ( $reason != 'other' && $this->DeleteReason != '' ) {
// Entry from drop down menu + additional comment
$reason .= wfMsgForContent( 'colon-separator' ) . $this->DeleteReason;
- } elseif( $reason == 'other' ) {
+ } elseif ( $reason == 'other' ) {
$reason = $this->DeleteReason;
}
# Flag to hide all contents of the archived revisions
@@ -2244,7 +2563,7 @@ class Article {
# This code desperately needs to be totally rewritten
# Read-only check...
- if( wfReadOnly() ) {
+ if ( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
@@ -2252,7 +2571,7 @@ class Article {
# Check permissions
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
- if( count( $permission_errors ) > 0 ) {
+ if ( count( $permission_errors ) > 0 ) {
$wgOut->showPermissionsErrorPage( $permission_errors );
return;
}
@@ -2263,27 +2582,37 @@ class Article {
$dbw = wfGetDB( DB_MASTER );
$conds = $this->mTitle->pageCond();
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
- if( $latest === false ) {
- $wgOut->showFatalError( wfMsgExt( 'cannotdelete', array( 'parse' ) ) );
+ if ( $latest === false ) {
+ $wgOut->showFatalError(
+ Html::rawElement(
+ 'div',
+ array( 'class' => 'error mw-error-cannotdelete' ),
+ wfMsgExt( 'cannotdelete', array( 'parse' ), $this->mTitle->getPrefixedText() )
+ )
+ );
$wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
- LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
+ LogEventsList::showLogExtract(
+ $wgOut,
+ 'delete',
+ $this->mTitle->getPrefixedText()
+ );
return;
}
# Hack for big sites
$bigHistory = $this->isBigDeletion();
- if( $bigHistory && !$this->mTitle->userCan( 'bigdelete' ) ) {
+ if ( $bigHistory && !$this->mTitle->userCan( 'bigdelete' ) ) {
global $wgLang, $wgDeleteRevisionsLimit;
$wgOut->wrapWikiMsg( "<div class='error'>\n$1</div>\n",
array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
return;
}
- if( $confirm ) {
+ if ( $confirm ) {
$this->doDelete( $reason, $suppress );
- if( $wgRequest->getCheck( 'wpWatch' ) ) {
+ if ( $wgRequest->getCheck( 'wpWatch' ) && $wgUser->isLoggedIn() ) {
$this->doWatch();
- } elseif( $this->mTitle->userIsWatching() ) {
+ } elseif ( $this->mTitle->userIsWatching() ) {
$this->doUnwatch();
}
return;
@@ -2291,14 +2620,20 @@ class Article {
// Generate deletion reason
$hasHistory = false;
- if( !$reason ) $reason = $this->generateReason($hasHistory);
+ if ( !$reason ) $reason = $this->generateReason( $hasHistory );
// If the page has a history, insert a warning
- if( $hasHistory && !$confirm ) {
+ if ( $hasHistory && !$confirm ) {
+ global $wgLang;
$skin = $wgUser->getSkin();
- $wgOut->addHTML( '<strong>' . wfMsgExt( 'historywarning', array( 'parseinline' ) ) . ' ' . $skin->historyLink() . '</strong>' );
- if( $bigHistory ) {
- global $wgLang, $wgDeleteRevisionsLimit;
+ $revisions = $this->estimateRevisionCount();
+ $wgOut->addHTML( '<strong class="mw-delete-warning-revisions">' .
+ wfMsgExt( 'historywarning', array( 'parseinline' ), $wgLang->formatNum( $revisions ) ) .
+ wfMsgHtml( 'word-separator' ) . $skin->historyLink() .
+ '</strong>'
+ );
+ if ( $bigHistory ) {
+ global $wgDeleteRevisionsLimit;
$wgOut->wrapWikiMsg( "<div class='error'>\n$1</div>\n",
array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
}
@@ -2312,7 +2647,7 @@ class Article {
*/
public function isBigDeletion() {
global $wgDeleteRevisionsLimit;
- if( $wgDeleteRevisionsLimit ) {
+ if ( $wgDeleteRevisionsLimit ) {
$revCount = $this->estimateRevisionCount();
return $revCount > $wgDeleteRevisionsLimit;
}
@@ -2325,10 +2660,10 @@ class Article {
public function estimateRevisionCount() {
$dbr = wfGetDB( DB_SLAVE );
// For an exact count...
- //return $dbr->selectField( 'revision', 'COUNT(*)',
+ // return $dbr->selectField( 'revision', 'COUNT(*)',
// array( 'rev_page' => $this->getId() ), __METHOD__ );
return $dbr->estimateRowCount( 'revision', '*',
- array( 'rev_page' => $this->getId() ), __METHOD__ );
+ array( 'rev_page' => $this->getId() ), __METHOD__ );
}
/**
@@ -2355,12 +2690,12 @@ class Article {
'LIMIT' => $num
) )
);
- if( !$res ) {
+ if ( !$res ) {
wfProfileOut( __METHOD__ );
return array();
}
$row = $db->fetchObject( $res );
- if( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
+ if ( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
$db = wfGetDB( DB_MASTER );
$continue--;
} else {
@@ -2385,24 +2720,33 @@ class Article {
wfDebug( "Article::confirmDelete\n" );
- $wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->mTitle ) ) );
+ $deleteBackLink = $wgUser->getSkin()->link(
+ $this->mTitle,
+ null,
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ );
+ $wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $deleteBackLink ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'confirmdeletetext' );
- if( $wgUser->isAllowed( 'suppressrevision' ) ) {
+ wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) );
+
+ if ( $wgUser->isAllowed( 'suppressrevision' ) ) {
$suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\">
<td></td>
- <td class='mw-input'>" .
+ <td class='mw-input'><strong>" .
Xml::checkLabel( wfMsg( 'revdelete-suppress' ),
'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) .
- "</td>
+ "</strong></td>
</tr>";
} else {
$suppress = '';
}
$checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching();
- $form = Xml::openElement( 'form', array( 'method' => 'post',
+ $form = Xml::openElement( 'form', array( 'method' => 'post',
'action' => $this->mTitle->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
Xml::tags( 'legend', null, wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) ) .
@@ -2422,17 +2766,27 @@ class Article {
Xml::label( wfMsg( 'deleteotherreason' ), 'wpReason' ) .
"</td>
<td class='mw-input'>" .
- Xml::input( 'wpReason', 60, $reason, array( 'type' => 'text', 'maxlength' => '255',
- 'tabindex' => '2', 'id' => 'wpReason' ) ) .
+ Html::input( 'wpReason', $reason, 'text', array(
+ 'size' => '60',
+ 'maxlength' => '255',
+ 'tabindex' => '2',
+ 'id' => 'wpReason',
+ 'autofocus'
+ ) ) .
"</td>
- </tr>
+ </tr>";
+ # Dissalow watching is user is not logged in
+ if ( $wgUser->isLoggedIn() ) {
+ $form .= "
<tr>
<td></td>
<td class='mw-input'>" .
Xml::checkLabel( wfMsg( 'watchthis' ),
'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
"</td>
- </tr>
+ </tr>";
+ }
+ $form .= "
$suppress
<tr>
<td></td>
@@ -2446,14 +2800,25 @@ class Article {
Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
Xml::closeElement( 'form' );
- if( $wgUser->isAllowed( 'editinterface' ) ) {
+ if ( $wgUser->isAllowed( 'editinterface' ) ) {
$skin = $wgUser->getSkin();
- $link = $skin->makeLink ( 'MediaWiki:Deletereason-dropdown', wfMsgHtml( 'delete-edit-reasonlist' ) );
+ $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' );
+ $link = $skin->link(
+ $title,
+ wfMsgHtml( 'delete-edit-reasonlist' ),
+ array(),
+ array( 'action' => 'edit' )
+ );
$form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
}
$wgOut->addHTML( $form );
- LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
+ $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
+ LogEventsList::showLogExtract(
+ $wgOut,
+ 'delete',
+ $this->mTitle->getPrefixedText()
+ );
}
/**
@@ -2464,8 +2829,8 @@ class Article {
$id = $this->mTitle->getArticleID( GAID_FOR_UPDATE );
$error = '';
- if( wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason, &$error)) ) {
- if( $this->doDeleteArticle( $reason, $suppress, $id ) ) {
+ if ( wfRunHooks( 'ArticleDelete', array( &$this, &$wgUser, &$reason, &$error ) ) ) {
+ if ( $this->doDeleteArticle( $reason, $suppress, $id ) ) {
$deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
@@ -2475,15 +2840,25 @@ class Article {
$wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
$wgOut->returnToMain( false );
- wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason, $id));
+ wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$wgUser, $reason, $id ) );
+ }
+ } else {
+ if ( $error == '' ) {
+ $wgOut->showFatalError(
+ Html::rawElement(
+ 'div',
+ array( 'class' => 'error mw-error-cannotdelete' ),
+ wfMsgExt( 'cannotdelete', array( 'parse' ), $this->mTitle->getPrefixedText() )
+ )
+ );
+ $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
+ LogEventsList::showLogExtract(
+ $wgOut,
+ 'delete',
+ $this->mTitle->getPrefixedText()
+ );
} else {
- if( $error == '' ) {
- $wgOut->showFatalError( wfMsgExt( 'cannotdelete', array( 'parse' ) ) );
- $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
- LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
- } else {
- $wgOut->showFatalError( $error );
- }
+ $wgOut->showFatalError( $error );
}
}
}
@@ -2497,22 +2872,22 @@ class Article {
global $wgUseSquid, $wgDeferredUpdateList;
global $wgUseTrackbacks;
- wfDebug( __METHOD__."\n" );
+ wfDebug( __METHOD__ . "\n" );
$dbw = wfGetDB( DB_MASTER );
$ns = $this->mTitle->getNamespace();
$t = $this->mTitle->getDBkey();
$id = $id ? $id : $this->mTitle->getArticleID( GAID_FOR_UPDATE );
- if( $t == '' || $id == 0 ) {
+ if ( $t == '' || $id == 0 ) {
return false;
}
- $u = new SiteStatsUpdate( 0, 1, -(int)$this->isCountable( $this->getRawText() ), -1 );
+ $u = new SiteStatsUpdate( 0, 1, - (int)$this->isCountable( $this->getRawText() ), -1 );
array_push( $wgDeferredUpdateList, $u );
// Bitfields to further suppress the content
- if( $suppress ) {
+ if ( $suppress ) {
$bitfield = 0;
// This should be 15...
$bitfield |= Revision::DELETED_TEXT;
@@ -2560,26 +2935,26 @@ class Article {
$dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
# Now that it's safely backed up, delete it
- $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
+ $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__ );
$ok = ( $dbw->affectedRows() > 0 ); // getArticleId() uses slave, could be laggy
- if( !$ok ) {
+ if ( !$ok ) {
$dbw->rollback();
return false;
}
-
+
# Fix category table counts
$cats = array();
$res = $dbw->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
- foreach( $res as $row ) {
- $cats []= $row->cl_to;
+ foreach ( $res as $row ) {
+ $cats [] = $row->cl_to;
}
$this->updateCategoryCounts( array(), $cats );
# If using cascading deletes, we can skip some explicit deletes
- if( !$dbw->cascadingDeletes() ) {
+ if ( !$dbw->cascadingDeletes() ) {
$dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
- if($wgUseTrackbacks)
+ if ( $wgUseTrackbacks )
$dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
# Delete outgoing links
@@ -2593,15 +2968,15 @@ class Article {
}
# If using cleanup triggers, we can skip some manual deletes
- if( !$dbw->cleanupTriggers() ) {
+ if ( !$dbw->cleanupTriggers() ) {
# Clean up recentchanges entries...
$dbw->delete( 'recentchanges',
- array( 'rc_type != '.RC_LOG,
+ array( 'rc_type != ' . RC_LOG,
'rc_namespace' => $this->mTitle->getNamespace(),
- 'rc_title' => $this->mTitle->getDBKey() ),
+ 'rc_title' => $this->mTitle->getDBkey() ),
__METHOD__ );
$dbw->delete( 'recentchanges',
- array( 'rc_type != '.RC_LOG, 'rc_cur_id' => $id ),
+ array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
__METHOD__ );
}
@@ -2653,17 +3028,17 @@ class Article {
$rollbackErrors = $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser );
$errors = array_merge( $editErrors, wfArrayDiff2( $rollbackErrors, $editErrors ) );
- if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
+ if ( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
$errors[] = array( 'sessionfailure' );
- if( $wgUser->pingLimiter( 'rollback' ) || $wgUser->pingLimiter() ) {
+ if ( $wgUser->pingLimiter( 'rollback' ) || $wgUser->pingLimiter() ) {
$errors[] = array( 'actionthrottledtext' );
}
# If there were errors, bail out now
- if( !empty( $errors ) )
+ if ( !empty( $errors ) )
return $errors;
- return $this->commitRollback($fromP, $summary, $bot, $resultDetails);
+ return $this->commitRollback( $fromP, $summary, $bot, $resultDetails );
}
/**
@@ -2675,95 +3050,102 @@ class Article {
* ly if you want to use custom permissions checks. If you don't, use
* doRollback() instead.
*/
- public function commitRollback($fromP, $summary, $bot, &$resultDetails) {
+ public function commitRollback( $fromP, $summary, $bot, &$resultDetails ) {
global $wgUseRCPatrol, $wgUser, $wgLang;
$dbw = wfGetDB( DB_MASTER );
- if( wfReadOnly() ) {
+ if ( wfReadOnly() ) {
return array( array( 'readonlytext' ) );
}
# Get the last editor
$current = Revision::newFromTitle( $this->mTitle );
- if( is_null( $current ) ) {
+ if ( is_null( $current ) ) {
# Something wrong... no page?
- return array(array('notanarticle'));
+ return array( array( 'notanarticle' ) );
}
$from = str_replace( '_', ' ', $fromP );
- if( $from != $current->getUserText() ) {
+ # User name given should match up with the top revision.
+ # If the user was deleted then $from should be empty.
+ if ( $from != $current->getUserText() ) {
$resultDetails = array( 'current' => $current );
- return array(array('alreadyrolled',
- htmlspecialchars($this->mTitle->getPrefixedText()),
- htmlspecialchars($fromP),
- htmlspecialchars($current->getUserText())
- ));
+ return array( array( 'alreadyrolled',
+ htmlspecialchars( $this->mTitle->getPrefixedText() ),
+ htmlspecialchars( $fromP ),
+ htmlspecialchars( $current->getUserText() )
+ ) );
}
- # Get the last edit not by this guy
- $user = intval( $current->getUser() );
- $user_text = $dbw->addQuotes( $current->getUserText() );
+ # Get the last edit not by this guy...
+ # Note: these may not be public values
+ $user = intval( $current->getRawUser() );
+ $user_text = $dbw->addQuotes( $current->getRawUserText() );
$s = $dbw->selectRow( 'revision',
array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
- array( 'rev_page' => $current->getPage(),
+ array( 'rev_page' => $current->getPage(),
"rev_user != {$user} OR rev_user_text != {$user_text}"
), __METHOD__,
- array( 'USE INDEX' => 'page_timestamp',
+ array( 'USE INDEX' => 'page_timestamp',
'ORDER BY' => 'rev_timestamp DESC' )
);
- if( $s === false ) {
+ if ( $s === false ) {
# No one else ever edited this page
- return array(array('cantrollback'));
- } else if( $s->rev_deleted & REVISION::DELETED_TEXT || $s->rev_deleted & REVISION::DELETED_USER ) {
+ return array( array( 'cantrollback' ) );
+ } else if ( $s->rev_deleted & REVISION::DELETED_TEXT || $s->rev_deleted & REVISION::DELETED_USER ) {
# Only admins can see this text
- return array(array('notvisiblerev'));
+ return array( array( 'notvisiblerev' ) );
}
$set = array();
- if( $bot && $wgUser->isAllowed('markbotedits') ) {
+ if ( $bot && $wgUser->isAllowed( 'markbotedits' ) ) {
# Mark all reverted edits as bot
$set['rc_bot'] = 1;
}
- if( $wgUseRCPatrol ) {
+ if ( $wgUseRCPatrol ) {
# Mark all reverted edits as patrolled
$set['rc_patrolled'] = 1;
}
- if( $set ) {
+ if ( count( $set ) ) {
$dbw->update( 'recentchanges', $set,
- array( /* WHERE */
- 'rc_cur_id' => $current->getPage(),
- 'rc_user_text' => $current->getUserText(),
- "rc_timestamp > '{$s->rev_timestamp}'",
- ), __METHOD__
- );
+ array( /* WHERE */
+ 'rc_cur_id' => $current->getPage(),
+ 'rc_user_text' => $current->getUserText(),
+ "rc_timestamp > '{$s->rev_timestamp}'",
+ ), __METHOD__
+ );
}
# Generate the edit summary if necessary
$target = Revision::newFromId( $s->rev_id );
- if( empty( $summary ) ){
- $summary = wfMsgForContent( 'revertpage' );
+ if ( empty( $summary ) ) {
+ if ( $from == '' ) { // no public user name
+ $summary = wfMsgForContent( 'revertpage-nouser' );
+ } else {
+ $summary = wfMsgForContent( 'revertpage' );
+ }
}
# Allow the custom summary to use the same args as the default message
$args = array(
$target->getUserText(), $from, $s->rev_id,
- $wgLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp), true),
- $current->getId(), $wgLang->timeanddate($current->getTimestamp())
+ $wgLang->timeanddate( wfTimestamp( TS_MW, $s->rev_timestamp ), true ),
+ $current->getId(), $wgLang->timeanddate( $current->getTimestamp() )
);
$summary = wfMsgReplaceArgs( $summary, $args );
# Save
$flags = EDIT_UPDATE;
- if( $wgUser->isAllowed('minoredit') )
+ if ( $wgUser->isAllowed( 'minoredit' ) )
$flags |= EDIT_MINOR;
- if( $bot && ($wgUser->isAllowed('markbotedits') || $wgUser->isAllowed('bot')) )
+ if ( $bot && ( $wgUser->isAllowed( 'markbotedits' ) || $wgUser->isAllowed( 'bot' ) ) )
$flags |= EDIT_FORCE_BOT;
# Actually store the edit
$status = $this->doEdit( $target->getText(), $summary, $flags, $target->getId() );
- if( !empty( $status->value['revision'] ) ) {
+ if ( !empty( $status->value['revision'] ) ) {
$revId = $status->value['revision']->getId();
} else {
$revId = false;
@@ -2774,8 +3156,8 @@ class Article {
$resultDetails = array(
'summary' => $summary,
'current' => $current,
- 'target' => $target,
- 'newid' => $revId
+ 'target' => $target,
+ 'newid' => $revId
);
return array();
}
@@ -2795,19 +3177,19 @@ class Article {
$details
);
- if( in_array( array( 'actionthrottledtext' ), $result ) ) {
+ if ( in_array( array( 'actionthrottledtext' ), $result ) ) {
$wgOut->rateLimited();
return;
}
- if( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ) {
+ if ( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ) {
$wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
$errArray = $result[0];
$errMsg = array_shift( $errArray );
$wgOut->addWikiMsgArray( $errMsg, $errArray );
- if( isset( $details['current'] ) ){
+ if ( isset( $details['current'] ) ) {
$current = $details['current'];
- if( $current->getComment() != '' ) {
- $wgOut->addWikiMsgArray( 'editcomment', array(
+ if ( $current->getComment() != '' ) {
+ $wgOut->addWikiMsgArray( 'editcomment', array(
$wgUser->getSkin()->formatComment( $current->getComment() ) ), array( 'replaceafter' ) );
}
}
@@ -2816,19 +3198,19 @@ class Article {
# Display permissions errors before read-only message -- there's no
# point in misleading the user into thinking the inability to rollback
# is only temporary.
- if( !empty( $result ) && $result !== array( array( 'readonlytext' ) ) ) {
+ if ( !empty( $result ) && $result !== array( array( 'readonlytext' ) ) ) {
# array_diff is completely broken for arrays of arrays, sigh. Re-
# move any 'readonlytext' error manually.
$out = array();
- foreach( $result as $error ) {
- if( $error != array( 'readonlytext' ) ) {
- $out []= $error;
+ foreach ( $result as $error ) {
+ if ( $error != array( 'readonlytext' ) ) {
+ $out [] = $error;
}
}
$wgOut->showPermissionsErrorPage( $out );
return;
}
- if( $result == array( array( 'readonlytext' ) ) ) {
+ if ( $result == array( array( 'readonlytext' ) ) ) {
$wgOut->readOnlyPage();
return;
}
@@ -2838,14 +3220,18 @@ class Article {
$newId = $details['newid'];
$wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
- $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
- . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
+ if ( $current->getUserText() === '' ) {
+ $old = wfMsg( 'rev-deleted-user' );
+ } else {
+ $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
+ . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
+ }
$new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
. $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
$wgOut->addHTML( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
$wgOut->returnToMain( false, $this->mTitle );
- if( !$wgRequest->getBool( 'hidediff', false ) && !$wgUser->getBoolOption( 'norollbackdiff', false ) ) {
+ if ( !$wgRequest->getBool( 'hidediff', false ) && !$wgUser->getBoolOption( 'norollbackdiff', false ) ) {
$de = new DifferenceEngine( $this->mTitle, $current->getId(), $newId, false, true );
$de->showDiff( '', '' );
}
@@ -2857,8 +3243,11 @@ class Article {
*/
public function viewUpdates() {
global $wgDeferredUpdateList, $wgDisableCounters, $wgUser;
+ if ( wfReadOnly() ) {
+ return;
+ }
# Don't update page view counters on views from bot users (bug 14044)
- if( !$wgDisableCounters && !$wgUser->isAllowed('bot') && $this->getID() ) {
+ if ( !$wgDisableCounters && !$wgUser->isAllowed( 'bot' ) && $this->getID() ) {
Article::incViewCount( $this->getID() );
$u = new SiteStatsUpdate( 1, 0, 0 );
array_push( $wgDeferredUpdateList, $u );
@@ -2871,8 +3260,8 @@ class Article {
* Prepare text which is about to be saved.
* Returns a stdclass with source, pst and output members
*/
- public function prepareTextForEdit( $text, $revid=null ) {
- if( $this->mPreparedEdit && $this->mPreparedEdit->newText == $text && $this->mPreparedEdit->revid == $revid) {
+ public function prepareTextForEdit( $text, $revid = null ) {
+ if ( $this->mPreparedEdit && $this->mPreparedEdit->newText == $text && $this->mPreparedEdit->revid == $revid ) {
// Already prepared
return $this->mPreparedEdit;
}
@@ -2881,9 +3270,7 @@ class Article {
$edit->revid = $revid;
$edit->newText = $text;
$edit->pst = $this->preSaveTransform( $text );
- $options = new ParserOptions;
- $options->setTidy( true );
- $options->enableLimitReport();
+ $options = $this->getParserOptions();
$edit->output = $wgParser->parse( $edit->pst, $this->mTitle, $options, true, true, $revid );
$edit->oldText = $this->getContent();
$this->mPreparedEdit = $edit;
@@ -2905,13 +3292,13 @@ class Article {
* @param $changed Whether or not the content actually changed
*/
public function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange, $newid, $changed = true ) {
- global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgParser, $wgEnableParserCache;
+ global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgEnableParserCache;
wfProfileIn( __METHOD__ );
# Parse the text
# Be careful not to double-PST: $text is usually already PST-ed once
- if( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
+ if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
$editInfo = $this->prepareTextForEdit( $text, $newid );
} else {
@@ -2920,10 +3307,8 @@ class Article {
}
# Save it to the parser cache
- if( $wgEnableParserCache ) {
- $popts = new ParserOptions;
- $popts->setTidy( true );
- $popts->enableLimitReport();
+ if ( $wgEnableParserCache ) {
+ $popts = $this->getParserOptions();
$parserCache = ParserCache::singleton();
$parserCache->save( $editInfo->output, $this, $popts );
}
@@ -2931,11 +3316,11 @@ class Article {
# Update the links tables
$u = new LinksUpdate( $this->mTitle, $editInfo->output );
$u->doUpdate();
-
+
wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $changed ) );
- if( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
- if( 0 == mt_rand( 0, 99 ) ) {
+ if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
+ if ( 0 == mt_rand( 0, 99 ) ) {
// Flush old entries from the `recentchanges` table; we do this on
// random requests so as to avoid an increase in writes for no good reason
global $wgRCMaxAge;
@@ -2951,7 +3336,7 @@ class Article {
$title = $this->mTitle->getPrefixedDBkey();
$shortTitle = $this->mTitle->getDBkey();
- if( 0 == $id ) {
+ if ( 0 == $id ) {
wfProfileOut( __METHOD__ );
return;
}
@@ -2965,24 +3350,24 @@ class Article {
# Don't do this if $changed = false otherwise some idiot can null-edit a
# load of user talk pages and piss people off, nor if it's a minor edit
# by a properly-flagged bot.
- if( $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getTitleKey() && $changed
+ if ( $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getTitleKey() && $changed
&& !( $minoredit && $wgUser->isAllowed( 'nominornewtalk' ) ) ) {
- if( wfRunHooks('ArticleEditUpdateNewTalk', array( &$this ) ) ) {
+ if ( wfRunHooks( 'ArticleEditUpdateNewTalk', array( &$this ) ) ) {
$other = User::newFromName( $shortTitle, false );
- if( !$other ) {
- wfDebug( __METHOD__.": invalid username\n" );
- } elseif( User::isIP( $shortTitle ) ) {
+ if ( !$other ) {
+ wfDebug( __METHOD__ . ": invalid username\n" );
+ } elseif ( User::isIP( $shortTitle ) ) {
// An anonymous user
$other->setNewtalk( true );
- } elseif( $other->isLoggedIn() ) {
+ } elseif ( $other->isLoggedIn() ) {
$other->setNewtalk( true );
} else {
- wfDebug( __METHOD__. ": don't need to notify a nonexistent user\n" );
+ wfDebug( __METHOD__ . ": don't need to notify a nonexistent user\n" );
}
}
}
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
$wgMessageCache->replace( $shortTitle, $text );
}
@@ -3016,56 +3401,112 @@ class Article {
public function setOldSubtitle( $oldid = 0 ) {
global $wgLang, $wgOut, $wgUser, $wgRequest;
- if( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
+ if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
return;
}
+ $unhide = $wgRequest->getInt( 'unhide' ) == 1 &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'token' ), $oldid );
+ # Cascade unhide param in links for easy deletion browsing
+ $extraParams = array();
+ if ( $wgRequest->getVal( 'unhide' ) ) {
+ $extraParams['unhide'] = 1;
+ }
$revision = Revision::newFromId( $oldid );
$current = ( $oldid == $this->mLatest );
$td = $wgLang->timeanddate( $this->mTimestamp, true );
+ $tddate = $wgLang->date( $this->mTimestamp, true );
+ $tdtime = $wgLang->time( $this->mTimestamp, true );
$sk = $wgUser->getSkin();
$lnk = $current
? wfMsgHtml( 'currentrevisionlink' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'currentrevisionlink' ) );
+ : $sk->link(
+ $this->mTitle,
+ wfMsgHtml( 'currentrevisionlink' ),
+ array(),
+ $extraParams,
+ array( 'known', 'noclasses' )
+ );
$curdiff = $current
? wfMsgHtml( 'diff' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'diff' ), 'diff=cur&oldid='.$oldid );
+ : $sk->link(
+ $this->mTitle,
+ wfMsgHtml( 'diff' ),
+ array(),
+ array(
+ 'diff' => 'cur',
+ 'oldid' => $oldid
+ ) + $extraParams,
+ array( 'known', 'noclasses' )
+ );
$prev = $this->mTitle->getPreviousRevisionID( $oldid ) ;
$prevlink = $prev
- ? $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'previousrevision' ), 'direction=prev&oldid='.$oldid )
+ ? $sk->link(
+ $this->mTitle,
+ wfMsgHtml( 'previousrevision' ),
+ array(),
+ array(
+ 'direction' => 'prev',
+ 'oldid' => $oldid
+ ) + $extraParams,
+ array( 'known', 'noclasses' )
+ )
: wfMsgHtml( 'previousrevision' );
$prevdiff = $prev
- ? $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'diff' ), 'diff=prev&oldid='.$oldid )
+ ? $sk->link(
+ $this->mTitle,
+ wfMsgHtml( 'diff' ),
+ array(),
+ array(
+ 'diff' => 'prev',
+ 'oldid' => $oldid
+ ) + $extraParams,
+ array( 'known', 'noclasses' )
+ )
: wfMsgHtml( 'diff' );
$nextlink = $current
? wfMsgHtml( 'nextrevision' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'nextrevision' ), 'direction=next&oldid='.$oldid );
+ : $sk->link(
+ $this->mTitle,
+ wfMsgHtml( 'nextrevision' ),
+ array(),
+ array(
+ 'direction' => 'next',
+ 'oldid' => $oldid
+ ) + $extraParams,
+ array( 'known', 'noclasses' )
+ );
$nextdiff = $current
? wfMsgHtml( 'diff' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'diff' ), 'diff=next&oldid='.$oldid );
-
- $cdel='';
- if( $wgUser->isAllowed( 'deleterevision' ) ) {
- $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
- if( $revision->isCurrent() ) {
- // We don't handle top deleted edits too well
- $cdel = wfMsgHtml( 'rev-delundel' );
- } else if( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
- // If revision was hidden from sysops
- $cdel = wfMsgHtml( 'rev-delundel' );
+ : $sk->link(
+ $this->mTitle,
+ wfMsgHtml( 'diff' ),
+ array(),
+ array(
+ 'diff' => 'next',
+ 'oldid' => $oldid
+ ) + $extraParams,
+ array( 'known', 'noclasses' )
+ );
+
+ $cdel = '';
+ // User can delete revisions or view deleted revisions...
+ $canHide = $wgUser->isAllowed( 'deleterevision' );
+ if ( $canHide || ( $revision->getVisibility() && $wgUser->isAllowed( 'deletedhistory' ) ) ) {
+ if ( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
+ $cdel = $sk->revDeleteLinkDisabled( $canHide ); // rev was hidden from Sysops
} else {
- $cdel = $sk->makeKnownLinkObj( $revdel,
- wfMsgHtml('rev-delundel'),
- 'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
- '&oldid=' . urlencode( $oldid ) );
- // Bolden oversighted content
- if( $revision->isDeleted( Revision::DELETED_RESTRICTED ) )
- $cdel = "<strong>$cdel</strong>";
+ $query = array(
+ 'type' => 'revision',
+ 'target' => $this->mTitle->getPrefixedDbkey(),
+ 'ids' => $oldid
+ );
+ $cdel = $sk->revDeleteLink( $query, $revision->isDeleted( File::DELETED_RESTRICTED ), $canHide );
}
- $cdel = "(<small>$cdel</small>) ";
+ $cdel .= ' ';
}
- $unhide = $wgRequest->getInt('unhide') == 1 && $wgUser->matchEditToken( $wgRequest->getVal('token'), $oldid );
+
# Show user links if allowed to see them. If hidden, then show them only if requested...
$userlinks = $sk->revUserTools( $revision, !$unhide );
@@ -3074,11 +3515,20 @@ class Article {
? 'revision-info-current'
: 'revision-info';
- $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsgExt( $infomsg, array( 'parseinline', 'replaceafter' ),
- $td, $userlinks, $revision->getID() ) . "</div>\n" .
-
- "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ),
- $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
+ $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" .
+ wfMsgExt(
+ $infomsg,
+ array( 'parseinline', 'replaceafter' ),
+ $td,
+ $userlinks,
+ $revision->getID(),
+ $tddate,
+ $tdtime,
+ $revision->getUser()
+ ) .
+ "</div>\n" .
+ "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ),
+ $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
@@ -3102,20 +3552,20 @@ class Article {
*/
protected function tryFileCache() {
static $called = false;
- if( $called ) {
+ if ( $called ) {
wfDebug( "Article::tryFileCache(): called twice!?\n" );
return false;
}
$called = true;
- if( $this->isFileCacheable() ) {
+ if ( $this->isFileCacheable() ) {
$cache = new HTMLFileCache( $this->mTitle );
- if( $cache->isFileCacheGood( $this->mTouched ) ) {
+ if ( $cache->isFileCacheGood( $this->mTouched ) ) {
wfDebug( "Article::tryFileCache(): about to load file\n" );
$cache->loadFromFileCache();
return true;
} else {
wfDebug( "Article::tryFileCache(): starting buffer\n" );
- ob_start( array(&$cache, 'saveToFileCache' ) );
+ ob_start( array( &$cache, 'saveToFileCache' ) );
}
} else {
wfDebug( "Article::tryFileCache(): not cacheable\n" );
@@ -3129,10 +3579,10 @@ class Article {
*/
public function isFileCacheable() {
$cacheable = false;
- if( HTMLFileCache::useFileCache() ) {
+ if ( HTMLFileCache::useFileCache() ) {
$cacheable = $this->getID() && !$this->mRedirectedFrom;
// Extension may have reason to disable file caching on some pages.
- if( $cacheable ) {
+ if ( $cacheable ) {
$cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) );
}
}
@@ -3144,7 +3594,7 @@ class Article {
*
*/
public function checkTouched() {
- if( !$this->mDataLoaded ) {
+ if ( !$this->mDataLoaded ) {
$this->loadPageData();
}
return !$this->mIsRedirect;
@@ -3155,7 +3605,7 @@ class Article {
*/
public function getTouched() {
# Ensure that page data has been loaded
- if( !$this->mDataLoaded ) {
+ if ( !$this->mDataLoaded ) {
$this->loadPageData();
}
return $this->mTouched;
@@ -3165,7 +3615,7 @@ class Article {
* Get the page_latest field
*/
public function getLatest() {
- if( !$this->mDataLoaded ) {
+ if ( !$this->mDataLoaded ) {
$this->loadPageData();
}
return (int)$this->mLatest;
@@ -3193,7 +3643,7 @@ class Article {
$revision->insertOn( $dbw );
$this->updateRevisionOn( $dbw, $revision );
- wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false, $wgUser) );
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $wgUser ) );
wfProfileOut( __METHOD__ );
}
@@ -3205,14 +3655,15 @@ class Article {
*/
public static function incViewCount( $id ) {
$id = intval( $id );
- global $wgHitcounterUpdateFreq, $wgDBtype;
+ global $wgHitcounterUpdateFreq;
$dbw = wfGetDB( DB_MASTER );
$pageTable = $dbw->tableName( 'page' );
$hitcounterTable = $dbw->tableName( 'hitcounter' );
$acchitsTable = $dbw->tableName( 'acchits' );
+ $dbType = $dbw->getType();
- if( $wgHitcounterUpdateFreq <= 1 ) {
+ if ( $wgHitcounterUpdateFreq <= 1 || $dbType == 'sqlite' ) {
$dbw->query( "UPDATE $pageTable SET page_counter = page_counter + 1 WHERE page_id = $id" );
return;
}
@@ -3222,37 +3673,36 @@ class Article {
$dbw->query( "INSERT INTO $hitcounterTable (hc_id) VALUES ({$id})" );
- $checkfreq = intval( $wgHitcounterUpdateFreq/25 + 1 );
- if( (rand() % $checkfreq != 0) or ($dbw->lastErrno() != 0) ){
+ $checkfreq = intval( $wgHitcounterUpdateFreq / 25 + 1 );
+ if ( ( rand() % $checkfreq != 0 ) or ( $dbw->lastErrno() != 0 ) ) {
# Most of the time (or on SQL errors), skip row count check
$dbw->ignoreErrors( $oldignore );
return;
}
- $res = $dbw->query("SELECT COUNT(*) as n FROM $hitcounterTable");
+ $res = $dbw->query( "SELECT COUNT(*) as n FROM $hitcounterTable" );
$row = $dbw->fetchObject( $res );
$rown = intval( $row->n );
- if( $rown >= $wgHitcounterUpdateFreq ){
+ if ( $rown >= $wgHitcounterUpdateFreq ) {
wfProfileIn( 'Article::incViewCount-collect' );
$old_user_abort = ignore_user_abort( true );
- if($wgDBtype == 'mysql')
- $dbw->query("LOCK TABLES $hitcounterTable WRITE");
- $tabletype = $wgDBtype == 'mysql' ? "ENGINE=HEAP " : '';
- $dbw->query("CREATE TEMPORARY TABLE $acchitsTable $tabletype AS ".
- "SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable ".
- 'GROUP BY hc_id');
- $dbw->query("DELETE FROM $hitcounterTable");
- if($wgDBtype == 'mysql') {
- $dbw->query('UNLOCK TABLES');
- $dbw->query("UPDATE $pageTable,$acchitsTable SET page_counter=page_counter + hc_n ".
- 'WHERE page_id = hc_id');
+ $dbw->lockTables( array(), array( 'hitcounter' ), __METHOD__, false );
+ $tabletype = $dbType == 'mysql' ? "ENGINE=HEAP " : '';
+ $dbw->query( "CREATE TEMPORARY TABLE $acchitsTable $tabletype AS " .
+ "SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable " .
+ 'GROUP BY hc_id', __METHOD__ );
+ $dbw->delete( 'hitcounter', '*', __METHOD__ );
+ $dbw->unlockTables( __METHOD__ );
+ if ( $dbType == 'mysql' ) {
+ $dbw->query( "UPDATE $pageTable,$acchitsTable SET page_counter=page_counter + hc_n " .
+ 'WHERE page_id = hc_id', __METHOD__ );
}
else {
- $dbw->query("UPDATE $pageTable SET page_counter=page_counter + hc_n ".
- "FROM $acchitsTable WHERE page_id = hc_id");
+ $dbw->query( "UPDATE $pageTable SET page_counter=page_counter + hc_n " .
+ "FROM $acchitsTable WHERE page_id = hc_id", __METHOD__ );
}
- $dbw->query("DROP TABLE $acchitsTable");
+ $dbw->query( "DROP TABLE $acchitsTable", __METHOD__ );
ignore_user_abort( $old_user_abort );
wfProfileOut( 'Article::incViewCount-collect' );
@@ -3271,10 +3721,9 @@ class Article {
*
* @param $title a title object
*/
-
public static function onArticleCreate( $title ) {
# Update existence markers on article/talk tabs...
- if( $title->isTalkPage() ) {
+ if ( $title->isTalkPage() ) {
$other = $title->getSubjectPage();
} else {
$other = $title->getTalkPage();
@@ -3290,7 +3739,7 @@ class Article {
public static function onArticleDelete( $title ) {
global $wgMessageCache;
# Update existence markers on article/talk tabs...
- if( $title->isTalkPage() ) {
+ if ( $title->isTalkPage() ) {
$other = $title->getSubjectPage();
} else {
$other = $title->getTalkPage();
@@ -3305,16 +3754,16 @@ class Article {
HTMLFileCache::clearFileCache( $title );
# Messages
- if( $title->getNamespace() == NS_MEDIAWIKI ) {
+ if ( $title->getNamespace() == NS_MEDIAWIKI ) {
$wgMessageCache->replace( $title->getDBkey(), false );
}
# Images
- if( $title->getNamespace() == NS_FILE ) {
+ if ( $title->getNamespace() == NS_FILE ) {
$update = new HTMLCacheUpdate( $title, 'imagelinks' );
$update->doUpdate();
}
# User talk pages
- if( $title->getNamespace() == NS_USER_TALK ) {
+ if ( $title->getNamespace() == NS_USER_TALK ) {
$user = User::newFromName( $title->getText(), false );
$user->setNewtalk( false );
}
@@ -3359,7 +3808,7 @@ class Article {
public function info() {
global $wgLang, $wgOut, $wgAllowPageInfo, $wgUser;
- if( !$wgAllowPageInfo ) {
+ if ( !$wgAllowPageInfo ) {
$wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
return;
}
@@ -3370,9 +3819,9 @@ class Article {
$wgOut->setPageTitleActionText( wfMsg( 'info_short' ) );
$wgOut->setSubtitle( wfMsgHtml( 'infosubtitle' ) );
- if( !$this->mTitle->exists() ) {
+ if ( !$this->mTitle->exists() ) {
$wgOut->addHTML( '<div class="noarticletext">' );
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
// This doesn't quite make sense; the user is asking for
// information about the _page_, not the message... -- RC
$wgOut->addHTML( htmlspecialchars( wfMsgWeirdKey( $this->mTitle->getText() ) ) );
@@ -3398,14 +3847,14 @@ class Article {
$pageInfo = $this->pageCountInfo( $page );
$talkInfo = $this->pageCountInfo( $page->getTalkPage() );
- $wgOut->addHTML( "<ul><li>" . wfMsg("numwatchers", $wgLang->formatNum( $numwatchers ) ) . '</li>' );
- $wgOut->addHTML( "<li>" . wfMsg('numedits', $wgLang->formatNum( $pageInfo['edits'] ) ) . '</li>');
- if( $talkInfo ) {
- $wgOut->addHTML( '<li>' . wfMsg("numtalkedits", $wgLang->formatNum( $talkInfo['edits'] ) ) . '</li>');
+ $wgOut->addHTML( "<ul><li>" . wfMsg( "numwatchers", $wgLang->formatNum( $numwatchers ) ) . '</li>' );
+ $wgOut->addHTML( "<li>" . wfMsg( 'numedits', $wgLang->formatNum( $pageInfo['edits'] ) ) . '</li>' );
+ if ( $talkInfo ) {
+ $wgOut->addHTML( '<li>' . wfMsg( "numtalkedits", $wgLang->formatNum( $talkInfo['edits'] ) ) . '</li>' );
}
- $wgOut->addHTML( '<li>' . wfMsg("numauthors", $wgLang->formatNum( $pageInfo['authors'] ) ) . '</li>' );
- if( $talkInfo ) {
- $wgOut->addHTML( '<li>' . wfMsg('numtalkauthors', $wgLang->formatNum( $talkInfo['authors'] ) ) . '</li>' );
+ $wgOut->addHTML( '<li>' . wfMsg( "numauthors", $wgLang->formatNum( $pageInfo['authors'] ) ) . '</li>' );
+ if ( $talkInfo ) {
+ $wgOut->addHTML( '<li>' . wfMsg( 'numtalkauthors', $wgLang->formatNum( $talkInfo['authors'] ) ) . '</li>' );
}
$wgOut->addHTML( '</ul>' );
}
@@ -3418,9 +3867,9 @@ class Article {
* @param $title Title object
* @return array
*/
- protected function pageCountInfo( $title ) {
+ public function pageCountInfo( $title ) {
$id = $title->getArticleId();
- if( $id == 0 ) {
+ if ( $id == 0 ) {
return false;
}
$dbr = wfGetDB( DB_SLAVE );
@@ -3451,7 +3900,7 @@ class Article {
public function getUsedTemplates() {
$result = array();
$id = $this->mTitle->getArticleID();
- if( $id == 0 ) {
+ if ( $id == 0 ) {
return array();
}
$dbr = wfGetDB( DB_SLAVE );
@@ -3459,8 +3908,8 @@ class Article {
array( 'tl_namespace', 'tl_title' ),
array( 'tl_from' => $id ),
__METHOD__ );
- if( $res !== false ) {
- foreach( $res as $row ) {
+ if ( $res !== false ) {
+ foreach ( $res as $row ) {
$result[] = Title::makeTitle( $row->tl_namespace, $row->tl_title );
}
}
@@ -3477,17 +3926,17 @@ class Article {
public function getHiddenCategories() {
$result = array();
$id = $this->mTitle->getArticleID();
- if( $id == 0 ) {
+ if ( $id == 0 ) {
return array();
}
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ),
array( 'cl_to' ),
array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
- 'page_namespace' => NS_CATEGORY, 'page_title=cl_to'),
+ 'page_namespace' => NS_CATEGORY, 'page_title=cl_to' ),
__METHOD__ );
- if( $res !== false ) {
- foreach( $res as $row ) {
+ if ( $res !== false ) {
+ foreach ( $res as $row ) {
$result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
}
}
@@ -3508,24 +3957,24 @@ class Article {
# Redirect autosummaries
$ot = Title::newFromRedirect( $oldtext );
$rt = Title::newFromRedirect( $newtext );
- if( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) {
+ if ( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) {
return wfMsgForContent( 'autoredircomment', $rt->getFullText() );
}
# New page autosummaries
- if( $flags & EDIT_NEW && strlen( $newtext ) ) {
+ if ( $flags & EDIT_NEW && strlen( $newtext ) ) {
# If they're making a new article, give its text, truncated, in the summary.
global $wgContLang;
$truncatedtext = $wgContLang->truncate(
- str_replace("\n", ' ', $newtext),
+ str_replace( "\n", ' ', $newtext ),
max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ) );
return wfMsgForContent( 'autosumm-new', $truncatedtext );
}
# Blanking autosummaries
- if( $oldtext != '' && $newtext == '' ) {
+ if ( $oldtext != '' && $newtext == '' ) {
return wfMsgForContent( 'autosumm-blank' );
- } elseif( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500) {
+ } elseif ( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500 ) {
# Removing more than 90% of the article
global $wgContLang;
$truncatedtext = $wgContLang->truncate(
@@ -3547,72 +3996,108 @@ class Article {
* @param $text String
* @param $cache Boolean
*/
- public function outputWikiText( $text, $cache = true ) {
- global $wgParser, $wgUser, $wgOut, $wgEnableParserCache, $wgUseFileCache;
-
- $popts = $wgOut->parserOptions();
- $popts->setTidy(true);
- $popts->enableLimitReport();
- $parserOutput = $wgParser->parse( $text, $this->mTitle,
- $popts, true, true, $this->getRevIdFetched() );
- $popts->setTidy(false);
- $popts->enableLimitReport( false );
- if( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
+ public function outputWikiText( $text, $cache = true, $parserOptions = false ) {
+ global $wgOut;
+
+ $this->mParserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions );
+ $wgOut->addParserOutput( $this->mParserOutput );
+ }
+
+ /**
+ * This does all the heavy lifting for outputWikitext, except it returns the parser
+ * output instead of sending it straight to $wgOut. Makes things nice and simple for,
+ * say, embedding thread pages within a discussion system (LiquidThreads)
+ */
+ public function getOutputFromWikitext( $text, $cache = true, $parserOptions = false ) {
+ global $wgParser, $wgOut, $wgEnableParserCache, $wgUseFileCache;
+
+ if ( !$parserOptions ) {
+ $parserOptions = $this->getParserOptions();
+ }
+
+ $time = - wfTime();
+ $this->mParserOutput = $wgParser->parse( $text, $this->mTitle,
+ $parserOptions, true, true, $this->getRevIdFetched() );
+ $time += wfTime();
+
+ # Timing hack
+ if ( $time > 3 ) {
+ wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
+ $this->mTitle->getPrefixedDBkey() ) );
+ }
+
+ if ( $wgEnableParserCache && $cache && $this && $this->mParserOutput->getCacheTime() != -1 ) {
$parserCache = ParserCache::singleton();
- $parserCache->save( $parserOutput, $this, $popts );
+ $parserCache->save( $this->mParserOutput, $this, $parserOptions );
}
// Make sure file cache is not used on uncacheable content.
// Output that has magic words in it can still use the parser cache
// (if enabled), though it will generally expire sooner.
- if( $parserOutput->getCacheTime() == -1 || $parserOutput->containsOldMagic() ) {
+ if ( $this->mParserOutput->getCacheTime() == -1 || $this->mParserOutput->containsOldMagic() ) {
$wgUseFileCache = false;
}
+ $this->doCascadeProtectionUpdates( $this->mParserOutput );
+ return $this->mParserOutput;
+ }
- if( $this->isCurrent() && !wfReadOnly() && $this->mTitle->areRestrictionsCascading() ) {
- // templatelinks table may have become out of sync,
- // especially if using variable-based transclusions.
- // For paranoia, check if things have changed and if
- // so apply updates to the database. This will ensure
- // that cascaded protections apply as soon as the changes
- // are visible.
+ /**
+ * Get parser options suitable for rendering the primary article wikitext
+ */
+ public function getParserOptions() {
+ global $wgUser;
+ if ( !$this->mParserOptions ) {
+ $this->mParserOptions = new ParserOptions( $wgUser );
+ $this->mParserOptions->setTidy( true );
+ $this->mParserOptions->enableLimitReport();
+ }
+ return $this->mParserOptions;
+ }
- # Get templates from templatelinks
- $id = $this->mTitle->getArticleID();
+ protected function doCascadeProtectionUpdates( $parserOutput ) {
+ if ( !$this->isCurrent() || wfReadOnly() || !$this->mTitle->areRestrictionsCascading() ) {
+ return;
+ }
- $tlTemplates = array();
+ // templatelinks table may have become out of sync,
+ // especially if using variable-based transclusions.
+ // For paranoia, check if things have changed and if
+ // so apply updates to the database. This will ensure
+ // that cascaded protections apply as soon as the changes
+ // are visible.
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( array( 'templatelinks' ),
- array( 'tl_namespace', 'tl_title' ),
- array( 'tl_from' => $id ),
- __METHOD__ );
+ # Get templates from templatelinks
+ $id = $this->mTitle->getArticleID();
- global $wgContLang;
- foreach( $res as $row ) {
- $tlTemplates["{$row->tl_namespace}:{$row->tl_title}"] = true;
- }
+ $tlTemplates = array();
- # Get templates from parser output.
- $poTemplates = array();
- foreach ( $parserOutput->getTemplates() as $ns => $templates ) {
- foreach ( $templates as $dbk => $id ) {
- $key = $row->tl_namespace . ':'. $row->tl_title;
- $poTemplates["$ns:$dbk"] = true;
- }
- }
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( array( 'templatelinks' ),
+ array( 'tl_namespace', 'tl_title' ),
+ array( 'tl_from' => $id ),
+ __METHOD__ );
- # Get the diff
- # Note that we simulate array_diff_key in PHP <5.0.x
- $templates_diff = array_diff_key( $poTemplates, $tlTemplates );
+ global $wgContLang;
+ foreach ( $res as $row ) {
+ $tlTemplates["{$row->tl_namespace}:{$row->tl_title}"] = true;
+ }
- if( count( $templates_diff ) > 0 ) {
- # Whee, link updates time.
- $u = new LinksUpdate( $this->mTitle, $parserOutput, false );
- $u->doUpdate();
+ # Get templates from parser output.
+ $poTemplates = array();
+ foreach ( $parserOutput->getTemplates() as $ns => $templates ) {
+ foreach ( $templates as $dbk => $id ) {
+ $poTemplates["$ns:$dbk"] = true;
}
}
- $wgOut->addParserOutput( $parserOutput );
+ # Get the diff
+ # Note that we simulate array_diff_key in PHP <5.0.x
+ $templates_diff = array_diff_key( $poTemplates, $tlTemplates );
+
+ if ( count( $templates_diff ) > 0 ) {
+ # Whee, link updates time.
+ $u = new LinksUpdate( $this->mTitle, $parserOutput, false );
+ $u->doUpdate();
+ }
}
/**
@@ -3634,27 +4119,30 @@ class Article {
#
# Sometimes I wish we had INSERT ... ON DUPLICATE KEY UPDATE.
$insertCats = array_merge( $added, $deleted );
- if( !$insertCats ) {
+ if ( !$insertCats ) {
# Okay, nothing to do
return;
}
$insertRows = array();
- foreach( $insertCats as $cat ) {
- $insertRows[] = array( 'cat_title' => $cat );
+ foreach ( $insertCats as $cat ) {
+ $insertRows[] = array(
+ 'cat_id' => $dbw->nextSequenceValue( 'category_cat_id_seq' ),
+ 'cat_title' => $cat
+ );
}
$dbw->insert( 'category', $insertRows, __METHOD__, 'IGNORE' );
$addFields = array( 'cat_pages = cat_pages + 1' );
$removeFields = array( 'cat_pages = cat_pages - 1' );
- if( $ns == NS_CATEGORY ) {
+ if ( $ns == NS_CATEGORY ) {
$addFields[] = 'cat_subcats = cat_subcats + 1';
$removeFields[] = 'cat_subcats = cat_subcats - 1';
- } elseif( $ns == NS_FILE ) {
+ } elseif ( $ns == NS_FILE ) {
$addFields[] = 'cat_files = cat_files + 1';
$removeFields[] = 'cat_files = cat_files - 1';
}
- if( $added ) {
+ if ( $added ) {
$dbw->update(
'category',
$addFields,
@@ -3662,7 +4150,7 @@ class Article {
__METHOD__
);
}
- if( $deleted ) {
+ if ( $deleted ) {
$dbw->update(
'category',
$removeFields,
@@ -3671,4 +4159,37 @@ class Article {
);
}
}
+
+ /** Lightweight method to get the parser output for a page, checking the parser cache
+ * and so on. Doesn't consider most of the stuff that Article::view is forced to
+ * consider, so it's not appropriate to use there.
+ */
+ function getParserOutput( $oldid = null ) {
+ global $wgEnableParserCache, $wgUser, $wgOut;
+
+ // Should the parser cache be used?
+ $useParserCache = $wgEnableParserCache &&
+ intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 &&
+ $this->exists() &&
+ $oldid === null;
+
+ wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
+ if ( $wgUser->getOption( 'stubthreshold' ) ) {
+ wfIncrStats( 'pcache_miss_stub' );
+ }
+
+ $parserOutput = false;
+ if ( $useParserCache ) {
+ $parserOutput = ParserCache::singleton()->get( $this, $this->getParserOptions() );
+ }
+
+ if ( $parserOutput === false ) {
+ // Cache miss; parse and output it.
+ $rev = Revision::newFromTitle( $this->getTitle(), $oldid );
+
+ return $this->getOutputFromWikitext( $rev->getText(), $useParserCache );
+ } else {
+ return $parserOutput;
+ }
+ }
}
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index b29e13f2..87ac8adb 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -63,8 +63,9 @@ class AuthPlugin {
* Modify options in the login template.
*
* @param $template UserLoginTemplate object.
+ * @param $type String 'signup' or 'login'.
*/
- public function modifyUITemplate( &$template ) {
+ public function modifyUITemplate( &$template, &$type ) {
# Override this!
$template->set( 'usedomain', false );
}
@@ -97,7 +98,7 @@ class AuthPlugin {
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
- * @param User $user
+ * @param $user User object
*/
public function updateUser( &$user ) {
# Override this and do something
@@ -116,13 +117,32 @@ class AuthPlugin {
*
* This is just a question, and shouldn't perform any actions.
*
- * @return bool
+ * @return Boolean
*/
public function autoCreate() {
return false;
}
/**
+ * Allow a property change? Properties are the same as preferences
+ * and use the same keys. 'Realname' 'Emailaddress' and 'Nickname'
+ * all reference this.
+ *
+ * @return Boolean
+ */
+ public function allowPropChange( $prop = '' ) {
+ if( $prop == 'realname' && is_callable( array( $this, 'allowRealNameChange' ) ) ) {
+ return $this->allowRealNameChange();
+ } elseif( $prop == 'emailaddress' && is_callable( array( $this, 'allowEmailChange' ) ) ) {
+ return $this->allowEmailChange();
+ } elseif( $prop == 'nickname' && is_callable( array( $this, 'allowNickChange' ) ) ) {
+ return $this->allowNickChange();
+ } else {
+ return true;
+ }
+ }
+
+ /**
* Can users change their passwords?
*
* @return bool
@@ -152,7 +172,7 @@ class AuthPlugin {
* Return true if successful.
*
* @param $user User object.
- * @return bool
+ * @return Boolean
*/
public function updateExternalDB( $user ) {
return true;
@@ -161,7 +181,7 @@ class AuthPlugin {
/**
* Check to see if external accounts can be created.
* Return true if external accounts can be created.
- * @return bool
+ * @return Boolean
*/
public function canCreateAccounts() {
return false;
@@ -171,11 +191,11 @@ class AuthPlugin {
* Add a user to the external authentication database.
* Return true if successful.
*
- * @param User $user - only the name should be assumed valid at this point
- * @param string $password
- * @param string $email
- * @param string $realname
- * @return bool
+ * @param $user User: only the name should be assumed valid at this point
+ * @param $password String
+ * @param $email String
+ * @param $realname String
+ * @return Boolean
*/
public function addUser( $user, $password, $email='', $realname='' ) {
return true;
@@ -188,7 +208,7 @@ class AuthPlugin {
*
* This is just a question, and shouldn't perform any actions.
*
- * @return bool
+ * @return Boolean
*/
public function strict() {
return false;
@@ -199,7 +219,7 @@ class AuthPlugin {
* If either this or strict() returns true, local authentication is not used.
*
* @param $username String: username.
- * @return bool
+ * @return Boolean
*/
public function strictUserAuth( $username ) {
return false;
@@ -214,7 +234,7 @@ class AuthPlugin {
* forget the & on your function declaration.
*
* @param $user User object.
- * @param $autocreate bool True if user is being autocreated on login
+ * @param $autocreate Boolean: True if user is being autocreated on login
*/
public function initUser( &$user, $autocreate=false ) {
# Override this to do something.
@@ -232,7 +252,6 @@ class AuthPlugin {
* Get an instance of a User object
*
* @param $user User
- * @public
*/
public function getUserInstance( User &$user ) {
return new AuthPluginUser( $user );
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 85e7e668..cecb53f9 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -1,9 +1,6 @@
<?php
-
/* This defines autoloading handler for whole MediaWiki framework */
-ini_set('unserialize_callback_func', '__autoload' );
-
# Locations of core classes
# Extension classes are specified with $wgAutoloadClasses
# This array is a global instead of a static member of AutoLoader to work around a bug in APC
@@ -27,11 +24,23 @@ $wgAutoloadLocalClasses = array(
'Categoryfinder' => 'includes/Categoryfinder.php',
'CategoryPage' => 'includes/CategoryPage.php',
'CategoryViewer' => 'includes/CategoryPage.php',
+ 'CdbFunctions' => 'includes/Cdb_PHP.php',
+ 'CdbReader' => 'includes/Cdb.php',
+ 'CdbReader_DBA' => 'includes/Cdb.php',
+ 'CdbReader_PHP' => 'includes/Cdb_PHP.php',
+ 'CdbWriter' => 'includes/Cdb.php',
+ 'CdbWriter_DBA' => 'includes/Cdb.php',
+ 'CdbWriter_PHP' => 'includes/Cdb_PHP.php',
'ChangesList' => 'includes/ChangesList.php',
'ChangesFeed' => 'includes/ChangesFeed.php',
'ChangeTags' => 'includes/ChangeTags.php',
'ChannelFeed' => 'includes/Feed.php',
+ 'Cookie' => 'includes/HttpFunctions.php',
+ 'CookieJar' => 'includes/HttpFunctions.php',
'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
+ 'ConfEditor' => 'includes/ConfEditor.php',
+ 'ConfEditorParseError' => 'includes/ConfEditor.php',
+ 'ConfEditorToken' => 'includes/ConfEditor.php',
'ConstantDependency' => 'includes/CacheDependency.php',
'CreativeCommonsRdf' => 'includes/Metadata.php',
'Credits' => 'includes/Credits.php',
@@ -66,33 +75,58 @@ $wgAutoloadLocalClasses = array(
'ExternalStoreDB' => 'includes/ExternalStoreDB.php',
'ExternalStoreHttp' => 'includes/ExternalStoreHttp.php',
'ExternalStore' => 'includes/ExternalStore.php',
+ 'ExternalUser' => 'includes/ExternalUser.php',
+ 'ExternalUser_Hardcoded' => 'includes/extauth/Hardcoded.php',
+ 'ExternalUser_MediaWiki' => 'includes/extauth/MediaWiki.php',
+ 'ExternalUser_vB' => 'includes/extauth/vB.php',
'FatalError' => 'includes/Exception.php',
'FakeTitle' => 'includes/FakeTitle.php',
+ 'FakeMemCachedClient' => 'includes/ObjectCache.php',
'FauxRequest' => 'includes/WebRequest.php',
+ 'FauxResponse' => 'includes/WebResponse.php',
'FeedItem' => 'includes/Feed.php',
'FeedUtils' => 'includes/FeedUtils.php',
'FileDeleteForm' => 'includes/FileDeleteForm.php',
'FileDependency' => 'includes/CacheDependency.php',
'FileRevertForm' => 'includes/FileRevertForm.php',
- 'FileStore' => 'includes/FileStore.php',
'ForkController' => 'includes/ForkController.php',
'FormatExif' => 'includes/Exif.php',
'FormOptions' => 'includes/FormOptions.php',
- 'FSException' => 'includes/FileStore.php',
- 'FSTransaction' => 'includes/FileStore.php',
+ 'GIFMetadataExtractor' => 'includes/media/GIFMetadataExtractor.php',
+ 'GIFHandler' => 'includes/media/GIF.php',
'GlobalDependency' => 'includes/CacheDependency.php',
'HashBagOStuff' => 'includes/BagOStuff.php',
'HashtableReplacer' => 'includes/StringUtils.php',
'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
'HistoryBlob' => 'includes/HistoryBlob.php',
'HistoryBlobStub' => 'includes/HistoryBlob.php',
+ 'HistoryPage' => 'includes/HistoryPage.php',
+ 'HistoryPager' => 'includes/HistoryPage.php',
+ 'Html' => 'includes/Html.php',
'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php',
'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
'HTMLFileCache' => 'includes/HTMLFileCache.php',
+ 'HTMLForm' => 'includes/HTMLForm.php',
+ 'HTMLFormField' => 'includes/HTMLForm.php',
+ 'HTMLTextField' => 'includes/HTMLForm.php',
+ 'HTMLIntField' => 'includes/HTMLForm.php',
+ 'HTMLTextAreaField' => 'includes/HTMLForm.php',
+ 'HTMLFloatField' => 'includes/HTMLForm.php',
+ 'HTMLHiddenField' => 'includes/HTMLForm.php',
+ 'HTMLSubmitField' => 'includes/HTMLForm.php',
+ 'HTMLEditTools' => 'includes/HTMLForm.php',
+ 'HTMLCheckField' => 'includes/HTMLForm.php',
+ 'HTMLSelectField' => 'includes/HTMLForm.php',
+ 'HTMLSelectOrOtherField' => 'includes/HTMLForm.php',
+ 'HTMLMultiSelectField' => 'includes/HTMLForm.php',
+ 'HTMLRadioField' => 'includes/HTMLForm.php',
+ 'HTMLInfoField' => 'includes/HTMLForm.php',
'Http' => 'includes/HttpFunctions.php',
+ 'HttpRequest' => 'includes/HttpFunctions.php',
'IEContentAnalyzer' => 'includes/IEContentAnalyzer.php',
'ImageGallery' => 'includes/ImageGallery.php',
'ImageHistoryList' => 'includes/ImagePage.php',
+ 'ImageHistoryPseudoPager' => 'includes/ImagePage.php',
'ImagePage' => 'includes/ImagePage.php',
'ImageQueryPage' => 'includes/ImageQueryPage.php',
'IncludableSpecialPage' => 'includes/SpecialPage.php',
@@ -100,6 +134,10 @@ $wgAutoloadLocalClasses = array(
'Interwiki' => 'includes/Interwiki.php',
'IP' => 'includes/IP.php',
'Job' => 'includes/JobQueue.php',
+ 'JSMin' => 'includes/JSMin.php',
+ 'LCStore_DB' => 'includes/LocalisationCache.php',
+ 'LCStore_CDB' => 'includes/LocalisationCache.php',
+ 'LCStore_Null' => 'includes/LocalisationCache.php',
'License' => 'includes/Licenses.php',
'Licenses' => 'includes/Licenses.php',
'LinkBatch' => 'includes/LinkBatch.php',
@@ -107,6 +145,8 @@ $wgAutoloadLocalClasses = array(
'Linker' => 'includes/Linker.php',
'LinkFilter' => 'includes/LinkFilter.php',
'LinksUpdate' => 'includes/LinksUpdate.php',
+ 'LocalisationCache' => 'includes/LocalisationCache.php',
+ 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php',
'LogPage' => 'includes/LogPage.php',
'LogPager' => 'includes/LogEventsList.php',
'LogEventsList' => 'includes/LogEventsList.php',
@@ -122,24 +162,24 @@ $wgAutoloadLocalClasses = array(
'MediaWikiBagOStuff' => 'includes/BagOStuff.php',
'MediaWiki_I18N' => 'includes/SkinTemplate.php',
'MediaWiki' => 'includes/Wiki.php',
- 'memcached' => 'includes/memcached-client.php',
+ 'MemCachedClientforWiki' => 'includes/memcached-client.php',
'MessageCache' => 'includes/MessageCache.php',
'MimeMagic' => 'includes/MimeMagic.php',
'MWException' => 'includes/Exception.php',
+ 'MWMemcached' => 'includes/memcached-client.php',
'MWNamespace' => 'includes/Namespace.php',
- 'MySQLSearchResultSet' => 'includes/SearchMySQL.php',
'Namespace' => 'includes/NamespaceCompat.php', // Compat
'OldChangesList' => 'includes/ChangesList.php',
- 'OracleSearchResultSet' => 'includes/SearchOracle.php',
'OutputPage' => 'includes/OutputPage.php',
- 'PageHistory' => 'includes/PageHistory.php',
- 'PageHistoryPager' => 'includes/PageHistory.php',
'PageQueryPage' => 'includes/PageQueryPage.php',
+ 'PageHistory' => 'includes/HistoryPage.php',
+ 'PageHistoryPager' => 'includes/HistoryPage.php',
'Pager' => 'includes/Pager.php',
'PasswordError' => 'includes/User.php',
'PatrolLog' => 'includes/PatrolLog.php',
- 'PostgresSearchResult' => 'includes/SearchPostgres.php',
- 'PostgresSearchResultSet' => 'includes/SearchPostgres.php',
+ 'PoolCounter' => 'includes/PoolCounter.php',
+ 'PoolCounter_Stub' => 'includes/PoolCounter.php',
+ 'Preferences' => 'includes/Preferences.php',
'PrefixSearch' => 'includes/PrefixSearch.php',
'Profiler' => 'includes/Profiler.php',
'ProfilerSimple' => 'includes/ProfilerSimple.php',
@@ -161,20 +201,9 @@ $wgAutoloadLocalClasses = array(
'Revision' => 'includes/Revision.php',
'RSSFeed' => 'includes/Feed.php',
'Sanitizer' => 'includes/Sanitizer.php',
- 'SearchEngineDummy' => 'includes/SearchEngine.php',
- 'SearchEngine' => 'includes/SearchEngine.php',
- 'SearchHighlighter' => 'includes/SearchEngine.php',
- 'SearchMySQL4' => 'includes/SearchMySQL4.php',
- 'SearchMySQL' => 'includes/SearchMySQL.php',
- 'SearchOracle' => 'includes/SearchOracle.php',
- 'SearchPostgres' => 'includes/SearchPostgres.php',
- 'SearchResult' => 'includes/SearchEngine.php',
- 'SearchResultSet' => 'includes/SearchEngine.php',
- 'SearchResultTooMany' => 'includes/SearchEngine.php',
- 'SearchUpdate' => 'includes/SearchUpdate.php',
- 'SearchUpdateMyISAM' => 'includes/SearchUpdate.php',
'SiteConfiguration' => 'includes/SiteConfiguration.php',
'SiteStats' => 'includes/SiteStats.php',
+ 'SiteStatsInit' => 'includes/SiteStats.php',
'SiteStatsUpdate' => 'includes/SiteStats.php',
'Skin' => 'includes/Skin.php',
'SkinTemplate' => 'includes/SkinTemplate.php',
@@ -185,7 +214,13 @@ $wgAutoloadLocalClasses = array(
'SpecialRedirectToSpecial' => 'includes/SpecialPage.php',
'SqlBagOStuff' => 'includes/BagOStuff.php',
'SquidUpdate' => 'includes/SquidUpdate.php',
+ 'SquidPurgeClient' => 'includes/SquidPurgeClient.php',
+ 'SquidPurgeClientPool' => 'includes/SquidPurgeClient.php',
'Status' => 'includes/Status.php',
+ 'StubContLang' => 'includes/StubObject.php',
+ 'StubUser' => 'includes/StubObject.php',
+ 'StubUserLang' => 'includes/StubObject.php',
+ 'StubObject' => 'includes/StubObject.php',
'StringUtils' => 'includes/StringUtils.php',
'TablePager' => 'includes/Pager.php',
'ThumbnailImage' => 'includes/MediaTransformOutput.php',
@@ -193,15 +228,20 @@ $wgAutoloadLocalClasses = array(
'TitleDependency' => 'includes/CacheDependency.php',
'Title' => 'includes/Title.php',
'TitleArray' => 'includes/TitleArray.php',
+ 'TitleArrayFromResult' => 'includes/TitleArray.php',
'TitleListDependency' => 'includes/CacheDependency.php',
'TransformParameterError' => 'includes/MediaTransformOutput.php',
- 'TurckBagOStuff' => 'includes/BagOStuff.php',
'UnlistedSpecialPage' => 'includes/SpecialPage.php',
+ 'UploadBase' => 'includes/upload/UploadBase.php',
+ 'UploadFromStash' => 'includes/upload/UploadFromStash.php',
+ 'UploadFromFile' => 'includes/upload/UploadFromFile.php',
+ 'UploadFromUrl' => 'includes/upload/UploadFromUrl.php',
'User' => 'includes/User.php',
'UserArray' => 'includes/UserArray.php',
'UserArrayFromResult' => 'includes/UserArray.php',
'UserMailer' => 'includes/UserMailer.php',
'UserRightsProxy' => 'includes/UserRightsProxy.php',
+ 'WantedQueryPage' => 'includes/QueryPage.php',
'WatchedItem' => 'includes/WatchedItem.php',
'WatchlistEditor' => 'includes/WatchlistEditor.php',
'WebRequest' => 'includes/WebRequest.php',
@@ -209,6 +249,8 @@ $wgAutoloadLocalClasses = array(
'WikiError' => 'includes/WikiError.php',
'WikiErrorMsg' => 'includes/WikiError.php',
'WikiExporter' => 'includes/Export.php',
+ 'WikiMap' => 'includes/WikiMap.php',
+ 'WikiReference' => 'includes/WikiMap.php',
'WikiXmlError' => 'includes/WikiError.php',
'XCacheBagOStuff' => 'includes/BagOStuff.php',
'XmlDumpWriter' => 'includes/Export.php',
@@ -282,6 +324,7 @@ $wgAutoloadLocalClasses = array(
'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
+ 'ApiQueryTags' => 'includes/api/ApiQueryTags.php',
'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php',
'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php',
'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
@@ -290,56 +333,58 @@ $wgAutoloadLocalClasses = array(
'ApiRollback' => 'includes/api/ApiRollback.php',
'ApiUnblock' => 'includes/api/ApiUnblock.php',
'ApiUndelete' => 'includes/api/ApiUndelete.php',
+ 'ApiUserrights' => 'includes/api/ApiUserrights.php',
+ 'ApiUpload' => 'includes/api/ApiUpload.php',
'ApiWatch' => 'includes/api/ApiWatch.php',
- 'Services_JSON' => 'includes/api/ApiFormatJson_json.php',
- 'Services_JSON_Error' => 'includes/api/ApiFormatJson_json.php',
+
'Spyc' => 'includes/api/ApiFormatYaml_spyc.php',
'UsageException' => 'includes/api/ApiMain.php',
+ # includes/json
+ 'Services_JSON' => 'includes/json/Services_JSON.php',
+ 'Services_JSON_Error' => 'includes/json/Services_JSON.php',
+ 'FormatJson' => 'includes/json/FormatJson.php',
+
# includes/db
'Blob' => 'includes/db/Database.php',
'ChronologyProtector' => 'includes/db/LBFactory.php',
- 'Database' => 'includes/db/Database.php',
+ 'Database' => 'includes/db/DatabaseMysql.php',
+ 'DatabaseBase' => 'includes/db/Database.php',
'DatabaseMssql' => 'includes/db/DatabaseMssql.php',
- 'DatabaseMysql' => 'includes/db/Database.php',
+ 'DatabaseMysql' => 'includes/db/DatabaseMysql.php',
'DatabaseOracle' => 'includes/db/DatabaseOracle.php',
'DatabasePostgres' => 'includes/db/DatabasePostgres.php',
'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php',
+ 'DatabaseSqliteStandalone' => 'includes/db/DatabaseSqlite.php',
'DBConnectionError' => 'includes/db/Database.php',
'DBError' => 'includes/db/Database.php',
'DBObject' => 'includes/db/Database.php',
'DBQueryError' => 'includes/db/Database.php',
'DBUnexpectedError' => 'includes/db/Database.php',
+ 'IBM_DB2Blob' => 'includes/db/DatabaseIbm_db2.php',
'LBFactory' => 'includes/db/LBFactory.php',
'LBFactory_Multi' => 'includes/db/LBFactory_Multi.php',
'LBFactory_Simple' => 'includes/db/LBFactory.php',
+ 'LikeMatch' => 'includes/db/Database.php',
'LoadBalancer' => 'includes/db/LoadBalancer.php',
'LoadMonitor' => 'includes/db/LoadMonitor.php',
'LoadMonitor_MySQL' => 'includes/db/LoadMonitor.php',
'MSSQLField' => 'includes/db/DatabaseMssql.php',
'MySQLField' => 'includes/db/Database.php',
- 'MySQLMasterPos' => 'includes/db/Database.php',
+ 'MySQLMasterPos' => 'includes/db/DatabaseMysql.php',
'ORABlob' => 'includes/db/DatabaseOracle.php',
+ 'ORAField' => 'includes/db/DatabaseOracle.php',
'ORAResult' => 'includes/db/DatabaseOracle.php',
'PostgresField' => 'includes/db/DatabasePostgres.php',
'ResultWrapper' => 'includes/db/Database.php',
'SQLiteField' => 'includes/db/DatabaseSqlite.php',
-
'DatabaseIbm_db2' => 'includes/db/DatabaseIbm_db2.php',
'IBM_DB2Field' => 'includes/db/DatabaseIbm_db2.php',
- 'IBM_DB2SearchResultSet' => 'includes/SearchIBM_DB2.php',
- 'SearchIBM_DB2' => 'includes/SearchIBM_DB2.php',
# includes/diff
- 'AncestorComparator' => 'includes/diff/HTMLDiff.php',
- 'AnchorToString' => 'includes/diff/HTMLDiff.php',
'ArrayDiffFormatter' => 'includes/diff/DifferenceEngine.php',
- 'BodyNode' => 'includes/diff/Nodes.php',
- 'ChangeText' => 'includes/diff/HTMLDiff.php',
- 'ChangeTextGenerator' => 'includes/diff/HTMLDiff.php',
- 'DelegatingContentHandler' => 'includes/diff/HTMLDiff.php',
'_DiffEngine' => 'includes/diff/DifferenceEngine.php',
- 'DifferenceEngine' => 'includes/diff/DifferenceEngine.php',
+ 'DifferenceEngine' => 'includes/diff/DifferenceInterface.php',
'DiffFormatter' => 'includes/diff/DifferenceEngine.php',
'Diff' => 'includes/diff/DifferenceEngine.php',
'_DiffOp_Add' => 'includes/diff/DifferenceEngine.php',
@@ -347,34 +392,17 @@ $wgAutoloadLocalClasses = array(
'_DiffOp_Copy' => 'includes/diff/DifferenceEngine.php',
'_DiffOp_Delete' => 'includes/diff/DifferenceEngine.php',
'_DiffOp' => 'includes/diff/DifferenceEngine.php',
- 'DomTreeBuilder' => 'includes/diff/HTMLDiff.php',
- 'DummyNode' => 'includes/diff/Nodes.php',
- 'HTMLDiffer' => 'includes/diff/HTMLDiff.php',
- 'HTMLOutput' => 'includes/diff/HTMLDiff.php',
'_HWLDF_WordAccumulator' => 'includes/diff/DifferenceEngine.php',
- 'ImageNode' => 'includes/diff/Nodes.php',
- 'LastCommonParentResult' => 'includes/diff/HTMLDiff.php',
'MappedDiff' => 'includes/diff/DifferenceEngine.php',
- 'Modification' => 'includes/diff/HTMLDiff.php',
- 'NoContentTagToString' => 'includes/diff/HTMLDiff.php',
- 'Node' => 'includes/diff/Nodes.php',
'RangeDifference' => 'includes/diff/Diff.php',
'TableDiffFormatter' => 'includes/diff/DifferenceEngine.php',
- 'TagNode' => 'includes/diff/Nodes.php',
- 'TagToString' => 'includes/diff/HTMLDiff.php',
- 'TagToStringFactory' => 'includes/diff/HTMLDiff.php',
- 'TextNode' => 'includes/diff/Nodes.php',
- 'TextNodeDiffer' => 'includes/diff/HTMLDiff.php',
- 'TextOnlyComparator' => 'includes/diff/HTMLDiff.php',
'UnifiedDiffFormatter' => 'includes/diff/DifferenceEngine.php',
- 'WhiteSpaceNode' => 'includes/diff/Nodes.php',
'WikiDiff3' => 'includes/diff/Diff.php',
'WordLevelDiff' => 'includes/diff/DifferenceEngine.php',
# includes/filerepo
'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
'File' => 'includes/filerepo/File.php',
- 'FileCache' => 'includes/filerepo/FileCache.php',
'FileRepo' => 'includes/filerepo/FileRepo.php',
'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
'ForeignAPIFile' => 'includes/filerepo/ForeignAPIFile.php',
@@ -408,10 +436,13 @@ $wgAutoloadLocalClasses = array(
# includes/parser
'CoreLinkFunctions' => 'includes/parser/CoreLinkFunctions.php',
'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php',
+ 'CoreTagHooks' => 'includes/parser/CoreTagHooks.php',
'DateFormatter' => 'includes/parser/DateFormatter.php',
'LinkHolderArray' => 'includes/parser/LinkHolderArray.php',
- 'LinkMarkerReplacer' => 'includes/parser/LinkMarkerReplacer.php',
+ 'LinkMarkerReplacer' => 'includes/parser/Parser_LinkHooks.php',
'OnlyIncludeReplacer' => 'includes/parser/Parser.php',
+ 'PPCustomFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPCustomFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php',
'PPDPart' => 'includes/parser/Preprocessor_DOM.php',
'PPDPart_Hash' => 'includes/parser/Preprocessor_Hash.php',
@@ -442,7 +473,31 @@ $wgAutoloadLocalClasses = array(
'StripState' => 'includes/parser/Parser.php',
'MWTidy' => 'includes/parser/Tidy.php',
+ # includes/search
+ 'MySQLSearchResultSet' => 'includes/search/SearchMySQL.php',
+ 'PostgresSearchResult' => 'includes/search/SearchPostgres.php',
+ 'PostgresSearchResultSet' => 'includes/search/SearchPostgres.php',
+ 'SearchEngineDummy' => 'includes/search/SearchEngine.php',
+ 'SearchEngine' => 'includes/search/SearchEngine.php',
+ 'SearchHighlighter' => 'includes/search/SearchEngine.php',
+ 'SearchIBM_DB2' => 'includes/search/SearchIBM_DB2.php',
+ 'SearchMySQL4' => 'includes/search/SearchMySQL4.php',
+ 'SearchMySQL' => 'includes/search/SearchMySQL.php',
+ 'SearchOracle' => 'includes/search/SearchOracle.php',
+ 'SearchPostgres' => 'includes/search/SearchPostgres.php',
+ 'SearchResult' => 'includes/search/SearchEngine.php',
+ 'SearchResultSet' => 'includes/search/SearchEngine.php',
+ 'SearchResultTooMany' => 'includes/search/SearchEngine.php',
+ 'SearchSqlite' => 'includes/search/SearchSqlite.php',
+ 'SearchUpdate' => 'includes/search/SearchUpdate.php',
+ 'SearchUpdateMyISAM' => 'includes/search/SearchUpdate.php',
+ 'SqliteSearchResultSet' => 'includes/search/SearchSqlite.php',
+ 'SqlSearchResultSet' => 'includes/search/SearchEngine.php',
+
# includes/specials
+ 'SpecialAllmessages' => 'includes/specials/SpecialAllmessages.php',
+ 'ActiveUsersPager' => 'includes/specials/SpecialActiveusers.php',
+ 'AllmessagesTablePager' => 'includes/specials/SpecialAllmessages.php',
'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
'ContribsPager' => 'includes/specials/SpecialContributions.php',
@@ -456,6 +511,7 @@ $wgAutoloadLocalClasses = array(
'EmailConfirmation' => 'includes/specials/SpecialConfirmemail.php',
'EmailInvalidation' => 'includes/specials/SpecialConfirmemail.php',
'EmailUserForm' => 'includes/specials/SpecialEmailuser.php',
+ 'FakeResultWrapper' => 'includes/specials/SpecialAllmessages.php',
'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php',
'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php',
'IPBlockForm' => 'includes/specials/SpecialBlockip.php',
@@ -482,26 +538,41 @@ $wgAutoloadLocalClasses = array(
'PageArchive' => 'includes/specials/SpecialUndelete.php',
'SpecialResetpass' => 'includes/specials/SpecialResetpass.php',
'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php',
- 'PreferencesForm' => 'includes/specials/SpecialPreferences.php',
+ 'PreferencesForm' => 'includes/Preferences.php',
'RandomPage' => 'includes/specials/SpecialRandompage.php',
'SpecialRevisionDelete' => 'includes/specials/SpecialRevisiondelete.php',
'RevisionDeleter' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_RevisionList' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_RevisionItem' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_ArchiveList' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_ArchiveItem' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_FileList' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_FileItem' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_ArchivedFileList' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_ArchivedFileItem' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_LogList' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevDel_LogItem' => 'includes/specials/SpecialRevisiondelete.php',
'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
+ 'SpecialActiveUsers' => 'includes/specials/SpecialActiveusers.php',
'SpecialAllpages' => 'includes/specials/SpecialAllpages.php',
+ 'SpecialBlankpage' => 'includes/specials/SpecialBlankpage.php',
'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
'SpecialExport' => 'includes/specials/SpecialExport.php',
'SpecialImport' => 'includes/specials/SpecialImport.php',
'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
'SpecialMostlinkedtemplates' => 'includes/specials/SpecialMostlinkedtemplates.php',
+ 'SpecialPreferences' => 'includes/specials/SpecialPreferences.php',
'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php',
'SpecialRandomredirect' => 'includes/specials/SpecialRandomredirect.php',
- 'SpecialRecentchanges' => 'includes/specials/SpecialRecentchanges.php',
+ 'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php',
'SpecialRecentchangeslinked' => 'includes/specials/SpecialRecentchangeslinked.php',
'SpecialSearch' => 'includes/specials/SpecialSearch.php',
- 'SpecialSearchOld' => 'includes/specials/SpecialSearch.php',
'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
'SpecialTags' => 'includes/specials/SpecialTags.php',
+ 'SpecialUpload' => 'includes/specials/SpecialUpload.php',
'SpecialVersion' => 'includes/specials/SpecialVersion.php',
+ 'SpecialWhatlinkshere' => 'includes/specials/SpecialWhatlinkshere.php',
+ 'SpecialWhatLinksHere' => 'includes/specials/SpecialWhatlinkshere.php',
'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
'UncategorizedTemplatesPage' => 'includes/specials/SpecialUncategorizedtemplates.php',
@@ -511,7 +582,7 @@ $wgAutoloadLocalClasses = array(
'UnusedtemplatesPage' => 'includes/specials/SpecialUnusedtemplates.php',
'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
'UploadForm' => 'includes/specials/SpecialUpload.php',
- 'UploadFormMogile' => 'includes/specials/SpecialUploadMogile.php',
+ 'UploadSourceField' => 'includes/specials/SpecialUpload.php',
'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
'UsersPager' => 'includes/specials/SpecialListusers.php',
'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
@@ -530,13 +601,14 @@ $wgAutoloadLocalClasses = array(
# languages
'Language' => 'languages/Language.php',
'FakeConverter' => 'languages/Language.php',
+ 'LanguageConverter' => 'languages/LanguageConverter.php',
# maintenance/language
'statsOutput' => 'maintenance/language/StatOutputs.php',
'wikiStatsOutput' => 'maintenance/language/StatOutputs.php',
- 'metawikiStatsOutput' => 'maintenance/language/StatOutputs.php',
'textStatsOutput' => 'maintenance/language/StatOutputs.php',
'csvStatsOutput' => 'maintenance/language/StatOutputs.php',
+ 'SevenZipStream' => 'maintenance/7zip.inc',
);
@@ -544,7 +616,7 @@ class AutoLoader {
/**
* autoload - take a class name and attempt to load it
*
- * @param string $className Name of class we're looking for.
+ * @param $className String: name of class we're looking for.
* @return bool Returning false is important on failure as
* it allows Zend to try and look in other registered autoloaders
* as well.
@@ -567,7 +639,7 @@ class AutoLoader {
}
}
if ( !$filename ) {
- if( function_exists( 'wfDebug' ) )
+ if( function_exists( 'wfDebug' ) )
wfDebug( "Class {$className} not found; skipped loading\n" );
# Give up
return false;
@@ -592,6 +664,17 @@ class AutoLoader {
}
}
}
+
+ /**
+ * Force a class to be run through the autoloader, helpful for things like
+ * Sanitizer that have define()s outside of their class definition. Of course
+ * this wouldn't be necessary if everything in MediaWiki was class-based. Sigh.
+ *
+ * @return Boolean Return the results of class_exists() so we know if we were successful
+ */
+ static function loadClass( $class ) {
+ return class_exists( $class );
+ }
}
function wfLoadAllExtensions() {
@@ -604,4 +687,6 @@ if ( function_exists( 'spl_autoload_register' ) ) {
function __autoload( $class ) {
AutoLoader::autoload( $class );
}
+
+ ini_set( 'unserialize_callback_func', '__autoload' );
}
diff --git a/includes/Autopromote.php b/includes/Autopromote.php
index c8a4c03b..c0adff43 100644
--- a/includes/Autopromote.php
+++ b/includes/Autopromote.php
@@ -1,5 +1,4 @@
<?php
-
/**
* This class checks if user can get extra rights
* because of conditions specified in $wgAutopromote
@@ -18,9 +17,9 @@ class Autopromote {
if( self::recCheckCondition( $cond, $user ) )
$promote[] = $group;
}
-
+
wfRunHooks( 'GetAutoPromoteGroups', array( $user, &$promote ) );
-
+
return $promote;
}
@@ -116,6 +115,8 @@ class Autopromote {
return $cond[1] == wfGetIP();
case APCOND_IPINRANGE:
return IP::isInRange( wfGetIP(), $cond[1] );
+ case APCOND_BLOCKED:
+ return $user->isBlocked();
default:
$result = null;
wfRunHooks( 'AutopromoteCondition', array( $cond[0], array_slice( $cond, 1 ), $user, &$result ) );
diff --git a/includes/BacklinkCache.php b/includes/BacklinkCache.php
index a7bcd858..53f92dd9 100644
--- a/includes/BacklinkCache.php
+++ b/includes/BacklinkCache.php
@@ -1,10 +1,9 @@
<?php
-
/**
* Class for fetching backlink lists, approximate backlink counts and partitions.
* Instances of this class should typically be fetched with $title->getBacklinkCache().
*
- * Ideally you should only get your backlinks from here when you think there is some
+ * Ideally you should only get your backlinks from here when you think there is some
* advantage in caching them. Otherwise it's just a waste of memory.
*/
class BacklinkCache {
@@ -47,44 +46,53 @@ class BacklinkCache {
/**
* Get the backlinks for a given table. Cached in process memory only.
- * @param string $table
+ * @param $table String
+ * @param $startId Integer or false
+ * @param $endId Integer or false
* @return TitleArray
*/
public function getLinks( $table, $startId = false, $endId = false ) {
wfProfileIn( __METHOD__ );
+ $fromField = $this->getPrefix( $table ) . '_from';
+
if ( $startId || $endId ) {
// Partial range, not cached
- wfDebug( __METHOD__.": from DB (uncacheable range)\n" );
+ wfDebug( __METHOD__ . ": from DB (uncacheable range)\n" );
$conds = $this->getConditions( $table );
// Use the from field in the condition rather than the joined page_id,
// because databases are stupid and don't necessarily propagate indexes.
- $fromField = $this->getPrefix( $table ) . '_from';
if ( $startId ) {
$conds[] = "$fromField >= " . intval( $startId );
}
if ( $endId ) {
$conds[] = "$fromField <= " . intval( $endId );
}
- $res = $this->getDB()->select(
+ $res = $this->getDB()->select(
array( $table, 'page' ),
- array( 'page_namespace', 'page_title', 'page_id'),
+ array( 'page_namespace', 'page_title', 'page_id' ),
$conds,
__METHOD__,
- array('STRAIGHT_JOIN') );
+ array(
+ 'STRAIGHT_JOIN',
+ 'ORDER BY' => $fromField
+ ) );
$ta = TitleArray::newFromResult( $res );
wfProfileOut( __METHOD__ );
return $ta;
}
if ( !isset( $this->fullResultCache[$table] ) ) {
- wfDebug( __METHOD__.": from DB\n" );
- $res = $this->getDB()->select(
+ wfDebug( __METHOD__ . ": from DB\n" );
+ $res = $this->getDB()->select(
array( $table, 'page' ),
array( 'page_namespace', 'page_title', 'page_id' ),
$this->getConditions( $table ),
__METHOD__,
- array('STRAIGHT_JOIN') );
+ array(
+ 'STRAIGHT_JOIN',
+ 'ORDER BY' => $fromField,
+ ) );
$this->fullResultCache[$table] = $res;
}
$ta = TitleArray::newFromResult( $this->fullResultCache[$table] );
@@ -103,6 +111,7 @@ class BacklinkCache {
'templatelinks' => 'tl',
'redirect' => 'rd',
);
+
if ( isset( $prefixes[$table] ) ) {
return $prefixes[$table];
} else {
@@ -115,6 +124,7 @@ class BacklinkCache {
*/
protected function getConditions( $table ) {
$prefix = $this->getPrefix( $table );
+
switch ( $table ) {
case 'pagelinks':
case 'templatelinks':
@@ -126,13 +136,13 @@ class BacklinkCache {
);
break;
case 'imagelinks':
- $conds = array(
+ $conds = array(
'il_to' => $this->title->getDBkey(),
'page_id=il_from'
);
break;
case 'categorylinks':
- $conds = array(
+ $conds = array(
'cl_to' => $this->title->getDBkey(),
'page_id=cl_from',
);
@@ -150,10 +160,12 @@ class BacklinkCache {
if ( isset( $this->fullResultCache[$table] ) ) {
return $this->fullResultCache[$table]->numRows();
}
+
if ( isset( $this->partitionCache[$table] ) ) {
$entry = reset( $this->partitionCache[$table] );
return $entry['numRows'];
}
+
$titleArray = $this->getLinks( $table );
return $titleArray->count();
}
@@ -163,33 +175,40 @@ class BacklinkCache {
* Returns an array giving the start and end of each range. The first batch has
* a start of false, and the last batch has an end of false.
*
- * @param string $table The links table name
- * @param integer $batchSize
- * @return array
+ * @param $table String: the links table name
+ * @param $batchSize Integer
+ * @return Array
*/
public function partition( $table, $batchSize ) {
// Try cache
if ( isset( $this->partitionCache[$table][$batchSize] ) ) {
- wfDebug( __METHOD__.": got from partition cache\n" );
+ wfDebug( __METHOD__ . ": got from partition cache\n" );
return $this->partitionCache[$table][$batchSize]['batches'];
}
+
$this->partitionCache[$table][$batchSize] = false;
$cacheEntry =& $this->partitionCache[$table][$batchSize];
// Try full result cache
if ( isset( $this->fullResultCache[$table] ) ) {
$cacheEntry = $this->partitionResult( $this->fullResultCache[$table], $batchSize );
- wfDebug( __METHOD__.": got from full result cache\n" );
+ wfDebug( __METHOD__ . ": got from full result cache\n" );
return $cacheEntry['batches'];
}
+
// Try memcached
global $wgMemc;
- $memcKey = wfMemcKey( 'backlinks', md5( $this->title->getPrefixedDBkey() ),
- $table, $batchSize );
+ $memcKey = wfMemcKey(
+ 'backlinks',
+ md5( $this->title->getPrefixedDBkey() ),
+ $table,
+ $batchSize
+ );
$memcValue = $wgMemc->get( $memcKey );
+
if ( is_array( $memcValue ) ) {
$cacheEntry = $memcValue;
- wfDebug( __METHOD__.": got from memcached $memcKey\n" );
+ wfDebug( __METHOD__ . ": got from memcached $memcKey\n" );
return $cacheEntry['batches'];
}
// Fetch from database
@@ -197,17 +216,18 @@ class BacklinkCache {
$cacheEntry = $this->partitionResult( $this->fullResultCache[$table], $batchSize );
// Save to memcached
$wgMemc->set( $memcKey, $cacheEntry, self::CACHE_EXPIRY );
- wfDebug( __METHOD__.": got from database\n" );
+ wfDebug( __METHOD__ . ": got from database\n" );
return $cacheEntry['batches'];
}
- /**
+ /**
* Partition a DB result with backlinks in it into batches
*/
protected function partitionResult( $res, $batchSize ) {
$batches = array();
$numRows = $res->numRows();
$numBatches = ceil( $numRows / $batchSize );
+
for ( $i = 0; $i < $numBatches; $i++ ) {
if ( $i == 0 ) {
$start = false;
@@ -217,6 +237,7 @@ class BacklinkCache {
$row = $res->fetchObject();
$start = $row->page_id;
}
+
if ( $i == $numBatches - 1 ) {
$end = false;
} else {
@@ -225,6 +246,12 @@ class BacklinkCache {
$row = $res->fetchObject();
$end = $row->page_id - 1;
}
+
+ # Sanity check order
+ if ( $start && $end && $start > $end ) {
+ throw new MWException( __METHOD__ . ': Internal error: query result out of order' );
+ }
+
$batches[] = array( $start, $end );
}
return array( 'numRows' => $numRows, 'batches' => $batches );
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index ffa8a0bb..ac0263d8 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -32,134 +32,129 @@
* backends for local hash array and SQL table included:
* <code>
* $bag = new HashBagOStuff();
- * $bag = new MediaWikiBagOStuff($tablename); # connect to db first
+ * $bag = new SqlBagOStuff(); # connect to db first
* </code>
*
* @ingroup Cache
*/
-class BagOStuff {
- var $debugmode;
+abstract class BagOStuff {
+ var $debugMode = false;
- function __construct() {
- $this->set_debug( false );
- }
-
- function set_debug($bool) {
- $this->debugmode = $bool;
+ public function set_debug( $bool ) {
+ $this->debugMode = $bool;
}
/* *** THE GUTS OF THE OPERATION *** */
/* Override these with functional things in subclasses */
- function get($key) {
- /* stub */
- return false;
- }
+ /**
+ * Get an item with the given key. Returns false if it does not exist.
+ * @param $key string
+ */
+ abstract public function get( $key );
- function set($key, $value, $exptime=0) {
- /* stub */
- return false;
- }
+ /**
+ * Set an item.
+ * @param $key string
+ * @param $value mixed
+ * @param $exptime int Either an interval in seconds or a unix timestamp for expiry
+ */
+ abstract public function set( $key, $value, $exptime = 0 );
- function delete($key, $time=0) {
- /* stub */
- return false;
- }
+ /*
+ * Delete an item.
+ * @param $key string
+ * @param $time int Amount of time to delay the operation (mostly memcached-specific)
+ */
+ abstract public function delete( $key, $time = 0 );
- function lock($key, $timeout = 0) {
+ public function lock( $key, $timeout = 0 ) {
/* stub */
return true;
}
- function unlock($key) {
+ public function unlock( $key ) {
/* stub */
return true;
}
- function keys() {
+ public function keys() {
/* stub */
return array();
}
/* *** Emulated functions *** */
/* Better performance can likely be got with custom written versions */
- function get_multi($keys) {
+ public function get_multi( $keys ) {
$out = array();
- foreach($keys as $key)
- $out[$key] = $this->get($key);
+
+ foreach ( $keys as $key ) {
+ $out[$key] = $this->get( $key );
+ }
+
return $out;
}
- function set_multi($hash, $exptime=0) {
- foreach($hash as $key => $value)
- $this->set($key, $value, $exptime);
+ public function set_multi( $hash, $exptime = 0 ) {
+ foreach ( $hash as $key => $value ) {
+ $this->set( $key, $value, $exptime );
+ }
}
- function add($key, $value, $exptime=0) {
- if( $this->get($key) == false ) {
- $this->set($key, $value, $exptime);
+ public function add( $key, $value, $exptime = 0 ) {
+ if ( $this->get( $key ) == false ) {
+ $this->set( $key, $value, $exptime );
return true;
}
}
- function add_multi($hash, $exptime=0) {
- foreach($hash as $key => $value)
- $this->add($key, $value, $exptime);
+ public function add_multi( $hash, $exptime = 0 ) {
+ foreach ( $hash as $key => $value ) {
+ $this->add( $key, $value, $exptime );
+ }
}
- function delete_multi($keys, $time=0) {
- foreach($keys as $key)
- $this->delete($key, $time);
+ public function delete_multi( $keys, $time = 0 ) {
+ foreach ( $keys as $key ) {
+ $this->delete( $key, $time );
+ }
}
- function replace($key, $value, $exptime=0) {
- if( $this->get($key) !== false )
- $this->set($key, $value, $exptime);
+ public function replace( $key, $value, $exptime = 0 ) {
+ if ( $this->get( $key ) !== false ) {
+ $this->set( $key, $value, $exptime );
+ }
}
- function incr($key, $value=1) {
- if ( !$this->lock($key) ) {
+ public function incr( $key, $value = 1 ) {
+ if ( !$this->lock( $key ) ) {
return false;
}
- $value = intval($value);
- if($value < 0) $value = 0;
+ $value = intval( $value );
$n = false;
- if( ($n = $this->get($key)) !== false ) {
+ if ( ( $n = $this->get( $key ) ) !== false ) {
$n += $value;
- $this->set($key, $n); // exptime?
+ $this->set( $key, $n ); // exptime?
}
- $this->unlock($key);
+ $this->unlock( $key );
return $n;
}
- function decr($key, $value=1) {
- if ( !$this->lock($key) ) {
- return false;
- }
- $value = intval($value);
- if($value < 0) $value = 0;
-
- $m = false;
- if( ($n = $this->get($key)) !== false ) {
- $m = $n - $value;
- if($m < 0) $m = 0;
- $this->set($key, $m); // exptime?
- }
- $this->unlock($key);
- return $m;
+ public function decr( $key, $value = 1 ) {
+ return $this->incr( $key, - $value );
}
- function _debug($text) {
- if($this->debugmode)
- wfDebug("BagOStuff debug: $text\n");
+ public function debug( $text ) {
+ 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)) {
+ protected function convertExpiry( $exptime ) {
+ if ( ( $exptime != 0 ) && ( $exptime < 3600 * 24 * 30 ) ) {
return time() + $exptime;
} else {
return $exptime;
@@ -167,7 +162,6 @@ class BagOStuff {
}
}
-
/**
* Functional versions!
* This is a test of the interface, mainly. It stores things in an associative
@@ -182,30 +176,34 @@ class HashBagOStuff extends BagOStuff {
$this->bag = array();
}
- function _expire($key) {
+ protected function expire( $key ) {
$et = $this->bag[$key][1];
- if(($et == 0) || ($et > time()))
+ if ( ( $et == 0 ) || ( $et > time() ) ) {
return false;
- $this->delete($key);
+ }
+ $this->delete( $key );
return true;
}
- function get($key) {
- if(!$this->bag[$key])
+ function get( $key ) {
+ if ( !isset( $this->bag[$key] ) ) {
return false;
- if($this->_expire($key))
+ }
+ if ( $this->expire( $key ) ) {
return false;
+ }
return $this->bag[$key][0];
}
- function set($key,$value,$exptime=0) {
- $this->bag[$key] = array( $value, BagOStuff::convertExpiry( $exptime ) );
+ function set( $key, $value, $exptime = 0 ) {
+ $this->bag[$key] = array( $value, $this->convertExpiry( $exptime ) );
}
- function delete($key,$time=0) {
- if(!$this->bag[$key])
+ function delete( $key, $time = 0 ) {
+ if ( !isset( $this->bag[$key] ) ) {
return false;
- unset($this->bag[$key]);
+ }
+ unset( $this->bag[$key] );
return true;
}
@@ -215,182 +213,196 @@ class HashBagOStuff extends BagOStuff {
}
/**
- * Generic class to store objects in a database
+ * Class to store objects in the database
*
* @ingroup Cache
*/
-abstract class SqlBagOStuff extends BagOStuff {
- var $table;
- var $lastexpireall = 0;
+class SqlBagOStuff extends BagOStuff {
+ var $lb, $db;
+ var $lastExpireAll = 0;
- /**
- * Constructor
- *
- * @param $tablename String: name of the table to use
- */
- function __construct($tablename = 'objectcache') {
- $this->table = $tablename;
+ protected function getDB() {
+ global $wgDBtype;
+ if ( !isset( $this->db ) ) {
+ /* We must keep a separate connection to MySQL in order to avoid deadlocks
+ * However, SQLite has an opposite behaviour.
+ * @todo Investigate behaviour for other databases
+ */
+ if ( $wgDBtype == 'sqlite' ) {
+ $this->db = wfGetDB( DB_MASTER );
+ } else {
+ $this->lb = wfGetLBFactory()->newMainLB();
+ $this->db = $this->lb->getConnection( DB_MASTER );
+ $this->db->clearFlag( DBO_TRX );
+ }
+ }
+ return $this->db;
}
- function get($key) {
- /* expire old entries if any */
+ public function get( $key ) {
+ # expire old entries if any
$this->garbageCollect();
-
- $res = $this->_query(
- "SELECT value,exptime FROM $0 WHERE keyname='$1'", $key);
- if(!$res) {
- $this->_debug("get: ** error: " . $this->_dberror($res) . " **");
+ $db = $this->getDB();
+ $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
+ array( 'keyname' => $key ), __METHOD__ );
+ if ( !$row ) {
+ $this->debug( 'get: no matching rows' );
return false;
}
- if($row=$this->_fetchobject($res)) {
- $this->_debug("get: retrieved data; exp time is " . $row->exptime);
- if ( $row->exptime != $this->_maxdatetime() &&
- wfTimestamp( TS_UNIX, $row->exptime ) < time() )
- {
- $this->_debug("get: key has expired, deleting");
- $this->delete($key);
- return false;
+
+ $this->debug( "get: retrieved data; expiry time is " . $row->exptime );
+ if ( $this->isExpired( $row->exptime ) ) {
+ $this->debug( "get: key has expired, deleting" );
+ try {
+ $db->begin();
+ # Put the expiry time in the WHERE condition to avoid deleting a
+ # newly-inserted value
+ $db->delete( 'objectcache',
+ array(
+ 'keyname' => $key,
+ 'exptime' => $row->exptime
+ ), __METHOD__ );
+ $db->commit();
+ } catch ( DBQueryError $e ) {
+ $this->handleWriteError( $e );
}
- return $this->_unserialize($this->_blobdecode($row->value));
- } else {
- $this->_debug('get: no matching rows');
+ return false;
}
- return false;
+ return $this->unserialize( $db->decodeBlob( $row->value ) );
}
- function set($key,$value,$exptime=0) {
- if ( $this->_readonly() ) {
- return false;
- }
- $exptime = intval($exptime);
- if($exptime < 0) $exptime = 0;
- if($exptime == 0) {
- $exp = $this->_maxdatetime();
+ public function set( $key, $value, $exptime = 0 ) {
+ $db = $this->getDB();
+ $exptime = intval( $exptime );
+ if ( $exptime < 0 ) $exptime = 0;
+ if ( $exptime == 0 ) {
+ $encExpiry = $this->getMaxDateTime();
} else {
- if($exptime < 3.16e8) # ~10 years
+ if ( $exptime < 3.16e8 ) # ~10 years
$exptime += time();
- $exp = $this->_fromunixtime($exptime);
+ $encExpiry = $db->timestamp( $exptime );
}
- $this->_begin();
- $this->_query(
- "DELETE FROM $0 WHERE keyname='$1'", $key );
- $this->_doinsert($this->getTableName(), array(
+ try {
+ $db->begin();
+ $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
+ $db->insert( 'objectcache',
+ array(
'keyname' => $key,
- 'value' => $this->_blobencode($this->_serialize($value)),
- 'exptime' => $exp
- ));
- $this->_commit();
- return true; /* ? */
+ 'value' => $db->encodeBlob( $this->serialize( $value ) ),
+ 'exptime' => $encExpiry
+ ), __METHOD__ );
+ $db->commit();
+ } catch ( DBQueryError $e ) {
+ $this->handleWriteError( $e );
+ return false;
+ }
+ return true;
}
- function delete($key,$time=0) {
- if ( $this->_readonly() ) {
+ public function delete( $key, $time = 0 ) {
+ $db = $this->getDB();
+ try {
+ $db->begin();
+ $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
+ $db->commit();
+ } catch ( DBQueryError $e ) {
+ $this->handleWriteError( $e );
return false;
}
- $this->_begin();
- $this->_query(
- "DELETE FROM $0 WHERE keyname='$1'", $key );
- $this->_commit();
- return true; /* ? */
+ return true;
}
- function keys() {
- $res = $this->_query( "SELECT keyname FROM $0" );
- if(!$res) {
- $this->_debug("keys: ** error: " . $this->_dberror($res) . " **");
- return array();
+ public function incr( $key, $step = 1 ) {
+ $db = $this->getDB();
+ $step = intval( $step );
+
+ try {
+ $db->begin();
+ $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
+ array( 'keyname' => $key ), __METHOD__, array( 'FOR UPDATE' ) );
+ if ( $row === false ) {
+ // Missing
+ $db->commit();
+ return false;
+ }
+ $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
+ if ( $this->isExpired( $row->exptime ) ) {
+ // Expired, do not reinsert
+ $db->commit();
+ return false;
+ }
+
+ $oldValue = intval( $this->unserialize( $db->decodeBlob( $row->value ) ) );
+ $newValue = $oldValue + $step;
+ $db->insert( 'objectcache',
+ array(
+ 'keyname' => $key,
+ 'value' => $db->encodeBlob( $this->serialize( $newValue ) ),
+ 'exptime' => $row->exptime
+ ), __METHOD__ );
+ $db->commit();
+ } catch ( DBQueryError $e ) {
+ $this->handleWriteError( $e );
+ return false;
}
+ return $newValue;
+ }
+
+ public function keys() {
+ $db = $this->getDB();
+ $res = $db->select( 'objectcache', array( 'keyname' ), false, __METHOD__ );
$result = array();
- while( $row = $this->_fetchobject($res) ) {
+ foreach ( $res as $row ) {
$result[] = $row->keyname;
}
return $result;
}
- function getTableName() {
- return $this->table;
+ protected function isExpired( $exptime ) {
+ return $exptime != $this->getMaxDateTime() && wfTimestamp( TS_UNIX, $exptime ) < time();
}
- function _query($sql) {
- $reps = func_get_args();
- $reps[0] = $this->getTableName();
- // ewwww
- for($i=0;$i<count($reps);$i++) {
- $sql = str_replace(
- '$' . $i,
- $i > 0 ? $this->_strencode($reps[$i]) : $reps[$i],
- $sql);
- }
- $res = $this->_doquery($sql);
- if($res == false) {
- $this->_debug('query failed: ' . $this->_dberror($res));
+ protected function getMaxDateTime() {
+ if ( time() > 0x7fffffff ) {
+ return $this->getDB()->timestamp( 1 << 62 );
+ } else {
+ return $this->getDB()->timestamp( 0x7fffffff );
}
- return $res;
- }
-
- function _strencode($str) {
- /* Protect strings in SQL */
- return str_replace( "'", "''", $str );
- }
- function _blobencode($str) {
- return $str;
- }
- function _blobdecode($str) {
- return $str;
- }
-
- abstract function _doinsert($table, $vals);
- abstract function _doquery($sql);
-
- abstract function _readonly();
-
- function _begin() {}
- function _commit() {}
-
- function _freeresult($result) {
- /* stub */
- return false;
}
- function _dberror($result) {
- /* stub */
- return 'unknown error';
- }
-
- abstract function _maxdatetime();
- abstract function _fromunixtime($ts);
-
- function garbageCollect() {
+ protected function garbageCollect() {
/* Ignore 99% of requests */
if ( !mt_rand( 0, 100 ) ) {
- $nowtime = time();
+ $now = time();
/* Avoid repeating the delete within a few seconds */
- if ( $nowtime > ($this->lastexpireall + 1) ) {
- $this->lastexpireall = $nowtime;
- $this->expireall();
+ if ( $now > ( $this->lastExpireAll + 1 ) ) {
+ $this->lastExpireAll = $now;
+ $this->expireAll();
}
}
}
- function expireall() {
- /* Remove any items that have expired */
- if ( $this->_readonly() ) {
- return false;
+ public function expireAll() {
+ $db = $this->getDB();
+ $now = $db->timestamp();
+ try {
+ $db->begin();
+ $db->delete( 'objectcache', array( 'exptime < ' . $db->addQuotes( $now ) ), __METHOD__ );
+ $db->commit();
+ } catch ( DBQueryError $e ) {
+ $this->handleWriteError( $e );
}
- $now = $this->_fromunixtime( time() );
- $this->_begin();
- $this->_query( "DELETE FROM $0 WHERE exptime < '$now'" );
- $this->_commit();
}
- function deleteall() {
- /* Clear *all* items from cache table */
- if ( $this->_readonly() ) {
- return false;
+ public function deleteAll() {
+ $db = $this->getDB();
+ try {
+ $db->begin();
+ $db->delete( 'objectcache', '*', __METHOD__ );
+ $db->commit();
+ } catch ( DBQueryError $e ) {
+ $this->handleWriteError( $e );
}
- $this->_begin();
- $this->_query( "DELETE FROM $0" );
- $this->_commit();
}
/**
@@ -401,9 +413,9 @@ abstract class SqlBagOStuff extends BagOStuff {
* @param $data mixed
* @return string
*/
- function _serialize( &$data ) {
+ protected function serialize( &$data ) {
$serial = serialize( $data );
- if( function_exists( 'gzdeflate' ) ) {
+ if ( function_exists( 'gzdeflate' ) ) {
return gzdeflate( $serial );
} else {
return $serial;
@@ -415,156 +427,39 @@ abstract class SqlBagOStuff extends BagOStuff {
* @param $serial string
* @return mixed
*/
- function _unserialize( $serial ) {
- if( function_exists( 'gzinflate' ) ) {
+ protected function unserialize( $serial ) {
+ if ( function_exists( 'gzinflate' ) ) {
$decomp = @gzinflate( $serial );
- if( false !== $decomp ) {
+ if ( false !== $decomp ) {
$serial = $decomp;
}
}
$ret = unserialize( $serial );
return $ret;
}
-}
-/**
- * Stores objects in the main database of the wiki
- *
- * @ingroup Cache
- */
-class MediaWikiBagOStuff extends SqlBagOStuff {
- var $tableInitialised = false;
- var $lb, $db;
-
- function _getDB(){
- global $wgDBtype;
- if ( !isset( $this->db ) ) {
- /* We must keep a separate connection to MySQL in order to avoid deadlocks
- * However, SQLite has an opposite behaviour.
- * @todo Investigate behaviour for other databases
- */
- if ( $wgDBtype == 'sqlite' ) {
- $this->db = wfGetDB( DB_MASTER );
- } else {
- $this->lb = wfGetLBFactory()->newMainLB();
- $this->db = $this->lb->getConnection( DB_MASTER );
- $this->db->clearFlag( DBO_TRX );
- }
- }
- return $this->db;
- }
- function _begin() {
- $this->_getDB()->begin();
- }
- function _commit() {
- $this->_getDB()->commit();
- }
- function _doquery($sql) {
- return $this->_getDB()->query( $sql, __METHOD__ );
- }
- function _doinsert($t, $v) {
- return $this->_getDB()->insert($t, $v, __METHOD__, array( 'IGNORE' ) );
- }
- function _fetchobject($result) {
- return $this->_getDB()->fetchObject($result);
- }
- function _freeresult($result) {
- return $this->_getDB()->freeResult($result);
- }
- function _dberror($result) {
- return $this->_getDB()->lastError();
- }
- function _maxdatetime() {
- if ( time() > 0x7fffffff ) {
- return $this->_fromunixtime( 1<<62 );
- } else {
- return $this->_fromunixtime( 0x7fffffff );
- }
- }
- function _fromunixtime($ts) {
- return $this->_getDB()->timestamp($ts);
- }
- /***
- * Note -- this should *not* check wfReadOnly().
- * Read-only mode has been repurposed from the original
- * "nothing must write to the database" to "users should not
- * be able to edit or alter anything user-visible".
- *
- * Backend bits like the object cache should continue
- * to work in this mode, otherwise things will blow up
- * like the message cache failing to save its state,
- * causing long delays (bug 11533).
+ /**
+ * Handle a DBQueryError which occurred during a write operation.
+ * Ignore errors which are due to a read-only database, rethrow others.
*/
- function _readonly(){
- return false;
- }
- function _strencode($s) {
- return $this->_getDB()->strencode($s);
- }
- function _blobencode($s) {
- return $this->_getDB()->encodeBlob($s);
- }
- function _blobdecode($s) {
- return $this->_getDB()->decodeBlob($s);
- }
- function getTableName() {
- if ( !$this->tableInitialised ) {
- $dbw = $this->_getDB();
- /* This is actually a hack, we should be able
- to use Language classes here... or not */
- if (!$dbw)
- throw new MWException("Could not connect to database");
- $this->table = $dbw->tableName( $this->table );
- $this->tableInitialised = true;
+ protected function handleWriteError( $exception ) {
+ $db = $this->getDB();
+ if ( !$db->wasReadOnlyError() ) {
+ throw $exception;
}
- return $this->table;
+ try {
+ $db->rollback();
+ } catch ( DBQueryError $e ) {
+ }
+ wfDebug( __METHOD__ . ": ignoring query error\n" );
+ $db->ignoreErrors( false );
}
}
/**
- * This is a wrapper for Turck MMCache's shared memory functions.
- *
- * You can store objects with mmcache_put() and mmcache_get(), but Turck seems
- * to use a weird custom serializer that randomly segfaults. So we wrap calls
- * with serialize()/unserialize().
- *
- * The thing I noticed about the Turck serialized data was that unlike ordinary
- * serialize(), it contained the names of methods, and judging by the amount of
- * binary data, perhaps even the bytecode of the methods themselves. It may be
- * that Turck's serializer is faster, so a possible future extension would be
- * to use it for arrays but not for objects.
- *
- * @ingroup Cache
+ * Backwards compatibility alias
*/
-class TurckBagOStuff extends BagOStuff {
- function get($key) {
- $val = mmcache_get( $key );
- if ( is_string( $val ) ) {
- $val = unserialize( $val );
- }
- return $val;
- }
-
- function set($key, $value, $exptime=0) {
- mmcache_put( $key, serialize( $value ), $exptime );
- return true;
- }
-
- function delete($key, $time=0) {
- mmcache_rm( $key );
- return true;
- }
-
- function lock($key, $waitTimeout = 0 ) {
- mmcache_lock( $key );
- return true;
- }
-
- function unlock($key) {
- mmcache_unlock( $key );
- return true;
- }
-}
+class MediaWikiBagOStuff extends SqlBagOStuff { }
/**
* This is a wrapper for APC's shared memory functions
@@ -572,36 +467,45 @@ class TurckBagOStuff extends BagOStuff {
* @ingroup Cache
*/
class APCBagOStuff extends BagOStuff {
- function get($key) {
- $val = apc_fetch($key);
+ public function get( $key ) {
+ $val = apc_fetch( $key );
if ( is_string( $val ) ) {
$val = unserialize( $val );
}
return $val;
}
- function set($key, $value, $exptime=0) {
- apc_store($key, serialize($value), $exptime);
+ public function set( $key, $value, $exptime = 0 ) {
+ apc_store( $key, serialize( $value ), $exptime );
return true;
}
- function delete($key, $time=0) {
- apc_delete($key);
+ public function delete( $key, $time = 0 ) {
+ apc_delete( $key );
return true;
}
-}
+ public function keys() {
+ $info = apc_cache_info( 'user' );
+ $list = $info['cache_list'];
+ $keys = array();
+ foreach ( $list as $entry ) {
+ $keys[] = $entry['info'];
+ }
+ return $keys;
+ }
+}
/**
* This is a wrapper for eAccelerator's shared memory functions.
*
- * This is basically identical to the Turck MMCache version,
+ * This is basically identical to the deceased Turck MMCache version,
* mostly because eAccelerator is based on Turck MMCache.
*
* @ingroup Cache
*/
class eAccelBagOStuff extends BagOStuff {
- function get($key) {
+ public function get( $key ) {
$val = eaccelerator_get( $key );
if ( is_string( $val ) ) {
$val = unserialize( $val );
@@ -609,22 +513,22 @@ class eAccelBagOStuff extends BagOStuff {
return $val;
}
- function set($key, $value, $exptime=0) {
+ public function set( $key, $value, $exptime = 0 ) {
eaccelerator_put( $key, serialize( $value ), $exptime );
return true;
}
- function delete($key, $time=0) {
+ public function delete( $key, $time = 0 ) {
eaccelerator_rm( $key );
return true;
}
- function lock($key, $waitTimeout = 0 ) {
+ public function lock( $key, $waitTimeout = 0 ) {
eaccelerator_lock( $key );
return true;
}
- function unlock($key) {
+ public function unlock( $key ) {
eaccelerator_unlock( $key );
return true;
}
@@ -646,7 +550,7 @@ class XCacheBagOStuff extends BagOStuff {
*/
public function get( $key ) {
$val = xcache_get( $key );
- if( is_string( $val ) )
+ if ( is_string( $val ) )
$val = unserialize( $val );
return $val;
}
@@ -675,25 +579,29 @@ class XCacheBagOStuff extends BagOStuff {
xcache_unset( $key );
return true;
}
-
}
/**
- * @todo document
+ * Cache that uses DBA as a backend.
+ * Slow due to the need to constantly open and close the file to avoid holding
+ * writer locks. Intended for development use only, as a memcached workalike
+ * for systems that don't have it.
+ *
* @ingroup Cache
*/
class DBABagOStuff extends BagOStuff {
var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
- function __construct( $handler = 'db3', $dir = false ) {
+ public function __construct( $dir = false ) {
+ global $wgDBAhandler;
if ( $dir === false ) {
global $wgTmpDirectory;
$dir = $wgTmpDirectory;
}
$this->mFile = "$dir/mw-cache-" . wfWikiID();
$this->mFile .= '.db';
- wfDebug( __CLASS__.": using cache file {$this->mFile}\n" );
- $this->mHandler = $handler;
+ wfDebug( __CLASS__ . ": using cache file {$this->mFile}\n" );
+ $this->mHandler = $wgDBAhandler;
}
/**
@@ -701,7 +609,7 @@ class DBABagOStuff extends BagOStuff {
*/
function encode( $value, $expiry ) {
# Convert to absolute time
- $expiry = BagOStuff::convertExpiry( $expiry );
+ $expiry = $this->convertExpiry( $expiry );
return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value );
}
@@ -715,7 +623,7 @@ class DBABagOStuff extends BagOStuff {
return array(
unserialize( substr( $blob, 11 ) ),
intval( substr( $blob, 0, 10 ) )
- );
+ );
}
}
@@ -741,7 +649,7 @@ class DBABagOStuff extends BagOStuff {
function get( $key ) {
wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__."($key)\n" );
+ wfDebug( __METHOD__ . "($key)\n" );
$handle = $this->getReader();
if ( !$handle ) {
return null;
@@ -756,16 +664,16 @@ class DBABagOStuff extends BagOStuff {
$handle = $this->getWriter();
dba_delete( $key, $handle );
dba_close( $handle );
- wfDebug( __METHOD__.": $key expired\n" );
+ wfDebug( __METHOD__ . ": $key expired\n" );
$val = null;
}
wfProfileOut( __METHOD__ );
return $val;
}
- function set( $key, $value, $exptime=0 ) {
+ function set( $key, $value, $exptime = 0 ) {
wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__."($key)\n" );
+ wfDebug( __METHOD__ . "($key)\n" );
$blob = $this->encode( $value, $exptime );
$handle = $this->getWriter();
if ( !$handle ) {
@@ -779,7 +687,7 @@ class DBABagOStuff extends BagOStuff {
function delete( $key, $time = 0 ) {
wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__."($key)\n" );
+ wfDebug( __METHOD__ . "($key)\n" );
$handle = $this->getWriter();
if ( !$handle ) {
return false;
@@ -817,11 +725,11 @@ class DBABagOStuff extends BagOStuff {
function keys() {
$reader = $this->getReader();
$k1 = dba_firstkey( $reader );
- if( !$k1 ) {
+ if ( !$k1 ) {
return array();
}
$result[] = $k1;
- while( $key = dba_nextkey( $reader ) ) {
+ while ( $key = dba_nextkey( $reader ) ) {
$result[] = $key;
}
return $result;
diff --git a/includes/Block.php b/includes/Block.php
index a44941f1..187ff2db 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -34,7 +34,7 @@ class Block {
$this->mUser = $user;
$this->mBy = $by;
$this->mReason = $reason;
- $this->mTimestamp = wfTimestamp(TS_MW,$timestamp);
+ $this->mTimestamp = wfTimestamp( TS_MW, $timestamp );
$this->mAuto = $auto;
$this->mAnonOnly = $anonOnly;
$this->mCreateAccount = $createAccount;
@@ -54,7 +54,7 @@ class Block {
* Load a block from the database, using either the IP address or
* user ID. Tries the user ID first, and if that doesn't work, tries
* the address.
- *
+ *
* @param $address String: IP address of user/anon
* @param $user Integer: user id of user
* @param $killExpired Boolean: delete expired blocks on load
@@ -87,14 +87,14 @@ class Block {
return null;
}
}
-
+
/**
* Check if two blocks are effectively equal
*
* @return Boolean
*/
public function equals( Block $block ) {
- return (
+ return (
$this->mAddress == $block->mAddress
&& $this->mUser == $block->mUser
&& $this->mAuto == $block->mAuto
@@ -130,9 +130,10 @@ class Block {
*/
protected function &getDBOptions( &$options ) {
global $wgAntiLockFlags;
+
if ( $this->mForUpdate || $this->mFromMaster ) {
$db = wfGetDB( DB_MASTER );
- if ( !$this->mForUpdate || ($wgAntiLockFlags & ALF_NO_BLOCK_LOCK) ) {
+ if ( !$this->mForUpdate || ( $wgAntiLockFlags & ALF_NO_BLOCK_LOCK ) ) {
$options = array();
} else {
$options = array( 'FOR UPDATE' );
@@ -180,12 +181,13 @@ class Block {
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
# (but not account creation blocks -- bug 13611)
- if( !$this->mCreateAccount ) {
+ if ( !$this->mCreateAccount ) {
$this->clear();
}
return false;
@@ -199,7 +201,7 @@ class Block {
if ( $this->loadRange( $address, $killExpired, $user ) ) {
if ( $user && $this->mAnonOnly ) {
# Respect account creation blocks on logged-in users -- bug 13611
- if( !$this->mCreateAccount ) {
+ if ( !$this->mCreateAccount ) {
$this->clear();
}
return false;
@@ -211,10 +213,13 @@ class Block {
# 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;
}
@@ -234,6 +239,7 @@ class Block {
*/
protected function loadFromResult( ResultWrapper $res, $killExpired = true ) {
$ret = false;
+
if ( 0 != $res->numRows() ) {
# Get first block
$row = $res->fetchObject();
@@ -274,6 +280,7 @@ class Block {
*/
public function loadRange( $address, $killExpired = true, $user = 0 ) {
$iaddr = IP::toHex( $address );
+
if ( $iaddr === false ) {
# Invalid address
return false;
@@ -286,7 +293,7 @@ class Block {
$options = array();
$db =& $this->getDBOptions( $options );
$conds = array(
- "ipb_range_start LIKE '$range%'",
+ 'ipb_range_start' . $db->buildLike( $range, $db->anyString() ),
"ipb_range_start <= '$iaddr'",
"ipb_range_end >= '$iaddr'"
);
@@ -309,7 +316,7 @@ class Block {
public function initFromRow( $row ) {
$this->mAddress = $row->ipb_address;
$this->mReason = $row->ipb_reason;
- $this->mTimestamp = wfTimestamp(TS_MW,$row->ipb_timestamp);
+ $this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
$this->mUser = $row->ipb_user;
$this->mBy = $row->ipb_by;
$this->mAuto = $row->ipb_auto;
@@ -321,17 +328,19 @@ class Block {
$this->mHideName = $row->ipb_deleted;
$this->mId = $row->ipb_id;
$this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
+
if ( isset( $row->user_name ) ) {
$this->mByName = $row->user_name;
} else {
$this->mByName = $row->ipb_by_text;
}
+
$this->mRangeStart = $row->ipb_range_start;
$this->mRangeEnd = $row->ipb_range_end;
}
/**
- * Once $mAddress has been set, get the range they came from.
+ * Once $mAddress has been set, get the range they came from.
* Wrapper for IP::parseRange
*/
protected function initialiseRange() {
@@ -352,6 +361,7 @@ class Block {
if ( wfReadOnly() ) {
return false;
}
+
if ( !$this->mId ) {
throw new MWException( "Block::delete() now requires that the mId member be filled\n" );
}
@@ -377,8 +387,9 @@ class Block {
# Don't collide with expired blocks
Block::purgeExpired();
- $ipb_id = $dbw->nextSequenceValue('ipblocks_ipb_id_val');
- $dbw->insert( 'ipblocks',
+ $ipb_id = $dbw->nextSequenceValue( 'ipblocks_ipb_id_seq' );
+ $dbw->insert(
+ 'ipblocks',
array(
'ipb_id' => $ipb_id,
'ipb_address' => $this->mAddress,
@@ -386,7 +397,7 @@ class Block {
'ipb_by' => $this->mBy,
'ipb_by_text' => $this->mByName,
'ipb_reason' => $this->mReason,
- 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
+ 'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
'ipb_auto' => $this->mAuto,
'ipb_anon_only' => $this->mAnonOnly,
'ipb_create_account' => $this->mCreateAccount,
@@ -394,14 +405,16 @@ class Block {
'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
'ipb_range_start' => $this->mRangeStart,
'ipb_range_end' => $this->mRangeEnd,
- 'ipb_deleted' => $this->mHideName,
+ 'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite
'ipb_block_email' => $this->mBlockEmail,
'ipb_allow_usertalk' => $this->mAllowUsertalk
- ), 'Block::insert', array( 'IGNORE' )
+ ),
+ 'Block::insert',
+ array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
- if ($affected)
+ if ( $affected )
$this->doRetroactiveAutoblock();
return (bool)$affected;
@@ -417,13 +430,14 @@ class Block {
$this->validateBlockParams();
- $dbw->update( 'ipblocks',
+ $dbw->update(
+ 'ipblocks',
array(
'ipb_user' => $this->mUser,
'ipb_by' => $this->mBy,
'ipb_by_text' => $this->mByName,
'ipb_reason' => $this->mReason,
- 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
+ 'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
'ipb_auto' => $this->mAuto,
'ipb_anon_only' => $this->mAnonOnly,
'ipb_create_account' => $this->mCreateAccount,
@@ -433,13 +447,15 @@ class Block {
'ipb_range_end' => $this->mRangeEnd,
'ipb_deleted' => $this->mHideName,
'ipb_block_email' => $this->mBlockEmail,
- 'ipb_allow_usertalk' => $this->mAllowUsertalk ),
+ 'ipb_allow_usertalk' => $this->mAllowUsertalk
+ ),
array( 'ipb_id' => $this->mId ),
- 'Block::update' );
+ 'Block::update'
+ );
return $dbw->affectedRows();
}
-
+
/**
* Make sure all the proper members are set to sane values
* before adding/updating a block
@@ -453,11 +469,14 @@ class Block {
# Unset ipb_enable_autoblock for IP blocks, makes no sense
if ( !$this->mUser ) {
$this->mEnableAutoblock = 0;
- $this->mBlockEmail = 0; //Same goes for email...
}
- if( !$this->mByName ) {
- if( $this->mBy ) {
+ # bug 18860: non-anon-only IP blocks should be allowed to block email
+ if ( !$this->mUser && $this->mAnonOnly ) {
+ $this->mBlockEmail = 0;
+ }
+ if ( !$this->mByName ) {
+ if ( $this->mBy ) {
$this->mByName = User::whoIs( $this->mBy );
} else {
global $wgUser;
@@ -465,28 +484,27 @@ class Block {
}
}
}
-
-
+
/**
- * Retroactively autoblocks the last IP used by the user (if it is a user)
- * blocked by this Block.
- *
- * @return Boolean: whether or not a retroactive autoblock was made.
- */
+ * Retroactively autoblocks the last IP used by the user (if it is a user)
+ * blocked by this Block.
+ *
+ * @return Boolean: whether or not a retroactive autoblock was made.
+ */
public function doRetroactiveAutoblock() {
$dbr = wfGetDB( DB_SLAVE );
- #If autoblock is enabled, autoblock the LAST IP used
+ # If autoblock is enabled, autoblock the LAST IP used
# - stolen shamelessly from CheckUser_body.php
- if ($this->mEnableAutoblock && $this->mUser) {
- wfDebug("Doing retroactive autoblocks for " . $this->mAddress . "\n");
-
+ if ( $this->mEnableAutoblock && $this->mUser ) {
+ wfDebug( "Doing retroactive autoblocks for " . $this->mAddress . "\n" );
+
$options = array( 'ORDER BY' => 'rc_timestamp DESC' );
$conds = array( 'rc_user_text' => $this->mAddress );
-
- if ($this->mAngryAutoblock) {
+
+ if ( $this->mAngryAutoblock ) {
// Block any IP used in the last 7 days. Up to five IPs.
- $conds[] = 'rc_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( time() - (7*86400) ) );
+ $conds[] = 'rc_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( time() - ( 7 * 86400 ) ) );
$options['LIMIT'] = 5;
} else {
// Just the last IP used.
@@ -494,11 +512,11 @@ class Block {
}
$res = $dbr->select( 'recentchanges', array( 'rc_ip' ), $conds,
- __METHOD__ , $options);
+ __METHOD__ , $options );
if ( !$dbr->numRows( $res ) ) {
- #No results, don't autoblock anything
- wfDebug("No IP found to retroactively autoblock\n");
+ # No results, don't autoblock anything
+ wfDebug( "No IP found to retroactively autoblock\n" );
} else {
while ( $row = $dbr->fetchObject( $res ) ) {
if ( $row->rc_ip )
@@ -507,7 +525,7 @@ class Block {
}
}
}
-
+
/**
* Checks whether a given IP is on the autoblock whitelist.
*
@@ -516,7 +534,7 @@ class Block {
*/
public static function isWhitelistedFromAutoblocks( $ip ) {
global $wgMemc;
-
+
// Try to get the autoblock_whitelist from the cache, as it's faster
// than getting the msg raw and explode()'ing it.
$key = wfMemcKey( 'ipb', 'autoblock', 'whitelist' );
@@ -526,28 +544,28 @@ class Block {
$wgMemc->set( $key, $lines, 3600 * 24 );
}
- wfDebug("Checking the autoblock whitelist..\n");
+ wfDebug( "Checking the autoblock whitelist..\n" );
- foreach( $lines as $line ) {
+ foreach ( $lines as $line ) {
# List items only
if ( substr( $line, 0, 1 ) !== '*' ) {
continue;
}
- $wlEntry = substr($line, 1);
- $wlEntry = trim($wlEntry);
+ $wlEntry = substr( $line, 1 );
+ $wlEntry = trim( $wlEntry );
- wfDebug("Checking $ip against $wlEntry...");
+ wfDebug( "Checking $ip against $wlEntry..." );
# Is the IP in this range?
- if (IP::isInRange( $ip, $wlEntry )) {
- wfDebug(" IP $ip matches $wlEntry, not autoblocking\n");
+ if ( IP::isInRange( $ip, $wlEntry ) ) {
+ wfDebug( " IP $ip matches $wlEntry, not autoblocking\n" );
return true;
} else {
wfDebug( " No match\n" );
}
}
-
+
return false;
}
@@ -565,12 +583,12 @@ class Block {
}
# Check for presence on the autoblock whitelist
- if (Block::isWhitelistedFromAutoblocks($autoblockIP)) {
+ if ( Block::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
return;
}
-
- ## Allow hooks to cancel the autoblock.
- if (!wfRunHooks( 'AbortAutoblock', array( $autoblockIP, &$this ) )) {
+
+ # # Allow hooks to cancel the autoblock.
+ if ( !wfRunHooks( 'AbortAutoblock', array( $autoblockIP, &$this ) ) ) {
wfDebug( "Autoblock aborted by hook.\n" );
return false;
}
@@ -582,8 +600,8 @@ class Block {
# If the user is already blocked. Then check if the autoblock would
# exceed the user block. If it would exceed, then do nothing, else
# prolong block time
- if ($this->mExpiry &&
- ($this->mExpiry < Block::getAutoblockExpiry($ipblock->mTimestamp))) {
+ if ( $this->mExpiry &&
+ ( $this->mExpiry < Block::getAutoblockExpiry( $ipblock->mTimestamp ) ) ) {
return;
}
# Just update the timestamp
@@ -610,8 +628,8 @@ class Block {
$ipblock->mAllowUsertalk = $this->mAllowUsertalk;
# If the user is already blocked with an expiry date, we don't
# want to pile on top of that!
- if($this->mExpiry) {
- $ipblock->mExpiry = min ( $this->mExpiry, Block::getAutoblockExpiry( $this->mTimestamp ));
+ if ( $this->mExpiry ) {
+ $ipblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $this->mTimestamp ) );
} else {
$ipblock->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
}
@@ -624,8 +642,7 @@ class Block {
* @return Boolean
*/
public function deleteIfExpired() {
- $fname = 'Block::deleteIfExpired';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
if ( $this->isExpired() ) {
wfDebug( "Block::deleteIfExpired() -- deleting\n" );
$this->delete();
@@ -634,7 +651,7 @@ class Block {
wfDebug( "Block::deleteIfExpired() -- not expired\n" );
$retVal = false;
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $retVal;
}
@@ -660,7 +677,7 @@ class Block {
}
/**
- * Update the timestamp on autoblocks.
+ * Update the timestamp on autoblocks.
*/
public function updateTimestamp() {
if ( $this->mAuto ) {
@@ -670,8 +687,8 @@ class Block {
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'ipblocks',
array( /* SET */
- 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
- 'ipb_expiry' => $dbw->timestamp($this->mExpiry),
+ 'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
+ 'ipb_expiry' => $dbw->timestamp( $this->mExpiry ),
), array( /* WHERE */
'ipb_address' => $this->mAddress
), 'Block::updateTimestamp'
@@ -700,14 +717,14 @@ class Block {
/**
* Get/set the SELECT ... FOR UPDATE flag
*/
- public function forUpdate( $x = NULL ) {
+ public function forUpdate( $x = null ) {
return wfSetVar( $this->mForUpdate, $x );
}
/**
* Get/set a flag determining whether the master is used for reads
*/
- public function fromMaster( $x = NULL ) {
+ public function fromMaster( $x = null ) {
return wfSetVar( $this->mFromMaster, $x );
}
@@ -726,7 +743,7 @@ class Block {
/**
* Encode expiry for DB
*
- * @param $expiry String: timestamp for expiry, or
+ * @param $expiry String: timestamp for expiry, or
* @param $db Database object
* @return String
*/
@@ -773,10 +790,10 @@ class Block {
$parts = explode( '/', $range );
if ( count( $parts ) == 2 ) {
// IPv6
- if ( IP::isIPv6($range) && $parts[1] >= 64 && $parts[1] <= 128 ) {
+ if ( IP::isIPv6( $range ) && $parts[1] >= 64 && $parts[1] <= 128 ) {
$bits = $parts[1];
$ipint = IP::toUnsigned6( $parts[0] );
- # Native 32 bit functions WONT work here!!!
+ # Native 32 bit functions WON'T work here!!!
# Convert to a padded binary number
$network = wfBaseConvert( $ipint, 10, 2, 128 );
# Truncate the last (128-$bits) bits and replace them with zeros
@@ -787,7 +804,7 @@ class Block {
$newip = IP::toOctet( $network );
$range = "$newip/{$parts[1]}";
} // IPv4
- else if ( IP::isIPv4($range) && $parts[1] >= 16 && $parts[1] <= 32 ) {
+ elseif ( IP::isIPv4( $range ) && $parts[1] >= 16 && $parts[1] <= 32 ) {
$shift = 32 - $parts[1];
$ipint = IP::toUnsigned( $parts[0] );
$ipint = $ipint >> $shift << $shift;
@@ -808,7 +825,7 @@ class Block {
/**
* Get a value to insert into expiry field of the database when infinite expiry
- * is desired. In principle this could be DBMS-dependant, but currently all
+ * is desired. In principle this could be DBMS-dependant, but currently all
* supported DBMS's support the string "infinity", so we just use that.
*
* @return String
@@ -818,35 +835,36 @@ class Block {
# works with CHAR(14) as well because "i" sorts after all numbers.
return 'infinity';
}
-
+
/**
* Convert a DB-encoded expiry into a real string that humans can read.
*
* @param $encoded_expiry String: Database encoded expiry time
- * @return String
+ * @return Html-escaped String
*/
public static function formatExpiry( $encoded_expiry ) {
static $msg = null;
-
- if( is_null( $msg ) ) {
+
+ if ( is_null( $msg ) ) {
$msg = array();
$keys = array( 'infiniteblock', 'expiringblock' );
- foreach( $keys as $key ) {
+ foreach ( $keys as $key ) {
$msg[$key] = wfMsgHtml( $key );
}
}
-
+
$expiry = Block::decodeExpiry( $encoded_expiry );
- if ($expiry == 'infinity') {
+ if ( $expiry == 'infinity' ) {
$expirystr = $msg['infiniteblock'];
} else {
global $wgLang;
- $expiretimestr = $wgLang->timeanddate( $expiry, true );
- $expirystr = wfMsgReplaceArgs( $msg['expiringblock'], array($expiretimestr) );
+ $expiredatestr = htmlspecialchars( $wgLang->date( $expiry, true ) );
+ $expiretimestr = htmlspecialchars( $wgLang->time( $expiry, true ) );
+ $expirystr = wfMsgReplaceArgs( $msg['expiringblock'], array( $expiredatestr, $expiretimestr ) );
}
return $expirystr;
}
-
+
/**
* Convert a typed-in expiry time into something we can put into the database.
* @param $expiry_input String: whatever was typed into the form
@@ -857,7 +875,8 @@ class Block {
$expiry = 'infinity';
} else {
$expiry = strtotime( $expiry_input );
- if ($expiry < 0 || $expiry === false) {
+
+ if ( $expiry < 0 || $expiry === false ) {
return false;
}
}
diff --git a/includes/CacheDependency.php b/includes/CacheDependency.php
index b050c46d..11e70738 100644
--- a/includes/CacheDependency.php
+++ b/includes/CacheDependency.php
@@ -1,5 +1,4 @@
<?php
-
/**
* This class stores an arbitrary value along with its dependencies.
* Users should typically only use DependencyWrapper::getFromCache(), rather
@@ -12,8 +11,8 @@ class DependencyWrapper {
/**
* Create an instance.
- * @param mixed $value The user-supplied value
- * @param mixed $deps A dependency or dependency array. All dependencies
+ * @param $value Mixed: the user-supplied value
+ * @param $deps Mixed: a dependency or dependency array. All dependencies
* must be objects implementing CacheDependency.
*/
function __construct( $value = false, $deps = array() ) {
@@ -66,12 +65,12 @@ class DependencyWrapper {
* it will be generated with the callback function (if present), and the newly
* calculated value will be stored to the cache in a wrapper.
*
- * @param object $cache A cache object such as $wgMemc
- * @param string $key The cache key
- * @param integer $expiry The expiry timestamp or interval in seconds
- * @param mixed $callback The callback for generating the value, or false
- * @param array $callbackParams The function parameters for the callback
- * @param array $deps The dependencies to store on a cache miss. Note: these
+ * @param $cache Object: a cache object such as $wgMemc
+ * @param $key String: the cache key
+ * @param $expiry Integer: the expiry timestamp or interval in seconds
+ * @param $callback Mixed: the callback for generating the value, or false
+ * @param $callbackParams Array: the function parameters for the callback
+ * @param $deps Array: the dependencies to store on a cache miss. Note: these
* are not the dependencies used on a cache hit! Cache hits use the stored
* dependency array.
*
@@ -108,7 +107,7 @@ abstract class CacheDependency {
/**
* Hook to perform any expensive pre-serialize loading of dependency values.
*/
- function loadDependencyValues() {}
+ function loadDependencyValues() { }
}
/**
@@ -120,8 +119,8 @@ class FileDependency extends CacheDependency {
/**
* Create a file dependency
*
- * @param string $filename The name of the file, preferably fully qualified
- * @param mixed $timestamp The unix last modified timestamp, or false if the
+ * @param $filename String: the name of the file, preferably fully qualified
+ * @param $timestamp Mixed: the unix last modified timestamp, or false if the
* file does not exist. If omitted, the timestamp will be loaded from
* the file.
*
@@ -134,6 +133,11 @@ class FileDependency extends CacheDependency {
$this->timestamp = $timestamp;
}
+ function __sleep() {
+ $this->loadDependencyValues();
+ return array( 'filename', 'timestamp' );
+ }
+
function loadDependencyValues() {
if ( is_null( $this->timestamp ) ) {
if ( !file_exists( $this->filename ) ) {
@@ -180,7 +184,7 @@ class TitleDependency extends CacheDependency {
/**
* Construct a title dependency
- * @param Title $title
+ * @param $title Title
*/
function __construct( Title $title ) {
$this->titleObj = $title;
@@ -208,6 +212,7 @@ class TitleDependency extends CacheDependency {
function isExpired() {
$touched = $this->getTitle()->getTouched();
+
if ( $this->touched === false ) {
if ( $touched === false ) {
# Still missing
@@ -246,6 +251,7 @@ class TitleListDependency extends CacheDependency {
function calculateTimestamps() {
# Initialise values to false
$timestamps = array();
+
foreach ( $this->getLinkBatch()->data as $ns => $dbks ) {
if ( count( $dbks ) > 0 ) {
$timestamps[$ns] = array();
@@ -259,9 +265,13 @@ class TitleListDependency extends CacheDependency {
if ( count( $timestamps ) ) {
$dbr = wfGetDB( DB_SLAVE );
$where = $this->getLinkBatch()->constructSet( 'page', $dbr );
- $res = $dbr->select( 'page',
+ $res = $dbr->select(
+ 'page',
array( 'page_namespace', 'page_title', 'page_touched' ),
- $where, __METHOD__ );
+ $where,
+ __METHOD__
+ );
+
while ( $row = $dbr->fetchObject( $res ) ) {
$timestamps[$row->page_namespace][$row->page_title] = $row->page_touched;
}
@@ -278,7 +288,7 @@ class TitleListDependency extends CacheDependency {
}
function getLinkBatch() {
- if ( !isset( $this->linkBatch ) ){
+ if ( !isset( $this->linkBatch ) ) {
$this->linkBatch = new LinkBatch;
$this->linkBatch->setArray( $this->timestamps );
}
@@ -290,6 +300,7 @@ class TitleListDependency extends CacheDependency {
foreach ( $this->timestamps as $ns => $dbks ) {
foreach ( $dbks as $dbk => $oldTimestamp ) {
$newTimestamp = $newTimestamps[$ns][$dbk];
+
if ( $oldTimestamp === false ) {
if ( $newTimestamp === false ) {
# Still missing
diff --git a/includes/Category.php b/includes/Category.php
index 50efdbc1..e9ffaecf 100644
--- a/includes/Category.php
+++ b/includes/Category.php
@@ -1,6 +1,6 @@
<?php
/**
- * Category objects are immutable, strictly speaking. If you call methods that change the database,
+ * Category objects are immutable, strictly speaking. If you call methods that change the database,
* like to refresh link counts, the objects will be appropriately reinitialized.
* Member variables are lazy-initialized.
*
@@ -18,21 +18,21 @@ class Category {
/** Counts of membership (cat_pages, cat_subcats, cat_files) */
private $mPages = null, $mSubcats = null, $mFiles = null;
- private function __construct() {}
+ private function __construct() { }
/**
* Set up all member variables using a database query.
* @return bool True on success, false on failure.
*/
protected function initialize() {
- if ( $this->mName === null && $this->mTitle )
- $this->mName = $title->getDBKey();
+ if ( $this->mName === null && $this->mTitle )
+ $this->mName = $title->getDBkey();
- if( $this->mName === null && $this->mID === null ) {
- throw new MWException( __METHOD__.' has both names and IDs null' );
- } elseif( $this->mID === null ) {
+ if ( $this->mName === null && $this->mID === null ) {
+ throw new MWException( __METHOD__ . ' has both names and IDs null' );
+ } elseif ( $this->mID === null ) {
$where = array( 'cat_title' => $this->mName );
- } elseif( $this->mName === null ) {
+ } elseif ( $this->mName === null ) {
$where = array( 'cat_id' => $this->mID );
} else {
# Already initialized
@@ -45,12 +45,13 @@ class Category {
$where,
__METHOD__
);
- if( !$row ) {
+
+ if ( !$row ) {
# Okay, there were no contents. Nothing to initialize.
if ( $this->mTitle ) {
# If there is a title object but no record in the category table, treat this as an empty category
$this->mID = false;
- $this->mName = $this->mTitle->getDBKey();
+ $this->mName = $this->mTitle->getDBkey();
$this->mPages = 0;
$this->mSubcats = 0;
$this->mFiles = 0;
@@ -60,6 +61,7 @@ class Category {
return false; # Fail
}
}
+
$this->mID = $row->cat_id;
$this->mName = $row->cat_title;
$this->mPages = $row->cat_pages;
@@ -69,7 +71,7 @@ class Category {
# (bug 13683) If the count is negative, then 1) it's obviously wrong
# and should not be kept, and 2) we *probably* don't have to scan many
# rows to obtain the correct figure, so let's risk a one-time recount.
- if( $this->mPages < 0 || $this->mSubcats < 0 || $this->mFiles < 0 ) {
+ if ( $this->mPages < 0 || $this->mSubcats < 0 || $this->mFiles < 0 ) {
$this->refreshCounts();
}
@@ -86,12 +88,13 @@ class Category {
public static function newFromName( $name ) {
$cat = new self();
$title = Title::makeTitleSafe( NS_CATEGORY, $name );
- if( !is_object( $title ) ) {
+
+ if ( !is_object( $title ) ) {
return false;
}
$cat->mTitle = $title;
- $cat->mName = $title->getDBKey();
+ $cat->mName = $title->getDBkey();
return $cat;
}
@@ -106,7 +109,7 @@ class Category {
$cat = new self();
$cat->mTitle = $title;
- $cat->mName = $title->getDBKey();
+ $cat->mName = $title->getDBkey();
return $cat;
}
@@ -126,7 +129,7 @@ class Category {
/**
* Factory function, for constructing a Category object from a result set
*
- * @param $row result set row, must contain the cat_xxx fields. If the fields are null,
+ * @param $row result set row, must contain the cat_xxx fields. If the fields are null,
* the resulting Category object will represent an empty category if a title object
* was given. If the fields are null and no title was given, this method fails and returns false.
* @param $title optional title object for the category represented by the given row.
@@ -137,8 +140,7 @@ class Category {
$cat = new self();
$cat->mTitle = $title;
-
- # NOTE: the row often results from a LEFT JOIN on categorylinks. This may result in
+ # NOTE: the row often results from a LEFT JOIN on categorylinks. This may result in
# all the cat_xxx fields being null, if the category page exists, but nothing
# was ever added to the category. This case should be treated linke an empty
# category, if possible.
@@ -149,7 +151,7 @@ class Category {
# but we can't know that here...
return false;
} else {
- $cat->mName = $title->getDBKey(); # if we have a title object, fetch the category name from there
+ $cat->mName = $title->getDBkey(); # if we have a title object, fetch the category name from there
}
$cat->mID = false;
@@ -169,12 +171,16 @@ class Category {
/** @return mixed DB key name, or false on failure */
public function getName() { return $this->getX( 'mName' ); }
+
/** @return mixed Category ID, or false on failure */
public function getID() { return $this->getX( 'mID' ); }
+
/** @return mixed Total number of member pages, or false on failure */
public function getPageCount() { return $this->getX( 'mPages' ); }
+
/** @return mixed Number of subcategories, or false on failure */
public function getSubcatCount() { return $this->getX( 'mSubcats' ); }
+
/** @return mixed Number of member files, or false on failure */
public function getFileCount() { return $this->getX( 'mFiles' ); }
@@ -182,9 +188,9 @@ class Category {
* @return mixed The Title for this category, or false on failure.
*/
public function getTitle() {
- if( $this->mTitle ) return $this->mTitle;
-
- if( !$this->initialize() ) {
+ if ( $this->mTitle ) return $this->mTitle;
+
+ if ( !$this->initialize() ) {
return false;
}
@@ -204,13 +210,19 @@ class Category {
$conds = array( 'cl_to' => $this->getName(), 'cl_from = page_id' );
$options = array( 'ORDER BY' => 'cl_sortkey' );
- if( $limit ) $options[ 'LIMIT' ] = $limit;
- if( $offset !== '' ) $conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset );
+
+ if ( $limit ) {
+ $options[ 'LIMIT' ] = $limit;
+ }
+
+ if ( $offset !== '' ) {
+ $conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset );
+ }
return TitleArray::newFromResult(
$dbr->select(
array( 'page', 'categorylinks' ),
- array( 'page_id', 'page_namespace','page_title', 'page_len',
+ array( 'page_id', 'page_namespace', 'page_title', 'page_len',
'page_is_redirect', 'page_latest' ),
$conds,
__METHOD__,
@@ -221,10 +233,10 @@ class Category {
/** Generic accessor */
private function getX( $key ) {
- if( !$this->initialize() ) {
+ if ( !$this->initialize() ) {
return false;
}
- return $this->{$key};
+ return $this-> { $key } ;
}
/**
@@ -233,29 +245,33 @@ class Category {
* @return bool True on success, false on failure
*/
public function refreshCounts() {
- if( wfReadOnly() ) {
+ if ( wfReadOnly() ) {
return false;
}
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
# Note, we must use names for this, since categorylinks does.
- if( $this->mName === null ) {
- if( !$this->initialize() ) {
+ if ( $this->mName === null ) {
+ if ( !$this->initialize() ) {
return false;
}
} else {
# Let's be sure that the row exists in the table. We don't need to
# do this if we got the row from the table in initialization!
+ $seqVal = $dbw->nextSequenceValue( 'category_cat_id_seq' );
$dbw->insert(
'category',
- array( 'cat_title' => $this->mName ),
+ array(
+ 'cat_id' => $seqVal,
+ 'cat_title' => $this->mName
+ ),
__METHOD__,
'IGNORE'
);
}
- $cond1 = $dbw->conditional( 'page_namespace='.NS_CATEGORY, 1, 'NULL' );
- $cond2 = $dbw->conditional( 'page_namespace='.NS_FILE, 1, 'NULL' );
+ $cond1 = $dbw->conditional( 'page_namespace=' . NS_CATEGORY, 1, 'NULL' );
+ $cond2 = $dbw->conditional( 'page_namespace=' . NS_FILE, 1, 'NULL' );
$result = $dbw->selectRow(
array( 'categorylinks', 'page' ),
array( 'COUNT(*) AS pages',
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 03ecb5dc..56f85faa 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -5,7 +5,7 @@
*
*/
-if( !defined( 'MEDIAWIKI' ) )
+if ( !defined( 'MEDIAWIKI' ) )
die( 1 );
/**
@@ -20,7 +20,7 @@ class CategoryPage extends Article {
if ( isset( $diff ) && $diffOnly )
return Article::view();
- if( !wfRunHooks( 'CategoryPageView', array( &$this ) ) )
+ if ( !wfRunHooks( 'CategoryPageView', array( &$this ) ) )
return;
if ( NS_CATEGORY == $this->mTitle->getNamespace() ) {
@@ -33,18 +33,17 @@ class CategoryPage extends Article {
$this->closeShowCategory();
}
}
-
+
/**
* Don't return a 404 for categories in use.
*/
function hasViewableContent() {
- if( parent::hasViewableContent() ) {
+ if ( parent::hasViewableContent() ) {
return true;
} else {
$cat = Category::newFromTitle( $this->mTitle );
return $cat->getId() != 0;
}
-
}
function openShowCategory() {
@@ -86,7 +85,7 @@ class CategoryViewer {
* @private
*/
function getHTML() {
- global $wgOut, $wgCategoryMagicGallery, $wgCategoryPagingLimit;
+ global $wgOut, $wgCategoryMagicGallery, $wgCategoryPagingLimit, $wgContLang;
wfProfileIn( __METHOD__ );
$this->showGallery = $wgCategoryMagicGallery && !$wgOut->mNoGallery;
@@ -95,11 +94,22 @@ class CategoryViewer {
$this->doCategoryQuery();
$this->finaliseCategoryState();
- $r = $this->getCategoryTop() .
- $this->getSubcategorySection() .
+ $r = $this->getSubcategorySection() .
$this->getPagesSection() .
- $this->getImageSection() .
- $this->getCategoryBottom();
+ $this->getImageSection();
+
+ if ( $r == '' ) {
+ // If there is no category content to display, only
+ // show the top part of the navigation links.
+ // FIXME: cannot be completely suppressed because it
+ // is unknown if 'until' or 'from' makes this
+ // give 0 results.
+ $r = $r . $this->getCategoryTop();
+ } else {
+ $r = $this->getCategoryTop() .
+ $r .
+ $this->getCategoryBottom();
+ }
// Give a proper message if category is empty
if ( $r == '' ) {
@@ -107,7 +117,7 @@ class CategoryViewer {
}
wfProfileOut( __METHOD__ );
- return $r;
+ return $wgContLang->convert( $r );
}
function clearCategoryState() {
@@ -115,7 +125,7 @@ class CategoryViewer {
$this->articles_start_char = array();
$this->children = array();
$this->children_start_char = array();
- if( $this->showGallery ) {
+ if ( $this->showGallery ) {
$this->gallery = new ImageGallery();
$this->gallery->setHideBadImages();
}
@@ -138,14 +148,18 @@ class CategoryViewer {
}
/**
- * Add a subcategory to the internal lists, using a title object
+ * Add a subcategory to the internal lists, using a title object
* @deprecated kept for compatibility, please use addSubcategoryObject instead
*/
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[] = $this->getSkin()->link(
+ $title,
+ null,
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ );
$this->children_start_char[] = $this->getSubcategorySortChar( $title, $sortkey );
}
@@ -160,7 +174,7 @@ class CategoryViewer {
function getSubcategorySortChar( $title, $sortkey ) {
global $wgContLang;
- if( $title->getPrefixedText() == $sortkey ) {
+ if ( $title->getPrefixedText() == $sortkey ) {
$firstChar = $wgContLang->firstChar( $title->getDBkey() );
} else {
$firstChar = $wgContLang->firstChar( $sortkey );
@@ -174,7 +188,7 @@ class CategoryViewer {
*/
function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) {
if ( $this->showGallery ) {
- if( $this->flip ) {
+ if ( $this->flip ) {
$this->gallery->insert( $title );
} else {
$this->gallery->add( $title );
@@ -189,15 +203,21 @@ class CategoryViewer {
*/
function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) {
global $wgContLang;
- $titletext = $wgContLang->convert( $title->getPrefixedText() );
$this->articles[] = $isRedirect
- ? '<span class="redirect-in-category">' . $this->getSkin()->makeKnownLinkObj( $title, $titletext ) . '</span>'
- : $this->getSkin()->makeSizeLinkObj( $pageLength, $title, $titletext );
+ ? '<span class="redirect-in-category">' .
+ $this->getSkin()->link(
+ $title,
+ null,
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ ) . '</span>'
+ : $this->getSkin()->makeSizeLinkObj( $pageLength, $title );
$this->articles_start_char[] = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
}
function finaliseCategoryState() {
- if( $this->flip ) {
+ 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 );
@@ -207,16 +227,17 @@ class CategoryViewer {
function doCategoryQuery() {
$dbr = wfGetDB( DB_SLAVE, 'category' );
- if( $this->from != '' ) {
+ if ( $this->from != '' ) {
$pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes( $this->from );
$this->flip = false;
- } elseif( $this->until != '' ) {
+ } elseif ( $this->until != '' ) {
$pageCondition = 'cl_sortkey < ' . $dbr->addQuotes( $this->until );
$this->flip = true;
} else {
$pageCondition = '1 = 1';
$this->flip = false;
}
+
$res = $dbr->select(
array( 'page', 'categorylinks', 'category' ),
array( 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey',
@@ -232,8 +253,9 @@ class CategoryViewer {
$count = 0;
$this->nextPage = null;
- while( $x = $dbr->fetchObject ( $res ) ) {
- if( ++$count > $this->limit ) {
+
+ while ( $x = $dbr->fetchObject ( $res ) ) {
+ if ( ++$count > $this->limit ) {
// We've reached the one extra which shows that there are
// additional pages to be had. Stop here...
$this->nextPage = $x->cl_sortkey;
@@ -242,26 +264,20 @@ class CategoryViewer {
$title = Title::makeTitle( $x->page_namespace, $x->page_title );
- if( $title->getNamespace() == NS_CATEGORY ) {
+ if ( $title->getNamespace() == NS_CATEGORY ) {
$cat = Category::newFromRow( $x, $title );
$this->addSubcategoryObject( $cat, $x->cl_sortkey, $x->page_len );
- } elseif( $this->showGallery && $title->getNamespace() == NS_FILE ) {
+ } elseif ( $this->showGallery && $title->getNamespace() == NS_FILE ) {
$this->addImage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
} else {
$this->addPage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
}
}
- $dbr->freeResult( $res );
}
function getCategoryTop() {
- $r = '';
- if( $this->until != '' ) {
- $r .= $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit );
- } elseif( $this->nextPage != '' || $this->from != '' ) {
- $r .= $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit );
- }
- return $r == ''
+ $r = $this->getCategoryBottom();
+ return $r === ''
? $r
: "<br style=\"clear:both;\"/>\n" . $r;
}
@@ -272,7 +288,8 @@ class CategoryViewer {
$rescnt = count( $this->children );
$dbcnt = $this->cat->getSubcatCount();
$countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'subcat' );
- if( $rescnt > 0 ) {
+
+ if ( $rescnt > 0 ) {
# Showing subcategories
$r .= "<div id=\"mw-subcategories\">\n";
$r .= '<h2>' . wfMsg( 'subcategories' ) . "</h2>\n";
@@ -297,7 +314,7 @@ class CategoryViewer {
$rescnt = count( $this->articles );
$countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'article' );
- if( $rescnt > 0 ) {
+ if ( $rescnt > 0 ) {
$r = "<div id=\"mw-pages\">\n";
$r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
$r .= $countmsg;
@@ -308,7 +325,7 @@ class CategoryViewer {
}
function getImageSection() {
- if( $this->showGallery && ! $this->gallery->isEmpty() ) {
+ if ( $this->showGallery && ! $this->gallery->isEmpty() ) {
$dbcnt = $this->cat->getFileCount();
$rescnt = $this->gallery->count();
$countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'file' );
@@ -322,9 +339,9 @@ class CategoryViewer {
}
function getCategoryBottom() {
- if( $this->until != '' ) {
+ if ( $this->until != '' ) {
return $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit );
- } elseif( $this->nextPage != '' || $this->from != '' ) {
+ } elseif ( $this->nextPage != '' || $this->from != '' ) {
return $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit );
} else {
return '';
@@ -344,7 +361,7 @@ class CategoryViewer {
function formatList( $articles, $articles_start_char, $cutoff = 6 ) {
if ( count ( $articles ) > $cutoff ) {
return $this->columnList( $articles, $articles_start_char );
- } elseif ( count($articles) > 0) {
+ } elseif ( count( $articles ) > 0 ) {
// for short lists of articles in categories.
return $this->shortList( $articles, $articles_start_char );
}
@@ -355,61 +372,60 @@ class CategoryViewer {
* Format a list of articles chunked by letter in a three-column
* list, ordered vertically.
*
+ * TODO: Take the headers into account when creating columns, so they're
+ * more visually equal.
+ *
+ * More distant TODO: Scrap this and use CSS columns, whenever IE finally
+ * supports those.
+ *
* @param $articles Array
* @param $articles_start_char Array
* @return String
* @private
*/
function columnList( $articles, $articles_start_char ) {
- // divide list into three equal chunks
- $chunk = (int) (count ( $articles ) / 3);
+ $columns = array_combine( $articles, $articles_start_char );
+ # Split into three columns
+ $columns = array_chunk( $columns, ceil( count( $columns ) / 3 ), true /* preserve keys */ );
- // get and display header
- $r = '<table width="100%"><tr valign="top">';
+ $ret = '<table width="100%"><tr valign="top"><td>';
+ $prevchar = null;
- $prev_start_char = 'none';
+ foreach ( $columns as $column ) {
+ $colContents = array();
- // loop through the chunks
- for($startChunk = 0, $endChunk = $chunk, $chunkIndex = 0;
- $chunkIndex < 3;
- $chunkIndex++, $startChunk = $endChunk, $endChunk += $chunk + 1)
- {
- $r .= "<td>\n";
- $atColumnTop = true;
+ # Kind of like array_flip() here, but we keep duplicates in an
+ # array instead of dropping them.
+ foreach ( $column as $article => $char ) {
+ if ( !isset( $colContents[$char] ) ) {
+ $colContents[$char] = array();
+ }
+ $colContents[$char][] = $article;
+ }
- // output all articles in category
- for ($index = $startChunk ;
- $index < $endChunk && $index < count($articles);
- $index++ )
- {
- // check for change of starting letter or begining of chunk
- if ( ($index == $startChunk) ||
- ($articles_start_char[$index] != $articles_start_char[$index - 1]) )
-
- {
- if( $atColumnTop ) {
- $atColumnTop = false;
- } else {
- $r .= "</ul>\n";
- }
- $cont_msg = "";
- if ( $articles_start_char[$index] == $prev_start_char )
- $cont_msg = ' ' . wfMsgHtml( 'listingcontinuesabbrev' );
- $r .= "<h3>" . htmlspecialchars( $articles_start_char[$index] ) . "$cont_msg</h3>\n<ul>";
- $prev_start_char = $articles_start_char[$index];
+ $first = true;
+ foreach ( $colContents as $char => $articles ) {
+ $ret .= '<h3>' . htmlspecialchars( $char );
+ if ( $first && $char === $prevchar ) {
+ # We're continuing a previous chunk at the top of a new
+ # column, so add " cont." after the letter.
+ $ret .= ' ' . wfMsgHtml( 'listingcontinuesabbrev' );
}
+ $ret .= "</h3>\n";
- $r .= "<li>{$articles[$index]}</li>";
- }
- if( !$atColumnTop ) {
- $r .= "</ul>\n";
- }
- $r .= "</td>\n";
+ $ret .= '<ul><li>';
+ $ret .= implode( "</li>\n<li>", $articles );
+ $ret .= '</li></ul>';
+ $first = false;
+ $prevchar = $char;
+ }
+ $ret .= "</td>\n<td>";
}
- $r .= '</tr></table>';
- return $r;
+
+ $ret .= '</td></tr></table>';
+ return $ret;
}
/**
@@ -421,10 +437,10 @@ class CategoryViewer {
*/
function shortList( $articles, $articles_start_char ) {
$r = '<h3>' . htmlspecialchars( $articles_start_char[0] ) . "</h3>\n";
- $r .= '<ul><li>'.$articles[0].'</li>';
- for ($index = 1; $index < count($articles); $index++ )
+ $r .= '<ul><li>' . $articles[0] . '</li>';
+ for ( $index = 1; $index < count( $articles ); $index++ )
{
- if ($articles_start_char[$index] != $articles_start_char[$index - 1])
+ if ( $articles_start_char[$index] != $articles_start_char[$index - 1] )
{
$r .= "</ul><h3>" . htmlspecialchars( $articles_start_char[$index] ) . "</h3>\n<ul>";
}
@@ -450,14 +466,29 @@ class CategoryViewer {
$limitText = $wgLang->formatNum( $limit );
$prevLink = wfMsgExt( 'prevn', array( 'escape', 'parsemag' ), $limitText );
- if( $first != '' ) {
- $prevLink = $sk->makeLinkObj( $title, $prevLink,
- wfArrayToCGI( $query + array( 'until' => $first ) ) );
+
+ if ( $first != '' ) {
+ $prevQuery = $query;
+ $prevQuery['until'] = $first;
+ $prevLink = $sk->linkKnown(
+ $title,
+ $prevLink,
+ array(),
+ $prevQuery
+ );
}
+
$nextLink = wfMsgExt( 'nextn', array( 'escape', 'parsemag' ), $limitText );
- if( $last != '' ) {
- $nextLink = $sk->makeLinkObj( $title, $nextLink,
- wfArrayToCGI( $query + array( 'from' => $last ) ) );
+
+ if ( $last != '' ) {
+ $lastQuery = $query;
+ $lastQuery['from'] = $last;
+ $nextLink = $sk->linkKnown(
+ $title,
+ $nextLink,
+ array(),
+ $lastQuery
+ );
}
return "($prevLink) ($nextLink)";
@@ -490,12 +521,14 @@ class CategoryViewer {
# know the right figure.
# 3) We have no idea.
$totalrescnt = count( $this->articles ) + count( $this->children ) +
- ($this->showGallery ? $this->gallery->count() : 0);
- if($dbcnt == $rescnt || (($totalrescnt == $this->limit || $this->from
- || $this->until) && $dbcnt > $rescnt)){
+ ( $this->showGallery ? $this->gallery->count() : 0 );
+
+ if ( $dbcnt == $rescnt || ( ( $totalrescnt == $this->limit || $this->from
+ || $this->until ) && $dbcnt > $rescnt ) )
+ {
# Case 1: seems sane.
$totalcnt = $dbcnt;
- } elseif($totalrescnt < $this->limit && !$this->from && !$this->until){
+ } elseif ( $totalrescnt < $this->limit && !$this->from && !$this->until ) {
# Case 2: not sane, but salvageable. Use the number of results.
# Since there are fewer than 200, we can also take this opportunity
# to refresh the incorrect category table entry -- which should be
@@ -504,10 +537,14 @@ class CategoryViewer {
$this->cat->refreshCounts();
} else {
# Case 3: hopeless. Don't give a total count at all.
- return wfMsgExt("category-$type-count-limited", 'parse',
+ return wfMsgExt( "category-$type-count-limited", 'parse',
$wgLang->formatNum( $rescnt ) );
}
- return wfMsgExt( "category-$type-count", 'parse', $wgLang->formatNum( $rescnt ),
- $wgLang->formatNum( $totalcnt ) );
+ return wfMsgExt(
+ "category-$type-count",
+ 'parse',
+ $wgLang->formatNum( $rescnt ),
+ $wgLang->formatNum( $totalcnt )
+ );
}
}
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index 7c1c2856..5ac8a9be 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -1,5 +1,4 @@
<?php
-
/**
* The "Categoryfinder" class takes a list of articles, creates an internal
* representation of all their parent categories (as well as parents of
@@ -23,15 +22,14 @@
*
*/
class Categoryfinder {
-
- var $articles = array () ; # The original article IDs passed to the seed function
- var $deadend = array () ; # Array of DBKEY category names for categories that don't have a page
- var $parents = array () ; # Array of [ID => array()]
- var $next = array () ; # Array of article/category IDs
- var $targets = array () ; # Array of DBKEY category names
- var $name2id = array () ;
- var $mode ; # "AND" or "OR"
- var $dbr ; # Read-DB slave
+ var $articles = array(); # The original article IDs passed to the seed function
+ var $deadend = array(); # Array of DBKEY category names for categories that don't have a page
+ var $parents = array(); # Array of [ID => array()]
+ var $next = array(); # Array of article/category IDs
+ var $targets = array(); # Array of DBKEY category names
+ var $name2id = array();
+ var $mode; # "AND" or "OR"
+ var $dbr; # Read-DB slave
/**
* Constructor (currently empty).
@@ -45,16 +43,16 @@ class Categoryfinder {
* @param $categories FIXME
* @param $mode String: FIXME, default 'AND'.
*/
- function seed ( $article_ids , $categories , $mode = "AND" ) {
- $this->articles = $article_ids ;
- $this->next = $article_ids ;
- $this->mode = $mode ;
+ function seed( $article_ids, $categories, $mode = "AND" ) {
+ $this->articles = $article_ids;
+ $this->next = $article_ids;
+ $this->mode = $mode;
# Set the list of target categories; convert them to DBKEY form first
- $this->targets = array () ;
- foreach ( $categories AS $c ) {
+ $this->targets = array();
+ foreach ( $categories as $c ) {
$ct = Title::makeTitleSafe( NS_CATEGORY, $c );
- if( $ct ) {
+ if ( $ct ) {
$c = $ct->getDBkey();
$this->targets[$c] = $c;
}
@@ -69,19 +67,20 @@ class Categoryfinder {
function run () {
$this->dbr = wfGetDB( DB_SLAVE );
while ( count ( $this->next ) > 0 ) {
- $this->scan_next_layer () ;
+ $this->scan_next_layer();
}
# Now check if this applies to the individual articles
- $ret = array () ;
- foreach ( $this->articles AS $article ) {
- $conds = $this->targets ;
- if ( $this->check ( $article , $conds ) ) {
+ $ret = array();
+
+ foreach ( $this->articles as $article ) {
+ $conds = $this->targets;
+ if ( $this->check( $article, $conds ) ) {
# Matches the conditions
- $ret[] = $article ;
+ $ret[] = $article;
}
}
- return $ret ;
+ return $ret;
}
/**
@@ -91,108 +90,111 @@ class Categoryfinder {
* @param $path used to check for recursion loops
* @return bool Does this match the conditions?
*/
- function check ( $id , &$conds, $path=array() ) {
+ function check( $id , &$conds, $path = array() ) {
// Check for loops and stop!
- if( in_array( $id, $path ) )
+ if ( in_array( $id, $path ) ) {
return false;
+ }
+
$path[] = $id;
# Shortcut (runtime paranoia): No contitions=all matched
- if ( count ( $conds ) == 0 ) return true ;
+ if ( count( $conds ) == 0 ) {
+ return true;
+ }
- if ( !isset ( $this->parents[$id] ) ) return false ;
+ if ( !isset( $this->parents[$id] ) ) {
+ return false;
+ }
# iterate through the parents
- foreach ( $this->parents[$id] AS $p ) {
+ foreach ( $this->parents[$id] as $p ) {
$pname = $p->cl_to ;
# Is this a condition?
- if ( isset ( $conds[$pname] ) ) {
+ if ( isset( $conds[$pname] ) ) {
# This key is in the category list!
if ( $this->mode == "OR" ) {
# One found, that's enough!
- $conds = array () ;
- return true ;
+ $conds = array();
+ return true;
} else {
# Assuming "AND" as default
- unset ( $conds[$pname] ) ;
- if ( count ( $conds ) == 0 ) {
+ unset( $conds[$pname] ) ;
+ if ( count( $conds ) == 0 ) {
# All conditions met, done
- return true ;
+ return true;
}
}
}
# Not done yet, try sub-parents
- if ( !isset ( $this->name2id[$pname] ) ) {
+ if ( !isset( $this->name2id[$pname] ) ) {
# No sub-parent
continue ;
}
- $done = $this->check ( $this->name2id[$pname] , $conds, $path );
- if ( $done OR count ( $conds ) == 0 ) {
+ $done = $this->check( $this->name2id[$pname], $conds, $path );
+ if ( $done || count( $conds ) == 0 ) {
# Subparents have done it!
- return true ;
+ return true;
}
}
- return false ;
+ return false;
}
/**
* Scans a "parent layer" of the articles/categories in $this->next
*/
- function scan_next_layer () {
- $fname = "Categoryfinder::scan_next_layer" ;
-
+ function scan_next_layer() {
# Find all parents of the article currently in $this->next
- $layer = array () ;
+ $layer = array();
$res = $this->dbr->select(
- /* FROM */ 'categorylinks',
- /* SELECT */ '*',
- /* WHERE */ array( 'cl_from' => $this->next ),
- $fname."-1"
+ /* FROM */ 'categorylinks',
+ /* SELECT */ '*',
+ /* WHERE */ array( 'cl_from' => $this->next ),
+ __METHOD__ . "-1"
);
while ( $o = $this->dbr->fetchObject( $res ) ) {
$k = $o->cl_to ;
# Update parent tree
- if ( !isset ( $this->parents[$o->cl_from] ) ) {
- $this->parents[$o->cl_from] = array () ;
+ if ( !isset( $this->parents[$o->cl_from] ) ) {
+ $this->parents[$o->cl_from] = array();
}
- $this->parents[$o->cl_from][$k] = $o ;
+ $this->parents[$o->cl_from][$k] = $o;
# Ignore those we already have
- if ( in_array ( $k , $this->deadend ) ) continue ;
- if ( isset ( $this->name2id[$k] ) ) continue ;
+ if ( in_array ( $k , $this->deadend ) ) continue;
+
+ if ( isset ( $this->name2id[$k] ) ) continue;
# Hey, new category!
- $layer[$k] = $k ;
+ $layer[$k] = $k;
}
- $this->dbr->freeResult( $res ) ;
- $this->next = array() ;
+ $this->next = array();
# Find the IDs of all category pages in $layer, if they exist
if ( count ( $layer ) > 0 ) {
$res = $this->dbr->select(
- /* FROM */ 'page',
- /* SELECT */ 'page_id,page_title',
- /* WHERE */ array( 'page_namespace' => NS_CATEGORY , 'page_title' => $layer ),
- $fname."-2"
+ /* FROM */ 'page',
+ /* SELECT */ array( 'page_id', 'page_title' ),
+ /* WHERE */ array( 'page_namespace' => NS_CATEGORY , 'page_title' => $layer ),
+ __METHOD__ . "-2"
);
while ( $o = $this->dbr->fetchObject( $res ) ) {
- $id = $o->page_id ;
- $name = $o->page_title ;
- $this->name2id[$name] = $id ;
- $this->next[] = $id ;
- unset ( $layer[$name] ) ;
- }
- $this->dbr->freeResult( $res ) ;
+ $id = $o->page_id;
+ $name = $o->page_title;
+ $this->name2id[$name] = $id;
+ $this->next[] = $id;
+ unset( $layer[$name] );
}
+ }
# Mark dead ends
- foreach ( $layer AS $v ) {
- $this->deadend[$v] = $v ;
+ foreach ( $layer as $v ) {
+ $this->deadend[$v] = $v;
}
}
-} # END OF CLASS "Categoryfinder"
+}
diff --git a/includes/Cdb.php b/includes/Cdb.php
new file mode 100644
index 00000000..ab429872
--- /dev/null
+++ b/includes/Cdb.php
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * Read from a CDB file.
+ * Native and pure PHP implementations are provided.
+ * http://cr.yp.to/cdb.html
+ */
+abstract class CdbReader {
+ /**
+ * Open a file and return a subclass instance
+ */
+ public static function open( $fileName ) {
+ if ( self::haveExtension() ) {
+ return new CdbReader_DBA( $fileName );
+ } else {
+ wfDebug( "Warning: no dba extension found, using emulation.\n" );
+ return new CdbReader_PHP( $fileName );
+ }
+ }
+
+ /**
+ * Returns true if the native extension is available
+ */
+ public static function haveExtension() {
+ if ( !function_exists( 'dba_handlers' ) ) {
+ return false;
+ }
+ $handlers = dba_handlers();
+ if ( !in_array( 'cdb', $handlers ) || !in_array( 'cdb_make', $handlers ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Construct the object and open the file
+ */
+ abstract function __construct( $fileName );
+
+ /**
+ * Close the file. Optional, you can just let the variable go out of scope.
+ */
+ abstract function close();
+
+ /**
+ * Get a value with a given key. Only string values are supported.
+ */
+ abstract public function get( $key );
+}
+
+/**
+ * Write to a CDB file.
+ * Native and pure PHP implementations are provided.
+ */
+abstract class CdbWriter {
+ /**
+ * Open a writer and return a subclass instance.
+ * The user must have write access to the directory, for temporary file creation.
+ */
+ public static function open( $fileName ) {
+ if ( CdbReader::haveExtension() ) {
+ return new CdbWriter_DBA( $fileName );
+ } else {
+ wfDebug( "Warning: no dba extension found, using emulation.\n" );
+ return new CdbWriter_PHP( $fileName );
+ }
+ }
+
+ /**
+ * Create the object and open the file
+ */
+ abstract function __construct( $fileName );
+
+ /**
+ * Set a key to a given value. The value will be converted to string.
+ */
+ abstract public function set( $key, $value );
+
+ /**
+ * Close the writer object. You should call this function before the object
+ * goes out of scope, to write out the final hashtables.
+ */
+ abstract public function close();
+}
+
+
+/**
+ * Reader class which uses the DBA extension
+ */
+class CdbReader_DBA {
+ var $handle;
+
+ function __construct( $fileName ) {
+ $this->handle = dba_open( $fileName, 'r-', 'cdb' );
+ if ( !$this->handle ) {
+ throw new MWException( 'Unable to open DB file "' . $fileName . '"' );
+ }
+ }
+
+ function close() {
+ if( isset($this->handle) )
+ dba_close( $this->handle );
+ unset( $this->handle );
+ }
+
+ function get( $key ) {
+ return dba_fetch( $key, $this->handle );
+ }
+}
+
+
+/**
+ * Writer class which uses the DBA extension
+ */
+class CdbWriter_DBA {
+ var $handle, $realFileName, $tmpFileName;
+
+ function __construct( $fileName ) {
+ $this->realFileName = $fileName;
+ $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
+ $this->handle = dba_open( $this->tmpFileName, 'n', 'cdb_make' );
+ if ( !$this->handle ) {
+ throw new MWException( 'Unable to open DB file for write "' . $fileName . '"' );
+ }
+ }
+
+ function set( $key, $value ) {
+ return dba_insert( $key, $value, $this->handle );
+ }
+
+ function close() {
+ if( isset($this->handle) )
+ dba_close( $this->handle );
+ if ( wfIsWindows() ) {
+ unlink( $this->realFileName );
+ }
+ if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
+ throw new MWException( 'Unable to move the new CDB file into place.' );
+ }
+ unset( $this->handle );
+ }
+
+ function __destruct() {
+ if ( isset( $this->handle ) ) {
+ $this->close();
+ }
+ }
+}
+
diff --git a/includes/Cdb_PHP.php b/includes/Cdb_PHP.php
new file mode 100644
index 00000000..49294f71
--- /dev/null
+++ b/includes/Cdb_PHP.php
@@ -0,0 +1,374 @@
+<?php
+
+/**
+ * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
+ * appears in PHP 5.3. Changes are:
+ * * Error returns replaced with exceptions
+ * * Exception thrown if sizes or offsets are between 2GB and 4GB
+ * * Some variables renamed
+ */
+
+/**
+ * Common functions for readers and writers
+ */
+class CdbFunctions {
+ /**
+ * Take a modulo of a signed integer as if it were an unsigned integer.
+ * $b must be less than 0x40000000 and greater than 0
+ */
+ public static function unsignedMod( $a, $b ) {
+ if ( $a & 0x80000000 ) {
+ $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b );
+ return $m % $b;
+ } else {
+ return $a % $b;
+ }
+ }
+
+ /**
+ * Shift a signed integer right as if it were unsigned
+ */
+ public static function unsignedShiftRight( $a, $b ) {
+ if ( $b == 0 ) {
+ return $a;
+ }
+ if ( $a & 0x80000000 ) {
+ return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) );
+ } else {
+ return $a >> $b;
+ }
+ }
+
+ /**
+ * The CDB hash function.
+ */
+ public static function hash( $s ) {
+ $h = 5381;
+ for ( $i = 0; $i < strlen( $s ); $i++ ) {
+ $h5 = ($h << 5) & 0xffffffff;
+ // Do a 32-bit sum
+ // Inlined here for speed
+ $sum = ($h & 0x3fffffff) + ($h5 & 0x3fffffff);
+ $h =
+ (
+ ( $sum & 0x40000000 ? 1 : 0 )
+ + ( $h & 0x80000000 ? 2 : 0 )
+ + ( $h & 0x40000000 ? 1 : 0 )
+ + ( $h5 & 0x80000000 ? 2 : 0 )
+ + ( $h5 & 0x40000000 ? 1 : 0 )
+ ) << 30
+ | ( $sum & 0x3fffffff );
+ $h ^= ord( $s[$i] );
+ $h &= 0xffffffff;
+ }
+ return $h;
+ }
+}
+
+/**
+ * CDB reader class
+ */
+class CdbReader_PHP extends CdbReader {
+ /** The file handle */
+ var $handle;
+
+ /* number of hash slots searched under this key */
+ var $loop;
+
+ /* initialized if loop is nonzero */
+ var $khash;
+
+ /* initialized if loop is nonzero */
+ var $kpos;
+
+ /* initialized if loop is nonzero */
+ var $hpos;
+
+ /* initialized if loop is nonzero */
+ var $hslots;
+
+ /* initialized if findNext() returns true */
+ var $dpos;
+
+ /* initialized if cdb_findnext() returns 1 */
+ var $dlen;
+
+ function __construct( $fileName ) {
+ $this->handle = fopen( $fileName, 'rb' );
+ if ( !$this->handle ) {
+ throw new MWException( 'Unable to open DB file "' . $fileName . '"' );
+ }
+ $this->findStart();
+ }
+
+ function close() {
+ if( isset($this->handle) )
+ fclose( $this->handle );
+ unset( $this->handle );
+ }
+
+ public function get( $key ) {
+ // strval is required
+ if ( $this->find( strval( $key ) ) ) {
+ return $this->read( $this->dlen, $this->dpos );
+ } else {
+ return false;
+ }
+ }
+
+ protected function match( $key, $pos ) {
+ $buf = $this->read( strlen( $key ), $pos );
+ return $buf === $key;
+ }
+
+ protected function findStart() {
+ $this->loop = 0;
+ }
+
+ protected function read( $length, $pos ) {
+ if ( fseek( $this->handle, $pos ) == -1 ) {
+ // This can easily happen if the internal pointers are incorrect
+ throw new MWException( __METHOD__.': seek failed, file may be corrupted.' );
+ }
+
+ if ( $length == 0 ) {
+ return '';
+ }
+
+ $buf = fread( $this->handle, $length );
+ if ( $buf === false || strlen( $buf ) !== $length ) {
+ throw new MWException( __METHOD__.': read from cdb file failed, file may be corrupted' );
+ }
+ return $buf;
+ }
+
+ /**
+ * Unpack an unsigned integer and throw an exception if it needs more than 31 bits
+ */
+ protected function unpack31( $s ) {
+ $data = unpack( 'V', $s );
+ if ( $data[1] > 0x7fffffff ) {
+ throw new MWException( __METHOD__.': error in CDB file, integer too big' );
+ }
+ return $data[1];
+ }
+
+ /**
+ * Unpack a 32-bit signed integer
+ */
+ protected function unpackSigned( $s ) {
+ $data = unpack( 'va/vb', $s );
+ return $data['a'] | ( $data['b'] << 16 );
+ }
+
+ protected function findNext( $key ) {
+ if ( !$this->loop ) {
+ $u = CdbFunctions::hash( $key );
+ $buf = $this->read( 8, ( $u << 3 ) & 2047 );
+ $this->hslots = $this->unpack31( substr( $buf, 4 ) );
+ if ( !$this->hslots ) {
+ return false;
+ }
+ $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
+ $this->khash = $u;
+ $u = CdbFunctions::unsignedShiftRight( $u, 8 );
+ $u = CdbFunctions::unsignedMod( $u, $this->hslots );
+ $u <<= 3;
+ $this->kpos = $this->hpos + $u;
+ }
+
+ while ( $this->loop < $this->hslots ) {
+ $buf = $this->read( 8, $this->kpos );
+ $pos = $this->unpack31( substr( $buf, 4 ) );
+ if ( !$pos ) {
+ return false;
+ }
+ $this->loop += 1;
+ $this->kpos += 8;
+ if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
+ $this->kpos = $this->hpos;
+ }
+ $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
+ if ( $u === $this->khash ) {
+ $buf = $this->read( 8, $pos );
+ $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
+ if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
+ // Found
+ $this->dlen = $this->unpack31( substr( $buf, 4 ) );
+ $this->dpos = $pos + 8 + $keyLen;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected function find( $key ) {
+ $this->findStart();
+ return $this->findNext( $key );
+ }
+}
+
+/**
+ * CDB writer class
+ */
+class CdbWriter_PHP extends CdbWriter {
+ var $handle, $realFileName, $tmpFileName;
+
+ var $hplist;
+ var $numEntries, $pos;
+
+ function __construct( $fileName ) {
+ $this->realFileName = $fileName;
+ $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
+ $this->handle = fopen( $this->tmpFileName, 'wb' );
+ if ( !$this->handle ) {
+ throw new MWException( 'Unable to open DB file for write "' . $fileName . '"' );
+ }
+ $this->hplist = array();
+ $this->numentries = 0;
+ $this->pos = 2048; // leaving space for the pointer array, 256 * 8
+ if ( fseek( $this->handle, $this->pos ) == -1 ) {
+ throw new MWException( __METHOD__.': fseek failed' );
+ }
+ }
+
+ function __destruct() {
+ if ( isset( $this->handle ) ) {
+ $this->close();
+ }
+ }
+
+ public function set( $key, $value ) {
+ if ( strval( $key ) === '' ) {
+ // DBA cross-check hack
+ return;
+ }
+ $this->addbegin( strlen( $key ), strlen( $value ) );
+ $this->write( $key );
+ $this->write( $value );
+ $this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
+ }
+
+ public function close() {
+ $this->finish();
+ if( isset($this->handle) )
+ fclose( $this->handle );
+ if ( wfIsWindows() && file_exists($this->realFileName) ) {
+ unlink( $this->realFileName );
+ }
+ if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
+ throw new MWException( 'Unable to move the new CDB file into place.' );
+ }
+ unset( $this->handle );
+ }
+
+ protected function write( $buf ) {
+ $len = fwrite( $this->handle, $buf );
+ if ( $len !== strlen( $buf ) ) {
+ throw new MWException( 'Error writing to CDB file.' );
+ }
+ }
+
+ protected function posplus( $len ) {
+ $newpos = $this->pos + $len;
+ if ( $newpos > 0x7fffffff ) {
+ throw new MWException( 'A value in the CDB file is too large' );
+ }
+ $this->pos = $newpos;
+ }
+
+ protected function addend( $keylen, $datalen, $h ) {
+ $this->hplist[] = array(
+ 'h' => $h,
+ 'p' => $this->pos
+ );
+
+ $this->numentries++;
+ $this->posplus( 8 );
+ $this->posplus( $keylen );
+ $this->posplus( $datalen );
+ }
+
+ protected function addbegin( $keylen, $datalen ) {
+ if ( $keylen > 0x7fffffff ) {
+ throw new MWException( __METHOD__.': key length too long' );
+ }
+ if ( $datalen > 0x7fffffff ) {
+ throw new MWException( __METHOD__.': data length too long' );
+ }
+ $buf = pack( 'VV', $keylen, $datalen );
+ $this->write( $buf );
+ }
+
+ protected function finish() {
+ // Hack for DBA cross-check
+ $this->hplist = array_reverse( $this->hplist );
+
+ // Calculate the number of items that will be in each hashtable
+ $counts = array_fill( 0, 256, 0 );
+ foreach ( $this->hplist as $item ) {
+ ++ $counts[ 255 & $item['h'] ];
+ }
+
+ // Fill in $starts with the *end* indexes
+ $starts = array();
+ $pos = 0;
+ for ( $i = 0; $i < 256; ++$i ) {
+ $pos += $counts[$i];
+ $starts[$i] = $pos;
+ }
+
+ // Excessively clever and indulgent code to simultaneously fill $packedTables
+ // with the packed hashtables, and adjust the elements of $starts
+ // to actually point to the starts instead of the ends.
+ $packedTables = array_fill( 0, $this->numentries, false );
+ foreach ( $this->hplist as $item ) {
+ $packedTables[--$starts[255 & $item['h']]] = $item;
+ }
+
+ $final = '';
+ for ( $i = 0; $i < 256; ++$i ) {
+ $count = $counts[$i];
+
+ // The size of the hashtable will be double the item count.
+ // The rest of the slots will be empty.
+ $len = $count + $count;
+ $final .= pack( 'VV', $this->pos, $len );
+
+ $hashtable = array();
+ for ( $u = 0; $u < $len; ++$u ) {
+ $hashtable[$u] = array( 'h' => 0, 'p' => 0 );
+ }
+
+ // Fill the hashtable, using the next empty slot if the hashed slot
+ // is taken.
+ for ( $u = 0; $u < $count; ++$u ) {
+ $hp = $packedTables[$starts[$i] + $u];
+ $where = CdbFunctions::unsignedMod(
+ CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
+ while ( $hashtable[$where]['p'] )
+ if ( ++$where == $len )
+ $where = 0;
+ $hashtable[$where] = $hp;
+ }
+
+ // Write the hashtable
+ for ( $u = 0; $u < $len; ++$u ) {
+ $buf = pack( 'vvV',
+ $hashtable[$u]['h'] & 0xffff,
+ CdbFunctions::unsignedShiftRight( $hashtable[$u]['h'], 16 ),
+ $hashtable[$u]['p'] );
+ $this->write( $buf );
+ $this->posplus( 8 );
+ }
+ }
+
+ // Write the pointer array at the start of the file
+ rewind( $this->handle );
+ if ( ftell( $this->handle ) != 0 ) {
+ throw new MWException( __METHOD__.': Error rewinding to start of file' );
+ }
+ $this->write( $final );
+ }
+}
diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php
index de804c5c..8dce679b 100644
--- a/includes/ChangeTags.php
+++ b/includes/ChangeTags.php
@@ -1,56 +1,63 @@
<?php
-if (!defined( 'MEDIAWIKI' ))
+if( !defined( 'MEDIAWIKI' ) )
die;
class ChangeTags {
static function formatSummaryRow( $tags, $page ) {
- if (!$tags)
- return array('',array());
+ if( !$tags )
+ return array( '', array() );
$classes = array();
-
+
$tags = explode( ',', $tags );
$displayTags = array();
foreach( $tags as $tag ) {
- $displayTags[] = self::tagDescription( $tag );
- $classes[] = "mw-tag-$tag";
+ $displayTags[] = Xml::tags(
+ 'span',
+ array( 'class' => 'mw-tag-marker ' .
+ Sanitizer::escapeClass( "mw-tag-marker-$tag" ) ),
+ self::tagDescription( $tag )
+ );
+ $classes[] = Sanitizer::escapeClass( "mw-tag-$tag" );
}
- return array( '(' . implode( ', ', $displayTags ) . ')', $classes );
+ $markers = '(' . implode( ', ', $displayTags ) . ')';
+ $markers = Xml::tags( 'span', array( 'class' => 'mw-tag-markers' ), $markers );
+ return array( $markers, $classes );
}
static function tagDescription( $tag ) {
$msg = wfMsgExt( "tag-$tag", 'parseinline' );
if ( wfEmptyMsg( "tag-$tag", $msg ) ) {
- return htmlspecialchars($tag);
+ return htmlspecialchars( $tag );
}
return $msg;
}
## Basic utility method to add tags to a particular change, given its rc_id, rev_id and/or log_id.
- static function addTags( $tags, $rc_id=null, $rev_id=null, $log_id=null, $params = null ) {
- if ( !is_array($tags) ) {
+ static function addTags( $tags, $rc_id = null, $rev_id = null, $log_id = null, $params = null ) {
+ if ( !is_array( $tags ) ) {
$tags = array( $tags );
}
$tags = array_filter( $tags ); // Make sure we're submitting all tags...
- if (!$rc_id && !$rev_id && !$log_id) {
+ if( !$rc_id && !$rev_id && !$log_id ) {
throw new MWException( "At least one of: RCID, revision ID, and log ID MUST be specified when adding a tag to a change!" );
}
$dbr = wfGetDB( DB_SLAVE );
// Might as well look for rcids and so on.
- if (!$rc_id) {
+ if( !$rc_id ) {
$dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
- if ($log_id) {
+ if( $log_id ) {
$rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_logid' => $log_id ), __METHOD__ );
- } elseif ($rev_id) {
+ } elseif( $rev_id ) {
$rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_this_oldid' => $rev_id ), __METHOD__ );
}
- } elseif (!$log_id && !$rev_id) {
+ } elseif( !$log_id && !$rev_id ) {
$dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
$log_id = $dbr->selectField( 'recentchanges', 'rc_logid', array( 'rc_id' => $rc_id ), __METHOD__ );
$rev_id = $dbr->selectField( 'recentchanges', 'rc_this_oldid', array( 'rc_id' => $rc_id ), __METHOD__ );
@@ -63,8 +70,8 @@ class ChangeTags {
$prevTags = $prevTags ? $prevTags : '';
$prevTags = array_filter( explode( ',', $prevTags ) );
$newTags = array_unique( array_merge( $prevTags, $tags ) );
- sort($prevTags);
- sort($newTags);
+ sort( $prevTags );
+ sort( $newTags );
if ( $prevTags == $newTags ) {
// No change.
@@ -72,15 +79,28 @@ class ChangeTags {
}
$dbw = wfGetDB( DB_MASTER );
- $dbw->replace( 'tag_summary', array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ), array_filter( array_merge( $tsConds, array( 'ts_tags' => implode( ',', $newTags ) ) ) ), __METHOD__ );
+ $dbw->replace(
+ 'tag_summary',
+ array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ),
+ array_filter( array_merge( $tsConds, array( 'ts_tags' => implode( ',', $newTags ) ) ) ),
+ __METHOD__
+ );
// Insert the tags rows.
$tagsRows = array();
foreach( $tags as $tag ) { // Filter so we don't insert NULLs as zero accidentally.
- $tagsRows[] = array_filter( array( 'ct_tag' => $tag, 'ct_rc_id' => $rc_id, 'ct_log_id' => $log_id, 'ct_rev_id' => $rev_id, 'ct_params' => $params ) );
+ $tagsRows[] = array_filter(
+ array(
+ 'ct_tag' => $tag,
+ 'ct_rc_id' => $rc_id,
+ 'ct_log_id' => $log_id,
+ 'ct_rev_id' => $rev_id,
+ 'ct_params' => $params
+ )
+ );
}
- $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array('IGNORE') );
+ $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array( 'IGNORE' ) );
return true;
}
@@ -89,38 +109,40 @@ class ChangeTags {
* Applies all tags-related changes to a query.
* Handles selecting tags, and filtering.
* Needs $tables to be set up properly, so we can figure out which join conditions to use.
- */
+ */
static function modifyDisplayQuery( &$tables, &$fields, &$conds,
&$join_conds, &$options, $filter_tag = false ) {
global $wgRequest, $wgUseTagFilter;
-
- if ($filter_tag === false) {
+
+ if( $filter_tag === false ) {
$filter_tag = $wgRequest->getVal( 'tagfilter' );
}
// Figure out which conditions can be done.
$join_field = '';
- if ( in_array('recentchanges', $tables) ) {
+ if ( in_array( 'recentchanges', $tables ) ) {
$join_cond = 'rc_id';
- } elseif( in_array('logging', $tables) ) {
+ } elseif( in_array( 'logging', $tables ) ) {
$join_cond = 'log_id';
- } elseif ( in_array('revision', $tables) ) {
+ } elseif ( in_array( 'revision', $tables ) ) {
$join_cond = 'rev_id';
} else {
- throw new MWException( "Unable to determine appropriate JOIN condition for tagging." );
+ throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
}
// JOIN on tag_summary
$tables[] = 'tag_summary';
$join_conds['tag_summary'] = array( 'LEFT JOIN', "ts_$join_cond=$join_cond" );
$fields[] = 'ts_tags';
-
- if ($wgUseTagFilter && $filter_tag) {
+
+ if( $wgUseTagFilter && $filter_tag ) {
// Somebody wants to filter on a tag.
// Add an INNER JOIN on change_tag
// FORCE INDEX -- change_tags will almost ALWAYS be the correct query plan.
- $options['USE INDEX'] = array( 'change_tag' => 'change_tag_tag_id' );
+ global $wgOldChangeTagsIndex;
+ $index = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id';
+ $options['USE INDEX'] = array( 'change_tag' => $index );
unset( $options['FORCE INDEX'] );
$tables[] = 'change_tag';
$join_conds['change_tag'] = array( 'INNER JOIN', "ct_$join_cond=$join_cond" );
@@ -134,15 +156,15 @@ class ChangeTags {
*/
static function buildTagFilterSelector( $selected='', $fullForm = false /* used to put a full form around the selector */ ) {
global $wgUseTagFilter;
-
+
if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) )
return $fullForm ? '' : array();
-
+
global $wgTitle;
-
+
$data = array( wfMsgExt( 'tag-filter', 'parseinline' ), Xml::input( 'tagfilter', 20, $selected ) );
- if (!$fullForm) {
+ if ( !$fullForm ) {
return $data;
}
@@ -160,9 +182,9 @@ class ChangeTags {
global $wgMemc;
$key = wfMemcKey( 'valid-tags' );
- if ($tags = $wgMemc->get( $key ))
+ if ( $tags = $wgMemc->get( $key ) )
return $tags;
-
+
$emptyTags = array();
// Some DB stuff
@@ -171,8 +193,8 @@ class ChangeTags {
while( $row = $res->fetchObject() ) {
$emptyTags[] = $row->vt_tag;
}
-
- wfRunHooks( 'ListDefinedTags', array(&$emptyTags) );
+
+ wfRunHooks( 'ListDefinedTags', array( &$emptyTags ) );
$emptyTags = array_filter( array_unique( $emptyTags ) );
diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php
index a0c2767a..bc50fe02 100644
--- a/includes/ChangesFeed.php
+++ b/includes/ChangesFeed.php
@@ -1,14 +1,31 @@
<?php
+/**
+ * Feed to Special:RecentChanges and Special:RecentChangesLiked
+ *
+ * @ingroup Feed
+ */
class ChangesFeed {
-
public $format, $type, $titleMsg, $descMsg;
+ /**
+ * Constructor
+ *
+ * @param $format String: feed's format (either 'rss' or 'atom')
+ * @param $type String: type of feed (for cache keys)
+ */
public function __construct( $format, $type ) {
$this->format = $format;
$this->type = $type;
}
+ /**
+ * Get a ChannelFeed subclass object to use
+ *
+ * @param $title String: feed's title
+ * @param $description String: feed's description
+ * @return ChannelFeed subclass or false on failure
+ */
public function getFeedObject( $title, $description ) {
global $wgSitename, $wgContLanguageCode, $wgFeedClasses, $wgTitle;
$feedTitle = "$wgSitename - {$title} [$wgContLanguageCode]";
@@ -18,16 +35,26 @@ class ChangesFeed {
$feedTitle, htmlspecialchars( $description ), $wgTitle->getFullUrl() );
}
- public function execute( $feed, $rows, $limit=0, $hideminor=false, $lastmod=false, $target='' ) {
+ /**
+ * Generates feed's content
+ *
+ * @param $feed ChannelFeed subclass object (generally the one returned by getFeedObject())
+ * @param $rows ResultWrapper object with rows in recentchanges table
+ * @param $lastmod Integer: timestamp of the last item in the recentchanges table (only used for the cache key)
+ * @param $opts FormOptions as in SpecialRecentChanges::getDefaultOptions()
+ * @return null or true
+ */
+ public function execute( $feed, $rows, $lastmod, $opts ) {
global $messageMemc, $wgFeedCacheTimeout;
- global $wgSitename, $wgContLanguageCode;
+ global $wgSitename, $wgLang;
if ( !FeedUtils::checkFeedOutput( $this->format ) ) {
return;
}
$timekey = wfMemcKey( $this->type, $this->format, 'timestamp' );
- $key = wfMemcKey( $this->type, $this->format, $limit, $hideminor, $target );
+ $optionsHash = md5( serialize( $opts->getAllValues() ) );
+ $key = wfMemcKey( $this->type, $this->format, $wgLang->getCode(), $optionsHash );
FeedUtils::checkPurge($timekey, $key);
@@ -52,13 +79,28 @@ class ChangesFeed {
return true;
}
+ /**
+ * Save to feed result to $messageMemc
+ *
+ * @param $feed String: feed's content
+ * @param $timekey String: memcached key of the last modification
+ * @param $key String: memcached key of the content
+ */
public function saveToCache( $feed, $timekey, $key ) {
global $messageMemc;
$expire = 3600 * 24; # One day
- $messageMemc->set( $key, $feed );
+ $messageMemc->set( $key, $feed, $expire );
$messageMemc->set( $timekey, wfTimestamp( TS_MW ), $expire );
}
+ /**
+ * Try to load the feed result from $messageMemc
+ *
+ * @param $lastmod Integer: timestamp of the last item in the recentchanges table
+ * @param $timekey String: memcached key of the last modification
+ * @param $key String: memcached key of the content
+ * @return feed's content on cache hit or false on cache miss
+ */
public function loadFromCache( $lastmod, $timekey, $key ) {
global $wgFeedCacheTimeout, $messageMemc;
$feedLastmod = $messageMemc->get( $timekey );
@@ -86,10 +128,10 @@ class ChangesFeed {
}
/**
- * Generate the feed items given a row from the database.
- * @param $rows Database resource with recentchanges rows
- * @param $feed Feed object
- */
+ * Generate the feed items given a row from the database.
+ * @param $rows DatabaseBase resource with recentchanges rows
+ * @param $feed Feed object
+ */
public static function generateFeed( $rows, &$feed ) {
wfProfileIn( __METHOD__ );
@@ -113,18 +155,20 @@ class ChangesFeed {
foreach( $sorted as $obj ) {
$title = Title::makeTitle( $obj->rc_namespace, $obj->rc_title );
$talkpage = $title->getTalkPage();
+ // Skip items with deleted content (avoids partially complete/inconsistent output)
+ if( $obj->rc_deleted ) continue;
$item = new FeedItem(
$title->getPrefixedText(),
FeedUtils::formatDiff( $obj ),
- $title->getFullURL( 'diff=' . $obj->rc_this_oldid . '&oldid=prev' ),
+ $obj->rc_this_oldid ? $title->getFullURL( 'diff=' . $obj->rc_this_oldid . '&oldid=prev' ) : $title->getFullURL(),
$obj->rc_timestamp,
($obj->rc_deleted & Revision::DELETED_USER) ? wfMsgHtml('rev-deleted-user') : $obj->rc_user_text,
$talkpage->getFullURL()
- );
+ );
$feed->outItem( $item );
}
$feed->outFooter();
wfProfileOut( __METHOD__ );
}
-} \ No newline at end of file
+}
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index 4eda1dbd..9f092991 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -25,13 +25,14 @@ class RCCacheEntry extends RecentChange {
class ChangesList {
# Called by history lists and recent changes
public $skin;
+ protected $watchlist = false;
/**
* Changeslist contructor
- * @param Skin $skin
+ * @param $skin Skin
*/
- public function __construct( &$skin ) {
- $this->skin =& $skin;
+ public function __construct( $skin ) {
+ $this->skin = $skin;
$this->preCacheMessages();
}
@@ -44,7 +45,7 @@ class ChangesList {
*/
public static function newFromUser( &$user ) {
$sk = $user->getSkin();
- $list = NULL;
+ $list = null;
if( wfRunHooks( 'FetchChangesList', array( &$user, &$sk, &$list ) ) ) {
return $user->getOption( 'usenewrc' ) ?
new EnhancedChangesList( $sk ) : new OldChangesList( $sk );
@@ -52,6 +53,14 @@ class ChangesList {
return $list;
}
}
+
+ /**
+ * Sets the list to use a <li class="watchlist-(namespace)-(page)"> tag
+ * @param $value Boolean
+ */
+ public function setWatchlistDivs( $value = true ) {
+ $this->watchlist = $value;
+ }
/**
* As we use the same small set of messages in various methods and that
@@ -59,8 +68,8 @@ class ChangesList {
*/
private function preCacheMessages() {
if( !isset( $this->message ) ) {
- foreach( explode(' ', 'cur diff hist minoreditletter newpageletter last '.
- 'blocklink history boteditletter semicolon-separator' ) as $msg ) {
+ foreach ( explode( ' ', 'cur diff hist last blocklink history ' .
+ 'semicolon-separator pipe-separator' ) as $msg ) {
$this->message[$msg] = wfMsgExt( $msg, array( 'escapenoentities' ) );
}
}
@@ -69,26 +78,95 @@ class ChangesList {
/**
* Returns the appropriate flags for new page, minor change and patrolling
- * @param bool $new
- * @param bool $minor
- * @param bool $patrolled
- * @param string $nothing, string to use for empty space
- * @param bool $bot
- * @return string
+ * @param $new Boolean
+ * @param $minor Boolean
+ * @param $patrolled Boolean
+ * @param $nothing String to use for empty space
+ * @param $bot Boolean
+ * @return String
*/
protected function recentChangesFlags( $new, $minor, $patrolled, $nothing = '&nbsp;', $bot = false ) {
- $f = $new ?
- '<span class="newpage">' . $this->message['newpageletter'] . '</span>' : $nothing;
- $f .= $minor ?
- '<span class="minor">' . $this->message['minoreditletter'] . '</span>' : $nothing;
- $f .= $bot ? '<span class="bot">' . $this->message['boteditletter'] . '</span>' : $nothing;
- $f .= $patrolled ? '<span class="unpatrolled">!</span>' : $nothing;
+ $f = $new ? self::flag( 'newpage' ) : $nothing;
+ $f .= $minor ? self::flag( 'minor' ) : $nothing;
+ $f .= $bot ? self::flag( 'bot' ) : $nothing;
+ $f .= $patrolled ? self::flag( 'unpatrolled' ) : $nothing;
return $f;
}
/**
+ * Provide the <abbr> element appropriate to a given abbreviated flag,
+ * namely the flag indicating a new page, a minor edit, a bot edit, or an
+ * unpatrolled edit. By default in English it will contain "N", "m", "b",
+ * "!" respectively, plus it will have an appropriate title and class.
+ *
+ * @param $key String: 'newpage', 'unpatrolled', 'minor', or 'bot'
+ * @return String: Raw HTML
+ */
+ public static function flag( $key ) {
+ static $messages = null;
+ if ( is_null( $messages ) ) {
+ foreach ( explode( ' ', 'minoreditletter boteditletter newpageletter ' .
+ 'unpatrolledletter recentchanges-label-minor recentchanges-label-bot ' .
+ 'recentchanges-label-newpage recentchanges-label-unpatrolled' ) as $msg ) {
+ $messages[$msg] = wfMsgExt( $msg, 'escapenoentities' );
+ }
+ }
+ # Inconsistent naming, bleh
+ if ( $key == 'newpage' || $key == 'unpatrolled' ) {
+ $key2 = $key;
+ } else {
+ $key2 = $key . 'edit';
+ }
+ return "<abbr class=\"$key\" title=\""
+ . $messages["recentchanges-label-$key"] . "\">"
+ . $messages["${key2}letter"]
+ . '</abbr>';
+ }
+
+ /**
+ * Some explanatory wrapper text for the given flag, to be used in a legend
+ * explaining what the flags mean. For instance, "N - new page". See
+ * also flag().
+ *
+ * @param $key String: 'newpage', 'unpatrolled', 'minor', or 'bot'
+ * @return String: Raw HTML
+ */
+ private static function flagLine( $key ) {
+ return wfMsgExt( "recentchanges-legend-$key", array( 'escapenoentities',
+ 'replaceafter' ), self::flag( $key ) );
+ }
+
+ /**
+ * A handy legend to tell users what the little "m", "b", and so on mean.
+ *
+ * @return String: Raw HTML
+ */
+ public static function flagLegend() {
+ global $wgGroupPermissions, $wgLang;
+
+ $flags = array( self::flagLine( 'newpage' ),
+ self::flagLine( 'minor' ) );
+
+ # Don't show info on bot edits unless there's a bot group of some kind
+ foreach ( $wgGroupPermissions as $rights ) {
+ if ( isset( $rights['bot'] ) && $rights['bot'] ) {
+ $flags[] = self::flagLine( 'bot' );
+ break;
+ }
+ }
+
+ if ( self::usePatrol() ) {
+ $flags[] = self::flagLine( 'unpatrolled' );
+ }
+
+ return '<div class="mw-rc-label-legend">' .
+ wfMsgExt( 'recentchanges-label-legend', 'parseinline',
+ $wgLang->commaList( $flags ) ) . '</div>';
+ }
+
+ /**
* Returns text for the start of the tabular part of RC
- * @return string
+ * @return String
*/
public function beginRecentChangesList() {
$this->rc_cache = array();
@@ -101,14 +179,26 @@ class ChangesList {
/**
* Show formatted char difference
- * @param int $old bytes
- * @param int $new bytes
- * @returns string
+ * @param $old Integer: bytes
+ * @param $new Integer: bytes
+ * @returns String
*/
public static function showCharacterDifference( $old, $new ) {
- global $wgRCChangedSizeThreshold, $wgLang;
+ global $wgRCChangedSizeThreshold, $wgLang, $wgMiserMode;
$szdiff = $new - $old;
- $formatedSize = wfMsgExt( 'rc-change-size', array( 'parsemag', 'escape' ), $wgLang->formatNum( $szdiff ) );
+
+ $code = $wgLang->getCode();
+ static $fastCharDiff = array();
+ if ( !isset($fastCharDiff[$code]) ) {
+ $fastCharDiff[$code] = $wgMiserMode || wfMsgNoTrans( 'rc-change-size' ) === '$1';
+ }
+
+ $formatedSize = $wgLang->formatNum($szdiff);
+
+ if ( !$fastCharDiff[$code] ) {
+ $formatedSize = wfMsgExt( 'rc-change-size', array( 'parsemag', 'escape' ), $formatedSize );
+ }
+
if( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
$tag = 'strong';
} else {
@@ -125,7 +215,7 @@ class ChangesList {
/**
* Returns text for the end of RC
- * @return string
+ * @return String
*/
public function endRecentChangesList() {
if( $this->rclistOpen ) {
@@ -135,73 +225,130 @@ class ChangesList {
}
}
- protected function insertMove( &$s, $rc ) {
+ public function insertMove( &$s, $rc ) {
# Diff
$s .= '(' . $this->message['diff'] . ') (';
# Hist
- $s .= $this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), $this->message['hist'],
- 'action=history' ) . ') . . ';
+ $s .= $this->skin->link(
+ $rc->getMovedToTitle(),
+ $this->message['hist'],
+ array(),
+ array( 'action' => 'history' ),
+ array( 'known', 'noclasses' )
+ ) . ') . . ';
# "[[x]] moved to [[y]]"
$msg = ( $rc->mAttribs['rc_type'] == RC_MOVE ) ? '1movedto2' : '1movedto2_redir';
- $s .= wfMsg( $msg, $this->skin->makeKnownLinkObj( $rc->getTitle(), '', 'redirect=no' ),
- $this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), '' ) );
+ $s .= wfMsg(
+ $msg,
+ $this->skin->link(
+ $rc->getTitle(),
+ null,
+ array(),
+ array( 'redirect' => 'no' ),
+ array( 'known', 'noclasses' )
+ ),
+ $this->skin->link(
+ $rc->getMovedToTitle(),
+ null,
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ )
+ );
}
- protected function insertDateHeader( &$s, $rc_timestamp ) {
+ public function insertDateHeader( &$s, $rc_timestamp ) {
global $wgLang;
# Make date header if necessary
$date = $wgLang->date( $rc_timestamp, true, true );
if( $date != $this->lastdate ) {
- if( '' != $this->lastdate ) {
+ if( $this->lastdate != '' ) {
$s .= "</ul>\n";
}
- $s .= '<h4>'.$date."</h4>\n<ul class=\"special\">";
+ $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
$this->lastdate = $date;
$this->rclistOpen = true;
}
}
- protected function insertLog( &$s, $title, $logtype ) {
+ public function insertLog( &$s, $title, $logtype ) {
$logname = LogPage::logName( $logtype );
- $s .= '(' . $this->skin->makeKnownLinkObj($title, $logname ) . ')';
+ $s .= '(' . $this->skin->link(
+ $title,
+ $logname,
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ ) . ')';
}
- protected function insertDiffHist( &$s, &$rc, $unpatrolled ) {
+ public function insertDiffHist( &$s, &$rc, $unpatrolled ) {
# Diff link
if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
$diffLink = $this->message['diff'];
- } else if( !$this->userCan($rc,Revision::DELETED_TEXT) ) {
+ } else if( !self::userCan($rc,Revision::DELETED_TEXT) ) {
$diffLink = $this->message['diff'];
} else {
- $rcidparam = $unpatrolled ? array( 'rcid' => $rc->mAttribs['rc_id'] ) : array();
- $diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['diff'],
- wfArrayToCGI( array(
- 'curid' => $rc->mAttribs['rc_cur_id'],
- 'diff' => $rc->mAttribs['rc_this_oldid'],
- 'oldid' => $rc->mAttribs['rc_last_oldid'] ),
- $rcidparam ),
- '', '', ' tabindex="'.$rc->counter.'"');
- }
- $s .= '('.$diffLink.') (';
+ $query = array(
+ 'curid' => $rc->mAttribs['rc_cur_id'],
+ 'diff' => $rc->mAttribs['rc_this_oldid'],
+ 'oldid' => $rc->mAttribs['rc_last_oldid']
+ );
+
+ if( $unpatrolled ) {
+ $query['rcid'] = $rc->mAttribs['rc_id'];
+ };
+
+ $diffLink = $this->skin->link(
+ $rc->getTitle(),
+ $this->message['diff'],
+ array( 'tabindex' => $rc->counter ),
+ $query,
+ array( 'known', 'noclasses' )
+ );
+ }
+ $s .= '(' . $diffLink . $this->message['pipe-separator'];
# History link
- $s .= $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['hist'],
- wfArrayToCGI( array(
+ $s .= $this->skin->link(
+ $rc->getTitle(),
+ $this->message['hist'],
+ array(),
+ array(
'curid' => $rc->mAttribs['rc_cur_id'],
- 'action' => 'history' ) ) );
+ 'action' => 'history'
+ ),
+ array( 'known', 'noclasses' )
+ );
$s .= ') . . ';
}
- protected function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
+ public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
global $wgContLang;
# If it's a new article, there is no diff link, but if it hasn't been
# patrolled yet, we need to give users a way to do so
- $params = ( $unpatrolled && $rc->mAttribs['rc_type'] == RC_NEW ) ?
- 'rcid='.$rc->mAttribs['rc_id'] : '';
+ $params = array();
+
+ if ( $unpatrolled && $rc->mAttribs['rc_type'] == RC_NEW ) {
+ $params['rcid'] = $rc->mAttribs['rc_id'];
+ }
+
if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
- $articlelink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
- $articlelink = '<span class="history-deleted">'.$articlelink.'</span>';
+ $articlelink = $this->skin->link(
+ $rc->getTitle(),
+ null,
+ array(),
+ $params,
+ array( 'known', 'noclasses' )
+ );
+ $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
} else {
- $articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
+ $articlelink = ' '. $this->skin->link(
+ $rc->getTitle(),
+ null,
+ array(),
+ $params,
+ array( 'known', 'noclasses' )
+ );
}
# Bolden pages watched by this user
if( $watched ) {
@@ -216,7 +363,7 @@ class ChangesList {
$s .= " $articlelink";
}
- protected function insertTimestamp( &$s, $rc ) {
+ public function insertTimestamp( &$s, $rc ) {
global $wgLang;
$s .= $this->message['semicolon-separator'] .
$wgLang->time( $rc->mAttribs['rc_timestamp'], true, true ) . ' . . ';
@@ -233,7 +380,7 @@ class ChangesList {
}
/** insert a formatted action */
- protected function insertAction( &$s, &$rc ) {
+ public function insertAction( &$s, &$rc ) {
if( $rc->mAttribs['rc_type'] == RC_LOG ) {
if( $this->isDeleted( $rc, LogPage::DELETED_ACTION ) ) {
$s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-event' ) . '</span>';
@@ -245,7 +392,7 @@ class ChangesList {
}
/** insert a formatted comment */
- protected function insertComment( &$s, &$rc ) {
+ public function insertComment( &$s, &$rc ) {
if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
if( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
$s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span>';
@@ -257,7 +404,7 @@ class ChangesList {
/**
* Check whether to enable recent changes patrol features
- * @return bool
+ * @return Boolean
*/
public static function usePatrol() {
global $wgUser;
@@ -283,9 +430,9 @@ class ChangesList {
/**
* Determine if said field of a revision is hidden
- * @param RCCacheEntry $rc
- * @param int $field one of DELETED_* bitfield constants
- * @return bool
+ * @param $rc RCCacheEntry
+ * @param $field Integer: one of DELETED_* bitfield constants
+ * @return Boolean
*/
public static function isDeleted( $rc, $field ) {
return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
@@ -294,24 +441,19 @@ class ChangesList {
/**
* Determine if the current user is allowed to view a particular
* field of this revision, if it's marked as deleted.
- * @param RCCacheEntry $rc
- * @param int $field
- * @return bool
+ * @param $rc RCCacheEntry
+ * @param $field Integer
+ * @return Boolean
*/
public static function userCan( $rc, $field ) {
- if( ( $rc->mAttribs['rc_deleted'] & $field ) == $field ) {
- global $wgUser;
- $permission = ( $rc->mAttribs['rc_deleted'] & Revision::DELETED_RESTRICTED ) == Revision::DELETED_RESTRICTED
- ? 'suppressrevision'
- : 'deleterevision';
- wfDebug( "Checking for $permission due to $field match on {$rc->mAttribs['rc_deleted']}\n" );
- return $wgUser->isAllowed( $permission );
+ if( $rc->mAttribs['rc_type'] == RC_LOG ) {
+ return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field );
} else {
- return true;
+ return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field );
}
}
- protected function maybeWatchedLink( $link, $watched=false ) {
+ protected function maybeWatchedLink( $link, $watched = false ) {
if( $watched ) {
return '<strong class="mw-watched">' . $link . '</strong>';
} else {
@@ -320,7 +462,7 @@ class ChangesList {
}
/** Inserts a rollback link */
- protected function insertRollback( &$s, &$rc ) {
+ public function insertRollback( &$s, &$rc ) {
global $wgUser;
if( !$rc->mAttribs['rc_new'] && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
$page = $rc->getTitle();
@@ -340,7 +482,7 @@ class ChangesList {
}
}
- protected function insertTags( &$s, &$rc, &$classes ) {
+ public function insertTags( &$s, &$rc, &$classes ) {
if ( empty($rc->mAttribs['ts_tags']) )
return;
@@ -349,7 +491,7 @@ class ChangesList {
$s .= ' ' . $tagSummary;
}
- protected function insertExtra( &$s, &$rc, &$classes ) {
+ public function insertExtra( &$s, &$rc, &$classes ) {
## Empty, used for subclassers to add anything special.
}
}
@@ -362,8 +504,8 @@ class OldChangesList extends ChangesList {
/**
* Format a line using the old system (aka without any javascript).
*/
- public function recentChangesLine( &$rc, $watched = false, $linenumber = NULL ) {
- global $wgContLang, $wgLang, $wgRCShowChangedSize, $wgUser;
+ public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
+ global $wgLang, $wgRCShowChangedSize, $wgUser;
wfProfileIn( __METHOD__ );
# Should patrol-related stuff be shown?
$unpatrolled = $wgUser->useRCPatrol() && !$rc->mAttribs['rc_patrolled'];
@@ -426,20 +568,20 @@ class OldChangesList extends ChangesList {
# For subclasses
$this->insertExtra( $s, $rc, $classes );
- # Mark revision as deleted if so
- if( !$rc->mAttribs['rc_log_type'] && $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
- $s .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
- }
# How many users watch this page
if( $rc->numberofWatchingusers > 0 ) {
$s .= ' ' . wfMsgExt( 'number_of_watching_users_RCview',
array( 'parsemag', 'escape' ), $wgLang->formatNum( $rc->numberofWatchingusers ) );
}
-
+
+ if( $this->watchlist ) {
+ $classes[] = Sanitizer::escapeClass( 'watchlist-'.$rc->mAttribs['rc_namespace'].'-'.$rc->mAttribs['rc_title'] );
+ }
+
wfRunHooks( 'OldChangesListRecentChangesLine', array(&$this, &$s, $rc) );
wfProfileOut( __METHOD__ );
- return "$dateheader<li class=\"".implode( ' ', $classes )."\">$s</li>\n";
+ return "$dateheader<li class=\"".implode( ' ', $classes )."\">".$s."</li>\n";
}
}
@@ -449,26 +591,24 @@ class OldChangesList extends ChangesList {
*/
class EnhancedChangesList extends ChangesList {
/**
- * Add the JavaScript file for enhanced changeslist
- * @ return string
- */
+ * Add the JavaScript file for enhanced changeslist
+ * @return String
+ */
public function beginRecentChangesList() {
- global $wgStylePath, $wgJsMimeType, $wgStyleVersion;
+ global $wgStylePath, $wgStyleVersion;
$this->rc_cache = array();
$this->rcMoveIndex = 0;
$this->rcCacheIndex = 0;
$this->lastdate = '';
$this->rclistOpen = false;
- $script = Xml::tags( 'script', array(
- 'type' => $wgJsMimeType,
- 'src' => $wgStylePath . "/common/enhancedchanges.js?$wgStyleVersion" ), '' );
+ $script = Html::linkedScript( $wgStylePath . "/common/enhancedchanges.js?$wgStyleVersion" );
return $script;
}
/**
* Format a line for enhanced recentchange (aka with javascript and block of lines).
*/
public function recentChangesLine( &$baseRC, $watched = false ) {
- global $wgLang, $wgContLang, $wgUser;
+ global $wgLang, $wgUser;
wfProfileIn( __METHOD__ );
@@ -479,7 +619,7 @@ class EnhancedChangesList extends ChangesList {
// FIXME: Would be good to replace this extract() call with something
// that explicitly initializes variables.
extract( $rc->mAttribs );
- $curIdEq = 'curid=' . $rc_cur_id;
+ $curIdEq = array( 'curid' => $rc_cur_id );
# If it's a new day, add the headline and flush the cache
$date = $wgLang->date( $rc_timestamp, true );
@@ -488,7 +628,7 @@ class EnhancedChangesList extends ChangesList {
# Process current cache
$ret = $this->recentChangesBlock();
$this->rc_cache = array();
- $ret .= "<h4>{$date}</h4>\n";
+ $ret .= Xml::element( 'h4', null, $date );
$this->lastdate = $date;
}
@@ -504,19 +644,21 @@ class EnhancedChangesList extends ChangesList {
// Page moves
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$msg = ( $rc_type == RC_MOVE ) ? "1movedto2" : "1movedto2_redir";
- $clink = wfMsg( $msg, $this->skin->makeKnownLinkObj( $rc->getTitle(), '', 'redirect=no' ),
- $this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), '' ) );
+ $clink = wfMsg( $msg, $this->skin->linkKnown( $rc->getTitle(), null,
+ array(), array( 'redirect' => 'no' ) ),
+ $this->skin->linkKnown( $rc->getMovedToTitle() ) );
// New unpatrolled pages
} else if( $rc->unpatrolled && $rc_type == RC_NEW ) {
- $clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', "rcid={$rc_id}" );
+ $clink = $this->skin->linkKnown( $rc->getTitle(), null, array(),
+ array( 'rcid' => $rc_id ) );
// Log entries
} else if( $rc_type == RC_LOG ) {
if( $rc_log_type ) {
$logtitle = SpecialPage::getTitleFor( 'Log', $rc_log_type );
- $clink = '(' . $this->skin->makeKnownLinkObj( $logtitle,
+ $clink = '(' . $this->skin->linkKnown( $logtitle,
LogPage::logName($rc_log_type) ) . ')';
} else {
- $clink = $this->skin->makeLinkObj( $rc->getTitle(), '' );
+ $clink = $this->skin->link( $rc->getTitle() );
}
$watched = false;
// Log entries (old format) and special pages
@@ -525,14 +667,14 @@ class EnhancedChangesList extends ChangesList {
if ( $specialName == 'Log' ) {
# Log updates, etc
$logname = LogPage::logName( $logtype );
- $clink = '(' . $this->skin->makeKnownLinkObj( $rc->getTitle(), $logname ) . ')';
+ $clink = '(' . $this->skin->linkKnown( $rc->getTitle(), $logname ) . ')';
} else {
wfDebug( "Unexpected special page in recentchanges\n" );
$clink = '';
}
// Edits
} else {
- $clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '' );
+ $clink = $this->skin->linkKnown( $rc->getTitle() );
}
# Don't show unusable diff links
@@ -540,44 +682,49 @@ class EnhancedChangesList extends ChangesList {
$showdifflinks = false;
}
- $time = $wgContLang->time( $rc_timestamp, true, true );
+ $time = $wgLang->time( $rc_timestamp, true, true );
$rc->watched = $watched;
$rc->link = $clink;
$rc->timestamp = $time;
$rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
- # Make "cur" and "diff" links
+ # Make "cur" and "diff" links. Do not use link(), it is too slow if
+ # called too many times (50% of CPU time on RecentChanges!).
if( $rc->unpatrolled ) {
- $rcIdQuery = "&rcid={$rc_id}";
+ $rcIdQuery = array( 'rcid' => $rc_id );
} else {
- $rcIdQuery = '';
+ $rcIdQuery = array();
}
- $querycur = $curIdEq."&diff=0&oldid=$rc_this_oldid";
- $querydiff = $curIdEq."&diff=$rc_this_oldid&oldid=$rc_last_oldid$rcIdQuery";
- $aprops = ' tabindex="'.$baseRC->counter.'"';
- $curLink = $this->skin->makeKnownLinkObj( $rc->getTitle(),
- $this->message['cur'], $querycur, '' ,'', $aprops );
+ $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $rc_this_oldid );
+ $querydiff = $curIdEq + array( 'diff' => $rc_this_oldid, 'oldid' =>
+ $rc_last_oldid ) + $rcIdQuery;
- # Make "diff" an "cur" links
if( !$showdifflinks ) {
- $curLink = $this->message['cur'];
- $diffLink = $this->message['diff'];
+ $curLink = $this->message['cur'];
+ $diffLink = $this->message['diff'];
} else if( in_array( $rc_type, array(RC_NEW,RC_LOG,RC_MOVE,RC_MOVE_OVER_REDIRECT) ) ) {
- $curLink = ($rc_type != RC_NEW) ? $this->message['cur'] : $curLink;
+ if ( $rc_type != RC_NEW ) {
+ $curLink = $this->message['cur'];
+ } else {
+ $curUrl = htmlspecialchars( $rc->getTitle()->getLinkUrl( $querycur ) );
+ $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
+ }
$diffLink = $this->message['diff'];
} else {
- $diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['diff'],
- $querydiff, '' ,'', $aprops );
+ $diffUrl = htmlspecialchars( $rc->getTitle()->getLinkUrl( $querydiff ) );
+ $curUrl = htmlspecialchars( $rc->getTitle()->getLinkUrl( $querycur ) );
+ $diffLink = "<a href=\"$diffUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['diff']}</a>";
+ $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
}
# Make "last" link
if( !$showdifflinks || !$rc_last_oldid ) {
- $lastLink = $this->message['last'];
+ $lastLink = $this->message['last'];
} else if( $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$lastLink = $this->message['last'];
} else {
- $lastLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['last'],
- $curIdEq.'&diff='.$rc_this_oldid.'&oldid='.$rc_last_oldid . $rcIdQuery );
+ $lastLink = $this->skin->linkKnown( $rc->getTitle(), $this->message['last'],
+ array(), $curIdEq + array('diff' => $rc_this_oldid, 'oldid' => $rc_last_oldid) + $rcIdQuery );
}
# Make user links
@@ -624,7 +771,7 @@ class EnhancedChangesList extends ChangesList {
wfProfileIn( __METHOD__ );
- $r = '<table cellpadding="0" cellspacing="0" border="0" style="background: none"><tr>';
+ $r = '<table class="mw-enhanced-rc"><tr>';
# Collate list of users
$userlinks = array();
@@ -694,13 +841,13 @@ class EnhancedChangesList extends ChangesList {
$tl = "<span id='mw-rc-openarrow-$jsid' class='mw-changeslist-expanded' style='visibility:hidden'><a href='#' $toggleLink title='$expandTitle'>" . $this->sideArrow() . "</a></span>";
$tl .= "<span id='mw-rc-closearrow-$jsid' class='mw-changeslist-hidden' style='display:none'><a href='#' $toggleLink title='$closeTitle'>" . $this->downArrow() . "</a></span>";
- $r .= '<td valign="top" style="white-space: nowrap"><tt>'.$tl.'&nbsp;';
+ $r .= '<td class="mw-enhanced-rc">'.$tl.'&nbsp;';
# Main line
$r .= $this->recentChangesFlags( $isnew, false, $unpatrolled, '&nbsp;', $bot );
# Timestamp
- $r .= '&nbsp;'.$block[0]->timestamp.'&nbsp;</tt></td><td>';
+ $r .= '&nbsp;'.$block[0]->timestamp.'&nbsp;</td><td style="padding:0px;">';
# Article link
if( $namehidden ) {
@@ -713,7 +860,7 @@ class EnhancedChangesList extends ChangesList {
$r .= $wgContLang->getDirMark();
- $curIdEq = 'curid=' . $curId;
+ $queryParams['curid'] = $curId;
# Changes message
$n = count($block);
static $nchanges = array();
@@ -729,8 +876,17 @@ class EnhancedChangesList extends ChangesList {
} else if( $isnew ) {
$r .= $nchanges[$n];
} else {
- $r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
- $nchanges[$n], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
+ $params = $queryParams;
+ $params['diff'] = $currentRevision;
+ $params['oldid'] = $oldid;
+
+ $r .= $this->skin->link(
+ $block[0]->getTitle(),
+ $nchanges[$n],
+ array(),
+ $params,
+ array( 'known', 'noclasses' )
+ );
}
}
@@ -738,10 +894,19 @@ class EnhancedChangesList extends ChangesList {
if( $allLogs ) {
// don't show history link for logs
} else if( $namehidden || !$block[0]->getTitle()->exists() ) {
- $r .= $this->message['semicolon-separator'] . $this->message['hist'] . ')';
+ $r .= $this->message['pipe-separator'] . $this->message['hist'] . ')';
} else {
- $r .= $this->message['semicolon-separator'] . $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
- $this->message['hist'], $curIdEq . '&action=history' ) . ')';
+ $params = $queryParams;
+ $params['action'] = 'history';
+
+ $r .= $this->message['pipe-separator'] .
+ $this->skin->link(
+ $block[0]->getTitle(),
+ $this->message['hist'],
+ array(),
+ $params,
+ array( 'known', 'noclasses' )
+ ) . ')';
}
$r .= ' . . ';
@@ -750,10 +915,10 @@ class EnhancedChangesList extends ChangesList {
$last = 0;
$first = count($block) - 1;
# Some events (like logs) have an "empty" size, so we need to skip those...
- while( $last < $first && $block[$last]->mAttribs['rc_new_len'] === NULL ) {
+ while( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
$last++;
}
- while( $first > $last && $block[$first]->mAttribs['rc_old_len'] === NULL ) {
+ while( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) {
$first--;
}
# Get net change
@@ -774,7 +939,7 @@ class EnhancedChangesList extends ChangesList {
# Sub-entries
$r .= '<div id="mw-rc-subentries-'.$jsid.'" class="mw-changeslist-hidden">';
- $r .= '<table cellpadding="0" cellspacing="0" border="0" style="background: none">';
+ $r .= '<table class="mw-enhanced-rc">';
foreach( $block as $rcObj ) {
# Extract fields from DB into the function scope (rc_xxxx variables)
// FIXME: Would be good to replace this extract() call with something
@@ -784,35 +949,44 @@ class EnhancedChangesList extends ChangesList {
extract( $rcObj->mAttribs );
#$r .= '<tr><td valign="top">'.$this->spacerArrow();
- $r .= '<tr><td valign="top">';
- $r .= '<tt>'.$this->spacerIndent() . $this->spacerIndent();
+ $r .= '<tr><td style="vertical-align:top;font-family:monospace; padding:0px;">';
+ $r .= $this->spacerIndent() . $this->spacerIndent();
$r .= $this->recentChangesFlags( $rc_new, $rc_minor, $rcObj->unpatrolled, '&nbsp;', $rc_bot );
- $r .= '&nbsp;</tt></td><td valign="top">';
+ $r .= '&nbsp;</td><td style="vertical-align:top; padding:0px;"><span style="font-family:monospace">';
+
+ $params = $queryParams;
- $o = '';
if( $rc_this_oldid != 0 ) {
- $o = 'oldid='.$rc_this_oldid;
+ $params['oldid'] = $rc_this_oldid;
}
+
# Log timestamp
if( $rc_type == RC_LOG ) {
- $link = '<tt>'.$rcObj->timestamp.'</tt> ';
+ $link = $rcObj->timestamp;
# Revision link
} else if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
- $link = '<span class="history-deleted"><tt>'.$rcObj->timestamp.'</tt></span> ';
+ $link = '<span class="history-deleted">'.$rcObj->timestamp.'</span> ';
} else {
- $rcIdEq = ($rcObj->unpatrolled && $rc_type == RC_NEW) ?
- '&rcid='.$rcObj->mAttribs['rc_id'] : '';
- $link = '<tt>'.$this->skin->makeKnownLinkObj( $rcObj->getTitle(),
- $rcObj->timestamp, $curIdEq.'&'.$o.$rcIdEq ).'</tt>';
+ if ( $rcObj->unpatrolled && $rc_type == RC_NEW) {
+ $params['rcid'] = $rcObj->mAttribs['rc_id'];
+ }
+
+ $link = $this->skin->link(
+ $rcObj->getTitle(),
+ $rcObj->timestamp,
+ array(),
+ $params,
+ array( 'known', 'noclasses' )
+ );
if( $this->isDeleted($rcObj,Revision::DELETED_TEXT) )
$link = '<span class="history-deleted">'.$link.'</span> ';
}
- $r .= $link;
+ $r .= $link . '</span>';
if ( !$rc_type == RC_LOG || $rc_type == RC_NEW ) {
$r .= ' (';
$r .= $rcObj->curlink;
- $r .= $this->message['semicolon-separator'];
+ $r .= $this->message['pipe-separator'];
$r .= $rcObj->lastlink;
$r .= ')';
}
@@ -833,11 +1007,6 @@ class EnhancedChangesList extends ChangesList {
$this->insertRollback( $r, $rcObj );
# Tags
$this->insertTags( $r, $rcObj, $classes );
-
- # Mark revision as deleted
- if( !$rc_log_type && $this->isDeleted($rcObj,Revision::DELETED_TEXT) ) {
- $r .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
- }
$r .= "</td></tr>\n";
}
@@ -852,10 +1021,10 @@ class EnhancedChangesList extends ChangesList {
/**
* Generate HTML for an arrow or placeholder graphic
- * @param string $dir one of '', 'd', 'l', 'r'
- * @param string $alt text
- * @param string $title text
- * @return string HTML <img> tag
+ * @param $dir String: one of '', 'd', 'l', 'r'
+ * @param $alt String: text
+ * @param $title String: text
+ * @return String: HTML <img> tag
*/
protected function arrow( $dir, $alt='', $title='' ) {
global $wgStylePath;
@@ -868,7 +1037,7 @@ class EnhancedChangesList extends ChangesList {
/**
* Generate HTML for a right- or left-facing arrow,
* depending on language direction.
- * @return string HTML <img> tag
+ * @return String: HTML <img> tag
*/
protected function sideArrow() {
global $wgContLang;
@@ -879,7 +1048,7 @@ class EnhancedChangesList extends ChangesList {
/**
* Generate HTML for a down-facing arrow
* depending on language direction.
- * @return string HTML <img> tag
+ * @return String: HTML <img> tag
*/
protected function downArrow() {
return $this->arrow( 'd', '-', wfMsg( 'rc-enhanced-hide' ) );
@@ -887,7 +1056,7 @@ class EnhancedChangesList extends ChangesList {
/**
* Generate HTML for a spacer image
- * @return string HTML <img> tag
+ * @return String: HTML <img> tag
*/
protected function spacerArrow() {
return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
@@ -895,7 +1064,7 @@ class EnhancedChangesList extends ChangesList {
/**
* Add a set of spaces
- * @return string HTML <td> tag
+ * @return String: HTML <td> tag
*/
protected function spacerIndent() {
return '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
@@ -903,10 +1072,10 @@ class EnhancedChangesList extends ChangesList {
/**
* Enhanced RC ungrouped line.
- * @return string a HTML formated line (generated using $r)
+ * @return String: a HTML formated line (generated using $r)
*/
protected function recentChangesBlockLine( $rcObj ) {
- global $wgContLang, $wgRCShowChangedSize;
+ global $wgRCShowChangedSize;
wfProfileIn( __METHOD__ );
@@ -915,30 +1084,42 @@ class EnhancedChangesList extends ChangesList {
// that explicitly initializes variables.
$classes = array(); // TODO implement
extract( $rcObj->mAttribs );
- $curIdEq = "curid={$rc_cur_id}";
+ $query['curid'] = $rc_cur_id;
- $r = '<table cellspacing="0" cellpadding="0" border="0" style="background: none"><tr>';
- $r .= '<td valign="top" style="white-space: nowrap"><tt>' . $this->spacerArrow() . '&nbsp;';
+ $r = '<table class="mw-enhanced-rc"><tr>';
+ $r .= '<td class="mw-enhanced-rc">' . $this->spacerArrow() . '&nbsp;';
# Flag and Timestamp
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$r .= '&nbsp;&nbsp;&nbsp;&nbsp;'; // 4 flags -> 4 spaces
} else {
$r .= $this->recentChangesFlags( $rc_type == RC_NEW, $rc_minor, $rcObj->unpatrolled, '&nbsp;', $rc_bot );
}
- $r .= '&nbsp;'.$rcObj->timestamp.'&nbsp;</tt></td><td>';
+ $r .= '&nbsp;'.$rcObj->timestamp.'&nbsp;</td><td style="padding:0px;">';
# Article or log link
if( $rc_log_type ) {
$logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
$logname = LogPage::logName( $rc_log_type );
- $r .= '(' . $this->skin->makeKnownLinkObj($logtitle, $logname ) . ')';
+ $r .= '(' . $this->skin->link(
+ $logtitle,
+ $logname,
+ array(),
+ array(),
+ array( 'known', 'noclasses' )
+ ) . ')';
} else {
$this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
}
# Diff and hist links
if ( $rc_type != RC_LOG ) {
- $r .= ' ('. $rcObj->difflink . $this->message['semicolon-separator'];
- $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), $this->message['hist'],
- $curIdEq.'&action=history' ) . ')';
+ $r .= ' ('. $rcObj->difflink . $this->message['pipe-separator'];
+ $query['action'] = 'history';
+ $r .= $this->skin->link(
+ $rcObj->getTitle(),
+ $this->message['hist'],
+ array(),
+ $query,
+ array( 'known', 'noclasses' )
+ ) . ')';
}
$r .= ' . . ';
# Character diff
diff --git a/includes/ConfEditor.php b/includes/ConfEditor.php
new file mode 100644
index 00000000..f862ebb7
--- /dev/null
+++ b/includes/ConfEditor.php
@@ -0,0 +1,1058 @@
+<?php
+
+/**
+ * This is a state machine style parser with two internal stacks:
+ * * A next state stack, which determines the state the machine will progress to next
+ * * A path stack, which keeps track of the logical location in the file.
+ *
+ * Reference grammar:
+ *
+ * file = T_OPEN_TAG *statement
+ * statement = T_VARIABLE "=" expression ";"
+ * expression = array / scalar / T_VARIABLE
+ * array = T_ARRAY "(" [ element *( "," element ) [ "," ] ] ")"
+ * element = assoc-element / expression
+ * assoc-element = scalar T_DOUBLE_ARROW expression
+ * scalar = T_LNUMBER / T_DNUMBER / T_STRING / T_CONSTANT_ENCAPSED_STRING
+ */
+class ConfEditor {
+ /** The text to parse */
+ var $text;
+
+ /** The token array from token_get_all() */
+ var $tokens;
+
+ /** The current position in the token array */
+ var $pos;
+
+ /** The current 1-based line number */
+ var $lineNum;
+
+ /** The current 1-based column number */
+ var $colNum;
+
+ /** The current 0-based byte number */
+ var $byteNum;
+
+ /** The current ConfEditorToken object */
+ var $currentToken;
+
+ /** The previous ConfEditorToken object */
+ var $prevToken;
+
+ /**
+ * The state machine stack. This is an array of strings where the topmost
+ * element will be popped off and become the next parser state.
+ */
+ var $stateStack;
+
+
+ /**
+ * The path stack is a stack of associative arrays with the following elements:
+ * name The name of top level of the path
+ * level The level (number of elements) of the path
+ * startByte The byte offset of the start of the path
+ * startToken The token offset of the start
+ * endByte The byte offset of thee
+ * endToken The token offset of the end, plus one
+ * valueStartToken The start token offset of the value part
+ * valueStartByte The start byte offset of the value part
+ * valueEndToken The end token offset of the value part, plus one
+ * valueEndByte The end byte offset of the value part, plus one
+ * nextArrayIndex The next numeric array index at this level
+ * hasComma True if the array element ends with a comma
+ * arrowByte The byte offset of the "=>", or false if there isn't one
+ */
+ var $pathStack;
+
+ /**
+ * The elements of the top of the pathStack for every path encountered, indexed
+ * by slash-separated path.
+ */
+ var $pathInfo;
+
+ /**
+ * Next serial number for whitespace placeholder paths (@extra-N)
+ */
+ var $serial;
+
+ /**
+ * Editor state. This consists of the internal copy/insert operations which
+ * are applied to the source string to obtain the destination string.
+ */
+ var $edits;
+
+ /**
+ * Simple entry point for command-line testing
+ */
+ static function test( $text ) {
+ try {
+ $ce = new self( $text );
+ $ce->parse();
+ } catch ( ConfEditorParseError $e ) {
+ return $e->getMessage() . "\n" . $e->highlight( $text );
+ }
+ return "OK";
+ }
+
+ /**
+ * Construct a new parser
+ */
+ public function __construct( $text ) {
+ $this->text = $text;
+ }
+
+ /**
+ * Edit the text. Returns the edited text.
+ * @param array $ops Array of operations.
+ *
+ * Operations are given as an associative array, with members:
+ * type: One of delete, set, append or insert (required)
+ * path: The path to operate on (required)
+ * key: The array key to insert/append, with PHP quotes
+ * value: The value, with PHP quotes
+ *
+ * delete
+ * Deletes an array element or statement with the specified path.
+ * e.g.
+ * array('type' => 'delete', 'path' => '$foo/bar/baz' )
+ * is equivalent to the runtime PHP code:
+ * unset( $foo['bar']['baz'] );
+ *
+ * set
+ * Sets the value of an array element. If the element doesn't exist, it
+ * is appended to the array. If it does exist, the value is set, with
+ * comments and indenting preserved.
+ *
+ * append
+ * Appends a new element to the end of the array. Adds a trailing comma.
+ * e.g.
+ * array( 'type' => 'append', 'path', '$foo/bar',
+ * 'key' => 'baz', 'value' => "'x'" )
+ * is like the PHP code:
+ * $foo['bar']['baz'] = 'x';
+ *
+ * insert
+ * Insert a new element at the start of the array.
+ *
+ */
+ public function edit( $ops ) {
+ $this->parse();
+
+ $this->edits = array(
+ array( 'copy', 0, strlen( $this->text ) )
+ );
+ foreach ( $ops as $op ) {
+ $type = $op['type'];
+ $path = $op['path'];
+ $value = isset( $op['value'] ) ? $op['value'] : null;
+ $key = isset( $op['key'] ) ? $op['key'] : null;
+
+ switch ( $type ) {
+ case 'delete':
+ list( $start, $end ) = $this->findDeletionRegion( $path );
+ $this->replaceSourceRegion( $start, $end, false );
+ break;
+ case 'set':
+ if ( isset( $this->pathInfo[$path] ) ) {
+ list( $start, $end ) = $this->findValueRegion( $path );
+ $encValue = $value; // var_export( $value, true );
+ $this->replaceSourceRegion( $start, $end, $encValue );
+ break;
+ }
+ // No existing path, fall through to append
+ $slashPos = strrpos( $path, '/' );
+ $key = var_export( substr( $path, $slashPos + 1 ), true );
+ $path = substr( $path, 0, $slashPos );
+ // Fall through
+ case 'append':
+ // Find the last array element
+ $lastEltPath = $this->findLastArrayElement( $path );
+ if ( $lastEltPath === false ) {
+ throw new MWException( "Can't find any element of array \"$path\"" );
+ }
+ $lastEltInfo = $this->pathInfo[$lastEltPath];
+
+ // Has it got a comma already?
+ if ( strpos( $lastEltPath, '@extra' ) === false && !$lastEltInfo['hasComma'] ) {
+ // No comma, insert one after the value region
+ list( $start, $end ) = $this->findValueRegion( $lastEltPath );
+ $this->replaceSourceRegion( $end - 1, $end - 1, ',' );
+ }
+
+ // Make the text to insert
+ list( $start, $end ) = $this->findDeletionRegion( $lastEltPath );
+
+ if ( $key === null ) {
+ list( $indent, $arrowIndent ) = $this->getIndent( $start );
+ $textToInsert = "$indent$value,";
+ } else {
+ list( $indent, $arrowIndent ) =
+ $this->getIndent( $start, $key, $lastEltInfo['arrowByte'] );
+ $textToInsert = "$indent$key$arrowIndent=> $value,";
+ }
+ $textToInsert .= ( $indent === false ? ' ' : "\n" );
+
+ // Insert the item
+ $this->replaceSourceRegion( $end, $end, $textToInsert );
+ break;
+ case 'insert':
+ // Find first array element
+ $firstEltPath = $this->findFirstArrayElement( $path );
+ if ( $firstEltPath === false ) {
+ throw new MWException( "Can't find array element of \"$path\"" );
+ }
+ list( $start, $end ) = $this->findDeletionRegion( $firstEltPath );
+ $info = $this->pathInfo[$firstEltPath];
+
+ // Make the text to insert
+ if ( $key === null ) {
+ list( $indent, $arrowIndent ) = $this->getIndent( $start );
+ $textToInsert = "$indent$value,";
+ } else {
+ list( $indent, $arrowIndent ) =
+ $this->getIndent( $start, $key, $info['arrowByte'] );
+ $textToInsert = "$indent$key$arrowIndent=> $value,";
+ }
+ $textToInsert .= ( $indent === false ? ' ' : "\n" );
+
+ // Insert the item
+ $this->replaceSourceRegion( $start, $start, $textToInsert );
+ break;
+ default:
+ throw new MWException( "Unrecognised operation: \"$type\"" );
+ }
+ }
+
+ // Do the edits
+ $out = '';
+ foreach ( $this->edits as $edit ) {
+ if ( $edit[0] == 'copy' ) {
+ $out .= substr( $this->text, $edit[1], $edit[2] - $edit[1] );
+ } else { // if ( $edit[0] == 'insert' )
+ $out .= $edit[1];
+ }
+ }
+
+ // Do a second parse as a sanity check
+ $this->text = $out;
+ try {
+ $this->parse();
+ } catch ( ConfEditorParseError $e ) {
+ throw new MWException(
+ "Sorry, ConfEditor broke the file during editing and it won't parse anymore: " .
+ $e->getMessage() );
+ }
+ return $out;
+ }
+
+ /**
+ * Get the variables defined in the text
+ * @return array( varname => value )
+ */
+ function getVars() {
+ $vars = array();
+ $this->parse();
+ foreach( $this->pathInfo as $path => $data ) {
+ if ( $path[0] != '$' )
+ continue;
+ $trimmedPath = substr( $path, 1 );
+ $name = $data['name'];
+ if ( $name[0] == '@' )
+ continue;
+ if ( $name[0] == '$' )
+ $name = substr( $name, 1 );
+ $parentPath = substr( $trimmedPath, 0,
+ strlen( $trimmedPath ) - strlen( $name ) );
+ if( substr( $parentPath, -1 ) == '/' )
+ $parentPath = substr( $parentPath, 0, -1 );
+
+ $value = substr( $this->text, $data['valueStartByte'],
+ $data['valueEndByte'] - $data['valueStartByte']
+ );
+ $this->setVar( $vars, $parentPath, $name,
+ $this->parseScalar( $value ) );
+ }
+ return $vars;
+ }
+
+ /**
+ * Set a value in an array, unless it's set already. For instance,
+ * setVar( $arr, 'foo/bar', 'baz', 3 ); will set
+ * $arr['foo']['bar']['baz'] = 3;
+ * @param $array array
+ * @param $path string slash-delimited path
+ * @param $key mixed Key
+ * @param $value mixed Value
+ */
+ function setVar( &$array, $path, $key, $value ) {
+ $pathArr = explode( '/', $path );
+ $target =& $array;
+ if ( $path !== '' ) {
+ foreach ( $pathArr as $p ) {
+ if( !isset( $target[$p] ) )
+ $target[$p] = array();
+ $target =& $target[$p];
+ }
+ }
+ if ( !isset( $target[$key] ) )
+ $target[$key] = $value;
+ }
+
+ /**
+ * Parse a scalar value in PHP
+ * @return mixed Parsed value
+ */
+ function parseScalar( $str ) {
+ if ( $str !== '' && $str[0] == '\'' )
+ // Single-quoted string
+ // @todo Fixme: trim() call is due to mystery bug where whitespace gets
+ // appended to the token; without it we ended up reading in the
+ // extra quote on the end!
+ return strtr( substr( trim( $str ), 1, -1 ),
+ array( '\\\'' => '\'', '\\\\' => '\\' ) );
+ if ( $str !== '' && @$str[0] == '"' )
+ // Double-quoted string
+ // @todo Fixme: trim() call is due to mystery bug where whitespace gets
+ // appended to the token; without it we ended up reading in the
+ // extra quote on the end!
+ return stripcslashes( substr( trim( $str ), 1, -1 ) );
+ if ( substr( $str, 0, 4 ) == 'true' )
+ return true;
+ if ( substr( $str, 0, 5 ) == 'false' )
+ return false;
+ if ( substr( $str, 0, 4 ) == 'null' )
+ return null;
+ // Must be some kind of numeric value, so let PHP's weak typing
+ // be useful for a change
+ return $str;
+ }
+
+ /**
+ * Replace the byte offset region of the source with $newText.
+ * Works by adding elements to the $this->edits array.
+ */
+ function replaceSourceRegion( $start, $end, $newText = false ) {
+ // Split all copy operations with a source corresponding to the region
+ // in question.
+ $newEdits = array();
+ foreach ( $this->edits as $i => $edit ) {
+ if ( $edit[0] !== 'copy' ) {
+ $newEdits[] = $edit;
+ continue;
+ }
+ $copyStart = $edit[1];
+ $copyEnd = $edit[2];
+ if ( $start >= $copyEnd || $end <= $copyStart ) {
+ // Outside this region
+ $newEdits[] = $edit;
+ continue;
+ }
+ if ( ( $start < $copyStart && $end > $copyStart )
+ || ( $start < $copyEnd && $end > $copyEnd )
+ ) {
+ throw new MWException( "Overlapping regions found, can't do the edit" );
+ }
+ // Split the copy
+ $newEdits[] = array( 'copy', $copyStart, $start );
+ if ( $newText !== false ) {
+ $newEdits[] = array( 'insert', $newText );
+ }
+ $newEdits[] = array( 'copy', $end, $copyEnd );
+ }
+ $this->edits = $newEdits;
+ }
+
+ /**
+ * Finds the source byte region which you would want to delete, if $pathName
+ * was to be deleted. Includes the leading spaces and tabs, the trailing line
+ * break, and any comments in between.
+ */
+ function findDeletionRegion( $pathName ) {
+ if ( !isset( $this->pathInfo[$pathName] ) ) {
+ throw new MWException( "Can't find path \"$pathName\"" );
+ }
+ $path = $this->pathInfo[$pathName];
+ // Find the start
+ $this->firstToken();
+ while ( $this->pos != $path['startToken'] ) {
+ $this->nextToken();
+ }
+ $regionStart = $path['startByte'];
+ for ( $offset = -1; $offset >= -$this->pos; $offset-- ) {
+ $token = $this->getTokenAhead( $offset );
+ if ( !$token->isSkip() ) {
+ // If there is other content on the same line, don't move the start point
+ // back, because that will cause the regions to overlap.
+ $regionStart = $path['startByte'];
+ break;
+ }
+ $lfPos = strrpos( $token->text, "\n" );
+ if ( $lfPos === false ) {
+ $regionStart -= strlen( $token->text );
+ } else {
+ // The line start does not include the LF
+ $regionStart -= strlen( $token->text ) - $lfPos - 1;
+ break;
+ }
+ }
+ // Find the end
+ while ( $this->pos != $path['endToken'] ) {
+ $this->nextToken();
+ }
+ $regionEnd = $path['endByte']; // past the end
+ for ( $offset = 0; $offset < count( $this->tokens ) - $this->pos; $offset++ ) {
+ $token = $this->getTokenAhead( $offset );
+ if ( !$token->isSkip() ) {
+ break;
+ }
+ $lfPos = strpos( $token->text, "\n" );
+ if ( $lfPos === false ) {
+ $regionEnd += strlen( $token->text );
+ } else {
+ // This should point past the LF
+ $regionEnd += $lfPos + 1;
+ break;
+ }
+ }
+ return array( $regionStart, $regionEnd );
+ }
+
+ /**
+ * Find the byte region in the source corresponding to the value part.
+ * This includes the quotes, but does not include the trailing comma
+ * or semicolon.
+ *
+ * The end position is the past-the-end (end + 1) value as per convention.
+ */
+ function findValueRegion( $pathName ) {
+ if ( !isset( $this->pathInfo[$pathName] ) ) {
+ throw new MWEXception( "Can't find path \"$pathName\"" );
+ }
+ $path = $this->pathInfo[$pathName];
+ if ( $path['valueStartByte'] === false || $path['valueEndByte'] === false ) {
+ throw new MWException( "Can't find value region for path \"$pathName\"" );
+ }
+ return array( $path['valueStartByte'], $path['valueEndByte'] );
+ }
+
+ /**
+ * Find the path name of the last element in the array.
+ * If the array is empty, this will return the @extra interstitial element.
+ * If the specified path is not found or is not an array, it will return false.
+ */
+ function findLastArrayElement( $path ) {
+ // Try for a real element
+ $lastEltPath = false;
+ foreach ( $this->pathInfo as $candidatePath => $info ) {
+ $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+ $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
+ if ( $part2 == '@' ) {
+ // Do nothing
+ } elseif ( $part1 == "$path/" ) {
+ $lastEltPath = $candidatePath;
+ } elseif ( $lastEltPath !== false ) {
+ break;
+ }
+ }
+ if ( $lastEltPath !== false ) {
+ return $lastEltPath;
+ }
+
+ // Try for an interstitial element
+ $extraPath = false;
+ foreach ( $this->pathInfo as $candidatePath => $info ) {
+ $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+ if ( $part1 == "$path/" ) {
+ $extraPath = $candidatePath;
+ } elseif ( $extraPath !== false ) {
+ break;
+ }
+ }
+ return $extraPath;
+ }
+
+ /*
+ * Find the path name of first element in the array.
+ * If the array is empty, this will return the @extra interstitial element.
+ * If the specified path is not found or is not an array, it will return false.
+ */
+ function findFirstArrayElement( $path ) {
+ // Try for an ordinary element
+ foreach ( $this->pathInfo as $candidatePath => $info ) {
+ $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+ $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
+ if ( $part1 == "$path/" && $part2 != '@' ) {
+ return $candidatePath;
+ }
+ }
+
+ // Try for an interstitial element
+ foreach ( $this->pathInfo as $candidatePath => $info ) {
+ $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+ if ( $part1 == "$path/" ) {
+ return $candidatePath;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the indent string which sits after a given start position.
+ * Returns false if the position is not at the start of the line.
+ */
+ function getIndent( $pos, $key = false, $arrowPos = false ) {
+ $arrowIndent = ' ';
+ if ( $pos == 0 || $this->text[$pos-1] == "\n" ) {
+ $indentLength = strspn( $this->text, " \t", $pos );
+ $indent = substr( $this->text, $pos, $indentLength );
+ } else {
+ $indent = false;
+ }
+ if ( $indent !== false && $arrowPos !== false ) {
+ $textToInsert = "$indent$key ";
+ $arrowIndentLength = $arrowPos - $pos - $indentLength - strlen( $key );
+ if ( $arrowIndentLength > 0 ) {
+ $arrowIndent = str_repeat( ' ', $arrowIndentLength );
+ }
+ }
+ return array( $indent, $arrowIndent );
+ }
+
+ /**
+ * Run the parser on the text. Throws an exception if the string does not
+ * match our defined subset of PHP syntax.
+ */
+ public function parse() {
+ $this->initParse();
+ $this->pushState( 'file' );
+ $this->pushPath( '@extra-' . ($this->serial++) );
+ $token = $this->firstToken();
+
+ while ( !$token->isEnd() ) {
+ $state = $this->popState();
+ if ( !$state ) {
+ $this->error( 'internal error: empty state stack' );
+ }
+
+ switch ( $state ) {
+ case 'file':
+ $token = $this->expect( T_OPEN_TAG );
+ $token = $this->skipSpace();
+ if ( $token->isEnd() ) {
+ break 2;
+ }
+ $this->pushState( 'statement', 'file 2' );
+ break;
+ case 'file 2':
+ $token = $this->skipSpace();
+ if ( $token->isEnd() ) {
+ break 2;
+ }
+ $this->pushState( 'statement', 'file 2' );
+ break;
+ case 'statement':
+ $token = $this->skipSpace();
+ if ( !$this->validatePath( $token->text ) ) {
+ $this->error( "Invalid variable name \"{$token->text}\"" );
+ }
+ $this->nextPath( $token->text );
+ $this->expect( T_VARIABLE );
+ $this->skipSpace();
+ $arrayAssign = false;
+ if ( $this->currentToken()->type == '[' ) {
+ $this->nextToken();
+ $token = $this->skipSpace();
+ if ( !$token->isScalar() ) {
+ $this->error( "expected a string or number for the array key" );
+ }
+ if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
+ $text = $this->parseScalar( $token->text );
+ } else {
+ $text = $token->text;
+ }
+ if ( !$this->validatePath( $text ) ) {
+ $this->error( "Invalid associative array name \"$text\"" );
+ }
+ $this->pushPath( $text );
+ $this->nextToken();
+ $this->skipSpace();
+ $this->expect( ']' );
+ $this->skipSpace();
+ $arrayAssign = true;
+ }
+ $this->expect( '=' );
+ $this->skipSpace();
+ $this->startPathValue();
+ if ( $arrayAssign )
+ $this->pushState( 'expression', 'array assign end' );
+ else
+ $this->pushState( 'expression', 'statement end' );
+ break;
+ case 'array assign end':
+ case 'statement end':
+ $this->endPathValue();
+ if ( $state == 'array assign end' )
+ $this->popPath();
+ $this->skipSpace();
+ $this->expect( ';' );
+ $this->nextPath( '@extra-' . ($this->serial++) );
+ break;
+ case 'expression':
+ $token = $this->skipSpace();
+ if ( $token->type == T_ARRAY ) {
+ $this->pushState( 'array' );
+ } elseif ( $token->isScalar() ) {
+ $this->nextToken();
+ } elseif ( $token->type == T_VARIABLE ) {
+ $this->nextToken();
+ } else {
+ $this->error( "expected simple expression" );
+ }
+ break;
+ case 'array':
+ $this->skipSpace();
+ $this->expect( T_ARRAY );
+ $this->skipSpace();
+ $this->expect( '(' );
+ $this->skipSpace();
+ $this->pushPath( '@extra-' . ($this->serial++) );
+ if ( $this->isAhead( ')' ) ) {
+ // Empty array
+ $this->pushState( 'array end' );
+ } else {
+ $this->pushState( 'element', 'array end' );
+ }
+ break;
+ case 'array end':
+ $this->skipSpace();
+ $this->popPath();
+ $this->expect( ')' );
+ break;
+ case 'element':
+ $token = $this->skipSpace();
+ // Look ahead to find the double arrow
+ if ( $token->isScalar() && $this->isAhead( T_DOUBLE_ARROW, 1 ) ) {
+ // Found associative element
+ $this->pushState( 'assoc-element', 'element end' );
+ } else {
+ // Not associative
+ $this->nextPath( '@next' );
+ $this->startPathValue();
+ $this->pushState( 'expression', 'element end' );
+ }
+ break;
+ case 'element end':
+ $token = $this->skipSpace();
+ if ( $token->type == ',' ) {
+ $this->endPathValue();
+ $this->markComma();
+ $this->nextToken();
+ $this->nextPath( '@extra-' . ($this->serial++) );
+ // Look ahead to find ending bracket
+ if ( $this->isAhead( ")" ) ) {
+ // Found ending bracket, no continuation
+ $this->skipSpace();
+ } else {
+ // No ending bracket, continue to next element
+ $this->pushState( 'element' );
+ }
+ } elseif ( $token->type == ')' ) {
+ // End array
+ $this->endPathValue();
+ } else {
+ $this->error( "expected the next array element or the end of the array" );
+ }
+ break;
+ case 'assoc-element':
+ $token = $this->skipSpace();
+ if ( !$token->isScalar() ) {
+ $this->error( "expected a string or number for the array key" );
+ }
+ if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
+ $text = $this->parseScalar( $token->text );
+ } else {
+ $text = $token->text;
+ }
+ if ( !$this->validatePath( $text ) ) {
+ $this->error( "Invalid associative array name \"$text\"" );
+ }
+ $this->nextPath( $text );
+ $this->nextToken();
+ $this->skipSpace();
+ $this->markArrow();
+ $this->expect( T_DOUBLE_ARROW );
+ $this->skipSpace();
+ $this->startPathValue();
+ $this->pushState( 'expression' );
+ break;
+ }
+ }
+ if ( count( $this->stateStack ) ) {
+ $this->error( 'unexpected end of file' );
+ }
+ $this->popPath();
+ }
+
+ /**
+ * Initialise a parse.
+ */
+ protected function initParse() {
+ $this->tokens = token_get_all( $this->text );
+ $this->stateStack = array();
+ $this->pathStack = array();
+ $this->firstToken();
+ $this->pathInfo = array();
+ $this->serial = 1;
+ }
+
+ /**
+ * Set the parse position. Do not call this except from firstToken() and
+ * nextToken(), there is more to update than just the position.
+ */
+ protected function setPos( $pos ) {
+ $this->pos = $pos;
+ if ( $this->pos >= count( $this->tokens ) ) {
+ $this->currentToken = ConfEditorToken::newEnd();
+ } else {
+ $this->currentToken = $this->newTokenObj( $this->tokens[$this->pos] );
+ }
+ return $this->currentToken;
+ }
+
+ /**
+ * Create a ConfEditorToken from an element of token_get_all()
+ */
+ function newTokenObj( $internalToken ) {
+ if ( is_array( $internalToken ) ) {
+ return new ConfEditorToken( $internalToken[0], $internalToken[1] );
+ } else {
+ return new ConfEditorToken( $internalToken, $internalToken );
+ }
+ }
+
+ /**
+ * Reset the parse position
+ */
+ function firstToken() {
+ $this->setPos( 0 );
+ $this->prevToken = ConfEditorToken::newEnd();
+ $this->lineNum = 1;
+ $this->colNum = 1;
+ $this->byteNum = 0;
+ return $this->currentToken;
+ }
+
+ /**
+ * Get the current token
+ */
+ function currentToken() {
+ return $this->currentToken;
+ }
+
+ /**
+ * Advance the current position and return the resulting next token
+ */
+ function nextToken() {
+ if ( $this->currentToken ) {
+ $text = $this->currentToken->text;
+ $lfCount = substr_count( $text, "\n" );
+ if ( $lfCount ) {
+ $this->lineNum += $lfCount;
+ $this->colNum = strlen( $text ) - strrpos( $text, "\n" );
+ } else {
+ $this->colNum += strlen( $text );
+ }
+ $this->byteNum += strlen( $text );
+ }
+ $this->prevToken = $this->currentToken;
+ $this->setPos( $this->pos + 1 );
+ return $this->currentToken;
+ }
+
+ /**
+ * Get the token $offset steps ahead of the current position.
+ * $offset may be negative, to get tokens behind the current position.
+ */
+ function getTokenAhead( $offset ) {
+ $pos = $this->pos + $offset;
+ if ( $pos >= count( $this->tokens ) || $pos < 0 ) {
+ return ConfEditorToken::newEnd();
+ } else {
+ return $this->newTokenObj( $this->tokens[$pos] );
+ }
+ }
+
+ /**
+ * Advances the current position past any whitespace or comments
+ */
+ function skipSpace() {
+ while ( $this->currentToken && $this->currentToken->isSkip() ) {
+ $this->nextToken();
+ }
+ return $this->currentToken;
+ }
+
+ /**
+ * Throws an error if the current token is not of the given type, and
+ * then advances to the next position.
+ */
+ function expect( $type ) {
+ if ( $this->currentToken && $this->currentToken->type == $type ) {
+ return $this->nextToken();
+ } else {
+ $this->error( "expected " . $this->getTypeName( $type ) .
+ ", got " . $this->getTypeName( $this->currentToken->type ) );
+ }
+ }
+
+ /**
+ * Push a state or two on to the state stack.
+ */
+ function pushState( $nextState, $stateAfterThat = null ) {
+ if ( $stateAfterThat !== null ) {
+ $this->stateStack[] = $stateAfterThat;
+ }
+ $this->stateStack[] = $nextState;
+ }
+
+ /**
+ * Pop a state from the state stack.
+ */
+ function popState() {
+ return array_pop( $this->stateStack );
+ }
+
+ /**
+ * Returns true if the user input path is valid.
+ * This exists to allow "/" and "@" to be reserved for string path keys
+ */
+ function validatePath( $path ) {
+ return strpos( $path, '/' ) === false && substr( $path, 0, 1 ) != '@';
+ }
+
+ /**
+ * Internal function to update some things at the end of a path region. Do
+ * not call except from popPath() or nextPath().
+ */
+ function endPath() {
+ $i = count( $this->pathStack ) - 1;
+ $key = '';
+ foreach ( $this->pathStack as $pathInfo ) {
+ if ( $key !== '' ) {
+ $key .= '/';
+ }
+ $key .= $pathInfo['name'];
+ }
+ $pathInfo['endByte'] = $this->byteNum;
+ $pathInfo['endToken'] = $this->pos;
+ $this->pathInfo[$key] = $pathInfo;
+ }
+
+ /**
+ * Go up to a new path level, for example at the start of an array.
+ */
+ function pushPath( $path ) {
+ $this->pathStack[] = array(
+ 'name' => $path,
+ 'level' => count( $this->pathStack ) + 1,
+ 'startByte' => $this->byteNum,
+ 'startToken' => $this->pos,
+ 'valueStartToken' => false,
+ 'valueStartByte' => false,
+ 'valueEndToken' => false,
+ 'valueEndByte' => false,
+ 'nextArrayIndex' => 0,
+ 'hasComma' => false,
+ 'arrowByte' => false
+ );
+ }
+
+ /**
+ * Go down a path level, for example at the end of an array.
+ */
+ function popPath() {
+ $this->endPath();
+ array_pop( $this->pathStack );
+ }
+
+ /**
+ * Go to the next path on the same level. This ends the current path and
+ * starts a new one. If $path is @next, the new path is set to the next
+ * numeric array element.
+ */
+ function nextPath( $path ) {
+ $this->endPath();
+ $i = count( $this->pathStack ) - 1;
+ if ( $path == '@next' ) {
+ $nextArrayIndex =& $this->pathStack[$i]['nextArrayIndex'];
+ $this->pathStack[$i]['name'] = $nextArrayIndex;
+ $nextArrayIndex++;
+ } else {
+ $this->pathStack[$i]['name'] = $path;
+ }
+ $this->pathStack[$i] =
+ array(
+ 'startByte' => $this->byteNum,
+ 'startToken' => $this->pos,
+ 'valueStartToken' => false,
+ 'valueStartByte' => false,
+ 'valueEndToken' => false,
+ 'valueEndByte' => false,
+ 'hasComma' => false,
+ 'arrowByte' => false,
+ ) + $this->pathStack[$i];
+ }
+
+ /**
+ * Mark the start of the value part of a path.
+ */
+ function startPathValue() {
+ $path =& $this->pathStack[count( $this->pathStack ) - 1];
+ $path['valueStartToken'] = $this->pos;
+ $path['valueStartByte'] = $this->byteNum;
+ }
+
+ /**
+ * Mark the end of the value part of a path.
+ */
+ function endPathValue() {
+ $path =& $this->pathStack[count( $this->pathStack ) - 1];
+ $path['valueEndToken'] = $this->pos;
+ $path['valueEndByte'] = $this->byteNum;
+ }
+
+ /**
+ * Mark the comma separator in an array element
+ */
+ function markComma() {
+ $path =& $this->pathStack[count( $this->pathStack ) - 1];
+ $path['hasComma'] = true;
+ }
+
+ /**
+ * Mark the arrow separator in an associative array element
+ */
+ function markArrow() {
+ $path =& $this->pathStack[count( $this->pathStack ) - 1];
+ $path['arrowByte'] = $this->byteNum;
+ }
+
+ /**
+ * Generate a parse error
+ */
+ function error( $msg ) {
+ throw new ConfEditorParseError( $this, $msg );
+ }
+
+ /**
+ * Get a readable name for the given token type.
+ */
+ function getTypeName( $type ) {
+ if ( is_int( $type ) ) {
+ return token_name( $type );
+ } else {
+ return "\"$type\"";
+ }
+ }
+
+ /**
+ * Looks ahead to see if the given type is the next token type, starting
+ * from the current position plus the given offset. Skips any intervening
+ * whitespace.
+ */
+ function isAhead( $type, $offset = 0 ) {
+ $ahead = $offset;
+ $token = $this->getTokenAhead( $offset );
+ while ( !$token->isEnd() ) {
+ if ( $token->isSkip() ) {
+ $ahead++;
+ $token = $this->getTokenAhead( $ahead );
+ continue;
+ } elseif ( $token->type == $type ) {
+ // Found the type
+ return true;
+ } else {
+ // Not found
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the previous token object
+ */
+ function prevToken() {
+ return $this->prevToken;
+ }
+
+ /**
+ * Echo a reasonably readable representation of the tokenizer array.
+ */
+ function dumpTokens() {
+ $out = '';
+ foreach ( $this->tokens as $token ) {
+ $obj = $this->newTokenObj( $token );
+ $out .= sprintf( "%-28s %s\n",
+ $this->getTypeName( $obj->type ),
+ addcslashes( $obj->text, "\0..\37" ) );
+ }
+ echo "<pre>" . htmlspecialchars( $out ) . "</pre>";
+ }
+}
+
+/**
+ * Exception class for parse errors
+ */
+class ConfEditorParseError extends MWException {
+ var $lineNum, $colNum;
+ function __construct( $editor, $msg ) {
+ $this->lineNum = $editor->lineNum;
+ $this->colNum = $editor->colNum;
+ parent::__construct( "Parse error on line {$editor->lineNum} " .
+ "col {$editor->colNum}: $msg" );
+ }
+
+ function highlight( $text ) {
+ $lines = StringUtils::explode( "\n", $text );
+ foreach ( $lines as $lineNum => $line ) {
+ if ( $lineNum == $this->lineNum - 1 ) {
+ return "$line\n" .str_repeat( ' ', $this->colNum - 1 ) . "^\n";
+ }
+ }
+ }
+
+}
+
+/**
+ * Class to wrap a token from the tokenizer.
+ */
+class ConfEditorToken {
+ var $type, $text;
+
+ static $scalarTypes = array( T_LNUMBER, T_DNUMBER, T_STRING, T_CONSTANT_ENCAPSED_STRING );
+ static $skipTypes = array( T_WHITESPACE, T_COMMENT, T_DOC_COMMENT );
+
+ static function newEnd() {
+ return new self( 'END', '' );
+ }
+
+ function __construct( $type, $text ) {
+ $this->type = $type;
+ $this->text = $text;
+ }
+
+ function isSkip() {
+ return in_array( $this->type, self::$skipTypes );
+ }
+
+ function isScalar() {
+ return in_array( $this->type, self::$scalarTypes );
+ }
+
+ function isEnd() {
+ return $this->type == 'END';
+ }
+}
+
diff --git a/includes/Credits.php b/includes/Credits.php
index ae9377f2..91ba3f16 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -55,13 +55,13 @@ class Credits {
* @param $showIfMax Bool: whether to contributors if there more than $cnt
* @return String: html
*/
- public static function getCredits($article, $cnt, $showIfMax=true) {
+ public static function getCredits( Article $article, $cnt, $showIfMax = true ) {
wfProfileIn( __METHOD__ );
$s = '';
if( isset( $cnt ) && $cnt != 0 ){
$s = self::getAuthor( $article );
- if ($cnt > 1 || $cnt < 0) {
+ if ( $cnt > 1 || $cnt < 0 ) {
$s .= ' ' . self::getContributors( $article, $cnt - 1, $showIfMax );
}
}
@@ -75,7 +75,7 @@ class Credits {
* @param $article Article object
*/
protected static function getAuthor( Article $article ){
- global $wgLang, $wgAllowRealName;
+ global $wgLang;
$user = User::newFromId( $article->getUser() );
@@ -87,7 +87,7 @@ class Credits {
$d = '';
$t = '';
}
- return wfMsg( 'lastmodifiedatby', $d, $t, self::userLink( $user ) );
+ return wfMsgExt( 'lastmodifiedatby', 'parsemag', $d, $t, self::userLink( $user ), $user->getName() );
}
/**
@@ -98,11 +98,11 @@ class Credits {
* @return String: html
*/
protected static function getContributors( Article $article, $cnt, $showIfMax ) {
- global $wgLang, $wgAllowRealName;
+ global $wgLang, $wgHiddenPrefs;
$contributors = $article->getContributors();
- $others_link = '';
+ $others_link = false;
# Hmm... too many to fit!
if( $cnt > 0 && $contributors->count() > $cnt ){
@@ -113,38 +113,48 @@ class Credits {
$real_names = array();
$user_names = array();
- $anon = 0;
+ $anon_ips = array();
# Sift for real versus user names
foreach( $contributors as $user ) {
$cnt--;
if( $user->isLoggedIn() ){
$link = self::link( $user );
- if( $wgAllowRealName && $user->getRealName() )
+ if( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() )
$real_names[] = $link;
else
$user_names[] = $link;
} else {
- $anon++;
+ $anon_ips[] = self::link( $user );
}
if( $cnt == 0 ) break;
}
- # Two strings: real names, and user names
- $real = $wgLang->listToText( $real_names );
- $user = $wgLang->listToText( $user_names );
- if( $anon )
- $anon = wfMsgExt( 'anonymous', array( 'parseinline' ), $anon );
+ if ( count( $real_names ) ) {
+ $real = $wgLang->listToText( $real_names );
+ } else {
+ $real = false;
+ }
# "ThisSite user(s) A, B and C"
- if( !empty( $user ) ){
- $user = wfMsgExt( 'siteusers', array( 'parsemag' ), $user, count( $user_names ) );
+ if( count( $user_names ) ){
+ $user = wfMsgExt( 'siteusers', array( 'parsemag' ),
+ $wgLang->listToText( $user_names ), count( $user_names ) );
+ } else {
+ $user = false;
+ }
+
+ if( count( $anon_ips ) ){
+ $anon = wfMsgExt( 'anonusers', array( 'parsemag' ),
+ $wgLang->listToText( $anon_ips ), count( $anon_ips ) );
+ } else {
+ $anon = false;
}
# This is the big list, all mooshed together. We sift for blank strings
$fulllist = array();
foreach( array( $real, $user, $anon, $others_link ) as $s ){
- if( !empty( $s ) ){
+ if( $s ){
array_push( $fulllist, $s );
}
}
@@ -153,40 +163,42 @@ class Credits {
$creds = $wgLang->listToText( $fulllist );
# "Based on work by ..."
- return empty( $creds ) ? '' : wfMsg( 'othercontribs', $creds );
+ return strlen( $creds ) ? wfMsg( 'othercontribs', $creds ) : '';
}
/**
- * Get a link to $user_name page
+ * Get a link to $user's user page
* @param $user User object
* @return String: html
*/
protected static function link( User $user ) {
- global $wgUser, $wgAllowRealName;
- if( $wgAllowRealName )
+ global $wgUser, $wgHiddenPrefs;
+ if( !in_array( 'realname', $wgHiddenPrefs ) && !$user->isAnon() )
$real = $user->getRealName();
else
$real = false;
$skin = $wgUser->getSkin();
- $page = $user->getUserPage();
-
+ $page = $user->isAnon() ?
+ SpecialPage::getTitleFor( 'Contributions', $user->getName() ) :
+ $user->getUserPage();
+
return $skin->link( $page, htmlspecialchars( $real ? $real : $user->getName() ) );
}
/**
- * Get a link to $user_name page
+ * Get a link to $user's user page
* @param $user_name String: user name
* @param $linkText String: optional display
* @return String: html
*/
protected static function userLink( User $user ) {
- global $wgUser, $wgAllowRealName;
+ $link = self::link( $user );
if( $user->isAnon() ){
- return wfMsgExt( 'anonymous', array( 'parseinline' ), 1 );
+ return wfMsgExt( 'anonuser', array( 'parseinline', 'replaceafter' ), $link );
} else {
- $link = self::link( $user );
- if( $wgAllowRealName && $user->getRealName() )
+ global $wgHiddenPrefs;
+ if( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() )
return $link;
else
return wfMsgExt( 'siteuser', array( 'parseinline', 'replaceafter' ), $link );
@@ -203,4 +215,4 @@ class Credits {
$skin = $wgUser->getSkin();
return $skin->link( $article->getTitle(), wfMsgHtml( 'others' ), array(), array( 'action' => 'credits' ), array( 'known' ) );
}
-} \ No newline at end of file
+}
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
index 52e9a8c8..2df56115 100644
--- a/includes/DatabaseFunctions.php
+++ b/includes/DatabaseFunctions.php
@@ -58,7 +58,7 @@ function wfIgnoreSQLErrors( $newstate, $dbi = DB_LAST ) {
if ( $db !== false ) {
return $db->ignoreErrors( $newstate );
} else {
- return NULL;
+ return null;
}
}
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 160273b8..a369fccd 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -33,7 +33,7 @@ if ( !defined( 'MW_PHP4' ) ) {
}
/** MediaWiki version number */
-$wgVersion = '1.15.5';
+$wgVersion = '1.16.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
@@ -142,16 +142,17 @@ $wgRedirectScript = false; ///< defaults to "{$wgScriptPath}/redirect{$wgScrip
* splitting style sheets or images outside the main document root.
*/
/**
- * style path as seen by users
+ * asset paths as seen by users
*/
$wgStylePath = false; ///< defaults to "{$wgScriptPath}/skins"
+$wgExtensionAssetsPath = false; ///< defaults to "{$wgScriptPath}/extensions"
+
/**
* filesystem stylesheets directory
*/
$wgStyleDirectory = false; ///< defaults to "{$IP}/skins"
$wgStyleSheetPath = &$wgStylePath;
$wgArticlePath = false; ///< default to "{$wgScript}/$1" or "{$wgScript}?title=$1", depending on $wgUsePathInfo
-$wgVariantArticlePath = false;
$wgUploadPath = false; ///< defaults to "{$wgScriptPath}/images"
$wgUploadDirectory = false; ///< defaults to "{$IP}/images"
$wgHashedUploadDirectory = true;
@@ -165,6 +166,16 @@ $wgUploadBaseUrl = "";
/**@}*/
/**
+ * Directory for caching data in the local filesystem. Should not be accessible
+ * from the web. Set this to false to not use any local caches.
+ *
+ * Note: if multiple wikis share the same localisation cache directory, they
+ * must all have the same set of extensions. You can set a directory just for
+ * the localisation cache using $wgLocalisationCacheConf['storeDirectory'].
+ */
+$wgCacheDirectory = false;
+
+/**
* Default value for chmoding of new directories.
*/
$wgDirectoryMode = 0777;
@@ -181,11 +192,14 @@ $wgFileStore['deleted']['directory'] = false;///< Defaults to $wgUploadDirectory
$wgFileStore['deleted']['url'] = null; ///< Private
$wgFileStore['deleted']['hash'] = 3; ///< 3-level subdirectory split
+$wgImgAuthDetails = false; ///< defaults to false - only set to true if you use img_auth and want the user to see details on why access failed
+$wgImgAuthPublicTest = true; ///< defaults to true - if public read is turned on, no need for img_auth, config error unless other access is used
+
/**@{
* File repository structures
*
- * $wgLocalFileRepo is a single repository structure, and $wgForeignFileRepo is
- * a an array of such structures. Each repository structure is an associative
+ * $wgLocalFileRepo is a single repository structure, and $wgForeignFileRepos is
+ * an array of such structures. Each repository structure is an associative
* array of properties configuring the repository.
*
* Properties required for all repos:
@@ -194,20 +208,27 @@ $wgFileStore['deleted']['hash'] = 3; ///< 3-level subdirectory split
*
* name A unique name for the repository.
*
- * For all core repos:
+ * For most core repos:
* url Base public URL
* hashLevels The number of directory levels for hash-based division of files
* thumbScriptUrl The URL for thumb.php (optional, not recommended)
* transformVia404 Whether to skip media file transformation on parse and rely on a 404
* handler instead.
- * initialCapital Equivalent to $wgCapitalLinks, determines whether filenames implicitly
- * start with a capital letter. The current implementation may give incorrect
- * description page links when the local $wgCapitalLinks and initialCapital
- * are mismatched.
+ * initialCapital Equivalent to $wgCapitalLinks (or $wgCapitalLinkOverrides[NS_FILE],
+ * determines whether filenames implicitly start with a capital letter.
+ * The current implementation may give incorrect description page links
+ * when the local $wgCapitalLinks and initialCapital are mismatched.
* pathDisclosureProtection
* May be 'paranoid' to remove all parameters from error messages, 'none' to
* leave the paths in unchanged, or 'simple' to replace paths with
* placeholders. Default for LocalRepo is 'simple'.
+ * fileMode This allows wikis to set the file mode when uploading/moving files. Default
+ * is 0644.
+ * directory The local filesystem directory where public files are stored. Not used for
+ * some remote repos.
+ * thumbDir The base thumbnail directory. Defaults to <directory>/thumb.
+ * thumbUrl The base thumbnail URL. Defaults to <url>/thumb.
+ *
*
* These settings describe a foreign MediaWiki installation. They are optional, and will be ignored
* for local repositories:
@@ -224,7 +245,7 @@ $wgFileStore['deleted']['hash'] = 3; ///< 3-level subdirectory split
* equivalent to the corresponding member of $wgDBservers
* tablePrefix Table prefix, the foreign wiki's $wgDBprefix
* hasSharedCache True if the wiki's shared cache is accessible via the local $wgMemc
- *
+ *
* ForeignAPIRepo:
* apibase Use for the foreign API's URL
* apiThumbCacheExpiry How long to locally cache thumbs for
@@ -237,6 +258,13 @@ $wgForeignFileRepos = array();
/**@}*/
/**
+ * Use Commons as a remote file repository. Essentially a wrapper, when this
+ * is enabled $wgForeignFileRepos will point at Commons with a set of default
+ * settings
+ */
+$wgUseInstantCommons = false;
+
+/**
* Allowed title characters -- regex character class
* Don't change this unless you know what you're doing
*
@@ -263,6 +291,7 @@ $wgForeignFileRepos = array();
* this breaks interlanguage links
*/
$wgLegalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+";
+$wgIllegalFileChars = ":"; // These are additional characters that should be replaced with '-' in file names
/**
@@ -285,7 +314,7 @@ $wgUrlProtocols = array(
/** internal name of virus scanner. This servers as a key to the $wgAntivirusSetup array.
* Set this to NULL to disable virus scanning. If not null, every file uploaded will be scanned for viruses.
*/
-$wgAntivirus= NULL;
+$wgAntivirus= null;
/** Configuration for different virus scanners. This an associative array of associative arrays:
* it contains on setup array per known scanner type. The entry is selected by $wgAntivirus, i.e.
@@ -352,11 +381,11 @@ $wgVerifyMimeType= true;
/** Sets the mime type definition file to use by MimeMagic.php. */
$wgMimeTypeFile= "includes/mime.types";
#$wgMimeTypeFile= "/etc/mime.types";
-#$wgMimeTypeFile= NULL; #use built-in defaults only.
+#$wgMimeTypeFile= null; #use built-in defaults only.
/** Sets the mime type info file to use by MimeMagic.php. */
$wgMimeInfoFile= "includes/mime.info";
-#$wgMimeInfoFile= NULL; #use built-in defaults only.
+#$wgMimeInfoFile= null; #use built-in defaults only.
/** Switch for loading the FileInfo extension by PECL at runtime.
* This should be used only if fileinfo is installed as a shared object
@@ -369,7 +398,7 @@ $wgLoadFileinfoExtension= false;
* The name of the file to process will be appended to the command given here.
* If not set or NULL, mime_content_type will be used if available.
*/
-$wgMimeDetectorCommand= NULL; # use internal mime_content_type function, available since php 4.3.0
+$wgMimeDetectorCommand= null; # use internal mime_content_type function, available since php 4.3.0
#$wgMimeDetectorCommand= "file -bi"; #use external mime detector (Linux)
/** Switch for trivial mime detection. Used by thumb.php to disable all fance
@@ -426,8 +455,12 @@ $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 */
+/**
+* Allow for upload to be copied from an URL. Requires Special:Upload?source=web
+* The timeout for copy uploads is set by $wgHTTPTimeout.
+*/
$wgAllowCopyUploads = false;
+
/**
* Max size for uploads, in bytes. Currently only works for uploads from URL
* via CURL (see $wgAllowCopyUploads). The only way to impose limits on
@@ -440,6 +473,9 @@ $wgMaxUploadSize = 1024*1024*100; # 100MB
* Useful if you want to use a shared repository by default
* without disabling local uploads (use $wgEnableUploads = false for that)
* e.g. $wgUploadNavigationUrl = 'http://commons.wikimedia.org/wiki/Special:Upload';
+ *
+ * This also affects images inline images that do not exist. In that case the URL will get
+ * (?|&)wpDestFile=<filename> appended to it as appropriate.
*/
$wgUploadNavigationUrl = false;
@@ -564,6 +600,10 @@ $wgDBpassword = '';
/** Database type */
$wgDBtype = 'mysql';
+/** Separate username and password for maintenance tasks. Leave as null to use the default */
+$wgDBadminuser = null;
+$wgDBadminpassword = null;
+
/** Search type
* Leave as null to select the default search engine for the
* selected database type (eg SearchMySQL), or set to a class
@@ -610,6 +650,8 @@ $wgCheckDBSchema = true;
* main database.
* For backwards compatibility the shared prefix is set to the same as the local
* prefix, and the user table is listed in the default list of shared tables.
+ * The user_properties table is also added so that users will continue to have their
+ * preferences shared (preferences were stored in the user table prior to 1.16)
*
* $wgSharedTables may be customized with a list of tables to share in the shared
* datbase. However it is advised to limit what tables you do share as many of
@@ -618,7 +660,7 @@ $wgCheckDBSchema = true;
*/
$wgSharedDB = null;
$wgSharedPrefix = false; # Defaults to $wgDBprefix
-$wgSharedTables = array( 'user' );
+$wgSharedTables = array( 'user', 'user_properties' );
/**
* Database load balancer
@@ -732,8 +774,17 @@ $wgParserCacheType = CACHE_ANYTHING;
$wgParserCacheExpireTime = 86400;
+// Select which DBA handler <http://www.php.net/manual/en/dba.requirements.php> to use as CACHE_DBA backend
+$wgDBAhandler = 'db3';
+
$wgSessionsInMemcached = false;
+/** This is used for setting php's session.save_handler. In practice, you will
+ * almost never need to change this ever. Other options might be 'user' or
+ * 'session_mysql.' Setting to null skips setting this entirely (which might be
+ * useful if you're doing cross-application sessions, see bug 11381) */
+$wgSessionHandler = 'files';
+
/**@{
* Memcached-specific settings
* See docs/memcached.txt
@@ -742,12 +793,15 @@ $wgUseMemCached = false;
$wgMemCachedDebug = false; ///< Will be set to false in Setup.php, if the server isn't working
$wgMemCachedServers = array( '127.0.0.1:11000' );
$wgMemCachedPersistent = false;
+$wgMemCachedTimeout = 100000; //Data timeout in microseconds
/**@}*/
/**
- * Directory for local copy of message cache, for use in addition to memcached
+ * Set this to true to make a local copy of the message cache, for use in
+ * addition to memcached. The files will be put in $wgCacheDirectory.
*/
-$wgLocalMessageCache = false;
+$wgUseLocalMessageCache = false;
+
/**
* Defines format of local cache
* true - Serialized object
@@ -755,6 +809,34 @@ $wgLocalMessageCache = false;
*/
$wgLocalMessageCacheSerialized = true;
+/**
+ * Localisation cache configuration. Associative array with keys:
+ * class: The class to use. May be overridden by extensions.
+ *
+ * store: The location to store cache data. May be 'files', 'db' or
+ * 'detect'. If set to "files", data will be in CDB files. If set
+ * to "db", data will be stored to the database. If set to
+ * "detect", files will be used if $wgCacheDirectory is set,
+ * otherwise the database will be used.
+ *
+ * storeClass: The class name for the underlying storage. If set to a class
+ * name, it overrides the "store" setting.
+ *
+ * storeDirectory: If the store class puts its data in files, this is the
+ * directory it will use. If this is false, $wgCacheDirectory
+ * will be used.
+ *
+ * manualRecache: Set this to true to disable cache updates on web requests.
+ * Use maintenance/rebuildLocalisationCache.php instead.
+ */
+$wgLocalisationCacheConf = array(
+ 'class' => 'LocalisationCache',
+ 'store' => 'detect',
+ 'storeClass' => false,
+ 'storeDirectory' => false,
+ 'manualRecache' => false,
+);
+
# Language settings
#
/** Site language code, should be one of ./languages/Language(.*).php */
@@ -776,14 +858,42 @@ $wgHideInterlanguageLinks = false;
/** List of language names or overrides for default names in Names.php */
$wgExtraLanguageNames = array();
+/**
+ * List of language codes that don't correspond to an actual language.
+ * These codes are leftoffs from renames, or other legacy things.
+ * Also, qqq is a dummy "language" for documenting messages.
+ */
+$wgDummyLanguageCodes = array( 'qqq', 'als', 'be-x-old', 'dk', 'fiu-vro', 'iu', 'nb', 'simple', 'tp' );
+
/** We speak UTF-8 all the time now, unless some oddities happen */
$wgInputEncoding = 'UTF-8';
$wgOutputEncoding = 'UTF-8';
$wgEditEncoding = '';
/**
+ * Set this to true to replace Arabic presentation forms with their standard
+ * forms in the U+0600-U+06FF block. This only works if $wgLanguageCode is
+ * set to "ar".
+ *
+ * Note that pages with titles containing presentation forms will become
+ * inaccessible, run maintenance/cleanupTitles.php to fix this.
+ */
+$wgFixArabicUnicode = true;
+
+/**
+ * Set this to true to replace ZWJ-based chillu sequences in Malayalam text
+ * with their Unicode 5.1 equivalents. This only works if $wgLanguageCode is
+ * set to "ml". Note that some clients (even new clients as of 2010) do not
+ * support these characters.
+ *
+ * If you enable this on an existing wiki, run maintenance/cleanupTitles.php to
+ * fix any ZWJ sequences in existing page titles.
+ */
+$wgFixMalayalamUnicode = true;
+
+/**
* Locale for LC_CTYPE, to work around http://bugs.php.net/bug.php?id=45132
- * For Unix-like operating systems, set this to to a locale that has a UTF-8
+ * For Unix-like operating systems, set this to to a locale that has a UTF-8
* character set. Only the character set is relevant.
*/
$wgShellLocale = 'en_US.utf8';
@@ -817,11 +927,54 @@ $wgLegacyEncoding = false;
*/
$wgLegacySchemaConversion = false;
-$wgMimeType = 'text/html';
-$wgJsMimeType = 'text/javascript';
-$wgDocType = '-//W3C//DTD XHTML 1.0 Transitional//EN';
-$wgDTD = 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd';
-$wgXhtmlDefaultNamespace = 'http://www.w3.org/1999/xhtml';
+$wgMimeType = 'text/html';
+$wgJsMimeType = 'text/javascript';
+$wgDocType = '-//W3C//DTD XHTML 1.0 Transitional//EN';
+$wgDTD = 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd';
+$wgXhtmlDefaultNamespace = 'http://www.w3.org/1999/xhtml';
+
+/**
+ * Should we output an HTML5 doctype? This mode is still experimental, but
+ * all indications are that it should be usable, so it's enabled by default.
+ * If all goes well, it will be removed and become always true before the 1.16
+ * release.
+ */
+$wgHtml5 = true;
+
+/**
+ * Defines the value of the version attribute in the &lt;html&gt; tag, if any.
+ * Will be initialized later if not set explicitly.
+ */
+$wgHtml5Version = null;
+
+/**
+ * Enabled RDFa attributes for use in wikitext.
+ * NOTE: Interaction with HTML5 is somewhat underspecified.
+ */
+$wgAllowRdfaAttributes = false;
+
+/**
+ * Enabled HTML5 microdata attributes for use in wikitext, if $wgHtml5 is also true.
+ */
+$wgAllowMicrodataAttributes = false;
+
+/**
+ * Should we try to make our HTML output well-formed XML? If set to false,
+ * output will be a few bytes shorter, and the HTML will arguably be more
+ * readable. If set to true, life will be much easier for the authors of
+ * screen-scraping bots, and the HTML will arguably be more readable.
+ *
+ * Setting this to false may omit quotation marks on some attributes, omit
+ * slashes from some self-closing tags, omit some ending tags, etc., where
+ * permitted by HTML5. Setting it to true will not guarantee that all pages
+ * will be well-formed, although non-well-formed pages should be rare and it's
+ * a bug if you find one. Conversely, setting it to false doesn't mean that
+ * all XML-y constructs will be omitted, just that they might be.
+ *
+ * Because of compatibility with screen-scraping bots, and because it's
+ * controversial, this is currently left to true by default.
+ */
+$wgWellFormedXml = true;
/**
* Permit other namespaces in addition to the w3.org default.
@@ -831,7 +984,7 @@ $wgXhtmlDefaultNamespace = 'http://www.w3.org/1999/xhtml';
* Normally we wouldn't have to define this in the root <html>
* element, but IE needs it there in some circumstances.
*/
-$wgXhtmlNamespaces = array();
+$wgXhtmlNamespaces = array();
/** Enable to allow rewriting dates in page text.
* DOES NOT FORMAT CORRECTLY FOR MOST LANGUAGES */
@@ -885,6 +1038,32 @@ $wgDisableTitleConversion = false;
/** Default variant code, if false, the default will be the language code */
$wgDefaultLanguageVariant = false;
+/** Disabled variants array of language variant conversion.
+ * example:
+ * $wgDisabledVariants[] = 'zh-mo';
+ * $wgDisabledVariants[] = 'zh-my';
+ *
+ * or:
+ * $wgDisabledVariants = array('zh-mo', 'zh-my');
+ */
+$wgDisabledVariants = array();
+
+/**
+ * Like $wgArticlePath, but on multi-variant wikis, this provides a
+ * path format that describes which parts of the URL contain the
+ * language variant. For Example:
+ *
+ * $wgLanguageCode = 'sr';
+ * $wgVariantArticlePath = '/$2/$1';
+ * $wgArticlePath = '/wiki/$1';
+ *
+ * A link to /wiki/ would be redirected to /sr/Главна_страна
+ *
+ * It is important that $wgArticlePath not overlap with possible values
+ * of $wgVariantArticlePath.
+ */
+$wgVariantArticlePath = false;///< defaults to false
+
/**
* Show a bar of language selection links in the user login and user
* registration forms; edit the "loginlanguagelinks" message to
@@ -970,11 +1149,11 @@ $wgExtraSubtitle = '';
$wgSiteSupportPage = ''; # A page where you users can receive donations
/**
- * Set this to a string to put the wiki into read-only mode. The text will be
- * used as an explanation to users.
+ * Set this to a string to put the wiki into read-only mode. The text will be
+ * used as an explanation to users.
*
- * This prevents most write operations via the web interface. Cache updates may
- * still be possible. To prevent database writes completely, use the read_only
+ * This prevents most write operations via the web interface. Cache updates may
+ * still be possible. To prevent database writes completely, use the read_only
* option in MySQL.
*/
$wgReadOnly = null;
@@ -989,7 +1168,7 @@ $wgReadOnlyFile = false; ///< defaults to "{$wgUploadDirectory}/lock_yBg
/**
* Filename for debug logging. See http://www.mediawiki.org/wiki/How_to_debug
* The debug log file should be not be publicly accessible if it is used, as it
- * may contain private data.
+ * may contain private data.
*/
$wgDebugLogFile = '';
@@ -999,14 +1178,14 @@ $wgDebugLogFile = '';
$wgDebugLogPrefix = '';
/**
- * If true, instead of redirecting, show a page with a link to the redirect
+ * If true, instead of redirecting, show a page with a link to the redirect
* destination. This allows for the inspection of PHP error messages, and easy
* resubmission of form data. For developer use only.
*/
$wgDebugRedirects = false;
/**
- * If true, log debugging data from action=raw.
+ * If true, log debugging data from action=raw.
* This is normally false to avoid overlapping debug entries due to gen=css and
* gen=js requests.
*/
@@ -1017,14 +1196,11 @@ $wgDebugRawPage = false;
*
* This may occasionally be useful when supporting a non-technical end-user. It's
* more secure than exposing the debug log file to the web, since the output only
- * contains private data for the current user. But it's not ideal for development
+ * contains private data for the current user. But it's not ideal for development
* use since data is lost on fatal errors and redirects.
*/
$wgDebugComments = false;
-/** Does nothing. Obsolete? */
-$wgLogQueries = false;
-
/**
* Write SQL queries to the debug log
*/
@@ -1046,6 +1222,16 @@ $wgDebugLogGroups = array();
$wgShowDebug = false;
/**
+ * Prefix debug messages with relative timestamp. Very-poor man's profiler.
+ */
+$wgDebugTimestamps = false;
+
+/**
+ * Print HTTP headers for every request in the debug information.
+ */
+$wgDebugPrintHttpHeaders = true;
+
+/**
* Show the contents of $wgHooks in Special:Version
*/
$wgSpecialVersionShowHooks = false;
@@ -1073,22 +1259,33 @@ $wgColorErrors = true;
$wgShowExceptionDetails = false;
/**
+ * If true, show a backtrace for database errors
+ */
+$wgShowDBErrorBacktrace = false;
+
+/**
* Expose backend server host names through the API and various HTML comments
*/
$wgShowHostnames = false;
/**
+ * If set to true MediaWiki will throw notices for some possible error
+ * conditions and for deprecated functions.
+ */
+$wgDevelopmentWarnings = false;
+
+/**
* Use experimental, DMOZ-like category browser
*/
$wgUseCategoryBrowser = false;
/**
- * Keep parsed pages in a cache (objectcache table, turck, or memcached)
+ * Keep parsed pages in a cache (objectcache table or memcached)
* to speed up output of the same page viewed by another user with the
* same options.
*
* This can provide a significant speedup for medium to large pages,
- * so you probably want to keep it on. Extensions that conflict with the
+ * so you probably want to keep it on. Extensions that conflict with the
* parser cache should disable the cache on a per-page basis instead.
*/
$wgEnableParserCache = true;
@@ -1120,7 +1317,7 @@ $wgSidebarCacheExpiry = 86400;
* as a valid article? If $wgUseCommaCount is set to true, it will be
* counted if it contains at least one comma. If it is set to false
* (default), it will only be counted if it contains at least one [[wiki
- * link]]. See http://meta.wikimedia.org/wiki/Help:Article_count
+ * link]]. See http://www.mediawiki.org/wiki/Manual:Article_count
*
* Retroactively changing this variable will not affect
* the existing count (cf. maintenance/recount.sql).
@@ -1142,6 +1339,19 @@ $wgSysopRangeBans = true; # Allow sysops to ban IP ranges
$wgAutoblockExpiry = 86400; # Number of seconds before autoblock entries expire
$wgBlockAllowsUTEdit = false; # Default setting for option on block form to allow self talkpage editing whilst blocked
$wgSysopEmailBans = true; # Allow sysops to ban users from accessing Emailuser
+$wgBlockCIDRLimit = array(
+ 'IPv4' => 16, # Blocks larger than a /16 (64k addresses) will not be allowed
+ 'IPv6' => 64, # 2^64 = ~1.8x10^19 addresses
+);
+
+/**
+ * If true, blocked users will not be allowed to login. When using this with
+ * a public wiki, the effect of logging out blocked users may actually be
+ * avers: unless the user's address is also blocked (e.g. auto-block),
+ * logging the user out will again allow reading and editing, just as for
+ * anonymous visitors.
+ */
+$wgBlockDisablesLogin = false; #
# Pages anonymous user may see as an array, e.g.:
# array ( "Main Page", "Wikipedia:Help");
@@ -1186,6 +1396,7 @@ $wgGroupPermissions['*']['edit'] = true;
$wgGroupPermissions['*']['createpage'] = true;
$wgGroupPermissions['*']['createtalk'] = true;
$wgGroupPermissions['*']['writeapi'] = true;
+//$wgGroupPermissions['*']['patrolmarks'] = false; // let anons see what was patrolled
// Implicit group for all logged-in accounts
$wgGroupPermissions['user']['move'] = true;
@@ -1202,6 +1413,7 @@ $wgGroupPermissions['user']['reupload'] = true;
$wgGroupPermissions['user']['reupload-shared'] = true;
$wgGroupPermissions['user']['minoredit'] = true;
$wgGroupPermissions['user']['purge'] = true; // can use ?action=purge without clicking "ok"
+$wgGroupPermissions['user']['sendemail'] = true;
// Implicit group for accounts that pass $wgAutoConfirmAge
$wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
@@ -1223,9 +1435,11 @@ $wgGroupPermissions['sysop']['createaccount'] = true;
$wgGroupPermissions['sysop']['delete'] = true;
$wgGroupPermissions['sysop']['bigdelete'] = true; // can be separately configured for pages with > $wgDeleteRevisionsLimit revs
$wgGroupPermissions['sysop']['deletedhistory'] = true; // can view deleted history entries, but not see or restore the text
+$wgGroupPermissions['sysop']['deletedtext'] = true; // can view deleted revision text
$wgGroupPermissions['sysop']['undelete'] = true;
$wgGroupPermissions['sysop']['editinterface'] = true;
-$wgGroupPermissions['sysop']['editusercssjs'] = true;
+$wgGroupPermissions['sysop']['editusercss'] = true;
+$wgGroupPermissions['sysop']['edituserjs'] = true;
$wgGroupPermissions['sysop']['import'] = true;
$wgGroupPermissions['sysop']['importupload'] = true;
$wgGroupPermissions['sysop']['move'] = true;
@@ -1249,6 +1463,7 @@ $wgGroupPermissions['sysop']['markbotedits'] = true;
$wgGroupPermissions['sysop']['apihighlimits'] = true;
$wgGroupPermissions['sysop']['browsearchive'] = true;
$wgGroupPermissions['sysop']['noratelimit'] = true;
+$wgGroupPermissions['sysop']['versiondetail'] = true;
$wgGroupPermissions['sysop']['movefile'] = true;
#$wgGroupPermissions['sysop']['mergehistory'] = true;
@@ -1276,6 +1491,15 @@ $wgGroupPermissions['bureaucrat']['noratelimit'] = true;
*/
# $wgGroupPermissions['developer']['siteadmin'] = true;
+/**
+ * Permission keys revoked from users in each group.
+ * This acts the same way as wgGroupPermissions above, except that
+ * if the user is in a group here, the permission will be removed from them.
+ *
+ * Improperly setting this could mean that your users will be unable to perform
+ * certain essential tasks, so use at your own risk!
+ */
+$wgRevokePermissions = array();
/**
* Implicit groups, aren't shown on Special:Listusers or somewhere else
@@ -1287,7 +1511,7 @@ $wgImplicitGroups = array( '*', 'user', 'autoconfirmed' );
* are allowed to add or revoke.
*
* Setting the list of groups to add or revoke to true is equivalent to "any group".
- *
+ *
* For example, to allow sysops to add themselves to the "bot" group:
*
* $wgGroupsAddToSelf = array( 'sysop' => array( 'bot' ) );
@@ -1298,7 +1522,7 @@ $wgImplicitGroups = array( '*', 'user', 'autoconfirmed' );
*
* This allows users in the '*' group (i.e. any user) to remove themselves from
* any group that they happen to be in.
- *
+ *
*/
$wgGroupsAddToSelf = array();
$wgGroupsRemoveFromSelf = array();
@@ -1372,6 +1596,7 @@ $wgAutoConfirmCount = 0;
* array( APCOND_ISIP, ip ), *OR*
* array( APCOND_IPINRANGE, range ), *OR*
* array( APCOND_AGE_FROM_EDIT, seconds since first edit ), *OR*
+ * array( APCOND_BLOCKED ), *OR*
* similar constructs defined by extensions.
*
* If $wgEmailAuthentication is off, APCOND_EMAILCONFIRMED will be true for any
@@ -1412,14 +1637,6 @@ $wgAvailableRights = array();
*/
$wgDeleteRevisionsLimit = 0;
-/**
- * Used to figure out if a user is "active" or not. User::isActiveEditor()
- * sees if a user has made at least $wgActiveUserEditCount number of edits
- * within the last $wgActiveUserDays days.
- */
-$wgActiveUserEditCount = 30;
-$wgActiveUserDays = 30;
-
# Proxy scanner settings
#
@@ -1466,10 +1683,10 @@ $wgCacheEpoch = '20030516000000';
/**
* Bump this number when changing the global style sheets and JavaScript.
* It should be appended in the query string of static CSS and JS includes,
- * to ensure that client-side caches don't keep obsolete copies of global
+ * to ensure that client-side caches do not keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '207';
+$wgStyleVersion = '270';
# Server-side caching:
@@ -1482,7 +1699,7 @@ $wgStyleVersion = '207';
$wgUseFileCache = false;
/** Directory where the cached page will be saved */
-$wgFileCacheDirectory = false; ///< defaults to "{$wgUploadDirectory}/cache";
+$wgFileCacheDirectory = false; ///< defaults to "$wgCacheDirectory/html";
/**
* When using the file cache, we can store the cached HTML gzipped to save disk
@@ -1581,6 +1798,9 @@ $wgUseSquid = false;
/** If you run Squid3 with ESI support, enable this (default:false): */
$wgUseESI = false;
+/** Send X-Vary-Options header for better caching (requires patched Squid) */
+$wgUseXVO = false;
+
/** Internal server name as known to Squid, if different */
# $wgInternalServer = 'http://yourinternal.tld:8000';
$wgInternalServer = $wgServer;
@@ -1692,7 +1912,7 @@ $wgAllowExternalImagesFrom = '';
* Or false to disable it
*/
$wgEnableImageWhitelist = true;
-
+
/** Allows to move images and other media files */
$wgAllowImageMoving = true;
@@ -1738,6 +1958,26 @@ $wgSpecialPageCacheUpdates = array(
$wgUseTeX = false;
/** Location of the texvc binary */
$wgTexvc = './math/texvc';
+/**
+ * Texvc background color
+ * use LaTeX color format as used in \special function
+ * for transparent background use value 'Transparent' for alpha transparency or
+ * 'transparent' for binary transparency.
+ */
+$wgTexvcBackgroundColor = 'transparent';
+
+/**
+ * Normally when generating math images, we double-check that the
+ * directories we want to write to exist, and that files that have
+ * been generated still exist when we need to bring them up again.
+ *
+ * This lets us give useful error messages in case of permission
+ * problems, and automatically rebuild images that have been lost.
+ *
+ * On a big site with heavy NFS traffic this can be slow and flaky,
+ * so sometimes we want to short-circuit it by setting this to false.
+ */
+$wgMathCheckFiles = true;
#
# Profiling / debugging
@@ -1766,8 +2006,6 @@ $wgUDPProfilerPort = '3811';
$wgDebugProfiling = false;
/** Output debug message on every wfProfileIn/wfProfileOut */
$wgDebugFunctionEntry = 0;
-/** Lots of debugging output from SquidUpdate.php */
-$wgDebugSquid = false;
/*
* Destination for wfIncrStats() data...
@@ -1800,6 +2038,18 @@ $wgSearchHighlightBoundaries = version_compare("5.1", PHP_VERSION, "<")? '[\p{Z}
: '[ ,.;:!?~!@#$%\^&*\(\)+=\-\\|\[\]"\'<>\n\r\/{}]'; // PHP 5.0 workaround
/**
+ * Set to true to have the search engine count total
+ * search matches to present in the Special:Search UI.
+ * Not supported by every search engine shipped with MW.
+ *
+ * This could however be slow on larger wikis, and is pretty flaky
+ * with the current title vs content split. Recommend avoiding until
+ * that's been worked out cleanly; but this may aid in testing the
+ * search UI and API to confirm that the result count works.
+ */
+$wgCountTotalSearchHits = false;
+
+/**
* Template for OpenSearch suggestions, defaults to API action=opensearch
*
* Sites with heavy load would tipically have these point to a custom
@@ -1813,10 +2063,23 @@ $wgOpenSearchTemplate = false;
/**
* Enable suggestions while typing in search boxes
* (results are passed around in OpenSearch format)
+ * Requires $wgEnableOpenSearchSuggest = true;
*/
$wgEnableMWSuggest = false;
/**
+ * Enable OpenSearch suggestions requested by MediaWiki. Set this to
+ * false if you've disabled MWSuggest or another suggestion script and
+ * want reduce load caused by cached scripts pulling suggestions.
+ */
+$wgEnableOpenSearchSuggest = true;
+
+/**
+ * Expiry time for search suggestion responses
+ */
+$wgSearchSuggestCacheExpiry = 1200;
+
+/**
* Template for internal MediaWiki suggestion engine, defaults to API action=opensearch
*
* Placeholders: {searchTerms}, {namespaces}, {dbname}
@@ -1850,6 +2113,11 @@ $wgShowEXIF = function_exists( 'exif_read_data' );
* uploads do work.
*/
$wgRemoteUploads = false;
+
+/**
+ * Disable links to talk pages of anonymous users (IPs) in listings on special
+ * pages like page history, Special:Recentchanges, etc.
+ */
$wgDisableAnonTalk = false;
/**
* Do DELETE/INSERT for link updates instead of incremental
@@ -1900,7 +2168,7 @@ $wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' );
/** Files with these extensions will never be allowed as uploads. */
$wgFileBlacklist = array(
# HTML may contain cookie-stealing JavaScript and web bugs
- 'html', 'htm', 'js', 'jsb', 'mhtml', 'mht',
+ 'html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht',
# PHP scripts may execute arbitrary code on the server
'php', 'phtml', 'php3', 'php4', 'php5', 'phps',
# Other types that may be interpreted by some servers
@@ -1951,37 +2219,50 @@ $wgNamespacesWithSubpages = array(
NS_USER_TALK => true,
NS_PROJECT_TALK => true,
NS_FILE_TALK => true,
+ NS_MEDIAWIKI => true,
NS_MEDIAWIKI_TALK => true,
NS_TEMPLATE_TALK => true,
NS_HELP_TALK => true,
NS_CATEGORY_TALK => true
);
+/**
+ * Which namespaces have special treatment where they should be preview-on-open
+ * Internaly only Category: pages apply, but using this extensions (e.g. Semantic MediaWiki)
+ * can specify namespaces of pages they have special treatment for
+ */
+$wgPreviewOnOpenNamespaces = array(
+ NS_CATEGORY => true
+);
+
$wgNamespacesToBeSearchedDefault = array(
NS_MAIN => true,
);
/**
- * Additional namespaces to those in $wgNamespacesToBeSearchedDefault that
- * will be added to default search for "project" page inclusive searches
- *
+ * Namespaces to be searched when user clicks the "Help" tab
+ * on Special:Search
+ *
* Same format as $wgNamespacesToBeSearchedDefault
- */
-$wgNamespacesToBeSearchedProject = array(
- NS_USER => true,
- NS_PROJECT => true,
+ */
+$wgNamespacesToBeSearchedHelp = array(
+ NS_PROJECT => true,
NS_HELP => true,
- NS_CATEGORY => true,
);
-$wgUseOldSearchUI = true; // temp testing variable
+/**
+ * If set to true the 'searcheverything' preference will be effective only for logged-in users.
+ * Useful for big wikis to maintain different search profiles for anonymous and logged-in users.
+ *
+ */
+$wgSearchEverythingOnlyLoggedIn = false;
/**
* Site notice shown at the top of each page
*
- * This message can contain wiki text, and can also be set through the
- * MediaWiki:Sitenotice page. You can also provide a separate message for
- * logged-out users using the MediaWiki:Anonnotice page.
+ * MediaWiki:Sitenotice page, which will override this. You can also
+ * provide a separate message for logged-out users using the
+ * MediaWiki:Anonnotice page.
*/
$wgSiteNotice = '';
@@ -1996,7 +2277,7 @@ $wgSiteNotice = '';
$wgMediaHandlers = array(
'image/jpeg' => 'BitmapHandler',
'image/png' => 'BitmapHandler',
- 'image/gif' => 'BitmapHandler',
+ 'image/gif' => 'GIFHandler',
'image/tiff' => 'TiffHandler',
'image/x-ms-bmp' => 'BmpHandler',
'image/x-bmp' => 'BmpHandler',
@@ -2026,8 +2307,8 @@ $wgSharpenParameter = '0x0.4';
/** Reduction in linear dimensions below which sharpening will be enabled */
$wgSharpenReductionThreshold = 0.85;
-/**
- * Temporary directory used for ImageMagick. The directory must exist. Leave
+/**
+ * Temporary directory used for ImageMagick. The directory must exist. Leave
* this set to false to let ImageMagick decide for itself.
*/
$wgImageMagickTempDir = false;
@@ -2084,7 +2365,8 @@ $wgMaxAnimatedGifArea = 1.0e6;
* // JPEG is good for photos, but has no transparency support. Bad for diagrams.
* $wgTiffThumbnailType = array( 'jpg', 'image/jpeg' );
*/
-$wgTiffThumbnailType = false;
+ $wgTiffThumbnailType = false;
+
/**
* If rendered thumbnail files are older than this timestamp, they
* will be rerendered on demand as if the file didn't already exist.
@@ -2115,9 +2397,15 @@ $wgIgnoreImageErrors = false;
*/
$wgGenerateThumbnailOnParse = true;
-/** Whether or not to use image resizing */
+/**
+* Show thumbnails for old images on the image description page
+*/
+$wgShowArchiveThumbnails = true;
+
+/** Obsolete, always true, kept for compatibility with extensions */
$wgUseImageResize = true;
+
/** Set $wgCommandLineMode if it's not set already, to avoid notices */
if( !isset( $wgCommandLineMode ) ) {
$wgCommandLineMode = false;
@@ -2126,6 +2414,13 @@ if( !isset( $wgCommandLineMode ) ) {
/** For colorized maintenance script output, is your terminal background dark ? */
$wgCommandLineDarkBg = false;
+/**
+ * Array for extensions to register their maintenance scripts with the
+ * system. The key is the name of the class and the value is the full
+ * path to the file
+ */
+$wgMaintenanceScripts = array();
+
#
# Recent changes settings
#
@@ -2136,9 +2431,9 @@ $wgPutIPinRC = true;
/**
* Recentchanges items are periodically purged; entries older than this many
* seconds will go.
- * For one week : 7 * 24 * 3600
+ * Default: 13 weeks = about three months
*/
-$wgRCMaxAge = 7 * 24 * 3600;
+$wgRCMaxAge = 13 * 7 * 24 * 3600;
/**
* Filter $wgRCLinkDays by $wgRCMaxAge to avoid showing links for numbers higher than what will be stored.
@@ -2167,19 +2462,19 @@ $wgRC2UDPPort = false;
/**
* Prefix to prepend to each UDP packet.
* This can be used to identify the wiki. A script is available called
- * mxircecho.py which listens on a UDP port, and uses a prefix ending in a
+ * mxircecho.py which listens on a UDP port, and uses a prefix ending in a
* tab to identify the IRC channel to send the log line to.
*/
$wgRC2UDPPrefix = '';
/**
- * If this is set to true, $wgLocalInterwiki will be prepended to links in the
+ * If this is set to true, $wgLocalInterwiki will be prepended to links in the
* IRC feed. If this is set to a string, that string will be used as the prefix.
*/
$wgRC2UDPInterwikiPrefix = false;
/**
- * Set to true to omit "bot" edits (by users with the bot permission) from the
+ * Set to true to omit "bot" edits (by users with the bot permission) from the
* UDP feed.
*/
$wgRC2UDPOmitBots = false;
@@ -2191,16 +2486,6 @@ $wgRC2UDPOmitBots = false;
*/
$wgEnableNewpagesUserFilter = true;
-/**
- * Whether to use metadata edition
- * This will put categories, language links and allowed templates in a separate text box
- * while editing pages
- * EXPERIMENTAL
- */
-$wgUseMetadataEdit = false;
-/** Full name (including namespace) of the page containing templates names that will be allowed as metadata */
-$wgMetadataWhitelist = '';
-
#
# Copyright and credits settings
#
@@ -2212,13 +2497,13 @@ $wgEnableCreativeCommonsRdf = false;
/** Override for copyright metadata.
* TODO: these options need documentation
*/
-$wgRightsPage = NULL;
-$wgRightsUrl = NULL;
-$wgRightsText = NULL;
-$wgRightsIcon = NULL;
+$wgRightsPage = null;
+$wgRightsUrl = null;
+$wgRightsText = null;
+$wgRightsIcon = null;
/** Set this to some HTML to override the rights icon with an arbitrary logo */
-$wgCopyrightIcon = NULL;
+$wgCopyrightIcon = null;
/** Set this to true if you want detailed copyright information forms on Upload. */
$wgUseCopyrightUpload = false;
@@ -2251,6 +2536,18 @@ $wgShowCreditsIfMax = true;
$wgCapitalLinks = true;
/**
+ * @since 1.16 - This can now be set per-namespace. Some special namespaces (such
+ * as Special, see MWNamespace::$alwaysCapitalizedNamespaces for the full list) must be
+ * true by default (and setting them has no effect), due to various things that
+ * require them to be so. Also, since Talk namespaces need to directly mirror their
+ * associated content namespaces, the values for those are ignored in favor of the
+ * subject namespace's setting. Setting for NS_MEDIA is taken automatically from
+ * NS_FILE.
+ * EX: $wgCapitalLinkOverrides[ NS_FILE ] = false;
+ */
+$wgCapitalLinkOverrides = array();
+
+/**
* List of interwiki prefixes for wikis we'll accept as sources for
* Special:Import (for sysops). Since complete page history can be imported,
* these should be 'trusted'.
@@ -2283,6 +2580,9 @@ $wgExportAllowHistory = true;
*/
$wgExportMaxHistory = 0;
+/**
+* Return distinct author list (when not returning full history)
+*/
$wgExportAllowListContributors = false ;
/**
@@ -2299,8 +2599,8 @@ $wgExportAllowListContributors = false ;
$wgExportMaxLinkDepth = 0;
/**
- * Whether to allow the "export all pages in namespace" option
- */
+* Whether to allow the "export all pages in namespace" option
+*/
$wgExportFromNamespaces = false;
/**
@@ -2311,6 +2611,7 @@ $wgExportFromNamespaces = false;
* May be an array of regexes or a single string for backwards compatibility.
*
* See http://en.wikipedia.org/wiki/Regular_expression
+ * Note that each regex needs a beginning/end delimiter, eg: # or /
*/
$wgSpamRegex = array();
@@ -2375,7 +2676,10 @@ $wgValidateAllHtml = false;
/** See list of skins and their symbolic names in languages/Language.php */
$wgDefaultSkin = 'monobook';
-/** Should we allow the user's to select their own skin that will override the default? */
+/**
+* Should we allow the user's to select their own skin that will override the default?
+* @deprecated in 1.16, use $wgHiddenPrefs[] = 'skin' to disable it
+*/
$wgAllowUserSkin = true;
/**
@@ -2475,11 +2779,21 @@ $wgDefaultUserOptions = array(
'watchdeletion' => 0,
'noconvertlink' => 0,
'gender' => 'unknown',
+ 'ccmeonemails' => 0,
+ 'disablemail' => 0,
+ 'editfont' => 'default',
);
-/** Whether or not to allow and use real name fields. Defaults to true. */
+/**
+ * Whether or not to allow and use real name fields.
+ * @deprecated in 1.16, use $wgHiddenPrefs[] = 'realname' below to disable real
+ * names
+ */
$wgAllowRealName = true;
+/** An array of preferences to not show for the user */
+$wgHiddenPrefs = array();
+
/*****************************************************************************
* Extensions
*/
@@ -2496,10 +2810,15 @@ $wgExtensionFunctions = array();
$wgSkinExtensionFunctions = array();
/**
- * Extension messages files
- * Associative array mapping extension name to the filename where messages can be found.
- * The file must create a variable called $messages.
- * When the messages are needed, the extension should call wfLoadExtensionMessages().
+ * Extension messages files.
+ *
+ * Associative array mapping extension name to the filename where messages can be
+ * found. The file should contain variable assignments. Any of the variables
+ * present in languages/messages/MessagesEn.php may be defined, but $messages
+ * is the most common.
+ *
+ * Variables defined in extensions will override conflicting variables defined
+ * in the core.
*
* Example:
* $wgExtensionMessagesFiles['ConfirmEdit'] = dirname(__FILE__).'/ConfirmEdit.i18n.php';
@@ -2509,13 +2828,7 @@ $wgExtensionMessagesFiles = array();
/**
* Aliases for special pages provided by extensions.
- * Associative array mapping special page to array of aliases. First alternative
- * for each special page will be used as the normalised name for it. English
- * aliases will be added to the end of the list so that they always work. The
- * file must define a variable $aliases.
- *
- * Example:
- * $wgExtensionAliasesFiles['Translate'] = dirname(__FILE__).'/Translate.alias.php';
+ * @deprecated Use $specialPageAliases in a file referred to by $wgExtensionMessagesFiles
*/
$wgExtensionAliasesFiles = array();
@@ -2560,8 +2873,8 @@ $wgAutoloadClasses = array();
* <code>
* $wgExtensionCredits[$type][] = array(
* 'name' => 'Example extension',
- * 'version' => 1.9,
- * 'svn-revision' => '$LastChangedRevision: 70070 $',
+ * 'version' => 1.9,
+ * 'path' => __FILE__,
* 'author' => 'Foo Barstein',
* 'url' => 'http://wwww.example.com/Example%20Extension/',
* 'description' => 'An example extension',
@@ -2570,6 +2883,8 @@ $wgAutoloadClasses = array();
* </code>
*
* Where $type is 'specialpage', 'parserhook', 'variable', 'media' or 'other'.
+ * Where 'descriptionmsg' can be an array with message key and parameters:
+ * 'descriptionmsg' => array( 'exampleextension-desc', param1, param2, ... ),
*/
$wgExtensionCredits = array();
/*
@@ -2596,7 +2911,11 @@ $wgUseSiteJs = true;
/** Use the site's Cascading Style Sheets (CSS)? */
$wgUseSiteCss = true;
-/** Filter for Special:Randompage. Part of a WHERE clause */
+/**
+ * Filter for Special:Randompage. Part of a WHERE clause
+ * @deprecated as of 1.16, use the SpecialRandomGetRandomTitle hook
+*/
+
$wgExtraRandompageSQL = false;
/** Allow the "info" action, very inefficient at the moment */
@@ -2608,9 +2927,6 @@ $wgMaxTocLevel = 999;
/** Name of the external diff engine to use */
$wgExternalDiffEngine = false;
-/** Whether to use inline diff */
-$wgEnableHtmlDiff = false;
-
/** Use RC Patrolling to check for vandalism */
$wgUseRCPatrol = true;
@@ -2646,6 +2962,12 @@ $wgFeedDiffCutoff = 32768;
$wgOverrideSiteFeed = array();
/**
+ * Which feed types should we provide by default? This can include 'rss',
+ * 'atom', neither, or both.
+ */
+$wgAdvertisedFeedTypes = array( 'atom' );
+
+/**
* Additional namespaces. If the namespaces defined in Language.php and
* Namespace.php are insufficient, you can create new ones here, for example,
* to import Help files in other languages.
@@ -2662,7 +2984,7 @@ $wgOverrideSiteFeed = array();
# 102 => "Aide",
# 103 => "Discussion_Aide"
# );
-$wgExtraNamespaces = NULL;
+$wgExtraNamespaces = null;
/**
* Namespace aliases
@@ -2777,10 +3099,10 @@ $wgBrowserBlackList = array(
/**
* Fake out the timezone that the server thinks it's in. This will be used for
* date display and not for what's stored in the DB. Leave to null to retain
- * your server's OS-based timezone value. This is the same as the timezone.
+ * your server's OS-based timezone value.
*
- * This variable is currently used ONLY for signature formatting, not for
- * anything else.
+ * This variable is currently used only for signature formatting and for local
+ * time/date parser variables ({{LOCALTIME}} etc.)
*
* Timezones can be translated by editing MediaWiki messages of type
* timezone-nameinlowercase like timezone-utc.
@@ -2802,10 +3124,10 @@ $wgLocaltimezone = null;
* $wgLocalTZoffset = date("Z") / 60;
*
* If your server is not configured for the timezone you want, you can set
- * this in conjunction with the signature timezone and override the TZ
- * environment variable like so:
+ * this in conjunction with the signature timezone and override the PHP default
+ * timezone like so:
* $wgLocaltimezone="Europe/Berlin";
- * putenv("TZ=$wgLocaltimezone");
+ * date_default_timezone_set( $wgLocaltimezone );
* $wgLocalTZoffset = date("Z") / 60;
*
* Leave at NULL to show times in universal time (UTC/GMT).
@@ -2871,6 +3193,7 @@ $wgLogTypes = array( '',
* Users without this will not see it in the option menu and can not view it
* Restricted logs are not added to recent changes
* Logs should remain non-transcludable
+ * Format: logtype => permissiontype
*/
$wgLogRestrictions = array(
'suppress' => 'suppressionlog'
@@ -2881,7 +3204,7 @@ $wgLogRestrictions = array(
*
* This is associative array of log type => boolean "hide by default"
*
- * See $wgLogTypes for a list of available log types.
+ * See $wgLogTypes for a list of available log types.
*
* For example:
* $wgFilterLogTypes => array(
@@ -2890,7 +3213,7 @@ $wgLogRestrictions = array(
* );
*
* Will display show/hide links for the move and import logs. Move logs will be
- * hidden by default unless the link is clicked. Import logs will be shown by
+ * hidden by default unless the link is clicked. Import logs will be shown by
* default, and hidden when the link is clicked.
*
* A message of the form log-show-hide-<type> should be added, and will be used
@@ -3025,7 +3348,7 @@ $wgSpecialPageGroups = array(
'Newimages' => 'changes',
'Newpages' => 'changes',
'Log' => 'changes',
- 'Tags' => 'changes',
+ 'Tags' => 'changes',
'Upload' => 'media',
'Listfiles' => 'media',
@@ -3034,6 +3357,7 @@ $wgSpecialPageGroups = array(
'Filepath' => 'media',
'Listusers' => 'users',
+ 'Activeusers' => 'users',
'Listgrouprights' => 'users',
'Ipblocklist' => 'users',
'Contributions' => 'users',
@@ -3088,14 +3412,6 @@ $wgSpecialPageGroups = array(
);
/**
- * Experimental preview feature to fetch rendered text
- * over an XMLHttpRequest from JavaScript instead of
- * forcing a submit and reload of the whole page.
- * Leave disabled unless you're testing it.
- */
-$wgLivePreview = false;
-
-/**
* Disable the internal MySQL-based search, to allow it to be
* implemented by an extension instead.
*/
@@ -3180,7 +3496,7 @@ $wgNamespaceRobotPolicies = array();
* 'Main_Page' => 'noindex,follow',
* # "Project", not the actual project name!
* 'Project:X' => 'index,follow',
- * # Needs to be "Abc", not "abc" (unless $wgCapitalLinks is false)!
+ * # Needs to be "Abc", not "abc" (unless $wgCapitalLinks is false for that namespace)!
* 'abc' => 'noindex,nofollow'
* );
*/
@@ -3199,11 +3515,11 @@ $wgExemptFromUserRobotsControl = null;
* Specifies the minimal length of a user password. If set to 0, empty pass-
* words are allowed.
*/
-$wgMinimalPasswordLength = 0;
+$wgMinimalPasswordLength = 1;
/**
* Activate external editor interface for files and pages
- * See http://meta.wikimedia.org/wiki/Help:External_editors
+ * See http://www.mediawiki.org/wiki/Manual:External_editors
*/
$wgUseExternalEditor = true;
@@ -3231,10 +3547,35 @@ $wgDisabledActions = array();
$wgDisableHardRedirects = false;
/**
- * Use http.dnsbl.sorbs.net to check for open proxies
+ * Set to false to disable application of access keys and tooltips,
+ * eg to avoid keyboard conflicts with system keys or as a low-level
+ * optimization.
+ */
+$wgEnableTooltipsAndAccesskeys = true;
+
+/**
+ * Whether to use DNS blacklists in $wgDnsBlacklistUrls to check for open proxies
+ * @since 1.16
+ */
+$wgEnableDnsBlacklist = false;
+
+/**
+ * @deprecated Use $wgEnableDnsBlacklist instead, only kept for backward
+ * compatibility
*/
$wgEnableSorbs = false;
-$wgSorbsUrl = 'http.dnsbl.sorbs.net.';
+
+/**
+ * List of DNS blacklists to use, if $wgEnableDnsBlacklist is true
+ * @since 1.16
+ */
+$wgDnsBlacklistUrls = array( 'http.dnsbl.sorbs.net.' );
+
+/**
+ * @deprecated Use $wgDnsBlacklistUrls instead, only kept for backward
+ * compatibility
+ */
+$wgSorbsUrl = array();
/**
* Proxy whitelist, list of addresses that are assumed to be non-proxy despite
@@ -3266,7 +3607,7 @@ $wgRateLimits = array(
'subnet' => null,
),
'mailpassword' => array(
- 'anon' => NULL,
+ 'anon' => null,
),
'emailuser' => array(
'user' => null,
@@ -3364,9 +3705,14 @@ $wgTrustedMediaFormats= array(
$wgAllowSpecialInclusion = true;
/**
- * Timeout for HTTP requests done via CURL
+ * Timeout for HTTP requests done internally
+ */
+$wgHTTPTimeout = 25;
+
+/**
+ * Timeout for Asynchronous (background) HTTP requests
*/
-$wgHTTPTimeout = 3;
+$wgAsyncHTTPTimeout = 25;
/**
* Proxy to use for CURL requests.
@@ -3409,7 +3755,7 @@ $wgUpdateRowsPerJob = 500;
/**
* Number of rows to update per query
*/
-$wgUpdateRowsPerQuery = 10;
+$wgUpdateRowsPerQuery = 100;
/**
* Enable AJAX framework
@@ -3435,7 +3781,7 @@ $wgAjaxWatch = true;
$wgAjaxUploadDestCheck = true;
/**
- * Enable previewing licences via AJAX
+ * Enable previewing licences via AJAX. Also requires $wgEnableAPI to be true.
*/
$wgAjaxLicensePreview = true;
@@ -3495,9 +3841,9 @@ $wgMaxShellFileSize = 102400;
$wgMaxShellTime = 180;
/**
-* Executable name of PHP cli client (php/php5)
-*/
-$wgPhpCli = 'php';
+ * Executable path of the PHP cli binary (php/php5). Should be set up on install.
+ */
+$wgPhpCli = '/usr/bin/php';
/**
* DJVU settings
@@ -3515,6 +3861,13 @@ $wgDjvuDump = null;
$wgDjvuRenderer = null;
/**
+ * Path of the djvutxt DJVU text extraction utility
+ * Enable this and $wgDjvuDump to enable text layer extraction from djvu files
+ */
+# $wgDjvuTxt = 'djvutxt';
+$wgDjvuTxt = null;
+
+/**
* Path of the djvutoxml executable
* This works like djvudump except much, much slower as of version 3.5.
*
@@ -3582,6 +3935,24 @@ $wgAPIMaxResultSize = 8388608;
$wgAPIMaxUncachedDiffs = 1;
/**
+ * Log file or URL (TCP or UDP) to log API requests to, or false to disable
+ * API request logging
+ */
+$wgAPIRequestLog = false;
+
+/**
+ * Cache the API help text for up to an hour. Disable this during API
+ * debugging and development
+ */
+$wgAPICacheHelp = true;
+
+/**
+ * Set the timeout for the API help text cache. Ignored if $wgAPICacheHelp
+ * is false.
+ */
+$wgAPICacheHelpTimeout = 60*60;
+
+/**
* Parser test suite files to be run by parserTests.php when no specific
* filename is passed to it.
*
@@ -3595,6 +3966,21 @@ $wgParserTestFiles = array(
);
/**
+ * If configured, specifies target CodeReview installation to send test
+ * result data from 'parserTests.php --upload'
+ *
+ * Something like this:
+ * $wgParserTestRemote = array(
+ * 'api-url' => 'http://www.mediawiki.org/w/api.php',
+ * 'repo' => 'MediaWiki',
+ * 'suite' => 'ParserTests',
+ * 'path' => '/trunk/phase3', // not used client-side; for reference
+ * 'secret' => 'qmoicj3mc4mcklmqw', // Shared secret used in HMAC validation
+ * );
+ */
+$wgParserTestRemote = false;
+
+/**
* Break out of framesets. This can be used to prevent external sites from
* framing your site with ads.
*/
@@ -3652,6 +4038,12 @@ $wgParserConf = array(
$wgLinkHolderBatchSize = 1000;
/**
+ * By default MediaWiki does not register links pointing to same server in externallinks dataset,
+ * use this value to override:
+ */
+$wgRegisterInternalExternals = false;
+
+/**
* Hooks that are used for outputting exceptions. Format is:
* $wgExceptionHooks[] = $funcname
* or:
@@ -3661,8 +4053,11 @@ $wgLinkHolderBatchSize = 1000;
$wgExceptionHooks = array();
/**
- * Page property link table invalidation lists. Should only be set by exten-
- * sions.
+ * Page property link table invalidation lists. When a page property
+ * changes, this may require other link tables to be updated (eg
+ * adding __HIDDENCAT__ means the hiddencat tracking category will
+ * have been added, so the categorylinks table needs to be rebuilt).
+ * This array can be added to by extensions.
*/
$wgPagePropLinkInvalidations = array(
'hiddencat' => 'categorylinks',
@@ -3687,7 +4082,7 @@ $wgMaximumMovedPages = 100;
/**
* Fix double redirects after a page move.
- * Tends to conflict with page move vandalism, use only on a private wiki.
+ * Tends to conflict with page move vandalism, use only on a private wiki.
*/
$wgFixDoubleRedirects = false;
@@ -3709,7 +4104,7 @@ $wgMaxRedirects = 1;
* other namespaces cannot be invalidated by this variable.
*/
$wgInvalidRedirectTargets = array( 'Filepath', 'Mypage', 'Mytalk' );
-
+
/**
* Array of namespaces to generate a sitemap for when the
* maintenance/generateSitemap.php script is run, or false if one is to be ge-
@@ -3744,11 +4139,15 @@ $wgEdititis = false;
$wgUniversalEditButton = true;
/**
- * Allow id's that don't conform to HTML4 backward compatibility requirements.
- * This is currently for testing; if all goes well, this option will be removed
- * and the functionality will be enabled universally.
+ * Should we allow a broader set of characters in id attributes, per HTML5? If
+ * not, use only HTML 4-compatible IDs. This option is for testing -- when the
+ * functionality is ready, it will be on by default with no option.
+ *
+ * Currently this appears to work fine in Chrome 4 and 5, Firefox 3.5 and 3.6, IE6
+ * and 8, and Opera 10.50, but it fails in Opera 10.10: Unicode IDs don't seem
+ * to work as anchors. So not quite ready for general use yet.
*/
-$wgEnforceHtmlIds = true;
+$wgExperimentalHtmlIds = false;
/**
* Search form behavior
@@ -3758,6 +4157,28 @@ $wgEnforceHtmlIds = true;
$wgUseTwoButtonsSearchForm = true;
/**
+ * Search form behavior for Vector skin only
+ * true = use an icon search button
+ * false = use Go & Search buttons
+ */
+$wgVectorUseSimpleSearch = false;
+
+/**
+ * Watch and unwatch as an icon rather than a link for Vector skin only
+ * true = use an icon watch/unwatch button
+ * false = use watch/unwatch text link
+ */
+$wgVectorUseIconWatch = false;
+
+/**
+ * Add extra stylesheets for Vector - This is only being used so that we can play around with different options while
+ * keeping our CSS code in the SVN and not having to change the main Vector styles. This will probably go away later on.
+ * null = add no extra styles
+ * array = list of style paths relative to skins/vector/
+ */
+$wgVectorExtraStyles = null;
+
+/**
* Preprocessor caching threshold
*/
$wgPreprocessorCacheThreshold = 1000;
@@ -3791,3 +4212,122 @@ $wgInvalidUsernameCharacters = '@';
* modify the user rights of those users via Special:UserRights
*/
$wgUserrightsInterwikiDelimiter = '@';
+
+/**
+ * Configuration for processing pool control, for use in high-traffic wikis.
+ * An implementation is provided in the PoolCounter extension.
+ *
+ * This configuration array maps pool types to an associative array. The only
+ * defined key in the associative array is "class", which gives the class name.
+ * The remaining elements are passed through to the class as constructor
+ * parameters. Example:
+ *
+ * $wgPoolCounterConf = array( 'Article::view' => array(
+ * 'class' => 'PoolCounter_Client',
+ * ... any extension-specific options...
+ * );
+ */
+$wgPoolCounterConf = null;
+
+/**
+ * Use some particular type of external authentication. The specific
+ * authentication module you use will normally require some extra settings to
+ * be specified.
+ *
+ * null indicates no external authentication is to be used. Otherwise,
+ * $wgExternalAuthType must be the name of a non-abstract class that extends
+ * ExternalUser.
+ *
+ * Core authentication modules can be found in includes/extauth/.
+ */
+$wgExternalAuthType = null;
+
+/**
+ * Configuration for the external authentication. This may include arbitrary
+ * keys that depend on the authentication mechanism. For instance,
+ * authentication against another web app might require that the database login
+ * info be provided. Check the file where your auth mechanism is defined for
+ * info on what to put here.
+ */
+$wgExternalAuthConfig = array();
+
+/**
+ * When should we automatically create local accounts when external accounts
+ * already exist, if using ExternalAuth? Can have three values: 'never',
+ * 'login', 'view'. 'view' requires the external database to support cookies,
+ * and implies 'login'.
+ *
+ * TODO: Implement 'view' (currently behaves like 'login').
+ */
+$wgAutocreatePolicy = 'login';
+
+/**
+ * Policies for how each preference is allowed to be changed, in the presence
+ * of external authentication. The keys are preference keys, e.g., 'password'
+ * or 'emailaddress' (see Preferences.php et al.). The value can be one of the
+ * following:
+ *
+ * - local: Allow changes to this pref through the wiki interface but only
+ * apply them locally (default).
+ * - semiglobal: Allow changes through the wiki interface and try to apply them
+ * to the foreign database, but continue on anyway if that fails.
+ * - global: Allow changes through the wiki interface, but only let them go
+ * through if they successfully update the foreign database.
+ * - message: Allow no local changes for linked accounts; replace the change
+ * form with a message provided by the auth plugin, telling the user how to
+ * change the setting externally (maybe providing a link, etc.). If the auth
+ * plugin provides no message for this preference, hide it entirely.
+ *
+ * Accounts that are not linked to an external account are never affected by
+ * this setting. You may want to look at $wgHiddenPrefs instead.
+ * $wgHiddenPrefs supersedes this option.
+ *
+ * TODO: Implement message, global.
+ */
+$wgAllowPrefChange = array();
+
+
+/**
+ * Settings for incoming cross-site AJAX requests:
+ * Newer browsers support cross-site AJAX when the target resource allows requests
+ * from the origin domain by the Access-Control-Allow-Origin header.
+ * This is currently only used by the API (requests to api.php)
+ * $wgCrossSiteAJAXdomains can be set using a wildcard syntax:
+ *
+ * '*' matches any number of characters
+ * '?' matches any 1 character
+ *
+ * Example:
+ $wgCrossSiteAJAXdomains = array(
+ 'www.mediawiki.org',
+ '*.wikipedia.org',
+ '*.wikimedia.org',
+ '*.wiktionary.org',
+ );
+ *
+ */
+$wgCrossSiteAJAXdomains = array();
+
+/**
+ * Domains that should not be allowed to make AJAX requests,
+ * even if they match one of the domains allowed by $wgCrossSiteAJAXdomains
+ * Uses the same syntax as $wgCrossSiteAJAXdomains
+ */
+
+$wgCrossSiteAJAXdomainExceptions = array();
+
+/**
+ * The minimum amount of memory that MediaWiki "needs"; MediaWiki will try to raise PHP's memory limit if it's below this amount.
+ */
+$wgMemoryLimit = "50M";
+
+/**
+ * To disable file delete/restore temporarily
+ */
+$wgUploadMaintenance = false;
+
+/**
+ * Use old names for change_tags indices.
+ */
+$wgOldChangeTagsIndex = false;
+
diff --git a/includes/Defines.php b/includes/Defines.php
index 8de6c5a1..7be569af 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -18,6 +18,7 @@ define( 'DBO_IGNORE', 4 );
define( 'DBO_TRX', 8 );
define( 'DBO_DEFAULT', 16 );
define( 'DBO_PERSISTENT', 32 );
+define( 'DBO_SYSDBA', 64 ); //for oracle maintenance
/**#@-*/
# Valid database indexes
@@ -102,7 +103,7 @@ define( 'CACHE_ANYTHING', -1 ); // Use anything, as long as it works
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_ACCEL', 3 ); // eAccelerator
define( 'CACHE_DBA', 4 ); // Use PHP's DBA extension to store in a DBM-style database
/**#@-*/
@@ -200,6 +201,7 @@ require_once dirname(__FILE__).'/normal/UtfNormalDefines.php';
# Hook support constants
define( 'MW_SUPPORTS_EDITFILTERMERGED', 1 );
define( 'MW_SUPPORTS_PARSERFIRSTCALLINIT', 1 );
+define( 'MW_SUPPORTS_LOCALISATIONCACHE', 1 );
# Allowed values for Parser::$mOutputType
# Parameter to Parser::startExternalParse().
@@ -227,3 +229,4 @@ define( 'APCOND_INGROUPS', 4 );
define( 'APCOND_ISIP', 5 );
define( 'APCOND_IPINRANGE', 6 );
define( 'APCOND_AGE_FROM_EDIT', 7 );
+define( 'APCOND_BLOCKED', 8 );
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index 8e7caf63..75df0fd5 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -224,7 +224,7 @@ class DjVuImage {
* @return string
*/
function retrieveMetaData() {
- global $wgDjvuToXML, $wgDjvuDump;
+ global $wgDjvuToXML, $wgDjvuDump, $wgDjvuTxt;
if ( isset( $wgDjvuDump ) ) {
# djvudump is faster as of version 3.5
# http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
@@ -242,6 +242,30 @@ class DjVuImage {
} else {
$xml = null;
}
+ # Text layer
+ if ( isset( $wgDjvuTxt ) ) {
+ wfProfileIn( 'djvutxt' );
+ $cmd = wfEscapeShellArg( $wgDjvuTxt ) . ' --detail=page ' . wfEscapeShellArg( $this->mFilename ) ;
+ wfDebug( __METHOD__.": $cmd\n" );
+ $txt = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'djvutxt' );
+ if( $retval == 0) {
+ # Get rid of invalid UTF-8, strip control characters
+ if( is_callable( 'iconv' ) ) {
+ wfSuppressWarnings();
+ $txt = iconv( "UTF-8","UTF-8//IGNORE", $txt );
+ wfRestoreWarnings();
+ } else {
+ $txt = UtfNormal::cleanUp( $txt );
+ }
+ $txt = preg_replace( "/[\013\035\037]/", "", $txt );
+ $txt = htmlspecialchars($txt);
+ $txt = preg_replace( "/\((page\s[\d-]*\s[\d-]*\s[\d-]*\s[\d-]*\s*\&quot;([^<]*?)\&quot;\s*|)\)/s", "<PAGE value=\"$2\" />", $txt );
+ $txt = "<DjVuTxt>\n<HEAD></HEAD>\n<BODY>\n" . $txt . "</BODY>\n</DjVuTxt>\n";
+ $xml = preg_replace( "/<DjVuXML>/", "<mw-djvu><DjVuXML>", $xml );
+ $xml = $xml . $txt. '</mw-djvu>' ;
+ }
+ }
return $xml;
}
diff --git a/includes/DoubleRedirectJob.php b/includes/DoubleRedirectJob.php
index 889beecf..0857408a 100644
--- a/includes/DoubleRedirectJob.php
+++ b/includes/DoubleRedirectJob.php
@@ -1,13 +1,19 @@
<?php
+/**
+ * Job to fix double redirects after moving a page
+ *
+ * @ingroup JobQueue
+ */
class DoubleRedirectJob extends Job {
var $reason, $redirTitle, $destTitleText;
static $user;
/**
* Insert jobs into the job queue to fix redirects to the given title
- * @param string $type The reason for the fix, see message double-redirect-fixed-<reason>
- * @param Title $redirTitle The title which has changed, redirects pointing to this title are fixed
+ * @param $reason String: the reason for the fix, see message double-redirect-fixed-<reason>
+ * @param $redirTitle Title: the title which has changed, redirects pointing to this title are fixed
+ * @param $destTitle Not used
*/
public static function fixRedirects( $reason, $redirTitle, $destTitle = false ) {
# Need to use the master to get the redirect table updated in the same transaction
@@ -116,7 +122,7 @@ class DoubleRedirectJob extends Job {
/**
* Get the final destination of a redirect
- * Returns false if the specified title is not a redirect, or if it is a circular redirect
+ * @return false if the specified title is not a redirect, or if it is a circular redirect
*/
public static function getFinalDestination( $title ) {
$dbw = wfGetDB( DB_MASTER );
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 3589b52d..b4cbf0de 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -17,37 +17,38 @@
* usually the same, but they are now allowed to be different.
*/
class EditPage {
- const AS_SUCCESS_UPDATE = 200;
- const AS_SUCCESS_NEW_ARTICLE = 201;
- const AS_HOOK_ERROR = 210;
- const AS_FILTERING = 211;
- const AS_HOOK_ERROR_EXPECTED = 212;
- const AS_BLOCKED_PAGE_FOR_USER = 215;
- const AS_CONTENT_TOO_BIG = 216;
- const AS_USER_CANNOT_EDIT = 217;
- const AS_READ_ONLY_PAGE_ANON = 218;
- const AS_READ_ONLY_PAGE_LOGGED = 219;
- const AS_READ_ONLY_PAGE = 220;
- const AS_RATE_LIMITED = 221;
- const AS_ARTICLE_WAS_DELETED = 222;
- const AS_NO_CREATE_PERMISSION = 223;
- const AS_BLANK_ARTICLE = 224;
- const AS_CONFLICT_DETECTED = 225;
- const AS_SUMMARY_NEEDED = 226;
- const AS_TEXTBOX_EMPTY = 228;
- const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229;
- const AS_OK = 230;
- const AS_END = 231;
- const AS_SPAM_ERROR = 232;
- const AS_IMAGE_REDIRECT_ANON = 233;
- const AS_IMAGE_REDIRECT_LOGGED = 234;
+ const AS_SUCCESS_UPDATE = 200;
+ const AS_SUCCESS_NEW_ARTICLE = 201;
+ const AS_HOOK_ERROR = 210;
+ const AS_FILTERING = 211;
+ const AS_HOOK_ERROR_EXPECTED = 212;
+ const AS_BLOCKED_PAGE_FOR_USER = 215;
+ const AS_CONTENT_TOO_BIG = 216;
+ const AS_USER_CANNOT_EDIT = 217;
+ const AS_READ_ONLY_PAGE_ANON = 218;
+ const AS_READ_ONLY_PAGE_LOGGED = 219;
+ const AS_READ_ONLY_PAGE = 220;
+ const AS_RATE_LIMITED = 221;
+ const AS_ARTICLE_WAS_DELETED = 222;
+ const AS_NO_CREATE_PERMISSION = 223;
+ const AS_BLANK_ARTICLE = 224;
+ const AS_CONFLICT_DETECTED = 225;
+ const AS_SUMMARY_NEEDED = 226;
+ const AS_TEXTBOX_EMPTY = 228;
+ const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229;
+ const AS_OK = 230;
+ const AS_END = 231;
+ const AS_SPAM_ERROR = 232;
+ const AS_IMAGE_REDIRECT_ANON = 233;
+ const AS_IMAGE_REDIRECT_LOGGED = 234;
var $mArticle;
var $mTitle;
var $action;
- var $mMetaData = '';
var $isConflict = false;
var $isCssJsSubpage = false;
+ var $isCssSubpage = false;
+ var $isJsSubpage = false;
var $deletedSinceEdit = false;
var $formtype;
var $firsttime;
@@ -65,13 +66,14 @@ class EditPage {
#var $mPreviewTemplates;
var $mParserOutput;
var $mBaseRevision = false;
+ var $mShowSummaryField = true;
# Form values
var $save = false, $preview = false, $diff = false;
var $minoredit = false, $watchthis = false, $recreate = false;
- var $textbox1 = '', $textbox2 = '', $summary = '';
+ var $textbox1 = '', $textbox2 = '', $summary = '', $nosummary = false;
var $edittime = '', $section = '', $starttime = '';
- var $oldid = 0, $editintro = '', $scrolltop = null;
+ var $oldid = 0, $editintro = '', $scrolltop = null, $bot = true;
# Placeholders for text injection by hooks (must be HTML)
# extensions should take care to _append_ to the present value
@@ -81,6 +83,8 @@ class EditPage {
public $editFormTextAfterWarn;
public $editFormTextAfterTools;
public $editFormTextBottom;
+ public $editFormTextAfterContent;
+ public $previewTextAfterContent;
/* $didSave should be set to true whenever an article was succesfully altered. */
public $didSave = false;
@@ -103,15 +107,20 @@ class EditPage {
$this->editFormTextBeforeContent =
$this->editFormTextAfterWarn =
$this->editFormTextAfterTools =
- $this->editFormTextBottom = "";
+ $this->editFormTextBottom =
+ $this->editFormTextAfterContent =
+ $this->previewTextAfterContent =
+ $this->mPreloadText = "";
}
-
+
function getArticle() {
return $this->mArticle;
}
+
/**
* Fetch initial editing page content.
+ * @returns mixed string on success, $def_text for invalid sections
* @private
*/
function getContent( $def_text = '' ) {
@@ -120,7 +129,10 @@ class EditPage {
wfProfileIn( __METHOD__ );
# Get variables from query string :P
$section = $wgRequest->getVal( 'section' );
- $preload = $wgRequest->getVal( 'preload' );
+
+ $preload = $wgRequest->getVal( 'preload',
+ // Custom preload text for new sections
+ $section === 'new' ? 'MediaWiki:addsection-preload' : '' );
$undoafter = $wgRequest->getVal( 'undoafter' );
$undo = $wgRequest->getVal( 'undo' );
@@ -134,7 +146,7 @@ class EditPage {
$wgMessageCache->loadAllMessages( $lang );
$text = wfMsgGetKey( $message, false, $lang, false );
if( wfEmptyMsg( $message, $text ) )
- $text = '';
+ $text = $this->getPreloadedText( $preload );
} else {
# If requested, preload some text.
$text = $this->getPreloadedText( $preload );
@@ -150,10 +162,10 @@ class EditPage {
# Undoing a specific edit overrides section editing; section-editing
# doesn't work with undoing.
if ( $undoafter ) {
- $undorev = Revision::newFromId($undo);
- $oldrev = Revision::newFromId($undoafter);
+ $undorev = Revision::newFromId( $undo );
+ $oldrev = Revision::newFromId( $undoafter );
} else {
- $undorev = Revision::newFromId($undo);
+ $undorev = Revision::newFromId( $undo );
$oldrev = $undorev ? $undorev->getPrevious() : null;
}
@@ -165,7 +177,7 @@ class EditPage {
$undorev->getPage() == $this->mArticle->getID() &&
!$undorev->isDeleted( Revision::DELETED_TEXT ) &&
!$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
-
+
$undotext = $this->mArticle->getUndoText( $undorev, $oldrev );
if ( $undotext === false ) {
# Warn the user that something went wrong
@@ -192,6 +204,7 @@ class EditPage {
if ( $section == 'new' ) {
$text = $this->getPreloadedText( $preload );
} else {
+ // Get section edit text (returns $def_text for invalid sections)
$text = $wgParser->getSection( $text, $section, $def_text );
}
}
@@ -201,6 +214,11 @@ class EditPage {
return $text;
}
+ /** Use this method before edit() to preload some text into the edit box */
+ public function setPreloadedText( $text ) {
+ $this->mPreloadText = $text;
+ }
+
/**
* Get the contents of a page from its title and remove includeonly tags
*
@@ -208,12 +226,14 @@ class EditPage {
* @return string The contents of the page.
*/
protected function getPreloadedText( $preload ) {
- if ( $preload === '' ) {
+ if ( !empty( $this->mPreloadText ) ) {
+ return $this->mPreloadText;
+ } elseif ( $preload === '' ) {
return '';
} else {
$preloadTitle = Title::newFromText( $preload );
if ( isset( $preloadTitle ) && $preloadTitle->userCanRead() ) {
- $rev = Revision::newFromTitle($preloadTitle);
+ $rev = Revision::newFromTitle( $preloadTitle );
if ( is_object( $rev ) ) {
$text = $rev->getText();
// TODO FIXME: AAAAAAAAAAA, this shouldn't be implementing
@@ -226,96 +246,7 @@ class EditPage {
}
}
- /**
- * This is the function that extracts metadata from the article body on the first view.
- * To turn the feature on, set $wgUseMetadataEdit = true ; in LocalSettings
- * and set $wgMetadataWhitelist to the *full* title of the template whitelist
- */
- function extractMetaDataFromArticle () {
- global $wgUseMetadataEdit, $wgMetadataWhitelist, $wgContLang;
- $this->mMetaData = '';
- if ( !$wgUseMetadataEdit ) return;
- if ( $wgMetadataWhitelist == '' ) return;
- $s = '';
- $t = $this->getContent();
-
- # MISSING : <nowiki> filtering
-
- # Categories and language links
- $t = explode ( "\n" , $t );
- $catlow = strtolower ( $wgContLang->getNsText( NS_CATEGORY ) );
- $cat = $ll = array();
- foreach ( $t AS $key => $x ) {
- $y = trim ( strtolower ( $x ) );
- while ( substr ( $y , 0 , 2 ) == '[[' ) {
- $y = explode ( ']]' , trim ( $x ) );
- $first = array_shift ( $y );
- $first = explode ( ':' , $first );
- $ns = array_shift ( $first );
- $ns = trim ( str_replace ( '[' , '' , $ns ) );
- if ( $wgContLang->getLanguageName( $ns ) || strtolower ( $ns ) == $catlow ) {
- $add = '[[' . $ns . ':' . implode ( ':' , $first ) . ']]';
- if ( strtolower ( $ns ) == $catlow ) $cat[] = $add;
- else $ll[] = $add;
- $x = implode ( ']]' , $y );
- $t[$key] = $x;
- $y = trim ( strtolower ( $x ) );
- } else {
- $x = implode ( ']]' , $y );
- $y = trim ( strtolower ( $x ) );
- }
- }
- }
- if ( count ( $cat ) ) $s .= implode ( ' ' , $cat ) . "\n";
- if ( count ( $ll ) ) $s .= implode ( ' ' , $ll ) . "\n";
- $t = implode ( "\n" , $t );
-
- # Load whitelist
- $sat = array () ; # stand-alone-templates; must be lowercase
- $wl_title = Title::newFromText ( $wgMetadataWhitelist );
- $wl_article = new Article ( $wl_title );
- $wl = explode ( "\n" , $wl_article->getContent() );
- foreach ( $wl AS $x ) {
- $isentry = false;
- $x = trim ( $x );
- while ( substr ( $x , 0 , 1 ) == '*' ) {
- $isentry = true;
- $x = trim ( substr ( $x , 1 ) );
- }
- if ( $isentry ) {
- $sat[] = strtolower ( $x );
- }
-
- }
-
- # Templates, but only some
- $t = explode ( '{{' , $t );
- $tl = array () ;
- foreach ( $t AS $key => $x ) {
- $y = explode ( '}}' , $x , 2 );
- if ( count ( $y ) == 2 ) {
- $z = $y[0];
- $z = explode ( '|' , $z );
- $tn = array_shift ( $z );
- if ( in_array ( strtolower ( $tn ) , $sat ) ) {
- $tl[] = '{{' . $y[0] . '}}';
- $t[$key] = $y[1];
- $y = explode ( '}}' , $y[1] , 2 );
- }
- else $t[$key] = '{{' . $x;
- }
- else if ( $key != 0 ) $t[$key] = '{{' . $x;
- else $t[$key] = $x;
- }
- if ( count ( $tl ) ) $s .= implode ( ' ' , $tl );
- $t = implode ( '' , $t );
-
- $t = str_replace ( "\n\n\n" , "\n" , $t );
- $this->mArticle->mContent = $t;
- $this->mMetaData = $s;
- }
-
- /*
+ /*
* Check if a page was deleted while the user was editing it, before submit.
* Note that we rely on the logging table, which hasn't been always there,
* but that doesn't matter, because this only applies to brand new
@@ -352,9 +283,9 @@ class EditPage {
* the newly-edited page.
*/
function edit() {
- global $wgOut, $wgUser, $wgRequest;
+ global $wgOut, $wgRequest, $wgUser;
// Allow extensions to modify/prevent this form or submission
- if ( !wfRunHooks( 'AlternateEdit', array( &$this ) ) ) {
+ if ( !wfRunHooks( 'AlternateEdit', array( $this ) ) ) {
return;
}
@@ -374,16 +305,24 @@ class EditPage {
}
if ( wfReadOnly() && $this->save ) {
- // Force preview
- $this->save = false;
- $this->preview = true;
+ // Force preview
+ $this->save = false;
+ $this->preview = true;
}
$wgOut->addScriptFile( 'edit.js' );
+
+ if ( $wgUser->getOption( 'uselivepreview', false ) ) {
+ $wgOut->includeJQuery();
+ $wgOut->addScriptFile( 'preview.js' );
+ }
+ // Bug #19334: textarea jumps when editing articles in IE8
+ $wgOut->addStyle( 'common/IE80Fixes.css', 'screen', 'IE 8' );
+
$permErrors = $this->getEditPermissionErrors();
if ( $permErrors ) {
- wfDebug( __METHOD__.": User can't edit\n" );
- $this->readOnlyPage( $this->getContent(), true, $permErrors, 'edit' );
+ wfDebug( __METHOD__ . ": User can't edit\n" );
+ $this->readOnlyPage( $this->getContent( false ), true, $permErrors, 'edit' );
wfProfileOut( __METHOD__ );
return;
} else {
@@ -398,12 +337,11 @@ class EditPage {
if ( $this->previewOnOpen() ) {
$this->formtype = 'preview';
} else {
- $this->extractMetaDataFromArticle () ;
$this->formtype = 'initial';
}
}
}
-
+
// If they used redlink=1 and the page exists, redirect to the main article
if ( $wgRequest->getBool( 'redlink' ) && $this->mTitle->exists() ) {
$wgOut->redirect( $this->mTitle->getFullURL() );
@@ -414,6 +352,8 @@ class EditPage {
$this->isConflict = false;
// css / js subpages of user pages get a special treatment
$this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
+ $this->isCssSubpage = $this->mTitle->isCssSubpage();
+ $this->isJsSubpage = $this->mTitle->isJsSubpage();
$this->isValidCssJsSubpage = $this->mTitle->isValidCssJsSubpage();
# Show applicable editing introductions
@@ -456,7 +396,7 @@ class EditPage {
# First time through: get contents, set time for conflict
# checking, etc.
if ( 'initial' == $this->formtype || $this->firsttime ) {
- if ( $this->initialiseForm() === false) {
+ if ( $this->initialiseForm() === false ) {
$this->noSuchSectionPage();
wfProfileOut( __METHOD__."-business-end" );
wfProfileOut( __METHOD__ );
@@ -464,13 +404,15 @@ class EditPage {
}
if ( !$this->mTitle->getArticleId() )
wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
+ else
+ wfRunHooks( 'EditFormInitialText', array( $this ) );
}
$this->showEditForm();
wfProfileOut( __METHOD__."-business-end" );
wfProfileOut( __METHOD__ );
}
-
+
protected function getEditPermissionErrors() {
global $wgUser;
$permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
@@ -482,8 +424,8 @@ class EditPage {
# Ignore some permissions errors when a user is just previewing/viewing diffs
$remove = array();
foreach( $permErrors as $error ) {
- if ( ($this->preview || $this->diff) &&
- ($error[0] == 'blockedtext' || $error[0] == 'autoblockedtext') )
+ if ( ( $this->preview || $this->diff ) &&
+ ( $error[0] == 'blockedtext' || $error[0] == 'autoblockedtext' ) )
{
$remove[] = $error;
}
@@ -515,7 +457,7 @@ class EditPage {
* @return bool
*/
protected function previewOnOpen() {
- global $wgRequest, $wgUser;
+ global $wgRequest, $wgUser, $wgPreviewOnOpenNamespaces;
if ( $wgRequest->getVal( 'preview' ) == 'yes' ) {
// Explicit override from request
return true;
@@ -528,7 +470,10 @@ class EditPage {
} elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
// Standard preference behaviour
return true;
- } elseif ( !$this->mTitle->exists() && $this->mTitle->getNamespace() == NS_CATEGORY ) {
+ } elseif ( !$this->mTitle->exists() &&
+ isset($wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]) &&
+ $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] )
+ {
// Categories are special
return true;
} else {
@@ -537,13 +482,36 @@ class EditPage {
}
/**
+ * Does this EditPage class support section editing?
+ * This is used by EditPage subclasses to indicate their ui cannot handle section edits
+ *
+ * @return bool
+ */
+ protected function isSectionEditSupported() {
+ return true;
+ }
+
+ /**
+ * Returns the URL to use in the form's action attribute.
+ * This is used by EditPage subclasses when simply customizing the action
+ * variable in the constructor is not enough. This can be used when the
+ * EditPage lives inside of a Special page rather than a custom page action.
+ *
+ * @param Title $title The title for which is being edited (where we go to for &action= links)
+ * @return string
+ */
+ protected function getActionURL( Title $title ) {
+ return $title->getLocalURL( array( 'action' => $this->action ) );
+ }
+
+ /**
* @todo document
* @param $request
*/
function importFormData( &$request ) {
global $wgLang, $wgUser;
- $fname = 'EditPage::importFormData';
- wfProfileIn( $fname );
+
+ wfProfileIn( __METHOD__ );
# Section edit can come from either the form or a link
$this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
@@ -553,8 +521,17 @@ class EditPage {
# Also remove trailing whitespace, but don't remove _initial_
# whitespace from the text boxes. This may be significant formatting.
$this->textbox1 = $this->safeUnicodeInput( $request, 'wpTextbox1' );
- $this->textbox2 = $this->safeUnicodeInput( $request, 'wpTextbox2' );
- $this->mMetaData = rtrim( $request->getText( 'metadata' ) );
+ if ( !$request->getCheck('wpTextbox2') ) {
+ // Skip this if wpTextbox2 has input, it indicates that we came
+ // from a conflict page with raw page text, not a custom form
+ // modified by subclasses
+ wfProfileIn( get_class($this)."::importContentFormData" );
+ $textbox1 = $this->importContentFormData( $request );
+ if ( isset($textbox1) )
+ $this->textbox1 = $textbox1;
+ wfProfileOut( get_class($this)."::importContentFormData" );
+ }
+
# Truncate for whole multibyte characters. +5 bytes for ellipsis
$this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250, '' );
@@ -568,7 +545,7 @@ class EditPage {
if ( is_null( $this->edittime ) ) {
# If the form is incomplete, force to preview.
- wfDebug( "$fname: Form data appears to be incomplete\n" );
+ wfDebug( __METHOD__ . ": Form data appears to be incomplete\n" );
wfDebug( "POST DATA: " . var_export( $_POST, true ) . "\n" );
$this->preview = true;
} else {
@@ -585,23 +562,23 @@ class EditPage {
# if the user hits enter in the comment box.
# The unmarked state will be assumed to be a save,
# if the form seems otherwise complete.
- wfDebug( "$fname: Passed token check.\n" );
+ wfDebug( __METHOD__ . ": Passed token check.\n" );
} else if ( $this->diff ) {
# Failed token check, but only requested "Show Changes".
- wfDebug( "$fname: Failed token check; Show Changes requested.\n" );
+ wfDebug( __METHOD__ . ": Failed token check; Show Changes requested.\n" );
} else {
# Page might be a hack attempt posted from
# an external site. Preview instead of saving.
- wfDebug( "$fname: Failed token check; forcing preview\n" );
+ wfDebug( __METHOD__ . ": Failed token check; forcing preview\n" );
$this->preview = true;
}
}
$this->save = !$this->preview && !$this->diff;
- if ( !preg_match( '/^\d{14}$/', $this->edittime )) {
+ if ( !preg_match( '/^\d{14}$/', $this->edittime ) ) {
$this->edittime = null;
}
- if ( !preg_match( '/^\d{14}$/', $this->starttime )) {
+ if ( !preg_match( '/^\d{14}$/', $this->starttime ) ) {
$this->starttime = null;
}
@@ -611,8 +588,8 @@ class EditPage {
$this->watchthis = $request->getCheck( 'wpWatchthis' );
# Don't force edit summaries when a user is editing their own user or talk page
- if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) &&
- $this->mTitle->getText() == $wgUser->getName() )
+ if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) &&
+ $this->mTitle->getText() == $wgUser->getName() )
{
$this->allowBlankSummary = true;
} else {
@@ -622,10 +599,8 @@ class EditPage {
$this->autoSumm = $request->getText( 'wpAutoSummary' );
} else {
# Not a posted form? Start with nothing.
- wfDebug( "$fname: Not a posted form.\n" );
+ wfDebug( __METHOD__ . ": Not a posted form.\n" );
$this->textbox1 = '';
- $this->textbox2 = '';
- $this->mMetaData = '';
$this->summary = '';
$this->edittime = '';
$this->starttime = wfTimestampNow();
@@ -634,7 +609,7 @@ class EditPage {
$this->save = false;
$this->diff = false;
$this->minoredit = false;
- $this->watchthis = false;
+ $this->watchthis = $request->getBool( 'watchthis', false ); // Watch may be overriden by request parameters
$this->recreate = false;
if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
@@ -643,18 +618,39 @@ class EditPage {
elseif ( $this->section != 'new' && $request->getVal( 'summary' ) ) {
$this->summary = $request->getText( 'summary' );
}
-
+
if ( $request->getVal( 'minor' ) ) {
$this->minoredit = true;
}
}
+ $this->bot = $request->getBool( 'bot', true );
+ $this->nosummary = $request->getBool( 'nosummary' );
+
+ // FIXME: unused variable?
$this->oldid = $request->getInt( 'oldid' );
$this->live = $request->getCheck( 'live' );
- $this->editintro = $request->getText( 'editintro' );
+ $this->editintro = $request->getText( 'editintro',
+ // Custom edit intro for new sections
+ $this->section === 'new' ? 'MediaWiki:addsection-editintro' : '' );
+
+ wfProfileOut( __METHOD__ );
- wfProfileOut( $fname );
+ // Allow extensions to modify form data
+ wfRunHooks( 'EditPage::importFormData', array( $this, $request ) );
+ }
+
+ /**
+ * Subpage overridable method for extracting the page content data from the
+ * posted form to be placed in $this->textbox1, if using customized input
+ * this method should be overrided and return the page text that will be used
+ * for saving, preview parsing and so on...
+ *
+ * @praram WebRequest $request
+ */
+ protected function importContentFormData( &$request ) {
+ return; // Don't do anything, EditPage already extracted wpTextbox1
}
/**
@@ -688,28 +684,49 @@ class EditPage {
$wgOut->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1</div>", 'editinginterface' );
}
- # Show a warning message when someone creates/edits a user (talk) page but the user does not exists
+ # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
+ # Show log extract when the user is currently blocked
if ( $namespace == NS_USER || $namespace == NS_USER_TALK ) {
$parts = explode( '/', $this->mTitle->getText(), 2 );
$username = $parts[0];
- $id = User::idFromName( $username );
+ $user = User::newFromName( $username, false /* allow IP users*/ );
$ip = User::isIP( $username );
- if ( $id == 0 && !$ip ) {
- $wgOut->wrapWikiMsg( '<div class="mw-userpage-userdoesnotexist error">$1</div>',
+ if ( !$user->isLoggedIn() && !$ip ) { # User does not exist
+ $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1</div>",
array( 'userpage-userdoesnotexist', $username ) );
+ } else if ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
+ LogEventsList::showLogExtract(
+ $wgOut,
+ 'block',
+ $user->getUserPage()->getPrefixedText(),
+ '',
+ array(
+ 'lim' => 1,
+ 'showIfEmpty' => false,
+ 'msgKey' => array(
+ 'blocked-notice-logextract',
+ $user->getName() # Support GENDER in notice
+ )
+ )
+ );
}
}
# Try to add a custom edit intro, or use the standard one if this is not possible.
if ( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
if ( $wgUser->isLoggedIn() ) {
- $wgOut->wrapWikiMsg( '<div class="mw-newarticletext">$1</div>', 'newarticletext' );
+ $wgOut->wrapWikiMsg( "<div class=\"mw-newarticletext\">\n$1</div>", 'newarticletext' );
} else {
- $wgOut->wrapWikiMsg( '<div class="mw-newarticletextanon">$1</div>', 'newarticletextanon' );
+ $wgOut->wrapWikiMsg( "<div class=\"mw-newarticletextanon\">\n$1</div>", 'newarticletextanon' );
}
}
- # Give a notice if the user is editing a deleted page...
+ # Give a notice if the user is editing a deleted/moved page...
if ( !$this->mTitle->exists() ) {
- $this->showDeletionLog( $wgOut );
+ LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle->getPrefixedText(),
+ '', array( 'lim' => 10,
+ 'conds' => array( "log_action != 'revision'" ),
+ 'showIfEmpty' => false,
+ 'msgKey' => array( 'recreate-moveddeleted-warn') )
+ );
}
}
@@ -742,12 +759,10 @@ class EditPage {
global $wgFilterCallback, $wgUser, $wgOut, $wgParser;
global $wgMaxArticleSize;
- $fname = 'EditPage::attemptSave';
- wfProfileIn( $fname );
- wfProfileIn( "$fname-checks" );
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-checks' );
- if ( !wfRunHooks( 'EditPage::attemptSave', array( &$this ) ) )
- {
+ if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) {
wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" );
return self::AS_HOOK_ERROR;
}
@@ -763,10 +778,6 @@ class EditPage {
}
}
- # Reintegrate metadata
- if ( $this->mMetaData != '' ) $this->textbox1 .= "\n" . $this->mMetaData ;
- $this->mMetaData = '' ;
-
# Check for spam
$match = self::matchSummarySpamRegex( $this->summary );
if ( $match === false ) {
@@ -778,104 +789,107 @@ class EditPage {
$pdbk = $this->mTitle->getPrefixedDBkey();
$match = str_replace( "\n", '', $match );
wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" );
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_SPAM_ERROR;
}
if ( $wgFilterCallback && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary ) ) {
# Error messages or other handling should be performed by the filter function
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_FILTERING;
}
if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) ) ) {
# Error messages etc. could be handled within the hook...
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_HOOK_ERROR;
} elseif ( $this->hookError != '' ) {
# ...or the hook could be expecting us to produce an error
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_HOOK_ERROR_EXPECTED;
}
if ( $wgUser->isBlockedFrom( $this->mTitle, false ) ) {
# Check block state against master, thus 'false'.
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_BLOCKED_PAGE_FOR_USER;
}
- $this->kblength = (int)(strlen( $this->textbox1 ) / 1024);
+ $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
if ( $this->kblength > $wgMaxArticleSize ) {
// Error will be displayed by showEditForm()
$this->tooBig = true;
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_CONTENT_TOO_BIG;
}
- if ( !$wgUser->isAllowed('edit') ) {
+ if ( !$wgUser->isAllowed( 'edit' ) ) {
if ( $wgUser->isAnon() ) {
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_READ_ONLY_PAGE_ANON;
- }
- else {
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ } else {
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_READ_ONLY_PAGE_LOGGED;
}
}
if ( wfReadOnly() ) {
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_READ_ONLY_PAGE;
}
if ( $wgUser->pingLimiter() ) {
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_RATE_LIMITED;
}
# If the article has been deleted while editing, don't save it without
# confirmation
if ( $this->wasDeletedSinceLastEdit() && !$this->recreate ) {
- wfProfileOut( "$fname-checks" );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
return self::AS_ARTICLE_WAS_DELETED;
}
- wfProfileOut( "$fname-checks" );
+ wfProfileOut( __METHOD__ . '-checks' );
# If article is new, insert it.
$aid = $this->mTitle->getArticleID( GAID_FOR_UPDATE );
if ( 0 == $aid ) {
// Late check for create permission, just in case *PARANOIA*
if ( !$this->mTitle->userCan( 'create' ) ) {
- wfDebug( "$fname: no create permission\n" );
- wfProfileOut( $fname );
+ wfDebug( __METHOD__ . ": no create permission\n" );
+ wfProfileOut( __METHOD__ );
return self::AS_NO_CREATE_PERMISSION;
}
# Don't save a new article if it's blank.
- if ( '' == $this->textbox1 ) {
- wfProfileOut( $fname );
+ if ( $this->textbox1 == '' ) {
+ wfProfileOut( __METHOD__ );
return self::AS_BLANK_ARTICLE;
}
// Run post-section-merge edit filter
if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError, $this->summary ) ) ) {
# Error messages etc. could be handled within the hook...
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_HOOK_ERROR;
+ } elseif ( $this->hookError != '' ) {
+ # ...or the hook could be expecting us to produce an error
+ wfProfileOut( __METHOD__ );
+ return self::AS_HOOK_ERROR_EXPECTED;
}
-
+
# Handle the user preference to force summaries here. Check if it's not a redirect.
if ( !$this->allowBlankSummary && !Title::newFromRedirect( $this->textbox1 ) ) {
if ( md5( $this->summary ) == $this->autoSumm ) {
$this->missingSummary = true;
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_SUMMARY_NEEDED;
}
}
@@ -885,7 +899,7 @@ class EditPage {
$this->mArticle->insertNewArticle( $this->textbox1, $this->summary,
$this->minoredit, $this->watchthis, false, $isComment, $bot );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_SUCCESS_NEW_ARTICLE;
}
@@ -894,7 +908,7 @@ class EditPage {
$this->mArticle->clear(); # Force reload of dates, etc.
$this->mArticle->forUpdate( true ); # Lock the article
- wfDebug("timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n");
+ wfDebug( "timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n" );
if ( $this->mArticle->getTimestamp() != $this->edittime ) {
$this->isConflict = true;
@@ -904,32 +918,32 @@ class EditPage {
// Probably a duplicate submission of a new comment.
// This can happen when squid resends a request after
// a timeout but the first one actually went through.
- wfDebug( "EditPage::editForm duplicate new section submission; trigger edit conflict!\n" );
+ wfDebug( __METHOD__ . ": duplicate new section submission; trigger edit conflict!\n" );
} else {
// New comment; suppress conflict.
$this->isConflict = false;
- wfDebug( "EditPage::editForm conflict suppressed; new section\n" );
+ wfDebug( __METHOD__ .": conflict suppressed; new section\n" );
}
}
}
$userid = $wgUser->getId();
-
+
# Suppress edit conflict with self, except for section edits where merging is required.
- if ( $this->isConflict && $this->section == '' && $this->userWasLastToEdit($userid,$this->edittime) ) {
- wfDebug( "EditPage::editForm Suppressing edit conflict, same user.\n" );
+ if ( $this->isConflict && $this->section == '' && $this->userWasLastToEdit( $userid, $this->edittime ) ) {
+ wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" );
$this->isConflict = false;
}
if ( $this->isConflict ) {
- wfDebug( "EditPage::editForm conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
+ wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
$this->mArticle->getTimestamp() . "')\n" );
$text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime );
} else {
- wfDebug( "EditPage::editForm getting section '$this->section'\n" );
+ wfDebug( __METHOD__ . ": getting section '$this->section'\n" );
$text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary );
}
if ( is_null( $text ) ) {
- wfDebug( "EditPage::editForm activating conflict; section replace failed.\n" );
+ wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" );
$this->isConflict = true;
$text = $this->textbox1; // do not try to merge here!
} else if ( $this->isConflict ) {
@@ -937,16 +951,16 @@ class EditPage {
if ( $this->mergeChangesInto( $text ) ) {
// Successful merge! Maybe we should tell the user the good news?
$this->isConflict = false;
- wfDebug( "EditPage::editForm Suppressing edit conflict, successful merge.\n" );
+ wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" );
} else {
$this->section = '';
$this->textbox1 = $text;
- wfDebug( "EditPage::editForm Keeping edit conflict, failed merge.\n" );
+ wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" );
}
}
if ( $this->isConflict ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_CONFLICT_DETECTED;
}
@@ -955,36 +969,42 @@ class EditPage {
// Run post-section-merge edit filter
if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError, $this->summary ) ) ) {
# Error messages etc. could be handled within the hook...
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_HOOK_ERROR;
+ } elseif ( $this->hookError != '' ) {
+ # ...or the hook could be expecting us to produce an error
+ wfProfileOut( __METHOD__ );
+ return self::AS_HOOK_ERROR_EXPECTED;
}
# Handle the user preference to force summaries here, but not for null edits
- if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp($oldtext,$text)
+ if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp( $oldtext, $text )
&& !Title::newFromRedirect( $text ) ) # check if it's not a redirect
{
if ( md5( $this->summary ) == $this->autoSumm ) {
$this->missingSummary = true;
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_SUMMARY_NEEDED;
}
}
# And a similar thing for new sections
if ( $this->section == 'new' && !$this->allowBlankSummary ) {
- if (trim($this->summary) == '') {
+ if ( trim( $this->summary ) == '' ) {
$this->missingSummary = true;
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_SUMMARY_NEEDED;
}
}
# All's well
- wfProfileIn( "$fname-sectionanchor" );
+ wfProfileIn( __METHOD__ . '-sectionanchor' );
$sectionanchor = '';
if ( $this->section == 'new' ) {
if ( $this->textbox1 == '' ) {
$this->missingComment = true;
+ wfProfileOut( __METHOD__ . '-sectionanchor' );
+ wfProfileOut( __METHOD__ );
return self::AS_TEXTBOX_EMPTY;
}
if ( $this->summary != '' ) {
@@ -1001,11 +1021,11 @@ class EditPage {
$hasmatch = preg_match( "/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches );
# we can't deal with anchors, includes, html etc in the header for now,
# headline would need to be parsed to improve this
- if ( $hasmatch and strlen($matches[2]) > 0 ) {
+ if ( $hasmatch and strlen( $matches[2] ) > 0 ) {
$sectionanchor = $wgParser->guessSectionNameFromWikiText( $matches[2] );
}
}
- wfProfileOut( "$fname-sectionanchor" );
+ wfProfileOut( __METHOD__ . '-sectionanchor' );
// Save errors may fall down to the edit form, but we've now
// merged the section into full text. Clear the section field
@@ -1015,26 +1035,26 @@ class EditPage {
$this->section = '';
// Check for length errors again now that the section is merged in
- $this->kblength = (int)(strlen( $text ) / 1024);
+ $this->kblength = (int)( strlen( $text ) / 1024 );
if ( $this->kblength > $wgMaxArticleSize ) {
$this->tooBig = true;
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_MAX_ARTICLE_SIZE_EXCEEDED;
}
# update the article here
if ( $this->mArticle->updateArticle( $text, $this->summary, $this->minoredit,
- $this->watchthis, $bot, $sectionanchor ) )
+ $this->watchthis, $bot, $sectionanchor ) )
{
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_SUCCESS_UPDATE;
} else {
$this->isConflict = true;
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return self::AS_END;
}
-
+
/**
* Check if no edits were made by other users since
* the time a user started editing the page. Limit to
@@ -1045,7 +1065,7 @@ class EditPage {
$dbw = wfGetDB( DB_MASTER );
$res = $dbw->select( 'revision',
'rev_user',
- array(
+ array(
'rev_page' => $this->mArticle->getId(),
'rev_timestamp > '.$dbw->addQuotes( $dbw->timestamp($edittime) )
),
@@ -1058,7 +1078,7 @@ class EditPage {
}
return true;
}
-
+
/**
* Check given input text against $wgSpamRegex, and return the text of the first match.
* @return mixed -- matching string or false
@@ -1069,7 +1089,7 @@ class EditPage {
$regexes = (array)$wgSpamRegex;
return self::matchSpamRegexInternal( $text, $regexes );
}
-
+
/**
* Check given input text against $wgSpamRegex, and return the text of the first match.
* @return mixed -- matching string or false
@@ -1079,7 +1099,7 @@ class EditPage {
$regexes = (array)$wgSummarySpamRegex;
return self::matchSpamRegexInternal( $text, $regexes );
}
-
+
protected static function matchSpamRegexInternal( $text, $regexes ) {
foreach( $regexes as $regex ) {
$matches = array();
@@ -1093,10 +1113,25 @@ class EditPage {
/**
* Initialise form fields in the object
* Called on the first invocation, e.g. when a user clicks an edit link
+ * @returns bool -- if the requested section is valid
*/
function initialiseForm() {
+ global $wgUser;
$this->edittime = $this->mArticle->getTimestamp();
$this->textbox1 = $this->getContent( false );
+ // activate checkboxes if user wants them to be always active
+ # Sort out the "watch" checkbox
+ if ( $wgUser->getOption( 'watchdefault' ) ) {
+ # Watch all edits
+ $this->watchthis = true;
+ } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
+ # Watch creations
+ $this->watchthis = true;
+ } elseif ( $this->mTitle->userIsWatching() ) {
+ # Already watched
+ $this->watchthis = true;
+ }
+ if ( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
if ( $this->textbox1 === false ) return false;
wfProxyCheck();
return true;
@@ -1115,7 +1150,7 @@ class EditPage {
$wgOut->setPageTitle( wfMsg( $msg, $wgTitle->getPrefixedText() ) );
} else {
# Use the title defined by DISPLAYTITLE magic word when present
- if ( isset($this->mParserOutput)
+ if ( isset( $this->mParserOutput )
&& ( $dt = $this->mParserOutput->getDisplayTitle() ) !== false ) {
$title = $dt;
} else {
@@ -1132,22 +1167,19 @@ class EditPage {
* near the top, for captchas and the like.
*/
function showEditForm( $formCallback=null ) {
- global $wgOut, $wgUser, $wgLang, $wgContLang, $wgMaxArticleSize, $wgTitle, $wgRequest;
+ global $wgOut, $wgUser, $wgTitle;
# If $wgTitle is null, that means we're in API mode.
# Some hook probably called this function without checking
# for is_null($wgTitle) first. Bail out right here so we don't
# do lots of work just to discard it right after.
- if (is_null($wgTitle))
+ if ( is_null( $wgTitle ) )
return;
- $fname = 'EditPage::showEditForm';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$sk = $wgUser->getSkin();
- wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) ) ;
-
#need to parse the preview early so that we know which templates are used,
#otherwise users with "show preview after edit box" will get a blank list
#we parse this near the beginning so that setHeaders can do the title
@@ -1157,18 +1189,158 @@ class EditPage {
$previewOutput = $this->getPreviewText();
}
+ wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) );
+
$this->setHeaders();
# Enabled article-related sidebar, toplinks, etc.
$wgOut->setArticleRelated( true );
+ if ( $this->showHeader() === false )
+ return;
+
+ $action = htmlspecialchars($this->getActionURL($wgTitle));
+
+ if ( $wgUser->getOption( 'showtoolbar' ) and !$this->isCssJsSubpage ) {
+ # prepare toolbar for edit buttons
+ $toolbar = EditPage::getEditToolbar();
+ } else {
+ $toolbar = '';
+ }
+
+
+ $wgOut->addHTML( $this->editFormPageTop );
+
+ if ( $wgUser->getOption( 'previewontop' ) ) {
+ $this->displayPreviewArea( $previewOutput, true );
+ }
+
+ $wgOut->addHTML( $this->editFormTextTop );
+
+ $templates = $this->getTemplates();
+ $formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
+
+ $hiddencats = $this->mArticle->getHiddenCategories();
+ $formattedhiddencats = $sk->formatHiddenCategories( $hiddencats );
+
+ if ( $this->wasDeletedSinceLastEdit() && 'save' != $this->formtype ) {
+ $wgOut->wrapWikiMsg(
+ "<div class='error mw-deleted-while-editing'>\n$1</div>",
+ 'deletedwhileediting' );
+ } elseif ( $this->wasDeletedSinceLastEdit() ) {
+ // Hide the toolbar and edit area, user can click preview to get it back
+ // Add an confirmation checkbox and explanation.
+ $toolbar = '';
+ // @todo move this to a cleaner conditional instead of blanking a variable
+ }
+ $wgOut->addHTML( <<<HTML
+{$toolbar}
+<form id="editform" name="editform" method="post" action="$action" enctype="multipart/form-data">
+HTML
+);
+
+ if ( is_callable( $formCallback ) ) {
+ call_user_func_array( $formCallback, array( &$wgOut ) );
+ }
+
+ wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
+
+ // Put these up at the top to ensure they aren't lost on early form submission
+ $this->showFormBeforeText();
+
+ if ( $this->wasDeletedSinceLastEdit() && 'save' == $this->formtype ) {
+ $wgOut->addHTML(
+ '<div class="mw-confirm-recreate">' .
+ $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ) ) .
+ Xml::checkLabel( wfMsg( 'recreate' ), 'wpRecreate', 'wpRecreate', false,
+ array( 'title' => $sk->titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' )
+ ) .
+ '</div>'
+ );
+ }
+
+ # If a blank edit summary was previously provided, and the appropriate
+ # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
+ # user being bounced back more than once in the event that a summary
+ # is not required.
+ #####
+ # For a bit more sophisticated detection of blank summaries, hash the
+ # automatic one and pass that in the hidden field wpAutoSummary.
+ if ( $this->missingSummary ||
+ ( $this->section == 'new' && $this->nosummary ) )
+ $wgOut->addHTML( Xml::hidden( 'wpIgnoreBlankSummary', true ) );
+ $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
+ $wgOut->addHTML( Xml::hidden( 'wpAutoSummary', $autosumm ) );
+
+ $wgOut->addHTML( Xml::hidden( 'oldid', $this->mArticle->getOldID() ) );
+
+ if ( $this->section == 'new' ) {
+ $this->showSummaryInput( true, $this->summary );
+ $wgOut->addHTML( $this->getSummaryPreview( true, $this->summary ) );
+ }
+
+ $wgOut->addHTML( $this->editFormTextBeforeContent );
+
if ( $this->isConflict ) {
- $wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1</div>", 'explainconflict' );
+ // In an edit conflict bypass the overrideable content form method
+ // and fallback to the raw wpTextbox1 since editconflicts can't be
+ // resolved between page source edits and custom ui edits using the
+ // custom edit ui.
+ $this->showTextbox1( null, $this->getContent() );
+ } else {
+ $this->showContentForm();
+ }
+
+ $wgOut->addHTML( $this->editFormTextAfterContent );
+
+ $wgOut->addWikiText( $this->getCopywarn() );
+ if ( isset($this->editFormTextAfterWarn) && $this->editFormTextAfterWarn !== '' )
+ $wgOut->addHTML( $this->editFormTextAfterWarn );
+
+ $this->showStandardInputs();
+
+ $this->showFormAfterText();
+
+ $this->showTosSummary();
+ $this->showEditTools();
+
+ $wgOut->addHTML( <<<HTML
+{$this->editFormTextAfterTools}
+<div class='templatesUsed'>
+{$formattedtemplates}
+</div>
+<div class='hiddencats'>
+{$formattedhiddencats}
+</div>
+HTML
+);
+
+ if ( $this->isConflict )
+ $this->showConflict();
+
+ $wgOut->addHTML( $this->editFormTextBottom );
+ $wgOut->addHTML( "</form>\n" );
+ if ( !$wgUser->getOption( 'previewontop' ) ) {
+ $this->displayPreviewArea( $previewOutput, false );
+ }
- $this->textbox2 = $this->textbox1;
- $this->textbox1 = $this->getContent();
+ wfProfileOut( __METHOD__ );
+ }
+
+ protected function showHeader() {
+ global $wgOut, $wgUser, $wgTitle, $wgMaxArticleSize, $wgLang;
+ if ( $this->isConflict ) {
+ $wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1</div>", 'explainconflict' );
$this->edittime = $this->mArticle->getTimestamp();
} else {
+ if ( $this->section != '' && !$this->isSectionEditSupported() ) {
+ // We use $this->section to much before this and getVal('wgSection') directly in other places
+ // at this point we can't reset $this->section to '' to fallback to non-section editing.
+ // Someone is welcome to try refactoring though
+ $wgOut->showErrorPage( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' );
+ return false;
+ }
+
if ( $this->section != '' && $this->section != 'new' ) {
$matches = array();
if ( !$this->summary && !$this->preview && !$this->diff ) {
@@ -1183,15 +1355,15 @@ class EditPage {
}
if ( $this->missingComment ) {
- $wgOut->wrapWikiMsg( '<div id="mw-missingcommenttext">$1</div>', 'missingcommenttext' );
+ $wgOut->wrapWikiMsg( "<div id='mw-missingcommenttext'>\n$1</div>", 'missingcommenttext' );
}
if ( $this->missingSummary && $this->section != 'new' ) {
- $wgOut->wrapWikiMsg( '<div id="mw-missingsummary">$1</div>', 'missingsummary' );
+ $wgOut->wrapWikiMsg( "<div id='mw-missingsummary'>\n$1</div>", 'missingsummary' );
}
if ( $this->missingSummary && $this->section == 'new' ) {
- $wgOut->wrapWikiMsg( '<div id="mw-missingcommentheader">$1</div>', 'missingcommentheader' );
+ $wgOut->wrapWikiMsg( "<div id='mw-missingcommentheader'>\n$1</div>", 'missingcommentheader' );
}
if ( $this->hookError !== '' ) {
@@ -1201,6 +1373,7 @@ class EditPage {
if ( !$this->checkUnicodeCompliantBrowser() ) {
$wgOut->addWikiMsg( 'nonunicodebrowser' );
}
+
if ( isset( $this->mArticle ) && isset( $this->mArticle->mRevision ) ) {
// Let sysop know that this will make private content public if saved
@@ -1220,41 +1393,37 @@ class EditPage {
if ( wfReadOnly() ) {
$wgOut->wrapWikiMsg( "<div id=\"mw-read-only-warning\">\n$1\n</div>", array( 'readonlywarning', wfReadOnlyReason() ) );
} elseif ( $wgUser->isAnon() && $this->formtype != 'preview' ) {
- $wgOut->wrapWikiMsg( '<div id="mw-anon-edit-warning">$1</div>', 'anoneditwarning' );
+ $wgOut->wrapWikiMsg( "<div id=\"mw-anon-edit-warning\">\n$1</div>", 'anoneditwarning' );
} else {
if ( $this->isCssJsSubpage ) {
# Check the skin exists
- if ( $this->isValidCssJsSubpage ) {
- if ( $this->formtype !== 'preview' ) {
- $wgOut->addWikiMsg( 'usercssjsyoucanpreview' );
- }
- } else {
+ if ( !$this->isValidCssJsSubpage ) {
$wgOut->addWikiMsg( 'userinvalidcssjstitle', $wgTitle->getSkinFromCssJsSubpage() );
}
+ if ( $this->formtype !== 'preview' ) {
+ if ( $this->isCssSubpage )
+ $wgOut->addWikiMsg( 'usercssyoucanpreview' );
+ if ( $this->isJsSubpage )
+ $wgOut->addWikiMsg( 'userjsyoucanpreview' );
+ }
}
}
- $classes = array(); // Textarea CSS
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- } elseif ( $this->mTitle->isProtected( 'edit' ) ) {
+ if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) {
# Is the title semi-protected?
if ( $this->mTitle->isSemiProtected() ) {
$noticeMsg = 'semiprotectedpagewarning';
- $classes[] = 'mw-textarea-sprotected';
} else {
# Then it must be protected based on static groups (regular)
$noticeMsg = 'protectedpagewarning';
- $classes[] = 'mw-textarea-protected';
}
- $wgOut->addHTML( "<div class='mw-warning-with-logexcerpt'>\n" );
- $wgOut->addWikiMsg( $noticeMsg );
- LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '', 1 );
- $wgOut->addHTML( "</div>\n" );
+ LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '',
+ array( 'lim' => 1, 'msgKey' => array( $noticeMsg ) ) );
}
if ( $this->mTitle->isCascadeProtected() ) {
# Is this page under cascading protection from some source pages?
list($cascadeSources, /* $restrictions */) = $this->mTitle->getCascadeProtectionSources();
- $notice = "<div class='mw-cascadeprotectedwarning'>$1\n";
+ $notice = "<div class='mw-cascadeprotectedwarning'>\n$1\n";
$cascadeSourcesCount = count( $cascadeSources );
if ( $cascadeSourcesCount > 0 ) {
# Explain, and list the titles responsible
@@ -1266,12 +1435,17 @@ class EditPage {
$wgOut->wrapWikiMsg( $notice, array( 'cascadeprotectedwarning', $cascadeSourcesCount ) );
}
if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) ) {
- $wgOut->wrapWikiMsg( '<div class="mw-titleprotectedwarning">$1</div>', 'titleprotectedwarning' );
+ LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '',
+ array( 'lim' => 1,
+ 'showIfEmpty' => false,
+ 'msgKey' => array( 'titleprotectedwarning' ),
+ 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n$1</div>" ) );
}
if ( $this->kblength === false ) {
- $this->kblength = (int)(strlen( $this->textbox1 ) / 1024);
+ $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
}
+
if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) {
$wgOut->addHTML( "<div class='error' id='mw-edit-longpageerror'>\n" );
$wgOut->addWikiMsg( 'longpageerror', $wgLang->formatNum( $this->kblength ), $wgLang->formatNum( $wgMaxArticleSize ) );
@@ -1281,245 +1455,109 @@ class EditPage {
$wgOut->addWikiMsg( 'longpagewarning', $wgLang->formatNum( $this->kblength ) );
$wgOut->addHTML( "</div>\n" );
}
+ }
- $q = 'action='.$this->action;
- #if ( "no" == $redirect ) { $q .= "&redirect=no"; }
- $action = $wgTitle->escapeLocalURL( $q );
-
- $summary = wfMsg( 'summary' );
- $subject = wfMsg( 'subject' );
-
- $cancel = $sk->makeKnownLink( $wgTitle->getPrefixedText(),
- wfMsgExt('cancel', array('parseinline')) );
- $separator = wfMsgExt( 'pipe-separator' , 'escapenoentities' );
- $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ));
- $edithelp = '<a target="helpwindow" href="'.$edithelpurl.'">'.
- htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '.
- htmlspecialchars( wfMsg( 'newwindow' ) );
-
- global $wgRightsText;
- if ( $wgRightsText ) {
- $copywarnMsg = array( 'copyrightwarning',
- '[[' . wfMsgForContent( 'copyrightpage' ) . ']]',
- $wgRightsText );
- } else {
- $copywarnMsg = array( 'copyrightwarning2',
- '[[' . wfMsgForContent( 'copyrightpage' ) . ']]' );
- }
-
- if ( $wgUser->getOption('showtoolbar') and !$this->isCssJsSubpage ) {
- # prepare toolbar for edit buttons
- $toolbar = EditPage::getEditToolbar();
- } else {
- $toolbar = '';
- }
-
- // activate checkboxes if user wants them to be always active
- if ( !$this->preview && !$this->diff ) {
- # Sort out the "watch" checkbox
- if ( $wgUser->getOption( 'watchdefault' ) ) {
- # Watch all edits
- $this->watchthis = true;
- } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
- # Watch creations
- $this->watchthis = true;
- } elseif ( $this->mTitle->userIsWatching() ) {
- # Already watched
- $this->watchthis = true;
- }
-
- # May be overriden by request parameters
- if( $wgRequest->getBool( 'watchthis' ) ) {
- $this->watchthis = true;
- }
-
- if ( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
- }
-
- $wgOut->addHTML( $this->editFormPageTop );
+ /**
+ * Standard summary input and label (wgSummary), abstracted so EditPage
+ * subclasses may reorganize the form.
+ * Note that you do not need to worry about the label's for=, it will be
+ * inferred by the id given to the input. You can remove them both by
+ * passing array( 'id' => false ) to $userInputAttrs.
+ *
+ * @param $summary The value of the summary input
+ * @param $labelText The html to place inside the label
+ * @param $userInputAttrs An array of attrs to use on the input
+ * @param $userSpanAttrs An array of attrs to use on the span inside the label
+ *
+ * @return array An array in the format array( $label, $input )
+ */
+ function getSummaryInput($summary = "", $labelText = null, $inputAttrs = null, $spanLabelAttrs = null) {
+ $inputAttrs = ( is_array($inputAttrs) ? $inputAttrs : array() ) + array(
+ 'id' => 'wpSummary',
+ 'maxlength' => '200',
+ 'tabindex' => '1',
+ 'size' => 60,
+ 'spellcheck' => 'true',
+ );
+
+ $spanLabelAttrs = ( is_array($spanLabelAttrs) ? $spanLabelAttrs : array() ) + array(
+ 'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary',
+ 'id' => "wpSummaryLabel"
+ );
- if ( $wgUser->getOption( 'previewontop' ) ) {
- $this->displayPreviewArea( $previewOutput, true );
+ $label = null;
+ if ( $labelText ) {
+ $label = Xml::tags( 'label', $inputAttrs['id'] ? array( 'for' => $inputAttrs['id'] ) : null, $labelText );
+ $label = Xml::tags( 'span', $spanLabelAttrs, $label );
}
+ $input = Html::input( 'wpSummary', $summary, 'text', $inputAttrs );
- $wgOut->addHTML( $this->editFormTextTop );
-
- # if this is a comment, show a subject line at the top, which is also the edit summary.
- # Otherwise, show a summary field at the bottom
- $summarytext = $wgContLang->recodeForEdit( $this->summary );
+ return array( $label, $input );
+ }
- # If a blank edit summary was previously provided, and the appropriate
- # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
- # user being bounced back more than once in the event that a summary
- # is not required.
- #####
- # For a bit more sophisticated detection of blank summaries, hash the
- # automatic one and pass that in the hidden field wpAutoSummary.
- $summaryhiddens = '';
- if ( $this->missingSummary ) $summaryhiddens .= Xml::hidden( 'wpIgnoreBlankSummary', true );
- $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
- $summaryhiddens .= Xml::hidden( 'wpAutoSummary', $autosumm );
- if ( $this->section == 'new' ) {
- $commentsubject = '';
- if ( !$wgRequest->getBool( 'nosummary' ) ) {
- # Add a class if 'missingsummary' is triggered to allow styling of the summary line
- $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
-
- $commentsubject =
- Xml::tags( 'label', array( 'for' => 'wpSummary' ), $subject );
- $commentsubject =
- Xml::tags( 'span', array( 'class' => $summaryClass, 'id' => "wpSummaryLabel" ),
- $commentsubject );
- $commentsubject .= '&nbsp;';
- $commentsubject .= Xml::input( 'wpSummary',
- 60,
- $summarytext,
- array(
- 'id' => 'wpSummary',
- 'maxlength' => '200',
- 'tabindex' => '1'
- ) );
- }
- $editsummary = "<div class='editOptions'>\n";
- global $wgParser;
- $formattedSummary = wfMsgForContent( 'newsectionsummary', $wgParser->stripSectionName( $this->summary ) );
- $subjectpreview = $summarytext && $this->preview ? "<div class=\"mw-summary-preview\">". wfMsg('subject-preview') . $sk->commentBlock( $formattedSummary, $this->mTitle, true )."</div>\n" : '';
- $summarypreview = '';
+ /**
+ * @param bool $isSubjectPreview true if this is the section subject/title
+ * up top, or false if this is the comment
+ * summary down below the textarea
+ * @param string $summary The text of the summary to display
+ * @return string
+ */
+ protected function showSummaryInput( $isSubjectPreview, $summary = "" ) {
+ global $wgOut, $wgContLang;
+ # Add a class if 'missingsummary' is triggered to allow styling of the summary line
+ $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
+ if ( $isSubjectPreview ) {
+ if ( $this->nosummary )
+ return;
} else {
- $commentsubject = '';
-
- # Add a class if 'missingsummary' is triggered to allow styling of the summary line
- $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
-
- $editsummary = Xml::tags( 'label', array( 'for' => 'wpSummary' ), $summary );
- $editsummary = Xml::tags( 'span', array( 'class' => $summaryClass, 'id' => "wpSummaryLabel" ),
- $editsummary ) . ' ';
-
- $editsummary .= Xml::input( 'wpSummary',
- 60,
- $summarytext,
- array(
- 'id' => 'wpSummary',
- 'maxlength' => '200',
- 'tabindex' => '1'
- ) );
-
- // No idea where this is closed.
- $editsummary = Xml::openElement( 'div', array( 'class' => 'editOptions' ) )
- . $editsummary . '<br/>';
-
- $summarypreview = '';
- if ( $summarytext && $this->preview ) {
- $summarypreview =
- Xml::tags( 'div',
- array( 'class' => 'mw-summary-preview' ),
- wfMsg( 'summary-preview' ) .
- $sk->commentBlock( $this->summary, $this->mTitle )
- );
- }
- $subjectpreview = '';
- }
- $commentsubject .= $summaryhiddens;
-
- # Set focus to the edit box on load, except on preview or diff, where it would interfere with the display
- if ( !$this->preview && !$this->diff ) {
- $wgOut->setOnloadHandler( 'document.editform.wpTextbox1.focus()' );
- }
- $templates = $this->getTemplates();
- $formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
-
- $hiddencats = $this->mArticle->getHiddenCategories();
- $formattedhiddencats = $sk->formatHiddenCategories( $hiddencats );
-
- global $wgUseMetadataEdit ;
- if ( $wgUseMetadataEdit ) {
- $metadata = $this->mMetaData ;
- $metadata = htmlspecialchars( $wgContLang->recodeForEdit( $metadata ) ) ;
- $top = wfMsgWikiHtml( 'metadata_help' );
- /* ToDo: Replace with clean code */
- $ew = $wgUser->getOption( 'editwidth' );
- if ( $ew ) $ew = " style=\"width:100%\"";
- else $ew = '';
- $cols = $wgUser->getIntOption( 'cols' );
- /* /ToDo */
- $metadata = $top . "<textarea name='metadata' rows='3' cols='{$cols}'{$ew}>{$metadata}</textarea>" ;
- }
- else $metadata = "" ;
-
- $recreate = '';
- if ( $this->wasDeletedSinceLastEdit() ) {
- if ( 'save' != $this->formtype ) {
- $wgOut->wrapWikiMsg(
- "<div class='error mw-deleted-while-editing'>\n$1</div>",
- 'deletedwhileediting' );
- } else {
- // Hide the toolbar and edit area, user can click preview to get it back
- // Add an confirmation checkbox and explanation.
- $toolbar = '';
- $recreate = '<div class="mw-confirm-recreate">' .
- $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ) ) .
- Xml::checkLabel( wfMsg( 'recreate' ), 'wpRecreate', 'wpRecreate', false,
- array( 'title' => $sk->titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' )
- ) . '</div>';
- }
- }
-
- $tabindex = 2;
-
- $checkboxes = $this->getCheckboxes( $tabindex, $sk,
- array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) );
-
- $checkboxhtml = implode( $checkboxes, "\n" );
-
- $buttons = $this->getEditButtons( $tabindex );
- $buttonshtml = implode( $buttons, "\n" );
-
- $safemodehtml = $this->checkUnicodeCompliantBrowser()
- ? '' : Xml::hidden( 'safemode', '1' );
-
- $wgOut->addHTML( <<<END
-{$toolbar}
-<form id="editform" name="editform" method="post" action="$action" enctype="multipart/form-data">
-END
-);
-
- if ( is_callable( $formCallback ) ) {
- call_user_func_array( $formCallback, array( &$wgOut ) );
+ if ( !$this->mShowSummaryField )
+ return;
}
+ $summary = $wgContLang->recodeForEdit( $summary );
+ $labelText = wfMsgExt( $isSubjectPreview ? 'subject' : 'summary', 'parseinline' );
+ list($label, $input) = $this->getSummaryInput($summary, $labelText, array( 'class' => $summaryClass ), array());
+ $wgOut->addHTML("{$label} {$input}");
+ }
- wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
-
- // Put these up at the top to ensure they aren't lost on early form submission
- $this->showFormBeforeText();
-
- $wgOut->addHTML( <<<END
-{$recreate}
-{$commentsubject}
-{$subjectpreview}
-{$this->editFormTextBeforeContent}
-END
-);
- $this->showTextbox1( $classes );
-
- $wgOut->wrapWikiMsg( "<div id=\"editpage-copywarn\">\n$1\n</div>", $copywarnMsg );
- $wgOut->addHTML( <<<END
-{$this->editFormTextAfterWarn}
-{$metadata}
-{$editsummary}
-{$summarypreview}
-{$checkboxhtml}
-{$safemodehtml}
-END
-);
+ /**
+ * @param bool $isSubjectPreview true if this is the section subject/title
+ * up top, or false if this is the comment
+ * summary down below the textarea
+ * @param string $summary The text of the summary to display
+ * @return string
+ */
+ protected function getSummaryPreview( $isSubjectPreview, $summary = "" ) {
+ if ( !$summary || ( !$this->preview && !$this->diff ) )
+ return "";
+
+ global $wgParser, $wgUser;
+ $sk = $wgUser->getSkin();
+
+ if ( $isSubjectPreview )
+ $summary = wfMsgForContent( 'newsectionsummary', $wgParser->stripSectionName( $summary ) );
- $wgOut->addHTML(
-"<div class='editButtons'>
-{$buttonshtml}
- <span class='editHelp'>{$cancel}{$separator}{$edithelp}</span>
-</div><!-- editButtons -->
-</div><!-- editOptions -->");
+ $summary = wfMsgExt( 'subject-preview', 'parseinline' ) . $sk->commentBlock( $summary, $this->mTitle, !!$isSubjectPreview );
+ return Xml::tags( 'div', array( 'class' => 'mw-summary-preview' ), $summary );
+ }
+ protected function showFormBeforeText() {
+ global $wgOut;
+ $section = htmlspecialchars( $this->section );
+ $wgOut->addHTML( <<<INPUTS
+<input type='hidden' value="{$section}" name="wpSection" />
+<input type='hidden' value="{$this->starttime}" name="wpStarttime" />
+<input type='hidden' value="{$this->edittime}" name="wpEdittime" />
+<input type='hidden' value="{$this->scrolltop}" name="wpScrolltop" id="wpScrolltop" />
+
+INPUTS
+ );
+ if ( !$this->checkUnicodeCompliantBrowser() )
+ $wgOut->addHTML(Xml::hidden( 'safemode', '1' ));
+ }
+
+ protected function showFormAfterText() {
+ global $wgOut, $wgUser;
/**
* To make it harder for someone to slip a user a page
* which submits an edit form to the wiki without their
@@ -1532,68 +1570,64 @@ END
* include the constant suffix to prevent editing from
* broken text-mangling proxies.
*/
- $token = htmlspecialchars( $wgUser->editToken() );
- $wgOut->addHTML( "\n<input type='hidden' value=\"$token\" name=\"wpEditToken\" />\n" );
-
- $this->showEditTools();
-
- $wgOut->addHTML( <<<END
-{$this->editFormTextAfterTools}
-<div class='templatesUsed'>
-{$formattedtemplates}
-</div>
-<div class='hiddencats'>
-{$formattedhiddencats}
-</div>
-END
-);
-
- if ( $this->isConflict && wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
- $wgOut->wrapWikiMsg( '==$1==', "yourdiff" );
-
- $de = new DifferenceEngine( $this->mTitle );
- $de->setText( $this->textbox2, $this->textbox1 );
- $de->showDiff( wfMsg( "yourtext" ), wfMsg( "storedversion" ) );
-
- $wgOut->wrapWikiMsg( '==$1==', "yourtext" );
- $this->showTextbox2();
- }
- $wgOut->addHTML( $this->editFormTextBottom );
- $wgOut->addHTML( "</form>\n" );
- if ( !$wgUser->getOption( 'previewontop' ) ) {
- $this->displayPreviewArea( $previewOutput, false );
- }
-
- wfProfileOut( $fname );
+ $wgOut->addHTML( "\n" . Xml::hidden( "wpEditToken", $wgUser->editToken() ) . "\n" );
}
- protected function showFormBeforeText() {
- global $wgOut;
- $wgOut->addHTML( "
-<input type='hidden' value=\"" . htmlspecialchars( $this->section ) . "\" name=\"wpSection\" />
-<input type='hidden' value=\"{$this->starttime}\" name=\"wpStarttime\" />\n
-<input type='hidden' value=\"{$this->edittime}\" name=\"wpEdittime\" />\n
-<input type='hidden' value=\"{$this->scrolltop}\" name=\"wpScrolltop\" id=\"wpScrolltop\" />\n" );
+ /**
+ * Subpage overridable method for printing the form for page content editing
+ * By default this simply outputs wpTextbox1
+ * Subclasses can override this to provide a custom UI for editing;
+ * be it a form, or simply wpTextbox1 with a modified content that will be
+ * reverse modified when extracted from the post data.
+ * Note that this is basically the inverse for importContentFormData
+ *
+ * @praram WebRequest $request
+ */
+ protected function showContentForm() {
+ $this->showTextbox1();
}
-
- protected function showTextbox1( $classes ) {
+
+ /**
+ * Method to output wpTextbox1
+ * The $textoverride method can be used by subclasses overriding showContentForm
+ * to pass back to this method.
+ *
+ * @param array $customAttribs An array of html attributes to use in the textarea
+ * @param string $textoverride Optional text to override $this->textarea1 with
+ */
+ protected function showTextbox1($customAttribs = null, $textoverride = null) {
+ $classes = array(); // Textarea CSS
+ if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) {
+ # Is the title semi-protected?
+ if ( $this->mTitle->isSemiProtected() ) {
+ $classes[] = 'mw-textarea-sprotected';
+ } else {
+ # Then it must be protected based on static groups (regular)
+ $classes[] = 'mw-textarea-protected';
+ }
+ }
$attribs = array( 'tabindex' => 1 );
-
+ if ( is_array($customAttribs) )
+ $attribs += $customAttribs;
+
if ( $this->wasDeletedSinceLastEdit() )
$attribs['type'] = 'hidden';
- if ( !empty($classes) )
- $attribs['class'] = implode(' ',$classes);
+ if ( !empty( $classes ) ) {
+ if ( isset($attribs['class']) )
+ $classes[] = $attribs['class'];
+ $attribs['class'] = implode( ' ', $classes );
+ }
- $this->showTextbox( $this->textbox1, 'wpTextbox1', $attribs );
+ $this->showTextbox( isset($textoverride) ? $textoverride : $this->textbox1, 'wpTextbox1', $attribs );
}
-
+
protected function showTextbox2() {
$this->showTextbox( $this->textbox2, 'wpTextbox2', array( 'tabindex' => 6 ) );
}
-
- protected function showTextbox( $content, $name, $attribs = array() ) {
+
+ protected function showTextbox( $content, $name, $customAttribs = array() ) {
global $wgOut, $wgUser;
-
+
$wikitext = $this->safeUnicodeOutput( $content );
if ( $wikitext !== '' ) {
// Ensure there's a newline at the end, otherwise adding lines
@@ -1602,18 +1636,19 @@ END
// mode will show an extra newline. A bit annoying.
$wikitext .= "\n";
}
-
- $attribs['accesskey'] = ',';
- $attribs['id'] = $name;
-
+
+ $attribs = $customAttribs + array(
+ 'accesskey' => ',',
+ 'id' => $name,
+ 'cols' => $wgUser->getIntOption( 'cols' ),
+ 'rows' => $wgUser->getIntOption( 'rows' ),
+ 'style' => '' // avoid php notices when appending for editwidth preference (appending allows customAttribs['style'] to still work
+ );
+
if ( $wgUser->getOption( 'editwidth' ) )
- $attribs['style'] = 'width: 100%';
-
- $wgOut->addHTML( Xml::textarea(
- $name,
- $wikitext,
- $wgUser->getIntOption( 'cols' ), $wgUser->getIntOption( 'rows' ),
- $attribs ) );
+ $attribs['style'] .= 'width: 100%';
+
+ $wgOut->addHTML( Html::textarea( $name, $wikitext, $attribs ) );
}
protected function displayPreviewArea( $previewOutput, $isOnTop = false ) {
@@ -1660,23 +1695,22 @@ END
}
}
- /**
- * Live Preview lets us fetch rendered preview page content and
- * add it to the page without refreshing the whole page.
- * If not supported by the browser it will fall through to the normal form
- * submission method.
- *
- * This function outputs a script tag to support live preview, and
- * returns an onclick handler which should be added to the attributes
- * of the preview button
- */
- function doLivePreviewScript() {
- global $wgOut, $wgTitle;
- $wgOut->addScriptFile( 'preview.js' );
- $liveAction = $wgTitle->getLocalUrl( "action={$this->action}&wpPreview=true&live=true" );
- return "return !lpDoPreview(" .
- "editform.wpTextbox1.value," .
- '"' . $liveAction . '"' . ")";
+ protected function showTosSummary() {
+ $msg = 'editpage-tos-summary';
+ // Give a chance for site and per-namespace customizations of
+ // terms of service summary link that might exist separately
+ // from the copyright notice.
+ //
+ // This will display between the save button and the edit tools,
+ // so should remain short!
+ wfRunHooks( 'EditPageTosSummary', array( $this->mTitle, &$msg ) );
+ $text = wfMsg( $msg );
+ if( !wfEmptyMsg( $msg, $text ) && $text !== '-' ) {
+ global $wgOut;
+ $wgOut->addHTML( '<div class="mw-tos-summary">' );
+ $wgOut->addWikiMsgArray( $msg, array() );
+ $wgOut->addHTML( '</div>' );
+ }
}
protected function showEditTools() {
@@ -1685,6 +1719,67 @@ END
$wgOut->addWikiMsgArray( 'edittools', array(), array( 'content' ) );
$wgOut->addHTML( '</div>' );
}
+
+ protected function getCopywarn() {
+ global $wgRightsText;
+ if ( $wgRightsText ) {
+ $copywarnMsg = array( 'copyrightwarning',
+ '[[' . wfMsgForContent( 'copyrightpage' ) . ']]',
+ $wgRightsText );
+ } else {
+ $copywarnMsg = array( 'copyrightwarning2',
+ '[[' . wfMsgForContent( 'copyrightpage' ) . ']]' );
+ }
+ // Allow for site and per-namespace customization of contribution/copyright notice.
+ wfRunHooks( 'EditPageCopyrightWarning', array( $this->mTitle, &$copywarnMsg ) );
+
+ return "<div id=\"editpage-copywarn\">\n" . call_user_func_array("wfMsgNoTrans", $copywarnMsg) . "\n</div>";
+ }
+
+ protected function showStandardInputs( &$tabindex = 2 ) {
+ global $wgOut, $wgUser;
+ $wgOut->addHTML( "<div class='editOptions'>\n" );
+
+ if ( $this->section != 'new' ) {
+ $this->showSummaryInput( false, $this->summary );
+ $wgOut->addHTML( $this->getSummaryPreview( false, $this->summary ) );
+ }
+
+ $checkboxes = $this->getCheckboxes( $tabindex, $wgUser->getSkin(),
+ array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) );
+ $wgOut->addHTML( "<div class='editCheckboxes'>" . implode( $checkboxes, "\n" ) . "</div>\n" );
+ $wgOut->addHTML( "<div class='editButtons'>\n" );
+ $wgOut->addHTML( implode( $this->getEditButtons( $tabindex ), "\n" ) . "\n" );
+
+ $cancel = $this->getCancelLink();
+ $separator = wfMsgExt( 'pipe-separator' , 'escapenoentities' );
+ $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ) );
+ $edithelp = '<a target="helpwindow" href="'.$edithelpurl.'">'.
+ htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '.
+ htmlspecialchars( wfMsg( 'newwindow' ) );
+ $wgOut->addHTML( " <span class='editHelp'>{$cancel}{$separator}{$edithelp}</span>\n" );
+ $wgOut->addHTML( "</div><!-- editButtons -->\n</div><!-- editOptions -->\n" );
+ }
+
+ /*
+ * Show an edit conflict. textbox1 is already shown in showEditForm().
+ * If you want to use another entry point to this function, be careful.
+ */
+ protected function showConflict() {
+ global $wgOut;
+ $this->textbox2 = $this->textbox1;
+ $this->textbox1 = $this->getContent();
+ if ( wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
+ $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
+
+ $de = new DifferenceEngine( $this->mTitle );
+ $de->setText( $this->textbox2, $this->textbox1 );
+ $de->showDiff( wfMsg( "yourtext" ), wfMsg( "storedversion" ) );
+
+ $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
+ $this->showTextbox2();
+ }
+ }
protected function getLastDelete() {
$dbr = wfGetDB( DB_SLAVE );
@@ -1698,7 +1793,7 @@ END
'log_title',
'log_comment',
'log_params',
- 'log_deleted',
+ 'log_deleted',
'user_name' ),
array( 'log_namespace' => $this->mTitle->getNamespace(),
'log_title' => $this->mTitle->getDBkey(),
@@ -1709,11 +1804,11 @@ END
array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' )
);
// Quick paranoid permission checks...
- if( is_object($data) ) {
+ if( is_object( $data ) ) {
if( $data->log_deleted & LogPage::DELETED_USER )
- $data->user_name = wfMsgHtml('rev-deleted-user');
+ $data->user_name = wfMsgHtml( 'rev-deleted-user' );
if( $data->log_deleted & LogPage::DELETED_COMMENT )
- $data->log_comment = wfMsgHtml('rev-deleted-comment');
+ $data->log_comment = wfMsgHtml( 'rev-deleted-comment' );
}
return $data;
}
@@ -1754,12 +1849,12 @@ END
# XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here
if ( $this->isCssJsSubpage ) {
- if (preg_match("/\\.css$/", $this->mTitle->getText() ) ) {
- $previewtext = wfMsg('usercsspreview');
- } else if (preg_match("/\\.js$/", $this->mTitle->getText() ) ) {
- $previewtext = wfMsg('userjspreview');
+ if (preg_match( "/\\.css$/", $this->mTitle->getText() ) ) {
+ $previewtext = wfMsg( 'usercsspreview' );
+ } else if (preg_match( "/\\.js$/", $this->mTitle->getText() ) ) {
+ $previewtext = wfMsg( 'userjspreview' );
}
- $parserOptions->setTidy(true);
+ $parserOptions->setTidy( true );
$parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions );
$previewHTML = $parserOutput->mText;
} elseif ( $rt = Title::newFromRedirectArray( $this->textbox1 ) ) {
@@ -1769,11 +1864,11 @@ END
# If we're adding a comment, we need to show the
# summary as the headline
- if ( $this->section=="new" && $this->summary!="" ) {
- $toparse="== {$this->summary} ==\n\n".$toparse;
+ if ( $this->section == "new" && $this->summary != "" ) {
+ $toparse = "== {$this->summary} ==\n\n" . $toparse;
}
- if ( $this->mMetaData != "" ) $toparse .= "\n" . $this->mMetaData;
+ wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) );
// Parse mediawiki messages with correct target language
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
@@ -1783,7 +1878,7 @@ END
}
- $parserOptions->setTidy(true);
+ $parserOptions->setTidy( true );
$parserOptions->enableLimitReport();
$parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $toparse ),
$this->mTitle, $parserOptions );
@@ -1797,20 +1892,24 @@ END
}
}
- $previewhead = '<h2>' . htmlspecialchars( wfMsg( 'preview' ) ) . "</h2>\n" .
- "<div class='previewnote'>" . $wgOut->parse( $note ) . "</div>\n";
- if ( $this->isConflict ) {
- $previewhead .='<h2>' . htmlspecialchars( wfMsg( 'previewconflict' ) ) . "</h2>\n";
+ if( $this->isConflict ) {
+ $conflict = '<h2 id="mw-previewconflict">' . htmlspecialchars( wfMsg( 'previewconflict' ) ) . "</h2>\n";
+ } else {
+ $conflict = '<hr />';
}
+ $previewhead = "<div class='previewnote'>\n" .
+ '<h2 id="mw-previewheader">' . htmlspecialchars( wfMsg( 'preview' ) ) . "</h2>" .
+ $wgOut->parse( $note ) . $conflict . "</div>\n";
+
wfProfileOut( __METHOD__ );
- return $previewhead . $previewHTML;
+ return $previewhead . $previewHTML . $this->previewTextAfterContent;
}
-
+
function getTemplates() {
if ( $this->preview || $this->section != '' ) {
$templates = array();
- if ( !isset($this->mParserOutput) ) return $templates;
+ if ( !isset( $this->mParserOutput ) ) return $templates;
foreach( $this->mParserOutput->getTemplates() as $ns => $template) {
foreach( array_keys( $template ) as $dbk ) {
$templates[] = Title::makeTitle($ns, $dbk);
@@ -1826,7 +1925,7 @@ END
* Call the stock "user is blocked" page
*/
function blockedPage() {
- global $wgOut, $wgUser;
+ global $wgOut;
$wgOut->blockedPage( false ); # Standard block notice on the top, don't 'return'
# If the user made changes, preserve them when showing the markup
@@ -1840,14 +1939,9 @@ END
# Spit out the source or the user's modified version
if ( $source !== false ) {
- $rows = $wgUser->getIntOption( 'rows' );
- $cols = $wgUser->getIntOption( 'cols' );
- $attribs = array( 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $cols, 'rows' => $rows, 'readonly' => 'readonly' );
$wgOut->addHTML( '<hr />' );
$wgOut->addWikiMsg( $first ? 'blockedoriginalsource' : 'blockededitsource', $this->mTitle->getPrefixedText() );
- # Why we don't use Xml::element here?
- # Is it because if $source is '', it returns <textarea />?
- $wgOut->addHTML( Xml::openElement( 'textarea', $attribs ) . htmlspecialchars( $source ) . Xml::closeElement( 'textarea' ) );
+ $this->showTextbox1( array( 'readonly' ), $source );
}
}
@@ -1859,7 +1953,13 @@ END
$skin = $wgUser->getSkin();
$loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
- $loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $wgTitle->getPrefixedUrl() );
+ $loginLink = $skin->link(
+ $loginTitle,
+ wfMsgHtml( 'loginreqlink' ),
+ array(),
+ array( 'returnto' => $wgTitle->getPrefixedText() ),
+ array( 'known', 'noclasses' )
+ );
$wgOut->setPageTitle( wfMsg( 'whitelistedittitle' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
@@ -1874,14 +1974,17 @@ END
* they have attempted to edit a nonexistent section.
*/
function noSuchSectionPage() {
- global $wgOut, $wgTitle;
+ global $wgOut;
$wgOut->setPageTitle( wfMsg( 'nosuchsectiontitle' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
- $wgOut->addWikiMsg( 'nosuchsectiontext', $this->section );
- $wgOut->returnToMain( false, $wgTitle );
+ $res = wfMsgExt( 'nosuchsectiontext', 'parse', $this->section );
+ wfRunHooks( 'EditPageNoSuchSection', array( &$this, &$res ) );
+ $wgOut->addHTML( $res );
+
+ $wgOut->returnToMain( false, $this->mTitle );
}
/**
@@ -1910,15 +2013,14 @@ END
* @todo document
*/
function mergeChangesInto( &$editText ){
- $fname = 'EditPage::mergeChangesInto';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$db = wfGetDB( DB_MASTER );
// This is the revision the editor started from
$baseRevision = $this->getBaseRevision();
if ( is_null( $baseRevision ) ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
}
$baseText = $baseRevision->getText();
@@ -1926,7 +2028,7 @@ END
// The current state, we want to merge updates into it
$currentRevision = Revision::loadFromTitle( $db, $this->mTitle );
if ( is_null( $currentRevision ) ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
}
$currentText = $currentRevision->getText();
@@ -1934,10 +2036,10 @@ END
$result = '';
if ( wfMerge( $baseText, $editText, $currentText, $result ) ) {
$editText = $result;
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return true;
} else {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
}
}
@@ -1987,13 +2089,14 @@ END
* Shows a bulletin board style toolbar for common editing functions.
* It can be disabled in the user preferences.
* The necessary JavaScript code can be found in skins/common/edit.js.
- *
+ *
* @return string
*/
static function getEditToolbar() {
- global $wgStylePath, $wgContLang, $wgLang, $wgJsMimeType;
+ global $wgStylePath, $wgContLang, $wgLang;
/**
+
* toolarray an array of arrays which each include the filename of
* the button image (without path), the opening tag, the closing tag,
* and optionally a sample text that is inserted between the two when no
@@ -2006,111 +2109,111 @@ END
*/
$toolarray = array(
array(
- 'image' => $wgLang->getImageFile('button-bold'),
+ 'image' => $wgLang->getImageFile( 'button-bold' ),
'id' => 'mw-editbutton-bold',
'open' => '\'\'\'',
'close' => '\'\'\'',
- 'sample' => wfMsg('bold_sample'),
- 'tip' => wfMsg('bold_tip'),
+ 'sample' => wfMsg( 'bold_sample' ),
+ 'tip' => wfMsg( 'bold_tip' ),
'key' => 'B'
),
array(
- 'image' => $wgLang->getImageFile('button-italic'),
+ 'image' => $wgLang->getImageFile( 'button-italic' ),
'id' => 'mw-editbutton-italic',
'open' => '\'\'',
'close' => '\'\'',
- 'sample' => wfMsg('italic_sample'),
- 'tip' => wfMsg('italic_tip'),
+ 'sample' => wfMsg( 'italic_sample' ),
+ 'tip' => wfMsg( 'italic_tip' ),
'key' => 'I'
),
array(
- 'image' => $wgLang->getImageFile('button-link'),
+ 'image' => $wgLang->getImageFile( 'button-link' ),
'id' => 'mw-editbutton-link',
'open' => '[[',
'close' => ']]',
- 'sample' => wfMsg('link_sample'),
- 'tip' => wfMsg('link_tip'),
+ 'sample' => wfMsg( 'link_sample' ),
+ 'tip' => wfMsg( 'link_tip' ),
'key' => 'L'
),
array(
- 'image' => $wgLang->getImageFile('button-extlink'),
+ 'image' => $wgLang->getImageFile( 'button-extlink' ),
'id' => 'mw-editbutton-extlink',
'open' => '[',
'close' => ']',
- 'sample' => wfMsg('extlink_sample'),
- 'tip' => wfMsg('extlink_tip'),
+ 'sample' => wfMsg( 'extlink_sample' ),
+ 'tip' => wfMsg( 'extlink_tip' ),
'key' => 'X'
),
array(
- 'image' => $wgLang->getImageFile('button-headline'),
+ 'image' => $wgLang->getImageFile( 'button-headline' ),
'id' => 'mw-editbutton-headline',
'open' => "\n== ",
'close' => " ==\n",
- 'sample' => wfMsg('headline_sample'),
- 'tip' => wfMsg('headline_tip'),
+ 'sample' => wfMsg( 'headline_sample' ),
+ 'tip' => wfMsg( 'headline_tip' ),
'key' => 'H'
),
array(
- 'image' => $wgLang->getImageFile('button-image'),
+ 'image' => $wgLang->getImageFile( 'button-image' ),
'id' => 'mw-editbutton-image',
- 'open' => '[['.$wgContLang->getNsText(NS_FILE).':',
+ 'open' => '[[' . $wgContLang->getNsText( NS_FILE ) . ':',
'close' => ']]',
- 'sample' => wfMsg('image_sample'),
- 'tip' => wfMsg('image_tip'),
+ 'sample' => wfMsg( 'image_sample' ),
+ 'tip' => wfMsg( 'image_tip' ),
'key' => 'D'
),
array(
- 'image' => $wgLang->getImageFile('button-media'),
+ 'image' => $wgLang->getImageFile( 'button-media' ),
'id' => 'mw-editbutton-media',
- 'open' => '[['.$wgContLang->getNsText(NS_MEDIA).':',
+ 'open' => '[[' . $wgContLang->getNsText( NS_MEDIA ) . ':',
'close' => ']]',
- 'sample' => wfMsg('media_sample'),
- 'tip' => wfMsg('media_tip'),
+ 'sample' => wfMsg( 'media_sample' ),
+ 'tip' => wfMsg( 'media_tip' ),
'key' => 'M'
),
array(
- 'image' => $wgLang->getImageFile('button-math'),
+ 'image' => $wgLang->getImageFile( 'button-math' ),
'id' => 'mw-editbutton-math',
'open' => "<math>",
'close' => "</math>",
- 'sample' => wfMsg('math_sample'),
- 'tip' => wfMsg('math_tip'),
+ 'sample' => wfMsg( 'math_sample' ),
+ 'tip' => wfMsg( 'math_tip' ),
'key' => 'C'
),
array(
- 'image' => $wgLang->getImageFile('button-nowiki'),
+ 'image' => $wgLang->getImageFile( 'button-nowiki' ),
'id' => 'mw-editbutton-nowiki',
'open' => "<nowiki>",
'close' => "</nowiki>",
- 'sample' => wfMsg('nowiki_sample'),
- 'tip' => wfMsg('nowiki_tip'),
+ 'sample' => wfMsg( 'nowiki_sample' ),
+ 'tip' => wfMsg( 'nowiki_tip' ),
'key' => 'N'
),
array(
- 'image' => $wgLang->getImageFile('button-sig'),
+ 'image' => $wgLang->getImageFile( 'button-sig' ),
'id' => 'mw-editbutton-signature',
'open' => '--~~~~',
'close' => '',
'sample' => '',
- 'tip' => wfMsg('sig_tip'),
+ 'tip' => wfMsg( 'sig_tip' ),
'key' => 'Y'
),
array(
- 'image' => $wgLang->getImageFile('button-hr'),
+ 'image' => $wgLang->getImageFile( 'button-hr' ),
'id' => 'mw-editbutton-hr',
'open' => "\n----\n",
'close' => '',
'sample' => '',
- 'tip' => wfMsg('hr_tip'),
+ 'tip' => wfMsg( 'hr_tip' ),
'key' => 'R'
)
);
$toolbar = "<div id='toolbar'>\n";
- $toolbar.="<script type='$wgJsMimeType'>\n/*<![CDATA[*/\n";
- foreach($toolarray as $tool) {
+ $script = '';
+ foreach ( $toolarray as $tool ) {
$params = array(
- $image = $wgStylePath.'/common/images/'.$tool['image'],
+ $image = $wgStylePath . '/common/images/' . $tool['image'],
// Note that we use the tip both for the ALT tag and the TITLE tag of the image.
// Older browsers show a "speedtip" type message only for ALT.
// Ideally these should be different, realistically they
@@ -2124,11 +2227,14 @@ END
$paramList = implode( ',',
array_map( array( 'Xml', 'encodeJsVar' ), $params ) );
- $toolbar.="addButton($paramList);\n";
+ $script .= "addButton($paramList);\n";
}
+ $toolbar .= Html::inlineScript( "\n$script\n" );
+
+ $toolbar .= "\n</div>";
+
+ wfRunHooks( 'EditPageBeforeEditToolbar', array( &$toolbar ) );
- $toolbar.="/*]]>*/\n</script>";
- $toolbar.="\n</div>";
return $toolbar;
}
@@ -2149,8 +2255,8 @@ END
$checkboxes = array();
$checkboxes['minor'] = '';
- $minorLabel = wfMsgExt('minoredit', array('parseinline'));
- if ( $wgUser->isAllowed('minoredit') ) {
+ $minorLabel = wfMsgExt( 'minoredit', array( 'parseinline' ) );
+ if ( $wgUser->isAllowed( 'minoredit' ) ) {
$attribs = array(
'tabindex' => ++$tabindex,
'accesskey' => wfMsg( 'accesskey-minoredit' ),
@@ -2158,10 +2264,10 @@ END
);
$checkboxes['minor'] =
Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
- "&nbsp;<label for='wpMinoredit'".$skin->tooltip('minoredit', 'withaccess').">{$minorLabel}</label>";
+ "&nbsp;<label for='wpMinoredit'" . $skin->tooltip( 'minoredit', 'withaccess' ) . ">{$minorLabel}</label>";
}
- $watchLabel = wfMsgExt('watchthis', array('parseinline'));
+ $watchLabel = wfMsgExt( 'watchthis', array( 'parseinline' ) );
$checkboxes['watch'] = '';
if ( $wgUser->isLoggedIn() ) {
$attribs = array(
@@ -2171,7 +2277,7 @@ END
);
$checkboxes['watch'] =
Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
- "&nbsp;<label for='wpWatchthis'".$skin->tooltip('watch', 'withaccess').">{$watchLabel}</label>";
+ "&nbsp;<label for='wpWatchthis'" . $skin->tooltip( 'watch', 'withaccess' ) . ">{$watchLabel}</label>";
}
wfRunHooks( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
return $checkboxes;
@@ -2186,8 +2292,6 @@ END
* @return array
*/
public function getEditButtons(&$tabindex) {
- global $wgLivePreview, $wgUser;
-
$buttons = array();
$temp = array(
@@ -2195,61 +2299,35 @@ END
'name' => 'wpSave',
'type' => 'submit',
'tabindex' => ++$tabindex,
- 'value' => wfMsg('savearticle'),
- 'accesskey' => wfMsg('accesskey-save'),
+ 'value' => wfMsg( 'savearticle' ),
+ 'accesskey' => wfMsg( 'accesskey-save' ),
'title' => wfMsg( 'tooltip-save' ).' ['.wfMsg( 'accesskey-save' ).']',
);
$buttons['save'] = Xml::element('input', $temp, '');
++$tabindex; // use the same for preview and live preview
- if ( $wgLivePreview && $wgUser->getOption( 'uselivepreview' ) ) {
- $temp = array(
- 'id' => 'wpPreview',
- 'name' => 'wpPreview',
- 'type' => 'submit',
- 'tabindex' => $tabindex,
- 'value' => wfMsg('showpreview'),
- 'accesskey' => '',
- 'title' => wfMsg( 'tooltip-preview' ).' ['.wfMsg( 'accesskey-preview' ).']',
- 'style' => 'display: none;',
- );
- $buttons['preview'] = Xml::element('input', $temp, '');
-
- $temp = array(
- 'id' => 'wpLivePreview',
- 'name' => 'wpLivePreview',
- 'type' => 'submit',
- 'tabindex' => $tabindex,
- 'value' => wfMsg('showlivepreview'),
-