summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxDispatcher.php23
-rw-r--r--includes/AjaxFunctions.php33
-rw-r--r--includes/AjaxResponse.php10
-rw-r--r--includes/Article.php708
-rw-r--r--includes/AuthPlugin.php4
-rw-r--r--includes/AutoLoader.php669
-rw-r--r--includes/Autopromote.php24
-rw-r--r--includes/BagOStuff.php135
-rw-r--r--includes/Block.php138
-rw-r--r--includes/CacheDependency.php46
-rw-r--r--includes/Category.php261
-rw-r--r--includes/CategoryPage.php125
-rw-r--r--includes/Categoryfinder.php2
-rw-r--r--includes/ChangesFeed.php129
-rw-r--r--includes/ChangesList.php471
-rw-r--r--includes/Credits.php4
-rw-r--r--includes/DatabaseFunctions.php4
-rw-r--r--includes/DefaultSettings.php1107
-rw-r--r--includes/Defines.php83
-rw-r--r--includes/DifferenceEngine.php367
-rw-r--r--includes/DjVuImage.php55
-rw-r--r--includes/DoubleRedirectJob.php166
-rw-r--r--includes/EditPage.php590
-rw-r--r--includes/EmaillingJob.php5
-rw-r--r--includes/EnotifNotifyJob.php24
-rw-r--r--includes/Exception.php91
-rw-r--r--includes/Exif.php15
-rw-r--r--includes/Export.php149
-rw-r--r--includes/ExternalEdit.php3
-rw-r--r--includes/ExternalStore.php34
-rw-r--r--includes/ExternalStoreDB.php20
-rw-r--r--includes/ExternalStoreHttp.php5
-rw-r--r--includes/FakeTitle.php2
-rw-r--r--includes/Feed.php2
-rw-r--r--includes/FeedUtils.php152
-rw-r--r--includes/FileDeleteForm.php142
-rw-r--r--includes/FileRevertForm.php91
-rw-r--r--includes/FileStore.php80
-rw-r--r--includes/FormOptions.php202
-rw-r--r--includes/GlobalFunctions.php632
-rw-r--r--includes/HTMLCacheUpdate.php33
-rw-r--r--includes/HTMLFileCache.php17
-rw-r--r--includes/HistoryBlob.php8
-rw-r--r--includes/Hooks.php7
-rw-r--r--includes/HttpFunctions.php3
-rw-r--r--includes/IP.php34
-rw-r--r--includes/ImageFunctions.php13
-rw-r--r--includes/ImageGallery.php29
-rw-r--r--includes/ImagePage.php647
-rw-r--r--includes/ImageQueryPage.php5
-rw-r--r--includes/JobQueue.php57
-rw-r--r--includes/Licenses.php5
-rw-r--r--includes/LinkBatch.php64
-rw-r--r--includes/LinkCache.php121
-rw-r--r--includes/LinkFilter.php1
-rw-r--r--includes/Linker.php464
-rw-r--r--includes/LinksUpdate.php227
-rw-r--r--includes/LogEventsList.php742
-rw-r--r--includes/LogPage.php112
-rw-r--r--includes/MacBinary.php4
-rw-r--r--includes/MagicWord.php68
-rw-r--r--includes/Math.php9
-rw-r--r--includes/MediaTransformOutput.php51
-rw-r--r--includes/MemcachedSessions.php4
-rw-r--r--includes/MessageCache.php726
-rw-r--r--includes/Metadata.php4
-rw-r--r--includes/MimeMagic.php51
-rw-r--r--includes/Namespace.php55
-rw-r--r--includes/NamespaceCompat.php9
-rw-r--r--includes/ObjectCache.php11
-rw-r--r--includes/OutputHandler.php16
-rw-r--r--includes/OutputPage.php451
-rw-r--r--includes/PageHistory.php258
-rw-r--r--includes/PageQueryPage.php9
-rw-r--r--includes/Pager.php260
-rw-r--r--includes/PatrolLog.php6
-rw-r--r--includes/PrefixSearch.php85
-rw-r--r--includes/Profiler.php243
-rw-r--r--includes/ProfilerSimple.php17
-rw-r--r--includes/ProfilerSimpleText.php35
-rw-r--r--includes/ProfilerSimpleUDP.php11
-rw-r--r--includes/ProfilerStub.php46
-rw-r--r--includes/ProtectionForm.php221
-rw-r--r--includes/ProxyTools.php48
-rw-r--r--includes/QueryPage.php56
-rw-r--r--includes/RawPage.php20
-rw-r--r--includes/RecentChange.php125
-rw-r--r--includes/RefreshLinksJob.php5
-rw-r--r--includes/Revision.php283
-rw-r--r--includes/Sanitizer.php35
-rw-r--r--includes/SearchEngine.php831
-rw-r--r--includes/SearchMySQL.php71
-rw-r--r--includes/SearchMySQL4.php60
-rw-r--r--includes/SearchOracle.php13
-rw-r--r--includes/SearchPostgres.php36
-rw-r--r--includes/SearchUpdate.php10
-rw-r--r--includes/Setup.php78
-rw-r--r--includes/SiteConfiguration.php10
-rw-r--r--includes/SiteStats.php19
-rw-r--r--includes/Skin.php229
-rw-r--r--includes/SkinTemplate.php167
-rw-r--r--includes/SpecialPage.php170
-rw-r--r--includes/SquidUpdate.php19
-rw-r--r--includes/Status.php194
-rw-r--r--includes/StreamFile.php8
-rw-r--r--includes/StringUtils.php49
-rw-r--r--includes/StubObject.php98
-rw-r--r--includes/Title.php619
-rw-r--r--includes/User.php905
-rw-r--r--includes/UserArray.php62
-rw-r--r--includes/UserMailer.php85
-rw-r--r--includes/UserRightsProxy.php45
-rw-r--r--includes/WatchedItem.php13
-rw-r--r--includes/WatchlistEditor.php105
-rw-r--r--includes/WebRequest.php150
-rw-r--r--includes/WebResponse.php2
-rw-r--r--includes/WebStart.php39
-rw-r--r--includes/Wiki.php281
-rw-r--r--includes/WikiError.php19
-rw-r--r--includes/Xml.php279
-rw-r--r--includes/XmlFunctions.php6
-rw-r--r--includes/XmlTypeCheck.php22
-rw-r--r--includes/ZhClient.php17
-rw-r--r--includes/ZhConversion.php4946
-rw-r--r--includes/api/ApiBase.php134
-rw-r--r--includes/api/ApiBlock.php10
-rw-r--r--includes/api/ApiDelete.php117
-rw-r--r--includes/api/ApiEditPage.php299
-rw-r--r--includes/api/ApiEmailUser.php114
-rw-r--r--includes/api/ApiExpandTemplates.php38
-rw-r--r--includes/api/ApiFeedWatchlist.php33
-rw-r--r--includes/api/ApiFormatBase.php51
-rw-r--r--includes/api/ApiFormatDbg.php5
-rw-r--r--includes/api/ApiFormatJson.php7
-rw-r--r--includes/api/ApiFormatJson_json.php19
-rw-r--r--includes/api/ApiFormatPhp.php5
-rw-r--r--includes/api/ApiFormatTxt.php5
-rw-r--r--includes/api/ApiFormatWddx.php5
-rw-r--r--includes/api/ApiFormatXml.php34
-rw-r--r--includes/api/ApiFormatYaml.php5
-rw-r--r--includes/api/ApiFormatYaml_spyc.php235
-rw-r--r--includes/api/ApiHelp.php7
-rw-r--r--includes/api/ApiLogin.php73
-rw-r--r--includes/api/ApiLogout.php10
-rw-r--r--includes/api/ApiMain.php150
-rw-r--r--includes/api/ApiMove.php50
-rw-r--r--includes/api/ApiOpenSearch.php22
-rw-r--r--includes/api/ApiPageSet.php200
-rw-r--r--includes/api/ApiParamInfo.php11
-rw-r--r--includes/api/ApiParse.php83
-rw-r--r--includes/api/ApiProtect.php15
-rw-r--r--includes/api/ApiQuery.php67
-rw-r--r--includes/api/ApiQueryAllCategories.php63
-rw-r--r--includes/api/ApiQueryAllLinks.php49
-rw-r--r--includes/api/ApiQueryAllUsers.php81
-rw-r--r--includes/api/ApiQueryAllimages.php205
-rw-r--r--includes/api/ApiQueryAllmessages.php25
-rw-r--r--includes/api/ApiQueryAllpages.php48
-rw-r--r--includes/api/ApiQueryBacklinks.php371
-rw-r--r--includes/api/ApiQueryBase.php208
-rw-r--r--includes/api/ApiQueryBlocks.php59
-rw-r--r--includes/api/ApiQueryCategories.php73
-rw-r--r--includes/api/ApiQueryCategoryInfo.php91
-rw-r--r--includes/api/ApiQueryCategoryMembers.php75
-rw-r--r--includes/api/ApiQueryDeletedrevs.php30
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php65
-rw-r--r--includes/api/ApiQueryExternalLinks.php51
-rw-r--r--includes/api/ApiQueryImageInfo.php162
-rw-r--r--includes/api/ApiQueryImages.php70
-rw-r--r--includes/api/ApiQueryInfo.php344
-rw-r--r--includes/api/ApiQueryLangLinks.php61
-rw-r--r--includes/api/ApiQueryLinks.php78
-rw-r--r--includes/api/ApiQueryLogEvents.php57
-rw-r--r--includes/api/ApiQueryRandom.php16
-rw-r--r--includes/api/ApiQueryRecentChanges.php120
-rw-r--r--includes/api/ApiQueryRevisions.php183
-rw-r--r--includes/api/ApiQuerySearch.php19
-rw-r--r--includes/api/ApiQuerySiteinfo.php304
-rw-r--r--includes/api/ApiQueryUserContributions.php69
-rw-r--r--includes/api/ApiQueryUserInfo.php18
-rw-r--r--includes/api/ApiQueryUsers.php47
-rw-r--r--includes/api/ApiQueryWatchlist.php29
-rw-r--r--includes/api/ApiResult.php36
-rw-r--r--includes/api/ApiRollback.php13
-rw-r--r--includes/api/ApiUnblock.php11
-rw-r--r--includes/api/ApiUndelete.php17
-rw-r--r--includes/cbt/CBTCompiler.php35
-rw-r--r--includes/cbt/CBTProcessor.php81
-rw-r--r--includes/cbt/README44
-rw-r--r--includes/db/Database.php2699
-rw-r--r--includes/db/DatabaseMssql.php1029
-rw-r--r--includes/db/DatabaseOracle.php720
-rw-r--r--includes/db/DatabasePostgres.php1394
-rw-r--r--includes/db/DatabaseSqlite.php405
-rw-r--r--includes/db/LBFactory.php261
-rw-r--r--includes/db/LBFactory_Multi.php233
-rw-r--r--includes/db/LoadBalancer.php918
-rw-r--r--includes/db/LoadMonitor.php121
-rw-r--r--includes/filerepo/ArchivedFile.php72
-rw-r--r--includes/filerepo/FSRepo.php44
-rw-r--r--includes/filerepo/File.php221
-rw-r--r--includes/filerepo/FileRepo.php174
-rw-r--r--includes/filerepo/FileRepoStatus.php151
-rw-r--r--includes/filerepo/ForeignAPIFile.php101
-rw-r--r--includes/filerepo/ForeignAPIRepo.php110
-rw-r--r--includes/filerepo/ForeignDBFile.php33
-rw-r--r--includes/filerepo/ForeignDBRepo.php13
-rw-r--r--includes/filerepo/ForeignDBViaLBRepo.php39
-rw-r--r--includes/filerepo/Image.php74
-rw-r--r--includes/filerepo/LocalFile.php533
-rw-r--r--includes/filerepo/LocalRepo.php103
-rw-r--r--includes/filerepo/NullRepo.php8
-rw-r--r--includes/filerepo/OldLocalFile.php192
-rw-r--r--includes/filerepo/README14
-rw-r--r--includes/filerepo/RepoGroup.php74
-rw-r--r--includes/filerepo/UnregisteredLocalFile.php9
-rw-r--r--includes/media/BMP.php8
-rw-r--r--includes/media/Bitmap.php22
-rw-r--r--includes/media/DjVu.php20
-rw-r--r--includes/media/Generic.php56
-rw-r--r--includes/media/SVG.php18
-rw-r--r--includes/memcached-client.php79
-rw-r--r--includes/mime.info15
-rw-r--r--includes/mime.types9
-rw-r--r--includes/normal/CleanUpTest.php3
-rw-r--r--includes/normal/Makefile9
-rw-r--r--includes/normal/RandomTest.php4
-rw-r--r--includes/normal/Utf8Case.php2078
-rw-r--r--includes/normal/Utf8CaseGenerate.php112
-rw-r--r--includes/normal/Utf8Test.php4
-rw-r--r--includes/normal/UtfNormal.php56
-rw-r--r--includes/normal/UtfNormalBench.php4
-rw-r--r--includes/normal/UtfNormalData.inc2
-rw-r--r--includes/normal/UtfNormalDataK.inc2
-rw-r--r--includes/normal/UtfNormalDefines.php53
-rw-r--r--includes/normal/UtfNormalGenerate.php4
-rw-r--r--includes/normal/UtfNormalTest.php4
-rw-r--r--includes/normal/UtfNormalUtil.php5
-rw-r--r--includes/parser/CoreParserFunctions.php385
-rw-r--r--includes/parser/DateFormatter.php283
-rw-r--r--includes/parser/Parser.php5002
-rw-r--r--includes/parser/ParserCache.php116
-rw-r--r--includes/parser/ParserOptions.php149
-rw-r--r--includes/parser/ParserOutput.php206
-rw-r--r--includes/parser/Parser_DiffTest.php87
-rw-r--r--includes/parser/Parser_OldPP.php4944
-rw-r--r--includes/parser/Preprocessor.php163
-rw-r--r--includes/parser/Preprocessor_DOM.php1421
-rw-r--r--includes/parser/Preprocessor_Hash.php1539
-rw-r--r--includes/proxy_check.php2
-rw-r--r--includes/specials/SpecialAllmessages.php217
-rw-r--r--includes/specials/SpecialAllpages.php404
-rw-r--r--includes/specials/SpecialAncientpages.php60
-rw-r--r--includes/specials/SpecialBlankpage.php6
-rw-r--r--includes/specials/SpecialBlockip.php494
-rw-r--r--includes/specials/SpecialBlockme.php37
-rw-r--r--includes/specials/SpecialBooksources.php110
-rw-r--r--includes/specials/SpecialBrokenRedirects.php93
-rw-r--r--includes/specials/SpecialCategories.php112
-rw-r--r--includes/specials/SpecialConfirmemail.php139
-rw-r--r--includes/specials/SpecialContributions.php470
-rw-r--r--includes/specials/SpecialDeadendpages.php62
-rw-r--r--includes/specials/SpecialDisambiguations.php108
-rw-r--r--includes/specials/SpecialDoubleRedirects.php103
-rw-r--r--includes/specials/SpecialEmailuser.php286
-rw-r--r--includes/specials/SpecialExport.php284
-rw-r--r--includes/specials/SpecialFewestrevisions.php74
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php135
-rw-r--r--includes/specials/SpecialFilepath.php53
-rw-r--r--includes/specials/SpecialImagelist.php161
-rw-r--r--includes/specials/SpecialImport.php1154
-rw-r--r--includes/specials/SpecialIpblocklist.php427
-rw-r--r--includes/specials/SpecialListgrouprights.php112
-rw-r--r--includes/specials/SpecialListredirects.php58
-rw-r--r--includes/specials/SpecialListusers.php235
-rw-r--r--includes/specials/SpecialLockdb.php131
-rw-r--r--includes/specials/SpecialLog.php65
-rw-r--r--includes/specials/SpecialLonelypages.php58
-rw-r--r--includes/specials/SpecialLongpages.php31
-rw-r--r--includes/specials/SpecialMIMEsearch.php138
-rw-r--r--includes/specials/SpecialMergeHistory.php448
-rw-r--r--includes/specials/SpecialMostcategories.php58
-rw-r--r--includes/specials/SpecialMostimages.php54
-rw-r--r--includes/specials/SpecialMostlinked.php95
-rw-r--r--includes/specials/SpecialMostlinkedcategories.php78
-rw-r--r--includes/specials/SpecialMostlinkedtemplates.php132
-rw-r--r--includes/specials/SpecialMostrevisions.php64
-rw-r--r--includes/specials/SpecialMovepage.php452
-rw-r--r--includes/specials/SpecialNewimages.php211
-rw-r--r--includes/specials/SpecialNewpages.php437
-rw-r--r--includes/specials/SpecialPopularpages.php67
-rw-r--r--includes/specials/SpecialPreferences.php1126
-rw-r--r--includes/specials/SpecialPrefixindex.php152
-rw-r--r--includes/specials/SpecialProtectedpages.php309
-rw-r--r--includes/specials/SpecialProtectedtitles.php216
-rw-r--r--includes/specials/SpecialRandompage.php100
-rw-r--r--includes/specials/SpecialRandomredirect.php19
-rw-r--r--includes/specials/SpecialRecentchanges.php662
-rw-r--r--includes/specials/SpecialRecentchangeslinked.php178
-rw-r--r--includes/specials/SpecialResetpass.php167
-rw-r--r--includes/specials/SpecialRevisiondelete.php1474
-rw-r--r--includes/specials/SpecialSearch.php651
-rw-r--r--includes/specials/SpecialShortpages.php98
-rw-r--r--includes/specials/SpecialSpecialpages.php82
-rw-r--r--includes/specials/SpecialStatistics.php93
-rw-r--r--includes/specials/SpecialUncategorizedcategories.php30
-rw-r--r--includes/specials/SpecialUncategorizedimages.php48
-rw-r--r--includes/specials/SpecialUncategorizedpages.php55
-rw-r--r--includes/specials/SpecialUncategorizedtemplates.php33
-rw-r--r--includes/specials/SpecialUndelete.php1276
-rw-r--r--includes/specials/SpecialUnlockdb.php107
-rw-r--r--includes/specials/SpecialUnusedcategories.php46
-rw-r--r--includes/specials/SpecialUnusedimages.php60
-rw-r--r--includes/specials/SpecialUnusedtemplates.php54
-rw-r--r--includes/specials/SpecialUnwatchedpages.php68
-rw-r--r--includes/specials/SpecialUpload.php1755
-rw-r--r--includes/specials/SpecialUploadMogile.php135
-rw-r--r--includes/specials/SpecialUserlogin.php929
-rw-r--r--includes/specials/SpecialUserlogout.php23
-rw-r--r--includes/specials/SpecialUserrights.php589
-rw-r--r--includes/specials/SpecialVersion.php391
-rw-r--r--includes/specials/SpecialWantedcategories.php90
-rw-r--r--includes/specials/SpecialWantedpages.php131
-rw-r--r--includes/specials/SpecialWatchlist.php383
-rw-r--r--includes/specials/SpecialWhatlinkshere.php408
-rw-r--r--includes/specials/SpecialWithoutinterwiki.php88
-rw-r--r--includes/templates/NoLocalSettings.php5
-rw-r--r--includes/templates/Userlogin.php68
-rw-r--r--includes/tidy.conf6
-rw-r--r--includes/zhtable/Makefile118
-rw-r--r--includes/zhtable/README21
-rw-r--r--includes/zhtable/simp2trad.manual96
-rw-r--r--includes/zhtable/simp2trad_noconvert.manual4
-rw-r--r--includes/zhtable/simp2trad_supp_set.manual2
-rw-r--r--includes/zhtable/simpphrases.manual389
-rw-r--r--includes/zhtable/simpphrases_exclude.manual0
-rw-r--r--includes/zhtable/toCN.manual11
-rw-r--r--includes/zhtable/toHK.manual10
-rw-r--r--includes/zhtable/toSG.manual6
-rw-r--r--includes/zhtable/toSimp.manual21
-rw-r--r--includes/zhtable/toTW.manual34
-rw-r--r--includes/zhtable/toTrad.manual42
-rw-r--r--includes/zhtable/trad2simp.manual11
-rw-r--r--includes/zhtable/trad2simp_noconvert.manual4
-rw-r--r--includes/zhtable/trad2simp_supp_set.manual1
-rw-r--r--includes/zhtable/trad2simp_supp_unset.manual0
-rw-r--r--includes/zhtable/tradphrases.manual1522
-rw-r--r--includes/zhtable/tradphrases_exclude.manual161
348 files changed, 67745 insertions, 8718 deletions
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index 7b85ed20..c489cf1c 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -1,5 +1,9 @@
<?php
/**
+ * @defgroup Ajax Ajax
+ *
+ * @file
+ * @ingroup Ajax
* Handle ajax requests and send them to the proper handler.
*/
@@ -11,7 +15,7 @@ require_once( 'AjaxFunctions.php' );
/**
* Object-Oriented Ajax functions.
- * @addtogroup Ajax
+ * @ingroup Ajax
*/
class AjaxDispatcher {
/** The way the request was made, either a 'get' or a 'post' */
@@ -58,6 +62,7 @@ class AjaxDispatcher {
break;
default:
+ wfProfileOut( __METHOD__ );
return;
# Or we could throw an exception:
#throw new MWException( __METHOD__ . ' called without any data (mode empty).' );
@@ -81,9 +86,13 @@ class AjaxDispatcher {
wfProfileIn( __METHOD__ );
if (! in_array( $this->func_name, $wgAjaxExportList ) ) {
+ wfDebug( __METHOD__ . ' Bad Request for unknown function ' . $this->func_name . "\n" );
+
wfHttpError( 400, 'Bad Request',
"unknown function " . (string) $this->func_name );
} else {
+ wfDebug( __METHOD__ . ' dispatching ' . $this->func_name . "\n" );
+
if ( strpos( $this->func_name, '::' ) !== false ) {
$func = explode( '::', $this->func_name, 2 );
} else {
@@ -93,6 +102,10 @@ class AjaxDispatcher {
$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 ) . "): "
+ . "no data returned\n" );
+
wfHttpError( 500, 'Internal Error',
"{$this->func_name} returned no data" );
}
@@ -103,9 +116,15 @@ class AjaxDispatcher {
$result->sendHeaders();
$result->printText();
+
+ 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" );
+
if (!headers_sent()) {
wfHttpError( 500, 'Internal Error',
$e->getMessage() );
@@ -119,5 +138,3 @@ class AjaxDispatcher {
$wgOut = null;
}
}
-
-
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index ffd3168a..9daca9e5 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -1,8 +1,7 @@
<?php
-
-/**
- * @package MediaWiki
- * @addtogroup Ajax
+/**
+ * @file
+ * @ingroup Ajax
*/
if( !defined( 'MEDIAWIKI' ) ) {
@@ -56,7 +55,7 @@ function js_unescape($source, $iconv_to = 'UTF-8') {
/**
* Function coverts number of utf char into that character.
- * Function taken from: http://sk2.php.net/manual/en/function.utf8-encode.php#49336
+ * Function taken from: http://www.php.net/manual/en/function.utf8-encode.php#49336
*
* @param $num Integer
* @return utf8char
@@ -76,7 +75,7 @@ function code2utf($num){
define( 'AJAX_SEARCH_VERSION', 2 ); //AJAX search cache version
function wfSajaxSearch( $term ) {
- global $wgContLang, $wgOut, $wgUser, $wgCapitalLinks, $wgMemc;
+ global $wgContLang, $wgUser, $wgCapitalLinks, $wgMemc;
$limit = 16;
$sk = $wgUser->getSkin();
$output = '';
@@ -84,7 +83,7 @@ function wfSajaxSearch( $term ) {
$term = trim( $term );
$term = $wgContLang->checkTitleEncoding( $wgContLang->recodeInput( js_unescape( $term ) ) );
if ( $wgCapitalLinks )
- $term = $wgContLang->ucfirst( $term );
+ $term = $wgContLang->ucfirst( $term );
$term_title = Title::newFromText( $term );
$memckey = $term_title ? wfMemcKey( 'ajaxsearch', md5( $term_title->getFullText() ) ) : wfMemcKey( 'ajaxsearch', md5( $term ) );
@@ -97,12 +96,12 @@ function wfSajaxSearch( $term ) {
$r = $more = '';
$canSearch = true;
-
+
$results = PrefixSearch::titleSearch( $term, $limit + 1 );
foreach( array_slice( $results, 0, $limit ) as $titleText ) {
$r .= '<li>' . $sk->makeKnownLink( $titleText ) . "</li>\n";
}
-
+
// Hack to check for specials
if( $results ) {
$t = Title::newFromText( $results[0] );
@@ -128,9 +127,10 @@ function wfSajaxSearch( $term ) {
$valid = (bool) $term_title;
$term_url = urlencode( $term );
- $term_diplay = htmlspecialchars( $valid ? $term_title->getFullText() : $term );
+ $term_normalized = $valid ? $term_title->getFullText() : $term;
+ $term_display = htmlspecialchars( $term );
$subtitlemsg = ( $valid ? 'searchsubtitle' : 'searchsubtitleinvalid' );
- $subtitle = wfMsgWikiHtml( $subtitlemsg, $term_diplay );
+ $subtitle = wfMsgExt( $subtitlemsg, array( 'parse' ), wfEscapeWikiText( $term_normalized ) );
$html = '<div id="searchTargetHide"><a onclick="Searching_Hide_Results();">'
. wfMsgHtml( 'hideresults' ) . '</a></div>'
. '<h1 class="firstHeading">'.wfMsgHtml('search')
@@ -138,15 +138,15 @@ function wfSajaxSearch( $term ) {
if( $canSearch ) {
$html .= '<ul><li>'
. $sk->makeKnownLink( $wgContLang->specialPage( 'Search' ),
- wfMsgHtml( 'searchcontaining', $term_diplay ),
+ wfMsgHtml( 'searchcontaining', $term_display ),
"search={$term_url}&fulltext=Search" )
. '</li><li>' . $sk->makeKnownLink( $wgContLang->specialPage( 'Search' ),
- wfMsgHtml( 'searchnamed', $term_diplay ) ,
+ wfMsgHtml( 'searchnamed', $term_display ) ,
"search={$term_url}&go=Go" )
. "</li></ul>";
}
if( $r ) {
- $html .= "<h2>" . wfMsgHtml( 'articletitles', $term_diplay ) . "</h2>"
+ $html .= "<h2>" . wfMsgHtml( 'articletitles', $term_display ) . "</h2>"
. '<ul>' .$r .'</ul>' . $more;
}
@@ -161,7 +161,7 @@ function wfSajaxSearch( $term ) {
* Called for AJAX watch/unwatch requests.
* @param $pagename Prefixed title string for page to watch/unwatch
* @param $watch String 'w' to watch, 'u' to unwatch
- * @return String '<w#>' or '<u#>' on successful watch or unwatch,
+ * @return String '<w#>' or '<u#>' on successful watch or unwatch,
* respectively, followed by an HTML message to display in the alert box; or
* '<err#>' on error
*/
@@ -169,7 +169,7 @@ function wfAjaxWatch($pagename = "", $watch = "") {
if(wfReadOnly()) {
// redirect to action=(un)watch, which will display the database lock
// message
- return '<err#>';
+ return '<err#>';
}
if('w' !== $watch && 'u' !== $watch) {
@@ -206,4 +206,3 @@ function wfAjaxWatch($pagename = "", $watch = "") {
return '<u#>'.wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
}
}
-
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index 8fa08539..c79e928b 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -1,11 +1,16 @@
<?php
+/**
+ * @file
+ * @ingroup Ajax
+ */
+
if( !defined( 'MEDIAWIKI' ) ) {
die( 1 );
}
/**
* @todo document
- * @addtogroup Ajax
+ * @ingroup Ajax
*/
class AjaxResponse {
@@ -99,7 +104,7 @@ class AjaxResponse {
if ( $this->mCacheDuration ) {
- # If squid caches are configured, tell them to cache the response,
+ # If squid caches are configured, tell them to cache the response,
# and tell the client to always check with the squid. Otherwise,
# tell the client to use a cached copy, without a way to purge it.
@@ -220,4 +225,3 @@ class AjaxResponse {
return true;
}
}
-
diff --git a/includes/Article.php b/includes/Article.php
index 0544db7d..4d8277bb 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -1,6 +1,7 @@
<?php
/**
* File for articles
+ * @file
*/
/**
@@ -34,6 +35,8 @@ class Article {
var $mTouched; //!<
var $mUser; //!<
var $mUserText; //!<
+ var $mRedirectTarget; //!<
+ var $mIsRedirect;
/**@}}*/
/**
@@ -57,12 +60,69 @@ class Article {
}
/**
+ * If this page is a redirect, get its target
+ *
+ * The target will be fetched from the redirect table if possible.
+ * If this page doesn't have an entry there, call insertRedirect()
+ * @return mixed Title object, or null if this page is not a redirect
+ */
+ public function getRedirectTarget() {
+ if(!$this->mTitle || !$this->mTitle->isRedirect())
+ return null;
+ if(!is_null($this->mRedirectTarget))
+ return $this->mRedirectTarget;
+
+ # Query the redirect table
+ $dbr = wfGetDB(DB_SLAVE);
+ $res = $dbr->select('redirect',
+ array('rd_namespace', 'rd_title'),
+ array('rd_from' => $this->getID()),
+ __METHOD__
+ );
+ $row = $dbr->fetchObject($res);
+ 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();
+ }
+
+ /**
+ * Insert an entry for this page into the redirect table.
+ *
+ * Don't call this function directly unless you know what you're doing.
+ * @return Title object
+ */
+ public function insertRedirect() {
+ $retval = Title::newFromRedirect($this->getContent());
+ if(!$retval)
+ return null;
+ $dbw = wfGetDB(DB_MASTER);
+ $dbw->replace('redirect', array('rd_from'), array(
+ 'rd_from' => $this->getID(),
+ 'rd_namespace' => $retval->getNamespace(),
+ 'rd_title' => $retval->getDBKey()
+ ), __METHOD__);
+ return $retval;
+ }
+
+ /**
+ * Get the Title object this page redirects to
+ *
* @return mixed false, Title of in-wiki target, or string with URL
*/
- function followRedirect() {
+ public function followRedirect() {
$text = $this->getContent();
+ return self::followRedirectText( $text );
+ }
+
+ /**
+ * Get the Title object this text redirects to
+ *
+ * @return mixed false, Title of in-wiki target, or string with URL
+ */
+ public function followRedirectText( $text ) {
$rt = Title::newFromRedirect( $text );
-
# process if title object is valid and not special:userlogout
if( $rt ) {
if( $rt->getInterwiki() != '' ) {
@@ -92,7 +152,6 @@ class Article {
return $rt;
}
}
-
// No or invalid redirect
return false;
}
@@ -114,6 +173,7 @@ class Article {
$this->mCurID = $this->mUser = $this->mCounter = -1; # Not loaded
$this->mRedirectedFrom = null; # Title object if set
+ $this->mRedirectTarget = null; # Title object if set
$this->mUserText =
$this->mTimestamp = $this->mComment = '';
$this->mGoodAdjustment = $this->mTotalAdjustment = 0;
@@ -138,7 +198,7 @@ class Article {
* @return Return the text of this revision
*/
function getContent() {
- global $wgUser, $wgOut;
+ global $wgUser, $wgOut, $wgMessageCache;
wfProfileIn( __METHOD__ );
@@ -147,12 +207,13 @@ class Article {
$wgOut->setRobotpolicy( 'noindex,nofollow' );
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ $wgMessageCache->loadAllMessages();
$ret = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
} else {
$ret = wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' );
}
- return "<div class='noarticletext'>$ret</div>";
+ return "<div class='noarticletext'>\n$ret\n</div>";
} else {
$this->loadContent();
wfProfileOut( __METHOD__ );
@@ -298,13 +359,13 @@ class Article {
*/
function loadPageData( $data = 'fromdb' ) {
if ( $data === 'fromdb' ) {
- $dbr = $this->getDB();
+ $dbr = wfGetDB( DB_MASTER );
$data = $this->pageDataFromId( $dbr, $this->getId() );
}
- $lc =& LinkCache::singleton();
+ $lc = LinkCache::singleton();
if ( $data ) {
- $lc->addGoodLinkObj( $data->page_id, $this->mTitle );
+ $lc->addGoodLinkObj( $data->page_id, $this->mTitle, $data->page_len, $data->page_is_redirect );
$this->mTitle->mArticleID = $data->page_id;
@@ -336,15 +397,13 @@ class Article {
return $this->mContent;
}
- $dbr = $this->getDB();
+ $dbr = wfGetDB( DB_MASTER );
# Pre-fill content with error message so that if something
# fails we'll have something telling us what we intended.
$t = $this->mTitle->getPrefixedText();
- if( $oldid ) {
- $t .= ',oldid='.$oldid;
- }
- $this->mContent = wfMsg( 'missingarticle', $t ) ;
+ $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
+ $this->mContent = wfMsg( 'missing-article', $t, $d ) ;
if( $oldid ) {
$revision = Revision::newFromId( $oldid );
@@ -377,15 +436,14 @@ class Article {
// FIXME: Horrible, horrible! This content-loading interface just plain sucks.
// We should instead work with the Revision object when we need it...
- $this->mContent = $revision->userCan( Revision::DELETED_TEXT ) ? $revision->getRawText() : "";
- //$this->mContent = $revision->getText();
+ $this->mContent = $revision->revText(); // Loads if user is allowed
$this->mUser = $revision->getUser();
$this->mUserText = $revision->getUserText();
$this->mComment = $revision->getComment();
$this->mTimestamp = wfTimestamp( TS_MW, $revision->getTimestamp() );
- $this->mRevIdFetched = $revision->getID();
+ $this->mRevIdFetched = $revision->getId();
$this->mContentLoaded = true;
$this->mRevision =& $revision;
@@ -407,8 +465,10 @@ class Article {
* Get the database which should be used for reads
*
* @return Database
+ * @deprecated - just call wfGetDB( DB_MASTER ) instead
*/
function getDB() {
+ wfDeprecated( __METHOD__ );
return wfGetDB( DB_MASTER );
}
@@ -490,6 +550,10 @@ class Article {
*/
function isRedirect( $text = false ) {
if ( $text === false ) {
+ if ( $this->mDataLoaded )
+ return $this->mIsRedirect;
+
+ // Apparently loadPageData was never called
$this->loadContent();
$titleObj = Title::newFromRedirect( $this->fetchContent() );
} else {
@@ -526,14 +590,14 @@ class Article {
$id = $this->getID();
if ( 0 == $id ) return;
- $this->mLastRevision = Revision::loadFromPageId( $this->getDB(), $id );
+ $this->mLastRevision = Revision::loadFromPageId( wfGetDB( DB_MASTER ), $id );
if( !is_null( $this->mLastRevision ) ) {
$this->mUser = $this->mLastRevision->getUser();
$this->mUserText = $this->mLastRevision->getUserText();
$this->mTimestamp = $this->mLastRevision->getTimestamp();
$this->mComment = $this->mLastRevision->getComment();
$this->mMinorEdit = $this->mLastRevision->isMinor();
- $this->mRevIdFetched = $this->mLastRevision->getID();
+ $this->mRevIdFetched = $this->mLastRevision->getId();
}
}
@@ -571,7 +635,6 @@ class Article {
}
/**
- * @todo Document, fixme $offset never used.
* @param $limit Integer: default 0.
* @param $offset Integer: default 0.
*/
@@ -593,6 +656,8 @@ class Article {
ORDER BY timestamp DESC";
if ($limit > 0) { $sql .= ' LIMIT '.$limit; }
+ if ($offset > 0) { $sql .= ' OFFSET '.$offset; }
+
$sql .= ' '. $this->getSelectOptions();
$res = $dbr->query($sql, __METHOD__);
@@ -609,7 +674,7 @@ class Article {
* This is the default action of the script: just view the page of
* the given title.
*/
- function view() {
+ function view() {
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgParser;
global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
@@ -618,7 +683,7 @@ class Article {
wfProfileIn( __METHOD__ );
- $parserCache =& ParserCache::singleton();
+ $parserCache = ParserCache::singleton();
$ns = $this->mTitle->getNamespace(); # shortcut
# Get variables from query string
@@ -689,12 +754,7 @@ class Article {
}
# Should the parser cache be used?
- $pcache = $wgEnableParserCache
- && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
- && $this->exists()
- && empty( $oldid )
- && !$this->mTitle->isCssOrJsPage()
- && !$this->mTitle->isCssJsSubpage();
+ $pcache = $this->useParserCache( $oldid );
wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" );
if ( $wgUser->getOption( 'stubthreshold' ) ) {
wfIncrStats( 'pcache_miss_stub' );
@@ -705,7 +765,6 @@ class Article {
// This is an internally redirected page view.
// We'll need a backlink to the source page for navigation.
if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
- $sk = $wgUser->getSkin();
$redir = $sk->makeKnownLinkObj( $this->mRedirectedFrom, '', 'redirect=no' );
$s = wfMsg( 'redirectedfrom', $redir );
$wgOut->setSubtitle( $s );
@@ -722,7 +781,6 @@ class Article {
// If it was reported from a trusted site, supply a backlink.
global $wgRedirectSources;
if( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
- $sk = $wgUser->getSkin();
$redir = $sk->makeExternalLink( $rdfrom, $rdfrom );
$s = wfMsg( 'redirectedfrom', $redir );
$wgOut->setSubtitle( $s );
@@ -740,16 +798,17 @@ class Article {
$outputDone = true;
}
}
+ # Fetch content and check for errors
if ( !$outputDone ) {
$text = $this->getContent();
if ( $text === false ) {
# Failed to load, replace text with error message
$t = $this->mTitle->getPrefixedText();
if( $oldid ) {
- $t .= ',oldid='.$oldid;
- $text = wfMsg( 'missingarticle', $t );
+ $d = wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid );
+ $text = wfMsg( 'missing-article', $t, $d );
} else {
- $text = wfMsg( 'noarticletext', $t );
+ $text = wfMsg( 'noarticletext' );
}
}
@@ -757,11 +816,11 @@ class Article {
if ( !$this->mTitle->userCanRead() ) {
$wgOut->loginToUse();
$wgOut->output();
+ wfProfileOut( __METHOD__ );
exit;
}
# We're looking at an old revision
-
if ( !empty( $oldid ) ) {
$wgOut->setRobotpolicy( 'noindex,nofollow' );
if( is_null( $this->mRevision ) ) {
@@ -772,6 +831,7 @@ class Article {
if( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
$wgOut->addWikiMsg( 'rev-deleted-text-permission' );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ wfProfileOut( __METHOD__ );
return;
} else {
$wgOut->addWikiMsg( 'rev-deleted-text-view' );
@@ -779,16 +839,13 @@ class Article {
}
}
}
-
}
- }
- if( !$outputDone ) {
- $wgOut->setRevisionId( $this->getRevIdFetched() );
+ $wgOut->setRevisionId( $this->getRevIdFetched() );
+
// Pages containing custom CSS or JavaScript get special treatment
if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
$wgOut->addHtml( wfMsgExt( 'clearyourcache', 'parse' ) );
-
// Give hooks a chance to customise the output
if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
// Wrap the whole lot in a <pre> and don't parse
@@ -798,22 +855,9 @@ class Article {
$wgOut->addHtml( htmlspecialchars( $this->mContent ) );
$wgOut->addHtml( "\n</pre>\n" );
}
-
- }
-
- elseif ( $rt = Title::newFromRedirect( $text ) ) {
- # Display redirect
- $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
- $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
- # Don't overwrite the subtitle if this was an old revision
- if( !$wasRedirected && $this->isCurrent() ) {
- $wgOut->setSubtitle( wfMsgHtml( 'redirectpagesub' ) );
- }
- $link = $sk->makeLinkObj( $rt, $rt->getFullText() );
-
- $wgOut->addHTML( '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
- '<span class="redirectText">'.$link.'</span>' );
-
+ } else if ( $rt = Title::newFromRedirect( $text ) ) {
+ # Don't append the subtitle if this was an old revision
+ $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() );
$parseout = $wgParser->parse($text, $this->mTitle, ParserOptions::newFromUser($wgUser));
$wgOut->addParserOutputNoText( $parseout );
} else if ( $pcache ) {
@@ -840,7 +884,6 @@ class Article {
if( !$this->isCurrent() ) {
$wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
}
-
}
}
/* title may have been set from the cache */
@@ -850,8 +893,7 @@ class Article {
}
# check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
- if( $ns == NS_USER_TALK &&
- User::isIP( $this->mTitle->getText() ) ) {
+ if( $ns == NS_USER_TALK && IP::isValid( $this->mTitle->getText() ) ) {
$wgOut->addWikiMsg('anontalkpagetext');
}
@@ -875,6 +917,41 @@ class Article {
$this->viewUpdates();
wfProfileOut( __METHOD__ );
}
+
+ /*
+ * Should the parser cache be used?
+ */
+ protected function useParserCache( $oldid ) {
+ global $wgUser, $wgEnableParserCache;
+
+ return $wgEnableParserCache
+ && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
+ && $this->exists()
+ && empty( $oldid )
+ && !$this->mTitle->isCssOrJsPage()
+ && !$this->mTitle->isCssJsSubpage();
+ }
+
+ protected function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
+ global $wgParser, $wgOut, $wgContLang, $wgStylePath, $wgUser;
+
+ # Display redirect
+ $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
+ $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
+
+ if( $appendSubtitle ) {
+ $wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) );
+ }
+ $sk = $wgUser->getSkin();
+ if ( $forceKnown )
+ $link = $sk->makeKnownLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+ else
+ $link = $sk->makeLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+
+ $wgOut->addHTML( '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
+ '<span class="redirectText">'.$link.'</span>' );
+
+ }
function addTrackbacks() {
global $wgOut, $wgUser;
@@ -897,6 +974,7 @@ class Article {
. $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
$rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
+ $tbtext .= "\n";
$tbtext .= wfMsg(strlen($o->tb_ex) ? 'trackbackexcerpt' : 'trackback',
$o->tb_title,
$o->tb_url,
@@ -1067,7 +1145,6 @@ class Article {
$result = $dbw->affectedRows() != 0;
if ($result) {
- // FIXME: Should the result from updateRedirectOn() be returned instead?
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
}
@@ -1112,6 +1189,8 @@ class Article {
$dbw->delete( 'redirect', $where, __METHOD__);
}
+ if( $this->getTitle()->getNamespace() == NS_IMAGE )
+ RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() );
wfProfileOut( __METHOD__ );
return ( $dbw->affectedRows() != 0 );
}
@@ -1252,10 +1331,10 @@ class Article {
}
}
- $extraq = ''; // Give extensions a chance to modify URL query on update
- wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraq ) );
+ $extraQuery = ''; // Give extensions a chance to modify URL query on update
+ wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraQuery ) );
- $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraq );
+ $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraQuery );
}
return $good;
}
@@ -1291,11 +1370,12 @@ class Article {
* EDIT_NEW is specified and the article does exist, a duplicate key error will cause an exception
* to be thrown from the Database. These two conditions are also possible with auto-detection due
* to MediaWiki's performance-optimised locking strategy.
+ * @param $baseRevId, the revision ID this edit was based off, if any
*
* @return bool success
*/
- function doEdit( $text, $summary, $flags = 0 ) {
- global $wgUser, $wgDBtransactions;
+ function doEdit( $text, $summary, $flags = 0, $baseRevId = false ) {
+ global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
wfProfileIn( __METHOD__ );
$good = true;
@@ -1325,9 +1405,10 @@ class Article {
$oldtext = $this->getContent();
$oldsize = strlen( $oldtext );
- # Provide autosummaries if one is not provided.
- if ($flags & EDIT_AUTOSUMMARY && $summary == '')
+ # Provide autosummaries if one is not provided and autosummaries are enabled.
+ if( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
$summary = $this->getAutosummary( $oldtext, $text, $flags );
+ }
$editInfo = $this->prepareTextForEdit( $text );
$text = $editInfo->pst;
@@ -1346,7 +1427,7 @@ class Article {
$lastRevision = 0;
$revisionId = 0;
-
+
$changed = ( strcmp( $text, $oldtext ) != 0 );
if ( $changed ) {
@@ -1368,7 +1449,8 @@ class Article {
'page' => $this->getId(),
'comment' => $summary,
'minor_edit' => $isminor,
- 'text' => $text
+ 'text' => $text,
+ 'parent_id' => $lastRevision
) );
$dbw->begin();
@@ -1382,6 +1464,8 @@ class Article {
$good = false;
$dbw->rollback();
} else {
+ wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId ) );
+
# Update recentchanges
if( !( $flags & EDIT_SUPPRESS_RC ) ) {
$rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary,
@@ -1414,7 +1498,7 @@ class Article {
# Invalidate cache of this article and all pages using this article
# as a template. Partly deferred.
Article::onArticleEdit( $this->mTitle );
-
+
# Update links tables, site stats, etc.
$this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
}
@@ -1446,6 +1530,8 @@ class Article {
# Update the page record with revision data
$this->updateRevisionOn( $dbw, $revision, 0 );
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
if( !( $flags & EDIT_SUPPRESS_RC ) ) {
$rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot,
@@ -1495,15 +1581,16 @@ class Article {
*
* @param boolean $noRedir Add redirect=no
* @param string $sectionAnchor section to redirect to, including "#"
+ * @param string $extraQuery, extra query params
*/
- function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) {
+ function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
global $wgOut;
if ( $noRedir ) {
$query = 'redirect=no';
- if( $extraq )
+ if( $extraQuery )
$query .= "&$query";
} else {
- $query = $extraq;
+ $query = $extraQuery;
}
$wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
}
@@ -1518,8 +1605,8 @@ class Article {
# Check patrol config options
if ( !($wgUseNPPatrol || $wgUseRCPatrol)) {
- $wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
- return;
+ $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
+ return;
}
# If we haven't been given an rc_id value, we can't do anything
@@ -1527,18 +1614,18 @@ class Article {
$rc = $rcid ? RecentChange::newFromId($rcid) : null;
if ( is_null ( $rc ) )
{
- $wgOut->errorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
+ $wgOut->showErrorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
return;
}
- if ( !$wgUseRCPatrol && $rc->mAttribs['rc_type'] != RC_NEW) {
+ if ( !$wgUseRCPatrol && $rc->getAttribute( 'rc_type' ) != RC_NEW) {
// Only new pages can be patrolled if the general patrolling is off....???
// @fixme -- is this necessary? Shouldn't we only bother controlling the
// front end here?
- $wgOut->errorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
+ $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
-
+
# Check permissions
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'patrol', $wgUser );
@@ -1554,7 +1641,7 @@ class Article {
}
#It would be nice to see where the user had actually come from, but for now just guess
- $returnto = $rc->mAttribs['rc_type'] == RC_NEW ? 'Newpages' : 'Recentchanges';
+ $returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
$return = Title::makeTitle( NS_SPECIAL, $returnto );
# If it's left up to us, check that the user is allowed to patrol this edit
@@ -1575,10 +1662,14 @@ class Article {
}
}
- # Mark the edit as patrolled
- RecentChange::markPatrolled( $rcid );
- PatrolLog::record( $rcid );
- wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
+ # Check that the revision isn't patrolled already
+ # Prevents duplicate log entries
+ if( !$rc->getAttribute( 'rc_patrolled' ) ) {
+ # Mark the edit as patrolled
+ RecentChange::markPatrolled( $rcid );
+ PatrolLog::record( $rcid );
+ wfRunHooks( 'MarkPatrolledComplete', array( &$rcid, &$wgUser, false ) );
+ }
# Inform the user
$wgOut->setPageTitle( wfMsg( 'markedaspatrolled' ) );
@@ -1724,8 +1815,8 @@ class Article {
$updated = Article::flattenRestrictions( $limit );
$changed = ( $current != $updated );
- $changed = $changed || ($this->mTitle->areRestrictionsCascading() != $cascade);
- $changed = $changed || ($this->mTitle->mRestrictionsExpiry != $expiry);
+ $changed = $changed || ($updated && $this->mTitle->areRestrictionsCascading() != $cascade);
+ $changed = $changed || ($updated && $this->mTitle->mRestrictionsExpiry != $expiry);
$protect = ( $updated != '' );
# If nothing's changed, do nothing
@@ -1751,12 +1842,16 @@ class Article {
}
$comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $this->mTitle->getPrefixedText() ) );
- foreach( $limit as $action => $restrictions ) {
- # Check if the group level required to edit also can protect pages
- # Otherwise, people who cannot normally protect can "protect" pages via transclusion
- $cascade = ( $cascade && isset($wgGroupPermissions[$restrictions]['protect']) && $wgGroupPermissions[$restrictions]['protect'] );
+ # Only restrictions with the 'protect' right can cascade...
+ # Otherwise, people who cannot normally protect can "protect" pages via transclusion
+ foreach( $limit as $action => $restriction ) {
+ # FIXME: can $restriction be an array or what? (same as fixme above)
+ if( $restriction != 'protect' && $restriction != 'sysop' ) {
+ $cascade = false;
+ break;
+ }
}
-
+
$cascade_description = '';
if ($cascade) {
$cascade_description = ' ['.wfMsg('protect-summary-cascade').']';
@@ -1770,8 +1865,7 @@ class Article {
$comment .= "$expiry_description";
if ( $cascade )
$comment .= "$cascade_description";
-
- $rowsAffected = false;
+
# Update restrictions table
foreach( $limit as $action => $restrictions ) {
if ($restrictions != '' ) {
@@ -1779,18 +1873,11 @@ class Article {
array( 'pr_page' => $id, 'pr_type' => $action
, 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0
, 'pr_expiry' => $encodedExpiry ), __METHOD__ );
- if($dbw->affectedRows() != 0)
- $rowsAffected = true;
} else {
$dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
'pr_type' => $action ), __METHOD__ );
- if($dbw->affectedRows() != 0)
- $rowsAffected = true;
}
}
- if(!$rowsAffected)
- // No change
- return true;
# Insert a null revision
$nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
@@ -1806,15 +1893,15 @@ class Article {
'page_id' => $id
), 'Article::protect'
);
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $nullRevision, false) );
wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, $limit, $reason ) );
# Update the protection log
$log = new LogPage( 'protect' );
-
-
-
if( $protect ) {
- $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
+ $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle,
+ trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
@@ -1845,7 +1932,7 @@ class Article {
}
return implode( ':', $bits );
}
-
+
/**
* Auto-generates a deletion reason
* @param bool &$hasHistory Whether the page has a history
@@ -1907,7 +1994,7 @@ class Article {
else
$reason = wfMsgForContent('excontent', '$1');
}
-
+
// Replace newlines with spaces to prevent uglyness
$contents = preg_replace("/[\n\r]/", ' ', $contents);
// Calculate the maximum amount of chars to get
@@ -1930,18 +2017,20 @@ class Article {
$confirm = $wgRequest->wasPosted() &&
$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
-
+
$this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' );
$this->DeleteReason = $wgRequest->getText( 'wpReason' );
-
+
$reason = $this->DeleteReasonList;
-
+
if ( $reason != 'other' && $this->DeleteReason != '') {
// Entry from drop down menu + additional comment
$reason .= ': ' . $this->DeleteReason;
} elseif ( $reason == 'other' ) {
$reason = $this->DeleteReason;
}
+ # Flag to hide all contents of the archived revisions
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('suppressrevision');
# This code desperately needs to be totally rewritten
@@ -1950,7 +2039,7 @@ class Article {
$wgOut->readOnlyPage();
return;
}
-
+
# Check permissions
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
@@ -1980,7 +2069,7 @@ class Article {
}
if( $confirm ) {
- $this->doDelete( $reason );
+ $this->doDelete( $reason, $suppress );
if( $wgRequest->getCheck( 'wpWatch' ) ) {
$this->doWatch();
} elseif( $this->mTitle->userIsWatching() ) {
@@ -2003,10 +2092,10 @@ class Article {
array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
}
}
-
- return $this->confirmDelete( '', $reason );
+
+ return $this->confirmDelete( $reason );
}
-
+
/**
* @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
*/
@@ -2018,7 +2107,7 @@ class Article {
}
return false;
}
-
+
/**
* @return int approximate revision count
*/
@@ -2079,10 +2168,9 @@ class Article {
/**
* Output deletion confirmation dialog
- * @param $par string FIXME: do we need this parameter? One Call from Article::delete with '' only.
* @param $reason string Prefilled reason
*/
- function confirmDelete( $par, $reason ) {
+ function confirmDelete( $reason ) {
global $wgOut, $wgUser, $wgContLang;
$align = $wgContLang->isRtl() ? 'left' : 'right';
@@ -2092,9 +2180,17 @@ class Article {
$wgOut->setRobotpolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'confirmdeletetext' );
- $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->mTitle->getLocalURL( 'action=delete' . $par ), 'id' => 'deleteconfirm' ) ) .
- Xml::openElement( 'fieldset' ) .
- Xml::element( 'legend', array(), wfMsg( 'delete-legend' ) ) .
+ if( $wgUser->isAllowed( 'suppressrevision' ) ) {
+ $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\"><td></td><td>";
+ $suppress .= Xml::checkLabel( wfMsg( 'revdelete-suppress' ), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '2' ) );
+ $suppress .= "</td></tr>";
+ } else {
+ $suppress = '';
+ }
+
+ $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' ) ) ) .
Xml::openElement( 'table' ) .
"<tr id=\"wpDeleteReasonListRow\">
<td align='$align'>" .
@@ -2102,7 +2198,7 @@ class Article {
"</td>
<td>" .
Xml::listDropDown( 'wpDeleteReasonList',
- wfMsgForContent( 'deletereason-dropdown' ),
+ wfMsgForContent( 'deletereason-dropdown' ),
wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) .
"</td>
</tr>
@@ -2120,6 +2216,7 @@ class Article {
Xml::checkLabel( wfMsg( 'watchthis' ), 'wpWatch', 'wpWatch', $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching(), array( 'tabindex' => '3' ) ) .
"</td>
</tr>
+ $suppress
<tr>
<td></td>
<td>" .
@@ -2131,6 +2228,12 @@ class Article {
Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
Xml::closeElement( 'form' );
+ if ( $wgUser->isAllowed( 'editinterface' ) ) {
+ $skin = $wgUser->getSkin();
+ $link = $skin->makeLink ( 'MediaWiki:Deletereason-dropdown', wfMsgHtml( 'delete-edit-reasonlist' ) );
+ $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
+ }
+
$wgOut->addHTML( $form );
$this->showLogExtract( $wgOut );
}
@@ -2140,25 +2243,24 @@ class Article {
* Show relevant lines from the deletion log
*/
function showLogExtract( $out ) {
- $out->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . '</h2>' );
- $logViewer = new LogViewer(
- new LogReader(
- new FauxRequest(
- array( 'page' => $this->mTitle->getPrefixedText(),
- 'type' => 'delete' ) ) ) );
- $logViewer->showList( $out );
+ $out->addHtml( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
+ LogEventsList::showLogExtract( $out, 'delete', $this->mTitle->getPrefixedText() );
}
/**
* Perform a deletion and output success or failure messages
*/
- function doDelete( $reason ) {
+ function doDelete( $reason, $suppress = false ) {
global $wgOut, $wgUser;
wfDebug( __METHOD__."\n" );
+
+ $id = $this->getId();
+
+ $error = '';
- if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
- if ( $this->doDeleteArticle( $reason ) ) {
+ if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason, &$error))) {
+ if ( $this->doDeleteArticle( $reason, $suppress ) ) {
$deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
@@ -2168,9 +2270,12 @@ class Article {
$wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
$wgOut->returnToMain( false );
- wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason));
+ wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason, $id));
} else {
- $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
+ if ($error = '')
+ $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
+ else
+ $wgOut->showFatalError( $error );
}
}
}
@@ -2180,7 +2285,7 @@ class Article {
* Deletes the article with database consistency, writes logs, purges caches
* Returns success
*/
- function doDeleteArticle( $reason ) {
+ function doDeleteArticle( $reason, $suppress = false ) {
global $wgUseSquid, $wgDeferredUpdateList;
global $wgUseTrackbacks;
@@ -2198,6 +2303,19 @@ class Article {
$u = new SiteStatsUpdate( 0, 1, -(int)$this->isCountable( $this->getContent() ), -1 );
array_push( $wgDeferredUpdateList, $u );
+ // Bitfields to further suppress the content
+ if ( $suppress ) {
+ $bitfield = 0;
+ // This should be 15...
+ $bitfield |= Revision::DELETED_TEXT;
+ $bitfield |= Revision::DELETED_COMMENT;
+ $bitfield |= Revision::DELETED_USER;
+ $bitfield |= Revision::DELETED_RESTRICTED;
+ } else {
+ $bitfield = 'rev_deleted';
+ }
+
+ $dbw->begin();
// For now, shunt the revision data into the archive table.
// Text is *not* removed from the text table; bulk storage
// is left intact to avoid breaking block-compression or
@@ -2221,8 +2339,9 @@ class Article {
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len',
+ 'ar_len' => 'rev_len',
'ar_page_id' => 'page_id',
+ 'ar_deleted' => $bitfield
), array(
'page_id' => $id,
'page_id = rev_page'
@@ -2232,12 +2351,25 @@ class Article {
# Delete restrictions for it
$dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
+ # 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;
+ }
+ $this->updateCategoryCounts( array(), $cats );
+
# Now that it's safely backed up, delete it
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__);
+ $ok = ( $dbw->affectedRows() > 0 ); // getArticleId() uses slave, could be laggy
+ if( !$ok ) {
+ $dbw->rollback();
+ return false;
+ }
# If using cascading deletes, we can skip some explicit deletes
if ( !$dbw->cascadingDeletes() ) {
-
$dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
if ($wgUseTrackbacks)
@@ -2257,19 +2389,26 @@ class Article {
if ( !$dbw->cleanupTriggers() ) {
# Clean up recentchanges entries...
- $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__ );
+ $dbw->delete( 'recentchanges',
+ array( 'rc_namespace' => $ns, 'rc_title' => $t, 'rc_type != '.RC_LOG ),
+ __METHOD__ );
}
+ $dbw->commit();
# Clear caches
Article::onArticleDelete( $this->mTitle );
- # Log the deletion
- $log = new LogPage( 'delete' );
- $log->addEntry( 'delete', $this->mTitle, $reason );
-
# Clear the cached article id so the interface doesn't act like we exist
$this->mTitle->resetArticleID( 0 );
$this->mTitle->mArticleID = 0;
+
+ # Log the deletion, if the page was suppressed, log it at Oversight instead
+ $logtype = $suppress ? 'suppress' : 'delete';
+ $log = new LogPage( $logtype );
+
+ # Make sure logging got through
+ $log->addEntry( 'delete', $this->mTitle, $reason, array() );
+
return true;
}
@@ -2280,15 +2419,15 @@ class Article {
* performs permissions checks on $wgUser, then calls commitRollback()
* to do the dirty work
*
- * @param string $fromP - Name of the user whose edits to rollback.
+ * @param string $fromP - Name of the user whose edits to rollback.
* @param string $summary - Custom summary. Set to default summary if empty.
* @param string $token - Rollback token.
* @param bool $bot - If true, mark all reverted edits as bot.
- *
+ *
* @param array $resultDetails contains result-specific array of additional values
* 'alreadyrolled' : 'current' (rev)
* success : 'summary' (str), 'current' (rev), 'target' (rev)
- *
+ *
* @return array of errors, each error formatted as
* array(messagekey, param1, param2, ...).
* On success, the array is empty. This array can also be passed to
@@ -2310,10 +2449,10 @@ class Article {
# If there were errors, bail out now
if(!empty($errors))
return $errors;
-
+
return $this->commitRollback($fromP, $summary, $bot, $resultDetails);
}
-
+
/**
* Backend implementation of doRollback(), please refer there for parameter
* and return value documentation
@@ -2322,9 +2461,9 @@ class Article {
* rollback to the DB Therefore, you should only call this function direct-
* ly if you want to use custom permissions checks. If you don't, use
* doRollback() instead.
- */
+ */
public function commitRollback($fromP, $summary, $bot, &$resultDetails) {
- global $wgUseRCPatrol, $wgUser;
+ global $wgUseRCPatrol, $wgUser, $wgLang;
$dbw = wfGetDB( DB_MASTER );
if( wfReadOnly() ) {
@@ -2352,7 +2491,7 @@ class Article {
$user = intval( $current->getUser() );
$user_text = $dbw->addQuotes( $current->getUserText() );
$s = $dbw->selectRow( 'revision',
- array( 'rev_id', 'rev_timestamp' ),
+ array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
array( 'rev_page' => $current->getPage(),
"rev_user <> {$user} OR rev_user_text <> {$user_text}"
), __METHOD__,
@@ -2362,8 +2501,11 @@ class Article {
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 ) {
+ # Only admins can see this text
+ return array(array('notvisiblerev'));
}
-
+
$set = array();
if ( $bot && $wgUser->isAllowed('markbotedits') ) {
# Mark all reverted edits as bot
@@ -2386,15 +2528,17 @@ class Article {
# Generate the edit summary if necessary
$target = Revision::newFromId( $s->rev_id );
- if( empty( $summary ) )
- {
- global $wgLang;
- $summary = wfMsgForContent( 'revertpage',
- $target->getUserText(), $from,
- $s->rev_id, $wgLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp), true),
- $current->getId(), $wgLang->timeanddate($current->getTimestamp())
- );
+ if( empty( $summary ) ){
+ $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())
+ );
+ $summary = wfMsgReplaceArgs( $summary, $args );
# Save
$flags = EDIT_UPDATE;
@@ -2404,7 +2548,7 @@ class Article {
if( $bot && ($wgUser->isAllowed('markbotedits') || $wgUser->isAllowed('bot')) )
$flags |= EDIT_FORCE_BOT;
- $this->doEdit( $target->getText(), $summary, $flags );
+ $this->doEdit( $target->getText(), $summary, $flags, $target->getId() );
wfRunHooks( 'ArticleRollbackComplete', array( $this, $wgUser, $target ) );
@@ -2439,6 +2583,19 @@ class Article {
$wgOut->rateLimited();
return;
}
+ 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'] ) ){
+ $current = $details['current'];
+ if( $current->getComment() != '' ) {
+ $wgOut->addWikiMsgArray( 'editcomment', array( $wgUser->getSkin()->formatComment( $current->getComment() ) ), array( 'replaceafter' ) );
+ }
+ }
+ return;
+ }
# Display permissions errors before read-only message -- there's no
# point in misleading the user into thinking the inability to rollback
# is only temporary.
@@ -2469,6 +2626,11 @@ class Article {
. $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 ) ) {
+ $de = new DifferenceEngine( $this->mTitle, $current->getId(), 'next', false, true );
+ $de->showDiff( '', '' );
+ }
}
@@ -2477,11 +2639,12 @@ class Article {
* @private
*/
function viewUpdates() {
- global $wgDeferredUpdateList;
+ global $wgDeferredUpdateList, $wgUser;
if ( 0 != $this->getID() ) {
+ # Don't update page view counters on views from bot users (bug 14044)
global $wgDisableCounters;
- if( !$wgDisableCounters ) {
+ if( !$wgDisableCounters && !$wgUser->isAllowed( 'bot' ) ) {
Article::incViewCount( $this->getID() );
$u = new SiteStatsUpdate( 1, 0, 0 );
array_push( $wgDeferredUpdateList, $u );
@@ -2489,7 +2652,6 @@ class Article {
}
# Update newtalk / watchlist notification status
- global $wgUser;
$wgUser->clearNotification( $this->mTitle );
}
@@ -2546,7 +2708,7 @@ class Article {
# Save it to the parser cache
if ( $wgEnableParserCache ) {
- $parserCache =& ParserCache::singleton();
+ $parserCache = ParserCache::singleton();
$parserCache->save( $editInfo->output, $this, $wgUser );
}
@@ -2646,7 +2808,7 @@ class Article {
$sk = $wgUser->getSkin();
$lnk = $current
? wfMsg( 'currentrevisionlink' )
- : $lnk = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
+ : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
$curdiff = $current
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=cur&oldid='.$oldid );
@@ -2664,16 +2826,38 @@ class Article {
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
- $userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
- . $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
+ $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');
+ } 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>";
+ }
+ $cdel = "(<small>$cdel</small>) ";
+ }
+ # Show user links if allowed to see them. Normally they
+ # are hidden regardless, but since we can already see the text here...
+ $userlinks = $sk->revUserTools( $revision, false );
$m = wfMsg( 'revision-info-current' );
$infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
? 'revision-info-current'
: 'revision-info';
-
+
$r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsg( $infomsg, $td, $userlinks ) . "</div>\n" .
- "\n\t\t\t\t<div id=\"mw-revision-nav\">" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
+
+ "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
@@ -2731,9 +2915,9 @@ class Article {
$printable = $wgRequest->getVal( 'printable' );
$page = $wgRequest->getVal( 'page' );
- //check for non-standard user language; this covers uselang,
+ //check for non-standard user language; this covers uselang,
//and extensions for auto-detecting user language.
- $ulang = $wgLang->getCode();
+ $ulang = $wgLang->getCode();
$clang = $wgContLang->getCode();
$cacheable = $wgUseFileCache
@@ -2814,6 +2998,8 @@ class Article {
$revision->insertOn( $dbw );
$this->updateRevisionOn( $dbw, $revision );
$dbw->commit();
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
wfProfileOut( __METHOD__ );
}
@@ -2911,6 +3097,15 @@ class Article {
static function onArticleDelete( $title ) {
global $wgUseFileCache, $wgMessageCache;
+ // Update existence markers on article/talk tabs...
+ if( $title->isTalkPage() ) {
+ $other = $title->getSubjectPage();
+ } else {
+ $other = $title->getTalkPage();
+ }
+ $other->invalidateCache();
+ $other->purgeSquid();
+
$title->touchLinks();
$title->purgeSquid();
@@ -2920,13 +3115,20 @@ class Article {
@unlink( $cm->fileCacheName() );
}
- if( $title->getNamespace() == NS_MEDIAWIKI) {
+ # Messages
+ if( $title->getNamespace() == NS_MEDIAWIKI ) {
$wgMessageCache->replace( $title->getDBkey(), false );
}
+ # Images
if( $title->getNamespace() == NS_IMAGE ) {
$update = new HTMLCacheUpdate( $title, 'imagelinks' );
$update->doUpdate();
}
+ # User talk pages
+ if( $title->getNamespace() == NS_USER_TALK ) {
+ $user = User::newFromName( $title->getText(), false );
+ $user->setNewtalk( false );
+ }
}
/**
@@ -2940,7 +3142,7 @@ class Article {
// Invalidate the caches of all pages which redirect here
$wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'redirect' );
-
+
# Purge squid for this page only
$title->purgeSquid();
@@ -2954,6 +3156,15 @@ class Article {
/**#@-*/
/**
+ * Overriden by ImagePage class, only present here to avoid a fatal error
+ * Called for ?action=revert
+ */
+ public function revert(){
+ global $wgOut;
+ $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
+ }
+
+ /**
* Info about this page
* Called for ?action=info when $wgAllowPageInfo is on.
*
@@ -3080,38 +3291,33 @@ class Article {
}
/**
- * Return an auto-generated summary if the text provided is a redirect.
+ * Returns a list of hidden categories this page is a member of.
+ * Uses the page_props and categorylinks tables.
*
- * @param string $text The wikitext to check
- * @return string '' or an appropriate summary
+ * @return array Array of Title objects
*/
- public static function getRedirectAutosummary( $text ) {
- $rt = Title::newFromRedirect( $text );
- if( is_object( $rt ) )
- return wfMsgForContent( 'autoredircomment', $rt->getFullText() );
- else
- return '';
- }
+ function getHiddenCategories() {
+ $result = array();
+ $id = $this->mTitle->getArticleID();
+ if( $id == 0 ) {
+ return array();
+ }
- /**
- * Return an auto-generated summary if the new text is much shorter than
- * the old text.
- *
- * @param string $oldtext The previous text of the page
- * @param string $text The submitted text of the page
- * @return string An appropriate autosummary, or an empty string.
- */
- public static function getBlankingAutosummary( $oldtext, $text ) {
- if ($oldtext!='' && $text=='') {
- return wfMsgForContent('autosumm-blank');
- } elseif (strlen($oldtext) > 10 * strlen($text) && strlen($text) < 500) {
- #Removing more than 90% of the article
- global $wgContLang;
- $truncatedtext = $wgContLang->truncate($text, max(0, 200 - strlen(wfMsgForContent('autosumm-replace'))), '...');
- return wfMsgForContent('autosumm-replace', $truncatedtext);
- } else {
- return '';
+ $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'),
+ 'Article:getHiddenCategories' );
+ if ( false !== $res ) {
+ if ( $dbr->numRows( $res ) ) {
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
+ }
+ }
}
+ $dbr->freeResult( $res );
+ return $result;
}
/**
@@ -3122,38 +3328,42 @@ class Article {
* @return string An appropriate autosummary, or an empty string.
*/
public static function getAutosummary( $oldtext, $newtext, $flags ) {
+ # Decide what kind of autosummary is needed.
- # This code is UGLY UGLY UGLY.
- # Somebody PLEASE come up with a more elegant way to do it.
-
- #Redirect autosummaries
- $summary = self::getRedirectAutosummary( $newtext );
-
- if ($summary)
- return $summary;
-
- #Blanking autosummaries
- if (!($flags & EDIT_NEW))
- $summary = self::getBlankingAutosummary( $oldtext, $newtext );
-
- if ($summary)
- return $summary;
+ # Redirect autosummaries
+ $rt = Title::newFromRedirect( $newtext );
+ if( is_object( $rt ) ) {
+ return wfMsgForContent( 'autoredircomment', $rt->getFullText() );
+ }
- #New page autosummaries
- if ($flags & EDIT_NEW && strlen($newtext)) {
- #If they're making a new article, give its text, truncated, in the summary.
+ # New page autosummaries
+ 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),
max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new') ) ),
'...' );
- $summary = wfMsgForContent( 'autosumm-new', $truncatedtext );
+ return wfMsgForContent( 'autosumm-new', $truncatedtext );
}
- if ($summary)
- return $summary;
+ # Blanking autosummaries
+ if( $oldtext != '' && $newtext == '' ) {
+ return wfMsgForContent('autosumm-blank');
+ } elseif( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500) {
+ # Removing more than 90% of the article
+ global $wgContLang;
+ $truncatedtext = $wgContLang->truncate(
+ $newtext,
+ max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ),
+ '...'
+ );
+ return wfMsgForContent( 'autosumm-replace', $truncatedtext );
+ }
- return $summary;
+ # If we reach this point, there's no applicable autosummary for our case, so our
+ # autosummary is empty.
+ return '';
}
/**
@@ -3175,7 +3385,7 @@ class Article {
$popts->setTidy(false);
$popts->enableLimitReport( false );
if ( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
- $parserCache =& ParserCache::singleton();
+ $parserCache = ParserCache::singleton();
$parserCache->save( $parserOutput, $this, $wgUser );
}
@@ -3235,4 +3445,60 @@ class Article {
$wgOut->addParserOutput( $parserOutput );
}
+ /**
+ * Update all the appropriate counts in the category table, given that
+ * we've added the categories $added and deleted the categories $deleted.
+ *
+ * @param $added array The names of categories that were added
+ * @param $deleted array The names of categories that were deleted
+ * @return null
+ */
+ public function updateCategoryCounts( $added, $deleted ) {
+ $ns = $this->mTitle->getNamespace();
+ $dbw = wfGetDB( DB_MASTER );
+
+ # First make sure the rows exist. If one of the "deleted" ones didn't
+ # exist, we might legitimately not create it, but it's simpler to just
+ # create it and then give it a negative value, since the value is bogus
+ # anyway.
+ #
+ # Sometimes I wish we had INSERT ... ON DUPLICATE KEY UPDATE.
+ $insertCats = array_merge( $added, $deleted );
+ if( !$insertCats ) {
+ # Okay, nothing to do
+ return;
+ }
+ $insertRows = array();
+ foreach( $insertCats as $cat ) {
+ $insertRows[] = array( '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 ) {
+ $addFields[] = 'cat_subcats = cat_subcats + 1';
+ $removeFields[] = 'cat_subcats = cat_subcats - 1';
+ } elseif( $ns == NS_IMAGE ) {
+ $addFields[] = 'cat_files = cat_files + 1';
+ $removeFields[] = 'cat_files = cat_files - 1';
+ }
+
+ if ( $added ) {
+ $dbw->update(
+ 'category',
+ $addFields,
+ array( 'cat_title' => $added ),
+ __METHOD__
+ );
+ }
+ if ( $deleted ) {
+ $dbw->update(
+ 'category',
+ $removeFields,
+ array( 'cat_title' => $deleted ),
+ __METHOD__
+ );
+ }
+ }
}
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index 2ad137e2..7717e001 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -230,7 +230,7 @@ class AuthPlugin {
* @param $autocreate bool True if user is being autocreated on login
* @public
*/
- function initUser( $user, $autocreate=false ) {
+ function initUser( &$user, $autocreate=false ) {
# Override this to do something.
}
@@ -242,5 +242,3 @@ class AuthPlugin {
return $username;
}
}
-
-
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 2e2083b2..4f36784a 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -4,352 +4,265 @@
ini_set('unserialize_callback_func', '__autoload' );
-function __autoload($className) {
- global $wgAutoloadClasses;
-
+class AutoLoader {
# Locations of core classes
# Extension classes are specified with $wgAutoloadClasses
static $localClasses = array(
# Includes
'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
- 'AjaxCachePolicy' => 'includes/AjaxFunctions.php',
'AjaxResponse' => 'includes/AjaxResponse.php',
'AlphabeticPager' => 'includes/Pager.php',
+ 'APCBagOStuff' => 'includes/BagOStuff.php',
+ 'ArrayDiffFormatter' => 'includes/DifferenceEngine.php',
'Article' => 'includes/Article.php',
+ 'AtomFeed' => 'includes/Feed.php',
'AuthPlugin' => 'includes/AuthPlugin.php',
'Autopromote' => 'includes/Autopromote.php',
'BagOStuff' => 'includes/BagOStuff.php',
- 'HashBagOStuff' => 'includes/BagOStuff.php',
- 'SqlBagOStuff' => 'includes/BagOStuff.php',
- 'MediaWikiBagOStuff' => 'includes/BagOStuff.php',
- 'TurckBagOStuff' => 'includes/BagOStuff.php',
- 'APCBagOStuff' => 'includes/BagOStuff.php',
- 'eAccelBagOStuff' => 'includes/BagOStuff.php',
- 'XCacheBagOStuff' => 'includes/BagOStuff.php',
- 'DBABagOStuff' => 'includes/BagOStuff.php',
'Block' => 'includes/Block.php',
- 'HTMLFileCache' => 'includes/HTMLFileCache.php',
- 'DependencyWrapper' => 'includes/CacheDependency.php',
- 'FileDependency' => 'includes/CacheDependency.php',
- 'TitleDependency' => 'includes/CacheDependency.php',
- 'TitleListDependency' => 'includes/CacheDependency.php',
+ 'CacheDependency' => 'includes/CacheDependency.php',
+ 'Category' => 'includes/Category.php',
+ 'Categoryfinder' => 'includes/Categoryfinder.php',
'CategoryPage' => 'includes/CategoryPage.php',
'CategoryViewer' => 'includes/CategoryPage.php',
- 'Categoryfinder' => 'includes/Categoryfinder.php',
- 'RCCacheEntry' => 'includes/ChangesList.php',
'ChangesList' => 'includes/ChangesList.php',
- 'OldChangesList' => 'includes/ChangesList.php',
- 'EnhancedChangesList' => 'includes/ChangesList.php',
- 'CoreParserFunctions' => 'includes/CoreParserFunctions.php',
- 'DBObject' => 'includes/Database.php',
- 'Database' => 'includes/Database.php',
- 'DatabaseMysql' => 'includes/Database.php',
- 'ResultWrapper' => 'includes/Database.php',
- 'DatabasePostgres' => 'includes/DatabasePostgres.php',
- 'DatabaseOracle' => 'includes/DatabaseOracle.php',
- 'DateFormatter' => 'includes/DateFormatter.php',
+ 'ChangesFeed' => 'includes/ChangesFeed.php',
+ 'ChannelFeed' => 'includes/Feed.php',
+ 'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
+ 'ConstantDependency' => 'includes/CacheDependency.php',
+ 'DBABagOStuff' => 'includes/BagOStuff.php',
+ 'DependencyWrapper' => 'includes/CacheDependency.php',
+ '_DiffEngine' => 'includes/DifferenceEngine.php',
'DifferenceEngine' => 'includes/DifferenceEngine.php',
- '_DiffOp' => 'includes/DifferenceEngine.php',
- '_DiffOp_Copy' => 'includes/DifferenceEngine.php',
- '_DiffOp_Delete' => 'includes/DifferenceEngine.php',
+ 'DiffFormatter' => 'includes/DifferenceEngine.php',
+ 'Diff' => 'includes/DifferenceEngine.php',
'_DiffOp_Add' => 'includes/DifferenceEngine.php',
'_DiffOp_Change' => 'includes/DifferenceEngine.php',
- '_DiffEngine' => 'includes/DifferenceEngine.php',
- 'Diff' => 'includes/DifferenceEngine.php',
- 'MappedDiff' => 'includes/DifferenceEngine.php',
- 'DiffFormatter' => 'includes/DifferenceEngine.php',
- 'UnifiedDiffFormatter' => 'includes/DifferenceEngine.php',
- 'ArrayDiffFormatter' => 'includes/DifferenceEngine.php',
+ '_DiffOp_Copy' => 'includes/DifferenceEngine.php',
+ '_DiffOp_Delete' => 'includes/DifferenceEngine.php',
+ '_DiffOp' => 'includes/DifferenceEngine.php',
'DjVuImage' => 'includes/DjVuImage.php',
- '_HWLDF_WordAccumulator' => 'includes/DifferenceEngine.php',
- 'WordLevelDiff' => 'includes/DifferenceEngine.php',
- 'TableDiffFormatter' => 'includes/DifferenceEngine.php',
- 'EditPage' => 'includes/EditPage.php',
- 'MWException' => 'includes/Exception.php',
- 'Exif' => 'includes/Exif.php',
- 'FormatExif' => 'includes/Exif.php',
- 'WikiExporter' => 'includes/Export.php',
- 'XmlDumpWriter' => 'includes/Export.php',
- 'DumpOutput' => 'includes/Export.php',
- 'DumpFileOutput' => 'includes/Export.php',
- 'DumpPipeOutput' => 'includes/Export.php',
- 'DumpGZipOutput' => 'includes/Export.php',
- 'DumpBZip2Output' => 'includes/Export.php',
+ 'DoubleReplacer' => 'includes/StringUtils.php',
+ 'DoubleRedirectJob' => 'includes/DoubleRedirectJob.php',
'Dump7ZipOutput' => 'includes/Export.php',
+ 'DumpBZip2Output' => 'includes/Export.php',
+ 'DumpFileOutput' => 'includes/Export.php',
'DumpFilter' => 'includes/Export.php',
- 'DumpNotalkFilter' => 'includes/Export.php',
- 'DumpNamespaceFilter' => 'includes/Export.php',
+ 'DumpGZipOutput' => 'includes/Export.php',
'DumpLatestFilter' => 'includes/Export.php',
'DumpMultiWriter' => 'includes/Export.php',
+ 'DumpNamespaceFilter' => 'includes/Export.php',
+ 'DumpNotalkFilter' => 'includes/Export.php',
+ 'DumpOutput' => 'includes/Export.php',
+ 'DumpPipeOutput' => 'includes/Export.php',
+ 'eAccelBagOStuff' => 'includes/BagOStuff.php',
+ 'EditPage' => 'includes/EditPage.php',
+ 'EmaillingJob' => 'includes/EmaillingJob.php',
+ 'EmailNotification' => 'includes/UserMailer.php',
+ 'EnhancedChangesList' => 'includes/ChangesList.php',
+ 'EnotifNotifyJob' => 'includes/EnotifNotifyJob.php',
+ 'ErrorPageError' => 'includes/Exception.php',
+ 'Exif' => 'includes/Exif.php',
'ExternalEdit' => 'includes/ExternalEdit.php',
- 'ExternalStore' => 'includes/ExternalStore.php',
'ExternalStoreDB' => 'includes/ExternalStoreDB.php',
'ExternalStoreHttp' => 'includes/ExternalStoreHttp.php',
+ 'ExternalStore' => 'includes/ExternalStore.php',
+ 'FatalError' => 'includes/Exception.php',
'FakeTitle' => 'includes/FakeTitle.php',
+ 'FauxRequest' => 'includes/WebRequest.php',
'FeedItem' => 'includes/Feed.php',
- 'ChannelFeed' => 'includes/Feed.php',
- 'RSSFeed' => 'includes/Feed.php',
- 'AtomFeed' => 'includes/Feed.php',
+ 'FeedUtils' => 'includes/FeedUtils.php',
+ 'FileDeleteForm' => 'includes/FileDeleteForm.php',
+ 'FileDependency' => 'includes/CacheDependency.php',
+ 'FileRevertForm' => 'includes/FileRevertForm.php',
'FileStore' => 'includes/FileStore.php',
+ 'FormatExif' => 'includes/Exif.php',
+ 'FormOptions' => 'includes/FormOptions.php',
'FSException' => 'includes/FileStore.php',
'FSTransaction' => 'includes/FileStore.php',
+ 'GlobalDependency' => 'includes/CacheDependency.php',
+ 'HashBagOStuff' => 'includes/BagOStuff.php',
+ 'HashtableReplacer' => 'includes/StringUtils.php',
+ 'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
'HistoryBlob' => 'includes/HistoryBlob.php',
- 'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
'HistoryBlobStub' => 'includes/HistoryBlob.php',
- 'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php',
+ 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
+ 'HTMLFileCache' => 'includes/HTMLFileCache.php',
'Http' => 'includes/HttpFunctions.php',
- 'IP' => 'includes/IP.php',
+ '_HWLDF_WordAccumulator' => 'includes/DifferenceEngine.php',
'ImageGallery' => 'includes/ImageGallery.php',
- 'ImagePage' => 'includes/ImagePage.php',
'ImageHistoryList' => 'includes/ImagePage.php',
- 'FileDeleteForm' => 'includes/FileDeleteForm.php',
- 'FileRevertForm' => 'includes/FileRevertForm.php',
+ 'ImagePage' => 'includes/ImagePage.php',
+ 'ImageQueryPage' => 'includes/ImageQueryPage.php',
+ 'IncludableSpecialPage' => 'includes/SpecialPage.php',
+ 'IndexPager' => 'includes/Pager.php',
+ 'IP' => 'includes/IP.php',
'Job' => 'includes/JobQueue.php',
- 'EmaillingJob' => 'includes/EmaillingJob.php',
- 'EnotifNotifyJob' => 'includes/EnotifNotifyJob.php',
- 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
- 'RefreshLinksJob' => 'includes/RefreshLinksJob.php',
- 'Licenses' => 'includes/Licenses.php',
'License' => 'includes/Licenses.php',
+ 'Licenses' => 'includes/Licenses.php',
'LinkBatch' => 'includes/LinkBatch.php',
'LinkCache' => 'includes/LinkCache.php',
- 'LinkFilter' => 'includes/LinkFilter.php',
'Linker' => 'includes/Linker.php',
+ 'LinkFilter' => 'includes/LinkFilter.php',
'LinksUpdate' => 'includes/LinksUpdate.php',
- 'LoadBalancer' => 'includes/LoadBalancer.php',
'LogPage' => 'includes/LogPage.php',
+ 'LogPager' => 'includes/LogEventsList.php',
+ 'LogEventsList' => 'includes/LogEventsList.php',
+ 'LogReader' => 'includes/LogEventsList.php',
+ 'LogViewer' => 'includes/LogEventsList.php',
'MacBinary' => 'includes/MacBinary.php',
- 'MagicWord' => 'includes/MagicWord.php',
'MagicWordArray' => 'includes/MagicWord.php',
+ 'MagicWord' => 'includes/MagicWord.php',
+ 'MailAddress' => 'includes/UserMailer.php',
+ 'MappedDiff' => 'includes/DifferenceEngine.php',
'MathRenderer' => 'includes/Math.php',
- 'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
- 'ThumbnailImage' => 'includes/MediaTransformOutput.php',
'MediaTransformError' => 'includes/MediaTransformOutput.php',
- 'TransformParameterError' => 'includes/MediaTransformOutput.php',
+ 'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
+ 'MediaWikiBagOStuff' => 'includes/BagOStuff.php',
+ 'MediaWiki_I18N' => 'includes/SkinTemplate.php',
+ 'MediaWiki' => 'includes/Wiki.php',
+ 'memcached' => 'includes/memcached-client.php',
'MessageCache' => 'includes/MessageCache.php',
'MimeMagic' => 'includes/MimeMagic.php',
- 'Namespace' => 'includes/Namespace.php',
- 'FakeMemCachedClient' => 'includes/ObjectCache.php',
+ 'MWException' => 'includes/Exception.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',
- 'IndexPager' => 'includes/Pager.php',
- 'ReverseChronologicalPager' => 'includes/Pager.php',
- 'TablePager' => 'includes/Pager.php',
- 'Parser' => 'includes/Parser.php',
- 'Parser_OldPP' => 'includes/Parser_OldPP.php',
- 'Parser_DiffTest' => 'includes/Parser_DiffTest.php',
- 'ParserCache' => 'includes/ParserCache.php',
- 'ParserOutput' => 'includes/ParserOutput.php',
- 'ParserOptions' => 'includes/ParserOptions.php',
+ 'PageHistoryPager' => 'includes/PageHistory.php',
+ 'PageQueryPage' => 'includes/PageQueryPage.php',
+ 'Pager' => 'includes/Pager.php',
+ 'PasswordError' => 'includes/User.php',
'PatrolLog' => 'includes/PatrolLog.php',
- 'Preprocessor' => 'includes/Preprocessor.php',
+ 'PostgresSearchResult' => 'includes/SearchPostgres.php',
+ 'PostgresSearchResultSet' => 'includes/SearchPostgres.php',
'PrefixSearch' => 'includes/PrefixSearch.php',
- 'PPFrame' => 'includes/Preprocessor.php',
- 'PPNode' => 'includes/Preprocessor.php',
- 'Preprocessor_DOM' => 'includes/Preprocessor_DOM.php',
- 'PPFrame_DOM' => 'includes/Preprocessor_DOM.php',
- 'PPTemplateFrame_DOM' => 'includes/Preprocessor_DOM.php',
- 'PPDStack' => 'includes/Preprocessor_DOM.php',
- 'PPDStackElement' => 'includes/Preprocessor_DOM.php',
- 'PPNode_DOM' => 'includes/Preprocessor_DOM.php',
- 'Preprocessor_Hash' => 'includes/Preprocessor_Hash.php',
+ 'Profiler' => 'includes/Profiler.php',
'ProfilerSimple' => 'includes/ProfilerSimple.php',
+ 'ProfilerSimpleText' => 'includes/ProfilerSimpleText.php',
'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
- 'Profiler' => 'includes/Profiler.php',
- 'ProxyTools' => 'includes/ProxyTools.php',
'ProtectionForm' => 'includes/ProtectionForm.php',
'QueryPage' => 'includes/QueryPage.php',
- 'PageQueryPage' => 'includes/PageQueryPage.php',
- 'ImageQueryPage' => 'includes/ImageQueryPage.php',
+ 'QuickTemplate' => 'includes/SkinTemplate.php',
'RawPage' => 'includes/RawPage.php',
+ 'RCCacheEntry' => 'includes/ChangesList.php',
'RecentChange' => 'includes/RecentChange.php',
+ 'RefreshLinksJob' => 'includes/RefreshLinksJob.php',
+ 'RegexlikeReplacer' => 'includes/StringUtils.php',
+ 'ReplacementArray' => 'includes/StringUtils.php',
+ 'Replacer' => 'includes/StringUtils.php',
+ 'ReverseChronologicalPager' => 'includes/Pager.php',
'Revision' => 'includes/Revision.php',
+ 'RSSFeed' => 'includes/Feed.php',
'Sanitizer' => 'includes/Sanitizer.php',
- 'SearchEngine' => 'includes/SearchEngine.php',
- 'SearchResultSet' => 'includes/SearchEngine.php',
- 'SearchResult' => 'includes/SearchEngine.php',
'SearchEngineDummy' => 'includes/SearchEngine.php',
- 'SearchMySQL' => 'includes/SearchMySQL.php',
- 'MySQLSearchResultSet' => 'includes/SearchMySQL.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',
- 'SearchOracle' => 'includes/SearchOracle.php',
'SiteConfiguration' => 'includes/SiteConfiguration.php',
'SiteStats' => 'includes/SiteStats.php',
'SiteStatsUpdate' => 'includes/SiteStats.php',
'Skin' => 'includes/Skin.php',
- 'MediaWiki_I18N' => 'includes/SkinTemplate.php',
'SkinTemplate' => 'includes/SkinTemplate.php',
- 'QuickTemplate' => 'includes/SkinTemplate.php',
- 'SpecialAllpages' => 'includes/SpecialAllpages.php',
- 'AncientPagesPage' => 'includes/SpecialAncientpages.php',
- 'IPBlockForm' => 'includes/SpecialBlockip.php',
- 'SpecialBookSources' => 'includes/SpecialBooksources.php',
- 'BrokenRedirectsPage' => 'includes/SpecialBrokenRedirects.php',
- 'EmailConfirmation' => 'includes/SpecialConfirmemail.php',
- 'ContributionsPage' => 'includes/SpecialContributions.php',
- 'DeadendPagesPage' => 'includes/SpecialDeadendpages.php',
- 'DisambiguationsPage' => 'includes/SpecialDisambiguations.php',
- 'DoubleRedirectsPage' => 'includes/SpecialDoubleRedirects.php',
- 'EmailUserForm' => 'includes/SpecialEmailuser.php',
- 'WikiRevision' => 'includes/SpecialImport.php',
- 'WikiImporter' => 'includes/SpecialImport.php',
- 'ImportStringSource' => 'includes/SpecialImport.php',
- 'ImportStreamSource' => 'includes/SpecialImport.php',
- 'IPUnblockForm' => 'includes/SpecialIpblocklist.php',
- 'ListredirectsPage' => 'includes/SpecialListredirects.php',
- 'DBLockForm' => 'includes/SpecialLockdb.php',
- 'LogReader' => 'includes/SpecialLog.php',
- 'LogViewer' => 'includes/SpecialLog.php',
- 'LonelyPagesPage' => 'includes/SpecialLonelypages.php',
- 'LongPagesPage' => 'includes/SpecialLongpages.php',
- 'MIMEsearchPage' => 'includes/SpecialMIMEsearch.php',
- 'MostcategoriesPage' => 'includes/SpecialMostcategories.php',
- 'MostimagesPage' => 'includes/SpecialMostimages.php',
- 'MostlinkedPage' => 'includes/SpecialMostlinked.php',
- 'MostlinkedCategoriesPage' => 'includes/SpecialMostlinkedcategories.php',
- 'SpecialMostlinkedtemplates' => 'includes/SpecialMostlinkedtemplates.php',
- 'MostrevisionsPage' => 'includes/SpecialMostrevisions.php',
- 'FewestrevisionsPage' => 'includes/SpecialFewestrevisions.php',
- 'MovePageForm' => 'includes/SpecialMovepage.php',
- 'NewbieContributionsPage' => 'includes/SpecialNewbieContributions.php',
- 'NewPagesPage' => 'includes/SpecialNewpages.php',
+ 'SpecialMycontributions' => 'includes/SpecialPage.php',
+ 'SpecialMypage' => 'includes/SpecialPage.php',
+ 'SpecialMytalk' => 'includes/SpecialPage.php',
'SpecialPage' => 'includes/SpecialPage.php',
- 'UnlistedSpecialPage' => 'includes/SpecialPage.php',
- 'IncludableSpecialPage' => 'includes/SpecialPage.php',
- 'PopularPagesPage' => 'includes/SpecialPopularpages.php',
- 'PreferencesForm' => 'includes/SpecialPreferences.php',
- 'SpecialPrefixindex' => 'includes/SpecialPrefixindex.php',
- 'RandomPage' => 'includes/SpecialRandompage.php',
- 'SpecialRandomredirect' => 'includes/SpecialRandomredirect.php',
- 'PasswordResetForm' => 'includes/SpecialResetpass.php',
- 'RevisionDeleteForm' => 'includes/SpecialRevisiondelete.php',
- 'RevisionDeleter' => 'includes/SpecialRevisiondelete.php',
- 'SpecialSearch' => 'includes/SpecialSearch.php',
- 'ShortPagesPage' => 'includes/SpecialShortpages.php',
- 'UncategorizedCategoriesPage' => 'includes/SpecialUncategorizedcategories.php',
- 'UncategorizedPagesPage' => 'includes/SpecialUncategorizedpages.php',
- 'UncategorizedTemplatesPage' => 'includes/SpecialUncategorizedtemplates.php',
- 'PageArchive' => 'includes/SpecialUndelete.php',
- 'UndeleteForm' => 'includes/SpecialUndelete.php',
- 'DBUnlockForm' => 'includes/SpecialUnlockdb.php',
- 'UnusedCategoriesPage' => 'includes/SpecialUnusedcategories.php',
- 'UnusedimagesPage' => 'includes/SpecialUnusedimages.php',
- 'UnusedtemplatesPage' => 'includes/SpecialUnusedtemplates.php',
- 'UnwatchedpagesPage' => 'includes/SpecialUnwatchedpages.php',
- 'UploadForm' => 'includes/SpecialUpload.php',
- 'UploadFormMogile' => 'includes/SpecialUploadMogile.php',
- 'LoginForm' => 'includes/SpecialUserlogin.php',
- 'UserrightsPage' => 'includes/SpecialUserrights.php',
- 'SpecialVersion' => 'includes/SpecialVersion.php',
- 'WantedCategoriesPage' => 'includes/SpecialWantedcategories.php',
- 'WantedPagesPage' => 'includes/SpecialWantedpages.php',
- 'WhatLinksHerePage' => 'includes/SpecialWhatlinkshere.php',
- 'WithoutInterwikiPage' => 'includes/SpecialWithoutinterwiki.php',
+ 'SpecialRedirectToSpecial' => 'includes/SpecialPage.php',
+ 'SqlBagOStuff' => 'includes/BagOStuff.php',
'SquidUpdate' => 'includes/SquidUpdate.php',
- 'ReplacementArray' => 'includes/StringUtils.php',
- 'Replacer' => 'includes/StringUtils.php',
- 'RegexlikeReplacer' => 'includes/StringUtils.php',
- 'DoubleReplacer' => 'includes/StringUtils.php',
- 'HashtableReplacer' => 'includes/StringUtils.php',
+ 'Status' => 'includes/Status.php',
'StringUtils' => 'includes/StringUtils.php',
+ 'TableDiffFormatter' => 'includes/DifferenceEngine.php',
+ 'TablePager' => 'includes/Pager.php',
+ 'ThumbnailImage' => 'includes/MediaTransformOutput.php',
+ 'TitleDependency' => 'includes/CacheDependency.php',
'Title' => 'includes/Title.php',
+ 'TitleListDependency' => 'includes/CacheDependency.php',
+ 'TransformParameterError' => 'includes/MediaTransformOutput.php',
+ 'TurckBagOStuff' => 'includes/BagOStuff.php',
+ 'UnifiedDiffFormatter' => 'includes/DifferenceEngine.php',
+ 'UnlistedSpecialPage' => 'includes/SpecialPage.php',
'User' => 'includes/User.php',
- 'UserRightsProxy' => 'includes/UserRightsProxy.php',
- 'MailAddress' => 'includes/UserMailer.php',
- 'EmailNotification' => 'includes/UserMailer.php',
+ 'UserArray' => 'includes/UserArray.php',
+ 'UserArrayFromResult' => 'includes/UserArray.php',
'UserMailer' => 'includes/UserMailer.php',
+ 'UserRightsProxy' => 'includes/UserRightsProxy.php',
'WatchedItem' => 'includes/WatchedItem.php',
+ 'WatchlistEditor' => 'includes/WatchlistEditor.php',
'WebRequest' => 'includes/WebRequest.php',
'WebResponse' => 'includes/WebResponse.php',
- 'FauxRequest' => 'includes/WebRequest.php',
- 'MediaWiki' => 'includes/Wiki.php',
'WikiError' => 'includes/WikiError.php',
'WikiErrorMsg' => 'includes/WikiError.php',
+ 'WikiExporter' => 'includes/Export.php',
'WikiXmlError' => 'includes/WikiError.php',
+ 'WordLevelDiff' => 'includes/DifferenceEngine.php',
+ 'XCacheBagOStuff' => 'includes/BagOStuff.php',
+ 'XmlDumpWriter' => 'includes/Export.php',
'Xml' => 'includes/Xml.php',
+ 'XmlSelect' => 'includes/Xml.php',
'XmlTypeCheck' => 'includes/XmlTypeCheck.php',
'ZhClient' => 'includes/ZhClient.php',
- 'memcached' => 'includes/memcached-client.php',
- 'EmaillingJob' => 'includes/JobQueue.php',
- 'WatchlistEditor' => 'includes/WatchlistEditor.php',
- # filerepo
- 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
- 'File' => 'includes/filerepo/File.php',
- 'FileRepo' => 'includes/filerepo/FileRepo.php',
- 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
- 'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php',
- 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
- 'FSRepo' => 'includes/filerepo/FSRepo.php',
- 'Image' => 'includes/filerepo/LocalFile.php',
- 'LocalFile' => 'includes/filerepo/LocalFile.php',
- 'LocalFileDeleteBatch' => 'includes/filerepo/LocalFile.php',
- 'LocalFileRestoreBatch' => 'includes/filerepo/LocalFile.php',
- 'LocalRepo' => 'includes/filerepo/LocalRepo.php',
- 'OldLocalFile' => 'includes/filerepo/OldLocalFile.php',
- 'RepoGroup' => 'includes/filerepo/RepoGroup.php',
- 'UnregisteredLocalFile' => 'includes/filerepo/UnregisteredLocalFile.php',
-
- # Media
- 'BitmapHandler' => 'includes/media/Bitmap.php',
- 'BmpHandler' => 'includes/media/BMP.php',
- 'DjVuHandler' => 'includes/media/DjVu.php',
- 'MediaHandler' => 'includes/media/Generic.php',
- 'ImageHandler' => 'includes/media/Generic.php',
- 'SvgHandler' => 'includes/media/SVG.php',
-
- # Normal
- 'UtfNormal' => 'includes/normal/UtfNormal.php',
-
- # Templates
- 'UsercreateTemplate' => 'includes/templates/Userlogin.php',
- 'UserloginTemplate' => 'includes/templates/Userlogin.php',
-
- # Languages
- 'Language' => 'languages/Language.php',
-
- # API
+ # includes/api
'ApiBase' => 'includes/api/ApiBase.php',
+ 'ApiBlock' => 'includes/api/ApiBlock.php',
+ 'ApiDelete' => 'includes/api/ApiDelete.php',
+ 'ApiEditPage' => 'includes/api/ApiEditPage.php',
+ 'ApiEmailUser' => 'includes/api/ApiEmailUser.php',
'ApiExpandTemplates' => 'includes/api/ApiExpandTemplates.php',
- 'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php',
'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
'ApiFormatBase' => 'includes/api/ApiFormatBase.php',
- 'Services_JSON' => 'includes/api/ApiFormatJson_json.php',
+ 'ApiFormatDbg' => 'includes/api/ApiFormatDbg.php',
+ 'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php',
'ApiFormatJson' => 'includes/api/ApiFormatJson.php',
'ApiFormatPhp' => 'includes/api/ApiFormatPhp.php',
+ 'ApiFormatTxt' => 'includes/api/ApiFormatTxt.php',
'ApiFormatWddx' => 'includes/api/ApiFormatWddx.php',
'ApiFormatXml' => 'includes/api/ApiFormatXml.php',
- 'ApiFormatTxt' => 'includes/api/ApiFormatTxt.php',
- 'ApiFormatDbg' => 'includes/api/ApiFormatDbg.php',
- 'Spyc' => 'includes/api/ApiFormatYaml_spyc.php',
'ApiFormatYaml' => 'includes/api/ApiFormatYaml.php',
'ApiHelp' => 'includes/api/ApiHelp.php',
'ApiLogin' => 'includes/api/ApiLogin.php',
'ApiLogout' => 'includes/api/ApiLogout.php',
'ApiMain' => 'includes/api/ApiMain.php',
+ 'ApiMove' => 'includes/api/ApiMove.php',
'ApiOpenSearch' => 'includes/api/ApiOpenSearch.php',
'ApiPageSet' => 'includes/api/ApiPageSet.php',
'ApiParamInfo' => 'includes/api/ApiParamInfo.php',
'ApiParse' => 'includes/api/ApiParse.php',
+ 'ApiProtect' => 'includes/api/ApiProtect.php',
'ApiQuery' => 'includes/api/ApiQuery.php',
- 'ApiQueryAllpages' => 'includes/api/ApiQueryAllpages.php',
- 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php',
+ 'ApiQueryAllimages' => 'includes/api/ApiQueryAllimages.php',
+ 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.php',
- 'ApiQueryBase' => 'includes/api/ApiQueryBase.php',
- 'ApiQueryGeneratorBase' => 'includes/api/ApiQueryBase.php',
+ 'ApiQueryAllmessages' => 'includes/api/ApiQueryAllmessages.php',
+ 'ApiQueryAllpages' => 'includes/api/ApiQueryAllpages.php',
'ApiQueryBacklinks' => 'includes/api/ApiQueryBacklinks.php',
+ 'ApiQueryBase' => 'includes/api/ApiQueryBase.php',
+ 'ApiQueryBlocks' => 'includes/api/ApiQueryBlocks.php',
'ApiQueryCategories' => 'includes/api/ApiQueryCategories.php',
+ 'ApiQueryCategoryInfo' => 'includes/api/ApiQueryCategoryInfo.php',
'ApiQueryCategoryMembers' => 'includes/api/ApiQueryCategoryMembers.php',
'ApiQueryContributions' => 'includes/api/ApiQueryUserContributions.php',
- 'ApiQueryExternalLinks' => 'includes/api/ApiQueryExternalLinks.php',
+ 'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
'ApiQueryExtLinksUsage' => 'includes/api/ApiQueryExtLinksUsage.php',
- 'ApiQueryImages' => 'includes/api/ApiQueryImages.php',
+ 'ApiQueryExternalLinks' => 'includes/api/ApiQueryExternalLinks.php',
+ 'ApiQueryGeneratorBase' => 'includes/api/ApiQueryBase.php',
'ApiQueryImageInfo' => 'includes/api/ApiQueryImageInfo.php',
+ 'ApiQueryImages' => 'includes/api/ApiQueryImages.php',
'ApiQueryInfo' => 'includes/api/ApiQueryInfo.php',
'ApiQueryLangLinks' => 'includes/api/ApiQueryLangLinks.php',
'ApiQueryLinks' => 'includes/api/ApiQueryLinks.php',
@@ -358,72 +271,264 @@ function __autoload($className) {
'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php',
'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
- 'ApiQueryAllmessages' => 'includes/api/ApiQueryAllmessages.php',
'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
- 'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php',
'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php',
+ 'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php',
'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
'ApiResult' => 'includes/api/ApiResult.php',
-
- # apiedit branch
- 'ApiBlock' => 'includes/api/ApiBlock.php',
- #'ApiChangeRights' => 'includes/api/ApiChangeRights.php',
- # Disabled for now
- 'ApiDelete' => 'includes/api/ApiDelete.php',
- 'ApiMove' => 'includes/api/ApiMove.php',
- 'ApiProtect' => 'includes/api/ApiProtect.php',
- 'ApiQueryBlocks' => 'includes/api/ApiQueryBlocks.php',
- 'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
'ApiRollback' => 'includes/api/ApiRollback.php',
'ApiUnblock' => 'includes/api/ApiUnblock.php',
- 'ApiUndelete' => 'includes/api/ApiUndelete.php'
+ 'ApiUndelete' => 'includes/api/ApiUndelete.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',
+ 'YAMLNode' => 'includes/api/ApiFormatYaml_spyc.php',
+
+ # includes/db
+ 'Blob' => 'includes/db/Database.php',
+ 'ChronologyProtector' => 'includes/db/LBFactory.php',
+ 'Database' => 'includes/db/Database.php',
+ 'DatabaseMssql' => 'includes/db/DatabaseMssql.php',
+ 'DatabaseMysql' => 'includes/db/Database.php',
+ 'DatabaseOracle' => 'includes/db/DatabaseOracle.php',
+ 'DatabasePostgres' => 'includes/db/DatabasePostgres.php',
+ 'DatabaseSqlite' => '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',
+ 'LBFactory' => 'includes/db/LBFactory.php',
+ 'LBFactory_Multi' => 'includes/db/LBFactory_Multi.php',
+ 'LBFactory_Simple' => 'includes/db/LBFactory.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',
+ 'ORABlob' => 'includes/db/DatabaseOracle.php',
+ 'ORAResult' => 'includes/db/DatabaseOracle.php',
+ 'PostgresField' => 'includes/db/DatabasePostgres.php',
+ 'ResultWrapper' => 'includes/db/Database.php',
+ 'SQLiteField' => 'includes/db/DatabaseSqlite.php',
+
+ # includes/filerepo
+ 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
+ 'File' => 'includes/filerepo/File.php',
+ 'FileRepo' => 'includes/filerepo/FileRepo.php',
+ 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
+ 'ForeignAPIFile' => 'includes/filerepo/ForeignAPIFile.php',
+ 'ForeignAPIRepo' => 'includes/filerepo/ForeignAPIRepo.php',
+ 'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php',
+ 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
+ 'ForeignDBViaLBRepo' => 'includes/filerepo/ForeignDBViaLBRepo.php',
+ 'FSRepo' => 'includes/filerepo/FSRepo.php',
+ 'Image' => 'includes/filerepo/Image.php',
+ 'LocalFile' => 'includes/filerepo/LocalFile.php',
+ 'LocalFileDeleteBatch' => 'includes/filerepo/LocalFile.php',
+ 'LocalFileMoveBatch' => 'includes/filerepo/LocalFile.php',
+ 'LocalFileRestoreBatch' => 'includes/filerepo/LocalFile.php',
+ 'LocalRepo' => 'includes/filerepo/LocalRepo.php',
+ 'OldLocalFile' => 'includes/filerepo/OldLocalFile.php',
+ 'RepoGroup' => 'includes/filerepo/RepoGroup.php',
+ 'UnregisteredLocalFile' => 'includes/filerepo/UnregisteredLocalFile.php',
+
+ # includes/media
+ 'BitmapHandler' => 'includes/media/Bitmap.php',
+ 'BmpHandler' => 'includes/media/BMP.php',
+ 'DjVuHandler' => 'includes/media/DjVu.php',
+ 'ImageHandler' => 'includes/media/Generic.php',
+ 'MediaHandler' => 'includes/media/Generic.php',
+ 'SvgHandler' => 'includes/media/SVG.php',
+
+ # includes/normal
+ 'UtfNormal' => 'includes/normal/UtfNormal.php',
+
+ # includes/parser
+ 'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php',
+ 'DateFormatter' => 'includes/parser/DateFormatter.php',
+ 'OnlyIncludeReplacer' => 'includes/parser/Parser.php',
+ 'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPDPart' => 'includes/parser/Preprocessor_DOM.php',
+ 'PPDPart_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPDStack' => 'includes/parser/Preprocessor_DOM.php',
+ 'PPDStackElement' => 'includes/parser/Preprocessor_DOM.php',
+ 'PPDStackElement_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPDStack_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPFrame' => 'includes/parser/Preprocessor.php',
+ 'PPFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
+ 'PPFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPNode' => 'includes/parser/Preprocessor.php',
+ 'PPNode_DOM' => 'includes/parser/Preprocessor_DOM.php',
+ 'PPNode_Hash_Array' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPNode_Hash_Attr' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPNode_Hash_Text' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPNode_Hash_Tree' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPTemplateFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
+ 'PPTemplateFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'Parser' => 'includes/parser/Parser.php',
+ 'ParserCache' => 'includes/parser/ParserCache.php',
+ 'ParserOptions' => 'includes/parser/ParserOptions.php',
+ 'ParserOutput' => 'includes/parser/ParserOutput.php',
+ 'Parser_DiffTest' => 'includes/parser/Parser_DiffTest.php',
+ 'Parser_OldPP' => 'includes/parser/Parser_OldPP.php',
+ 'Preprocessor' => 'includes/parser/Preprocessor.php',
+ 'Preprocessor_DOM' => 'includes/parser/Preprocessor_DOM.php',
+ 'Preprocessor_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'StripState' => 'includes/parser/Parser.php',
+
+ # includes/specials
+ 'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
+ 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
+ 'ContribsPager' => 'includes/specials/SpecialContributions.php',
+ 'DBLockForm' => 'includes/specials/SpecialLockdb.php',
+ 'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
+ 'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
+ 'DisambiguationsPage' => 'includes/specials/SpecialDisambiguations.php',
+ 'DoubleRedirectsPage' => 'includes/specials/SpecialDoubleRedirects.php',
+ 'EmailConfirmation' => 'includes/specials/SpecialConfirmemail.php',
+ 'EmailInvalidation' => 'includes/specials/SpecialConfirmemail.php',
+ 'EmailUserForm' => 'includes/specials/SpecialEmailuser.php',
+ 'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php',
+ 'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php',
+ 'IPBlockForm' => 'includes/specials/SpecialBlockip.php',
+ 'IPBlocklistPager' => 'includes/specials/SpecialIpblocklist.php',
+ 'IPUnblockForm' => 'includes/specials/SpecialIpblocklist.php',
+ 'ImportReporter' => 'includes/specials/SpecialImport.php',
+ 'ImportStreamSource' => 'includes/specials/SpecialImport.php',
+ 'ImportStringSource' => 'includes/specials/SpecialImport.php',
+ 'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
+ 'LoginForm' => 'includes/specials/SpecialUserlogin.php',
+ 'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
+ 'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
+ 'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
+ 'MostcategoriesPage' => 'includes/specials/SpecialMostcategories.php',
+ 'MostimagesPage' => 'includes/specials/SpecialMostimages.php',
+ 'MostlinkedCategoriesPage' => 'includes/specials/SpecialMostlinkedcategories.php',
+ 'MostlinkedPage' => 'includes/specials/SpecialMostlinked.php',
+ 'MostrevisionsPage' => 'includes/specials/SpecialMostrevisions.php',
+ 'MovePageForm' => 'includes/specials/SpecialMovepage.php',
+ 'SpecialNewpages' => 'includes/specials/SpecialNewpages.php',
+ 'NewPagesPager' => 'includes/specials/SpecialNewpages.php',
+ 'PageArchive' => 'includes/specials/SpecialUndelete.php',
+ 'PasswordResetForm' => 'includes/specials/SpecialResetpass.php',
+ 'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php',
+ 'PreferencesForm' => 'includes/specials/SpecialPreferences.php',
+ 'RandomPage' => 'includes/specials/SpecialRandompage.php',
+ 'RevisionDeleteForm' => 'includes/specials/SpecialRevisiondelete.php',
+ 'RevisionDeleter' => 'includes/specials/SpecialRevisiondelete.php',
+ 'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
+ 'SpecialAllpages' => 'includes/specials/SpecialAllpages.php',
+ 'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
+ 'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
+ 'SpecialMostlinkedtemplates' => 'includes/specials/SpecialMostlinkedtemplates.php',
+ 'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php',
+ 'SpecialRandomredirect' => 'includes/specials/SpecialRandomredirect.php',
+ 'SpecialRecentchanges' => 'includes/specials/SpecialRecentchanges.php',
+ 'SpecialRecentchangeslinked' => 'includes/specials/SpecialRecentchangeslinked.php',
+ 'SpecialSearch' => 'includes/specials/SpecialSearch.php',
+ 'SpecialVersion' => 'includes/specials/SpecialVersion.php',
+ 'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
+ 'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
+ 'UncategorizedTemplatesPage' => 'includes/specials/SpecialUncategorizedtemplates.php',
+ 'UndeleteForm' => 'includes/specials/SpecialUndelete.php',
+ 'UnusedCategoriesPage' => 'includes/specials/SpecialUnusedcategories.php',
+ 'UnusedimagesPage' => 'includes/specials/SpecialUnusedimages.php',
+ 'UnusedtemplatesPage' => 'includes/specials/SpecialUnusedtemplates.php',
+ 'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
+ 'UploadForm' => 'includes/specials/SpecialUpload.php',
+ 'UploadFormMogile' => 'includes/specials/SpecialUploadMogile.php',
+ 'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
+ 'UsersPager' => 'includes/specials/SpecialListusers.php',
+ 'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
+ 'WantedPagesPage' => 'includes/specials/SpecialWantedpages.php',
+ 'WhatLinksHerePage' => 'includes/specials/SpecialWhatlinkshere.php',
+ 'WikiImporter' => 'includes/specials/SpecialImport.php',
+ 'WikiRevision' => 'includes/specials/SpecialImport.php',
+ 'WithoutInterwikiPage' => 'includes/specials/SpecialWithoutinterwiki.php',
+
+ # includes/templates
+ 'UsercreateTemplate' => 'includes/templates/Userlogin.php',
+ 'UserloginTemplate' => 'includes/templates/Userlogin.php',
+
+ # languages
+ 'Language' => 'languages/Language.php',
+ 'FakeConverter' => 'languages/Language.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',
+
);
-
- wfProfileIn( __METHOD__ );
- if ( isset( $localClasses[$className] ) ) {
- $filename = $localClasses[$className];
- } elseif ( isset( $wgAutoloadClasses[$className] ) ) {
- $filename = $wgAutoloadClasses[$className];
- } else {
- # Try a different capitalisation
- # The case can sometimes be wrong when unserializing PHP 4 objects
- $filename = false;
- $lowerClass = strtolower( $className );
- foreach ( $localClasses as $class2 => $file2 ) {
- if ( strtolower( $class2 ) == $lowerClass ) {
- $filename = $file2;
+
+ /**
+ * autoload - take a class name and attempt to load it
+ *
+ * @param string $className 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.
+ */
+ static function autoload( $className ) {
+ global $wgAutoloadClasses;
+
+ wfProfileIn( __METHOD__ );
+ if ( isset( self::$localClasses[$className] ) ) {
+ $filename = self::$localClasses[$className];
+ } elseif ( isset( $wgAutoloadClasses[$className] ) ) {
+ $filename = $wgAutoloadClasses[$className];
+ } else {
+ # Try a different capitalisation
+ # The case can sometimes be wrong when unserializing PHP 4 objects
+ $filename = false;
+ $lowerClass = strtolower( $className );
+ foreach ( self::$localClasses as $class2 => $file2 ) {
+ if ( strtolower( $class2 ) == $lowerClass ) {
+ $filename = $file2;
+ }
+ }
+ if ( !$filename ) {
+ # Give up
+ wfProfileOut( __METHOD__ );
+ return false;
}
}
- if ( !$filename ) {
- # Give up
- wfProfileOut( __METHOD__ );
- return;
+
+ # Make an absolute path, this improves performance by avoiding some stat calls
+ if ( substr( $filename, 0, 1 ) != '/' && substr( $filename, 1, 1 ) != ':' ) {
+ global $IP;
+ $filename = "$IP/$filename";
}
+ require( $filename );
+ wfProfileOut( __METHOD__ );
+ return true;
}
- # Make an absolute path, this improves performance by avoiding some stat calls
- if ( substr( $filename, 0, 1 ) != '/' && substr( $filename, 1, 1 ) != ':' ) {
- global $IP;
- $filename = "$IP/$filename";
+ static function loadAllExtensions() {
+ global $wgAutoloadClasses;
+
+ foreach( $wgAutoloadClasses as $class => $file ) {
+ if( !( class_exists( $class ) || interface_exists( $class ) ) ) {
+ require( $file );
+ }
+ }
}
- require( $filename );
- wfProfileOut( __METHOD__ );
}
function wfLoadAllExtensions() {
- global $wgAutoloadClasses;
+ AutoLoader::loadAllExtensions();
+}
- # It is crucial that SpecialPage.php is included before any special page
- # extensions are loaded. Otherwise the parent class will not be available
- # when APC loads the early-bound extension class. Normally this is
- # guaranteed by entering special pages via SpecialPage members such as
- # executePath(), but here we have to take a more explicit measure.
-
- require_once( dirname(__FILE__) . '/SpecialPage.php' );
-
- foreach( $wgAutoloadClasses as $class => $file ) {
- if( !( class_exists( $class ) || interface_exists( $class ) ) ) {
- require( $file );
- }
+if ( function_exists( 'spl_autoload_register' ) ) {
+ spl_autoload_register( array( 'AutoLoader', 'autoload' ) );
+} else {
+ function __autoload( $class ) {
+ AutoLoader::autoload( $class );
}
}
+
diff --git a/includes/Autopromote.php b/includes/Autopromote.php
index b5097423..68fe6636 100644
--- a/includes/Autopromote.php
+++ b/includes/Autopromote.php
@@ -8,7 +8,7 @@ class Autopromote {
/**
* Get the groups for the given user based on $wgAutopromote.
*
- * @param User $user The user to get the groups for
+ * @param $user The user to get the groups for
* @return array Array of groups to promote to.
*/
public static function getAutopromoteGroups( User $user ) {
@@ -18,6 +18,9 @@ class Autopromote {
if( self::recCheckCondition( $cond, $user ) )
$promote[] = $group;
}
+
+ wfRunHooks( 'GetAutoPromoteGroups', array($user, &$promote) );
+
return $promote;
}
@@ -33,12 +36,12 @@ class Autopromote {
* This function evaluates the former type recursively, and passes off to
* self::checkCondition for evaluation of the latter type.
*
- * @param mixed $cond A condition, possibly containing other conditions
- * @param User $user The user to check the conditions against
+ * @param $cond Mixed: a condition, possibly containing other conditions
+ * @param $user The user to check the conditions against
* @return bool Whether the condition is true
*/
private static function recCheckCondition( $cond, User $user ) {
- $validOps = array( '&', '|', '^' );
+ $validOps = array( '&', '|', '^', '!' );
if( is_array( $cond ) && count( $cond ) >= 2 && in_array( $cond[0], $validOps ) ) {
# Recursive condition
if( $cond[0] == '&' ) {
@@ -47,7 +50,7 @@ class Autopromote {
return false;
return true;
} elseif( $cond[0] == '|' ) {
- foreach( array_slice( $cond, 1 ) as $subcond )
+ foreach( array_slice( $cond, 1 ) as $subcond )
if( self::recCheckCondition( $subcond, $user ) )
return true;
return false;
@@ -60,6 +63,11 @@ class Autopromote {
$res = ($res xor self::recCheckCondition( $subcond, $user ));
}
return $res;
+ } elseif ( $cond[0] = '!' ) {
+ foreach( array_slice( $cond, 1 ) as $subcond )
+ if( self::recCheckCondition( $subcond, $user ) )
+ return false;
+ return true;
}
}
# If we got here, the array presumably does not contain other condi-
@@ -75,8 +83,8 @@ class Autopromote {
* APCOND_AGE. Other types will throw an exception if no extension evalu-
* ates them.
*
- * @param array $cond A condition, which must not contain other conditions
- * @param User $user The user to check the condition against
+ * @param $cond Array: A condition, which must not contain other conditions
+ * @param $user The user to check the condition against
* @return bool Whether the condition is true for the user
*/
private static function checkCondition( $cond, User $user ) {
@@ -87,7 +95,7 @@ class Autopromote {
if( User::isValidEmailAddr( $user->getEmail() ) ) {
global $wgEmailAuthentication;
if( $wgEmailAuthentication ) {
- return $user->getEmailAuthenticationTimestamp() ? true : false;
+ return (bool)$user->getEmailAuthenticationTimestamp();
} else {
return true;
}
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index 226abb35..b4fefc97 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -17,22 +17,25 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
+
/**
+ * @defgroup Cache Cache
*
+ * @file
+ * @ingroup Cache
*/
/**
- * Simple generic object store
- *
* interface is intended to be more or less compatible with
* the PHP memcached client.
*
* backends for local hash array and SQL table included:
* <code>
* $bag = new HashBagOStuff();
- * $bag = new MysqlBagOStuff($tablename); # connect to db first
+ * $bag = new MediaWikiBagOStuff($tablename); # connect to db first
* </code>
*
+ * @ingroup Cache
*/
class BagOStuff {
var $debugmode;
@@ -167,14 +170,12 @@ class BagOStuff {
/**
* Functional versions!
- * @todo document
+ * This is a test of the interface, mainly. It stores things in an associative
+ * array, which is not going to persist between program runs.
+ *
+ * @ingroup Cache
*/
class HashBagOStuff extends BagOStuff {
- /*
- This is a test of the interface, mainly. It stores
- things in an associative array, which is not going to
- persist between program runs.
- */
var $bag;
function __construct() {
@@ -213,24 +214,20 @@ class HashBagOStuff extends BagOStuff {
}
}
-/*
-CREATE TABLE objectcache (
- keyname char(255) binary not null default '',
- value mediumblob,
- exptime datetime,
- unique key (keyname),
- key (exptime)
-);
-*/
-
/**
- * @todo document
- * @abstract
+ * Generic class to store objects in a database
+ *
+ * @ingroup Cache
*/
abstract class SqlBagOStuff extends BagOStuff {
var $table;
var $lastexpireall = 0;
+ /**
+ * Constructor
+ *
+ * @param $tablename String: name of the table to use
+ */
function __construct($tablename = 'objectcache') {
$this->table = $tablename;
}
@@ -247,8 +244,8 @@ abstract class SqlBagOStuff extends BagOStuff {
}
if($row=$this->_fetchobject($res)) {
$this->_debug("get: retrieved data; exp time is " . $row->exptime);
- if ( $row->exptime != $this->_maxdatetime() &&
- wfTimestamp( TS_UNIX, $row->exptime ) < time() )
+ if ( $row->exptime != $this->_maxdatetime() &&
+ wfTimestamp( TS_UNIX, $row->exptime ) < time() )
{
$this->_debug("get: key has expired, deleting");
$this->delete($key);
@@ -262,7 +259,7 @@ abstract class SqlBagOStuff extends BagOStuff {
}
function set($key,$value,$exptime=0) {
- if ( wfReadOnly() ) {
+ if ( $this->_readonly() ) {
return false;
}
$exptime = intval($exptime);
@@ -284,7 +281,7 @@ abstract class SqlBagOStuff extends BagOStuff {
}
function delete($key,$time=0) {
- if ( wfReadOnly() ) {
+ if ( $this->_readonly() ) {
return false;
}
$this->_query(
@@ -340,6 +337,8 @@ abstract class SqlBagOStuff extends BagOStuff {
abstract function _doinsert($table, $vals);
abstract function _doquery($sql);
+ abstract function _readonly();
+
function _freeresult($result) {
/* stub */
return false;
@@ -367,7 +366,7 @@ abstract class SqlBagOStuff extends BagOStuff {
function expireall() {
/* Remove any items that have expired */
- if ( wfReadOnly() ) {
+ if ( $this->_readonly() ) {
return false;
}
$now = $this->_fromunixtime( time() );
@@ -376,7 +375,7 @@ abstract class SqlBagOStuff extends BagOStuff {
function deleteall() {
/* Clear *all* items from cache table */
- if ( wfReadOnly() ) {
+ if ( $this->_readonly() ) {
return false;
}
$this->_query( "DELETE FROM $0" );
@@ -387,7 +386,7 @@ abstract class SqlBagOStuff extends BagOStuff {
* On typical message and page data, this can provide a 3X decrease
* in storage requirements.
*
- * @param mixed $data
+ * @param $data mixed
* @return string
*/
function _serialize( &$data ) {
@@ -401,7 +400,7 @@ abstract class SqlBagOStuff extends BagOStuff {
/**
* Unserialize and, if necessary, decompress an object.
- * @param string $serial
+ * @param $serial string
* @return mixed
*/
function _unserialize( $serial ) {
@@ -417,31 +416,33 @@ abstract class SqlBagOStuff extends BagOStuff {
}
/**
- * @todo document
+ * Stores objects in the main database of the wiki
+ *
+ * @ingroup Cache
*/
class MediaWikiBagOStuff extends SqlBagOStuff {
var $tableInitialised = false;
+ function _getDB(){
+ static $db;
+ if( !isset( $db ) )
+ $db = wfGetDB( DB_MASTER );
+ return $db;
+ }
function _doquery($sql) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->query($sql, 'MediaWikiBagOStuff::_doquery');
+ return $this->_getDB()->query( $sql, __METHOD__ );
}
function _doinsert($t, $v) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->insert($t, $v, 'MediaWikiBagOStuff::_doinsert',
- array( 'IGNORE' ) );
+ return $this->_getDB()->insert($t, $v, __METHOD__, array( 'IGNORE' ) );
}
function _fetchobject($result) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->fetchObject($result);
+ return $this->_getDB()->fetchObject($result);
}
function _freeresult($result) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->freeResult($result);
+ return $this->_getDB()->freeResult($result);
}
function _dberror($result) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->lastError();
+ return $this->_getDB()->lastError();
}
function _maxdatetime() {
if ( time() > 0x7fffffff ) {
@@ -451,24 +452,23 @@ class MediaWikiBagOStuff extends SqlBagOStuff {
}
}
function _fromunixtime($ts) {
- $dbw = wfGetDB(DB_MASTER);
- return $dbw->timestamp($ts);
+ return $this->_getDB()->timestamp($ts);
+ }
+ function _readonly(){
+ return wfReadOnly();
}
function _strencode($s) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->strencode($s);
+ return $this->_getDB()->strencode($s);
}
function _blobencode($s) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->encodeBlob($s);
+ return $this->_getDB()->encodeBlob($s);
}
function _blobdecode($s) {
- $dbw = wfGetDB( DB_MASTER );
- return $dbw->decodeBlob($s);
+ return $this->_getDB()->decodeBlob($s);
}
function getTableName() {
if ( !$this->tableInitialised ) {
- $dbw = wfGetDB( DB_MASTER );
+ $dbw = $this->_getDB();
/* This is actually a hack, we should be able
to use Language classes here... or not */
if (!$dbw)
@@ -493,6 +493,7 @@ class MediaWikiBagOStuff extends SqlBagOStuff {
* that Turck's serializer is faster, so a possible future extension would be
* to use it for arrays but not for objects.
*
+ * @ingroup Cache
*/
class TurckBagOStuff extends BagOStuff {
function get($key) {
@@ -527,6 +528,7 @@ class TurckBagOStuff extends BagOStuff {
/**
* This is a wrapper for APC's shared memory functions
*
+ * @ingroup Cache
*/
class APCBagOStuff extends BagOStuff {
function get($key) {
@@ -536,12 +538,12 @@ class APCBagOStuff extends BagOStuff {
}
return $val;
}
-
+
function set($key, $value, $exptime=0) {
apc_store($key, serialize($value), $exptime);
return true;
}
-
+
function delete($key, $time=0) {
apc_delete($key);
return true;
@@ -555,6 +557,7 @@ class APCBagOStuff extends BagOStuff {
* This is basically identical to the Turck MMCache version,
* mostly because eAccelerator is based on Turck MMCache.
*
+ * @ingroup Cache
*/
class eAccelBagOStuff extends BagOStuff {
function get($key) {
@@ -589,13 +592,15 @@ class eAccelBagOStuff extends BagOStuff {
/**
* Wrapper for XCache object caching functions; identical interface
* to the APC wrapper
+ *
+ * @ingroup Cache
*/
class XCacheBagOStuff extends BagOStuff {
/**
* Get a value from the XCache object cache
*
- * @param string $key Cache key
+ * @param $key String: cache key
* @return mixed
*/
public function get( $key ) {
@@ -604,40 +609,41 @@ class XCacheBagOStuff extends BagOStuff {
$val = unserialize( $val );
return $val;
}
-
+
/**
* Store a value in the XCache object cache
*
- * @param string $key Cache key
- * @param mixed $value Object to store
- * @param int $expire Expiration time
+ * @param $key String: cache key
+ * @param $value Mixed: object to store
+ * @param $expire Int: expiration time
* @return bool
*/
public function set( $key, $value, $expire = 0 ) {
xcache_set( $key, serialize( $value ), $expire );
return true;
}
-
+
/**
* Remove a value from the XCache object cache
*
- * @param string $key Cache key
- * @param int $time Not used in this implementation
+ * @param $key String: cache key
+ * @param $time Int: not used in this implementation
* @return bool
*/
public function delete( $key, $time = 0 ) {
xcache_unset( $key );
return true;
}
-
+
}
/**
* @todo document
+ * @ingroup Cache
*/
class DBABagOStuff extends BagOStuff {
var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
-
+
function __construct( $handler = 'db3', $dir = false ) {
if ( $dir === false ) {
global $wgTmpDirectory;
@@ -645,6 +651,7 @@ class DBABagOStuff extends BagOStuff {
}
$this->mFile = "$dir/mw-cache-" . wfWikiID();
$this->mFile .= '.db';
+ wfDebug( __CLASS__.": using cache file {$this->mFile}\n" );
$this->mHandler = $handler;
}
@@ -664,7 +671,7 @@ class DBABagOStuff extends BagOStuff {
if ( !is_string( $blob ) ) {
return array( null, 0 );
} else {
- return array(
+ return array(
unserialize( substr( $blob, 11 ) ),
intval( substr( $blob, 0, 10 ) )
);
@@ -779,5 +786,3 @@ class DBABagOStuff extends BagOStuff {
return $result;
}
}
-
-
diff --git a/includes/Block.php b/includes/Block.php
index 3688d7cf..b208fa8a 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -1,5 +1,6 @@
<?php
/**
+ * @file
* Blocks and bans object
*/
@@ -15,16 +16,16 @@
class Block
{
/* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
- $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName,
- $mBlockEmail;
- /* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster, $mByName;
-
+ $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName,
+ $mBlockEmail, $mByName, $mAngryAutoblock;
+ /* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster;
+
const EB_KEEP_EXPIRED = 1;
const EB_FOR_UPDATE = 2;
const EB_RANGE_ONLY = 4;
function __construct( $address = '', $user = 0, $by = 0, $reason = '',
- $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
+ $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
$hideName = 0, $blockEmail = 0 )
{
$this->mId = 0;
@@ -45,6 +46,7 @@ class Block
$this->mForUpdate = false;
$this->mFromMaster = false;
$this->mByName = false;
+ $this->mAngryAutoblock = false;
$this->initialiseRange();
}
@@ -59,10 +61,10 @@ class Block
}
}
- static function newFromID( $id )
+ static function newFromID( $id )
{
$dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->resultObject( $dbr->select( 'ipblocks', '*',
+ $res = $dbr->resultObject( $dbr->select( 'ipblocks', '*',
array( 'ipb_id' => $id ), __METHOD__ ) );
$block = new Block;
if ( $block->loadFromResult( $res ) ) {
@@ -75,8 +77,8 @@ class Block
function clear()
{
$this->mAddress = $this->mReason = $this->mTimestamp = '';
- $this->mId = $this->mAnonOnly = $this->mCreateAccount =
- $this->mEnableAutoblock = $this->mAuto = $this->mUser =
+ $this->mId = $this->mAnonOnly = $this->mCreateAccount =
+ $this->mEnableAutoblock = $this->mAuto = $this->mUser =
$this->mBy = $this->mHideName = $this->mBlockEmail = 0;
$this->mByName = false;
}
@@ -124,7 +126,7 @@ class Block
# Try user block
if ( $user ) {
- $res = $db->resultObject( $db->select( 'ipblocks', '*', array( 'ipb_user' => $user ),
+ $res = $db->resultObject( $db->select( 'ipblocks', '*', array( 'ipb_user' => $user ),
__METHOD__, $options ) );
if ( $this->loadFromResult( $res, $killExpired ) ) {
return true;
@@ -170,7 +172,7 @@ class Block
return true;
}
}
-
+
# Give up
$this->clear();
return false;
@@ -179,7 +181,7 @@ class Block
/**
* Fill in member variables from a result wrapper
*/
- function loadFromResult( ResultWrapper $res, $killExpired = true )
+ function loadFromResult( ResultWrapper $res, $killExpired = true )
{
$ret = false;
if ( 0 != $res->numRows() ) {
@@ -234,7 +236,7 @@ class Block
"ipb_range_start <= '$iaddr'",
"ipb_range_end >= '$iaddr'"
);
-
+
if ( $user ) {
$conds['ipb_anon_only'] = 0;
}
@@ -270,7 +272,7 @@ class Block
if ( isset( $row->user_name ) ) {
$this->mByName = $row->user_name;
} else {
- $this->mByName = false;
+ $this->mByName = $row->ipb_by_text;
}
$this->mRangeStart = $row->ipb_range_start;
$this->mRangeEnd = $row->ipb_range_end;
@@ -358,7 +360,7 @@ class Block
/**
* Insert a block into the block table.
- *@return Whether or not the insertion was successful.
+ * @return Whether or not the insertion was successful.
*/
function insert()
{
@@ -376,6 +378,15 @@ class Block
$this->mBlockEmail = 0; //Same goes for email...
}
+ if( !$this->mByName ) {
+ if( $this->mBy ) {
+ $this->mByName = User::whoIs( $this->mBy );
+ } else {
+ global $wgUser;
+ $this->mByName = $wgUser->getName();
+ }
+ }
+
# Don't collide with expired blocks
Block::purgeExpired();
@@ -386,6 +397,7 @@ class Block
'ipb_address' => $this->mAddress,
'ipb_user' => $this->mUser,
'ipb_by' => $this->mBy,
+ 'ipb_by_text' => $this->mByName,
'ipb_reason' => $this->mReason,
'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
'ipb_auto' => $this->mAuto,
@@ -400,7 +412,6 @@ class Block
), 'Block::insert', array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
- $dbw->commit();
if ($affected)
$this->doRetroactiveAutoblock();
@@ -420,17 +431,30 @@ class Block
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) {
+ // Block any IP used in the last 7 days. Up to five IPs.
+ $conds[] = 'rc_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( time() - (7*86400) ) );
+ $options['LIMIT'] = 5;
+ } else {
+ // Just the last IP used.
+ $options['LIMIT'] = 1;
+ }
- $row = $dbr->selectRow( 'recentchanges', array( 'rc_ip' ), array( 'rc_user_text' => $this->mAddress ),
- __METHOD__ , array( 'ORDER BY' => 'rc_timestamp DESC' ) );
+ $res = $dbr->select( 'recentchanges', array( 'rc_ip' ), $conds,
+ __METHOD__ , $options);
- if ( !$row || !$row->rc_ip ) {
+ if ( !$dbr->numRows( $res ) ) {
#No results, don't autoblock anything
wfDebug("No IP found to retroactively autoblock\n");
} else {
- #Limit is 1, so no loop needed.
- $retroblockip = $row->rc_ip;
- return $this->doAutoblock( $retroblockip, true );
+ while ( $row = $dbr->fetchObject( $res ) ) {
+ if ( $row->rc_ip )
+ $this->doAutoblock( $row->rc_ip );
+ }
}
}
}
@@ -476,6 +500,12 @@ class Block
wfDebug( " No match\n" );
}
}
+
+ ## Allow hooks to cancel the autoblock.
+ if (!wfRunHooks( 'AbortAutoblock', array( $autoblockip, &$this ) )) {
+ wfDebug( "Autoblock aborted by hook." );
+ return false;
+ }
# It's okay to autoblock. Go ahead and create/insert the block.
@@ -502,6 +532,7 @@ class Block
$ipblock->mAddress = $autoblockip;
$ipblock->mUser = 0;
$ipblock->mBy = $this->mBy;
+ $ipblock->mByName = $this->mByName;
$ipblock->mReason = wfMsgForContent( 'autoblocker', $this->mAddress, $this->mReason );
$ipblock->mTimestamp = wfTimestampNow();
$ipblock->mAuto = 1;
@@ -592,9 +623,6 @@ class Block
*/
function getByName()
{
- if ( $this->mByName === false ) {
- $this->mByName = User::whoIs( $this->mBy );
- }
return $this->mByName;
}
@@ -613,7 +641,7 @@ class Block
return $this->mAddress;
}
}
-
+
/**
* Encode expiry for DB
*/
@@ -625,7 +653,7 @@ class Block
}
}
- /**
+ /**
* Decode expiry which has come from the DB
*/
static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
@@ -635,14 +663,14 @@ class Block
return wfTimestamp( $timestampType, $expiry );
}
}
-
+
static function getAutoblockExpiry( $timestamp )
{
global $wgAutoblockExpiry;
return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
}
-
- /**
+
+ /**
* Gets rid of uneeded numbers in quad-dotted/octet IP strings
* For example, 127.111.113.151/24 -> 127.111.113.0/24
*/
@@ -675,7 +703,7 @@ class Block
return $range;
}
- /**
+ /**
* Purge expired blocks from the ipblocks table
*/
static function purgeExpired() {
@@ -684,8 +712,8 @@ class Block
}
static function infinity() {
- # This is a special keyword for timestamps in PostgreSQL, and
- # works with CHAR(14) as well because "i" sorts after all numbers.
+ # This is a special keyword for timestamps in PostgreSQL, and
+ # works with CHAR(14) as well because "i" sorts after all numbers.
return 'infinity';
/*
@@ -697,6 +725,48 @@ class Block
return $infinity;
*/
}
+
+ /**
+ * Convert a DB-encoded expiry into a real string that humans can read.
+ */
+ static function formatExpiry( $encoded_expiry ) {
+
+ static $msg = null;
+
+ if( is_null( $msg ) ) {
+ $msg = array();
+ $keys = array( 'infiniteblock', 'expiringblock' );
+ foreach( $keys as $key ) {
+ $msg[$key] = wfMsgHtml( $key );
+ }
+ }
+
+ $expiry = Block::decodeExpiry( $encoded_expiry );
+ if ($expiry == 'infinity') {
+ $expirystr = $msg['infiniteblock'];
+ } else {
+ global $wgLang;
+ $expiretimestr = $wgLang->timeanddate( $expiry, true );
+ $expirystr = wfMsgReplaceArgs( $msg['expiringblock'], array($expiretimestr) );
+ }
+
+ return $expirystr;
+ }
+
+ /**
+ * Convert a typed-in expiry time into something we can put into the database.
+ */
+ static function parseExpiryInput( $expiry_input ) {
+ if ( $expiry_input == 'infinite' || $expiry_input == 'indefinite' ) {
+ $expiry = 'infinity';
+ } else {
+ $expiry = strtotime( $expiry_input );
+ if ($expiry < 0 || $expiry === false) {
+ return false;
+ }
+ }
+
+ return $expiry;
+ }
}
-
diff --git a/includes/CacheDependency.php b/includes/CacheDependency.php
index 1d48c383..b050c46d 100644
--- a/includes/CacheDependency.php
+++ b/includes/CacheDependency.php
@@ -1,10 +1,10 @@
<?php
/**
- * This class stores an arbitrary value along with its dependencies.
+ * This class stores an arbitrary value along with its dependencies.
* Users should typically only use DependencyWrapper::getFromCache(), rather
* than instantiating one of these objects directly.
- * @addtogroup Cache
+ * @ingroup Cache
*/
class DependencyWrapper {
var $value;
@@ -24,7 +24,7 @@ class DependencyWrapper {
$this->deps = $deps;
}
- /**
+ /**
* Returns true if any of the dependencies have expired
*/
function isExpired() {
@@ -62,24 +62,24 @@ class DependencyWrapper {
}
/**
- * Attempt to get a value from the cache. If the value is expired or missing,
+ * Attempt to get a value from the cache. If the value is expired or missing,
* it will be generated with the callback function (if present), and the newly
- * calculated value will be stored to the cache in a wrapper.
+ * 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 array $deps 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.
*
* @return mixed The value, or null if it was not present in the cache and no
* callback was defined.
*/
- static function getValueFromCache( $cache, $key, $expiry = 0, $callback = false,
- $callbackParams = array(), $deps = array() )
+ static function getValueFromCache( $cache, $key, $expiry = 0, $callback = false,
+ $callbackParams = array(), $deps = array() )
{
$obj = $cache->get( $key );
if ( is_object( $obj ) && $obj instanceof DependencyWrapper && !$obj->isExpired() ) {
@@ -97,7 +97,7 @@ class DependencyWrapper {
}
/**
- * @addtogroup Cache
+ * @ingroup Cache
*/
abstract class CacheDependency {
/**
@@ -112,7 +112,7 @@ abstract class CacheDependency {
}
/**
- * @addtogroup Cache
+ * @ingroup Cache
*/
class FileDependency extends CacheDependency {
var $filename, $timestamp;
@@ -122,11 +122,11 @@ class FileDependency extends CacheDependency {
*
* @param string $filename The name of the file, preferably fully qualified
* @param mixed $timestamp The unix last modified timestamp, or false if the
- * file does not exist. If omitted, the timestamp will be loaded from
+ * file does not exist. If omitted, the timestamp will be loaded from
* the file.
*
- * A dependency on a nonexistent file will be triggered when the file is
- * created. A dependency on an existing file will be triggered when the
+ * A dependency on a nonexistent file will be triggered when the file is
+ * created. A dependency on an existing file will be triggered when the
* file is changed.
*/
function __construct( $filename, $timestamp = null ) {
@@ -171,7 +171,7 @@ class FileDependency extends CacheDependency {
}
/**
- * @addtogroup Cache
+ * @ingroup Cache
*/
class TitleDependency extends CacheDependency {
var $titleObj;
@@ -191,7 +191,7 @@ class TitleDependency extends CacheDependency {
function loadDependencyValues() {
$this->touched = $this->getTitle()->getTouched();
}
-
+
/**
* Get rid of bulky Title object for sleep
*/
@@ -202,7 +202,7 @@ class TitleDependency extends CacheDependency {
function getTitle() {
if ( !isset( $this->titleObj ) ) {
$this->titleObj = Title::makeTitle( $this->ns, $this->dbk );
- }
+ }
return $this->titleObj;
}
@@ -230,12 +230,12 @@ class TitleDependency extends CacheDependency {
}
/**
- * @addtogroup Cache
+ * @ingroup Cache
*/
class TitleListDependency extends CacheDependency {
var $linkBatch;
var $timestamps;
-
+
/**
* Construct a dependency on a list of titles
*/
@@ -259,7 +259,7 @@ 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__ );
while ( $row = $dbr->fetchObject( $res ) ) {
@@ -313,11 +313,11 @@ class TitleListDependency extends CacheDependency {
}
/**
- * @addtogroup Cache
+ * @ingroup Cache
*/
class GlobalDependency extends CacheDependency {
var $name, $value;
-
+
function __construct( $name ) {
$this->name = $name;
$this->value = $GLOBALS[$name];
@@ -329,7 +329,7 @@ class GlobalDependency extends CacheDependency {
}
/**
- * @addtogroup Cache
+ * @ingroup Cache
*/
class ConstantDependency extends CacheDependency {
var $name, $value;
@@ -343,5 +343,3 @@ class ConstantDependency extends CacheDependency {
return constant( $this->name ) != $this->value;
}
}
-
-
diff --git a/includes/Category.php b/includes/Category.php
new file mode 100644
index 00000000..acafc47a
--- /dev/null
+++ b/includes/Category.php
@@ -0,0 +1,261 @@
+<?php
+/**
+ * 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.
+ *
+ * TODO: Move some stuff from CategoryPage.php to here, and use that.
+ *
+ * @author Simetrical
+ */
+
+class Category {
+ /** Name of the category, normalized to DB-key form */
+ private $mName = null;
+ private $mID = null;
+ /** Category page title */
+ private $mTitle = null;
+ /** Counts of membership (cat_pages, cat_subcats, cat_files) */
+ private $mPages = null, $mSubcats = null, $mFiles = null;
+
+ 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->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 ) {
+ $where = array( 'cat_id' => $this->mID );
+ } else {
+ # Already initialized
+ return true;
+ }
+ $dbr = wfGetDB( DB_SLAVE );
+ $row = $dbr->selectRow(
+ 'category',
+ array( 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats',
+ 'cat_files' ),
+ $where,
+ __METHOD__
+ );
+ 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->mPages = 0;
+ $this->mSubcats = 0;
+ $this->mFiles = 0;
+
+ return true;
+ } else {
+ return false; # Fail
+ }
+ }
+ $this->mID = $row->cat_id;
+ $this->mName = $row->cat_title;
+ $this->mPages = $row->cat_pages;
+ $this->mSubcats = $row->cat_subcats;
+ $this->mFiles = $row->cat_files;
+
+ # (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 ) {
+ $this->refreshCounts();
+ }
+
+ return true;
+ }
+
+ /**
+ * Factory function.
+ *
+ * @param array $name A category name (no "Category:" prefix). It need
+ * not be normalized, with spaces replaced by underscores.
+ * @return mixed Category, or false on a totally invalid name
+ */
+ public static function newFromName( $name ) {
+ $cat = new self();
+ $title = Title::makeTitleSafe( NS_CATEGORY, $name );
+ if( !is_object( $title ) ) {
+ return false;
+ }
+
+ $cat->mTitle = $title;
+ $cat->mName = $title->getDBKey();
+
+ return $cat;
+ }
+
+ /**
+ * Factory function.
+ *
+ * @param array $title Title for the category page
+ * @return mixed Category, or false on a totally invalid name
+ */
+ public static function newFromTitle( $title ) {
+ $cat = new self();
+
+ $cat->mTitle = $title;
+ $cat->mName = $title->getDBKey();
+
+ return $cat;
+ }
+
+ /**
+ * Factory function.
+ *
+ * @param array $id A category id
+ * @return Category
+ */
+ public static function newFromID( $id ) {
+ $cat = new self();
+ $cat->mID = intval( $id );
+ return $cat;
+ }
+
+ /**
+ * 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,
+ * 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.
+ * May be provided if it is already known, to avoid having to re-create a title object later.
+ * @return Category
+ */
+ public static function newFromRow( $row, $title = null ) {
+ $cat = new self();
+ $cat->mTitle = $title;
+
+
+ # 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.
+
+ if ( $row->cat_title === null ) {
+ if ( $title === null ) {
+ # the name is probably somewhere in the row, for example as page_title,
+ # 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->mID = false;
+ $cat->mSubcats = 0;
+ $cat->mPages = 0;
+ $cat->mFiles = 0;
+ } else {
+ $cat->mName = $row->cat_title;
+ $cat->mID = $row->cat_id;
+ $cat->mSubcats = $row->cat_subcats;
+ $cat->mPages = $row->cat_pages;
+ $cat->mFiles = $row->cat_files;
+ }
+
+ return $cat;
+ }
+
+ /** @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' ); }
+
+ /**
+ * @return mixed The Title for this category, or false on failure.
+ */
+ public function getTitle() {
+ if( $this->mTitle ) return $this->mTitle;
+
+ if( !$this->initialize() ) {
+ return false;
+ }
+
+ $this->mTitle = Title::makeTitleSafe( NS_CATEGORY, $this->mName );
+ return $this->mTitle;
+ }
+
+ /** Generic accessor */
+ private function getX( $key ) {
+ if( !$this->initialize() ) {
+ return false;
+ }
+ return $this->{$key};
+ }
+
+ /**
+ * Refresh the counts for this category.
+ *
+ * @return bool True on success, false on failure
+ */
+ public function refreshCounts() {
+ 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() ) {
+ 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!
+ $dbw->insert(
+ 'category',
+ array( 'cat_title' => $this->mName ),
+ __METHOD__,
+ 'IGNORE'
+ );
+ }
+
+ $cond1 = $dbw->conditional( 'page_namespace='.NS_CATEGORY, 1, 'NULL' );
+ $cond2 = $dbw->conditional( 'page_namespace='.NS_IMAGE, 1, 'NULL' );
+ $result = $dbw->selectRow(
+ array( 'categorylinks', 'page' ),
+ array( 'COUNT(*) AS pages',
+ "COUNT($cond1) AS subcats",
+ "COUNT($cond2) AS files"
+ ),
+ array( 'cl_to' => $this->mName, 'page_id = cl_from' ),
+ __METHOD__,
+ 'LOCK IN SHARE MODE'
+ );
+ $ret = $dbw->update(
+ 'category',
+ array(
+ 'cat_pages' => $result->pages,
+ 'cat_subcats' => $result->subcats,
+ 'cat_files' => $result->files
+ ),
+ array( 'cat_title' => $this->mName ),
+ __METHOD__
+ );
+ $dbw->commit();
+
+ # Now we should update our local counts.
+ $this->mPages = $result->pages;
+ $this->mSubcats = $result->subcats;
+ $this->mFiles = $result->files;
+
+ return $ret;
+ }
+}
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 6fbcd3c1..92e4e279 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -66,10 +66,12 @@ class CategoryPage extends Article {
class CategoryViewer {
var $title, $limit, $from, $until,
- $articles, $articles_start_char,
+ $articles, $articles_start_char,
$children, $children_start_char,
$showGallery, $gallery,
$skin;
+ /** Category object for this page */
+ private $cat;
function __construct( $title, $from = '', $until = '' ) {
global $wgCategoryPagingLimit;
@@ -77,8 +79,9 @@ class CategoryViewer {
$this->from = $from;
$this->until = $until;
$this->limit = $wgCategoryPagingLimit;
+ $this->cat = Category::newFromName( $title->getDBKey() );
}
-
+
/**
* Format the category data list.
*
@@ -132,12 +135,21 @@ class CategoryViewer {
}
/**
- * Add a subcategory to the internal lists
+ * Add a subcategory to the internal lists, using a Category object
+ */
+ function addSubcategoryObject( $cat, $sortkey, $pageLength ) {
+ $title = $cat->getTitle();
+ $this->addSubcategory( $title, $sortkey, $pageLength );
+ }
+
+ /**
+ * Add a subcategory to the internal lists, using a title object
+ * @deprectated 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(
+ $this->children[] = $this->getSkin()->makeKnownLinkObj(
$title, $wgContLang->convertHtml( $title->getText() ) );
$this->children_start_char[] = $this->getSubcategorySortChar( $title, $sortkey );
@@ -152,13 +164,13 @@ class CategoryViewer {
*/
function getSubcategorySortChar( $title, $sortkey ) {
global $wgContLang;
-
+
if( $title->getPrefixedText() == $sortkey ) {
$firstChar = $wgContLang->firstChar( $title->getDBkey() );
} else {
$firstChar = $wgContLang->firstChar( $sortkey );
}
-
+
return $wgContLang->convert( $firstChar );
}
@@ -167,11 +179,10 @@ class CategoryViewer {
*/
function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) {
if ( $this->showGallery ) {
- $image = new Image( $title );
if( $this->flip ) {
- $this->gallery->insert( $image );
+ $this->gallery->insert( $title );
} else {
- $this->gallery->add( $image );
+ $this->gallery->add( $title );
}
} else {
$this->addPage( $title, $sortkey, $pageLength, $isRedirect );
@@ -211,17 +222,17 @@ class CategoryViewer {
$this->flip = false;
}
$res = $dbr->select(
- array( 'page', 'categorylinks' ),
- array( 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey' ),
+ array( 'page', 'categorylinks', 'category' ),
+ array( 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey',
+ 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files' ),
array( $pageCondition,
- 'cl_from = page_id',
- 'cl_to' => $this->title->getDBkey()),
- #'page_is_redirect' => 0),
- #+ $pageCondition,
+ 'cl_to' => $this->title->getDBkey() ),
__METHOD__,
array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : 'cl_sortkey',
- 'USE INDEX' => 'cl_sortkey',
- 'LIMIT' => $this->limit + 1 ) );
+ 'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ),
+ 'LIMIT' => $this->limit + 1 ),
+ array( 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ),
+ 'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY ) ) );
$count = 0;
$this->nextPage = null;
@@ -236,7 +247,8 @@ class CategoryViewer {
$title = Title::makeTitle( $x->page_namespace, $x->page_title );
if( $title->getNamespace() == NS_CATEGORY ) {
- $this->addSubcategory( $title, $x->cl_sortkey, $x->page_len );
+ $cat = Category::newFromRow( $x, $title );
+ $this->addSubcategoryObject( $cat, $x->cl_sortkey, $x->page_len );
} elseif( $this->showGallery && $title->getNamespace() == NS_IMAGE ) {
$this->addImage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
} else {
@@ -261,12 +273,14 @@ class CategoryViewer {
function getSubcategorySection() {
# Don't show subcategories section if there are none.
$r = '';
- $c = count( $this->children );
- if( $c > 0 ) {
+ $rescnt = count( $this->children );
+ $dbcnt = $this->cat->getSubcatCount();
+ $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'subcat' );
+ if( $rescnt > 0 ) {
# Showing subcategories
$r .= "<div id=\"mw-subcategories\">\n";
$r .= '<h2>' . wfMsg( 'subcategories' ) . "</h2>\n";
- $r .= wfMsgExt( 'subcategorycount', array( 'parse' ), $c );
+ $r .= $countmsg;
$r .= $this->formatList( $this->children, $this->children_start_char );
$r .= "\n</div>";
}
@@ -277,11 +291,20 @@ class CategoryViewer {
$ti = htmlspecialchars( $this->title->getText() );
# Don't show articles section if there are none.
$r = '';
- $c = count( $this->articles );
- if( $c > 0 ) {
+
+ # FIXME, here and in the other two sections: we don't need to bother
+ # with this rigamarole if the entire category contents fit on one page
+ # and have already been retrieved. We can just use $rescnt in that
+ # case and save a query and some logic.
+ $dbcnt = $this->cat->getPageCount() - $this->cat->getSubcatCount()
+ - $this->cat->getFileCount();
+ $rescnt = count( $this->articles );
+ $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'article' );
+
+ if( $rescnt > 0 ) {
$r = "<div id=\"mw-pages\">\n";
$r .= '<h2>' . wfMsg( 'category_header', $ti ) . "</h2>\n";
- $r .= wfMsgExt( 'categoryarticlecount', array( 'parse' ), $c );
+ $r .= $countmsg;
$r .= $this->formatList( $this->articles, $this->articles_start_char );
$r .= "\n</div>";
}
@@ -290,10 +313,13 @@ class CategoryViewer {
function getImageSection() {
if( $this->showGallery && ! $this->gallery->isEmpty() ) {
+ $dbcnt = $this->cat->getFileCount();
+ $rescnt = $this->gallery->count();
+ $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'file' );
+
return "<div id=\"mw-category-media\">\n" .
'<h2>' . wfMsg( 'category-media-header', htmlspecialchars($this->title->getText()) ) . "</h2>\n" .
- wfMsgExt( 'category-media-count', array( 'parse' ), $this->gallery->count() ) .
- $this->gallery->toHTML() . "\n</div>";
+ $countmsg . $this->gallery->toHTML() . "\n</div>";
} else {
return '';
}
@@ -440,7 +466,48 @@ class CategoryViewer {
return "($prevLink) ($nextLink)";
}
-}
-
-
+ /**
+ * What to do if the category table conflicts with the number of results
+ * returned? This function says what. It works the same whether the
+ * things being counted are articles, subcategories, or files.
+ *
+ * Note for grepping: uses the messages category-article-count,
+ * category-article-count-limited, category-subcat-count,
+ * category-subcat-count-limited, category-file-count,
+ * category-file-count-limited.
+ *
+ * @param int $rescnt The number of items returned by our database query.
+ * @param int $dbcnt The number of items according to the category table.
+ * @param string $type 'subcat', 'article', or 'file'
+ * @return string A message giving the number of items, to output to HTML.
+ */
+ private function getCountMessage( $rescnt, $dbcnt, $type ) {
+ global $wgLang;
+ # There are three cases:
+ # 1) The category table figure seems sane. It might be wrong, but
+ # we can't do anything about it if we don't recalculate it on ev-
+ # ery category view.
+ # 2) The category table figure isn't sane, like it's smaller than the
+ # number of actual results, *but* the number of results is less
+ # than $this->limit and there's no offset. In this case we still
+ # 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)){
+ # Case 1: seems sane.
+ $totalcnt = $dbcnt;
+ } elseif($totalrescnt < $this->limit && !$this->from && !$this->until){
+ # Case 2: not sane, but salvageable.
+ $totalcnt = $rescnt;
+ } else {
+ # Case 3: hopeless. Don't give a total count at all.
+ return wfMsgExt("category-$type-count-limited", 'parse',
+ $wgLang->formatNum( $rescnt ) );
+ }
+ return wfMsgExt( "category-$type-count", 'parse', $wgLang->formatNum( $rescnt ),
+ $wgLang->formatNum( $totalcnt ) );
+ }
+}
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index b94dcf5e..d28f2eeb 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -188,5 +188,3 @@ class Categoryfinder {
}
} # END OF CLASS "Categoryfinder"
-
-
diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php
new file mode 100644
index 00000000..9bee1790
--- /dev/null
+++ b/includes/ChangesFeed.php
@@ -0,0 +1,129 @@
+<?php
+
+class ChangesFeed {
+
+ public $format, $type, $titleMsg, $descMsg;
+
+ public function __construct( $format, $type ) {
+ $this->format = $format;
+ $this->type = $type;
+ }
+
+ public function getFeedObject( $title, $description ) {
+ global $wgSitename, $wgContLanguageCode, $wgFeedClasses, $wgTitle;
+ $feedTitle = "$wgSitename - {$title} [$wgContLanguageCode]";
+
+ return new $wgFeedClasses[$this->format](
+ $feedTitle, htmlspecialchars( $description ), $wgTitle->getFullUrl() );
+ }
+
+ public function execute( $feed, $rows, $limit = 0 , $hideminor = false, $lastmod = false ) {
+ global $messageMemc, $wgFeedCacheTimeout;
+ global $wgFeedClasses, $wgTitle, $wgSitename, $wgContLanguageCode;
+
+ if ( !FeedUtils::checkFeedOutput( $this->format ) ) {
+ return;
+ }
+
+ $timekey = wfMemcKey( $this->type, $this->format, 'timestamp' );
+ $key = wfMemcKey( $this->type, $this->format, 'limit', $limit, 'minor', $hideminor );
+
+ FeedUtils::checkPurge($timekey, $key);
+
+ /*
+ * Bumping around loading up diffs can be pretty slow, so where
+ * possible we want to cache the feed output so the next visitor
+ * gets it quick too.
+ */
+ $cachedFeed = $this->loadFromCache( $lastmod, $timekey, $key );
+ if( is_string( $cachedFeed ) ) {
+ wfDebug( "RC: Outputting cached feed\n" );
+ $feed->httpHeaders();
+ echo $cachedFeed;
+ } else {
+ wfDebug( "RC: rendering new feed and caching it\n" );
+ ob_start();
+ self::generateFeed( $rows, $feed );
+ $cachedFeed = ob_get_contents();
+ ob_end_flush();
+ $this->saveToCache( $cachedFeed, $timekey, $key );
+ }
+ return true;
+ }
+
+ public function saveToCache( $feed, $timekey, $key ) {
+ global $messageMemc;
+ $expire = 3600 * 24; # One day
+ $messageMemc->set( $key, $feed );
+ $messageMemc->set( $timekey, wfTimestamp( TS_MW ), $expire );
+ }
+
+ public function loadFromCache( $lastmod, $timekey, $key ) {
+ global $wgFeedCacheTimeout, $messageMemc;
+ $feedLastmod = $messageMemc->get( $timekey );
+
+ if( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
+ /*
+ * If the cached feed was rendered very recently, we may
+ * go ahead and use it even if there have been edits made
+ * since it was rendered. This keeps a swarm of requests
+ * from being too bad on a super-frequently edited wiki.
+ */
+
+ $feedAge = time() - wfTimestamp( TS_UNIX, $feedLastmod );
+ $feedLastmodUnix = wfTimestamp( TS_UNIX, $feedLastmod );
+ $lastmodUnix = wfTimestamp( TS_UNIX, $lastmod );
+
+ if( $feedAge < $wgFeedCacheTimeout || $feedLastmodUnix > $lastmodUnix) {
+ wfDebug( "RC: loading feed from cache ($key; $feedLastmod; $lastmod)...\n" );
+ return $messageMemc->get( $key );
+ } else {
+ wfDebug( "RC: cached feed timestamp check failed ($feedLastmod; $lastmod)\n" );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @todo document
+ * @param $rows Database resource with recentchanges rows
+ * @param $feed Feed object
+ */
+ public static function generateFeed( $rows, &$feed ) {
+ wfProfileIn( __METHOD__ );
+
+ $feed->outHeader();
+
+ # Merge adjacent edits by one user
+ $sorted = array();
+ $n = 0;
+ foreach( $rows as $obj ) {
+ if( $n > 0 &&
+ $obj->rc_namespace >= 0 &&
+ $obj->rc_cur_id == $sorted[$n-1]->rc_cur_id &&
+ $obj->rc_user_text == $sorted[$n-1]->rc_user_text ) {
+ $sorted[$n-1]->rc_last_oldid = $obj->rc_last_oldid;
+ } else {
+ $sorted[$n] = $obj;
+ $n++;
+ }
+ }
+
+ foreach( $sorted as $obj ) {
+ $title = Title::makeTitle( $obj->rc_namespace, $obj->rc_title );
+ $talkpage = $title->getTalkPage();
+ $item = new FeedItem(
+ $title->getPrefixedText(),
+ FeedUtils::formatDiff( $obj ),
+ $title->getFullURL( 'diff=' . $obj->rc_this_oldid . '&oldid=prev' ),
+ $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 507e88fa..436f006e 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -27,7 +27,10 @@ class ChangesList {
# Called by history lists and recent changes
#
- /** @todo document */
+ /**
+ * Changeslist contructor
+ * @param Skin $skin
+ */
function __construct( &$skin ) {
$this->skin =& $skin;
$this->preCacheMessages();
@@ -54,12 +57,12 @@ class ChangesList {
* As we use the same small set of messages in various methods and that
* they are called often, we call them once and save them in $this->message
*/
- function preCacheMessages() {
+ private function preCacheMessages() {
// Precache various messages
if( !isset( $this->message ) ) {
foreach( explode(' ', 'cur diff hist minoreditletter newpageletter last '.
'blocklink history boteditletter semicolon-separator' ) as $msg ) {
- $this->message[$msg] = wfMsgExt( $msg, array( 'escape') );
+ $this->message[$msg] = wfMsgExt( $msg, array( 'escapenoentities' ) );
}
}
}
@@ -67,8 +70,14 @@ 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
*/
- function recentChangesFlags( $new, $minor, $patrolled, $nothing = '&nbsp;', $bot = false ) {
+ 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>'
@@ -80,8 +89,9 @@ class ChangesList {
/**
* Returns text for the start of the tabular part of RC
+ * @return string
*/
- function beginRecentChangesList() {
+ public function beginRecentChangesList() {
$this->rc_cache = array();
$this->rcMoveIndex = 0;
$this->rcCacheIndex = 0;
@@ -92,8 +102,9 @@ class ChangesList {
/**
* Returns text for the end of RC
+ * @return string
*/
- function endRecentChangesList() {
+ public function endRecentChangesList() {
if( $this->rclistOpen ) {
return "</ul>\n";
} else {
@@ -101,8 +112,7 @@ class ChangesList {
}
}
-
- function insertMove( &$s, $rc ) {
+ protected function insertMove( &$s, $rc ) {
# Diff
$s .= '(' . $this->message['diff'] . ') (';
# Hist
@@ -115,7 +125,7 @@ class ChangesList {
$this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), '' ) );
}
- function insertDateHeader(&$s, $rc_timestamp) {
+ protected function insertDateHeader(&$s, $rc_timestamp) {
global $wgLang;
# Make date header if necessary
@@ -131,15 +141,16 @@ class ChangesList {
}
}
- function insertLog(&$s, $title, $logtype) {
+ protected function insertLog(&$s, $title, $logtype) {
$logname = LogPage::logName( $logtype );
$s .= '(' . $this->skin->makeKnownLinkObj($title, $logname ) . ')';
}
-
- function insertDiffHist(&$s, &$rc, $unpatrolled) {
+ protected function insertDiffHist(&$s, &$rc, $unpatrolled) {
# Diff link
- if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
+ if( !$this->userCan($rc,Revision::DELETED_TEXT) ) {
+ $diffLink = $this->message['diff'];
+ } else if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
$diffLink = $this->message['diff'];
} else {
$rcidparam = $unpatrolled
@@ -163,14 +174,19 @@ class ChangesList {
$s .= ') . . ';
}
- function insertArticleLink(&$s, &$rc, $unpatrolled, $watched) {
+ protected function insertArticleLink(&$s, &$rc, $unpatrolled, $watched) {
# Article link
# 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']
: '';
- $articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
+ if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
+ $articlelink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
+ $articlelink = '<span class="history-deleted">'.$articlelink.'</span>';
+ } else {
+ $articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
+ }
if( $watched )
$articlelink = "<strong class=\"mw-watched\">{$articlelink}</strong>";
global $wgContLang;
@@ -178,27 +194,50 @@ class ChangesList {
wfRunHooks('ChangesListInsertArticleLink',
array(&$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched));
-
+
$s .= ' '.$articlelink;
}
- function insertTimestamp(&$s, $rc) {
+ protected function insertTimestamp(&$s, $rc) {
global $wgLang;
# Timestamp
$s .= $this->message['semicolon-separator'] . ' ' . $wgLang->time( $rc->mAttribs['rc_timestamp'], true, true ) . ' . . ';
}
/** Insert links to user page, user talk page and eventually a blocking link */
- function insertUserRelatedLinks(&$s, &$rc) {
- $s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- $s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ protected function insertUserRelatedLinks(&$s, &$rc) {
+ if ( $this->isDeleted($rc,Revision::DELETED_USER) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-user') . '</span>';
+ } else {
+ $s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ $s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ }
+ }
+
+ /** insert a formatted action */
+ protected function insertAction(&$s, &$rc) {
+ # Add action
+ if( $rc->mAttribs['rc_type'] == RC_LOG ) {
+ // log action
+ if ( $this->isDeleted($rc,LogPage::DELETED_ACTION) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ } else {
+ $s .= ' ' . LogPage::actionText( $rc->mAttribs['rc_log_type'], $rc->mAttribs['rc_log_action'],
+ $rc->getTitle(), $this->skin, LogPage::extractParams($rc->mAttribs['rc_params']), true, true );
+ }
+ }
}
/** insert a formatted comment */
- function insertComment(&$s, &$rc) {
+ protected function insertComment(&$s, &$rc) {
# Add comment
if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
- $s .= $this->skin->commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
+ // log comment
+ if ( $this->isDeleted($rc,Revision::DELETED_COMMENT) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-comment') . '</span>';
+ } else {
+ $s .= $this->skin->commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
+ }
}
}
@@ -206,15 +245,15 @@ class ChangesList {
* Check whether to enable recent changes patrol features
* @return bool
*/
- function usePatrol() {
- global $wgUseRCPatrol, $wgUser;
- return( $wgUseRCPatrol && ($wgUser->isAllowed('patrol') || $wgUser->isAllowed('patrolmarks')) );
+ public static function usePatrol() {
+ global $wgUser;
+ return $wgUser->useRCPatrol();
}
/**
* Returns the string which indicates the number of watching users
*/
- function numberofWatchingusers( $count ) {
+ protected function numberofWatchingusers( $count ) {
global $wgLang;
static $cache = array();
if ( $count > 0 ) {
@@ -227,6 +266,36 @@ class ChangesList {
return '';
}
}
+
+ /**
+ * Determine if said field of a revision is hidden
+ * @param RCCacheEntry $rc
+ * @param int $field one of DELETED_* bitfield constants
+ * @return bool
+ */
+ public static function isDeleted( $rc, $field ) {
+ return ($rc->mAttribs['rc_deleted'] & $field) == $field;
+ }
+
+ /**
+ * 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
+ */
+ 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 );
+ } else {
+ return true;
+ }
+ }
}
@@ -237,8 +306,8 @@ class OldChangesList extends ChangesList {
/**
* Format a line using the old system (aka without any javascript).
*/
- function recentChangesLine( &$rc, $watched = false ) {
- global $wgContLang, $wgRCShowChangedSize;
+ public function recentChangesLine( &$rc, $watched = false ) {
+ global $wgContLang, $wgRCShowChangedSize, $wgUser;
$fname = 'ChangesList::recentChangesLineOld';
wfProfileIn( $fname );
@@ -248,31 +317,35 @@ class OldChangesList extends ChangesList {
extract( $rc->mAttribs );
# Should patrol-related stuff be shown?
- $unpatrolled = $this->usePatrol() && $rc_patrolled == 0;
+ $unpatrolled = $wgUser->useRCPatrol() && $rc_patrolled == 0;
$this->insertDateHeader($s,$rc_timestamp);
$s .= '<li>';
- // moved pages
+ // Moved pages
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$this->insertMove( $s, $rc );
- // log entries
- } elseif ( $rc_namespace == NS_SPECIAL ) {
+ // Log entries
+ } elseif( $rc_log_type ) {
+ $logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
+ $this->insertLog( $s, $logtitle, $rc_log_type );
+ // Log entries (old format) or log targets, and special pages
+ } elseif( $rc_namespace == NS_SPECIAL ) {
list( $specialName, $specialSubpage ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
if ( $specialName == 'Log' ) {
$this->insertLog( $s, $rc->getTitle(), $specialSubpage );
} else {
wfDebug( "Unexpected special page in recentchanges\n" );
}
- // all other stuff
+ // Regular entries
} else {
wfProfileIn($fname.'-page');
$this->insertDiffHist($s, $rc, $unpatrolled);
# M, N, b and ! (minor, new, bot and unpatrolled)
- $s .= ' ' . $this->recentChangesFlags( $rc_type == RC_NEW, $rc_minor, $unpatrolled, '', $rc_bot );
+ $s .= $this->recentChangesFlags( $rc_type == RC_NEW, $rc_minor, $unpatrolled, '', $rc_bot );
$this->insertArticleLink($s, $rc, $unpatrolled, $watched);
wfProfileOut($fname.'-page');
@@ -285,11 +358,19 @@ class OldChangesList extends ChangesList {
if( $wgRCShowChangedSize ) {
$s .= ( $rc->getCharacterDifference() == '' ? '' : $rc->getCharacterDifference() . ' . . ' );
}
-
+ # User tool links
$this->insertUserRelatedLinks($s,$rc);
+ # Log action text (if any)
+ $this->insertAction($s, $rc);
+ # Edit or log comment
$this->insertComment($s, $rc);
- $s .= rtrim(' ' . $this->numberofWatchingusers($rc->numberofWatchingusers));
+ # Mark revision as deleted if so
+ if ( !$rc_log_type && $this->isDeleted($rc,Revision::DELETED_TEXT) )
+ $s .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+ if($rc->numberofWatchingusers > 0) {
+ $s .= ' ' . wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rc->numberofWatchingusers));
+ }
$s .= "</li>\n";
@@ -308,8 +389,8 @@ class EnhancedChangesList extends ChangesList {
/**
* Format a line for enhanced recentchange (aka with javascript and block of lines).
*/
- function recentChangesLine( &$baseRC, $watched = false ) {
- global $wgLang, $wgContLang;
+ public function recentChangesLine( &$baseRC, $watched = false ) {
+ global $wgLang, $wgContLang, $wgUser;
# Create a specialised object
$rc = RCCacheEntry::newFromParent( $baseRC );
@@ -331,17 +412,20 @@ class EnhancedChangesList extends ChangesList {
}
# Should patrol-related stuff be shown?
- if( $this->usePatrol() ) {
+ if( $wgUser->useRCPatrol() ) {
$rc->unpatrolled = !$rc_patrolled;
} else {
$rc->unpatrolled = false;
}
+ $showdifflinks = true;
# Make article link
+ // 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(), '' ) );
+ // Log entries (old format) and special pages
} elseif( $rc_namespace == NS_SPECIAL ) {
list( $specialName, $logtype ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
if ( $specialName == 'Log' ) {
@@ -352,13 +436,28 @@ class EnhancedChangesList extends ChangesList {
wfDebug( "Unexpected special page in recentchanges\n" );
$clink = '';
}
- } elseif( $rc->unpatrolled && $rc_type == RC_NEW ) {
- # Unpatrolled new page, give rc_id in query
+ // New unpatrolled pages
+ } else if( $rc->unpatrolled && $rc_type == RC_NEW ) {
$clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', "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, LogPage::logName($rc_log_type) ) . ')';
+ } else {
+ $clink = $this->skin->makeLinkObj( $rc->getTitle(), '' );
+ }
+ $watched = false;
+ // Edits
} else {
$clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '' );
}
+ # Don't show unusable diff links
+ if ( !ChangesList::userCan($rc,Revision::DELETED_TEXT) ) {
+ $showdifflinks = false;
+ }
+
$time = $wgContLang->time( $rc_timestamp, true, true );
$rc->watched = $watched;
$rc->link = $clink;
@@ -375,7 +474,12 @@ class EnhancedChangesList extends ChangesList {
$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 );
- if( $rc_type == RC_NEW || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+
+ # Make "diff" an "cur" links
+ if( !$showdifflinks ) {
+ $curLink = $this->message['cur'];
+ $diffLink = $this->message['diff'];
+ } else if( $rc_type == RC_NEW || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
if( $rc_type != RC_NEW ) {
$curLink = $this->message['cur'];
}
@@ -385,21 +489,27 @@ class EnhancedChangesList extends ChangesList {
}
# Make "last" link
- if( $rc_last_oldid == 0 || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ if( !$showdifflinks ) {
+ $lastLink = $this->message['last'];
+ } else if( $rc_last_oldid == 0 || $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 );
+ $curIdEq.'&diff='.$rc_this_oldid.'&oldid='.$rc_last_oldid . $rcIdQuery );
}
- $rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text );
+ # Make user links
+ if( $this->isDeleted($rc,Revision::DELETED_USER) ) {
+ $rc->userlink = ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-user') . '</span>';
+ } else {
+ $rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text );
+ $rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text );
+ }
$rc->lastlink = $lastLink;
$rc->curlink = $curLink;
$rc->difflink = $diffLink;
- $rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text );
-
# Put accumulated information into the cache, for later display
# Page moves go on their own line
$title = $rc->getTitle();
@@ -408,7 +518,11 @@ class EnhancedChangesList extends ChangesList {
# Use an @ character to prevent collision with page names
$this->rc_cache['@@' . ($this->rcMoveIndex++)] = array($rc);
} else {
- if( !isset ( $this->rc_cache[$secureName] ) ) {
+ # Logs are grouped by type
+ if( $rc_type == RC_LOG ){
+ $secureName = SpecialPage::getTitleFor( 'Log', $rc_log_type )->getPrefixedDBkey();
+ }
+ if( !isset( $this->rc_cache[$secureName] ) ) {
$this->rc_cache[$secureName] = array();
}
array_push( $this->rc_cache[$secureName], $rc );
@@ -419,19 +533,29 @@ class EnhancedChangesList extends ChangesList {
/**
* Enhanced RC group
*/
- function recentChangesBlockGroup( $block ) {
+ protected function recentChangesBlockGroup( $block ) {
global $wgLang, $wgContLang, $wgRCShowChangedSize;
- $r = '';
+ $r = '<table cellpadding="0" cellspacing="0" border="0" style="background: none"><tr>';
# Collate list of users
- $isnew = false;
- $unpatrolled = false;
$userlinks = array();
+ # Other properties
+ $unpatrolled = false;
+ $isnew = false;
+ $curId = $currentRevision = 0;
+ # Some catalyst variables...
+ $namehidden = true;
+ $alllogs = true;
foreach( $block as $rcObj ) {
$oldid = $rcObj->mAttribs['rc_last_oldid'];
if( $rcObj->mAttribs['rc_new'] ) {
$isnew = true;
}
+ // If all log actions to this page were hidden, then don't
+ // give the name of the affected page for this block!
+ if( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
+ $namehidden = false;
+ }
$u = $rcObj->userlink;
if( !isset( $userlinks[$u] ) ) {
$userlinks[$u] = 0;
@@ -439,6 +563,18 @@ class EnhancedChangesList extends ChangesList {
if( $rcObj->unpatrolled ) {
$unpatrolled = true;
}
+ if( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
+ $alllogs = false;
+ }
+ # Get the latest entry with a page_id and oldid
+ # since logs may not have these.
+ if( !$curId && $rcObj->mAttribs['rc_cur_id'] ) {
+ $curId = $rcObj->mAttribs['rc_cur_id'];
+ }
+ if( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) {
+ $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
+ }
+
$bot = $rcObj->mAttribs['rc_bot'];
$userlinks[$u]++;
}
@@ -465,111 +601,148 @@ class EnhancedChangesList extends ChangesList {
$toggleLink = "javascript:toggleVisibility('$rci','$rcm','$rcl')";
$tl = '<span id="'.$rcm.'"><a href="'.$toggleLink.'">' . $this->sideArrow() . '</a></span>';
$tl .= '<span id="'.$rcl.'" style="display:none"><a href="'.$toggleLink.'">' . $this->downArrow() . '</a></span>';
- $r .= $tl;
+ $r .= '<td valign="top" style="white-space: nowrap"><tt>'.$tl.'&nbsp;';
# Main line
- $r .= '<tt>';
$r .= $this->recentChangesFlags( $isnew, false, $unpatrolled, '&nbsp;', $bot );
# Timestamp
- $r .= ' '.$block[0]->timestamp.' </tt>';
+ $r .= '&nbsp;'.$block[0]->timestamp.'&nbsp;</tt></td><td>';
# Article link
- $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
- $r .= $wgContLang->getDirMark();
-
- $curIdEq = 'curid=' . $block[0]->mAttribs['rc_cur_id'];
- $currentRevision = $block[0]->mAttribs['rc_this_oldid'];
- if( $block[0]->mAttribs['rc_type'] != RC_LOG ) {
- # Changes
-
- $n = count($block);
- static $nchanges = array();
- if ( !isset( $nchanges[$n] ) ) {
- $nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape'),
- $wgLang->formatNum( $n ) );
- }
+ if( $namehidden ) {
+ $r .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ } else {
+ $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+ }
- $r .= ' (';
+ $r .= $wgContLang->getDirMark();
- if( $isnew ) {
+ $curIdEq = 'curid=' . $curId;
+ # Changes message
+ $n = count($block);
+ static $nchanges = array();
+ if ( !isset( $nchanges[$n] ) ) {
+ $nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape' ), $wgLang->formatNum( $n ) );
+ }
+ # Total change link
+ $r .= ' ';
+ if( !$alllogs ) {
+ $r .= '(';
+ if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
+ $r .= $nchanges[$n];
+ } else if( $isnew ) {
$r .= $nchanges[$n];
} else {
$r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
$nchanges[$n], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
}
-
$r .= ') . . ';
+ }
- if( $wgRCShowChangedSize ) {
- # Character difference
- $chardiff = $rcObj->getCharacterDifference( $block[ count( $block ) - 1 ]->mAttribs['rc_old_len'],
- $block[0]->mAttribs['rc_new_len'] );
- if( $chardiff == '' ) {
- $r .= ' (';
- } else {
- $r .= ' ' . $chardiff. ' . . ';
- }
- }
-
- # History
+ # Character difference (does not apply if only log items)
+ if( $wgRCShowChangedSize && !$alllogs ) {
+ $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 ) {
+ $last++;
+ }
+ while( $first > $last && $block[$first]->mAttribs['rc_old_len'] === NULL ) {
+ $first--;
+ }
+ # Get net change
+ $chardiff = $rcObj->getCharacterDifference( $block[$first]->mAttribs['rc_old_len'],
+ $block[$last]->mAttribs['rc_new_len'] );
+
+ if( $chardiff == '' ) {
+ $r .= ' ';
+ } else {
+ $r .= ' ' . $chardiff. ' . . ';
+ }
+ }
+
+ # History
+ if( $alllogs ) {
+ // don't show history link for logs
+ } else if( $namehidden || !$block[0]->getTitle()->exists() ) {
+ $r .= '(' . $this->message['history'] . ')';
+ } else {
$r .= '(' . $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
- $this->message['history'], $curIdEq.'&action=history' );
- $r .= ')';
+ $this->message['history'], $curIdEq.'&action=history' ) . ')';
}
$r .= $users;
-
$r .= $this->numberofWatchingusers($block[0]->numberofWatchingusers);
- $r .= "<br />\n";
+
+ $r .= "</td></tr></table>\n";
# Sub-entries
- $r .= '<div id="'.$rci.'" style="display:none">';
+ $r .= '<div id="'.$rci.'" style="display:none;"><table cellpadding="0" cellspacing="0" border="0" style="background: none">';
foreach( $block as $rcObj ) {
# Get rc_xxxx variables
// FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rcObj->mAttribs );
- $r .= $this->spacerArrow();
- $r .= '<tt>&nbsp; &nbsp; &nbsp; &nbsp;';
+ #$r .= '<tr><td valign="top">'.$this->spacerArrow();
+ $r .= '<tr><td valign="top">';
+ $r .= '<tt>'.$this->spacerIndent() . $this->spacerIndent();
$r .= $this->recentChangesFlags( $rc_new, $rc_minor, $rcObj->unpatrolled, '&nbsp;', $rc_bot );
- $r .= '&nbsp;</tt>';
+ $r .= '&nbsp;</tt></td><td valign="top">';
$o = '';
if( $rc_this_oldid != 0 ) {
$o = 'oldid='.$rc_this_oldid;
}
+ # Log timestamp
if( $rc_type == RC_LOG ) {
- $link = $rcObj->timestamp;
+ $link = '<tt>'.$rcObj->timestamp.'</tt> ';
+ # Revision link
+ } else if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
+ $link = '<span class="history-deleted"><tt>'.$rcObj->timestamp.'</tt></span> ';
} else {
- $link = $this->skin->makeKnownLinkObj( $rcObj->getTitle(), $rcObj->timestamp, $curIdEq.'&'.$o );
+ $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( $this->isDeleted($rcObj,Revision::DELETED_TEXT) )
+ $link = '<span class="history-deleted">'.$link.'</span> ';
}
- $link = '<tt>'.$link.'</tt>';
-
$r .= $link;
- $r .= ' (';
- $r .= $rcObj->curlink;
- $r .= $this->message['semicolon-separator'] . ' ';
- $r .= $rcObj->lastlink;
- $r .= ') . . ';
+
+ if ( !$rc_type == RC_LOG || $rc_type == RC_NEW ) {
+ $r .= ' (';
+ $r .= $rcObj->curlink;
+ $r .= $this->message['semicolon-separator'] . ' ';
+ $r .= $rcObj->lastlink;
+ $r .= ')';
+ }
+ $r .= ' . . ';
# Character diff
if( $wgRCShowChangedSize ) {
$r .= ( $rcObj->getCharacterDifference() == '' ? '' : $rcObj->getCharacterDifference() . ' . . ' ) ;
}
-
+ # User links
$r .= $rcObj->userlink;
$r .= $rcObj->usertalklink;
- $r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
- $r .= "<br />\n";
+ // log action
+ parent::insertAction( $r, $rcObj );
+ // log comment
+ parent::insertComment( $r, $rcObj );
+ # Mark revision as deleted
+ if( !$rc_log_type && $this->isDeleted($rcObj,Revision::DELETED_TEXT) ) {
+ $r .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+ }
+
+ $r .= "</td></tr>\n";
}
- $r .= "</div>\n";
+ $r .= "</table></div>\n";
$this->rcCacheIndex++;
return $r;
}
- function maybeWatchedLink( $link, $watched=false ) {
+ protected function maybeWatchedLink( $link, $watched=false ) {
if( $watched ) {
// FIXME: css style might be more appropriate
return '<strong class="mw-watched">' . $link . '</strong>';
@@ -583,9 +756,8 @@ class EnhancedChangesList extends ChangesList {
* @param string $dir one of '', 'd', 'l', 'r'
* @param string $alt text
* @return string HTML <img> tag
- * @access private
*/
- function arrow( $dir, $alt='' ) {
+ protected function arrow( $dir, $alt='' ) {
global $wgStylePath;
$encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' );
$encAlt = htmlspecialchars( $alt );
@@ -596,9 +768,8 @@ class EnhancedChangesList extends ChangesList {
* Generate HTML for a right- or left-facing arrow,
* depending on language direction.
* @return string HTML <img> tag
- * @access private
*/
- function sideArrow() {
+ protected function sideArrow() {
global $wgContLang;
$dir = $wgContLang->isRTL() ? 'l' : 'r';
return $this->arrow( $dir, '+' );
@@ -608,26 +779,32 @@ class EnhancedChangesList extends ChangesList {
* Generate HTML for a down-facing arrow
* depending on language direction.
* @return string HTML <img> tag
- * @access private
*/
- function downArrow() {
+ protected function downArrow() {
return $this->arrow( 'd', '-' );
}
/**
* Generate HTML for a spacer image
* @return string HTML <img> tag
- * @access private
*/
- function spacerArrow() {
+ protected function spacerArrow() {
return $this->arrow( '', ' ' );
}
/**
+ * Add a set of spaces
+ * @return string HTML <td> tag
+ */
+ protected function spacerIndent() {
+ return '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
+ }
+
+ /**
* Enhanced RC ungrouped line.
* @return string a HTML formated line (generated using $r)
*/
- function recentChangesBlockLine( $rcObj ) {
+ protected function recentChangesBlockLine( $rcObj ) {
global $wgContLang, $wgRCShowChangedSize;
# Get rc_xxxx variables
@@ -635,29 +812,35 @@ class EnhancedChangesList extends ChangesList {
extract( $rcObj->mAttribs );
$curIdEq = 'curid='.$rc_cur_id;
- $r = '';
+ $r = '<table cellspacing="0" cellpadding="0" border="0" style="background: none"><tr>';
- # Spacer image
- $r .= $this->spacerArrow();
+ $r .= '<td valign="top" style="white-space: nowrap"><tt>' . $this->spacerArrow() . '&nbsp;';
# Flag and Timestamp
- $r .= '<tt>';
-
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
- $r .= '&nbsp;&nbsp;&nbsp;';
+ $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 .= ' '.$rcObj->timestamp.' </tt>';
-
- # Article link
- $r .= $this->maybeWatchedLink( $rcObj->link, $rcObj->watched );
-
- # Diff
- $r .= ' ('. $rcObj->difflink . $this->message['semicolon-separator'] . ' ';
+ $r .= '&nbsp;'.$rcObj->timestamp.'&nbsp;</tt></td><td>';
+
+ # 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 ) . ')';
+ } else if( !$this->userCan($rcObj,Revision::DELETED_TEXT) ) {
+ $r .= '<span class="history-deleted">' . $rcObj->link . '</span>';
+ } else {
+ $r .= $this->maybeWatchedLink( $rcObj->link, $rcObj->watched );
+ }
- # Hist
- $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), wfMsg( 'hist' ), $curIdEq.'&action=history' ) . ') . . ';
+ # Diff and hist links
+ if ( $rc_type != RC_LOG ) {
+ $r .= ' ('. $rcObj->difflink . $this->message['semicolon-separator'] . ' ';
+ $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), wfMsg( 'hist' ), $curIdEq.'&action=history' ) . ')';
+ }
+ $r .= ' . . ';
# Character diff
if( $wgRCShowChangedSize ) {
@@ -665,16 +848,32 @@ class EnhancedChangesList extends ChangesList {
}
# User/talk
- $r .= $rcObj->userlink . $rcObj->usertalklink;
+ $r .= ' '.$rcObj->userlink . $rcObj->usertalklink;
- # Comment
+ # Log action (if any)
+ if( $rc_log_type ) {
+ if( $this->isDeleted($rcObj,LogPage::DELETED_ACTION) ) {
+ $r .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ } else {
+ $r .= ' ' . LogPage::actionText( $rc_log_type, $rc_log_action, $rcObj->getTitle(),
+ $this->skin, LogPage::extractParams($rc_params), true, true );
+ }
+ }
+
+ # Edit or log comment
if( $rc_type != RC_MOVE && $rc_type != RC_MOVE_OVER_REDIRECT ) {
- $r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
+ // log comment
+ if ( $this->isDeleted($rcObj,LogPage::DELETED_COMMENT) ) {
+ $r .= ' <span class="history-deleted">' . wfMsg('rev-deleted-comment') . '</span>';
+ } else {
+ $r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
+ }
}
+ # Show how many people are watching this if enabled
$r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers);
- $r .= "<br />\n";
+ $r .= "</td></tr></table>\n";
return $r;
}
@@ -682,7 +881,7 @@ class EnhancedChangesList extends ChangesList {
* If enhanced RC is in use, this function takes the previously cached
* RC lines, arranges them, and outputs the HTML
*/
- function recentChangesBlock() {
+ protected function recentChangesBlock() {
if( count ( $this->rc_cache ) == 0 ) {
return '';
}
@@ -702,7 +901,7 @@ class EnhancedChangesList extends ChangesList {
* Returns text for the end of RC
* If enhanced RC is in use, returns pretty much all the text
*/
- function endRecentChangesList() {
+ public function endRecentChangesList() {
return $this->recentChangesBlock() . parent::endRecentChangesList();
}
diff --git a/includes/Credits.php b/includes/Credits.php
index 580a8d92..6326e3a2 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -29,7 +29,7 @@ function showCreditsPage($article) {
$fname = 'showCreditsPage';
wfProfileIn( $fname );
-
+
$wgOut->setPageTitle( $article->mTitle->getPrefixedText() );
$wgOut->setSubtitle( wfMsg( 'creditspage' ) );
$wgOut->setArticleFlag( false );
@@ -184,5 +184,3 @@ function creditOthersLink($article) {
$skin = $wgUser->getSkin();
return $skin->makeKnownLink($article->mTitle->getPrefixedText(), wfMsg('others'), 'action=credits');
}
-
-
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
index a4a0444f..ad6e7f6c 100644
--- a/includes/DatabaseFunctions.php
+++ b/includes/DatabaseFunctions.php
@@ -2,7 +2,8 @@
/**
* Legacy database functions, for compatibility with pre-1.3 code
* NOTE: this file is no longer loaded by default.
- *
+ * @file
+ * @ingroup Database
*/
/**
@@ -399,4 +400,3 @@ function wfUseIndexClause( $index, $dbi = DB_SLAVE ) {
return false;
}
}
-
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 376e55b1..d6db7030 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -31,20 +31,25 @@ require_once( "$IP/includes/SiteConfiguration.php" );
$wgConf = new SiteConfiguration;
/** MediaWiki version number */
-$wgVersion = '1.12.0';
+$wgVersion = '1.13.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
-/**
- * Name of the project namespace. If left set to false, $wgSitename will be
+/**
+ * Name of the project namespace. If left set to false, $wgSitename will be
* used instead.
*/
$wgMetaNamespace = false;
/**
- * Name of the project talk namespace. If left set to false, a name derived
- * from the name of the project namespace will be used.
+ * Name of the project talk namespace.
+ *
+ * Normally you can ignore this and it will be something like
+ * $wgMetaNamespace . "_talk". In some languages, you may want to set this
+ * manually for grammatical reasons. It is currently only respected by those
+ * languages where it might be relevant and where no automatic grammar converter
+ * exists.
*/
$wgMetaNamespaceTalk = false;
@@ -90,25 +95,20 @@ if( isset( $_SERVER['SERVER_PORT'] )
$wgScriptPath = '/wiki';
/**
- * Whether to support URLs like index.php/Page_title
- * These often break when PHP is set up in CGI mode.
- * PATH_INFO *may* be correct if cgi.fix_pathinfo is
- * set, but then again it may not; lighttpd converts
- * incoming path data to lowercase on systems with
- * case-insensitive filesystems, and there have been
- * reports of problems on Apache as well.
+ * Whether to support URLs like index.php/Page_title These often break when PHP
+ * is set up in CGI mode. PATH_INFO *may* be correct if cgi.fix_pathinfo is set,
+ * but then again it may not; lighttpd converts incoming path data to lowercase
+ * on systems with case-insensitive filesystems, and there have been reports of
+ * problems on Apache as well.
*
* To be safe we'll continue to keep it off by default.
*
- * Override this to false if $_SERVER['PATH_INFO']
- * contains unexpectedly incorrect garbage, or to
- * true if it is really correct.
- *
- * The default $wgArticlePath will be set based on
- * this value at runtime, but if you have customized
- * it, having this incorrectly set to true can
- * cause redirect loops when "pretty URLs" are used.
+ * Override this to false if $_SERVER['PATH_INFO'] contains unexpectedly
+ * incorrect garbage, or to true if it is really correct.
*
+ * The default $wgArticlePath will be set based on this value at runtime, but if
+ * you have customized it, having this incorrectly set to true can cause
+ * redirect loops when "pretty URLs" are used.
*/
$wgUsePathInfo =
( strpos( php_sapi_name(), 'cgi' ) === false ) &&
@@ -116,53 +116,56 @@ $wgUsePathInfo =
( strpos( php_sapi_name(), 'isapi' ) === false );
-/**#@+
+/**@{
* Script users will request to get articles
- * ATTN: Old installations used wiki.phtml and redirect.phtml -
- * make sure that LocalSettings.php is correctly set!
+ * ATTN: Old installations used wiki.phtml and redirect.phtml - make sure that
+ * LocalSettings.php is correctly set!
*
- * Will be set based on $wgScriptPath in Setup.php if not overridden
- * in LocalSettings.php. Generally you should not need to change this
- * unless you don't like seeing "index.php".
+ * Will be set based on $wgScriptPath in Setup.php if not overridden in
+ * LocalSettings.php. Generally you should not need to change this unless you
+ * don't like seeing "index.php".
*/
-$wgScriptExtension = '.php'; /// extension to append to script names by default
-$wgScript = false; /// defaults to "{$wgScriptPath}/index{$wgScriptExtension}"
-$wgRedirectScript = false; /// defaults to "{$wgScriptPath}/redirect{$wgScriptExtension}"
-/**#@-*/
+$wgScriptExtension = '.php'; ///< extension to append to script names by default
+$wgScript = false; ///< defaults to "{$wgScriptPath}/index{$wgScriptExtension}"
+$wgRedirectScript = false; ///< defaults to "{$wgScriptPath}/redirect{$wgScriptExtension}"
+/**@}*/
-/**#@+
+/**@{
* These various web and file path variables are set to their defaults
* in Setup.php if they are not explicitly set from LocalSettings.php.
* If you do override them, be sure to set them all!
*
* These will relatively rarely need to be set manually, unless you are
* splitting style sheets or images outside the main document root.
- *
- * @global string
*/
/**
* style path as seen by users
*/
-$wgStylePath = false; /// defaults to "{$wgScriptPath}/skins"
+$wgStylePath = false; ///< defaults to "{$wgScriptPath}/skins"
/**
* filesystem stylesheets directory
*/
-$wgStyleDirectory = false; /// defaults to "{$IP}/skins"
+$wgStyleDirectory = false; ///< defaults to "{$IP}/skins"
$wgStyleSheetPath = &$wgStylePath;
-$wgArticlePath = false; /// default to "{$wgScript}/$1" or "{$wgScript}?title=$1", depending on $wgUsePathInfo
+$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"
+$wgUploadPath = false; ///< defaults to "{$wgScriptPath}/images"
+$wgUploadDirectory = false; ///< defaults to "{$IP}/images"
$wgHashedUploadDirectory = true;
-$wgLogo = false; /// defaults to "{$wgStylePath}/common/images/wiki.png"
+$wgLogo = false; ///< defaults to "{$wgStylePath}/common/images/wiki.png"
$wgFavicon = '/favicon.ico';
-$wgAppleTouchIcon = false; /// This one'll actually default to off. For iPhone and iPod Touch web app bookmarks
-$wgMathPath = false; /// defaults to "{$wgUploadPath}/math"
-$wgMathDirectory = false; /// defaults to "{$wgUploadDirectory}/math"
-$wgTmpDirectory = false; /// defaults to "{$wgUploadDirectory}/tmp"
+$wgAppleTouchIcon = false; ///< This one'll actually default to off. For iPhone and iPod Touch web app bookmarks
+$wgMathPath = false; ///< defaults to "{$wgUploadPath}/math"
+$wgMathDirectory = false; ///< defaults to "{$wgUploadDirectory}/math"
+$wgTmpDirectory = false; ///< defaults to "{$wgUploadDirectory}/tmp"
$wgUploadBaseUrl = "";
-/**#@-*/
+/**@}*/
+
+/**
+ * Default value for chmoding of new directories.
+ */
+$wgDirectoryMode = 0777;
/**
* New file storage paths; currently used only for deleted files.
@@ -172,46 +175,46 @@ $wgUploadBaseUrl = "";
*
*/
$wgFileStore = array();
-$wgFileStore['deleted']['directory'] = false;// Defaults to $wgUploadDirectory/deleted
-$wgFileStore['deleted']['url'] = null; // Private
-$wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
+$wgFileStore['deleted']['directory'] = false;///< Defaults to $wgUploadDirectory/deleted
+$wgFileStore['deleted']['url'] = null; ///< Private
+$wgFileStore['deleted']['hash'] = 3; ///< 3-level subdirectory split
-/**#@+
+/**@{
* File repository structures
*
* $wgLocalFileRepo is a single repository structure, and $wgForeignFileRepo is
- * a an array of such structures. Each repository structure is an associative
- * array of properties configuring the repository.
+ * a an array of such structures. Each repository structure is an associative
+ * array of properties configuring the repository.
*
* Properties required for all repos:
- * class The class name for the repository. May come from the core or an extension.
+ * class The class name for the repository. May come from the core or an extension.
* The core repository classes are LocalRepo, ForeignDBRepo, FSRepo.
*
* name A unique name for the repository.
- *
+ *
* For all core repos:
* url Base public URL
* hashLevels The number of directory levels for hash-based division of files
* thumbScriptUrl The URL for thumb.php (optional, not recommended)
- * transformVia404 Whether to skip media file transformation on parse and rely on a 404
+ * transformVia404 Whether to skip media file transformation on parse and rely on a 404
* handler instead.
- * initialCapital Equivalent to $wgCapitalLinks, determines whether filenames implicitly
+ * 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
+ * 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
+ * May be 'paranoid' to remove all parameters from error messages, 'none' to
+ * leave the paths in unchanged, or 'simple' to replace paths with
* placeholders. Default for LocalRepo is 'simple'.
*
* These settings describe a foreign MediaWiki installation. They are optional, and will be ignored
* for local repositories:
* descBaseUrl URL of image description pages, e.g. http://en.wikipedia.org/wiki/Image:
- * scriptDirUrl URL of the MediaWiki installation, equivalent to $wgScriptPath, e.g.
+ * scriptDirUrl URL of the MediaWiki installation, equivalent to $wgScriptPath, e.g.
* http://en.wikipedia.org/w
*
* articleUrl Equivalent to $wgArticlePath, e.g. http://en.wikipedia.org/wiki/$1
- * fetchDescription Fetch the text of the remote file description page. Equivalent to
+ * fetchDescription Fetch the text of the remote file description page. Equivalent to
* $wgFetchCommonsDescriptions.
*
* ForeignDBRepo:
@@ -220,12 +223,12 @@ $wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
* tablePrefix Table prefix, the foreign wiki's $wgDBprefix
* hasSharedCache True if the wiki's shared cache is accessible via the local $wgMemc
*
- * The default is to initialise these arrays from the MW<1.11 backwards compatible settings:
+ * The default is to initialise these arrays from the MW<1.11 backwards compatible settings:
* $wgUploadPath, $wgThumbnailScriptPath, $wgSharedUploadDirectory, etc.
*/
$wgLocalFileRepo = false;
$wgForeignFileRepos = array();
-/**#@-*/
+/**@}*/
/**
* Allowed title characters -- regex character class
@@ -274,7 +277,6 @@ $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.
- * @global string $wgAntivirus
*/
$wgAntivirus= NULL;
@@ -301,8 +303,6 @@ $wgAntivirus= NULL;
* "messagepattern" is a perl regular expression to extract the meaningful part of the scanners
* output. The relevant part should be matched as group one (\1).
* If not defined or the pattern does not match, the full message is shown to the user.
- *
- * @global array $wgAntivirusSetup
*/
$wgAntivirusSetup = array(
@@ -336,51 +336,54 @@ $wgAntivirusSetup = array(
);
-/** Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected.
- * @global boolean $wgAntivirusRequired
-*/
+/** Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected. */
$wgAntivirusRequired= true;
-/** Determines if the mime type of uploaded files should be checked
- * @global boolean $wgVerifyMimeType
-*/
+/** Determines if the mime type of uploaded files should be checked */
$wgVerifyMimeType= true;
-/** Sets the mime type definition file to use by MimeMagic.php.
-* @global string $wgMimeTypeFile
-*/
+/** 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.
-/** Sets the mime type info file to use by MimeMagic.php.
-* @global string $wgMimeInfoFile
-*/
+/** Sets the mime type info file to use by MimeMagic.php. */
$wgMimeInfoFile= "includes/mime.info";
#$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
+ * This should be used only if fileinfo is installed as a shared object
* or a dynamic libary
- * @global string $wgLoadFileinfoExtension
-*/
+ */
$wgLoadFileinfoExtension= false;
/** Sets an external mime detector program. The command must print only
* the mime type to standard output.
* The name of the file to process will be appended to the command given here.
* If not set or NULL, mime_content_type will be used if available.
-*/
+ */
$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
* things, because only a few types of images are needed and file extensions
* can be trusted.
-*/
+ */
$wgTrivialMimeDetection= false;
/**
+ * Additional XML types we can allow via mime-detection.
+ * array = ( 'rootElement' => 'associatedMimeType' )
+ */
+$wgXMLMimeTypes = array(
+ 'http://www.w3.org/2000/svg:svg' => 'image/svg+xml',
+ 'svg' => 'image/svg+xml',
+ 'http://www.lysator.liu.se/~alla/dia/:diagram' => 'application/x-dia-diagram',
+ 'http://www.w3.org/1999/xhtml:html' => 'text/html', // application/xhtml+xml?
+ 'html' => 'text/html', // application/xhtml+xml?
+);
+
+/**
* To set 'pretty' URL paths for actions other than
* plain page views, add to this array. For instance:
* 'edit' => "$wgScriptPath/edit/$1"
@@ -430,7 +433,7 @@ $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';
-*/
+ */
$wgUploadNavigationUrl = false;
/**
@@ -461,12 +464,6 @@ $wgHashedSharedUploadDirectory = true;
*/
$wgRepositoryBaseUrl = "http://commons.wikimedia.org/wiki/Image:";
-/**
- * Experimental feature still under debugging.
- */
-$wgFileRedirects = false;
-
-
#
# Email settings
#
@@ -474,7 +471,6 @@ $wgFileRedirects = false;
/**
* Site admin email address
* Default to wikiadmin@SERVER_NAME
- * @global string $wgEmergencyContact
*/
$wgEmergencyContact = 'wikiadmin@' . $wgServerName;
@@ -482,7 +478,6 @@ $wgEmergencyContact = 'wikiadmin@' . $wgServerName;
* Password reminder email address
* The address we should use as sender when a user is requesting his password
* Default to apache@SERVER_NAME
- * @global string $wgPasswordSender
*/
$wgPasswordSender = 'MediaWiki Mail <apache@' . $wgServerName . '>';
@@ -498,14 +493,12 @@ $wgNoReplyAddress = 'reply@not.possible';
* Set to true to enable the e-mail basic features:
* Password reminders, etc. If sending e-mail on your
* server doesn't work, you might want to disable this.
- * @global bool $wgEnableEmail
*/
$wgEnableEmail = true;
/**
* Set to true to enable user-to-user e-mail.
* This can potentially be abused, as it's hard to track.
- * @global bool $wgEnableUserEmail
*/
$wgEnableUserEmail = true;
@@ -537,13 +530,11 @@ $wgPasswordReminderResendTime = 24;
* "username" => user,
* "password" => password
* </code>
- *
- * @global mixed $wgSMTP
*/
$wgSMTP = false;
-/**#@+
+/**@{
* Database settings
*/
/** database host name or ip address */
@@ -556,21 +547,39 @@ $wgDBname = 'wikidb';
$wgDBconnection = '';
/** Database username */
$wgDBuser = 'wikiuser';
-/** Database type
- */
-$wgDBtype = "mysql";
+/** Database user's password */
+$wgDBpassword = '';
+/** Database type */
+$wgDBtype = 'mysql';
+
/** Search type
* Leave as null to select the default search engine for the
- * selected database type (eg SearchMySQL4), or set to a class
+ * selected database type (eg SearchMySQL), or set to a class
* name to override to a custom search engine.
*/
$wgSearchType = null;
+
/** Table name prefix */
$wgDBprefix = '';
/** MySQL table options to use during installation or update */
-$wgDBTableOptions = 'TYPE=InnoDB';
+$wgDBTableOptions = 'ENGINE=InnoDB';
+
+/** Mediawiki schema */
+$wgDBmwschema = 'mediawiki';
+/** Tsearch2 schema */
+$wgDBts2schema = 'public';
+
+/** To override default SQLite data directory ($docroot/../data) */
+$wgSQLiteDataDir = '';
+
+/**
+ * Make all database connections secretly go to localhost. Fool the load balancer
+ * thinking there is an arbitrarily large cluster of servers to connect to.
+ * Useful for debugging.
+ */
+$wgAllDBsAreLocalhost = false;
-/**#@-*/
+/**@}*/
/** Live high performance sites should disable this - some checks acquire giant mysql locks */
@@ -578,55 +587,76 @@ $wgCheckDBSchema = true;
/**
- * Shared database for multiple wikis. Presently used for storing a user table
+ * Shared database for multiple wikis. Commonly used for storing a user table
* for single sign-on. The server for this database must be the same as for the
* 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.
+ *
+ * $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
+ * MediaWiki's tables may have side effects if you try to share them.
* EXPERIMENTAL
*/
-$wgSharedDB = null;
-
-# Database load balancer
-# This is a two-dimensional array, an array of server info structures
-# Fields are:
-# host: Host name
-# dbname: Default database name
-# user: DB user
-# password: DB password
-# type: "mysql" or "postgres"
-# load: ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0
-# groupLoads: array of load ratios, the key is the query group name. A query may belong
-# to several groups, the most specific group defined here is used.
-#
-# flags: bit field
-# DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
-# DBO_DEBUG -- equivalent of $wgDebugDumpSql
-# DBO_TRX -- wrap entire request in a transaction
-# DBO_IGNORE -- ignore errors (not useful in LocalSettings.php)
-# DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
-#
-# max lag: (optional) Maximum replication lag before a slave will taken out of rotation
-# max threads: (optional) Maximum number of running threads
-#
-# These and any other user-defined properties will be assigned to the mLBInfo member
-# variable of the Database object.
-#
-# Leave at false to use the single-server variables above. If you set this
-# variable, the single-server variables will generally be ignored (except
-# perhaps in some command-line scripts).
-#
-# The first server listed in this array (with key 0) will be the master. The
-# rest of the servers will be slaves. To prevent writes to your slaves due to
-# accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your
-# slaves in my.cnf. You can set read_only mode at runtime using:
-#
-# SET @@read_only=1;
-#
-# Since the effect of writing to a slave is so damaging and difficult to clean
-# up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even
-# our masters, and then set read_only=0 on masters at runtime.
-#
+$wgSharedDB = null;
+$wgSharedPrefix = false; # Defaults to $wgDBprefix
+$wgSharedTables = array( 'user' );
+
+/**
+ * Database load balancer
+ * This is a two-dimensional array, an array of server info structures
+ * Fields are:
+ * host: Host name
+ * dbname: Default database name
+ * user: DB user
+ * password: DB password
+ * type: "mysql" or "postgres"
+ * load: ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0
+ * groupLoads: array of load ratios, the key is the query group name. A query may belong
+ * to several groups, the most specific group defined here is used.
+ *
+ * flags: bit field
+ * DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
+ * DBO_DEBUG -- equivalent of $wgDebugDumpSql
+ * DBO_TRX -- wrap entire request in a transaction
+ * DBO_IGNORE -- ignore errors (not useful in LocalSettings.php)
+ * DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
+ *
+ * max lag: (optional) Maximum replication lag before a slave will taken out of rotation
+ * max threads: (optional) Maximum number of running threads
+ *
+ * These and any other user-defined properties will be assigned to the mLBInfo member
+ * variable of the Database object.
+ *
+ * Leave at false to use the single-server variables above. If you set this
+ * variable, the single-server variables will generally be ignored (except
+ * perhaps in some command-line scripts).
+ *
+ * The first server listed in this array (with key 0) will be the master. The
+ * rest of the servers will be slaves. To prevent writes to your slaves due to
+ * accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your
+ * slaves in my.cnf. You can set read_only mode at runtime using:
+ *
+ * SET @@read_only=1;
+ *
+ * Since the effect of writing to a slave is so damaging and difficult to clean
+ * up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even
+ * our masters, and then set read_only=0 on masters at runtime.
+ */
$wgDBservers = false;
+/**
+ * Load balancer factory configuration
+ * To set up a multi-master wiki farm, set the class here to something that
+ * can return a LoadBalancer with an appropriate master on a call to getMainLB().
+ * The class identified here is responsible for reading $wgDBservers,
+ * $wgDBserver, etc., so overriding it may cause those globals to be ignored.
+ *
+ * The LBFactory_Multi class is provided for this purpose, please see
+ * includes/db/LBFactory_Multi.php for configuration information.
+ */
+$wgLBFactoryConf = array( 'class' => 'LBFactory_Simple' );
+
/** How long to wait for a slave to catch up to the master */
$wgMasterWaitTimeout = 10;
@@ -681,40 +711,28 @@ $wgDBmysql5 = false;
*/
$wgLocalDatabases = array();
-/**
- * For multi-wiki clusters with multiple master servers; if an alternate
- * is listed for the requested database, a connection to it will be opened
- * instead of to the current wiki's regular master server when cross-wiki
- * data operations are done from here.
- *
- * Requires that the other server be accessible by network, with the same
- * username/password as the primary.
- *
- * eg $wgAlternateMaster['enwiki'] = 'ariel';
- */
-$wgAlternateMaster = array();
-
-/**
+/** @{
* Object cache settings
* See Defines.php for types
*/
$wgMainCacheType = CACHE_NONE;
$wgMessageCacheType = CACHE_ANYTHING;
$wgParserCacheType = CACHE_ANYTHING;
+/**@}*/
$wgParserCacheExpireTime = 86400;
$wgSessionsInMemcached = false;
-$wgLinkCacheMemcached = false; # Not fully tested
-/**
+/**@{
* Memcached-specific settings
* See docs/memcached.txt
*/
$wgUseMemCached = false;
-$wgMemCachedDebug = false; # Will be set to false in Setup.php, if the server isn't working
+$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;
+/**@}*/
/**
* Directory for local copy of message cache, for use in addition to memcached
@@ -759,14 +777,16 @@ $wgInputEncoding = 'UTF-8';
$wgOutputEncoding = 'UTF-8';
$wgEditEncoding = '';
-# Set this to eg 'ISO-8859-1' to perform character set
-# conversion when loading old revisions not marked with
-# "utf-8" flag. Use this when converting wiki to UTF-8
-# without the burdensome mass conversion of old text data.
-#
-# NOTE! This DOES NOT touch any fields other than old_text.
-# Titles, comments, user names, etc still must be converted
-# en masse in the database before continuing as a UTF-8 wiki.
+/**
+ * Set this to eg 'ISO-8859-1' to perform character set
+ * conversion when loading old revisions not marked with
+ * "utf-8" flag. Use this when converting wiki to UTF-8
+ * without the burdensome mass conversion of old text data.
+ *
+ * NOTE! This DOES NOT touch any fields other than old_text.
+ * Titles, comments, user names, etc still must be converted
+ * en masse in the database before continuing as a UTF-8 wiki.
+ */
$wgLegacyEncoding = false;
/**
@@ -792,12 +812,14 @@ $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';
-# Permit other namespaces in addition to the w3.org default.
-# Use the prefix for the key and the namespace for the value. For
-# example:
-# $wgXhtmlNamespaces['svg'] = 'http://www.w3.org/2000/svg';
-# Normally we wouldn't have to define this in the root <html>
-# element, but IE needs it there in some circumstances.
+/**
+ * Permit other namespaces in addition to the w3.org default.
+ * Use the prefix for the key and the namespace for the value. For
+ * example:
+ * $wgXhtmlNamespaces['svg'] = 'http://www.w3.org/2000/svg';
+ * Normally we wouldn't have to define this in the root <html>
+ * element, but IE needs it there in some circumstances.
+ */
$wgXhtmlNamespaces = array();
/** Enable to allow rewriting dates in page text.
@@ -836,10 +858,10 @@ $wgMaxMsgCacheEntrySize = 10000;
*/
$wgCheckSerialized = true;
-# Whether to enable language variant conversion.
+/** Whether to enable language variant conversion. */
$wgDisableLangConversion = false;
-# Default variant code, if false, the default will be the language code
+/** Default variant code, if false, the default will be the language code */
$wgDefaultLanguageVariant = false;
/**
@@ -849,21 +871,15 @@ $wgDefaultLanguageVariant = false;
*/
$wgLoginLanguageSelector = false;
-# Whether to use zhdaemon to perform Chinese text processing
-# zhdaemon is under developement, so normally you don't want to
-# use it unless for testing
+/**
+ * Whether to use zhdaemon to perform Chinese text processing
+ * zhdaemon is under developement, so normally you don't want to
+ * use it unless for testing
+ */
$wgUseZhdaemon = false;
$wgZhdaemonHost="localhost";
$wgZhdaemonPort=2004;
-/** Normally you can ignore this and it will be something
- like $wgMetaNamespace . "_talk". In some languages, you
- may want to set this manually for grammatical reasons.
- It is currently only respected by those languages
- where it might be relevant and where no automatic
- grammar converter exists.
-*/
-$wgMetaNamespaceTalk = false;
# Miscellaneous configuration settings
#
@@ -886,7 +902,7 @@ $wgInterwikiExpiry = 10800; # Expiry time for cache of interwiki table
2 - wiki and global levels
3 - site levels
$wgInterwikiFallbackSite - if unable to resolve from cache
-*/
+ */
$wgInterwikiCache = false;
$wgInterwikiScopes = 3;
$wgInterwikiFallbackSite = 'wiki';
@@ -907,19 +923,22 @@ $wgRedirectSources = false;
$wgShowIPinHeader = true; # For non-logged in users
-$wgMaxNameChars = 255; # Maximum number of bytes in username
$wgMaxSigChars = 255; # Maximum number of Unicode characters in signature
$wgMaxArticleSize = 2048; # Maximum article size in kilobytes
+# Maximum number of bytes in username. You want to run the maintenance
+# script ./maintenancecheckUsernames.php once you have changed this value
+$wgMaxNameChars = 255;
$wgMaxPPNodeCount = 1000000; # A complexity limit on template expansion
/**
* Maximum recursion depth for templates within templates.
- * The current parser adds two levels to the PHP call stack for each template,
+ * The current parser adds two levels to the PHP call stack for each template,
* and xdebug limits the call stack to 100 by default. So this should hopefully
* stop the parser before it hits the xdebug limit.
*/
$wgMaxTemplateDepth = 40;
+$wgMaxPPExpandDepth = 40;
$wgExtraSubtitle = '';
$wgSiteSupportPage = ''; # A page where you users can receive donations
@@ -929,16 +948,13 @@ $wgSiteSupportPage = ''; # A page where you users can receive donations
* Its contents will be shown to users as part of the read-only warning
* message.
*/
-$wgReadOnlyFile = false; /// defaults to "{$wgUploadDirectory}/lock_yBgMBwiR";
+$wgReadOnlyFile = false; ///< defaults to "{$wgUploadDirectory}/lock_yBgMBwiR";
/**
* The debug log file should be not be publicly accessible if it is used, as it
* may contain private data. */
$wgDebugLogFile = '';
-/**#@+
- * @global bool
- */
$wgDebugRedirects = false;
$wgDebugRawPage = false; # Avoid overlapping debug entries by leaving out CSS
@@ -960,6 +976,11 @@ $wgDebugDumpSql = false;
$wgDebugLogGroups = array();
/**
+ * Show the contents of $wgHooks in Special:Version
+ */
+$wgSpecialVersionShowHooks = false;
+
+/**
* Whether to show "we're sorry, but there has been a database error" pages.
* Displaying errors aids in debugging, but may display information useful
* to an attacker.
@@ -1026,18 +1047,16 @@ $wgSidebarCacheExpiry = 86400;
*
* Retroactively changing this variable will not affect
* the existing count (cf. maintenance/recount.sql).
-*/
+ */
$wgUseCommaCount = false;
-/**#@-*/
-
/**
* wgHitcounterUpdateFreq sets how often page counters should be updated, higher
* values are easier on the database. A value of 1 causes the counters to be
* updated on every hit, any higher value n cause them to update *on average*
* every n hits. Should be set to either 1 or something largish, eg 1000, for
* maximum efficiency.
-*/
+ */
$wgHitcounterUpdateFreq = 1;
# Basic user rights and block settings
@@ -1048,7 +1067,8 @@ $wgBlockAllowsUTEdit = false; # Blocks allow users to edit their own user tal
$wgSysopEmailBans = true; # Allow sysops to ban users from accessing Emailuser
# Pages anonymous user may see as an array, e.g.:
-# array ( "Main Page", "Special:Userlogin", "Wikipedia:Help");
+# array ( "Main Page", "Wikipedia:Help");
+# Special:Userlogin and Special:Resetpass are always whitelisted.
# NOTE: This will only work if $wgGroupPermissions['*']['read']
# is false -- see below. Otherwise, ALL pages are accessible,
# regardless of this setting.
@@ -1057,7 +1077,7 @@ $wgSysopEmailBans = true; # Allow sysops to ban users from accessing Email
# directory name unguessable, or use .htaccess to protect it.
$wgWhitelistRead = false;
-/**
+/**
* Should editors be required to have a validated e-mail
* address before being allowed to edit?
*/
@@ -1083,79 +1103,88 @@ $wgEmailConfirmToEdit=false;
$wgGroupPermissions = array();
// Implicit group for all visitors
-$wgGroupPermissions['*' ]['createaccount'] = true;
-$wgGroupPermissions['*' ]['read'] = true;
-$wgGroupPermissions['*' ]['edit'] = true;
-$wgGroupPermissions['*' ]['createpage'] = true;
-$wgGroupPermissions['*' ]['createtalk'] = true;
+$wgGroupPermissions['*' ]['createaccount'] = true;
+$wgGroupPermissions['*' ]['read'] = true;
+$wgGroupPermissions['*' ]['edit'] = true;
+$wgGroupPermissions['*' ]['createpage'] = true;
+$wgGroupPermissions['*' ]['createtalk'] = true;
+$wgGroupPermissions['*' ]['writeapi'] = true;
// Implicit group for all logged-in accounts
-$wgGroupPermissions['user' ]['move'] = true;
-$wgGroupPermissions['user' ]['read'] = true;
-$wgGroupPermissions['user' ]['edit'] = true;
-$wgGroupPermissions['user' ]['createpage'] = true;
-$wgGroupPermissions['user' ]['createtalk'] = true;
-$wgGroupPermissions['user' ]['upload'] = true;
-$wgGroupPermissions['user' ]['reupload'] = true;
-$wgGroupPermissions['user' ]['reupload-shared'] = true;
-$wgGroupPermissions['user' ]['minoredit'] = true;
-$wgGroupPermissions['user' ]['purge'] = true; // can use ?action=purge without clicking "ok"
+$wgGroupPermissions['user' ]['move'] = true;
+$wgGroupPermissions['user' ]['move-subpages'] = true;
+$wgGroupPermissions['user' ]['read'] = true;
+$wgGroupPermissions['user' ]['edit'] = true;
+$wgGroupPermissions['user' ]['createpage'] = true;
+$wgGroupPermissions['user' ]['createtalk'] = true;
+$wgGroupPermissions['user' ]['writeapi'] = true;
+$wgGroupPermissions['user' ]['upload'] = true;
+$wgGroupPermissions['user' ]['reupload'] = true;
+$wgGroupPermissions['user' ]['reupload-shared'] = true;
+$wgGroupPermissions['user' ]['minoredit'] = true;
+$wgGroupPermissions['user' ]['purge'] = true; // can use ?action=purge without clicking "ok"
// Implicit group for accounts that pass $wgAutoConfirmAge
$wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
-// Implicit group for accounts with confirmed email addresses
-// This has little use when email address confirmation is off
-$wgGroupPermissions['emailconfirmed']['emailconfirmed'] = true;
-
// Users with bot privilege can have their edits hidden
// from various log pages by default
-$wgGroupPermissions['bot' ]['bot'] = true;
-$wgGroupPermissions['bot' ]['autoconfirmed'] = true;
-$wgGroupPermissions['bot' ]['nominornewtalk'] = true;
-$wgGroupPermissions['bot' ]['autopatrol'] = true;
+$wgGroupPermissions['bot' ]['bot'] = true;
+$wgGroupPermissions['bot' ]['autoconfirmed'] = true;
+$wgGroupPermissions['bot' ]['nominornewtalk'] = true;
+$wgGroupPermissions['bot' ]['autopatrol'] = true;
$wgGroupPermissions['bot' ]['suppressredirect'] = true;
-$wgGroupPermissions['bot' ]['apihighlimits'] = true;
+$wgGroupPermissions['bot' ]['apihighlimits'] = true;
+$wgGroupPermissions['bot' ]['writeapi'] = true;
+#$wgGroupPermissions['bot' ]['editprotected'] = true; // can edit all protected pages without cascade protection enabled
// Most extra permission abilities go to this group
-$wgGroupPermissions['sysop']['block'] = true;
-$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']['undelete'] = true;
-$wgGroupPermissions['sysop']['editinterface'] = true;
-$wgGroupPermissions['sysop']['editusercssjs'] = true;
-$wgGroupPermissions['sysop']['import'] = true;
-$wgGroupPermissions['sysop']['importupload'] = true;
-$wgGroupPermissions['sysop']['move'] = true;
-$wgGroupPermissions['sysop']['patrol'] = true;
-$wgGroupPermissions['sysop']['autopatrol'] = true;
-$wgGroupPermissions['sysop']['protect'] = true;
-$wgGroupPermissions['sysop']['proxyunbannable'] = true;
-$wgGroupPermissions['sysop']['rollback'] = true;
-$wgGroupPermissions['sysop']['trackback'] = true;
-$wgGroupPermissions['sysop']['upload'] = true;
-$wgGroupPermissions['sysop']['reupload'] = true;
-$wgGroupPermissions['sysop']['reupload-shared'] = true;
-$wgGroupPermissions['sysop']['unwatchedpages'] = true;
-$wgGroupPermissions['sysop']['autoconfirmed'] = true;
-$wgGroupPermissions['sysop']['upload_by_url'] = true;
-$wgGroupPermissions['sysop']['ipblock-exempt'] = true;
-$wgGroupPermissions['sysop']['blockemail'] = true;
-$wgGroupPermissions['sysop']['markbotedits'] = true;
+$wgGroupPermissions['sysop']['block'] = true;
+$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']['undelete'] = true;
+$wgGroupPermissions['sysop']['editinterface'] = true;
+$wgGroupPermissions['sysop']['editusercssjs'] = true;
+$wgGroupPermissions['sysop']['import'] = true;
+$wgGroupPermissions['sysop']['importupload'] = true;
+$wgGroupPermissions['sysop']['move'] = true;
+$wgGroupPermissions['sysop']['move-subpages'] = true;
+$wgGroupPermissions['sysop']['patrol'] = true;
+$wgGroupPermissions['sysop']['autopatrol'] = true;
+$wgGroupPermissions['sysop']['protect'] = true;
+$wgGroupPermissions['sysop']['proxyunbannable'] = true;
+$wgGroupPermissions['sysop']['rollback'] = true;
+$wgGroupPermissions['sysop']['trackback'] = true;
+$wgGroupPermissions['sysop']['upload'] = true;
+$wgGroupPermissions['sysop']['reupload'] = true;
+$wgGroupPermissions['sysop']['reupload-shared'] = true;
+$wgGroupPermissions['sysop']['unwatchedpages'] = true;
+$wgGroupPermissions['sysop']['autoconfirmed'] = true;
+$wgGroupPermissions['sysop']['upload_by_url'] = true;
+$wgGroupPermissions['sysop']['ipblock-exempt'] = true;
+$wgGroupPermissions['sysop']['blockemail'] = true;
+$wgGroupPermissions['sysop']['markbotedits'] = true;
$wgGroupPermissions['sysop']['suppressredirect'] = true;
-$wgGroupPermissions['sysop']['apihighlimits'] = true;
-#$wgGroupPermissions['sysop']['mergehistory'] = true;
+$wgGroupPermissions['sysop']['apihighlimits'] = true;
+$wgGroupPermissions['sysop']['browsearchive'] = true;
+$wgGroupPermissions['sysop']['noratelimit'] = true;
+#$wgGroupPermissions['sysop']['mergehistory'] = true;
// Permission to change users' group assignments
-$wgGroupPermissions['bureaucrat']['userrights'] = true;
+$wgGroupPermissions['bureaucrat']['userrights'] = true;
+$wgGroupPermissions['bureaucrat']['noratelimit'] = true;
// Permission to change users' groups assignments across wikis
#$wgGroupPermissions['bureaucrat']['userrights-interwiki'] = true;
-// Experimental permissions, not ready for production use
-//$wgGroupPermissions['sysop']['deleterevision'] = true;
-//$wgGroupPermissions['bureaucrat']['hiderevision'] = true;
+#$wgGroupPermissions['sysop']['deleterevision'] = true;
+// To hide usernames from users and Sysops
+#$wgGroupPermissions['suppress']['hideuser'] = true;
+// To hide revisions/log items from users and Sysops
+#$wgGroupPermissions['suppress']['suppressrevision'] = true;
+// For private suppression log access
+#$wgGroupPermissions['suppress']['suppressionlog'] = true;
/**
* The developer group is deprecated, but can be activated if need be
@@ -1169,7 +1198,7 @@ $wgGroupPermissions['bureaucrat']['userrights'] = true;
/**
* Implicit groups, aren't shown on Special:Listusers or somewhere else
*/
-$wgImplicitGroups = array( '*', 'user', 'autoconfirmed', 'emailconfirmed' );
+$wgImplicitGroups = array( '*', 'user', 'autoconfirmed' );
/**
* These are the groups that users are allowed to add to or remove from
@@ -1206,11 +1235,11 @@ $wgNamespaceProtection = array();
$wgNamespaceProtection[ NS_MEDIAWIKI ] = array( 'editinterface' );
/**
-* Pages in namespaces in this array can not be used as templates.
-* Elements must be numeric namespace ids.
-* Among other things, this may be useful to enforce read-restrictions
-* which may otherwise be bypassed by using the template machanism.
-*/
+ * Pages in namespaces in this array can not be used as templates.
+ * Elements must be numeric namespace ids.
+ * Among other things, this may be useful to enforce read-restrictions
+ * which may otherwise be bypassed by using the template machanism.
+ */
$wgNonincludableNamespaces = array();
/**
@@ -1252,7 +1281,6 @@ $wgAutopromote = array(
array( APCOND_EDITCOUNT, &$wgAutoConfirmCount ),
array( APCOND_AGE, &$wgAutoConfirmAge ),
),
- 'emailconfirmed' => APCOND_EMAILCONFIRMED,
);
/**
@@ -1260,22 +1288,37 @@ $wgAutopromote = array(
* groups at Special:Userrights. Example configuration:
*
* // Bureaucrat can add any group
- * $wgAddGroups['bureaucrat'] = true;
+ * $wgAddGroups['bureaucrat'] = true;
* // Bureaucrats can only remove bots and sysops
- * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' );
+ * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' );
* // Sysops can make bots
- * $wgAddGroups['sysop'] = array( 'bot' );
+ * $wgAddGroups['sysop'] = array( 'bot' );
* // Sysops can disable other sysops in an emergency, and disable bots
- * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' );
+ * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' );
*/
$wgAddGroups = $wgRemoveGroups = array();
+
+/**
+ * A list of available rights, in addition to the ones defined by the core.
+ * For extensions only.
+ */
+$wgAvailableRights = array();
+
/**
* Optional to restrict deletion of pages with higher revision counts
* to users with the 'bigdelete' permission. (Default given to sysops.)
*/
$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
#
@@ -1325,7 +1368,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '116';
+$wgStyleVersion = '164';
# Server-side caching:
@@ -1338,7 +1381,7 @@ $wgStyleVersion = '116';
$wgUseFileCache = false;
/** Directory where the cached page will be saved */
-$wgFileCacheDirectory = false; /// defaults to "{$wgUploadDirectory}/cache";
+$wgFileCacheDirectory = false; ///< defaults to "{$wgUploadDirectory}/cache";
/**
* When using the file cache, we can store the cached HTML gzipped to save disk
@@ -1382,17 +1425,17 @@ $wgEnotifMinorEdits = true; # UPO; false: "minor edits" on pages do not trigger
$wgEnotifImpersonal = false;
-# Maximum number of users to mail at once when using impersonal mail. Should
+# Maximum number of users to mail at once when using impersonal mail. Should
# match the limit on your mail server.
$wgEnotifMaxRecips = 500;
# Send mails via the job queue.
$wgEnotifUseJobQ = false;
-/**
+/**
* Array of usernames who will be sent a notification email for every change which occurs on a wiki
*/
-$wgUsersNotifedOnAllChanges = array();
+$wgUsersNotifiedOnAllChanges = array();
/** Show watching users in recent changes, watchlist and page history views */
$wgRCShowWatchingUsers = false; # UPO
@@ -1403,7 +1446,7 @@ $wgRCShowChangedSize = true;
/**
* If the difference between the character counts of the text
- * before and after the edit is below that value, the value will be
+ * before and after the edit is below that value, the value will be
* highlighted on the RC page.
*/
$wgRCChangedSizeThreshold = -500;
@@ -1484,6 +1527,35 @@ $wgCookiePath = '/';
$wgCookieSecure = ($wgProto == 'https');
$wgDisableCookieCheck = false;
+/**
+ * Set $wgCookiePrefix to use a custom one. Setting to false sets the default of
+ * using the database name.
+ */
+$wgCookiePrefix = false;
+
+/**
+ * Set authentication cookies to HttpOnly to prevent access by JavaScript,
+ * in browsers that support this feature. This can mitigates some classes of
+ * XSS attack.
+ *
+ * Only supported on PHP 5.2 or higher.
+ */
+$wgCookieHttpOnly = version_compare("5.2", PHP_VERSION, "<");
+
+/**
+ * If the requesting browser matches a regex in this blacklist, we won't
+ * send it cookies with HttpOnly mode, even if $wgCookieHttpOnly is on.
+ */
+$wgHttpOnlyBlacklist = array(
+ // Internet Explorer for Mac; sometimes the cookies work, sometimes
+ // they don't. It's difficult to predict, as combinations of path
+ // and expiration options affect its parsing.
+ '/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
+);
+
+/** A list of cookies that vary the cache (for use by extensions) */
+$wgCacheVaryCookies = array();
+
/** Override to customise the session name */
$wgSessionName = false;
@@ -1499,6 +1571,9 @@ $wgAllowExternalImages = false;
*/
$wgAllowExternalImagesFrom = '';
+/** Allows to move images and other media files. Experemintal, not sure if it always works */
+$wgAllowImageMoving = false;
+
/** Disable database-intensive features */
$wgMiserMode = false;
/** Disable all query pages if miser mode is on, not just some */
@@ -1520,6 +1595,7 @@ $wgJobClasses = array(
'html_cache_update' => 'HTMLCacheUpdateJob', // backwards-compatible
'sendMail' => 'EmaillingJob',
'enotifNotify' => 'EnotifNotifyJob',
+ 'fixDoubleRedirect' => 'DoubleRedirectJob',
);
/**
@@ -1577,6 +1653,46 @@ $wgDisableCounters = false;
$wgDisableTextSearch = false;
$wgDisableSearchContext = false;
+
+
+/**
+ * Set to true to have nicer highligted text in search results,
+ * by default off due to execution overhead
+ */
+$wgAdvancedSearchHighlighting = false;
+
+/**
+ * Regexp to match word boundaries, defaults for non-CJK languages
+ * should be empty for CJK since the words are not separate
+ */
+$wgSearchHighlightBoundaries = version_compare("5.1", PHP_VERSION, "<")? '[\p{Z}\p{P}\p{C}]'
+ : '[ ,.;:!?~!@#$%\^&*\(\)+=\-\\|\[\]"\'<>\n\r\/{}]'; // PHP 5.0 workaround
+
+/**
+ * Template for OpenSearch suggestions, defaults to API action=opensearch
+ *
+ * Sites with heavy load would tipically have these point to a custom
+ * PHP wrapper to avoid firing up mediawiki for every keystroke
+ *
+ * Placeholders: {searchTerms}
+ *
+ */
+$wgOpenSearchTemplate = false;
+
+/**
+ * Enable suggestions while typing in search boxes
+ * (results are passed around in OpenSearch format)
+ */
+$wgEnableMWSuggest = false;
+
+/**
+ * Template for internal MediaWiki suggestion engine, defaults to API action=opensearch
+ *
+ * Placeholders: {searchTerms}, {namespaces}, {dbname}
+ *
+ */
+$wgMWSuggestTemplate = false;
+
/**
* If you've disabled search semi-permanently, this also disables updates to the
* table. If you ever re-enable, be sure to rebuild the search table.
@@ -1632,6 +1748,11 @@ $wgAntiLockFlags = 0;
$wgDiff3 = '/usr/bin/diff3';
/**
+ * Path to the GNU diff utility.
+ */
+$wgDiff = '/usr/bin/diff';
+
+/**
* We can also compress text stored in the 'text' table. If this is set on, new
* revisions will be compressed on page save if zlib support is available. Any
* compressed revisions will be decompressed on load regardless of this setting
@@ -1679,8 +1800,8 @@ $wgCheckFileExtensions = true;
*/
$wgStrictFileExtensions = true;
-/** Warn if uploaded files are larger than this (in bytes)*/
-$wgUploadSizeWarning = 150 * 1024;
+/** Warn if uploaded files are larger than this (in bytes), or false to disable*/
+$wgUploadSizeWarning = false;
/** For compatibility with old installations set to false */
$wgPasswordSalt = true;
@@ -1717,7 +1838,7 @@ $wgSiteNotice = '';
# Images settings
#
-/**
+/**
* Plugins for media file type handling.
* Each entry in the array maps a MIME type to a class name
*/
@@ -1726,6 +1847,7 @@ $wgMediaHandlers = array(
'image/png' => 'BitmapHandler',
'image/gif' => 'BitmapHandler',
'image/x-ms-bmp' => 'BmpHandler',
+ 'image/x-bmp' => 'BmpHandler',
'image/svg+xml' => 'SvgHandler', // official
'image/svg' => 'SvgHandler', // compat
'image/vnd.djvu' => 'DjVuHandler', // official
@@ -1774,13 +1896,14 @@ $wgSVGConverters = array(
'inkscape' => '$path/inkscape -z -w $width -f $input -e $output',
'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input',
'rsvg' => '$path/rsvg -w$width -h$height $input $output',
+ 'imgserv' => '$path/imgserv-wrapper -i svg -o png -w$width $input $output',
);
/** Pick one of the above */
$wgSVGConverter = 'ImageMagick';
/** If not in the executable PATH, specify */
$wgSVGConverterPath = '';
/** Don't scale a SVG larger than this */
-$wgSVGMaxSize = 1024;
+$wgSVGMaxSize = 2048;
/**
* Don't thumbnail an image if it will use too much working memory
* Default is 50 MB if decompressed to RGBA form, which corresponds to
@@ -1809,11 +1932,11 @@ $wgThumbnailEpoch = '20030516000000';
$wgIgnoreImageErrors = false;
/**
- * Allow thumbnail rendering on page view. If this is false, a valid
- * thumbnail URL is still output, but no file will be created at
- * the target location. This may save some time if you have a
- * thumb.php or 404 handler set up which is faster than the regular
- * webserver(s).
+ * Allow thumbnail rendering on page view. If this is false, a valid
+ * thumbnail URL is still output, but no file will be created at
+ * the target location. This may save some time if you have a
+ * thumb.php or 404 handler set up which is faster than the regular
+ * webserver(s).
*/
$wgGenerateThumbnailOnParse = true;
@@ -1843,11 +1966,29 @@ $wgPutIPinRC = true;
*/
$wgRCMaxAge = 7 * 24 * 3600;
+/**
+ * Filter $wgRCLinkDays by $wgRCMaxAge to avoid showing links for numbers higher than what will be stored.
+ * Note that this is disabled by default because we sometimes do have RC data which is beyond the limit
+ * for some reason, and some users may use the high numbers to display that data which is still there.
+ */
+$wgRCFilterByAge = false;
+
+/**
+ * List of Days and Limits options to list in the Special:Recentchanges and Special:Recentchangeslinked pages.
+ */
+$wgRCLinkLimits = array( 50, 100, 250, 500 );
+$wgRCLinkDays = array( 1, 3, 7, 14, 30 );
# Send RC updates via UDP
$wgRC2UDPAddress = false;
$wgRC2UDPPort = false;
$wgRC2UDPPrefix = '';
+$wgRC2UDPOmitBots = false;
+
+# Enable user search in Special:Newpages
+# This is really a temporary hack around an index install bug on some Wikipedias.
+# Kill it once fixed.
+$wgEnableNewpagesUserFilter = true;
#
# Copyright and credits settings
@@ -1987,7 +2128,7 @@ $wgTidyInternal = extension_loaded( 'tidy' );
$wgDebugTidy = false;
/**
- * Validate the overall output using tidy and refuse
+ * Validate the overall output using tidy and refuse
* to display the page if it's not valid.
*/
$wgValidateAllHtml = false;
@@ -2002,49 +2143,65 @@ $wgDefaultSkin = 'monobook';
* $wgDefaultUserOptions ['editsection'] = 0;
*
*/
-$wgDefaultUserOptions = array(
- 'quickbar' => 1,
- 'underline' => 2,
- 'cols' => 80,
- 'rows' => 25,
- 'searchlimit' => 20,
- 'contextlines' => 5,
- 'contextchars' => 50,
- 'skin' => false,
- 'math' => 1,
- 'rcdays' => 7,
- 'rclimit' => 50,
- 'wllimit' => 250,
- 'highlightbroken' => 1,
- 'stubthreshold' => 0,
- 'previewontop' => 1,
- 'editsection' => 1,
- 'editsectiononrightclick'=> 0,
- 'showtoc' => 1,
- 'showtoolbar' => 1,
- 'date' => 'default',
- 'imagesize' => 2,
- 'thumbsize' => 2,
- 'rememberpassword' => 0,
- 'enotifwatchlistpages' => 0,
- 'enotifusertalkpages' => 1,
- 'enotifminoredits' => 0,
- 'enotifrevealaddr' => 0,
- 'shownumberswatching' => 1,
- 'fancysig' => 0,
- 'externaleditor' => 0,
- 'externaldiff' => 0,
- 'showjumplinks' => 1,
- 'numberheadings' => 0,
- 'uselivepreview' => 0,
- 'watchlistdays' => 3.0,
+$wgDefaultUserOptions = array(
+ 'quickbar' => 1,
+ 'underline' => 2,
+ 'cols' => 80,
+ 'rows' => 25,
+ 'searchlimit' => 20,
+ 'contextlines' => 5,
+ 'contextchars' => 50,
+ 'disablesuggest' => 0,
+ 'ajaxsearch' => 0,
+ 'skin' => false,
+ 'math' => 1,
+ 'usenewrc' => 0,
+ 'rcdays' => 7,
+ 'rclimit' => 50,
+ 'wllimit' => 250,
+ 'hideminor' => 0,
+ 'highlightbroken' => 1,
+ 'stubthreshold' => 0,
+ 'previewontop' => 1,
+ 'previewonfirst' => 0,
+ 'editsection' => 1,
+ 'editsectiononrightclick' => 0,
+ 'editondblclick' => 0,
+ 'editwidth' => 0,
+ 'showtoc' => 1,
+ 'showtoolbar' => 1,
+ 'minordefault' => 0,
+ 'date' => 'default',
+ 'imagesize' => 2,
+ 'thumbsize' => 2,
+ 'rememberpassword' => 0,
+ 'enotifwatchlistpages' => 0,
+ 'enotifusertalkpages' => 1,
+ 'enotifminoredits' => 0,
+ 'enotifrevealaddr' => 0,
+ 'shownumberswatching' => 1,
+ 'fancysig' => 0,
+ 'externaleditor' => 0,
+ 'externaldiff' => 0,
+ 'showjumplinks' => 1,
+ 'numberheadings' => 0,
+ 'uselivepreview' => 0,
+ 'watchlistdays' => 3.0,
+ 'extendwatchlist' => 0,
+ 'watchlisthideminor' => 0,
+ 'watchlisthidebots' => 0,
+ 'watchlisthideown' => 0,
+ 'watchcreations' => 0,
+ 'watchdefault' => 0,
+ 'watchmoves' => 0,
+ 'watchdeletion' => 0,
);
/** Whether or not to allow and use real name fields. Defaults to true. */
$wgAllowRealName = true;
/*****************************************************************************
- * Extensions
+ * Extensions
*/
/**
@@ -2053,7 +2210,7 @@ $wgAllowRealName = true;
$wgExtensionFunctions = array();
/**
- * Extension functions for initialisation of skins. This is called somewhat earlier
+ * Extension functions for initialisation of skins. This is called somewhat earlier
* than $wgExtensionFunctions.
*/
$wgSkinExtensionFunctions = array();
@@ -2064,20 +2221,32 @@ $wgSkinExtensionFunctions = array();
* The file must create a variable called $messages.
* When the messages are needed, the extension should call wfLoadExtensionMessages().
*
- * Example:
+ * Example:
* $wgExtensionMessagesFiles['ConfirmEdit'] = dirname(__FILE__).'/ConfirmEdit.i18n.php';
*
*/
$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';
+ */
+$wgExtensionAliasesFiles = array();
+
+/**
* Parser output hooks.
* This is an associative array where the key is an extension-defined tag
* (typically the extension name), and the value is a PHP callback.
* These will be called as an OutputPageParserOutput hook, if the relevant
* tag has been registered with the parser output object.
*
- * Registration is done with $pout->addOutputHook( $tag, $data ).
+ * Registration is done with $pout->addOutputHook( $tag, $data ).
*
* The callback has the form:
* function outputHook( $outputPage, $parserOutput, $data ) { ... }
@@ -2087,7 +2256,7 @@ $wgParserOutputHooks = array();
/**
* List of valid skin names.
* The key should be the name in all lower case, the value should be a display name.
- * The default skins will be added later, by Skin::getSkinNames(). Use
+ * The default skins will be added later, by Skin::getSkinNames(). Use
* Skin::getSkinNames() as an accessor if you wish to have access to the full list.
*/
$wgValidSkinNames = array();
@@ -2096,7 +2265,7 @@ $wgValidSkinNames = array();
* Special page list.
* See the top of SpecialPage.php for documentation.
*/
-$wgSpecialPages = array();
+$wgSpecialPages = array();
/**
* Array mapping class names to filenames, for autoloading.
@@ -2111,7 +2280,8 @@ $wgAutoloadClasses = array();
* <code>
* $wgExtensionCredits[$type][] = array(
* 'name' => 'Example extension',
- * 'version' => 1.9,
+ * 'version' => 1.9,
+ * 'svn-revision' => '$LastChangedRevision: 39340 $',
* 'author' => 'Foo Barstein',
* 'url' => 'http://wwww.example.com/Example%20Extension/',
* 'description' => 'An example extension',
@@ -2161,9 +2331,12 @@ $wgExternalDiffEngine = false;
/** Use RC Patrolling to check for vandalism */
$wgUseRCPatrol = true;
-/** Use new page patrolling to check new pages on special:Newpages */
+/** Use new page patrolling to check new pages on Special:Newpages */
$wgUseNPPatrol = true;
+/** Provide syndication feeds (RSS, Atom) for, e.g., Recentchanges, Newpages */
+$wgFeed = true;
+
/** Set maximum number of results to return in syndication feeds (RSS, Atom) for
* eg Recentchanges, Newpages. */
$wgFeedLimit = 50;
@@ -2203,14 +2376,14 @@ $wgExtraNamespaces = NULL;
/**
* Namespace aliases
- * These are alternate names for the primary localised namespace names, which
- * are defined by $wgExtraNamespaces and the language file. If a page is
- * requested with such a prefix, the request will be redirected to the primary
- * name.
+ * These are alternate names for the primary localised namespace names, which
+ * are defined by $wgExtraNamespaces and the language file. If a page is
+ * requested with such a prefix, the request will be redirected to the primary
+ * name.
*
* Set this to a map from namespace names to IDs.
* Example:
- * $wgNamespaceAliases = array(
+ * $wgNamespaceAliases = array(
* 'Wikipedian' => NS_USER,
* 'Help' => 100,
* );
@@ -2274,16 +2447,16 @@ $wgBrowserBlackList = array(
* Netscape 2-4 detection
* The minor version may contain strings such as "Gold" or "SGoldC-SGI"
* Lots of non-netscape user agents have "compatible", so it's useful to check for that
- * with a negative assertion. The [UIN] identifier specifies the level of security
- * in a Netscape/Mozilla browser, checking for it rules out a number of fakers.
+ * with a negative assertion. The [UIN] identifier specifies the level of security
+ * in a Netscape/Mozilla browser, checking for it rules out a number of fakers.
* The language string is unreliable, it is missing on NS4 Mac.
- *
+ *
* Reference: http://www.psychedelix.com/agents/index.shtml
*/
'/^Mozilla\/2\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
'/^Mozilla\/3\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
'/^Mozilla\/4\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
-
+
/**
* MSIE on Mac OS 9 is teh sux0r, converts þ to <thorn>, ð to <eth>, Þ to <THORN> and Ð to <ETH>
*
@@ -2297,7 +2470,7 @@ $wgBrowserBlackList = array(
* @link http://en.wikipedia.org/wiki/Template%3AOS9
*/
'/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
-
+
/**
* Google wireless transcoder, seems to eat a lot of chars alive
* http://it.wikipedia.org/w/index.php?title=Luciano_Ligabue&diff=prev&oldid=8857361
@@ -2391,6 +2564,17 @@ $wgLogTypes = array( '',
'import',
'patrol',
'merge',
+ 'suppress',
+);
+
+/**
+ * This restricts log access to those who have a certain right
+ * 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
+ */
+$wgLogRestrictions = array(
+ 'suppress' => 'suppressionlog'
);
/**
@@ -2410,6 +2594,7 @@ $wgLogNames = array(
'import' => 'importlogpage',
'patrol' => 'patrol-log-page',
'merge' => 'mergelog',
+ 'suppress' => 'suppressionlog',
);
/**
@@ -2429,6 +2614,7 @@ $wgLogHeaders = array(
'import' => 'importlogpagetext',
'patrol' => 'patrol-log-header',
'merge' => 'mergelogpagetext',
+ 'suppress' => 'suppressionlogtext',
);
/**
@@ -2447,6 +2633,7 @@ $wgLogActions = array(
'delete/delete' => 'deletedarticle',
'delete/restore' => 'undeletedarticle',
'delete/revision' => 'revdelete-logentry',
+ 'delete/event' => 'logdelete-logentry',
'upload/upload' => 'uploadedimage',
'upload/overwrite' => 'overwroteimage',
'upload/revert' => 'uploadedimage',
@@ -2455,6 +2642,113 @@ $wgLogActions = array(
'import/upload' => 'import-logentry-upload',
'import/interwiki' => 'import-logentry-interwiki',
'merge/merge' => 'pagemerge-logentry',
+ 'suppress/revision' => 'revdelete-logentry',
+ 'suppress/file' => 'revdelete-logentry',
+ 'suppress/event' => 'logdelete-logentry',
+ 'suppress/delete' => 'suppressedarticle',
+ 'suppress/block' => 'blocklogentry',
+);
+
+/**
+ * The same as above, but here values are names of functions,
+ * not messages
+ */
+$wgLogActionsHandlers = array();
+
+/**
+ * List of special pages, followed by what subtitle they should go under
+ * at Special:SpecialPages
+ */
+$wgSpecialPageGroups = array(
+ 'DoubleRedirects' => 'maintenance',
+ 'BrokenRedirects' => 'maintenance',
+ 'Lonelypages' => 'maintenance',
+ 'Uncategorizedpages' => 'maintenance',
+ 'Uncategorizedcategories' => 'maintenance',
+ 'Uncategorizedimages' => 'maintenance',
+ 'Uncategorizedtemplates' => 'maintenance',
+ 'Unusedcategories' => 'maintenance',
+ 'Unusedimages' => 'maintenance',
+ 'Protectedpages' => 'maintenance',
+ 'Protectedtitles' => 'maintenance',
+ 'Unusedtemplates' => 'maintenance',
+ 'Withoutinterwiki' => 'maintenance',
+ 'Longpages' => 'maintenance',
+ 'Shortpages' => 'maintenance',
+ 'Ancientpages' => 'maintenance',
+ 'Deadendpages' => 'maintenance',
+ 'Wantedpages' => 'maintenance',
+ 'Wantedcategories' => 'maintenance',
+ 'Unwatchedpages' => 'maintenance',
+ 'Fewestrevisions' => 'maintenance',
+
+ 'Userlogin' => 'login',
+ 'Userlogout' => 'login',
+ 'CreateAccount' => 'login',
+
+ 'Recentchanges' => 'changes',
+ 'Recentchangeslinked' => 'changes',
+ 'Watchlist' => 'changes',
+ 'Newimages' => 'changes',
+ 'Newpages' => 'changes',
+ 'Log' => 'changes',
+
+ 'Upload' => 'media',
+ 'Imagelist' => 'media',
+ 'MIMEsearch' => 'media',
+ 'FileDuplicateSearch' => 'media',
+ 'Filepath' => 'media',
+
+ 'Listusers' => 'users',
+ 'Listgrouprights' => 'users',
+ 'Ipblocklist' => 'users',
+ 'Contributions' => 'users',
+ 'Emailuser' => 'users',
+ 'Listadmins' => 'users',
+ 'Listbots' => 'users',
+ 'Userrights' => 'users',
+ 'Blockip' => 'users',
+ 'Preferences' => 'users',
+ 'Resetpass' => 'users',
+
+ 'Mostlinked' => 'highuse',
+ 'Mostlinkedcategories' => 'highuse',
+ 'Mostlinkedtemplates' => 'highuse',
+ 'Mostcategories' => 'highuse',
+ 'Mostimages' => 'highuse',
+ 'Mostrevisions' => 'highuse',
+
+ 'Allpages' => 'pages',
+ 'Prefixindex' => 'pages',
+ 'Listredirects' => 'pages',
+ 'Categories' => 'pages',
+ 'Disambiguations' => 'pages',
+
+ 'Randompage' => 'redirects',
+ 'Randomredirect' => 'redirects',
+ 'Mypage' => 'redirects',
+ 'Mytalk' => 'redirects',
+ 'Mycontributions' => 'redirects',
+ 'Search' => 'redirects',
+
+ 'Movepage' => 'pagetools',
+ 'MergeHistory' => 'pagetools',
+ 'Revisiondelete' => 'pagetools',
+ 'Undelete' => 'pagetools',
+ 'Export' => 'pagetools',
+ 'Import' => 'pagetools',
+ 'Whatlinkshere' => 'pagetools',
+
+ 'Statistics' => 'wiki',
+ 'Version' => 'wiki',
+ 'Lockdb' => 'wiki',
+ 'Unlockdb' => 'wiki',
+ 'Allmessages' => 'wiki',
+ 'Popularpages' => 'wiki',
+
+ 'Specialpages' => 'other',
+ 'Blockme' => 'other',
+ 'Booksources' => 'other',
);
/**
@@ -2517,7 +2811,7 @@ $wgNamespaceRobotPolicies = array();
/**
* Robot policies per article.
* These override the per-namespace robot policies.
- * Must be in the form of an array where the key part is a properly
+ * Must be in the form of an array where the key part is a properly
* canonicalised text form title and the value is a robot policy.
* Example:
* $wgArticleRobotPolicies = array( 'Main Page' => 'noindex' );
@@ -2611,8 +2905,14 @@ $wgRateLimitLog = null;
/**
* Array of groups which should never trigger the rate limiter
+ *
+ * @deprecated as of 1.13.0, the preferred method is using
+ * $wgGroupPermissions[]['noratelimit']. However, this will still
+ * work if desired.
+ *
+ * $wgRateLimitsExcludedGroups = array( 'sysop', 'bureaucrat' );
*/
-$wgRateLimitsExcludedGroups = array( 'sysop', 'bureaucrat' );
+$wgRateLimitsExcludedGroups = array();
/**
* On Special:Unusedimages, consider images "used", if they are put
@@ -2634,6 +2934,7 @@ $wgExternalStores = false;
/**
* An array of external mysql servers, e.g.
* $wgExternalServers = array( 'cluster1' => array( 'srv28', 'srv29', 'srv30' ) );
+ * Used by LBFactory_Simple, may be ignored if $wgLBFactoryConf is set to another class.
*/
$wgExternalServers = array();
@@ -2641,7 +2942,7 @@ $wgExternalServers = array();
* The place to put new revisions, false to put them in the local text table.
* Part of a URL, e.g. DB://cluster1
*
- * Can be an array instead of a single string, to enable data distribution. Keys
+ * Can be an array instead of a single string, to enable data distribution. Keys
* must be consecutive integers, starting at zero. Example:
*
* $wgDefaultExternalStore = array( 'DB://cluster1', 'DB://cluster2' );
@@ -2658,15 +2959,15 @@ $wgDefaultExternalStore = false;
$wgRevisionCacheExpiry = 0;
/**
-* list of trusted media-types and mime types.
-* Use the MEDIATYPE_xxx constants to represent media types.
-* This list is used by Image::isSafeFile
-*
-* Types not listed here will have a warning about unsafe content
-* displayed on the images description page. It would also be possible
-* to use this for further restrictions, like disabling direct
-* [[media:...]] links for non-trusted formats.
-*/
+ * list of trusted media-types and mime types.
+ * Use the MEDIATYPE_xxx constants to represent media types.
+ * This list is used by Image::isSafeFile
+ *
+ * Types not listed here will have a warning about unsafe content
+ * displayed on the images description page. It would also be possible
+ * to use this for further restrictions, like disabling direct
+ * [[media:...]] links for non-trusted formats.
+ */
$wgTrustedMediaFormats= array(
MEDIATYPE_BITMAP, //all bitmap formats
MEDIATYPE_AUDIO, //all audio formats
@@ -2735,14 +3036,14 @@ $wgUpdateRowsPerQuery = 10;
$wgUseAjax = true;
/**
- * Enable auto suggestion for the search bar
+ * Enable auto suggestion for the search bar
* Requires $wgUseAjax to be true too.
* Causes wfSajaxSearch to be added to $wgAjaxExportList
*/
$wgAjaxSearch = false;
/**
- * List of Ajax-callable functions.
+ * List of Ajax-callable functions.
* Extensions acting as Ajax callbacks must register here
*/
$wgAjaxExportList = array( );
@@ -2778,6 +3079,7 @@ $wgReservedUsernames = array(
'Conversion script', // Used for the old Wikipedia software upgrade
'Maintenance script', // Maintenance scripts which perform editing, image import script
'Template namespace initialisation script', // Used in 1.2->1.3 upgrade
+ 'msg:double-redirect-fixer', // Automatic double redirect fix
);
/**
@@ -2798,7 +3100,7 @@ $wgAllowTitlesInSVG = false;
$wgContentNamespaces = array( NS_MAIN );
/**
- * Maximum amount of virtual memory available to shell processes under linux, in KB.
+ * Maximum amount of virtual memory available to shell processes under linux, in KB.
*/
$wgMaxShellMemory = 102400;
@@ -2825,7 +3127,7 @@ $wgDjvuRenderer = null;
/**
* Path of the djvutoxml executable
- * This works like djvudump except much, much slower as of version 3.5.
+ * This works like djvudump except much, much slower as of version 3.5.
*
* For now I recommend you use djvudump instead. The djvuxml output is
* probably more stable, so we'll switch back to it as soon as they fix
@@ -2868,6 +3170,15 @@ $wgEnableWriteAPI = false;
* Extension modules may override the core modules.
*/
$wgAPIModules = array();
+$wgAPIMetaModules = array();
+$wgAPIPropModules = array();
+$wgAPIListModules = array();
+
+/**
+ * Maximum amount of rows to scan in a DB query in the API
+ * The default value is generally fine
+ */
+$wgAPIMaxDBRows = 5000;
/**
* Parser test suite files to be run by parserTests.php when no specific
@@ -2884,28 +3195,23 @@ $wgParserTestFiles = array(
/**
* Break out of framesets. This can be used to prevent external sites from
- * framing your site with ads.
+ * framing your site with ads.
*/
$wgBreakFrames = false;
/**
- * Set this to an array of special page names to prevent
+ * Set this to an array of special page names to prevent
* maintenance/updateSpecialPages.php from updating those pages.
*/
$wgDisableQueryPageUpdate = false;
/**
- * Set this to false to disable cascading protection
- */
-$wgEnableCascadingProtection = true;
-
-/**
* Disable output compression (enabled by default if zlib is available)
*/
$wgDisableOutputCompression = false;
/**
- * If lag is higher than $wgSlaveLagWarning, show a warning in some special
+ * If lag is higher than $wgSlaveLagWarning, show a warning in some special
* pages (like watchlist). If the lag is higher than $wgSlaveLagCritical,
* show a more obvious warning.
*/
@@ -2915,25 +3221,76 @@ $wgSlaveLagCritical = 30;
/**
* Parser configuration. Associative array with the following members:
*
- * class The class name
- *
- * The entire associative array will be passed through to the constructor as
- * the first parameter. Note that only Setup.php can use this variable --
- * the configuration will change at runtime via $wgParser member functions, so
- * the contents of this variable will be out-of-date. The variable can only be
- * changed during LocalSettings.php, in particular, it can't be changed during
- * an extension setup function.
+ * class The class name
+ *
+ * preprocessorClass The preprocessor class. Two classes are currently available:
+ * Preprocessor_Hash, which uses plain PHP arrays for tempoarary
+ * storage, and Preprocessor_DOM, which uses the DOM module for
+ * temporary storage. Preprocessor_DOM generally uses less memory;
+ * the speed of the two is roughly the same.
+ *
+ * If this parameter is not given, it uses Preprocessor_DOM if the
+ * DOM module is available, otherwise it uses Preprocessor_Hash.
+ *
+ * Has no effect on Parser_OldPP.
+ *
+ * The entire associative array will be passed through to the constructor as
+ * the first parameter. Note that only Setup.php can use this variable --
+ * the configuration will change at runtime via $wgParser member functions, so
+ * the contents of this variable will be out-of-date. The variable can only be
+ * changed during LocalSettings.php, in particular, it can't be changed during
+ * an extension setup function.
*/
-$wgParserConf = array(
+$wgParserConf = array(
'class' => 'Parser',
+ #'preprocessorClass' => 'Preprocessor_Hash',
);
/**
- * Hooks that are used for outputting exceptions
- * Format is:
- * $wgExceptionHooks[] = $funcname
+ * Hooks that are used for outputting exceptions. Format is:
+ * $wgExceptionHooks[] = $funcname
* or:
- * $wgExceptionHooks[] = array( $class, $funcname )
+ * $wgExceptionHooks[] = array( $class, $funcname )
* Hooks should return strings or false
*/
$wgExceptionHooks = array();
+
+/**
+ * Page property link table invalidation lists. Should only be set by exten-
+ * sions.
+ */
+$wgPagePropLinkInvalidations = array(
+ 'hiddencat' => 'categorylinks',
+);
+
+/**
+ * Maximum number of links to a redirect page listed on
+ * Special:Whatlinkshere/RedirectDestination
+ */
+$wgMaxRedirectLinksRetrieved = 500;
+
+/**
+ * Maximum number of calls per parse to expensive parser functions such as
+ * PAGESINCATEGORY.
+ */
+$wgExpensiveParserFunctionLimit = 100;
+
+/**
+ * Maximum number of pages to move at once when moving subpages with a page.
+ */
+$wgMaximumMovedPages = 100;
+
+/**
+ * Array of namespaces to generate a sitemap for when the
+ * maintenance/generateSitemap.php script is run, or false if one is to be ge-
+ * nerated for all namespaces.
+ */
+$wgSitemapNamespaces = false;
+
+
+/**
+ * If user doesn't specify any edit summary when making a an edit, MediaWiki
+ * will try to automatically create one. This feature can be disabled by set-
+ * ting this variable false.
+ */
+$wgUseAutomaticEditSummaries = true;
diff --git a/includes/Defines.php b/includes/Defines.php
index 2d6aee5f..98cee57d 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -1,6 +1,7 @@
<?php
/**
* A few constants that might be needed during LocalSettings.php
+ * @file
*/
/**
@@ -84,30 +85,6 @@ define( 'MW_MATH_MODERN', 4 );
define( 'MW_MATH_MATHML', 5 );
/**#@-*/
-/**
- * User rights list
- * @deprecated
- */
-$wgAvailableRights = array(
- 'block',
- 'bot',
- 'createaccount',
- 'delete',
- 'edit',
- 'editinterface',
- 'import',
- 'importupload',
- 'move',
- 'patrol',
- 'protect',
- 'read',
- 'rollback',
- 'siteadmin',
- 'unwatchedpages',
- 'upload',
- 'userrights',
-);
-
/**#@+
* Cache type
*/
@@ -188,15 +165,15 @@ define( 'RC_MOVE_OVER_REDIRECT', 4);
*/
define( 'EDIT_NEW', 1 );
define( 'EDIT_UPDATE', 2 );
-define( 'EDIT_MINOR', 4 );
+define( 'EDIT_MINOR', 4 );
define( 'EDIT_SUPPRESS_RC', 8 );
define( 'EDIT_FORCE_BOT', 16 );
define( 'EDIT_DEFER_UPDATES', 32 );
define( 'EDIT_AUTOSUMMARY', 64 );
/**#@-*/
-/**
- * Flags for Database::makeList()
+/**
+ * Flags for Database::makeList()
* These are also available as Database class constants
*/
define( 'LIST_COMMA', 0 );
@@ -208,57 +185,7 @@ define( 'LIST_OR', 4);
/**
* Unicode and normalisation related
*/
-define( 'UNICODE_HANGUL_FIRST', 0xac00 );
-define( 'UNICODE_HANGUL_LAST', 0xd7a3 );
-
-define( 'UNICODE_HANGUL_LBASE', 0x1100 );
-define( 'UNICODE_HANGUL_VBASE', 0x1161 );
-define( 'UNICODE_HANGUL_TBASE', 0x11a7 );
-
-define( 'UNICODE_HANGUL_LCOUNT', 19 );
-define( 'UNICODE_HANGUL_VCOUNT', 21 );
-define( 'UNICODE_HANGUL_TCOUNT', 28 );
-define( 'UNICODE_HANGUL_NCOUNT', UNICODE_HANGUL_VCOUNT * UNICODE_HANGUL_TCOUNT );
-
-define( 'UNICODE_HANGUL_LEND', UNICODE_HANGUL_LBASE + UNICODE_HANGUL_LCOUNT - 1 );
-define( 'UNICODE_HANGUL_VEND', UNICODE_HANGUL_VBASE + UNICODE_HANGUL_VCOUNT - 1 );
-define( 'UNICODE_HANGUL_TEND', UNICODE_HANGUL_TBASE + UNICODE_HANGUL_TCOUNT - 1 );
-
-define( 'UNICODE_SURROGATE_FIRST', 0xd800 );
-define( 'UNICODE_SURROGATE_LAST', 0xdfff );
-define( 'UNICODE_MAX', 0x10ffff );
-define( 'UNICODE_REPLACEMENT', 0xfffd );
-
-
-define( 'UTF8_HANGUL_FIRST', "\xea\xb0\x80" /*codepointToUtf8( UNICODE_HANGUL_FIRST )*/ );
-define( 'UTF8_HANGUL_LAST', "\xed\x9e\xa3" /*codepointToUtf8( UNICODE_HANGUL_LAST )*/ );
-
-define( 'UTF8_HANGUL_LBASE', "\xe1\x84\x80" /*codepointToUtf8( UNICODE_HANGUL_LBASE )*/ );
-define( 'UTF8_HANGUL_VBASE', "\xe1\x85\xa1" /*codepointToUtf8( UNICODE_HANGUL_VBASE )*/ );
-define( 'UTF8_HANGUL_TBASE', "\xe1\x86\xa7" /*codepointToUtf8( UNICODE_HANGUL_TBASE )*/ );
-
-define( 'UTF8_HANGUL_LEND', "\xe1\x84\x92" /*codepointToUtf8( UNICODE_HANGUL_LEND )*/ );
-define( 'UTF8_HANGUL_VEND', "\xe1\x85\xb5" /*codepointToUtf8( UNICODE_HANGUL_VEND )*/ );
-define( 'UTF8_HANGUL_TEND', "\xe1\x87\x82" /*codepointToUtf8( UNICODE_HANGUL_TEND )*/ );
-
-define( 'UTF8_SURROGATE_FIRST', "\xed\xa0\x80" /*codepointToUtf8( UNICODE_SURROGATE_FIRST )*/ );
-define( 'UTF8_SURROGATE_LAST', "\xed\xbf\xbf" /*codepointToUtf8( UNICODE_SURROGATE_LAST )*/ );
-define( 'UTF8_MAX', "\xf4\x8f\xbf\xbf" /*codepointToUtf8( UNICODE_MAX )*/ );
-define( 'UTF8_REPLACEMENT', "\xef\xbf\xbd" /*codepointToUtf8( UNICODE_REPLACEMENT )*/ );
-#define( 'UTF8_REPLACEMENT', '!' );
-
-define( 'UTF8_OVERLONG_A', "\xc1\xbf" );
-define( 'UTF8_OVERLONG_B', "\xe0\x9f\xbf" );
-define( 'UTF8_OVERLONG_C', "\xf0\x8f\xbf\xbf" );
-
-# These two ranges are illegal
-define( 'UTF8_FDD0', "\xef\xb7\x90" /*codepointToUtf8( 0xfdd0 )*/ );
-define( 'UTF8_FDEF', "\xef\xb7\xaf" /*codepointToUtf8( 0xfdef )*/ );
-define( 'UTF8_FFFE', "\xef\xbf\xbe" /*codepointToUtf8( 0xfffe )*/ );
-define( 'UTF8_FFFF', "\xef\xbf\xbf" /*codepointToUtf8( 0xffff )*/ );
-
-define( 'UTF8_HEAD', false );
-define( 'UTF8_TAIL', true );
+require_once dirname(__FILE__).'/normal/UtfNormalDefines.php';
# Hook support constants
define( 'MW_SUPPORTS_EDITFILTERMERGED', 1 );
diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php
index 9aa17bbb..0b4028cb 100644
--- a/includes/DifferenceEngine.php
+++ b/includes/DifferenceEngine.php
@@ -1,8 +1,6 @@
<?php
/**
- * See diff.doc
- * @todo indicate where diff.doc can be found.
- * @addtogroup DifferenceEngine
+ * @defgroup DifferenceEngine DifferenceEngine
*/
/**
@@ -15,8 +13,7 @@ define( 'MW_DIFF_VERSION', '1.11a' );
/**
* @todo document
- * @public
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class DifferenceEngine {
/**#@+
@@ -40,7 +37,7 @@ class DifferenceEngine {
* @param $rcid Integer: ??? FIXME (default 0)
* @param $refreshCache boolean If set, refreshes the diff cache
*/
- function DifferenceEngine( $titleObj = null, $old = 0, $new = 0, $rcid = 0, $refreshCache = false ) {
+ function __construct( $titleObj = null, $old = 0, $new = 0, $rcid = 0, $refreshCache = false ) {
$this->mTitle = $titleObj;
wfDebug("DifferenceEngine old '$old' new '$new' rcid '$rcid'\n");
@@ -72,10 +69,13 @@ class DifferenceEngine {
$this->mRefreshCache = $refreshCache;
}
+ function getTitle() {
+ return $this->mTitle;
+ }
+
function showDiffPage( $diffOnly = false ) {
global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol;
- $fname = 'DifferenceEngine::showDiffPage';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
# If external diffs are enabled both globally and for the user,
# we'll use the application/x-external-editor interface to call
@@ -108,13 +108,14 @@ CONTROL;
$wgOut->setArticleFlag( false );
if ( ! $this->loadRevisionData() ) {
- $t = $this->mTitle->getPrefixedText() . " (Diff: {$this->mOldid}, {$this->mNewid})";
+ $t = $this->mTitle->getPrefixedText();
+ $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), $this->mOldid, $this->mNewid );
$wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) );
- $wgOut->addWikiMsg( 'missingarticle', "<nowiki>$t</nowiki>" );
- wfProfileOut( $fname );
+ $wgOut->addWikiMsg( 'missing-article', "<nowiki>$t</nowiki>", $d );
+ wfProfileOut( __METHOD__ );
return;
}
-
+
wfRunHooks( 'DiffViewHeader', array( $this, $this->mOldRev, $this->mNewRev ) );
if ( $this->mNewRev->isCurrent() ) {
@@ -127,7 +128,7 @@ CONTROL;
if ( $this->mOldid === false ) {
$this->showFirstRevision();
$this->renderNewRevision(); // should we respect $diffOnly here or not?
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return;
}
@@ -146,18 +147,20 @@ CONTROL;
if ( !( $this->mOldPage->userCanRead() && $this->mNewPage->userCanRead() ) ) {
$wgOut->loginToUse();
$wgOut->output();
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
exit;
}
$sk = $wgUser->getSkin();
- if ( $this->mNewRev->isCurrent() && $wgUser->isAllowed('rollback') ) {
+ // Check if page is editable
+ $editable = $this->mNewRev->getTitle()->userCan( 'edit' );
+ if ( $editable && $this->mNewRev->isCurrent() && $wgUser->isAllowed( 'rollback' ) ) {
$rollback = '&nbsp;&nbsp;&nbsp;' . $sk->generateRollback( $this->mNewRev );
} else {
$rollback = '';
}
-
+
// Prepare a change patrol link, if applicable
if( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) ) {
// If we've been given an explicit change identifier, use it; saves time
@@ -186,11 +189,11 @@ CONTROL;
}
// Build the link
if( $rcid ) {
- $patrol = ' [' . $sk->makeKnownLinkObj(
+ $patrol = ' <span class="patrollink">[' . $sk->makeKnownLinkObj(
$this->mTitle,
wfMsgHtml( 'markaspatrolleddiff' ),
"action=markpatrolled&rcid={$rcid}"
- ) . ']';
+ ) . ']</span>';
} else {
$patrol = '';
}
@@ -211,21 +214,19 @@ CONTROL;
$newminor = '';
if ($this->mOldRev->mMinorEdit == 1) {
- $oldminor = wfElement( 'span', array( 'class' => 'minor' ),
- wfMsg( 'minoreditletter') ) . ' ';
+ $oldminor = Xml::span( wfMsg( 'minoreditletter'), 'minor' ) . ' ';
}
if ($this->mNewRev->mMinorEdit == 1) {
- $newminor = wfElement( 'span', array( 'class' => 'minor' ),
- wfMsg( 'minoreditletter') ) . ' ';
+ $newminor = Xml::span( wfMsg( 'minoreditletter'), 'minor' ) . ' ';
}
-
+
$rdel = ''; $ldel = '';
if( $wgUser->isAllowed( 'deleterevision' ) ) {
$revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
if( !$this->mOldRev->userCan( Revision::DELETED_RESTRICTED ) ) {
// If revision was hidden from sysops
- $ldel = wfMsgHtml('rev-delundel');
+ $ldel = wfMsgHtml('rev-delundel');
} else {
$ldel = $sk->makeKnownLinkObj( $revdel,
wfMsgHtml('rev-delundel'),
@@ -239,10 +240,10 @@ CONTROL;
// We don't currently handle well changing the top revision's settings
if( $this->mNewRev->isCurrent() ) {
// If revision was hidden from sysops
- $rdel = wfMsgHtml('rev-delundel');
+ $rdel = wfMsgHtml('rev-delundel');
} else if( !$this->mNewRev->userCan( Revision::DELETED_RESTRICTED ) ) {
// If revision was hidden from sysops
- $rdel = wfMsgHtml('rev-delundel');
+ $rdel = wfMsgHtml('rev-delundel');
} else {
$rdel = $sk->makeKnownLinkObj( $revdel,
wfMsgHtml('rev-delundel'),
@@ -269,7 +270,7 @@ CONTROL;
if ( !$diffOnly )
$this->renderNewRevision();
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
/**
@@ -277,8 +278,7 @@ CONTROL;
*/
function renderNewRevision() {
global $wgOut;
- $fname = 'DifferenceEngine::renderNewRevision';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
#add deleted rev tag if needed
@@ -316,7 +316,7 @@ CONTROL;
$wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
/**
@@ -325,18 +325,16 @@ CONTROL;
*/
function showFirstRevision() {
global $wgOut, $wgUser;
-
- $fname = 'DifferenceEngine::showFirstRevision';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
# Get article text from the DB
#
if ( ! $this->loadNewText() ) {
- $t = $this->mTitle->getPrefixedText() . " (Diff: {$this->mOldid}, " .
- "{$this->mNewid})";
+ $t = $this->mTitle->getPrefixedText();
+ $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), $this->mOldid, $this->mNewid );
$wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) );
- $wgOut->addWikiMsg( 'missingarticle', "<nowiki>$t</nowiki>" );
- wfProfileOut( $fname );
+ $wgOut->addWikiMsg( 'missing-article', "<nowiki>$t</nowiki>", $d );
+ wfProfileOut( __METHOD__ );
return;
}
if ( $this->mNewRev->isCurrent() ) {
@@ -348,7 +346,7 @@ CONTROL;
if ( !( $this->mTitle->userCanRead() ) ) {
$wgOut->loginToUse();
$wgOut->output();
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
exit;
}
@@ -367,7 +365,7 @@ CONTROL;
$wgOut->setSubtitle( wfMsg( 'difference' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
/**
@@ -378,7 +376,7 @@ CONTROL;
global $wgOut;
$diff = $this->getDiff( $otitle, $ntitle );
if ( $diff === false ) {
- $wgOut->addWikiMsg( 'missingarticle', "<nowiki>(fixme, bug)</nowiki>" );
+ $wgOut->addWikiMsg( 'missing-article', "<nowiki>(fixme, bug)</nowiki>", '' );
return false;
} else {
$this->showDiffStyle();
@@ -386,14 +384,14 @@ CONTROL;
return true;
}
}
-
+
/**
* Add style sheets and supporting JS for diff display.
*/
function showDiffStyle() {
global $wgStylePath, $wgStyleVersion, $wgOut;
$wgOut->addStyle( 'common/diff.css' );
-
+
// JS is needed to detect old versions of Mozilla to work around an annoyance bug.
$wgOut->addScript( "<script type=\"text/javascript\" src=\"$wgStylePath/common/diff.js?$wgStyleVersion\"></script>" );
}
@@ -422,9 +420,13 @@ CONTROL;
*/
function getDiffBody() {
global $wgMemc;
- $fname = 'DifferenceEngine::getDiffBody';
- wfProfileIn( $fname );
-
+ wfProfileIn( __METHOD__ );
+ // Check if the diff should be hidden from this user
+ if ( $this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
+ return '';
+ } else if ( $this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
+ return '';
+ }
// Cacheable?
$key = false;
if ( $this->mOldid && $this->mNewid ) {
@@ -436,7 +438,7 @@ CONTROL;
wfIncrStats( 'diff_cache_hit' );
$difftext = $this->localiseLineNumbers( $difftext );
$difftext .= "\n<!-- diff cache key $key -->\n";
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $difftext;
}
} // don't try to load but save the result
@@ -444,23 +446,14 @@ CONTROL;
// Loadtext is permission safe, this just clears out the diff
if ( !$this->loadText() ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
- } else if ( $this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
- return '';
- } else if ( $this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
- return '';
}
$difftext = $this->generateDiffBody( $this->mOldtext, $this->mNewtext );
-
+
// Save to cache for 7 days
- // Only do this for public revs, otherwise an admin can view the diff and a non-admin can nab it!
- if ( $this->mOldRev && $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
- wfIncrStats( 'diff_uncacheable' );
- } else if ( $this->mNewRev && $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
- wfIncrStats( 'diff_uncacheable' );
- } else if ( $key !== false && $difftext !== false ) {
+ if ( $key !== false && $difftext !== false ) {
wfIncrStats( 'diff_cache_miss' );
$wgMemc->set( $key, $difftext, 7*86400 );
} else {
@@ -470,7 +463,7 @@ CONTROL;
if ( $difftext !== false ) {
$difftext = $this->localiseLineNumbers( $difftext );
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $difftext;
}
@@ -480,11 +473,10 @@ CONTROL;
*/
function generateDiffBody( $otext, $ntext ) {
global $wgExternalDiffEngine, $wgContLang;
- $fname = 'DifferenceEngine::generateDiffBody';
$otext = str_replace( "\r\n", "\n", $otext );
$ntext = str_replace( "\r\n", "\n", $ntext );
-
+
if ( $wgExternalDiffEngine == 'wikidiff' ) {
# For historical reasons, external diff engine expects
# input text to be HTML-escaped already
@@ -493,20 +485,22 @@ CONTROL;
if( !function_exists( 'wikidiff_do_diff' ) ) {
dl('php_wikidiff.so');
}
- return $wgContLang->unsegementForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) );
+ return $wgContLang->unsegementForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) ) .
+ $this->debug( 'wikidiff1' );
}
-
+
if ( $wgExternalDiffEngine == 'wikidiff2' ) {
# Better external diff engine, the 2 may some day be dropped
# This one does the escaping and segmenting itself
if ( !function_exists( 'wikidiff2_do_diff' ) ) {
- wfProfileIn( "$fname-dl" );
+ wfProfileIn( __METHOD__ . "-dl" );
@dl('php_wikidiff2.so');
- wfProfileOut( "$fname-dl" );
+ wfProfileOut( __METHOD__ . "-dl" );
}
if ( function_exists( 'wikidiff2_do_diff' ) ) {
wfProfileIn( 'wikidiff2_do_diff' );
$text = wikidiff2_do_diff( $otext, $ntext, 2 );
+ $text .= $this->debug( 'wikidiff2' );
wfProfileOut( 'wikidiff2_do_diff' );
return $text;
}
@@ -519,12 +513,12 @@ CONTROL;
$tempFile1 = fopen( $tempName1, "w" );
if ( !$tempFile1 ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
}
$tempFile2 = fopen( $tempName2, "w" );
if ( !$tempFile2 ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
}
fwrite( $tempFile1, $otext );
@@ -532,22 +526,42 @@ CONTROL;
fclose( $tempFile1 );
fclose( $tempFile2 );
$cmd = wfEscapeShellArg( $wgExternalDiffEngine, $tempName1, $tempName2 );
- wfProfileIn( "$fname-shellexec" );
+ wfProfileIn( __METHOD__ . "-shellexec" );
$difftext = wfShellExec( $cmd );
- wfProfileOut( "$fname-shellexec" );
+ $difftext .= $this->debug( "external $wgExternalDiffEngine" );
+ wfProfileOut( __METHOD__ . "-shellexec" );
unlink( $tempName1 );
unlink( $tempName2 );
return $difftext;
}
-
+
# Native PHP diff
$ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
$nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
$diffs = new Diff( $ota, $nta );
$formatter = new TableDiffFormatter();
- return $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) );
+ return $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) ) .
+ $this->debug();
+ }
+
+ /**
+ * Generate a debug comment indicating diff generating time,
+ * server node, and generator backend.
+ */
+ protected function debug( $generator="internal" ) {
+ global $wgShowHostnames, $wgNodeName;
+ $data = array( $generator );
+ if( $wgShowHostnames ) {
+ $data[] = $wgNodeName;
+ }
+ $data[] = wfTimestamp( TS_DB );
+ return "<!-- diff generator: " .
+ implode( " ",
+ array_map(
+ "htmlspecialchars",
+ $data ) ) .
+ " -->\n";
}
-
/**
* Replace line numbers with the text in the user's language
@@ -562,19 +576,19 @@ CONTROL;
return wfMsgExt( 'lineno', array('parseinline'), $wgLang->formatNum( $matches[1] ) );
}
-
+
/**
* If there are revisions between the ones being compared, return a note saying so.
*/
function getMultiNotice() {
if ( !is_object($this->mOldRev) || !is_object($this->mNewRev) )
return '';
-
+
if( !$this->mOldPage->equals( $this->mNewPage ) ) {
// Comparing two different pages? Count would be meaningless.
return '';
}
-
+
$oldid = $this->mOldRev->getId();
$newid = $this->mNewRev->getId();
if ( $oldid > $newid ) {
@@ -601,7 +615,7 @@ CONTROL;
<col class='diff-content' />
<col class='diff-marker' />
<col class='diff-content' />
- <tr>
+ <tr valign='top'>
<td colspan='2' class='diff-otitle'>{$otitle}</td>
<td colspan='2' class='diff-ntitle'>{$ntitle}</td>
</tr>
@@ -647,28 +661,31 @@ CONTROL;
: Revision::newFromTitle( $this->mTitle );
if( !$this->mNewRev instanceof Revision )
return false;
-
+
// Update the new revision ID in case it was 0 (makes life easier doing UI stuff)
$this->mNewid = $this->mNewRev->getId();
-
+
+ // Check if page is editable
+ $editable = $this->mNewRev->getTitle()->userCan( 'edit' );
+
// Set assorted variables
$timestamp = $wgLang->timeanddate( $this->mNewRev->getTimestamp(), true );
$this->mNewPage = $this->mNewRev->getTitle();
if( $this->mNewRev->isCurrent() ) {
- $newLink = $this->mNewPage->escapeLocalUrl();
+ $newLink = $this->mNewPage->escapeLocalUrl( 'oldid=' . $this->mNewid );
$this->mPagetitle = htmlspecialchars( wfMsg( 'currentrev' ) );
$newEdit = $this->mNewPage->escapeLocalUrl( 'action=edit' );
- $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a> ($timestamp)"
- . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
+ $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a> ($timestamp)";
+ $this->mNewtitle .= " (<a href='$newEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
} else {
$newLink = $this->mNewPage->escapeLocalUrl( 'oldid=' . $this->mNewid );
$newEdit = $this->mNewPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mNewid );
$this->mPagetitle = wfMsgHTML( 'revisionasof', $timestamp );
- $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>"
- . " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
+ $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>";
+ $this->mNewtitle .= " (<a href='$newEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
}
if ( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
$this->mNewtitle = "<span class='history-deleted'>{$this->mPagetitle}</span>";
@@ -703,18 +720,19 @@ CONTROL;
$oldLink = $this->mOldPage->escapeLocalUrl( 'oldid=' . $this->mOldid );
$oldEdit = $this->mOldPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mOldid );
$this->mOldPagetitle = htmlspecialchars( wfMsg( 'revisionasof', $t ) );
-
+
$this->mOldtitle = "<a href='$oldLink'>{$this->mOldPagetitle}</a>"
- . " (<a href='$oldEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
+ . " (<a href='$oldEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
// Add an "undo" link
$newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undoafter=' . $this->mOldid . '&undo=' . $this->mNewid);
- if ( $this->mNewRev->userCan(Revision::DELETED_TEXT) )
+ if( $editable && !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
$this->mNewtitle .= " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
-
- if ( !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
- $this->mOldtitle = "<span class='history-deleted'>{$this->mOldPagetitle}</span>";
- } else if ( $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
- $this->mOldtitle = '<span class="history-deleted">'.$this->mOldtitle.'</span>';
+ }
+
+ if( !$this->mOldRev->userCan( Revision::DELETED_TEXT ) ) {
+ $this->mOldtitle = '<span class="history-deleted">' . $this->mOldPagetitle . '</span>';
+ } else if( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) {
+ $this->mOldtitle = '<span class="history-deleted">' . $this->mOldtitle . '</span>';
}
}
@@ -780,7 +798,7 @@ define('USE_ASSERTS', function_exists('assert'));
/**
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class _DiffOp {
var $type;
@@ -803,7 +821,7 @@ class _DiffOp {
/**
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class _DiffOp_Copy extends _DiffOp {
var $type = 'copy';
@@ -823,7 +841,7 @@ class _DiffOp_Copy extends _DiffOp {
/**
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class _DiffOp_Delete extends _DiffOp {
var $type = 'delete';
@@ -841,7 +859,7 @@ class _DiffOp_Delete extends _DiffOp {
/**
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class _DiffOp_Add extends _DiffOp {
var $type = 'add';
@@ -859,7 +877,7 @@ class _DiffOp_Add extends _DiffOp {
/**
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class _DiffOp_Change extends _DiffOp {
var $type = 'change';
@@ -896,15 +914,13 @@ class _DiffOp_Change extends _DiffOp {
*
* @author Geoffrey T. Dairiki, Tim Starling
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
-class _DiffEngine
-{
+class _DiffEngine {
const MAX_XREF_LENGTH = 10000;
function diff ($from_lines, $to_lines) {
- $fname = '_DiffEngine::diff';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$n_from = sizeof($from_lines);
$n_to = sizeof($to_lines);
@@ -991,7 +1007,7 @@ class _DiffEngine
elseif ($add)
$edits[] = new _DiffOp_Add($add);
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $edits;
}
@@ -1024,8 +1040,7 @@ class _DiffEngine
* of the portions it is going to specify.
*/
function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) {
- $fname = '_DiffEngine::_diag';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$flip = false;
if ($xlim - $xoff > $ylim - $yoff) {
@@ -1051,7 +1066,7 @@ class _DiffEngine
$numer = $xlim - $xoff + $nchunks - 1;
$x = $xoff;
for ($chunk = 0; $chunk < $nchunks; $chunk++) {
- wfProfileIn( "$fname-chunk" );
+ wfProfileIn( __METHOD__ . "-chunk" );
if ($chunk > 0)
for ($i = 0; $i <= $this->lcs; $i++)
$ymids[$i][$chunk-1] = $this->seq[$i];
@@ -1085,7 +1100,7 @@ class _DiffEngine
}
}
}
- wfProfileOut( "$fname-chunk" );
+ wfProfileOut( __METHOD__ . "-chunk" );
}
$seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
@@ -1097,19 +1112,18 @@ class _DiffEngine
}
$seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return array($this->lcs, $seps);
}
function _lcs_pos ($ypos) {
- $fname = '_DiffEngine::_lcs_pos';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$end = $this->lcs;
if ($end == 0 || $ypos > $this->seq[$end]) {
$this->seq[++$this->lcs] = $ypos;
$this->in_seq[$ypos] = 1;
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $this->lcs;
}
@@ -1127,7 +1141,7 @@ class _DiffEngine
$this->in_seq[$this->seq[$end]] = false;
$this->seq[$end] = $ypos;
$this->in_seq[$ypos] = 1;
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $end;
}
@@ -1143,8 +1157,7 @@ class _DiffEngine
* All line numbers are origin-0 and discarded lines are not counted.
*/
function _compareseq ($xoff, $xlim, $yoff, $ylim) {
- $fname = '_DiffEngine::_compareseq';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
// Slide down the bottom initial diagonal.
while ($xoff < $xlim && $yoff < $ylim
@@ -1187,7 +1200,7 @@ class _DiffEngine
$pt1 = $pt2;
}
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
/* Adjust inserts/deletes of identical lines to join changes
@@ -1203,8 +1216,7 @@ class _DiffEngine
* This is extracted verbatim from analyze.c (GNU diffutils-2.7).
*/
function _shift_boundaries ($lines, &$changed, $other_changed) {
- $fname = '_DiffEngine::_shift_boundaries';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$i = 0;
$j = 0;
@@ -1309,7 +1321,7 @@ class _DiffEngine
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
}
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
}
@@ -1317,7 +1329,7 @@ class _DiffEngine
* Class representing a 'diff' between two sequences of strings.
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class Diff
{
@@ -1427,8 +1439,7 @@ class Diff
* This is here only for debugging purposes.
*/
function _check ($from_lines, $to_lines) {
- $fname = 'Diff::_check';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
if (serialize($from_lines) != serialize($this->orig()))
trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
if (serialize($to_lines) != serialize($this->closing()))
@@ -1450,14 +1461,14 @@ class Diff
$lcs = $this->lcs();
trigger_error('Diff okay: LCS = '.$lcs, E_USER_NOTICE);
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
}
/**
* @todo document, bad name.
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class MappedDiff extends Diff
{
@@ -1485,9 +1496,8 @@ class MappedDiff extends Diff
* have the same number of elements as $to_lines.
*/
function MappedDiff($from_lines, $to_lines,
- $mapped_from_lines, $mapped_to_lines) {
- $fname = 'MappedDiff::MappedDiff';
- wfProfileIn( $fname );
+ $mapped_from_lines, $mapped_to_lines) {
+ wfProfileIn( __METHOD__ );
assert(sizeof($from_lines) == sizeof($mapped_from_lines));
assert(sizeof($to_lines) == sizeof($mapped_to_lines));
@@ -1508,7 +1518,7 @@ class MappedDiff extends Diff
$yi += sizeof($closing);
}
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
}
@@ -1520,10 +1530,9 @@ class MappedDiff extends Diff
* to obtain fancier outputs.
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
-class DiffFormatter
-{
+class DiffFormatter {
/**
* Number of leading context "lines" to preserve.
*
@@ -1547,8 +1556,7 @@ class DiffFormatter
* @return string The formatted output.
*/
function format($diff) {
- $fname = 'DiffFormatter::format';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$xi = $yi = 1;
$block = false;
@@ -1602,13 +1610,12 @@ class DiffFormatter
$block);
$end = $this->_end_diff();
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $end;
}
function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) {
- $fname = 'DiffFormatter::_block';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$this->_start_block($this->_block_header($xbeg, $xlen, $ybeg, $ylen));
foreach ($edits as $edit) {
if ($edit->type == 'copy')
@@ -1623,7 +1630,7 @@ class DiffFormatter
trigger_error('Unknown edit type', E_USER_ERROR);
}
$this->_end_block();
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
function _start_diff() {
@@ -1677,14 +1684,13 @@ class DiffFormatter
/**
* A formatter that outputs unified diffs
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
-class UnifiedDiffFormatter extends DiffFormatter
-{
+class UnifiedDiffFormatter extends DiffFormatter {
var $leading_context_lines = 2;
var $trailing_context_lines = 2;
-
+
function _added($lines) {
$this->_lines($lines, '+');
}
@@ -1702,21 +1708,17 @@ class UnifiedDiffFormatter extends DiffFormatter
/**
* A pseudo-formatter that just passes along the Diff::$edits array
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
-class ArrayDiffFormatter extends DiffFormatter
-{
- function format($diff)
- {
+class ArrayDiffFormatter extends DiffFormatter {
+ function format($diff) {
$oldline = 1;
$newline = 1;
$retval = array();
foreach($diff->edits as $edit)
- switch($edit->type)
- {
+ switch($edit->type) {
case 'add':
- foreach($edit->closing as $l)
- {
+ foreach($edit->closing as $l) {
$retval[] = array(
'action' => 'add',
'new'=> $l,
@@ -1725,8 +1727,7 @@ class ArrayDiffFormatter extends DiffFormatter
}
break;
case 'delete':
- foreach($edit->orig as $l)
- {
+ foreach($edit->orig as $l) {
$retval[] = array(
'action' => 'delete',
'old' => $l,
@@ -1735,8 +1736,7 @@ class ArrayDiffFormatter extends DiffFormatter
}
break;
case 'change':
- foreach($edit->orig as $i => $l)
- {
+ foreach($edit->orig as $i => $l) {
$retval[] = array(
'action' => 'change',
'old' => $l,
@@ -1751,7 +1751,7 @@ class ArrayDiffFormatter extends DiffFormatter
$newline += count($edit->orig);
}
return $retval;
- }
+ }
}
/**
@@ -1759,12 +1759,12 @@ class ArrayDiffFormatter extends DiffFormatter
*
*/
-define('NBSP', '&#160;'); // iso-8859-x non-breaking space.
+define('NBSP', '&#160;'); // iso-8859-x non-breaking space.
/**
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
class _HWLDF_WordAccumulator {
function _HWLDF_WordAccumulator () {
@@ -1777,10 +1777,10 @@ class _HWLDF_WordAccumulator {
function _flushGroup ($new_tag) {
if ($this->_group !== '') {
if ($this->_tag == 'ins')
- $this->_line .= '<ins class="diffchange">' .
+ $this->_line .= '<ins class="diffchange diffchange-inline">' .
htmlspecialchars ( $this->_group ) . '</ins>';
elseif ($this->_tag == 'del')
- $this->_line .= '<del class="diffchange">' .
+ $this->_line .= '<del class="diffchange diffchange-inline">' .
htmlspecialchars ( $this->_group ) . '</del>';
else
$this->_line .= htmlspecialchars ( $this->_group );
@@ -1825,27 +1825,24 @@ class _HWLDF_WordAccumulator {
/**
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
-class WordLevelDiff extends MappedDiff
-{
+class WordLevelDiff extends MappedDiff {
const MAX_LINE_LENGTH = 10000;
function WordLevelDiff ($orig_lines, $closing_lines) {
- $fname = 'WordLevelDiff::WordLevelDiff';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
list ($orig_words, $orig_stripped) = $this->_split($orig_lines);
list ($closing_words, $closing_stripped) = $this->_split($closing_lines);
$this->MappedDiff($orig_words, $closing_words,
- $orig_stripped, $closing_stripped);
- wfProfileOut( $fname );
+ $orig_stripped, $closing_stripped);
+ wfProfileOut( __METHOD__ );
}
function _split($lines) {
- $fname = 'WordLevelDiff::_split';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$words = array();
$stripped = array();
@@ -1872,13 +1869,12 @@ class WordLevelDiff extends MappedDiff
}
}
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return array($words, $stripped);
}
function orig () {
- $fname = 'WordLevelDiff::orig';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$orig = new _HWLDF_WordAccumulator;
foreach ($this->edits as $edit) {
@@ -1888,13 +1884,12 @@ class WordLevelDiff extends MappedDiff
$orig->addWords($edit->orig, 'del');
}
$lines = $orig->getLines();
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $lines;
}
function closing () {
- $fname = 'WordLevelDiff::closing';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$closing = new _HWLDF_WordAccumulator;
foreach ($this->edits as $edit) {
@@ -1904,24 +1899,30 @@ class WordLevelDiff extends MappedDiff
$closing->addWords($edit->closing, 'ins');
}
$lines = $closing->getLines();
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return $lines;
}
}
/**
- * Wikipedia Table style diff formatter.
+ * Wikipedia Table style diff formatter.
* @todo document
* @private
- * @addtogroup DifferenceEngine
+ * @ingroup DifferenceEngine
*/
-class TableDiffFormatter extends DiffFormatter
-{
+class TableDiffFormatter extends DiffFormatter {
function TableDiffFormatter() {
$this->leading_context_lines = 2;
$this->trailing_context_lines = 2;
}
+ public static function escapeWhiteSpace( $msg ) {
+ $msg = preg_replace( '/^ /m', '&nbsp; ', $msg );
+ $msg = preg_replace( '/ $/m', ' &nbsp;', $msg );
+ $msg = preg_replace( '/ /', '&nbsp; ', $msg );
+ return $msg;
+ }
+
function _block_header( $xbeg, $xlen, $ybeg, $ylen ) {
$r = '<tr><td colspan="2" class="diff-lineno"><!--LINE '.$xbeg."--></td>\n" .
'<td colspan="2" class="diff-lineno"><!--LINE '.$ybeg."--></td></tr>\n";
@@ -1952,11 +1953,11 @@ class TableDiffFormatter extends DiffFormatter
function contextLine( $line ) {
return $this->wrapLine( ' ', 'diff-context', $line );
}
-
+
private function wrapLine( $marker, $class, $line ) {
if( $line !== '' ) {
// The <div> wrapper is needed for 'overflow: auto' style to scroll properly
- $line = "<div>$line</div>";
+ $line = Xml::tags( 'div', null, $this->escapeWhiteSpace( $line ) );
}
return "<td class='diff-marker'>$marker</td><td class='$class'>$line</td>";
}
@@ -1990,8 +1991,7 @@ class TableDiffFormatter extends DiffFormatter
}
function _changed( $orig, $closing ) {
- $fname = 'TableDiffFormatter::_changed';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
$diff = new WordLevelDiff( $orig, $closing );
$del = $diff->orig();
@@ -2009,9 +2009,6 @@ class TableDiffFormatter extends DiffFormatter
echo '<tr>' . $this->emptyLine() .
$this->addedLine( $line ) . "</tr>\n";
}
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
}
}
-
-
-
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index b48aaffd..8e7caf63 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -29,13 +29,13 @@
* File format docs are available in source package for DjVuLibre:
* http://djvulibre.djvuzone.org/
*
- * @addtogroup Media
+ * @ingroup Media
*/
class DjVuImage {
function __construct( $filename ) {
$this->mFilename = $filename;
}
-
+
/**
* Check if the given file is indeed a valid DjVu image file
* @return bool
@@ -44,27 +44,27 @@ class DjVuImage {
$info = $this->getInfo();
return $info !== false;
}
-
-
+
+
/**
* Return data in the style of getimagesize()
* @return array or false on failure
*/
public function getImageSize() {
$data = $this->getInfo();
-
+
if( $data !== false ) {
$width = $data['width'];
$height = $data['height'];
-
+
return array( $width, $height, 'DjVu',
"width=\"$width\" height=\"$height\"" );
}
return false;
}
-
+
// ---------
-
+
/**
* For debugging; dump the IFF chunk structure
*/
@@ -77,7 +77,7 @@ class DjVuImage {
$this->dumpForm( $file, $chunkLength, 1 );
fclose( $file );
}
-
+
private function dumpForm( $file, $length, $indent ) {
$start = ftell( $file );
$secondary = fread( $file, 4 );
@@ -90,7 +90,7 @@ class DjVuImage {
// FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4chunk/NchunkLength', $chunkHeader ) );
echo str_repeat( ' ', $indent * 4 ) . "$chunk $chunkLength\n";
-
+
if( $chunk == 'FORM' ) {
$this->dumpForm( $file, $chunkLength, $indent + 1 );
} else {
@@ -102,7 +102,7 @@ class DjVuImage {
}
}
}
-
+
function getInfo() {
wfSuppressWarnings();
$file = fopen( $this->mFilename, 'rb' );
@@ -111,16 +111,16 @@ class DjVuImage {
wfDebug( __METHOD__ . ": missing or failed file read\n" );
return false;
}
-
+
$header = fread( $file, 16 );
$info = false;
-
+
if( strlen( $header ) < 16 ) {
wfDebug( __METHOD__ . ": too short file header\n" );
} else {
// FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4magic/a4form/NformLength/a4subtype', $header ) );
-
+
if( $magic != 'AT&T' ) {
wfDebug( __METHOD__ . ": not a DjVu file\n" );
} elseif( $subtype == 'DJVU' ) {
@@ -136,7 +136,7 @@ class DjVuImage {
fclose( $file );
return $info;
}
-
+
private function readChunk( $file ) {
$header = fread( $file, 8 );
if( strlen( $header ) < 8 ) {
@@ -147,16 +147,16 @@ class DjVuImage {
return array( $chunk, $length );
}
}
-
+
private function skipChunk( $file, $chunkLength ) {
fseek( $file, $chunkLength, SEEK_CUR );
-
+
if( $chunkLength & 0x01 == 1 && !feof( $file ) ) {
// padding byte
fseek( $file, 1, SEEK_CUR );
}
}
-
+
private function getMultiPageInfo( $file, $formLength ) {
// For now, we'll just look for the first page in the file
// and report its information, hoping others are the same size.
@@ -166,7 +166,7 @@ class DjVuImage {
if( !$chunk ) {
break;
}
-
+
if( $chunk == 'FORM' ) {
$subtype = fread( $file, 4 );
if( $subtype == 'DJVU' ) {
@@ -179,18 +179,18 @@ class DjVuImage {
$this->skipChunk( $file, $length );
}
} while( $length != 0 && !feof( $file ) && ftell( $file ) - $start < $formLength );
-
+
wfDebug( __METHOD__ . ": multi-page DJVU file contained no pages\n" );
return false;
}
-
+
private function getPageInfo( $file, $formLength ) {
list( $chunk, $length ) = $this->readChunk( $file );
if( $chunk != 'INFO' ) {
wfDebug( __METHOD__ . ": expected INFO chunk, got '$chunk'\n" );
return false;
}
-
+
if( $length < 9 ) {
wfDebug( __METHOD__ . ": INFO should be 9 or 10 bytes, found $length\n" );
return false;
@@ -200,7 +200,7 @@ class DjVuImage {
wfDebug( __METHOD__ . ": INFO chunk cut off\n" );
return false;
}
-
+
// FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack(
'nwidth/' .
@@ -210,7 +210,7 @@ class DjVuImage {
'vresolution/' .
'Cgamma', $data ) );
# Newer files have rotation info in byte 10, but we don't use it yet.
-
+
return array(
'width' => $width,
'height' => $height,
@@ -320,14 +320,14 @@ EOT;
}
if ( preg_match( '/^ *INFO *\[\d*\] *DjVu *(\d+)x(\d+), *\w*, *(\d+) *dpi, *gamma=([0-9.-]+)/', $line, $m ) ) {
- $xml .= Xml::tags( 'OBJECT',
+ $xml .= Xml::tags( 'OBJECT',
array(
#'data' => '',
#'type' => 'image/x.djvu',
'height' => $m[2],
'width' => $m[1],
#'usemap' => '',
- ),
+ ),
"\n" .
Xml::element( 'PARAM', array( 'name' => 'DPI', 'value' => $m[3] ) ) . "\n" .
Xml::element( 'PARAM', array( 'name' => 'GAMMA', 'value' => $m[4] ) ) . "\n"
@@ -340,6 +340,3 @@ EOT;
return false;
}
}
-
-
-?>
diff --git a/includes/DoubleRedirectJob.php b/includes/DoubleRedirectJob.php
new file mode 100644
index 00000000..889beecf
--- /dev/null
+++ b/includes/DoubleRedirectJob.php
@@ -0,0 +1,166 @@
+<?php
+
+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
+ */
+ public static function fixRedirects( $reason, $redirTitle, $destTitle = false ) {
+ # Need to use the master to get the redirect table updated in the same transaction
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->select(
+ array( 'redirect', 'page' ),
+ array( 'page_namespace', 'page_title' ),
+ array(
+ 'page_id = rd_from',
+ 'rd_namespace' => $redirTitle->getNamespace(),
+ 'rd_title' => $redirTitle->getDBkey()
+ ), __METHOD__ );
+ if ( !$res->numRows() ) {
+ return;
+ }
+ $jobs = array();
+ foreach ( $res as $row ) {
+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ if ( !$title ) {
+ continue;
+ }
+
+ $jobs[] = new self( $title, array(
+ 'reason' => $reason,
+ 'redirTitle' => $redirTitle->getPrefixedDBkey() ) );
+ # Avoid excessive memory usage
+ if ( count( $jobs ) > 10000 ) {
+ Job::batchInsert( $jobs );
+ $jobs = array();
+ }
+ }
+ Job::batchInsert( $jobs );
+ }
+ function __construct( $title, $params = false, $id = 0 ) {
+ parent::__construct( 'fixDoubleRedirect', $title, $params, $id );
+ $this->reason = $params['reason'];
+ $this->redirTitle = Title::newFromText( $params['redirTitle'] );
+ $this->destTitleText = !empty( $params['destTitle'] ) ? $params['destTitle'] : '';
+ }
+
+ function run() {
+ if ( !$this->redirTitle ) {
+ $this->setLastError( 'Invalid title' );
+ return false;
+ }
+
+ $targetRev = Revision::newFromTitle( $this->title );
+ if ( !$targetRev ) {
+ wfDebug( __METHOD__.": target redirect already deleted, ignoring\n" );
+ return true;
+ }
+ $text = $targetRev->getText();
+ $currentDest = Title::newFromRedirect( $text );
+ if ( !$currentDest || !$currentDest->equals( $this->redirTitle ) ) {
+ wfDebug( __METHOD__.": Redirect has changed since the job was queued\n" );
+ return true;
+ }
+
+ # Check for a suppression tag (used e.g. in periodically archived discussions)
+ $mw = MagicWord::get( 'staticredirect' );
+ if ( $mw->match( $text ) ) {
+ wfDebug( __METHOD__.": skipping: suppressed with __STATICREDIRECT__\n" );
+ return true;
+ }
+
+ # Find the current final destination
+ $newTitle = self::getFinalDestination( $this->redirTitle );
+ if ( !$newTitle ) {
+ wfDebug( __METHOD__.": skipping: single redirect, circular redirect or invalid redirect destination\n" );
+ return true;
+ }
+ if ( $newTitle->equals( $this->redirTitle ) ) {
+ # The redirect is already right, no need to change it
+ # This can happen if the page was moved back (say after vandalism)
+ wfDebug( __METHOD__.": skipping, already good\n" );
+ }
+
+ # Preserve fragment (bug 14904)
+ $newTitle = Title::makeTitle( $newTitle->getNamespace(), $newTitle->getDBkey(),
+ $currentDest->getFragment() );
+
+ # Fix the text
+ # Remember that redirect pages can have categories, templates, etc.,
+ # so the regex has to be fairly general
+ $newText = preg_replace( '/ \[ \[ [^\]]* \] \] /x',
+ '[[' . $newTitle->getFullText() . ']]',
+ $text, 1 );
+
+ if ( $newText === $text ) {
+ $this->setLastError( 'Text unchanged???' );
+ return false;
+ }
+
+ # Save it
+ global $wgUser;
+ $oldUser = $wgUser;
+ $wgUser = $this->getUser();
+ $article = new Article( $this->title );
+ $reason = wfMsgForContent( 'double-redirect-fixed-' . $this->reason,
+ $this->redirTitle->getPrefixedText(), $newTitle->getPrefixedText() );
+ $article->doEdit( $newText, $reason, EDIT_UPDATE | EDIT_SUPPRESS_RC );
+ $wgUser = $oldUser;
+
+ return true;
+ }
+
+ /**
+ * Get the final destination of a redirect
+ * Returns 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 );
+
+ $seenTitles = array(); # Circular redirect check
+ $dest = false;
+
+ while ( true ) {
+ $titleText = $title->getPrefixedDBkey();
+ if ( isset( $seenTitles[$titleText] ) ) {
+ wfDebug( __METHOD__, "Circular redirect detected, aborting\n" );
+ return false;
+ }
+ $seenTitles[$titleText] = true;
+
+ $row = $dbw->selectRow(
+ array( 'redirect', 'page' ),
+ array( 'rd_namespace', 'rd_title' ),
+ array(
+ 'rd_from=page_id',
+ 'page_namespace' => $title->getNamespace(),
+ 'page_title' => $title->getDBkey()
+ ), __METHOD__ );
+ if ( !$row ) {
+ # No redirect from here, chain terminates
+ break;
+ } else {
+ $dest = $title = Title::makeTitle( $row->rd_namespace, $row->rd_title );
+ }
+ }
+ return $dest;
+ }
+
+ /**
+ * Get a user object for doing edits, from a request-lifetime cache
+ */
+ function getUser() {
+ if ( !self::$user ) {
+ self::$user = User::newFromName( wfMsgForContent( 'double-redirect-fixer' ), false );
+ if ( !self::$user->isLoggedIn() ) {
+ self::$user->addToDatabase();
+ }
+ }
+ return self::$user;
+ }
+}
+
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 8c3a37d4..a34964bc 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -1,6 +1,7 @@
<?php
/**
* Contains the EditPage class
+ * @file
*/
/**
@@ -61,6 +62,7 @@ class EditPage {
var $autoSumm = '';
var $hookError = '';
var $mPreviewTemplates;
+ var $mBaseRevision = false;
# Form values
var $save = false, $preview = false, $diff = false;
@@ -77,10 +79,10 @@ class EditPage {
public $editFormTextAfterWarn;
public $editFormTextAfterTools;
public $editFormTextBottom;
-
+
/* $didSave should be set to true whenever an article was succesfully altered. */
public $didSave = false;
-
+
public $suppressIntro = false;
/**
@@ -99,13 +101,13 @@ class EditPage {
$this->editFormTextAfterTools =
$this->editFormTextBottom = "";
}
-
+
/**
* Fetch initial editing page content.
* @private
*/
function getContent( $def_text = '' ) {
- global $wgOut, $wgRequest, $wgParser;
+ global $wgOut, $wgRequest, $wgParser, $wgMessageCache;
# Get variables from query string :P
$section = $wgRequest->getVal( 'section' );
@@ -118,7 +120,8 @@ class EditPage {
$text = '';
if( !$this->mTitle->exists() ) {
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- # If this is a system message, get the default text.
+ $wgMessageCache->loadAllMessages();
+ # If this is a system message, get the default text.
$text = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
} else {
# If requested, preload some text.
@@ -152,39 +155,43 @@ class EditPage {
$oldrev = $undorev ? $undorev->getPrevious() : null;
}
- #Sanity check, make sure it's the right page.
+ # Sanity check, make sure it's the right page,
+ # the revisions exist and they were not deleted.
# Otherwise, $text will be left as-is.
- if ( !is_null($undorev) && !is_null($oldrev) && $undorev->getPage()==$oldrev->getPage() && $undorev->getPage()==$this->mArticle->getID() ) {
+ if( !is_null( $undorev ) && !is_null( $oldrev ) &&
+ $undorev->getPage() == $oldrev->getPage() &&
+ $undorev->getPage() == $this->mArticle->getID() &&
+ !$undorev->isDeleted( Revision::DELETED_TEXT ) &&
+ !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
$undorev_text = $undorev->getText();
$oldrev_text = $oldrev->getText();
$currev_text = $text;
- #No use doing a merge if it's just a straight revert.
if ( $currev_text != $undorev_text ) {
- $result = wfMerge($undorev_text, $oldrev_text, $currev_text, $text);
+ $result = wfMerge( $undorev_text, $oldrev_text, $currev_text, $text );
} else {
+ # No use doing a merge if it's just a straight revert.
$text = $oldrev_text;
$result = true;
}
+ if( $result ) {
+ # Inform the user of our success and set an automatic edit summary
+ $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-success' ) );
+ $firstrev = $oldrev->getNext();
+ # If we just undid one rev, use an autosummary
+ if( $firstrev->mId == $undo ) {
+ $this->summary = wfMsgForContent('undo-summary', $undo, $undorev->getUserText());
+ }
+ $this->formtype = 'diff';
+ } else {
+ # Warn the user that something went wrong
+ $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-failure' ) );
+ }
} else {
// Failed basic sanity checks.
// Older revisions may have been removed since the link
// was created, or we may simply have got bogus input.
- $result = false;
- }
-
- if( $result ) {
- # Inform the user of our success and set an automatic edit summary
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-success' ) );
- $firstrev = $oldrev->getNext();
- # If we just undid one rev, use an autosummary
- if ( $firstrev->mId == $undo ) {
- $this->summary = wfMsgForContent('undo-summary', $undo, $undorev->getUserText());
- }
- $this->formtype = 'diff';
- } else {
- # Warn the user that something went wrong
- $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-failure' ) );
+ $this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-norev' ) );
}
} else if( $section != '' ) {
if( $section == 'new' ) {
@@ -205,7 +212,7 @@ class EditPage {
* @param $preload String: the title of the page.
* @return string The contents of the page.
*/
- private function getPreloadedText($preload) {
+ protected function getPreloadedText($preload) {
if ( $preload === '' )
return '';
else {
@@ -319,6 +326,25 @@ class EditPage {
$this->mMetaData = $s ;
}
+ protected function wasDeletedSinceLastEdit() {
+ /* 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
+ * deletes.
+ */
+ if ( $this->deletedSinceEdit )
+ return true;
+ if ( $this->mTitle->isDeleted() ) {
+ $this->lastDelete = $this->getLastDelete();
+ if ( !is_null($this->lastDelete) ) {
+ $deletetime = $this->lastDelete->log_timestamp;
+ if ( ($deletetime - $this->starttime) > 0 ) {
+ $this->deletedSinceEdit = true;
+ }
+ }
+ }
+ return $this->deletedSinceEdit;
+ }
+
function submit() {
$this->edit();
}
@@ -355,29 +381,25 @@ class EditPage {
return;
}
+ $wgOut->addScriptFile( 'edit.js' );
+
if( wfReadOnly() ) {
- $wgOut->readOnlyPage( $this->getContent() );
+ $this->readOnlyPage( $this->getContent() );
wfProfileOut( __METHOD__ );
return;
}
$permErrors = $this->mTitle->getUserPermissionsErrors('edit', $wgUser);
+
if( !$this->mTitle->exists() ) {
- # We can't use array_diff here, because that considers ANY TWO
- # ARRAYS TO BE EQUAL. Thanks, PHP.
- $createErrors = $this->mTitle->getUserPermissionsErrors('create', $wgUser);
- foreach( $createErrors as $error ) {
- # in_array() actually *does* work as expected.
- if( !in_array( $error, $permErrors ) ) {
- $permErrors[] = $error;
- }
- }
+ $permErrors = array_merge( $permErrors,
+ wfArrayDiff2( $this->mTitle->getUserPermissionsErrors('create', $wgUser), $permErrors ) );
}
# Ignore some permissions errors.
$remove = array();
foreach( $permErrors as $error ) {
- if ($this->preview || $this->diff &&
+ if ( ( $this->preview || $this->diff ) &&
($error[0] == 'blockedtext' || $error[0] == 'autoblockedtext'))
{
// Don't worry about blocks when previewing/diffing
@@ -393,11 +415,11 @@ class EditPage {
}
}
}
- $permErrors = array_diff( $permErrors, $remove );
-
- if ( !empty($permErrors) ) {
+ $permErrors = wfArrayDiff2( $permErrors, $remove );
+
+ if ( $permErrors ) {
wfDebug( __METHOD__.": User can't edit\n" );
- $wgOut->readOnlyPage( $this->getContent(), true, $permErrors );
+ $this->readOnlyPage( $this->getContent(), true, $permErrors, 'edit' );
wfProfileOut( __METHOD__ );
return;
} else {
@@ -425,31 +447,10 @@ class EditPage {
$this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
$this->isValidCssJsSubpage = $this->mTitle->isValidCssJsSubpage();
- /* Notice that we can't use isDeleted, because it returns true if article is ever deleted
- * no matter it's current state
- */
- $this->deletedSinceEdit = false;
- if ( $this->edittime != '' ) {
- /* Note that we rely on logging table, which hasn't been always there,
- * but that doesn't matter, because this only applies to brand new
- * deletes. This is done on every preview and save request. Move it further down
- * to only perform it on saves
- */
- if ( $this->mTitle->isDeleted() ) {
- $this->lastDelete = $this->getLastDelete();
- if ( !is_null($this->lastDelete) ) {
- $deletetime = $this->lastDelete->log_timestamp;
- if ( ($deletetime - $this->starttime) > 0 ) {
- $this->deletedSinceEdit = true;
- }
- }
- }
- }
-
# Show applicable editing introductions
if( $this->formtype == 'initial' || $this->firsttime )
$this->showIntro();
-
+
if( $this->mTitle->isTalkPage() ) {
$wgOut->addWikiMsg( 'talkpagetext' );
}
@@ -476,7 +477,7 @@ class EditPage {
wfProfileOut( __METHOD__ );
return;
}
- if( !$this->mTitle->getArticleId() )
+ if( !$this->mTitle->getArticleId() )
wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
}
@@ -486,11 +487,28 @@ class EditPage {
}
/**
+ * Show a read-only error
+ * Parameters are the same as OutputPage:readOnlyPage()
+ * Redirect to the article page if redlink=1
+ */
+ function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) {
+ global $wgRequest, $wgOut;
+ if ( $wgRequest->getBool( 'redlink' ) ) {
+ // The edit page was reached via a red link.
+ // Redirect to the article page and let them click the edit tab if
+ // they really want a permission error.
+ $wgOut->redirect( $this->mTitle->getFullUrl() );
+ } else {
+ $wgOut->readOnlyPage( $source, $protected, $reasons, $action );
+ }
+ }
+
+ /**
* Should we show a preview when the edit form is first shown?
*
* @return bool
*/
- private function previewOnOpen() {
+ protected function previewOnOpen() {
global $wgRequest, $wgUser;
if( $wgRequest->getVal( 'preview' ) == 'yes' ) {
// Explicit override from request
@@ -521,15 +539,21 @@ class EditPage {
$fname = 'EditPage::importFormData';
wfProfileIn( $fname );
+ # Section edit can come from either the form or a link
+ $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
+
if( $request->wasPosted() ) {
# These fields need to be checked for encoding.
# 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' ) );
+ $this->mMetaData = rtrim( $request->getText( 'metadata' ) );
# Truncate for whole multibyte characters. +5 bytes for ellipsis
- $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250 );
+ $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250 );
+
+ # Remove extra headings from summaries and new sections.
+ $this->summary = preg_replace('/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->summary);
$this->edittime = $request->getVal( 'wpEdittime' );
$this->starttime = $request->getVal( 'wpStarttime' );
@@ -566,7 +590,7 @@ class EditPage {
$this->preview = true;
}
}
- $this->save = ! ( $this->preview OR $this->diff );
+ $this->save = !$this->preview && !$this->diff;
if( !preg_match( '/^\d{14}$/', $this->edittime )) {
$this->edittime = null;
}
@@ -587,7 +611,7 @@ class EditPage {
$this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' );
}
- $this->autoSumm = $request->getText( 'wpAutoSummary' );
+ $this->autoSumm = $request->getText( 'wpAutoSummary' );
} else {
# Not a posted form? Start with nothing.
wfDebug( "$fname: Not a posted form.\n" );
@@ -604,13 +628,14 @@ class EditPage {
$this->minoredit = false;
$this->watchthis = false;
$this->recreate = false;
+
+ if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
+ $this->summary = $request->getVal( 'preloadtitle' );
+ }
}
$this->oldid = $request->getInt( 'oldid' );
- # Section edit can come from either the form or a link
- $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
-
$this->live = $request->getCheck( 'live' );
$this->editintro = $request->getText( 'editintro' );
@@ -635,7 +660,7 @@ class EditPage {
/**
* Show all applicable editing introductions
*/
- private function showIntro() {
+ protected function showIntro() {
global $wgOut, $wgUser;
if( $this->suppressIntro )
return;
@@ -648,7 +673,7 @@ class EditPage {
$ip = User::isIP( $username );
if ( $id == 0 && !$ip ) {
- $wgOut->wrapWikiMsg( '<div class="mw-userpage-userdoesnotexist error">$1</div>',
+ $wgOut->wrapWikiMsg( '<div class="mw-userpage-userdoesnotexist error">$1</div>',
array( 'userpage-userdoesnotexist', $username ) );
}
}
@@ -668,13 +693,13 @@ class EditPage {
*
* @return bool
*/
- private function showCustomIntro() {
+ protected function showCustomIntro() {
if( $this->editintro ) {
$title = Title::newFromText( $this->editintro );
if( $title instanceof Title && $title->exists() && $title->userCanRead() ) {
global $wgOut;
$revision = Revision::newFromTitle( $title );
- $wgOut->addSecondaryWikiText( $revision->getText() );
+ $wgOut->addWikiTextTitleTidy( $revision->getText(), $this->mTitle );
return true;
} else {
return false;
@@ -721,17 +746,21 @@ class EditPage {
$matches = array();
if ( $wgSpamRegex && preg_match( $wgSpamRegex, $this->textbox1, $matches ) ) {
$result['spam'] = $matches[0];
+ $ip = wfGetIP();
+ $pdbk = $this->mTitle->getPrefixedDBkey();
+ $match = str_replace( "\n", '', $matches[0] );
+ wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" );
wfProfileOut( "$fname-checks" );
wfProfileOut( $fname );
return self::AS_SPAM_ERROR;
}
- if ( $wgFilterCallback && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section ) ) {
+ 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 );
return self::AS_FILTERING;
}
- if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError ) ) ) {
+ 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 );
@@ -783,7 +812,7 @@ class EditPage {
# If the article has been deleted while editing, don't save it without
# confirmation
- if ( $this->deletedSinceEdit && !$this->recreate ) {
+ if ( $this->wasDeletedSinceLastEdit() && !$this->recreate ) {
wfProfileOut( "$fname-checks" );
wfProfileOut( $fname );
return self::AS_ARTICLE_WAS_DELETED;
@@ -809,14 +838,14 @@ class EditPage {
}
// Run post-section-merge edit filter
- if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError ) ) ) {
+ if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError, $this->summary ) ) ) {
# Error messages etc. could be handled within the hook...
wfProfileOut( $fname );
return self::AS_HOOK_ERROR;
}
$isComment = ( $this->section == 'new' );
-
+
$this->mArticle->insertNewArticle( $this->textbox1, $this->summary,
$this->minoredit, $this->watchthis, false, $isComment, $bot);
@@ -847,7 +876,7 @@ class EditPage {
}
}
}
- $userid = $wgUser->getID();
+ $userid = $wgUser->getId();
if ( $this->isConflict) {
wfDebug( "EditPage::editForm conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
@@ -892,15 +921,18 @@ class EditPage {
$oldtext = $this->mArticle->getContent();
// Run post-section-merge edit filter
- if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError ) ) ) {
+ if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError, $this->summary ) ) ) {
# Error messages etc. could be handled within the hook...
wfProfileOut( $fname );
return self::AS_HOOK_ERROR;
}
# Handle the user preference to force summaries here, but not for null edits
- if( $this->section != 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary')
- && 0 != strcmp($oldtext, $text) && !Article::getRedirectAutosummary( $text )) {
+ if( $this->section != 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary') &&
+ 0 != strcmp($oldtext, $text) &&
+ !is_object( Title::newFromRedirect( $text ) ) # check if it's not a redirect
+ ) {
+
if( md5( $this->summary ) == $this->autoSumm ) {
$this->missingSummary = true;
wfProfileOut( $fname );
@@ -908,7 +940,7 @@ class EditPage {
}
}
- #And a similar thing for new sections
+ # And a similar thing for new sections
if( $this->section == 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary' ) ) {
if (trim($this->summary) == '') {
$this->missingSummary = true;
@@ -978,7 +1010,6 @@ class EditPage {
*/
function initialiseForm() {
$this->edittime = $this->mArticle->getTimestamp();
- $this->summary = '';
$this->textbox1 = $this->getContent(false);
if ($this->textbox1 === false) return false;
@@ -997,6 +1028,13 @@ class EditPage {
function showEditForm( $formCallback=null ) {
global $wgOut, $wgUser, $wgLang, $wgContLang, $wgMaxArticleSize, $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))
+ return;
+
$fname = 'EditPage::showEditForm';
wfProfileIn( $fname );
@@ -1034,8 +1072,8 @@ class EditPage {
$matches );
if( !empty( $matches[2] ) ) {
global $wgParser;
- $this->summary = "/* " .
- $wgParser->stripSectionName(trim($matches[2])) .
+ $this->summary = "/* " .
+ $wgParser->stripSectionName(trim($matches[2])) .
" */ ";
}
}
@@ -1066,13 +1104,13 @@ class EditPage {
}
if ( isset( $this->mArticle ) && isset( $this->mArticle->mRevision ) ) {
// Let sysop know that this will make private content public if saved
-
+
if( !$this->mArticle->mRevision->userCan( Revision::DELETED_TEXT ) ) {
$wgOut->addWikiMsg( 'rev-deleted-text-permission' );
} else if( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
$wgOut->addWikiMsg( 'rev-deleted-text-view' );
}
-
+
if( !$this->mArticle->mRevision->isCurrent() ) {
$this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() );
$wgOut->addWikiMsg( 'editingold' );
@@ -1162,17 +1200,17 @@ class EditPage {
global $wgRightsText;
if ( $wgRightsText ) {
- $copywarnMsg = array( 'copyrightwarning',
+ $copywarnMsg = array( 'copyrightwarning',
'[[' . wfMsgForContent( 'copyrightpage' ) . ']]',
$wgRightsText );
} else {
- $copywarnMsg = array( 'copyrightwarning2',
+ $copywarnMsg = array( 'copyrightwarning2',
'[[' . wfMsgForContent( 'copyrightpage' ) . ']]' );
}
if( $wgUser->getOption('showtoolbar') and !$this->isCssJsSubpage ) {
# prepare toolbar for edit buttons
- $toolbar = $this->getEditToolbar();
+ $toolbar = EditPage::getEditToolbar();
} else {
$toolbar = '';
}
@@ -1215,14 +1253,28 @@ class EditPage {
# 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 = htmlspecialchars( $wgContLang->recodeForEdit( $this->summary ) ); # FIXME
+
+ # 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="<span id='wpSummaryLabel'><label for='wpSummary'>{$subject}:</label></span>\n<div class='editOptions'>\n<input tabindex='1' type='text' value=\"$summarytext\" name='wpSummary' id='wpSummary' maxlength='200' size='60' /><br />";
- $editsummary = '';
- $subjectpreview = $summarytext && $this->preview ? "<div class=\"mw-summary-preview\">".wfMsg('subject-preview').':'.$sk->commentBlock( $this->summary, $this->mTitle )."</div>\n" : '';
+ $commentsubject="<span id='wpSummaryLabel'><label for='wpSummary'>{$subject}:</label></span>\n<input tabindex='1' type='text' value=\"$summarytext\" name='wpSummary' id='wpSummary' maxlength='200' size='60' />{$summaryhiddens}<br />";
+ $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 = '';
} else {
$commentsubject = '';
- $editsummary="<span id='wpSummaryLabel'><label for='wpSummary'>{$summary}:</label></span>\n<div class='editOptions'>\n<input tabindex='2' type='text' value=\"$summarytext\" name='wpSummary' id='wpSummary' maxlength='200' size='60' /><br />";
+ $editsummary="<div class='editOptions'>\n<span id='wpSummaryLabel'><label for='wpSummary'>{$summary}:</label></span>\n<input tabindex='2' type='text' value=\"$summarytext\" name='wpSummary' id='wpSummary' maxlength='200' size='60' />{$summaryhiddens}<br />";
$summarypreview = $summarytext && $this->preview ? "<div class=\"mw-summary-preview\">".wfMsg('summary-preview').':'.$sk->commentBlock( $this->summary, $this->mTitle )."</div>\n" : '';
$subjectpreview = '';
}
@@ -1234,6 +1286,9 @@ class EditPage {
$templates = ($this->preview || $this->section != '') ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates();
$formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
+ $hiddencats = $this->mArticle->getHiddenCategories();
+ $formattedhiddencats = $sk->formatHiddenCategories( $hiddencats );
+
global $wgUseMetadataEdit ;
if ( $wgUseMetadataEdit ) {
$metadata = $this->mMetaData ;
@@ -1245,7 +1300,7 @@ class EditPage {
$hidden = '';
$recreate = '';
- if ($this->deletedSinceEdit) {
+ if ($this->wasDeletedSinceLastEdit()) {
if ( 'save' != $this->formtype ) {
$wgOut->addWikiMsg('deletedwhileediting');
} else {
@@ -1292,18 +1347,24 @@ END
<input type='hidden' value=\"{$this->edittime}\" name=\"wpEdittime\" />\n
<input type='hidden' value=\"{$this->scrolltop}\" name=\"wpScrolltop\" id=\"wpScrolltop\" />\n" );
+ $encodedtext = htmlspecialchars( $this->safeUnicodeOutput( $this->textbox1 ) );
+ if( $encodedtext !== '' ) {
+ // Ensure there's a newline at the end, otherwise adding lines
+ // is awkward.
+ // But don't add a newline if the ext is empty, or Firefox in XHTML
+ // mode will show an extra newline. A bit annoying.
+ $encodedtext .= "\n";
+ }
+
$wgOut->addHTML( <<<END
$recreate
{$commentsubject}
{$subjectpreview}
{$this->editFormTextBeforeContent}
<textarea tabindex='1' accesskey="," name="wpTextbox1" id="wpTextbox1" rows='{$rows}'
-cols='{$cols}'{$ew} $hidden>
+cols='{$cols}'{$ew} $hidden>{$encodedtext}</textarea>
END
-. htmlspecialchars( $this->safeUnicodeOutput( $this->textbox1 ) ) .
-"
-</textarea>
- " );
+);
$wgOut->wrapWikiMsg( "<div id=\"editpage-copywarn\">\n$1\n</div>", $copywarnMsg );
$wgOut->addHTML( $this->editFormTextAfterWarn );
@@ -1322,18 +1383,6 @@ END
</div><!-- editButtons -->
</div><!-- editOptions -->");
- $wgOut->addHtml( '<div class="mw-editTools">' );
- $wgOut->addWikiMsgArray( 'edittools', array(), array( 'content' ) );
- $wgOut->addHtml( '</div>' );
-
- $wgOut->addHTML( $this->editFormTextAfterTools );
-
- $wgOut->addHTML( "
-<div class='templatesUsed'>
-{$formattedtemplates}
-</div>
-" );
-
/**
* To make it harder for someone to slip a user a page
* which submits an edit form to the wiki without their
@@ -1349,21 +1398,22 @@ END
$token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "\n<input type='hidden' value=\"$token\" name=\"wpEditToken\" />\n" );
+ $wgOut->addHtml( '<div class="mw-editTools">' );
+ $wgOut->addWikiMsgArray( 'edittools', array(), array( 'content' ) );
+ $wgOut->addHtml( '</div>' );
- # If a blank edit summary was previously provided, and the appropriate
- # user preference is active, pass a hidden tag here. This will stop the
- # user being bounced back more than once in the event that a summary
- # is not required.
- if( $this->missingSummary ) {
- $wgOut->addHTML( "<input type=\"hidden\" name=\"wpIgnoreBlankSummary\" value=\"1\" />\n" );
- }
+ $wgOut->addHTML( $this->editFormTextAfterTools );
- # For a bit more sophisticated detection of blank summaries, hash the
- # automatic one and pass that in a hidden field.
- $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
- $wgOut->addHtml( wfHidden( 'wpAutoSummary', $autosumm ) );
+ $wgOut->addHTML( "
+<div class='templatesUsed'>
+{$formattedtemplates}
+</div>
+<div class='hiddencats'>
+{$formattedhiddencats}
+</div>
+");
- if ( $this->isConflict ) {
+ if ( $this->isConflict && wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
$wgOut->wrapWikiMsg( '==$1==', "yourdiff" );
$de = new DifferenceEngine( $this->mTitle );
@@ -1399,7 +1449,7 @@ END
*
* @param string $text The HTML to be output for the preview.
*/
- private function showPreview( $text ) {
+ protected function showPreview( $text ) {
global $wgOut;
$wgOut->addHTML( '<div id="wikiPreview">' );
@@ -1425,10 +1475,8 @@ END
* of the preview button
*/
function doLivePreviewScript() {
- global $wgStylePath, $wgJsMimeType, $wgStyleVersion, $wgOut, $wgTitle;
- $wgOut->addHTML( '<script type="'.$wgJsMimeType.'" src="' .
- htmlspecialchars( "$wgStylePath/common/preview.js?$wgStyleVersion" ) .
- '"></script>' . "\n" );
+ global $wgOut, $wgTitle;
+ $wgOut->addScriptFile( 'preview.js' );
$liveAction = $wgTitle->getLocalUrl( 'action=submit&wpPreview=true&live=true' );
return "return !lpDoPreview(" .
"editform.wpTextbox1.value," .
@@ -1471,7 +1519,7 @@ END
* @todo document
*/
function getPreviewText() {
- global $wgOut, $wgUser, $wgTitle, $wgParser;
+ global $wgOut, $wgUser, $wgTitle, $wgParser, $wgLang, $wgContLang;
$fname = 'EditPage::getPreviewText';
wfProfileIn( $fname );
@@ -1519,21 +1567,40 @@ END
$toparse="== {$this->summary} ==\n\n".$toparse;
}
- if ( $this->mMetaData != "" ) $toparse .= "\n" . $this->mMetaData ;
+ if ( $this->mMetaData != "" ) $toparse .= "\n" . $this->mMetaData;
+
+ // Parse mediawiki messages with correct target language
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ $pos = strrpos( $this->mTitle->getText(), '/' );
+ if ( $pos !== false ) {
+ $code = substr( $this->mTitle->getText(), $pos+1 );
+ switch ($code) {
+ case $wgLang->getCode():
+ $obj = $wgLang; break;
+ case $wgContLang->getCode():
+ $obj = $wgContLang; break;
+ default:
+ $obj = Language::factory( $code );
+ }
+ $parserOptions->setTargetLanguage( $obj );
+ }
+ }
+
+
$parserOptions->setTidy(true);
$parserOptions->enableLimitReport();
- $parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $toparse ) ."\n\n",
+ $parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $toparse ),
$this->mTitle, $parserOptions );
$previewHTML = $parserOutput->getText();
$wgOut->addParserOutputNoText( $parserOutput );
-
+
# ParserOutput might have altered the page title, so reset it
# Also, use the title defined by DISPLAYTITLE magic word when present
if( ( $dt = $parserOutput->getDisplayTitle() ) !== false ) {
$wgOut->setPageTitle( wfMsg( 'editing', $dt ) );
} else {
- $wgOut->setPageTitle( wfMsg( 'editing', $wgTitle->getPrefixedText() ) );
+ $wgOut->setPageTitle( wfMsg( 'editing', $wgTitle->getPrefixedText() ) );
}
foreach ( $parserOutput->getTemplates() as $ns => $template)
@@ -1551,8 +1618,15 @@ END
$previewhead.='<h2>' . htmlspecialchars( wfMsg( 'previewconflict' ) ) . "</h2>\n";
}
+ if( $wgUser->getOption( 'previewontop' ) ) {
+ // Spacer for the edit toolbar
+ $previewfoot = '<p><br /></p>';
+ } else {
+ $previewfoot = '';
+ }
+
wfProfileOut( $fname );
- return $previewhead . $previewHTML;
+ return $previewhead . $previewHTML . $previewfoot;
}
/**
@@ -1578,7 +1652,9 @@ END
$attribs = array( 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $cols, 'rows' => $rows, 'readonly' => 'readonly' );
$wgOut->addHtml( '<hr />' );
$wgOut->addWikiMsg( $first ? 'blockedoriginalsource' : 'blockededitsource', $this->mTitle->getPrefixedText() );
- $wgOut->addHtml( wfOpenElement( 'textarea', $attribs ) . htmlspecialchars( $source ) . wfCloseElement( 'textarea' ) );
+ # 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' ) );
}
}
@@ -1630,7 +1706,7 @@ END
$wgOut->addHtml( '<div id="spamprotected">' );
$wgOut->addWikiMsg( 'spamprotectiontext' );
if ( $match )
- $wgOut->addWikiMsg( 'spamprotectionmatch',wfEscapeWikiText( $match ) );
+ $wgOut->addWikiMsg( 'spamprotectionmatch', wfEscapeWikiText( $match ) );
$wgOut->addHtml( '</div>' );
$wgOut->returnToMain( false, $wgTitle );
@@ -1647,8 +1723,7 @@ END
$db = wfGetDB( DB_MASTER );
// This is the revision the editor started from
- $baseRevision = Revision::loadFromTimestamp(
- $db, $this->mTitle, $this->edittime );
+ $baseRevision = $this->getBaseRevision();
if( is_null( $baseRevision ) ) {
wfProfileOut( $fname );
return false;
@@ -1719,10 +1794,12 @@ 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 style/wikibits.js.
+ * The necessary JavaScript code can be found in skins/common/edit.js.
+ *
+ * @return string
*/
- function getEditToolbar() {
- global $wgStylePath, $wgContLang, $wgJsMimeType;
+ static function getEditToolbar() {
+ global $wgStylePath, $wgContLang, $wgLang, $wgJsMimeType;
/**
* toolarray an array of arrays which each include the filename of
@@ -1736,93 +1813,104 @@ END
* sure these keys are not defined on the edit page.
*/
$toolarray = array(
- array( 'image' => 'button_bold.png',
- 'id' => 'mw-editbutton-bold',
- 'open' => '\'\'\'',
- 'close' => '\'\'\'',
- 'sample'=> wfMsg('bold_sample'),
- 'tip' => wfMsg('bold_tip'),
- 'key' => 'B'
+ array(
+ 'image' => $wgLang->getImageFile('button-bold'),
+ 'id' => 'mw-editbutton-bold',
+ 'open' => '\'\'\'',
+ 'close' => '\'\'\'',
+ 'sample' => wfMsg('bold_sample'),
+ 'tip' => wfMsg('bold_tip'),
+ 'key' => 'B'
),
- array( 'image' => 'button_italic.png',
- 'id' => 'mw-editbutton-italic',
- 'open' => '\'\'',
- 'close' => '\'\'',
- 'sample'=> wfMsg('italic_sample'),
- 'tip' => wfMsg('italic_tip'),
- 'key' => 'I'
+ array(
+ 'image' => $wgLang->getImageFile('button-italic'),
+ 'id' => 'mw-editbutton-italic',
+ 'open' => '\'\'',
+ 'close' => '\'\'',
+ 'sample' => wfMsg('italic_sample'),
+ 'tip' => wfMsg('italic_tip'),
+ 'key' => 'I'
),
- array( 'image' => 'button_link.png',
- 'id' => 'mw-editbutton-link',
- 'open' => '[[',
- 'close' => ']]',
- 'sample'=> wfMsg('link_sample'),
- 'tip' => wfMsg('link_tip'),
- 'key' => 'L'
+ array(
+ 'image' => $wgLang->getImageFile('button-link'),
+ 'id' => 'mw-editbutton-link',
+ 'open' => '[[',
+ 'close' => ']]',
+ 'sample' => wfMsg('link_sample'),
+ 'tip' => wfMsg('link_tip'),
+ 'key' => 'L'
),
- array( 'image' => 'button_extlink.png',
- 'id' => 'mw-editbutton-extlink',
- 'open' => '[',
- 'close' => ']',
- 'sample'=> wfMsg('extlink_sample'),
- 'tip' => wfMsg('extlink_tip'),
- 'key' => 'X'
+ array(
+ 'image' => $wgLang->getImageFile('button-extlink'),
+ 'id' => 'mw-editbutton-extlink',
+ 'open' => '[',
+ 'close' => ']',
+ 'sample' => wfMsg('extlink_sample'),
+ 'tip' => wfMsg('extlink_tip'),
+ 'key' => 'X'
),
- array( 'image' => 'button_headline.png',
- 'id' => 'mw-editbutton-headline',
- 'open' => "\n== ",
- 'close' => " ==\n",
- 'sample'=> wfMsg('headline_sample'),
- 'tip' => wfMsg('headline_tip'),
- 'key' => 'H'
+ array(
+ 'image' => $wgLang->getImageFile('button-headline'),
+ 'id' => 'mw-editbutton-headline',
+ 'open' => "\n== ",
+ 'close' => " ==\n",
+ 'sample' => wfMsg('headline_sample'),
+ 'tip' => wfMsg('headline_tip'),
+ 'key' => 'H'
),
- array( 'image' => 'button_image.png',
- 'id' => 'mw-editbutton-image',
- 'open' => '[['.$wgContLang->getNsText(NS_IMAGE).":",
- 'close' => ']]',
- 'sample'=> wfMsg('image_sample'),
- 'tip' => wfMsg('image_tip'),
- 'key' => 'D'
+ array(
+ 'image' => $wgLang->getImageFile('button-image'),
+ 'id' => 'mw-editbutton-image',
+ 'open' => '[['.$wgContLang->getNsText(NS_IMAGE).':',
+ 'close' => ']]',
+ 'sample' => wfMsg('image_sample'),
+ 'tip' => wfMsg('image_tip'),
+ 'key' => 'D'
),
- array( 'image' => 'button_media.png',
- 'id' => 'mw-editbutton-media',
- 'open' => '[['.$wgContLang->getNsText(NS_MEDIA).':',
- 'close' => ']]',
- 'sample'=> wfMsg('media_sample'),
- 'tip' => wfMsg('media_tip'),
- 'key' => 'M'
+ array(
+ 'image' => $wgLang->getImageFile('button-media'),
+ 'id' => 'mw-editbutton-media',
+ 'open' => '[['.$wgContLang->getNsText(NS_MEDIA).':',
+ 'close' => ']]',
+ 'sample' => wfMsg('media_sample'),
+ 'tip' => wfMsg('media_tip'),
+ 'key' => 'M'
),
- array( 'image' => 'button_math.png',
- 'id' => 'mw-editbutton-math',
- 'open' => "<math>",
- 'close' => "</math>",
- 'sample'=> wfMsg('math_sample'),
- 'tip' => wfMsg('math_tip'),
- 'key' => 'C'
+ array(
+ 'image' => $wgLang->getImageFile('button-math'),
+ 'id' => 'mw-editbutton-math',
+ 'open' => "<math>",
+ 'close' => "</math>",
+ 'sample' => wfMsg('math_sample'),
+ 'tip' => wfMsg('math_tip'),
+ 'key' => 'C'
),
- array( 'image' => 'button_nowiki.png',
- 'id' => 'mw-editbutton-nowiki',
- 'open' => "<nowiki>",
- 'close' => "</nowiki>",
- 'sample'=> wfMsg('nowiki_sample'),
- 'tip' => wfMsg('nowiki_tip'),
- 'key' => 'N'
+ array(
+ 'image' => $wgLang->getImageFile('button-nowiki'),
+ 'id' => 'mw-editbutton-nowiki',
+ 'open' => "<nowiki>",
+ 'close' => "</nowiki>",
+ 'sample' => wfMsg('nowiki_sample'),
+ 'tip' => wfMsg('nowiki_tip'),
+ 'key' => 'N'
),
- array( 'image' => 'button_sig.png',
- 'id' => 'mw-editbutton-signature',
- 'open' => '--~~~~',
- 'close' => '',
- 'sample'=> '',
- 'tip' => wfMsg('sig_tip'),
- 'key' => 'Y'
+ array(
+ 'image' => $wgLang->getImageFile('button-sig'),
+ 'id' => 'mw-editbutton-signature',
+ 'open' => '--~~~~',
+ 'close' => '',
+ 'sample' => '',
+ 'tip' => wfMsg('sig_tip'),
+ 'key' => 'Y'
),
- array( 'image' => 'button_hr.png',
- 'id' => 'mw-editbutton-hr',
- 'open' => "\n----\n",
- 'close' => '',
- 'sample'=> '',
- 'tip' => wfMsg('hr_tip'),
- 'key' => 'R'
+ array(
+ 'image' => $wgLang->getImageFile('button-hr'),
+ 'id' => 'mw-editbutton-hr',
+ 'open' => "\n----\n",
+ 'close' => '',
+ 'sample' => '',
+ 'tip' => wfMsg('hr_tip'),
+ 'key' => 'R'
)
);
$toolbar = "<div id='toolbar'>\n";
@@ -1841,7 +1929,7 @@ END
$sample = $tool['sample'],
$cssId = $tool['id'],
);
-
+
$paramList = implode( ',',
array_map( array( 'Xml', 'encodeJsVar' ), $params ) );
$toolbar.="addButton($paramList);\n";
@@ -1878,7 +1966,7 @@ END
);
$checkboxes['minor'] =
Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
- "&nbsp;<label for='wpMinoredit'".$skin->tooltipAndAccesskey('minoredit').">{$minorLabel}</label>";
+ "&nbsp;<label for='wpMinoredit'".$skin->tooltip('minoredit', 'withaccess').">{$minorLabel}</label>";
}
$watchLabel = wfMsgExt('watchthis', array('parseinline'));
@@ -1891,7 +1979,7 @@ END
);
$checkboxes['watch'] =
Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
- "&nbsp;<label for='wpWatchthis'".$skin->tooltipAndAccesskey('watch').">{$watchLabel}</label>";
+ "&nbsp;<label for='wpWatchthis'".$skin->tooltip('watch', 'withaccess').">{$watchLabel}</label>";
}
return $checkboxes;
}
@@ -1918,7 +2006,7 @@ END
'accesskey' => wfMsg('accesskey-save'),
'title' => wfMsg( 'tooltip-save' ).' ['.wfMsg( 'accesskey-save' ).']',
);
- $buttons['save'] = wfElement('input', $temp, '');
+ $buttons['save'] = Xml::element('input', $temp, '');
++$tabindex; // use the same for preview and live preview
if ( $wgLivePreview && $wgUser->getOption( 'uselivepreview' ) ) {
@@ -1932,7 +2020,7 @@ END
'title' => wfMsg( 'tooltip-preview' ).' ['.wfMsg( 'accesskey-preview' ).']',
'style' => 'display: none;',
);
- $buttons['preview'] = wfElement('input', $temp, '');
+ $buttons['preview'] = Xml::element('input', $temp, '');
$temp = array(
'id' => 'wpLivePreview',
@@ -1944,7 +2032,7 @@ END
'title' => '',
'onclick' => $this->doLivePreviewScript(),
);
- $buttons['live'] = wfElement('input', $temp, '');
+ $buttons['live'] = Xml::element('input', $temp, '');
} else {
$temp = array(
'id' => 'wpPreview',
@@ -1955,7 +2043,7 @@ END
'accesskey' => wfMsg('accesskey-preview'),
'title' => wfMsg( 'tooltip-preview' ).' ['.wfMsg( 'accesskey-preview' ).']',
);
- $buttons['preview'] = wfElement('input', $temp, '');
+ $buttons['preview'] = Xml::element('input', $temp, '');
$buttons['live'] = '';
}
@@ -1968,8 +2056,8 @@ END
'accesskey' => wfMsg('accesskey-diff'),
'title' => wfMsg( 'tooltip-diff' ).' ['.wfMsg( 'accesskey-diff' ).']',
);
- $buttons['diff'] = wfElement('input', $temp, '');
-
+ $buttons['diff'] = Xml::element('input', $temp, '');
+
wfRunHooks( 'EditPageBeforeEditButtons', array( &$this, &$buttons ) );
return $buttons;
}
@@ -2152,28 +2240,25 @@ END
$wgOut->setPageTitle( wfMsg( 'nocreatetitle' ) );
$wgOut->addWikiMsg( 'nocreatetext' );
}
-
+
/**
* If there are rows in the deletion log for this page, show them,
* along with a nice little note for the user
*
* @param OutputPage $out
*/
- private function showDeletionLog( $out ) {
- $title = $this->mTitle;
- $reader = new LogReader(
- new FauxRequest(
- array(
- 'page' => $title->getPrefixedText(),
- 'type' => 'delete',
- )
- )
- );
- if( $reader->hasRows() ) {
+ protected function showDeletionLog( $out ) {
+ global $wgUser;
+ $loglist = new LogEventsList( $wgUser->getSkin(), $out );
+ $pager = new LogPager( $loglist, 'delete', false, $this->mTitle->getPrefixedText() );
+ if( $pager->getNumRows() > 0 ) {
$out->addHtml( '<div id="mw-recreate-deleted-warn">' );
$out->addWikiMsg( 'recreate-deleted-warn' );
- $viewer = new LogViewer( $reader );
- $viewer->showList( $out );
+ $out->addHTML(
+ $loglist->beginLogEventsList() .
+ $pager->getBody() .
+ $loglist->endLogEventsList()
+ );
$out->addHtml( '</div>' );
}
}
@@ -2187,7 +2272,7 @@ END
$resultDetails = false;
$value = $this->internalAttemptSave( $resultDetails, $wgUser->isAllowed('bot') && $wgRequest->getBool('bot', true) );
-
+
if( $value == self::AS_SUCCESS_UPDATE || $value == self::AS_SUCCESS_NEW_ARTICLE ) {
$this->didSave = true;
}
@@ -2237,7 +2322,7 @@ END
case self::AS_NO_CREATE_PERMISSION;
$this->noCreatePermission();
return;
-
+
case self::AS_BLANK_ARTICLE:
$wgOut->redirect( $wgTitle->getFullURL() );
return false;
@@ -2247,4 +2332,15 @@ END
return false;
}
}
+
+ function getBaseRevision() {
+ if ($this->mBaseRevision == false) {
+ $db = wfGetDB( DB_MASTER );
+ $baseRevision = Revision::loadFromTimestamp(
+ $db, $this->mTitle, $this->edittime );
+ return $this->mBaseRevision = $baseRevision;
+ } else {
+ return $this->mBaseRevision;
+ }
+ }
}
diff --git a/includes/EmaillingJob.php b/includes/EmaillingJob.php
index 73d71c56..380c8982 100644
--- a/includes/EmaillingJob.php
+++ b/includes/EmaillingJob.php
@@ -3,6 +3,8 @@
/**
* Old job used for sending single notification emails;
* kept for backwards-compatibility
+ *
+ * @ingroup JobQueue
*/
class EmaillingJob extends Job {
@@ -20,6 +22,5 @@ class EmaillingJob extends Job {
);
return true;
}
-
-}
+}
diff --git a/includes/EnotifNotifyJob.php b/includes/EnotifNotifyJob.php
index 70d1de69..31fcb0d5 100644
--- a/includes/EnotifNotifyJob.php
+++ b/includes/EnotifNotifyJob.php
@@ -2,6 +2,8 @@
/**
* Job for email notification mails
+ *
+ * @ingroup JobQueue
*/
class EnotifNotifyJob extends Job {
@@ -11,16 +13,22 @@ class EnotifNotifyJob extends Job {
function run() {
$enotif = new EmailNotification();
+ // Get the user from ID (rename safe). Anons are 0, so defer to name.
+ if( isset($this->params['editorID']) && $this->params['editorID'] ) {
+ $editor = User::newFromId( $this->params['editorID'] );
+ // B/C, only the name might be given.
+ } else {
+ $editor = User::newFromName( $this->params['editor'], false );
+ }
$enotif->actuallyNotifyOnPageChange(
- User::newFromName( $this->params['editor'], false ),
- $this->title,
- $this->params['timestamp'],
- $this->params['summary'],
- $this->params['minorEdit'],
- $this->params['oldid']
+ $editor,
+ $this->title,
+ $this->params['timestamp'],
+ $this->params['summary'],
+ $this->params['minorEdit'],
+ $this->params['oldid']
);
return true;
}
-
-}
+}
diff --git a/includes/Exception.php b/includes/Exception.php
index 2fd54352..74820204 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -1,24 +1,43 @@
<?php
+/**
+ * @defgroup Exception Exception
+ */
/**
* MediaWiki exception
- * @addtogroup Exception
+ * @ingroup Exception
*/
-class MWException extends Exception
-{
+class MWException extends Exception {
+
+ /**
+ * Should the exception use $wgOut to output the error ?
+ * @return bool
+ */
function useOutputPage() {
- return !empty( $GLOBALS['wgFullyInitialised'] ) &&
- !empty( $GLOBALS['wgArticle'] ) && !empty( $GLOBALS['wgTitle'] );
+ return !empty( $GLOBALS['wgFullyInitialised'] ) &&
+ ( !empty( $GLOBALS['wgArticle'] ) || ( !empty( $GLOBALS['wgOut'] ) && !$GLOBALS['wgOut']->isArticle() ) ) &&
+ !empty( $GLOBALS['wgTitle'] );
}
+ /**
+ * Can the extension use wfMsg() to get i18n messages ?
+ * @return bool
+ */
function useMessageCache() {
global $wgLang;
return is_object( $wgLang );
}
+ /**
+ * Run hook to allow extensions to modify the text of the exception
+ *
+ * @param String $name class name of the exception
+ * @param Array $args arguments to pass to the callback functions
+ * @return mixed string to output or null if any hook has been called
+ */
function runHooks( $name, $args = array() ) {
global $wgExceptionHooks;
- if( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) )
+ if( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) )
return; // Just silently ignore
if( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) )
return;
@@ -36,7 +55,15 @@ class MWException extends Exception
}
}
- /** Get a message from i18n */
+ /**
+ * Get a message from i18n
+ *
+ * @param String $key message name
+ * @param String $fallback default message if the message cache can't be
+ * called by the exception
+ * The function also has other parameters that are arguments for the message
+ * @return String message with arguments replaced
+ */
function msg( $key, $fallback /*[, params...] */ ) {
$args = array_slice( func_get_args(), 2 );
if ( $this->useMessageCache() ) {
@@ -46,11 +73,17 @@ class MWException extends Exception
}
}
- /* If wgShowExceptionDetails, return a HTML message with a backtrace to the error. */
+ /**
+ * If $wgShowExceptionDetails is true, return a HTML message with a
+ * backtrace to the error, otherwise show a message to ask to set it to true
+ * to show that information.
+ *
+ * @return String html to output
+ */
function getHTML() {
global $wgShowExceptionDetails;
if( $wgShowExceptionDetails ) {
- return '<p>' . htmlspecialchars( $this->getMessage() ) .
+ return '<p>' . htmlspecialchars( $this->getMessage() ) .
'</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
"</p>\n";
} else {
@@ -60,15 +93,18 @@ class MWException extends Exception
}
}
- /* If wgShowExceptionDetails, return a text message with a backtrace to the error */
+ /**
+ * If $wgShowExceptionDetails is true, return a text message with a
+ * backtrace to the error.
+ */
function getText() {
global $wgShowExceptionDetails;
if( $wgShowExceptionDetails ) {
return $this->getMessage() .
"\nBacktrace:\n" . $this->getTraceAsString() . "\n";
} else {
- return "<p>Set <tt>\$wgShowExceptionDetails = true;</tt> " .
- "in LocalSettings.php to show detailed debugging information.</p>";
+ return "Set \$wgShowExceptionDetails = true; " .
+ "in LocalSettings.php to show detailed debugging information.\n";
}
}
@@ -82,8 +118,11 @@ class MWException extends Exception
}
}
- /** Return the requested URL and point to file and line number from which the
+ /**
+ * Return the requested URL and point to file and line number from which the
* exception occured
+ *
+ * @return string
*/
function getLogMessage() {
global $wgRequest;
@@ -119,22 +158,27 @@ class MWException extends Exception
}
}
- /* Output a report about the exception and takes care of formatting.
+ /**
+ * Output a report about the exception and takes care of formatting.
* It will be either HTML or plain text based on $wgCommandLineMode.
*/
function report() {
global $wgCommandLineMode;
+ $log = $this->getLogMessage();
+ if ( $log ) {
+ wfDebugLog( 'exception', $log );
+ }
if ( $wgCommandLineMode ) {
fwrite( STDERR, $this->getText() );
} else {
- $log = $this->getLogMessage();
- if ( $log ) {
- wfDebugLog( 'exception', $log );
- }
$this->reportHTML();
}
}
+ /**
+ * Send headers and output the beginning of the html page if not using
+ * $wgOut to output the exception.
+ */
function htmlHeader() {
global $wgLogo, $wgSitename, $wgOutputEncoding;
@@ -155,6 +199,9 @@ class MWException extends Exception
";
}
+ /**
+ * print the end of the html page if not using $wgOut.
+ */
function htmlFooter() {
echo "</body></html>";
}
@@ -163,7 +210,7 @@ class MWException extends Exception
/**
* Exception class which takes an HTML error message, and does not
* produce a backtrace. Replacement for OutputPage::fatalError().
- * @addtogroup Exception
+ * @ingroup Exception
*/
class FatalError extends MWException {
function getHTML() {
@@ -176,11 +223,11 @@ class FatalError extends MWException {
}
/**
- * @addtogroup Exception
+ * @ingroup Exception
*/
class ErrorPageError extends MWException {
public $title, $msg;
-
+
/**
* Note: these arguments are keys into wfMsg(), not text!
*/
@@ -256,5 +303,3 @@ function wfExceptionHandler( $e ) {
// Exit value should be nonzero for the benefit of shell jobs
exit( 1 );
}
-
-
diff --git a/includes/Exif.php b/includes/Exif.php
index d98a8e0d..bd93eb76 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -1,7 +1,6 @@
<?php
/**
- * @addtogroup Media
- *
+ * @ingroup Media
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
@@ -26,7 +25,7 @@
/**
* @todo document (e.g. one-sentence class-overview description)
- * @addtogroup Media
+ * @ingroup Media
*/
class Exif {
//@{
@@ -431,7 +430,7 @@ class Exif {
if ( is_array( $in ) ) {
return false;
}
-
+
if ( preg_match( "/[^\x0a\x20-\x7e]/", $in ) ) {
$this->debug( $in, __FUNCTION__, 'found a character not in our whitelist' );
return false;
@@ -557,8 +556,8 @@ class Exif {
*
* @private
*
- * @param $in Mixed:
- * @param $fname String:
+ * @param $in Mixed:
+ * @param $fname String:
* @param $action Mixed: , default NULL.
*/
function debug( $in, $fname, $action = NULL ) {
@@ -604,7 +603,7 @@ class Exif {
/**
* @todo document (e.g. one-sentence class-overview description)
- * @addtogroup Media
+ * @ingroup Media
*/
class FormatExif {
/**
@@ -1130,5 +1129,3 @@ define( 'MW_EXIF_RATIONAL', Exif::RATIONAL );
define( 'MW_EXIF_UNDEFINED', Exif::UNDEFINED );
define( 'MW_EXIF_SLONG', Exif::SLONG );
define( 'MW_EXIF_SRATIONAL', Exif::SRATIONAL );
-
-
diff --git a/includes/Export.php b/includes/Export.php
index 69d88fc6..7d0a824e 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -17,15 +17,19 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
+/**
+ * @defgroup Dump Dump
+ */
/**
- *
- * @addtogroup SpecialPage
+ * @ingroup SpecialPage Dump
*/
class WikiExporter {
var $list_authors = false ; # Return distinct author list (when not returning full history)
var $author_list = "" ;
+ var $dumpUploads = false;
+
const FULL = 0;
const CURRENT = 1;
@@ -42,13 +46,13 @@ class WikiExporter {
* make additional queries to pull source data while the
* main query is still running.
*
- * @param Database $db
- * @param mixed $history one of WikiExporter::FULL or WikiExporter::CURRENT, or an
- * associative array:
- * offset: non-inclusive offset at which to start the query
- * limit: maximum number of rows to return
- * dir: "asc" or "desc" timestamp order
- * @param int $buffer one of WikiExporter::BUFFER or WikiExporter::STREAM
+ * @param $db Database
+ * @param $history Mixed: one of WikiExporter::FULL or WikiExporter::CURRENT,
+ * or an associative array:
+ * offset: non-inclusive offset at which to start the query
+ * limit: maximum number of rows to return
+ * dir: "asc" or "desc" timestamp order
+ * @param $buffer Int: one of WikiExporter::BUFFER or WikiExporter::STREAM
*/
function __construct( &$db, $history = WikiExporter::CURRENT,
$buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
@@ -65,7 +69,7 @@ class WikiExporter {
* various row objects and XML output for filtering. Filters
* can be chained or used as callbacks.
*
- * @param mixed $callback
+ * @param $sink mixed
*/
function setOutputSink( &$sink ) {
$this->sink =& $sink;
@@ -93,8 +97,8 @@ class WikiExporter {
/**
* Dumps a series of page and revision records for those pages
* in the database falling within the page_id range given.
- * @param int $start Inclusive lower limit (this id is included)
- * @param int $end Exclusive upper limit (this id is not included)
+ * @param $start Int: inclusive lower limit (this id is included)
+ * @param $end Int: Exclusive upper limit (this id is not included)
* If 0, no upper limit.
*/
function pagesByRange( $start, $end ) {
@@ -106,7 +110,7 @@ class WikiExporter {
}
/**
- * @param Title $title
+ * @param $title Title
*/
function pageByTitle( $title ) {
return $this->dumpFrom(
@@ -141,18 +145,18 @@ class WikiExporter {
$this->author_list = "<contributors>";
//rev_deleted
$nothidden = '(rev_deleted & '.Revision::DELETED_USER.') = 0';
-
+
$sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND $nothidden AND " . $cond ;
$result = $this->db->query( $sql, $fname );
$resultset = $this->db->resultObject( $result );
while( $row = $resultset->fetchObject() ) {
- $this->author_list .= "<contributor>" .
- "<username>" .
- htmlentities( $row->rev_user_text ) .
- "</username>" .
- "<id>" .
+ $this->author_list .= "<contributor>" .
+ "<username>" .
+ htmlentities( $row->rev_user_text ) .
+ "</username>" .
+ "<id>" .
$row->rev_user .
- "</id>" .
+ "</id>" .
"</contributor>";
}
wfProfileOut( $fname );
@@ -253,7 +257,7 @@ class WikiExporter {
* separate database connection not managed by LoadBalancer; some
* blob storage types will make queries to pull source data.
*
- * @param ResultWrapper $resultset
+ * @param $resultset ResultWrapper
* @access private
*/
function outputStream( $resultset ) {
@@ -263,7 +267,11 @@ class WikiExporter {
$last->page_namespace != $row->page_namespace ||
$last->page_title != $row->page_title ) {
if( isset( $last ) ) {
- $output = $this->writer->closePage();
+ $output = '';
+ if( $this->dumpUploads ) {
+ $output .= $this->writer->writeUploads( $last );
+ }
+ $output .= $this->writer->closePage();
$this->sink->writeClosePage( $output );
}
$output = $this->writer->openPage( $row );
@@ -274,7 +282,12 @@ class WikiExporter {
$this->sink->writeRevision( $row, $output );
}
if( isset( $last ) ) {
- $output = $this->author_list . $this->writer->closePage();
+ $output = '';
+ if( $this->dumpUploads ) {
+ $output .= $this->writer->writeUploads( $last );
+ }
+ $output .= $this->author_list;
+ $output .= $this->writer->closePage();
$this->sink->writeClosePage( $output );
}
$resultset->free();
@@ -282,7 +295,7 @@ class WikiExporter {
}
/**
- * @addtogroup Dump
+ * @ingroup Dump
*/
class XmlDumpWriter {
@@ -375,7 +388,7 @@ class XmlDumpWriter {
* Opens a <page> section on the output stream, with data
* from the given database row.
*
- * @param object $row
+ * @param $row object
* @return string
* @access private
*/
@@ -404,7 +417,7 @@ class XmlDumpWriter {
* Dumps a <revision> section on the output stream, with
* data filled in from the given database row.
*
- * @param object $row
+ * @param $row object
* @return string
* @access private
*/
@@ -415,20 +428,12 @@ class XmlDumpWriter {
$out = " <revision>\n";
$out .= " " . wfElement( 'id', null, strval( $row->rev_id ) ) . "\n";
- $ts = wfTimestamp( TS_ISO_8601, $row->rev_timestamp );
- $out .= " " . wfElement( 'timestamp', null, $ts ) . "\n";
+ $out .= $this->writeTimestamp( $row->rev_timestamp );
if( $row->rev_deleted & Revision::DELETED_USER ) {
$out .= " " . wfElement( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
} else {
- $out .= " <contributor>\n";
- if( $row->rev_user ) {
- $out .= " " . wfElementClean( 'username', null, strval( $row->rev_user_text ) ) . "\n";
- $out .= " " . wfElement( 'id', null, strval( $row->rev_user ) ) . "\n";
- } else {
- $out .= " " . wfElementClean( 'ip', null, strval( $row->rev_user_text ) ) . "\n";
- }
- $out .= " </contributor>\n";
+ $out .= $this->writeContributor( $row->rev_user, $row->rev_user_text );
}
if( $row->rev_minor_edit ) {
@@ -461,12 +466,58 @@ class XmlDumpWriter {
return $out;
}
+ function writeTimestamp( $timestamp ) {
+ $ts = wfTimestamp( TS_ISO_8601, $timestamp );
+ return " " . wfElement( 'timestamp', null, $ts ) . "\n";
+ }
+
+ function writeContributor( $id, $text ) {
+ $out = " <contributor>\n";
+ if( $id ) {
+ $out .= " " . wfElementClean( 'username', null, strval( $text ) ) . "\n";
+ $out .= " " . wfElement( 'id', null, strval( $id ) ) . "\n";
+ } else {
+ $out .= " " . wfElementClean( 'ip', null, strval( $text ) ) . "\n";
+ }
+ $out .= " </contributor>\n";
+ return $out;
+ }
+
+ /**
+ * Warning! This data is potentially inconsistent. :(
+ */
+ function writeUploads( $row ) {
+ if( $row->page_namespace == NS_IMAGE ) {
+ $img = wfFindFile( $row->page_title );
+ if( $img ) {
+ $out = '';
+ foreach( array_reverse( $img->getHistory() ) as $ver ) {
+ $out .= $this->writeUpload( $ver );
+ }
+ $out .= $this->writeUpload( $img );
+ return $out;
+ }
+ }
+ return '';
+ }
+
+ function writeUpload( $file ) {
+ return " <upload>\n" .
+ $this->writeTimestamp( $file->getTimestamp() ) .
+ $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
+ " " . wfElementClean( 'comment', null, $file->getDescription() ) . "\n" .
+ " " . wfElement( 'filename', null, $file->getName() ) . "\n" .
+ " " . wfElement( 'src', null, $file->getFullUrl() ) . "\n" .
+ " " . wfElement( 'size', null, $file->getSize() ) . "\n" .
+ " </upload>\n";
+ }
+
}
/**
* Base class for output stream; prints to stdout or buffer or whereever.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpOutput {
function writeOpenStream( $string ) {
@@ -500,7 +551,7 @@ class DumpOutput {
/**
* Stream outputter to send data to a file.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpFileOutput extends DumpOutput {
var $handle;
@@ -518,7 +569,7 @@ class DumpFileOutput extends DumpOutput {
* Stream outputter to send data to a file via some filter program.
* Even if compression is available in a library, using a separate
* program can allow us to make use of a multi-processor system.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpPipeOutput extends DumpFileOutput {
function DumpPipeOutput( $command, $file = null ) {
@@ -531,7 +582,7 @@ class DumpPipeOutput extends DumpFileOutput {
/**
* Sends dump output via the gzip compressor.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpGZipOutput extends DumpPipeOutput {
function DumpGZipOutput( $file ) {
@@ -541,7 +592,7 @@ class DumpGZipOutput extends DumpPipeOutput {
/**
* Sends dump output via the bgzip2 compressor.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpBZip2Output extends DumpPipeOutput {
function DumpBZip2Output( $file ) {
@@ -551,7 +602,7 @@ class DumpBZip2Output extends DumpPipeOutput {
/**
* Sends dump output via the p7zip compressor.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class Dump7ZipOutput extends DumpPipeOutput {
function Dump7ZipOutput( $file ) {
@@ -569,7 +620,7 @@ class Dump7ZipOutput extends DumpPipeOutput {
* Dump output filter class.
* This just does output filtering and streaming; XML formatting is done
* higher up, so be careful in what you do.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpFilter {
function DumpFilter( &$sink ) {
@@ -615,17 +666,17 @@ class DumpFilter {
/**
* Simple dump output filter to exclude all talk pages.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpNotalkFilter extends DumpFilter {
function pass( $page ) {
- return !Namespace::isTalk( $page->page_namespace );
+ return !MWNamespace::isTalk( $page->page_namespace );
}
}
/**
* Dump output filter to include or exclude pages in a given set of namespaces.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpNamespaceFilter extends DumpFilter {
var $invert = false;
@@ -680,7 +731,7 @@ class DumpNamespaceFilter extends DumpFilter {
/**
* Dump output filter to include only the last revision in each page sequence.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpLatestFilter extends DumpFilter {
var $page, $pageString, $rev, $revString;
@@ -712,7 +763,7 @@ class DumpLatestFilter extends DumpFilter {
/**
* Base class for output stream; prints to stdout or buffer or whereever.
- * @addtogroup Dump
+ * @ingroup Dump
*/
class DumpMultiWriter {
function DumpMultiWriter( $sinks ) {
@@ -766,5 +817,3 @@ function xmlsafe( $string ) {
wfProfileOut( $fname );
return $string;
}
-
-
diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php
index f5ce5b9d..1c58f442 100644
--- a/includes/ExternalEdit.php
+++ b/includes/ExternalEdit.php
@@ -6,8 +6,6 @@
*/
/**
- *
- *
* Support for external editors to modify both text and files
* in external applications. It works as follows: MediaWiki
* sends a meta-file with the MIME type 'application/x-external-editor'
@@ -68,4 +66,3 @@ CONTROL;
echo $control;
}
}
-
diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php
index 79937b85..e2b78566 100644
--- a/includes/ExternalStore.php
+++ b/includes/ExternalStore.php
@@ -1,49 +1,54 @@
<?php
/**
+ * @defgroup ExternalStorage ExternalStorage
+ */
+
+/**
* Constructor class for data kept in external repositories
*
* External repositories might be populated by maintenance/async
* scripts, thus partial moving of data may be possible, as well
* as possibility to have any storage format (i.e. for archives)
+ *
+ * @ingroup ExternalStorage
*/
-
class ExternalStore {
/* Fetch data from given URL */
static function fetchFromURL($url) {
global $wgExternalStores;
- if (!$wgExternalStores)
+ if( !$wgExternalStores )
return false;
- @list($proto,$path)=explode('://',$url,2);
+ @list( $proto, $path ) = explode( '://', $url, 2 );
/* Bad URL */
- if ($path=="")
+ if( $path == '' )
return false;
- $store =& ExternalStore::getStoreObject( $proto );
+ $store = self::getStoreObject( $proto );
if ( $store === false )
return false;
- return $store->fetchFromURL($url);
+ return $store->fetchFromURL( $url );
}
/**
* Get an external store object of the given type
*/
- static function &getStoreObject( $proto ) {
+ static function getStoreObject( $proto ) {
global $wgExternalStores;
- if (!$wgExternalStores)
+ if( !$wgExternalStores )
return false;
/* Protocol not enabled */
- if (!in_array( $proto, $wgExternalStores ))
+ if( !in_array( $proto, $wgExternalStores ) )
return false;
- $class='ExternalStore'.ucfirst($proto);
+ $class = 'ExternalStore' . ucfirst( $proto );
/* Any custom modules should be added to $wgAutoLoadClasses for on-demand loading */
- if (!class_exists($class)) {
+ if( !class_exists( $class ) ){
return false;
}
- $store=new $class();
- return $store;
+
+ return new $class();
}
/**
@@ -54,7 +59,7 @@ class ExternalStore {
*/
static function insert( $url, $data ) {
list( $proto, $params ) = explode( '://', $url, 2 );
- $store =& ExternalStore::getStoreObject( $proto );
+ $store = self::getStoreObject( $proto );
if ( $store === false ) {
return false;
} else {
@@ -62,4 +67,3 @@ class ExternalStore {
}
}
}
-
diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php
index f9046f74..549412d1 100644
--- a/includes/ExternalStoreDB.php
+++ b/includes/ExternalStoreDB.php
@@ -1,12 +1,4 @@
<?php
-/**
- *
- *
- * DB accessable external objects
- *
- */
-
-
/**
* External database storage will use one (or more) separate connection pools
@@ -28,16 +20,15 @@ $wgExternalLoadBalancers = array();
global $wgExternalBlobCache;
$wgExternalBlobCache = array();
+/**
+ * DB accessable external objects
+ * @ingroup ExternalStorage
+ */
class ExternalStoreDB {
/** @todo Document.*/
function &getLoadBalancer( $cluster ) {
- global $wgExternalServers, $wgExternalLoadBalancers;
- if ( !array_key_exists( $cluster, $wgExternalLoadBalancers ) ) {
- $wgExternalLoadBalancers[$cluster] = LoadBalancer::newFromParams( $wgExternalServers[$cluster] );
- }
- $wgExternalLoadBalancers[$cluster]->allowLagged(true);
- return $wgExternalLoadBalancers[$cluster];
+ return wfGetLBFactory()->getExternalLB( $cluster );
}
/** @todo Document.*/
@@ -144,4 +135,3 @@ class ExternalStoreDB {
return "DB://$cluster/$id";
}
}
-
diff --git a/includes/ExternalStoreHttp.php b/includes/ExternalStoreHttp.php
index ef907df5..6eb33b39 100644
--- a/includes/ExternalStoreHttp.php
+++ b/includes/ExternalStoreHttp.php
@@ -1,11 +1,9 @@
<?php
/**
- *
- *
* Example class for HTTP accessable external objects
*
+ * @ingroup ExternalStorage
*/
-
class ExternalStoreHttp {
/* Fetch data from given URL */
function fetchFromURL($url) {
@@ -19,4 +17,3 @@ class ExternalStoreHttp {
* whatever, for initial ext storage
*/
}
-
diff --git a/includes/FakeTitle.php b/includes/FakeTitle.php
index b63ae505..4c2eddc8 100644
--- a/includes/FakeTitle.php
+++ b/includes/FakeTitle.php
@@ -83,5 +83,3 @@ class FakeTitle {
function trackbackURL() { $this->error(); }
function trackbackRDF() { $this->error(); }
}
-
-
diff --git a/includes/Feed.php b/includes/Feed.php
index 309b29bd..512057d9 100644
--- a/includes/Feed.php
+++ b/includes/Feed.php
@@ -300,5 +300,3 @@ class AtomFeed extends ChannelFeed {
</feed><?php
}
}
-
-?>
diff --git a/includes/FeedUtils.php b/includes/FeedUtils.php
new file mode 100644
index 00000000..aa784c02
--- /dev/null
+++ b/includes/FeedUtils.php
@@ -0,0 +1,152 @@
+<?php
+
+// TODO: document
+class FeedUtils {
+
+ public static function checkPurge( $timekey, $key ) {
+ global $wgRequest, $wgUser, $messageMemc;
+ $purge = $wgRequest->getVal( 'action' ) === 'purge';
+ if ( $purge && $wgUser->isAllowed('purge') ) {
+ $messageMemc->delete( $timekey );
+ $messageMemc->delete( $key );
+ }
+ }
+
+ public static function checkFeedOutput( $type ) {
+ global $wgFeed, $wgOut, $wgFeedClasses;
+
+ if ( !$wgFeed ) {
+ global $wgOut;
+ $wgOut->addWikiMsg( 'feed-unavailable' );
+ return false;
+ }
+
+ if( !isset( $wgFeedClasses[$type] ) ) {
+ wfHttpError( 500, "Internal Server Error", "Unsupported feed type." );
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Format a diff for the newsfeed
+ */
+ public static function formatDiff( $row ) {
+ global $wgUser;
+
+ $titleObj = Title::makeTitle( $row->rc_namespace, $row->rc_title );
+ $timestamp = wfTimestamp( TS_MW, $row->rc_timestamp );
+ $actiontext = '';
+ if( $row->rc_type == RC_LOG ) {
+ if( $row->rc_deleted & LogPage::DELETED_ACTION ) {
+ $actiontext = wfMsgHtml('rev-deleted-event');
+ } else {
+ $actiontext = LogPage::actionText( $row->rc_log_type, $row->rc_log_action,
+ $titleObj, $wgUser->getSkin(), LogPage::extractParams($row->rc_params,true,true) );
+ }
+ }
+ return self::formatDiffRow( $titleObj,
+ $row->rc_last_oldid, $row->rc_this_oldid,
+ $timestamp,
+ ($row->rc_deleted & Revision::DELETED_COMMENT) ? wfMsgHtml('rev-deleted-comment') : $row->rc_comment,
+ $actiontext );
+ }
+
+ public static function formatDiffRow( $title, $oldid, $newid, $timestamp, $comment, $actiontext='' ) {
+ global $wgFeedDiffCutoff, $wgContLang, $wgUser;
+ wfProfileIn( __FUNCTION__ );
+
+ $skin = $wgUser->getSkin();
+ # log enties
+ $completeText = '<p>' . implode( ' ',
+ array_filter(
+ array(
+ $actiontext,
+ $skin->formatComment( $comment ) ) ) ) . "</p>\n";
+
+ //NOTE: Check permissions for anonymous users, not current user.
+ // No "privileged" version should end up in the cache.
+ // Most feed readers will not log in anway.
+ $anon = new User();
+ $accErrors = $title->getUserPermissionsErrors( 'read', $anon, true );
+
+ if( $title->getNamespace() >= 0 && !$accErrors ) {
+ if( $oldid ) {
+ wfProfileIn( __FUNCTION__."-dodiff" );
+
+ $de = new DifferenceEngine( $title, $oldid, $newid );
+ #$diffText = $de->getDiff( wfMsg( 'revisionasof',
+ # $wgContLang->timeanddate( $timestamp ) ),
+ # wfMsg( 'currentrev' ) );
+ $diffText = $de->getDiff(
+ wfMsg( 'previousrevision' ), // hack
+ wfMsg( 'revisionasof',
+ $wgContLang->timeanddate( $timestamp ) ) );
+
+
+ if ( strlen( $diffText ) > $wgFeedDiffCutoff ) {
+ // Omit large diffs
+ $diffLink = $title->escapeFullUrl(
+ 'diff=' . $newid .
+ '&oldid=' . $oldid );
+ $diffText = '<a href="' .
+ $diffLink .
+ '">' .
+ htmlspecialchars( wfMsgForContent( 'showdiff' ) ) .
+ '</a>';
+ } elseif ( $diffText === false ) {
+ // Error in diff engine, probably a missing revision
+ $diffText = "<p>Can't load revision $newid</p>";
+ } else {
+ // Diff output fine, clean up any illegal UTF-8
+ $diffText = UtfNormal::cleanUp( $diffText );
+ $diffText = self::applyDiffStyle( $diffText );
+ }
+ wfProfileOut( __FUNCTION__."-dodiff" );
+ } else {
+ $rev = Revision::newFromId( $newid );
+ if( is_null( $rev ) ) {
+ $newtext = '';
+ } else {
+ $newtext = $rev->getText();
+ }
+ $diffText = '<p><b>' . wfMsg( 'newpage' ) . '</b></p>' .
+ '<div>' . nl2br( htmlspecialchars( $newtext ) ) . '</div>';
+ }
+ $completeText .= $diffText;
+ }
+
+ wfProfileOut( __FUNCTION__ );
+ return $completeText;
+ }
+
+ /**
+ * Hacky application of diff styles for the feeds.
+ * Might be 'cleaner' to use DOM or XSLT or something,
+ * but *gack* it's a pain in the ass.
+ *
+ * @param $text String:
+ * @return string
+ * @private
+ */
+ public static function applyDiffStyle( $text ) {
+ $styles = array(
+ 'diff' => 'background-color: white; color:black;',
+ 'diff-otitle' => 'background-color: white; color:black;',
+ 'diff-ntitle' => 'background-color: white; color:black;',
+ 'diff-addedline' => 'background: #cfc; color:black; font-size: smaller;',
+ 'diff-deletedline' => 'background: #ffa; color:black; font-size: smaller;',
+ 'diff-context' => 'background: #eee; color:black; font-size: smaller;',
+ 'diffchange' => 'color: red; font-weight: bold; text-decoration: none;',
+ );
+
+ foreach( $styles as $class => $style ) {
+ $text = preg_replace( "/(<[^>]+)class=(['\"])$class\\2([^>]*>)/",
+ "\\1style=\"$style\"\\3", $text );
+ }
+
+ return $text;
+ }
+
+} \ No newline at end of file
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
index 71e2c1ae..bc80c2b2 100644
--- a/includes/FileDeleteForm.php
+++ b/includes/FileDeleteForm.php
@@ -3,7 +3,7 @@
/**
* File deletion user interface
*
- * @addtogroup Media
+ * @ingroup Media
* @author Rob Church <robchur@gmail.com>
*/
class FileDeleteForm {
@@ -13,7 +13,7 @@ class FileDeleteForm {
private $oldfile = null;
private $oldimage = '';
-
+
/**
* Constructor
*
@@ -23,7 +23,7 @@ class FileDeleteForm {
$this->title = $file->getTitle();
$this->file = $file;
}
-
+
/**
* Fulfil the request; shows the form or deletes the file,
* pending authentication, confirmation, etc.
@@ -35,32 +35,31 @@ class FileDeleteForm {
if( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
- } elseif( !$wgUser->isLoggedIn() ) {
- $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
- return;
- } elseif( !$wgUser->isAllowed( 'delete' ) ) {
- $wgOut->permissionRequired( 'delete' );
- return;
- } elseif( $wgUser->isBlocked() ) {
- $wgOut->blockedPage();
+ }
+ $permission_errors = $this->title->getUserPermissionsErrors('delete', $wgUser);
+ if (count($permission_errors)>0) {
+ $wgOut->showPermissionsErrorPage( $permission_errors );
return;
}
-
+
$this->oldimage = $wgRequest->getText( 'oldimage', false );
$token = $wgRequest->getText( 'wpEditToken' );
- if( $this->oldimage && !$this->isValidOldSpec() ) {
+ # Flag to hide all contents of the archived revisions
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('suppressrevision');
+
+ if( $this->oldimage && !self::isValidOldSpec($this->oldimage) ) {
$wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->oldimage ) );
return;
}
if( $this->oldimage )
$this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
-
- if( !$this->haveDeletableFile() ) {
+
+ if( !self::haveDeletableFile($this->file, $this->oldfile, $this->oldimage) ) {
$wgOut->addHtml( $this->prepareMessage( 'filedelete-nofile' ) );
$wgOut->addReturnTo( $this->title );
return;
}
-
+
// Perform the deletion if appropriate
if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
$this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList' );
@@ -72,24 +71,9 @@ class FileDeleteForm {
} elseif ( $reason == 'other' ) {
$reason = $this->DeleteReason;
}
- if( $this->oldimage ) {
- $status = $this->file->deleteOld( $this->oldimage, $reason );
- if( $status->ok ) {
- // Need to do a log item
- $log = new LogPage( 'delete' );
- $logComment = wfMsgForContent( 'deletedrevision', $this->oldimage );
- if( trim( $reason ) != '' )
- $logComment .= ": {$reason}";
- $log->addEntry( 'delete', $this->title, $logComment );
- }
- } else {
- $status = $this->file->delete( $reason );
- if( $status->ok ) {
- // Need to delete the associated article
- $article = new Article( $this->title );
- $article->doDeleteArticle( $reason );
- }
- }
+
+ $status = self::doDelete( $this->title, $this->file, $this->oldimage, $reason, $suppress );
+
if( !$status->isGood() )
$wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) );
if( $status->ok ) {
@@ -101,11 +85,40 @@ class FileDeleteForm {
}
return;
}
-
+
$this->showForm();
$this->showLogEntries();
}
+ public static function doDelete( &$title, &$file, &$oldimage, $reason, $suppress ) {
+ $article = null;
+ if( $oldimage ) {
+ $status = $file->deleteOld( $oldimage, $reason, $suppress );
+ if( $status->ok ) {
+ // Need to do a log item
+ $log = new LogPage( 'delete' );
+ $logComment = wfMsgForContent( 'deletedrevision', $oldimage );
+ if( trim( $reason ) != '' )
+ $logComment .= ": {$reason}";
+ $log->addEntry( 'delete', $title, $logComment );
+ }
+ } else {
+ $status = $file->delete( $reason, $suppress );
+ if( $status->ok ) {
+ // Need to delete the associated article
+ $article = new Article( $title );
+ if( wfRunHooks('ArticleDelete', array(&$article, &$wgUser, &$reason)) ) {
+ if( $article->doDeleteArticle( $reason, $suppress ) )
+ wfRunHooks('ArticleDeleteComplete', array(&$article, &$wgUser, $reason));
+ }
+ }
+ }
+ if( $status->isGood() ) wfRunHooks('FileDeleteComplete', array(
+ &$file, &$oldimage, &$article, &$wgUser, &$reason));
+
+ return $status;
+ }
+
/**
* Show the confirmation form
*/
@@ -113,6 +126,14 @@ class FileDeleteForm {
global $wgOut, $wgUser, $wgRequest, $wgContLang;
$align = $wgContLang->isRtl() ? 'left' : 'right';
+ if( $wgUser->isAllowed( 'suppressrevision' ) ) {
+ $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\"><td></td><td>";
+ $suppress .= Xml::checkLabel( wfMsg( 'revdelete-suppress' ), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '2' ) );
+ $suppress .= "</td></tr>";
+ } else {
+ $suppress = '';
+ }
+
$form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) ) .
Xml::openElement( 'fieldset' ) .
Xml::element( 'legend', null, wfMsg( 'filedelete-legend' ) ) .
@@ -125,7 +146,7 @@ class FileDeleteForm {
"</td>
<td>" .
Xml::listDropDown( 'wpDeleteReasonList',
- wfMsgForContent( 'filedelete-reason-dropdown' ),
+ wfMsgForContent( 'filedelete-reason-dropdown' ),
wfMsgForContent( 'filedelete-reason-otherlist' ), '', 'wpReasonDropDown', 1 ) .
"</td>
</tr>
@@ -137,6 +158,7 @@ class FileDeleteForm {
Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ), array( 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ) ) .
"</td>
</tr>
+ {$suppress}
<tr>
<td></td>
<td>" .
@@ -147,6 +169,12 @@ class FileDeleteForm {
Xml::closeElement( 'fieldset' ) .
Xml::closeElement( 'form' );
+ if ( $wgUser->isAllowed( 'editinterface' ) ) {
+ $skin = $wgUser->getSkin();
+ $link = $skin->makeLink ( 'MediaWiki:Filedelete-reason-dropdown', wfMsgHtml( 'filedelete-edit-reasonlist' ) );
+ $form .= '<p class="mw-filedelete-editreasons">' . $link . '</p>';
+ }
+
$wgOut->addHtml( $form );
}
@@ -156,19 +184,9 @@ class FileDeleteForm {
private function showLogEntries() {
global $wgOut;
$wgOut->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
- $reader = new LogViewer(
- new LogReader(
- new FauxRequest(
- array(
- 'type' => 'delete',
- 'page' => $this->title->getPrefixedText(),
- )
- )
- )
- );
- $reader->showList( $wgOut );
+ LogEventsList::showLogExtract( $wgOut, 'delete', $this->title->getPrefixedText() );
}
-
+
/**
* Prepare a message referring to the file being deleted,
* showing an appropriate message depending upon whether
@@ -196,7 +214,7 @@ class FileDeleteForm {
);
}
}
-
+
/**
* Set headers, titles and other bits
*/
@@ -206,18 +224,18 @@ class FileDeleteForm {
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setSubtitle( wfMsg( 'filedelete-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->title ) ) );
}
-
+
/**
* Is the provided `oldimage` value valid?
*
* @return bool
*/
- private function isValidOldSpec() {
- return strlen( $this->oldimage ) >= 16
- && strpos( $this->oldimage, '/' ) === false
- && strpos( $this->oldimage, '\\' ) === false;
+ public static function isValidOldSpec($oldimage) {
+ return strlen( $oldimage ) >= 16
+ && strpos( $oldimage, '/' ) === false
+ && strpos( $oldimage, '\\' ) === false;
}
-
+
/**
* Could we delete the file specified? If an `oldimage`
* value was provided, does it correspond to an
@@ -225,12 +243,12 @@ class FileDeleteForm {
*
* @return bool
*/
- private function haveDeletableFile() {
- return $this->oldimage
- ? $this->oldfile && $this->oldfile->exists() && $this->oldfile->isLocal()
- : $this->file && $this->file->exists() && $this->file->isLocal();
+ public static function haveDeletableFile(&$file, &$oldfile, $oldimage) {
+ return $oldimage
+ ? $oldfile && $oldfile->exists() && $oldfile->isLocal()
+ : $file && $file->exists() && $file->isLocal();
}
-
+
/**
* Prepare the form action
*
@@ -243,7 +261,7 @@ class FileDeleteForm {
$q[] = 'oldimage=' . urlencode( $this->oldimage );
return $this->title->getLocalUrl( implode( '&', $q ) );
}
-
+
/**
* Extract the timestamp of the old version
*
@@ -252,5 +270,5 @@ class FileDeleteForm {
private function getTimestamp() {
return $this->oldfile->getTimestamp();
}
-
+
}
diff --git a/includes/FileRevertForm.php b/includes/FileRevertForm.php
index f335d024..385d83bc 100644
--- a/includes/FileRevertForm.php
+++ b/includes/FileRevertForm.php
@@ -3,16 +3,17 @@
/**
* File reversion user interface
*
- * @addtogroup Media
+ * @ingroup Media
* @author Rob Church <robchur@gmail.com>
*/
class FileRevertForm {
- private $title = null;
- private $file = null;
- private $oldimage = '';
- private $timestamp = false;
-
+ protected $title = null;
+ protected $file = null;
+ protected $archiveName = '';
+ protected $timestamp = false;
+ protected $oldFile;
+
/**
* Constructor
*
@@ -22,7 +23,7 @@ class FileRevertForm {
$this->title = $file->getTitle();
$this->file = $file;
}
-
+
/**
* Fulfil the request; shows the form or reverts the file,
* pending authentication, confirmation, etc.
@@ -37,7 +38,7 @@ class FileRevertForm {
} elseif( !$wgUser->isLoggedIn() ) {
$wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
return;
- } elseif( !$this->title->userCan( 'edit' ) ) {
+ } elseif( !$this->title->userCan( 'edit' ) || !$this->title->userCan( 'upload' ) ) {
// The standard read-only thing doesn't make a whole lot of sense
// here; surely it should show the image or something? -- RC
$article = new Article( $this->title );
@@ -47,23 +48,23 @@ class FileRevertForm {
$wgOut->blockedPage();
return;
}
-
- $this->oldimage = $wgRequest->getText( 'oldimage' );
+
+ $this->archiveName = $wgRequest->getText( 'oldimage' );
$token = $wgRequest->getText( 'wpEditToken' );
if( !$this->isValidOldSpec() ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->oldimage ) );
+ $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->archiveName ) );
return;
}
-
+
if( !$this->haveOldVersion() ) {
$wgOut->addHtml( wfMsgExt( 'filerevert-badversion', 'parse' ) );
$wgOut->returnToMain( false, $this->title );
return;
}
-
+
// Perform the reversion if appropriate
- if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
- $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
+ if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->archiveName ) ) {
+ $source = $this->file->getArchiveVirtualUrl( $this->archiveName );
$comment = $wgRequest->getText( 'wpComment' );
// TODO: Preserve file properties from database instead of reloading from file
$status = $this->file->upload( $source, $comment, $comment );
@@ -71,96 +72,100 @@ class FileRevertForm {
$wgOut->addHtml( wfMsgExt( 'filerevert-success', 'parse', $this->title->getText(),
$wgLang->date( $this->getTimestamp(), true ),
$wgLang->time( $this->getTimestamp(), true ),
- wfExpandUrl( $this->file->getArchiveUrl( $this->oldimage ) ) ) );
+ wfExpandUrl( $this->file->getArchiveUrl( $this->archiveName ) ) ) );
$wgOut->returnToMain( false, $this->title );
} else {
$wgOut->addWikiText( $status->getWikiText() );
}
return;
}
-
+
// Show the form
- $this->showForm();
+ $this->showForm();
}
-
+
/**
* Show the confirmation form
*/
- private function showForm() {
+ protected function showForm() {
global $wgOut, $wgUser, $wgRequest, $wgLang, $wgContLang;
$timestamp = $this->getTimestamp();
$form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) );
- $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->oldimage ) );
+ $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->archiveName ) );
$form .= '<fieldset><legend>' . wfMsgHtml( 'filerevert-legend' ) . '</legend>';
$form .= wfMsgExt( 'filerevert-intro', 'parse', $this->title->getText(),
$wgLang->date( $timestamp, true ), $wgLang->time( $timestamp, true ),
- wfExpandUrl( $this->file->getArchiveUrl( $this->oldimage ) ) );
+ wfExpandUrl( $this->file->getArchiveUrl( $this->archiveName ) ) );
$form .= '<p>' . Xml::inputLabel( wfMsg( 'filerevert-comment' ), 'wpComment', 'wpComment',
60, wfMsgForContent( 'filerevert-defaultcomment',
$wgContLang->date( $timestamp, false, false ), $wgContLang->time( $timestamp, false, false ) ) ) . '</p>';
$form .= '<p>' . Xml::submitButton( wfMsg( 'filerevert-submit' ) ) . '</p>';
$form .= '</fieldset>';
$form .= '</form>';
-
+
$wgOut->addHtml( $form );
}
-
+
/**
* Set headers, titles and other bits
*/
- private function setHeaders() {
+ protected function setHeaders() {
global $wgOut, $wgUser;
$wgOut->setPageTitle( wfMsg( 'filerevert', $this->title->getText() ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setSubtitle( wfMsg( 'filerevert-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->title ) ) );
}
-
+
/**
* Is the provided `oldimage` value valid?
*
* @return bool
*/
- private function isValidOldSpec() {
- return strlen( $this->oldimage ) >= 16
- && strpos( $this->oldimage, '/' ) === false
- && strpos( $this->oldimage, '\\' ) === false;
+ protected function isValidOldSpec() {
+ return strlen( $this->archiveName ) >= 16
+ && strpos( $this->archiveName, '/' ) === false
+ && strpos( $this->archiveName, '\\' ) === false;
}
-
+
/**
* Does the provided `oldimage` value correspond
* to an existing, local, old version of this file?
*
* @return bool
*/
- private function haveOldVersion() {
- $file = wfFindFile( $this->title, $this->oldimage );
- return $file && $file->exists() && $file->isLocal();
+ protected function haveOldVersion() {
+ return $this->getOldFile()->exists();
}
-
+
/**
* Prepare the form action
*
* @return string
*/
- private function getAction() {
+ protected function getAction() {
$q = array();
$q[] = 'action=revert';
- $q[] = 'oldimage=' . urlencode( $this->oldimage );
+ $q[] = 'oldimage=' . urlencode( $this->archiveName );
return $this->title->getLocalUrl( implode( '&', $q ) );
}
-
+
/**
* Extract the timestamp of the old version
*
* @return string
*/
- private function getTimestamp() {
+ protected function getTimestamp() {
if( $this->timestamp === false ) {
- $file = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
- $this->timestamp = $file->getTimestamp();
+ $this->timestamp = $this->getOldFile()->getTimestamp();
}
return $this->timestamp;
}
-
-} \ No newline at end of file
+
+ protected function getOldFile() {
+ if ( !isset( $this->oldFile ) ) {
+ $this->oldFile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->archiveName );
+ }
+ return $this->oldFile;
+ }
+}
diff --git a/includes/FileStore.php b/includes/FileStore.php
index a547e7e4..c01350c0 100644
--- a/includes/FileStore.php
+++ b/includes/FileStore.php
@@ -5,13 +5,13 @@
*/
class FileStore {
const DELETE_ORIGINAL = 1;
-
+
/**
* Fetch the FileStore object for a given storage group
*/
static function get( $group ) {
global $wgFileStore;
-
+
if( isset( $wgFileStore[$group] ) ) {
$info = $wgFileStore[$group];
return new FileStore( $group,
@@ -22,14 +22,14 @@ class FileStore {
return null;
}
}
-
+
private function __construct( $group, $directory, $path, $hash ) {
$this->mGroup = $group;
$this->mDirectory = $directory;
$this->mPath = $path;
$this->mHashLevel = $hash;
}
-
+
/**
* Acquire a lock; use when performing write operations on a store.
* This is attached to your master database connection, so if you
@@ -47,7 +47,7 @@ class FileStore {
$result = $dbw->query( "SELECT GET_LOCK($lockname, 5) AS lockstatus", __METHOD__ );
$row = $dbw->fetchObject( $result );
$dbw->freeResult( $result );
-
+
if( $row->lockstatus == 1 ) {
return true;
} else {
@@ -55,7 +55,7 @@ class FileStore {
return false;
}
}
-
+
/**
* Release the global file store lock.
*/
@@ -69,11 +69,11 @@ class FileStore {
$dbw->fetchObject( $result );
$dbw->freeResult( $result );
}
-
+
private static function lockName() {
return 'MediaWiki.' . wfWikiID() . '.FileStore';
}
-
+
/**
* Copy a file into the file store from elsewhere in the filesystem.
* Should be protected by FileStore::lock() to avoid race conditions.
@@ -89,7 +89,7 @@ class FileStore {
$destPath = $this->filePath( $key );
return $this->copyFile( $sourcePath, $destPath, $flags );
}
-
+
/**
* Copy a file from the file store to elsewhere in the filesystem.
* Should be protected by FileStore::lock() to avoid race conditions.
@@ -105,19 +105,19 @@ class FileStore {
$sourcePath = $this->filePath( $key );
return $this->copyFile( $sourcePath, $destPath, $flags );
}
-
+
private function copyFile( $sourcePath, $destPath, $flags=0 ) {
if( !file_exists( $sourcePath ) ) {
// Abort! Abort!
throw new FSException( "missing source file '$sourcePath'" );
}
-
+
$transaction = new FSTransaction();
-
+
if( $flags & self::DELETE_ORIGINAL ) {
$transaction->addCommit( FSTransaction::DELETE_FILE, $sourcePath );
}
-
+
if( file_exists( $destPath ) ) {
// An identical file is already present; no need to copy.
} else {
@@ -125,17 +125,17 @@ class FileStore {
wfSuppressWarnings();
$ok = mkdir( dirname( $destPath ), 0777, true );
wfRestoreWarnings();
-
+
if( !$ok ) {
throw new FSException(
"failed to create directory for '$destPath'" );
}
}
-
+
wfSuppressWarnings();
$ok = copy( $sourcePath, $destPath );
wfRestoreWarnings();
-
+
if( $ok ) {
wfDebug( __METHOD__." copied '$sourcePath' to '$destPath'\n" );
$transaction->addRollback( FSTransaction::DELETE_FILE, $destPath );
@@ -144,10 +144,10 @@ class FileStore {
__METHOD__." failed to copy '$sourcePath' to '$destPath'" );
}
}
-
+
return $transaction;
}
-
+
/**
* Delete a file from the file store.
* Caller's responsibility to make sure it's not being used by another row.
@@ -167,7 +167,7 @@ class FileStore {
return FileStore::deleteFile( $destPath );
}
}
-
+
/**
* Delete a non-managed file on a transactional basis.
*
@@ -189,7 +189,7 @@ class FileStore {
throw new FSException( "cannot delete missing file '$path'" );
}
}
-
+
/**
* Stream a contained file directly to HTTP output.
* Will throw a 404 if file is missing; 400 if invalid key.
@@ -201,12 +201,12 @@ class FileStore {
wfHttpError( 400, "Bad request", "Invalid or badly-formed filename." );
return false;
}
-
+
if( file_exists( $path ) ) {
// Set the filename for more convenient save behavior from browsers
// FIXME: Is this safe?
header( 'Content-Disposition: inline; filename="' . $key . '"' );
-
+
require_once 'StreamFile.php';
wfStreamFile( $path );
} else {
@@ -214,7 +214,7 @@ class FileStore {
"The requested resource does not exist." );
}
}
-
+
/**
* Confirm that the given file key is valid.
* Note that a valid key may refer to a file that does not exist.
@@ -229,8 +229,8 @@ class FileStore {
static function validKey( $key ) {
return preg_match( '/^[0-9a-z]{31,32}(\.[0-9a-z]{1,31})?$/', $key );
}
-
-
+
+
/**
* Calculate file storage key from a file on disk.
* You must pass an extension to it, as some files may be calculated
@@ -248,14 +248,14 @@ class FileStore {
wfDebug( __METHOD__.": couldn't hash file '$path'\n" );
return false;
}
-
+
$base36 = wfBaseConvert( $hash, 16, 36, 31 );
if( $extension == '' ) {
$key = $base36;
} else {
$key = $base36 . '.' . $extension;
}
-
+
// Sanity check
if( self::validKey( $key ) ) {
return $key;
@@ -264,7 +264,7 @@ class FileStore {
return false;
}
}
-
+
/**
* Return filesystem path to the given file.
* Note that the file may or may not exist.
@@ -278,7 +278,7 @@ class FileStore {
return false;
}
}
-
+
/**
* Return URL path to the given file, if the store is public.
* @return string or false if not public
@@ -290,7 +290,7 @@ class FileStore {
return false;
}
}
-
+
private function hashPath( $key, $separator ) {
$parts = array();
for( $i = 0; $i < $this->mHashLevel; $i++ ) {
@@ -310,7 +310,7 @@ class FileStore {
*/
class FSTransaction {
const DELETE_FILE = 1;
-
+
/**
* Combine more items into a fancier transaction
*/
@@ -320,7 +320,7 @@ class FSTransaction {
$this->mOnRollback = array_merge(
$this->mOnRollback, $transaction->mOnRollback );
}
-
+
/**
* Perform final actions for success.
* @return true if actions applied ok, false if errors
@@ -328,7 +328,7 @@ class FSTransaction {
function commit() {
return $this->apply( $this->mOnCommit );
}
-
+
/**
* Perform final actions for failure.
* @return true if actions applied ok, false if errors
@@ -336,22 +336,22 @@ class FSTransaction {
function rollback() {
return $this->apply( $this->mOnRollback );
}
-
+
// --- Private and friend functions below...
-
+
function __construct() {
$this->mOnCommit = array();
$this->mOnRollback = array();
}
-
+
function addCommit( $action, $path ) {
$this->mOnCommit[] = array( $action, $path );
}
-
+
function addRollback( $action, $path ) {
$this->mOnRollback[] = array( $action, $path );
}
-
+
private function apply( $actions ) {
$result = true;
foreach( $actions as $item ) {
@@ -372,8 +372,6 @@ class FSTransaction {
}
/**
- * @addtogroup Exception
+ * @ingroup Exception
*/
class FSException extends MWException { }
-
-
diff --git a/includes/FormOptions.php b/includes/FormOptions.php
new file mode 100644
index 00000000..5888a0c4
--- /dev/null
+++ b/includes/FormOptions.php
@@ -0,0 +1,202 @@
+<?php
+/**
+ * Helper class to keep track of options when mixing links and form elements.
+ *
+ * @author Niklas Laxström
+ * @copyright Copyright © 2008, Niklas Laxström
+ */
+
+class FormOptions implements ArrayAccess {
+ const AUTO = -1; //! Automatically detects simple data types
+ const STRING = 0;
+ const INT = 1;
+ const BOOL = 2;
+ const INTNULL = 3; //! Useful for namespace selector
+
+ protected $options = array();
+
+ # Setting up
+
+ public function add( $name, $default, $type = self::AUTO ) {
+ $option = array();
+ $option['default'] = $default;
+ $option['value'] = null;
+ $option['consumed'] = false;
+
+ if ( $type !== self::AUTO ) {
+ $option['type'] = $type;
+ } else {
+ $option['type'] = self::guessType( $default );
+ }
+
+ $this->options[$name] = $option;
+ }
+
+ public function delete( $name ) {
+ $this->validateName( $name, true );
+ unset($this->options[$name]);
+ }
+
+ public static function guessType( $data ) {
+ if ( is_bool($data) ) {
+ return self::BOOL;
+ } elseif( is_int($data) ) {
+ return self::INT;
+ } elseif( is_string($data) ) {
+ return self::STRING;
+ } else {
+ throw new MWException( 'Unsupported datatype' );
+ }
+ }
+
+ # Handling values
+
+ public function validateName( $name, $strict = false ) {
+ if ( !isset($this->options[$name]) ) {
+ if ( $strict ) {
+ throw new MWException( "Invalid option $name" );
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public function setValue( $name, $value, $force = false ) {
+ $this->validateName( $name, true );
+ if ( !$force && $value === $this->options[$name]['default'] ) {
+ // null default values as unchanged
+ $this->options[$name]['value'] = null;
+ } else {
+ $this->options[$name]['value'] = $value;
+ }
+ }
+
+ public function getValue( $name ) {
+ $this->validateName( $name, true );
+ return $this->getValueReal( $this->options[$name] );
+ }
+
+ protected function getValueReal( $option ) {
+ if ( $option['value'] !== null ) {
+ return $option['value'];
+ } else {
+ return $option['default'];
+ }
+ }
+
+ public function reset( $name ) {
+ $this->validateName( $name, true );
+ $this->options[$name]['value'] = null;
+ }
+
+ public function consumeValue( $name ) {
+ $this->validateName( $name, true );
+ $this->options[$name]['consumed'] = true;
+ return $this->getValueReal( $this->options[$name] );
+ }
+
+ public function consumeValues( /*Array*/ $names ) {
+ $out = array();
+ foreach ( $names as $name ) {
+ $this->validateName( $name, true );
+ $this->options[$name]['consumed'] = true;
+ $out[] = $this->getValueReal( $this->options[$name] );
+ }
+ return $out;
+ }
+
+ # Validating values
+
+ public function validateIntBounds( $name, $min, $max ) {
+ $this->validateName( $name, true );
+
+ if ( $this->options[$name]['type'] !== self::INT )
+ throw new MWException( "Option $name is not of type int" );
+
+ $value = $this->getValueReal( $this->options[$name] );
+ $value = max( $min, min( $max, $value ) );
+
+ $this->setValue( $name, $value );
+ }
+
+ # Getting the data out for use
+
+ public function getUnconsumedValues( $all = false ) {
+ $values = array();
+ foreach ( $this->options as $name => $data ) {
+ if ( !$data['consumed'] ) {
+ if ( $all || $data['value'] !== null ) {
+ $values[$name] = $this->getValueReal( $data );
+ }
+ }
+ }
+ return $values;
+ }
+
+ public function getChangedValues() {
+ $values = array();
+ foreach ( $this->options as $name => $data ) {
+ if ( $data['value'] !== null ) {
+ $values[$name] = $data['value'];
+ }
+ }
+ return $values;
+ }
+
+ public function getAllValues() {
+ $values = array();
+ foreach ( $this->options as $name => $data ) {
+ $values[$name] = $this->getValueReal( $data );
+ }
+ return $values;
+ }
+
+ # Reading values
+
+ public function fetchValuesFromRequest( WebRequest $r, $values = false ) {
+ if ( !$values ) {
+ $values = array_keys($this->options);
+ }
+
+ foreach ( $values as $name ) {
+ $default = $this->options[$name]['default'];
+ $type = $this->options[$name]['type'];
+
+ switch( $type ) {
+ case self::BOOL:
+ $value = $r->getBool( $name, $default ); break;
+ case self::INT:
+ $value = $r->getInt( $name, $default ); break;
+ case self::STRING:
+ $value = $r->getText( $name, $default ); break;
+ case self::INTNULL:
+ $value = $r->getIntOrNull( $name ); break;
+ default:
+ throw new MWException( 'Unsupported datatype' );
+ }
+
+ if ( $value !== $default && $value !== null ) {
+ $this->options[$name]['value'] = $value;
+ }
+ }
+ }
+
+ /* ArrayAccess methods */
+ public function offsetExists( $name ) {
+ return isset($this->options[$name]);
+ }
+
+ public function offsetGet( $name ) {
+ return $this->getValue( $name );
+ }
+
+ public function offsetSet( $name, $value ) {
+ return $this->setValue( $name, $value );
+ }
+
+ public function offsetUnset( $name ) {
+ return $this->delete( $name );
+ }
+
+}
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 2b9543b4..f5a2660c 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -87,6 +87,29 @@ if ( !function_exists( 'array_diff_key' ) ) {
}
}
+/**
+ * Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
+ */
+function wfArrayDiff2( $a, $b ) {
+ return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
+}
+function wfArrayDiff2_cmp( $a, $b ) {
+ if ( !is_array( $a ) ) {
+ return strcmp( $a, $b );
+ } elseif ( count( $a ) !== count( $b ) ) {
+ return count( $a ) < count( $b ) ? -1 : 1;
+ } else {
+ reset( $a );
+ reset( $b );
+ while( ( list( $keyA, $valueA ) = each( $a ) ) && ( list( $keyB, $valueB ) = each( $b ) ) ) {
+ $cmp = strcmp( $valueA, $valueB );
+ if ( $cmp !== 0 ) {
+ return $cmp;
+ }
+ }
+ return 0;
+ }
+}
/**
* Wrapper for clone(), for compatibility with PHP4-friendly extensions.
@@ -153,12 +176,16 @@ function wfDebug( $text, $logonly = false ) {
global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage;
static $recursion = 0;
+ static $cache = array(); // Cache of unoutputted messages
+
# Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) {
return;
}
if ( $wgDebugComments && !$logonly ) {
+ $cache[] = $text;
+
if ( !isset( $wgOut ) ) {
return;
}
@@ -170,7 +197,10 @@ function wfDebug( $text, $logonly = false ) {
$wgOut->_unstub();
$recursion--;
}
- $wgOut->debug( $text );
+
+ // add the message and possible cached ones to the output
+ array_map( array( $wgOut, 'debug' ), $cache );
+ $cache = array();
}
if ( '' != $wgDebugLogFile && !$wgProfileOnly ) {
# Strip unprintables; they can switch terminal modes when binary data
@@ -232,29 +262,30 @@ function wfErrorLog( $text, $file ) {
*/
function wfLogProfilingData() {
global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
- global $wgProfiling, $wgUser;
- if ( $wgProfiling ) {
- $now = wfTime();
- $elapsed = $now - $wgRequestTime;
- $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
- $forward = '';
- if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
- $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
- if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) )
- $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
- if( !empty( $_SERVER['HTTP_FROM'] ) )
- $forward .= ' from ' . $_SERVER['HTTP_FROM'];
- if( $forward )
- $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
- // Don't unstub $wgUser at this late stage just for statistics purposes
- if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() )
- $forward .= ' anon';
- $log = sprintf( "%s\t%04.3f\t%s\n",
- gmdate( 'YmdHis' ), $elapsed,
- urldecode( $wgRequest->getRequestURL() . $forward ) );
- if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
- wfErrorLog( $log . $prof, $wgDebugLogFile );
- }
+ global $wgProfiler, $wgUser;
+ if ( !isset( $wgProfiler ) )
+ return;
+
+ $now = wfTime();
+ $elapsed = $now - $wgRequestTime;
+ $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
+ $forward = '';
+ if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
+ $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
+ if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) )
+ $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
+ if( !empty( $_SERVER['HTTP_FROM'] ) )
+ $forward .= ' from ' . $_SERVER['HTTP_FROM'];
+ if( $forward )
+ $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
+ // Don't unstub $wgUser at this late stage just for statistics purposes
+ if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() )
+ $forward .= ' anon';
+ $log = sprintf( "%s\t%04.3f\t%s\n",
+ gmdate( 'YmdHis' ), $elapsed,
+ urldecode( $wgRequest->getRequestURL() . $forward ) );
+ if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
+ wfErrorLog( $log . $prof, $wgDebugLogFile );
}
}
@@ -282,6 +313,11 @@ function wfReadOnly() {
return (bool)$wgReadOnly;
}
+function wfReadOnlyReason() {
+ global $wgReadOnly;
+ wfReadOnly();
+ return $wgReadOnly;
+}
/**
* Get a message from anywhere, for the current user language.
@@ -291,9 +327,9 @@ function wfReadOnly() {
*
* @param $key String: lookup key for the message, usually
* defined in languages/Language.php
- *
- * This function also takes extra optional parameters (not
- * shown in the function definition), which can by used to
+ *
+ * This function also takes extra optional parameters (not
+ * shown in the function definition), which can by used to
* insert variable text into the predefined message.
*/
function wfMsg( $key ) {
@@ -415,24 +451,38 @@ function wfMsgWeirdKey ( $key ) {
* Fetch a message string value, but don't replace any keys yet.
* @param string $key
* @param bool $useDB
- * @param bool $forContent
+ * @param string $langcode Code of the language to get the message for, or
+ * behaves as a content language switch if it is a
+ * boolean.
* @return string
* @private
*/
-function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
+function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) {
global $wgParser, $wgContLang, $wgMessageCache, $wgLang;
+ wfRunHooks('NormalizeMessageKey', array(&$key, &$useDB, &$langCode, &$transform));
+
# If $wgMessageCache isn't initialised yet, try to return something sensible.
if( is_object( $wgMessageCache ) ) {
- $message = $wgMessageCache->get( $key, $useDB, $forContent );
+ $message = $wgMessageCache->get( $key, $useDB, $langCode );
if ( $transform ) {
$message = $wgMessageCache->transform( $message );
}
} else {
- if( $forContent ) {
+ if( $langCode === true ) {
$lang = &$wgContLang;
- } else {
+ } elseif( $langCode === false ) {
$lang = &$wgLang;
+ } else {
+ $validCodes = array_keys( Language::getLanguageNames() );
+ if( in_array( $langCode, $validCodes ) ) {
+ # $langcode corresponds to a valid language.
+ $lang = Language::factory( $langCode );
+ } else {
+ # $langcode is a string, but not a valid language code; use content language.
+ $lang =& $wgContLang;
+ wfDebug( 'Invalid language code passed to wfMsgGetKey, falling back to content language.' );
+ }
}
# MessageCache::get() does this already, Language::getMessage() doesn't
@@ -523,6 +573,8 @@ function wfMsgWikiHtml( $key ) {
* <i>replaceafter</i>: parameters are substituted after parsing or escaping
* <i>parsemag</i>: transform the message using magic phrases
* <i>content</i>: fetch message for content language instead of interface
+ * <i>language</i>: language code to fetch message for (overriden by <i>content</i>), its behaviour
+ * with parser, parseinline and parsemag is undefined.
* Behavior for conflicting options (e.g., parse+parseinline) is undefined.
*/
function wfMsgExt( $key, $options ) {
@@ -536,12 +588,23 @@ function wfMsgExt( $key, $options ) {
$options = array($options);
}
- $forContent = false;
if( in_array('content', $options) ) {
$forContent = true;
+ $langCode = true;
+ } elseif( array_key_exists('language', $options) ) {
+ $forContent = false;
+ $langCode = $options['language'];
+ $validCodes = array_keys( Language::getLanguageNames() );
+ if( !in_array($options['language'], $validCodes) ) {
+ # Fallback to en, instead of whatever interface language we might have
+ $langCode = 'en';
+ }
+ } else {
+ $forContent = false;
+ $langCode = false;
}
- $string = wfMsgGetKey( $key, /*DB*/true, $forContent, /*Transform*/false );
+ $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false );
if( !in_array('replaceafter', $options) ) {
$string = wfMsgReplaceArgs( $string, $args );
@@ -585,7 +648,6 @@ function wfMsgExt( $key, $options ) {
* @deprecated Please return control to the caller or throw an exception
*/
function wfAbruptExit( $error = false ){
- global $wgLoadBalancer;
static $called = false;
if ( $called ){
exit( -1 );
@@ -606,7 +668,7 @@ function wfAbruptExit( $error = false ){
wfLogProfilingData();
if ( !$error ) {
- $wgLoadBalancer->closeAll();
+ wfGetLB()->closeAll();
}
exit( -1 );
}
@@ -629,7 +691,7 @@ function wfDie( $msg='' ) {
}
/**
- * Throw a debugging exception. This function previously once exited the process,
+ * Throw a debugging exception. This function previously once exited the process,
* but now throws an exception instead, with similar results.
*
* @param string $msg Message shown when dieing.
@@ -848,7 +910,7 @@ function wfClientAcceptsGzip() {
* @param $deflimit Default limit if none supplied
* @param $optionname Name of a user preference to check against
* @return array
- *
+ *
*/
function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
global $wgRequest;
@@ -947,7 +1009,20 @@ function wfArrayToCGI( $array1, $array2 = NULL )
if ( '' != $cgi ) {
$cgi .= '&';
}
- $cgi .= urlencode( $key ) . '=' . urlencode( $value );
+ if(is_array($value))
+ {
+ $firstTime = true;
+ foreach($value as $v)
+ {
+ $cgi .= ($firstTime ? '' : '&') .
+ urlencode( $key . '[]' ) . '=' .
+ urlencode( $v );
+ $firstTime = false;
+ }
+ }
+ else
+ $cgi .= urlencode( $key ) . '=' .
+ urlencode( $value );
}
}
return $cgi;
@@ -1104,6 +1179,68 @@ function wfMerge( $old, $mine, $yours, &$result ){
}
/**
+ * Returns unified plain-text diff of two texts.
+ * Useful for machine processing of diffs.
+ * @param $before string The text before the changes.
+ * @param $after string The text after the changes.
+ * @param $params string Command-line options for the diff command.
+ * @return string Unified diff of $before and $after
+ */
+function wfDiff( $before, $after, $params = '-u' ) {
+ global $wgDiff;
+
+ # This check may also protect against code injection in
+ # case of broken installations.
+ if( !file_exists( $wgDiff ) ){
+ wfDebug( "diff executable not found\n" );
+ $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
+ $format = new UnifiedDiffFormatter();
+ return $format->format( $diffs );
+ }
+
+ # Make temporary files
+ $td = wfTempDir();
+ $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
+ $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
+
+ fwrite( $oldtextFile, $before ); fclose( $oldtextFile );
+ fwrite( $newtextFile, $after ); fclose( $newtextFile );
+
+ // Get the diff of the two files
+ $cmd = "$wgDiff " . $params . ' ' .wfEscapeShellArg( $oldtextName, $newtextName );
+
+ $h = popen( $cmd, 'r' );
+
+ $diff = '';
+
+ do {
+ $data = fread( $h, 8192 );
+ if ( strlen( $data ) == 0 ) {
+ break;
+ }
+ $diff .= $data;
+ } while ( true );
+
+ // Clean up
+ pclose( $h );
+ unlink( $oldtextName );
+ unlink( $newtextName );
+
+ // Kill the --- and +++ lines. They're not useful.
+ $diff_lines = explode( "\n", $diff );
+ if (strpos( $diff_lines[0], '---' ) === 0) {
+ unset($diff_lines[0]);
+ }
+ if (strpos( $diff_lines[1], '+++' ) === 0) {
+ unset($diff_lines[1]);
+ }
+
+ $diff = implode( "\n", $diff_lines );
+
+ return $diff;
+}
+
+/**
* @todo document
*/
function wfVarDump( $var ) {
@@ -1208,7 +1345,7 @@ function wfClearOutputBuffers() {
function wfAcceptToPrefs( $accept, $def = '*/*' ) {
# No arg means accept anything (per HTTP spec)
if( !$accept ) {
- return array( $def => 1 );
+ return array( $def => 1.0 );
}
$prefs = array();
@@ -1217,12 +1354,12 @@ function wfAcceptToPrefs( $accept, $def = '*/*' ) {
foreach( $parts as $part ) {
# FIXME: doesn't deal with params like 'text/html; level=1'
- @list( $value, $qpart ) = explode( ';', $part );
+ @list( $value, $qpart ) = explode( ';', trim( $part ) );
$match = array();
if( !isset( $qpart ) ) {
- $prefs[$value] = 1;
+ $prefs[$value] = 1.0;
} elseif( preg_match( '/q\s*=\s*(\d*\.\d+)/', $qpart, $match ) ) {
- $prefs[$value] = $match[1];
+ $prefs[$value] = floatval($match[1]);
}
}
@@ -1415,41 +1552,35 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
$uts=time();
} elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) {
# TS_DB
- $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
- (int)$da[2],(int)$da[3],(int)$da[1]);
} elseif (preg_match('/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) {
# TS_EXIF
- $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
- (int)$da[2],(int)$da[3],(int)$da[1]);
} elseif (preg_match('/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D',$ts,$da)) {
# TS_MW
- $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
- (int)$da[2],(int)$da[3],(int)$da[1]);
- } elseif (preg_match('/^(\d{1,13})$/D',$ts,$da)) {
+ } elseif (preg_match('/^\d{1,13}$/D',$ts)) {
# TS_UNIX
$uts = $ts;
- } elseif (preg_match('/^(\d{1,2})-(...)-(\d\d(\d\d)?) (\d\d)\.(\d\d)\.(\d\d)/', $ts, $da)) {
+ } elseif (preg_match('/^\d{1,2}-...-\d\d(?:\d\d)? \d\d\.\d\d\.\d\d/', $ts)) {
# TS_ORACLE
$uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
str_replace("+00:00", "UTC", $ts)));
} elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/', $ts, $da)) {
# TS_ISO_8601
- $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
- (int)$da[2],(int)$da[3],(int)$da[1]);
} elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)[\+\- ](\d\d)$/',$ts,$da)) {
# TS_POSTGRES
- $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
- (int)$da[2],(int)$da[3],(int)$da[1]);
} elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/',$ts,$da)) {
# TS_POSTGRES
- $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
- (int)$da[2],(int)$da[3],(int)$da[1]);
} else {
# Bogus value; fall back to the epoch...
wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n");
$uts = 0;
}
+ if (count( $da ) ) {
+ // Warning! gmmktime() acts oddly if the month or day is set to 0
+ // We may want to handle that explicitly at some point
+ $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
+ (int)$da[2],(int)$da[3],(int)$da[1]);
+ }
switch($outputtype) {
case TS_UNIX:
@@ -1515,9 +1646,9 @@ function wfGetCachedNotice( $name ) {
global $wgOut, $parserMemc;
$fname = 'wfGetCachedNotice';
wfProfileIn( $fname );
-
+
$needParse = false;
-
+
if( $name === 'default' ) {
// special case
global $wgSiteNotice;
@@ -1533,7 +1664,7 @@ function wfGetCachedNotice( $name ) {
return( false );
}
}
-
+
$cachedNotice = $parserMemc->get( wfMemcKey( $name ) );
if( is_array( $cachedNotice ) ) {
if( md5( $notice ) == $cachedNotice['hash'] ) {
@@ -1544,7 +1675,7 @@ function wfGetCachedNotice( $name ) {
} else {
$needParse = true;
}
-
+
if( $needParse ) {
if( is_object( $wgOut ) ) {
$parsed = $wgOut->parse( $notice );
@@ -1555,21 +1686,21 @@ function wfGetCachedNotice( $name ) {
$notice = '';
}
}
-
+
wfProfileOut( $fname );
return $notice;
}
function wfGetNamespaceNotice() {
global $wgTitle;
-
+
# Paranoia
if ( !isset( $wgTitle ) || !is_object( $wgTitle ) )
return "";
$fname = 'wfGetNamespaceNotice';
wfProfileIn( $fname );
-
+
$key = "namespacenotice-" . $wgTitle->getNsText();
$namespaceNotice = wfGetCachedNotice( $key );
if ( $namespaceNotice && substr ( $namespaceNotice , 0 ,7 ) != "<p>&lt;" ) {
@@ -1586,8 +1717,8 @@ function wfGetSiteNotice() {
global $wgUser, $wgSiteNotice;
$fname = 'wfGetSiteNotice';
wfProfileIn( $fname );
- $siteNotice = '';
-
+ $siteNotice = '';
+
if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) {
if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) {
$siteNotice = wfGetCachedNotice( 'sitenotice' );
@@ -1609,7 +1740,7 @@ function wfGetSiteNotice() {
return $siteNotice;
}
-/**
+/**
* BC wrapper for MimeMagic::singleton()
* @deprecated
*/
@@ -1640,13 +1771,70 @@ function wfTempDir() {
/**
* Make directory, and make all parent directories if they don't exist
+ *
+ * @param string $fullDir Full path to directory to create
+ * @param int $mode Chmod value to use, default is $wgDirectoryMode
+ * @return bool
*/
-function wfMkdirParents( $fullDir, $mode = 0777 ) {
+function wfMkdirParents( $fullDir, $mode = null ) {
+ global $wgDirectoryMode;
if( strval( $fullDir ) === '' )
return true;
if( file_exists( $fullDir ) )
return true;
- return mkdir( str_replace( '/', DIRECTORY_SEPARATOR, $fullDir ), $mode, true );
+ // If not defined or isn't an int, set to default
+ if ( is_null( $mode ) ) {
+ $mode = $wgDirectoryMode;
+ }
+
+
+ # Go back through the paths to find the first directory that exists
+ $currentDir = $fullDir;
+ $createList = array();
+ while ( strval( $currentDir ) !== '' && !file_exists( $currentDir ) ) {
+ # Strip trailing slashes
+ $currentDir = rtrim( $currentDir, '/\\' );
+
+ # Add to create list
+ $createList[] = $currentDir;
+
+ # Find next delimiter searching from the end
+ $p = max( strrpos( $currentDir, '/' ), strrpos( $currentDir, '\\' ) );
+ if ( $p === false ) {
+ $currentDir = false;
+ } else {
+ $currentDir = substr( $currentDir, 0, $p );
+ }
+ }
+
+ if ( count( $createList ) == 0 ) {
+ # Directory specified already exists
+ return true;
+ } elseif ( $currentDir === false ) {
+ # Went all the way back to root and it apparently doesn't exist
+ wfDebugLog( 'mkdir', "Root doesn't exist?\n" );
+ return false;
+ }
+ # Now go forward creating directories
+ $createList = array_reverse( $createList );
+
+ # Is the parent directory writable?
+ if ( $currentDir === '' ) {
+ $currentDir = '/';
+ }
+ if ( !is_writable( $currentDir ) ) {
+ wfDebugLog( 'mkdir', "Not writable: $currentDir\n" );
+ return false;
+ }
+
+ foreach ( $createList as $dir ) {
+ # use chmod to override the umask, as suggested by the PHP manual
+ if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
+ wfDebugLog( 'mkdir', "Unable to create directory $dir\n" );
+ return false;
+ }
+ }
+ return true;
}
/**
@@ -1654,7 +1842,7 @@ function wfMkdirParents( $fullDir, $mode = 0777 ) {
*/
function wfIncrStats( $key ) {
global $wgStatsMethod;
-
+
if( $wgStatsMethod == 'udp' ) {
global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname;
static $socket;
@@ -1693,15 +1881,12 @@ function wfPercent( $nr, $acc = 2, $round = true ) {
* @param string $userid ID of the user
* @param string $password Password of the user
* @return string Hashed password
+ * @deprecated Use User::crypt() or User::oldCrypt() instead
*/
function wfEncryptPassword( $userid, $password ) {
- global $wgPasswordSalt;
- $p = md5( $password);
-
- if($wgPasswordSalt)
- return md5( "{$userid}-{$p}" );
- else
- return $p;
+ wfDeprecated(__FUNCTION__);
+ # Just wrap around User::oldCrypt()
+ return User::oldCrypt($password, $userid);
}
/**
@@ -1809,7 +1994,7 @@ function wfIniGetBool( $setting ) {
*/
function wfShellExec( $cmd, &$retval=null ) {
global $IP, $wgMaxShellMemory, $wgMaxShellFileSize;
-
+
if( wfIniGetBool( 'safe_mode' ) ) {
wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
$retval = 1;
@@ -1833,14 +2018,14 @@ function wfShellExec( $cmd, &$retval=null ) {
$cmd = '"' . $cmd . '"';
}
wfDebug( "wfShellExec: $cmd\n" );
-
+
$retval = 1; // error by default?
ob_start();
passthru( $cmd, $retval );
$output = ob_get_contents();
ob_end_clean();
return $output;
-
+
}
/**
@@ -1899,7 +2084,7 @@ function wfRegexReplacement( $string ) {
*
* PHP's basename() only considers '\' a pathchar on Windows and Netware.
* We'll consider it so always, as we don't want \s in our Unix paths either.
- *
+ *
* @param string $path
* @param string $suffix to remove if present
* @return string
@@ -1929,14 +2114,14 @@ function wfRelativePath( $path, $from ) {
// Normalize mixed input on Windows...
$path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
$from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
-
+
// Trim trailing slashes -- fix for drive root
$path = rtrim( $path, DIRECTORY_SEPARATOR );
$from = rtrim( $from, DIRECTORY_SEPARATOR );
-
+
$pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
$against = explode( DIRECTORY_SEPARATOR, $from );
-
+
if( $pieces[0] !== $against[0] ) {
// Non-matching Windows drive letters?
// Return a full path.
@@ -2002,7 +2187,7 @@ function wfMakeUrlIndex( $url ) {
$delimiter = ':';
// parse_url detects for news: and mailto: the host part of an url as path
// We have to correct this wrong detection
- if ( isset ( $bits['path'] ) ) {
+ if ( isset ( $bits['path'] ) ) {
$bits['host'] = $bits['path'];
$bits['path'] = '';
}
@@ -2099,7 +2284,7 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true
$digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$inDigits = array();
$outChars = '';
-
+
// Decode and validate input string
$input = strtolower( $input );
for( $i = 0; $i < strlen( $input ); $i++ ) {
@@ -2109,18 +2294,18 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true
}
$inDigits[] = $n;
}
-
+
// Iterate over the input, modulo-ing out an output digit
// at a time until input is gone.
while( count( $inDigits ) ) {
$work = 0;
$workDigits = array();
-
+
// Long division...
foreach( $inDigits as $digit ) {
$work *= $sourceBase;
$work += $digit;
-
+
if( $work < $destBase ) {
// Gonna need to pull another digit.
if( count( $workDigits ) ) {
@@ -2132,26 +2317,26 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true
} else {
// Finally! Actual division!
$workDigits[] = intval( $work / $destBase );
-
+
// Isn't it annoying that most programming languages
// don't have a single divide-and-remainder operator,
// even though the CPU implements it that way?
$work = $work % $destBase;
}
}
-
+
// All that division leaves us with a remainder,
// which is conveniently our next output digit.
$outChars .= $digitChars[$work];
-
+
// And we continue!
$inDigits = $workDigits;
}
-
+
while( strlen( $outChars ) < $pad ) {
$outChars .= '0';
}
-
+
return strrev( $outChars );
}
@@ -2183,20 +2368,44 @@ function wfCreateObject( $name, $p ){
}
/**
- * Aliases for modularized functions
+ * Alias for modularized function
+ * @deprecated Use Http::get() instead
*/
-function wfGetHTTP( $url, $timeout = 'default' ) {
- return Http::get( $url, $timeout );
+function wfGetHTTP( $url, $timeout = 'default' ) {
+ wfDeprecated(__FUNCTION__);
+ return Http::get( $url, $timeout );
}
-function wfIsLocalURL( $url ) {
- return Http::isLocalURL( $url );
+
+/**
+ * Alias for modularized function
+ * @deprecated Use Http::isLocalURL() instead
+ */
+function wfIsLocalURL( $url ) {
+ wfDeprecated(__FUNCTION__);
+ return Http::isLocalURL( $url );
+}
+
+function wfHttpOnlySafe() {
+ global $wgHttpOnlyBlacklist;
+ if( !version_compare("5.2", PHP_VERSION, "<") )
+ return false;
+
+ if( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ foreach( $wgHttpOnlyBlacklist as $regex ) {
+ if( preg_match( $regex, $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
/**
* Initialise php session
*/
function wfSetupSession() {
- global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure;
+ global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly;
if( $wgSessionsInMemcached ) {
require_once( 'MemcachedSessions.php' );
} elseif( 'files' != ini_get( 'session.save_handler' ) ) {
@@ -2204,9 +2413,25 @@ function wfSetupSession() {
# application, it will end up failing. Try to recover.
ini_set ( 'session.save_handler', 'files' );
}
- session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
+ $httpOnlySafe = wfHttpOnlySafe();
+ wfDebugLog( 'cookie',
+ 'session_set_cookie_params: "' . implode( '", "',
+ array(
+ 0,
+ $wgCookiePath,
+ $wgCookieDomain,
+ $wgCookieSecure,
+ $httpOnlySafe && $wgCookieHttpOnly ) ) . '"' );
+ if( $httpOnlySafe && $wgCookieHttpOnly ) {
+ session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly );
+ } else {
+ // PHP 5.1 throws warnings if you pass the HttpOnly parameter for 5.2.
+ session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure );
+ }
session_cache_limiter( 'private, must-revalidate' );
- @session_start();
+ wfSuppressWarnings();
+ session_start();
+ wfRestoreWarnings();
}
/**
@@ -2253,13 +2478,8 @@ function wfFormatStackFrame($frame) {
* Get a cache key
*/
function wfMemcKey( /*... */ ) {
- global $wgDBprefix, $wgDBname;
$args = func_get_args();
- if ( $wgDBprefix ) {
- $key = "$wgDBname-$wgDBprefix:" . implode( ':', $args );
- } else {
- $key = $wgDBname . ':' . implode( ':', $args );
- }
+ $key = wfWikiID() . ':' . implode( ':', $args );
return $key;
}
@@ -2280,42 +2500,80 @@ function wfForeignMemcKey( $db, $prefix /*, ... */ ) {
* Get an ASCII string identifying this wiki
* This is used as a prefix in memcached keys
*/
-function wfWikiID() {
- global $wgDBprefix, $wgDBname;
- if ( $wgDBprefix ) {
- return "$wgDBname-$wgDBprefix";
+function wfWikiID( $db = null ) {
+ if( $db instanceof Database ) {
+ return $db->getWikiID();
} else {
- return $wgDBname;
+ global $wgDBprefix, $wgDBname;
+ if ( $wgDBprefix ) {
+ return "$wgDBname-$wgDBprefix";
+ } else {
+ return $wgDBname;
+ }
}
}
+/**
+ * Split a wiki ID into DB name and table prefix
+ */
+function wfSplitWikiID( $wiki ) {
+ $bits = explode( '-', $wiki, 2 );
+ if ( count( $bits ) < 2 ) {
+ $bits[] = '';
+ }
+ return $bits;
+}
+
/*
- * Get a Database object
- * @param integer $db Index of the connection to get. May be DB_MASTER for the
- * master (for write queries), DB_SLAVE for potentially lagged
+ * Get a Database object.
+ * @param integer $db Index of the connection to get. May be DB_MASTER for the
+ * master (for write queries), DB_SLAVE for potentially lagged
* read queries, or an integer >= 0 for a particular server.
*
- * @param mixed $groups Query groups. An array of group names that this query
- * belongs to. May contain a single string if the query is only
+ * @param mixed $groups Query groups. An array of group names that this query
+ * belongs to. May contain a single string if the query is only
* in one group.
+ *
+ * @param string $wiki The wiki ID, or false for the current wiki
+ *
+ * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request
+ * will always return the same object, unless the underlying connection or load
+ * balancer is manually destroyed.
*/
-function &wfGetDB( $db = DB_LAST, $groups = array() ) {
- global $wgLoadBalancer;
- $ret = $wgLoadBalancer->getConnection( $db, true, $groups );
- return $ret;
+function &wfGetDB( $db = DB_LAST, $groups = array(), $wiki = false ) {
+ return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
}
/**
- * Find a file.
+ * Get a load balancer object.
+ *
+ * @param array $groups List of query groups
+ * @param string $wiki Wiki ID, or false for the current wiki
+ * @return LoadBalancer
+ */
+function wfGetLB( $wiki = false ) {
+ return wfGetLBFactory()->getMainLB( $wiki );
+}
+
+/**
+ * Get the load balancer factory object
+ */
+function &wfGetLBFactory() {
+ return LBFactory::singleton();
+}
+
+/**
+ * Find a file.
* Shortcut for RepoGroup::singleton()->findFile()
* @param mixed $title Title object or string. May be interwiki.
- * @param mixed $time Requested time for an archived image, or false for the
- * current version. An image object will be returned which
- * existed at the specified time.
+ * @param mixed $time Requested time for an archived image, or false for the
+ * current version. An image object will be returned which
+ * was created at the specified time.
+ * @param mixed $flags FileRepo::FIND_ flags
* @return File, or false if the file does not exist
*/
-function wfFindFile( $title, $time = false ) {
- return RepoGroup::singleton()->findFile( $title, $time );
+function wfFindFile( $title, $time = false, $flags = 0 ) {
+ return RepoGroup::singleton()->findFile( $title, $time, $flags );
}
/**
@@ -2364,13 +2622,39 @@ function wfBoolToStr( $value ) {
/**
* Load an extension messages file
+ *
+ * @param string $extensionName Name of extension to load messages from\for.
+ * @param string $langcode Language to load messages for, or false for default
+ * behvaiour (en, content language and user language).
*/
-function wfLoadExtensionMessages( $extensionName ) {
- global $wgExtensionMessagesFiles, $wgMessageCache;
- if ( !empty( $wgExtensionMessagesFiles[$extensionName] ) ) {
- $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName] );
- // Prevent double-loading
- $wgExtensionMessagesFiles[$extensionName] = false;
+function wfLoadExtensionMessages( $extensionName, $langcode = false ) {
+ global $wgExtensionMessagesFiles, $wgMessageCache, $wgLang, $wgContLang;
+
+ #For recording whether extension message files have been loaded in a given language.
+ static $loaded = array();
+
+ if( !array_key_exists( $extensionName, $loaded ) ) {
+ $loaded[$extensionName] = array();
+ }
+
+ if ( !isset($wgExtensionMessagesFiles[$extensionName]) ) {
+ throw new MWException( "Messages file for extensions $extensionName is not defined" );
+ }
+
+ if( !$langcode && !array_key_exists( '*', $loaded[$extensionName] ) ) {
+ # Just do en, content language and user language.
+ $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], false );
+ # Mark that they have been loaded.
+ $loaded[$extensionName]['en'] = true;
+ $loaded[$extensionName][$wgLang->getCode()] = true;
+ $loaded[$extensionName][$wgContLang->getCode()] = true;
+ # Mark that this part has been done to avoid weird if statements.
+ $loaded[$extensionName]['*'] = true;
+ } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $loaded[$extensionName] ) ) {
+ # Load messages for specified language.
+ $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], $langcode );
+ # Mark that they have been loaded.
+ $loaded[$extensionName][$langcode] = true;
}
}
@@ -2388,7 +2672,7 @@ function wfGetNull() {
/**
* Displays a maxlag error
- *
+ *
* @param string $host Server that lags the most
* @param int $lag Maxlag (actual)
* @param int $maxLag Maxlag (requested)
@@ -2405,3 +2689,81 @@ function wfMaxlagError( $host, $lag, $maxLag ) {
echo "Waiting for a database server: $lag seconds lagged\n";
}
}
+
+/**
+ * Throws an E_USER_NOTICE saying that $function is deprecated
+ * @param string $function
+ * @return null
+ */
+function wfDeprecated( $function ) {
+ global $wgDebugLogFile;
+ if ( !$wgDebugLogFile ) {
+ return;
+ }
+ $callers = wfDebugBacktrace();
+ if( isset( $callers[2] ) ){
+ $callerfunc = $callers[2];
+ $callerfile = $callers[1];
+ if( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ){
+ $file = $callerfile['file'] . ' at line ' . $callerfile['line'];
+ } else {
+ $file = '(internal function)';
+ }
+ $func = '';
+ if( isset( $callerfunc['class'] ) )
+ $func .= $callerfunc['class'] . '::';
+ $func .= @$callerfunc['function'];
+ $msg = "Use of $function is deprecated. Called from $func in $file";
+ } else {
+ $msg = "Use of $function is deprecated.";
+ }
+ wfDebug( "$msg\n" );
+}
+
+/**
+ * Sleep until the worst slave's replication lag is less than or equal to
+ * $maxLag, in seconds. Use this when updating very large numbers of rows, as
+ * in maintenance scripts, to avoid causing too much lag. Of course, this is
+ * a no-op if there are no slaves.
+ *
+ * Every time the function has to wait for a slave, it will print a message to
+ * that effect (and then sleep for a little while), so it's probably not best
+ * to use this outside maintenance scripts in its present form.
+ *
+ * @param int $maxLag
+ * @return null
+ */
+function wfWaitForSlaves( $maxLag ) {
+ if( $maxLag ) {
+ $lb = wfGetLB();
+ list( $host, $lag ) = $lb->getMaxLag();
+ while( $lag > $maxLag ) {
+ $name = @gethostbyaddr( $host );
+ if( $name !== false ) {
+ $host = $name;
+ }
+ print "Waiting for $host (lagged $lag seconds)...\n";
+ sleep($maxLag);
+ list( $host, $lag ) = $lb->getMaxLag();
+ }
+ }
+}
+
+/** Generate a random 32-character hexadecimal token.
+ * @param mixed $salt Some sort of salt, if necessary, to add to random characters before hashing.
+ */
+function wfGenerateToken( $salt = '' ) {
+ $salt = serialize($salt);
+
+ return md5( mt_rand( 0, 0x7fffffff ) . $salt );
+}
+
+/**
+ * Replace all invalid characters with -
+ * @param mixed $title Filename to process
+ */
+function wfStripIllegalFilenameChars( $name ) {
+ $name = wfBaseName( $name );
+ $name = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $name );
+ return $name;
+}
diff --git a/includes/HTMLCacheUpdate.php b/includes/HTMLCacheUpdate.php
index 050005dd..1f250214 100644
--- a/includes/HTMLCacheUpdate.php
+++ b/includes/HTMLCacheUpdate.php
@@ -5,27 +5,28 @@
* Small numbers of links will be done immediately, large numbers are pushed onto
* the job queue.
*
- * This class is designed to work efficiently with small numbers of links, and
+ * This class is designed to work efficiently with small numbers of links, and
* to work reasonably well with up to ~10^5 links. Above ~10^6 links, the memory
* and time requirements of loading all backlinked IDs in doUpdate() might become
* prohibitive. The requirements measured at Wikimedia are approximately:
- *
+ *
* memory: 48 bytes per row
* time: 16us per row for the query plus processing
*
* The reason this query is done is to support partitioning of the job
- * by backlinked ID. The memory issue could be allieviated by doing this query in
+ * by backlinked ID. The memory issue could be allieviated by doing this query in
* batches, but of course LIMIT with an offset is inefficient on the DB side.
*
- * The class is nevertheless a vast improvement on the previous method of using
+ * The class is nevertheless a vast improvement on the previous method of using
* Image::getLinksTo() and Title::touchArray(), which uses about 2KB of memory per
* link.
+ *
+ * @ingroup Cache
*/
class HTMLCacheUpdate
{
public $mTitle, $mTable, $mPrefix;
public $mRowsPerJob, $mRowsPerQuery;
- public $mResult;
function __construct( $titleTo, $table ) {
global $wgUpdateRowsPerJob, $wgUpdateRowsPerQuery;
@@ -41,7 +42,7 @@ class HTMLCacheUpdate
$cond = $this->getToCondition();
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( $this->mTable, $this->getFromField(), $cond, __METHOD__ );
- $this->mResult = $res;
+
if ( $dbr->numRows( $res ) != 0 ) {
if ( $dbr->numRows( $res ) > $this->mRowsPerJob ) {
$this->insertJobs( $res );
@@ -67,7 +68,7 @@ class HTMLCacheUpdate
break;
}
}
-
+
$params = array(
'table' => $this->mTable,
'start' => $start,
@@ -88,7 +89,7 @@ class HTMLCacheUpdate
'categorylinks' => 'cl',
'templatelinks' => 'tl',
'redirect' => 'rd',
-
+
# Not needed
# 'externallinks' => 'el',
# 'langlinks' => 'll'
@@ -102,7 +103,7 @@ class HTMLCacheUpdate
}
return $this->mPrefix;
}
-
+
function getFromField() {
return $this->getPrefix() . '_from';
}
@@ -113,7 +114,7 @@ class HTMLCacheUpdate
case 'pagelinks':
case 'templatelinks':
case 'redirect':
- return array(
+ return array(
"{$prefix}_namespace" => $this->mTitle->getNamespace(),
"{$prefix}_title" => $this->mTitle->getDBkey()
);
@@ -138,7 +139,7 @@ class HTMLCacheUpdate
$dbw = wfGetDB( DB_MASTER );
$timestamp = $dbw->timestamp();
$done = false;
-
+
while ( !$done ) {
# Get all IDs in this query into an array
$ids = array();
@@ -155,10 +156,10 @@ class HTMLCacheUpdate
if ( !count( $ids ) ) {
break;
}
-
+
# Update page_touched
- $dbw->update( 'page',
- array( 'page_touched' => $timestamp ),
+ $dbw->update( 'page',
+ array( 'page_touched' => $timestamp ),
array( 'page_id IN (' . $dbw->makeList( $ids ) . ')' ),
__METHOD__
);
@@ -185,6 +186,7 @@ class HTMLCacheUpdate
/**
* @todo document (e.g. one-sentence top-level class description).
+ * @ingroup JobQueue
*/
class HTMLCacheUpdateJob extends Job {
var $table, $start, $end;
@@ -216,9 +218,8 @@ class HTMLCacheUpdateJob extends Job {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( $this->table, $fromField, $conds, __METHOD__ );
- $update->invalidateIDs( new ResultWrapper( $dbr, $res ) );
+ $update->invalidateIDs( $res );
return true;
}
}
-
diff --git a/includes/HTMLFileCache.php b/includes/HTMLFileCache.php
index a7466814..ba2196eb 100644
--- a/includes/HTMLFileCache.php
+++ b/includes/HTMLFileCache.php
@@ -1,7 +1,8 @@
<?php
/**
* Contain the HTMLFileCache class
- * @addtogroup Cache
+ * @file
+ * @ingroup Cache
*/
/**
@@ -10,11 +11,13 @@
* emergency abort/fallback to cache.
*
* Global options that affect this module:
- * $wgCachePages
- * $wgCacheEpoch
- * $wgUseFileCache
- * $wgFileCacheDirectory
- * $wgUseGzip
+ * - $wgCachePages
+ * - $wgCacheEpoch
+ * - $wgUseFileCache
+ * - $wgFileCacheDirectory
+ * - $wgUseGzip
+ *
+ * @ingroup Cache
*/
class HTMLFileCache {
var $mTitle, $mFileCache;
@@ -153,5 +156,3 @@ class HTMLFileCache {
}
}
-
-
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index 984ee2d4..3772926d 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -1,11 +1,8 @@
<?php
-/**
- *
- */
/**
* Pure virtual parent
- * @todo document (needs a one-sentence top-level class description, that answers the question: "what is a HistoryBlob?")
+ * @todo document (needs a one-sentence top-level class description, that answers the question: "what is a HistoryBlob?")
*/
interface HistoryBlob
{
@@ -308,6 +305,3 @@ class HistoryBlobCurStub {
return $row->cur_text;
}
}
-
-
-
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 20103db4..046a149d 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -19,6 +19,7 @@
*
* @author Evan Prodromou <evan@wikitravel.org>
* @see hooks.txt
+ * @file
*/
@@ -27,7 +28,7 @@
* careful about its contents. So, there's a lot more error-checking
* in here than would normally be necessary.
*/
-function wfRunHooks($event, $args = null) {
+function wfRunHooks($event, $args = array()) {
global $wgHooks;
@@ -108,6 +109,9 @@ function wfRunHooks($event, $args = null) {
$callback = $func;
}
+ // Run autoloader (workaround for call_user_func_array bug)
+ is_callable( $callback );
+
/* Call the hook. */
wfProfileIn( $func );
$retval = call_user_func_array( $callback, $hook_args );
@@ -140,4 +144,3 @@ function wfRunHooks($event, $args = null) {
return true;
}
-
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index 6ea3abd0..555a79b7 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -24,7 +24,7 @@ class Http {
# Use curl if available
if ( function_exists( 'curl_init' ) ) {
$c = curl_init( $url );
- if ( wfIsLocalURL( $url ) ) {
+ if ( self::isLocalURL( $url ) ) {
curl_setopt( $c, CURLOPT_PROXY, 'localhost:80' );
} else if ($wgHTTPProxy) {
curl_setopt($c, CURLOPT_PROXY, $wgHTTPProxy);
@@ -118,4 +118,3 @@ class Http {
return false;
}
}
-
diff --git a/includes/IP.php b/includes/IP.php
index db712c3b..e76f66c1 100644
--- a/includes/IP.php
+++ b/includes/IP.php
@@ -48,7 +48,7 @@ class IP {
// IPv6 IPs with two "::" strings are ambiguous and thus invalid
return preg_match( '/^' . IP_ADDRESS_STRING . '$/', $ip) && ( substr_count($ip, '::') < 2 );
}
-
+
public static function isIPv6( $ip ) {
if ( !$ip ) return false;
if( is_array( $ip ) ) {
@@ -57,18 +57,18 @@ class IP {
// IPv6 IPs with two "::" strings are ambiguous and thus invalid
return preg_match( '/^' . RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)$/', $ip) && ( substr_count($ip, '::') < 2);
}
-
+
public static function isIPv4( $ip ) {
if ( !$ip ) return false;
return preg_match( '/^' . RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)$/', $ip);
}
-
+
/**
* Given an IP address in dotted-quad notation, returns an IPv6 octet.
* See http://www.answers.com/topic/ipv4-compatible-address
* IPs with the first 92 bits as zeros are reserved from IPv6
* @param $ip quad-dotted IP address.
- * @return string
+ * @return string
*/
public static function IPv4toIPv6( $ip ) {
if ( !$ip ) return null;
@@ -106,13 +106,13 @@ class IP {
$r_ip = wfBaseConvert( $r_ip, 16, 10 );
return $r_ip;
}
-
+
/**
* Given an IPv6 address in octet notation, returns the expanded octet.
* IPv4 IPs will be trimmed, thats it...
* @param $ip octet ipv6 IP address.
- * @return string
- */
+ * @return string
+ */
public static function sanitizeIP( $ip ) {
$ip = trim( $ip );
if ( $ip === '' ) return null;
@@ -132,11 +132,11 @@ class IP {
$ip = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip );
return $ip;
}
-
+
/**
* Given an unsigned integer, returns an IPv6 address in octet notation
* @param $ip integer IP address.
- * @return string
+ * @return string
*/
public static function toOctet( $ip_int ) {
// Convert to padded uppercase hex
@@ -181,9 +181,9 @@ class IP {
}
return array( $network, $bits );
}
-
+
/**
- * Given a string range in a number of formats, return the start and end of
+ * Given a string range in a number of formats, return the start and end of
* the range in hexadecimal. For IPv6.
*
* Formats are:
@@ -233,7 +233,7 @@ class IP {
return array( $start, $end );
}
}
-
+
/**
* Validate an IP address.
* @return boolean True if it is valid.
@@ -310,7 +310,7 @@ class IP {
* Return a zero-padded hexadecimal representation of an IP address.
*
* Hexadecimal addresses are used because they can easily be extended to
- * IPv6 support. To separate the ranges, the return value from this
+ * IPv6 support. To separate the ranges, the return value from this
* function for an IPv6 address will be prefixed with "v6-", a non-
* hexadecimal string which sorts after the IPv4 addresses.
*
@@ -396,14 +396,14 @@ class IP {
}
/**
- * Given a string range in a number of formats, return the start and end of
+ * Given a string range in a number of formats, return the start and end of
* the range in hexadecimal.
*
* Formats are:
* 1.2.3.4/24 CIDR
* 1.2.3.4 - 1.2.3.5 Explicit range
* 1.2.3.4 Single IP
- *
+ *
* 2001:0db8:85a3::7344/96 CIDR
* 2001:0db8:85a3::7344 - 2001:0db8:85a3::7344 Explicit range
* 2001:0db8:85a3::7344 Single IP
@@ -439,7 +439,7 @@ class IP {
}
if ( $start === false || $end === false ) {
return array( false, false );
- } else {
+ } else {
return array( $start, $end );
}
}
@@ -492,5 +492,3 @@ class IP {
return null; // give up
}
}
-
-
diff --git a/includes/ImageFunctions.php b/includes/ImageFunctions.php
index 3e87c994..af05c1c9 100644
--- a/includes/ImageFunctions.php
+++ b/includes/ImageFunctions.php
@@ -53,10 +53,10 @@ function wfGetSVGsize( $filename ) {
return false;
}
$tag = $matches[1];
- if( preg_match( '/\bwidth\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
+ if( preg_match( '/(?:^|\s)width\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
$width = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
}
- if( preg_match( '/\bheight\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
+ if( preg_match( '/(?:^|\s)height\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
$height = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
}
@@ -73,8 +73,8 @@ function wfGetSVGsize( $filename ) {
* * Any subsequent links on the same line are considered to be exceptions,
* i.e. articles where the image may occur inline.
*
- * @param string $name the image name to check
- * @param Title $contextTitle The page on which the image occurs, if known
+ * @param $name string the image name to check
+ * @param $contextTitle Title: the page on which the image occurs, if known
* @return bool
*/
function wfIsBadImage( $name, $contextTitle = false ) {
@@ -122,7 +122,7 @@ function wfIsBadImage( $name, $contextTitle = false ) {
}
}
}
-
+
$contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
$bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
wfProfileOut( __METHOD__ );
@@ -145,6 +145,3 @@ function wfFitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
else
return $roundedUp;
}
-
-
-
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
index 46ecd169..492a3e06 100644
--- a/includes/ImageGallery.php
+++ b/includes/ImageGallery.php
@@ -3,14 +3,11 @@ if ( ! defined( 'MEDIAWIKI' ) )
die( 1 );
/**
- */
-
-/**
* Image gallery
*
* Add images to the gallery using add(), then render that list to HTML using toHTML().
*
- * @addtogroup Media
+ * @ingroup Media
*/
class ImageGallery
{
@@ -37,7 +34,7 @@ class ImageGallery
private $mPerRow = 4; // How many images wide should the gallery be?
private $mWidths = 120, $mHeights = 120; // How wide/tall each thumbnail should be
-
+
private $mAttribs = array();
/**
@@ -196,11 +193,11 @@ class ImageGallery
function setShowFilename( $f ) {
$this->mShowFilename = ( $f == true);
}
-
+
/**
* Set arbitrary attributes to go on the HTML gallery output element.
* Should be suitable for a &lt;table&gt; element.
- *
+ *
* Note -- if taking from user input, you should probably run through
* Sanitizer::validateAttributes() first.
*
@@ -240,10 +237,10 @@ class ImageGallery
foreach ( $this->mImages as $pair ) {
$nt = $pair[0];
$text = $pair[1];
-
+
# Give extensions a chance to select the file revision for us
- $time = false;
- wfRunHooks( 'BeforeGalleryFindFile', array( &$this, &$nt, &$time ) );
+ $time = $descQuery = false;
+ wfRunHooks( 'BeforeGalleryFindFile', array( &$this, &$nt, &$time, &$descQuery ) );
$img = wfFindFile( $nt, $time );
@@ -261,14 +258,14 @@ class ImageGallery
. htmlspecialchars( $img->getLastError() ) . '</div>';
} else {
$vpad = floor( ( 1.25*$this->mHeights - $thumb->height ) /2 ) - 2;
-
+
$thumbhtml = "\n\t\t\t".
'<div class="thumb" style="padding: ' . $vpad . 'px 0; width: ' .($this->mWidths+30).'px;">'
# Auto-margin centering for block-level elements. Needed now that we have video
# handlers since they may emit block-level elements as opposed to simple <img> tags.
# ref http://css-discuss.incutio.com/?page=CenteringBlockElement
. '<div style="margin-left: auto; margin-right: auto; width: ' .$this->mWidths.'px;">'
- . $thumb->toHtml( array( 'desc-link' => true ) ) . '</div></div>';
+ . $thumb->toHtml( array( 'desc-link' => true, 'desc-query' => $descQuery ) ) . '</div></div>';
// Call parser transform hook
if ( $this->mParser && $img->getHandler() ) {
@@ -277,7 +274,7 @@ class ImageGallery
}
//TODO
- //$ul = $sk->makeLink( $wgContLang->getNsText( Namespace::getUser() ) . ":{$ut}", $ut );
+ //$ul = $sk->makeLink( $wgContLang->getNsText( MWNamespace::getUser() ) . ":{$ut}", $ut );
if( $this->mShowBytes ) {
if( $img ) {
@@ -328,7 +325,7 @@ class ImageGallery
public function count() {
return count( $this->mImages );
}
-
+
/**
* Set the contextual title
*
@@ -337,7 +334,7 @@ class ImageGallery
public function setContextTitle( $title ) {
$this->contextTitle = $title;
}
-
+
/**
* Get the contextual title, if applicable
*
@@ -350,5 +347,3 @@ class ImageGallery
}
} //class
-
-
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index 573bc4d7..30fcf13e 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -1,34 +1,46 @@
<?php
-/**
- */
-/**
- *
- */
if( !defined( 'MEDIAWIKI' ) )
die( 1 );
/**
* Special handling for image description pages
*
- * @addtogroup Media
+ * @ingroup Media
*/
class ImagePage extends Article {
- /* private */ var $img; // Image object this page is shown for
+ /* private */ var $img; // Image object
+ /* private */ var $displayImg;
/* private */ var $repo;
+ /* private */ var $fileLoaded;
var $mExtraDescription = false;
+ var $dupes;
- function __construct( $title, $time = false ) {
+ function __construct( $title ) {
parent::__construct( $title );
- $this->img = wfFindFile( $this->mTitle, $time );
+ $this->dupes = null;
+ $this->repo = null;
+ }
+
+ protected function loadFile() {
+ if ( $this->fileLoaded ) {
+ return true;
+ }
+ $this->fileLoaded = true;
+
+ $this->displayImg = $this->img = false;
+ wfRunHooks( 'ImagePageFindFile', array( $this, &$this->img, &$this->displayImg ) );
if ( !$this->img ) {
- $this->img = wfLocalFile( $this->mTitle );
- $this->current = $this->img;
- } else {
- $this->current = $time ? wfLocalFile( $this->mTitle ) : $this->img;
+ $this->img = wfFindFile( $this->mTitle );
+ if ( !$this->img ) {
+ $this->img = wfLocalFile( $this->mTitle );
+ }
+ }
+ if ( !$this->displayImg ) {
+ $this->displayImg = $this->img;
}
- $this->repo = $this->img->repo;
+ $this->repo = $this->img->getRepo();
}
/**
@@ -38,11 +50,28 @@ class ImagePage extends Article {
function render() {
global $wgOut;
$wgOut->setArticleBodyOnly( true );
- $wgOut->addSecondaryWikitext( $this->getContent() );
+ parent::view();
}
function view() {
global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
+ $this->loadFile();
+
+ if ( $this->mTitle->getNamespace() == NS_IMAGE && $this->img->getRedirected() ) {
+ if ( $this->mTitle->getDBkey() == $this->img->getName() ) {
+ // mTitle is the same as the redirect target so ask Article
+ // to perform the redirect for us.
+ return Article::view();
+ } else {
+ // mTitle is not the same as the redirect target so it is
+ // probably the redirect page itself. Fake the redirect symbol
+ $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ $this->viewRedirect( Title::makeTitle( NS_IMAGE, $this->img->getName() ),
+ /* $appendSubtitle */ true, /* $forceKnown */ true );
+ $this->viewUpdates();
+ return;
+ }
+ }
$diff = $wgRequest->getVal( 'diff' );
$diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
@@ -50,16 +79,16 @@ class ImagePage extends Article {
if ( $this->mTitle->getNamespace() != NS_IMAGE || ( isset( $diff ) && $diffOnly ) )
return Article::view();
- if ($wgShowEXIF && $this->img->exists()) {
- // FIXME: bad interface, see note on MediaHandler::formatMetadata().
- $formattedMetadata = $this->img->formatMetadata();
+ if ( $wgShowEXIF && $this->displayImg->exists() ) {
+ // FIXME: bad interface, see note on MediaHandler::formatMetadata().
+ $formattedMetadata = $this->displayImg->formatMetadata();
$showmeta = $formattedMetadata !== false;
} else {
$showmeta = false;
}
- if ($this->img->exists())
- $wgOut->addHTML($this->showTOC($showmeta));
+ if ( $this->displayImg->exists() )
+ $wgOut->addHTML( $this->showTOC($showmeta) );
$this->openShowImage();
@@ -81,10 +110,23 @@ class ImagePage extends Article {
$wgOut->addWikiText( $fol );
}
$wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . '</div>' );
+ } else {
+ $this->checkSharedConflict();
}
$this->closeShowImage();
$this->imageHistory();
+ // TODO: Cleanup the following
+
+ $wgOut->addHTML( Xml::element( 'h2',
+ array( 'id' => 'filelinks' ),
+ wfMsg( 'imagelinks' ) ) . "\n" );
+ $this->imageDupes();
+ // TODO: We may want to find local images redirecting to a foreign
+ // file: "The following local files redirect to this file"
+ if ( $this->img->isLocal() ) {
+ $this->imageRedirects();
+ }
$this->imageLinks();
if ( $showmeta ) {
@@ -93,11 +135,83 @@ class ImagePage extends Article {
$collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) );
$wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ). "\n" );
$wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
+ $wgOut->addScriptFile( 'metadata.js' );
$wgOut->addHTML(
- "<script type=\"text/javascript\" src=\"$wgStylePath/common/metadata.js?$wgStyleVersion\"></script>\n" .
"<script type=\"text/javascript\">attachMetadataToggle('mw_metadata', '$expand', '$collapse');</script>\n" );
}
}
+
+ public function getRedirectTarget() {
+ $this->loadFile();
+ if ( $this->img->isLocal() ) {
+ return parent::getRedirectTarget();
+ }
+ // Foreign image page
+ $from = $this->img->getRedirected();
+ $to = $this->img->getName();
+ if ( $from == $to ) {
+ return null;
+ }
+ return $this->mRedirectTarget = Title::makeTitle( NS_IMAGE, $to );
+ }
+ public function followRedirect() {
+ $this->loadFile();
+ if ( $this->img->isLocal() ) {
+ return parent::followRedirect();
+ }
+ $from = $this->img->getRedirected();
+ $to = $this->img->getName();
+ if ( $from == $to ) {
+ return false;
+ }
+ return Title::makeTitle( NS_IMAGE, $to );
+ }
+ public function isRedirect( $text = false ) {
+ $this->loadFile();
+ if ( $this->img->isLocal() )
+ return parent::isRedirect( $text );
+
+ return (bool)$this->img->getRedirected();
+ }
+
+ public function isLocal() {
+ $this->loadFile();
+ return $this->img->isLocal();
+ }
+
+ public function getFile() {
+ $this->loadFile();
+ return $this->img;
+ }
+
+ public function getDisplayedFile() {
+ $this->loadFile();
+ return $this->displayImg;
+ }
+
+ public function getDuplicates() {
+ $this->loadFile();
+ if ( !is_null($this->dupes) ) {
+ return $this->dupes;
+ }
+ if ( !( $hash = $this->img->getSha1() ) ) {
+ return $this->dupes = array();
+ }
+ $dupes = RepoGroup::singleton()->findBySha1( $hash );
+ // Remove duplicates with self and non matching file sizes
+ $self = $this->img->getRepoName().':'.$this->img->getName();
+ $size = $this->img->getSize();
+ foreach ( $dupes as $index => $file ) {
+ $key = $file->getRepoName().':'.$file->getName();
+ if ( $key == $self )
+ unset( $dupes[$index] );
+ if ( $file->getSize() != $size )
+ unset( $dupes[$index] );
+ }
+ return $this->dupes = $dupes;
+
+ }
+
/**
* Create the TOC
@@ -121,7 +235,7 @@ class ImagePage extends Article {
/**
* Make a table with metadata to be shown in the output page.
*
- * FIXME: bad interface, see note on MediaHandler::formatMetadata().
+ * FIXME: bad interface, see note on MediaHandler::formatMetadata().
*
* @access private
*
@@ -148,11 +262,12 @@ class ImagePage extends Article {
/**
* Overloading Article's getContent method.
- *
+ *
* Omit noarticletext if sharedupload; text will be fetched from the
* shared upload server if possible.
*/
function getContent() {
+ $this->loadFile();
if( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
return '';
}
@@ -162,7 +277,9 @@ class ImagePage extends Article {
function openShowImage() {
global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang, $wgContLang;
- $full_url = $this->img->getURL();
+ $this->loadFile();
+
+ $full_url = $this->displayImg->getURL();
$linkAttribs = false;
$sizeSel = intval( $wgUser->getOption( 'imagesize') );
if( !isset( $wgImageLimits[$sizeSel] ) ) {
@@ -181,7 +298,7 @@ class ImagePage extends Article {
$sk = $wgUser->getSkin();
$dirmark = $wgContLang->getDirMark();
- if ( $this->img->exists() ) {
+ if ( $this->displayImg->exists() ) {
# image
$page = $wgRequest->getIntOrNull( 'page' );
if ( is_null( $page ) ) {
@@ -190,22 +307,22 @@ class ImagePage extends Article {
} else {
$params = array( 'page' => $page );
}
- $width_orig = $this->img->getWidth();
+ $width_orig = $this->displayImg->getWidth();
$width = $width_orig;
- $height_orig = $this->img->getHeight();
+ $height_orig = $this->displayImg->getHeight();
$height = $height_orig;
- $mime = $this->img->getMimeType();
+ $mime = $this->displayImg->getMimeType();
$showLink = false;
$linkAttribs = array( 'href' => $full_url );
- $longDesc = $this->img->getLongDesc();
+ $longDesc = $this->displayImg->getLongDesc();
wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) ) ;
- if ( $this->img->allowInlineDisplay() ) {
+ if ( $this->displayImg->allowInlineDisplay() ) {
# image
# "Download high res version" link below the image
- #$msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
+ #$msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->displayImg->getSize() ), $mime );
# We'll show a thumbnail of this image
if ( $width > $maxWidth || $height > $maxHeight ) {
# Calculate the thumbnail size.
@@ -226,43 +343,43 @@ class ImagePage extends Article {
array( 'parseinline' ), $wgLang->formatNum( $width ), $wgLang->formatNum( $height ) );
} else {
# Image is small enough to show full size on image page
- $msgbig = htmlspecialchars( $this->img->getName() );
+ $msgbig = htmlspecialchars( $this->displayImg->getName() );
$msgsmall = wfMsgExt( 'file-nohires', array( 'parseinline' ) );
}
$params['width'] = $width;
- $thumbnail = $this->img->transform( $params );
+ $thumbnail = $this->displayImg->transform( $params );
$anchorclose = "<br />";
- if( $this->img->mustRender() ) {
+ if( $this->displayImg->mustRender() ) {
$showLink = true;
} else {
- $anchorclose .=
+ $anchorclose .=
$msgsmall .
'<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . "$dirmark " . $longDesc;
}
- if ( $this->img->isMultipage() ) {
+ if ( $this->displayImg->isMultipage() ) {
$wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
}
if ( $thumbnail ) {
- $options = array(
- 'alt' => $this->img->getTitle()->getPrefixedText(),
+ $options = array(
+ 'alt' => $this->displayImg->getTitle()->getPrefixedText(),
'file-link' => true,
);
- $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
+ $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
$thumbnail->toHtml( $options ) .
$anchorclose . '</div>' );
}
- if ( $this->img->isMultipage() ) {
- $count = $this->img->pageCount();
+ if ( $this->displayImg->isMultipage() ) {
+ $count = $this->displayImg->pageCount();
if ( $page > 1 ) {
$label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
$link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
- $thumb1 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none',
+ $thumb1 = $sk->makeThumbLinkObj( $this->mTitle, $this->displayImg, $link, $label, 'none',
array( 'page' => $page - 1 ) );
} else {
$thumb1 = '';
@@ -271,34 +388,42 @@ class ImagePage extends Article {
if ( $page < $count ) {
$label = wfMsg( 'imgmultipagenext' );
$link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
- $thumb2 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none',
+ $thumb2 = $sk->makeThumbLinkObj( $this->mTitle, $this->displayImg, $link, $label, 'none',
array( 'page' => $page + 1 ) );
} else {
$thumb2 = '';
}
global $wgScript;
- $select = '<form name="pageselector" action="' .
- htmlspecialchars( $wgScript ) .
- '" method="get" onchange="document.pageselector.submit();">' .
- Xml::hidden( 'title', $this->getTitle()->getPrefixedDbKey() );
- $select .= $wgOut->parse( wfMsg( 'imgmultigotopre' ), false ) .
- ' <select id="pageselector" name="page">';
+
+ $formParams = array(
+ 'name' => 'pageselector',
+ 'action' => $wgScript,
+ 'onchange' => 'document.pageselector.submit();',
+ );
+
+ $option = array();
for ( $i=1; $i <= $count; $i++ ) {
- $select .= Xml::option( $wgLang->formatNum( $i ), $i,
- $i == $page );
+ $options[] = Xml::option( $wgLang->formatNum($i), $i, $i == $page );
}
- $select .= '</select>' . $wgOut->parse( wfMsg( 'imgmultigotopost' ), false ) .
- '<input type="submit" value="' .
- htmlspecialchars( wfMsg( 'imgmultigo' ) ) . '"></form>';
-
- $wgOut->addHTML( '</td><td><div class="multipageimagenavbox">' .
- "$select<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>" );
+ $select = Xml::tags( 'select',
+ array( 'id' => 'pageselector', 'name' => 'page' ),
+ implode( "\n", $options ) );
+
+ $wgOut->addHTML(
+ '</td><td><div class="multipageimagenavbox">' .
+ Xml::openElement( 'form', $formParams ) .
+ Xml::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) .
+ wfMsgExt( 'imgmultigoto', array( 'parseinline', 'replaceafter' ), $select ) .
+ Xml::submitButton( wfMsg( 'imgmultigo' ) ) .
+ Xml::closeElement( 'form' ) .
+ "<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>"
+ );
}
} else {
#if direct link is allowed but it's not a renderable image, show an icon.
- if ($this->img->isSafeFile()) {
- $icon= $this->img->iconThumb();
+ if ( $this->displayImg->isSafeFile() ) {
+ $icon= $this->displayImg->iconThumb();
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
$icon->toHtml( array( 'desc-link' => true ) ) .
@@ -310,9 +435,9 @@ class ImagePage extends Article {
if ($showLink) {
- $filename = wfEscapeWikiText( $this->img->getName() );
+ $filename = wfEscapeWikiText( $this->displayImg->getName() );
- if (!$this->img->isSafeFile()) {
+ if ( !$this->displayImg->isSafeFile() ) {
$warning = wfMsgNoTrans( 'mediawarning' );
$wgOut->addWikiText( <<<EOT
<div class="fullMedia">
@@ -333,7 +458,7 @@ EOT
}
}
- if(!$this->img->isLocal()) {
+ if( !$this->displayImg->isLocal() ) {
$this->printSharedImageText();
}
} else {
@@ -341,31 +466,90 @@ EOT
$title = SpecialPage::getTitleFor( 'Upload' );
$link = $sk->makeKnownLinkObj($title, wfMsgHtml('noimage-linktext'),
- 'wpDestFile=' . urlencode( $this->img->getName() ) );
+ 'wpDestFile=' . urlencode( $this->displayImg->getName() ) );
$wgOut->addHTML( wfMsgWikiHtml( 'noimage', $link ) );
}
}
+ /**
+ * Show a notice that the file is from a shared repository
+ */
function printSharedImageText() {
global $wgOut, $wgUser;
+ $this->loadFile();
+
$descUrl = $this->img->getDescriptionUrl();
$descText = $this->img->getDescriptionText();
- $s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
- if ( $descUrl && !$descText) {
+ $s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml( 'sharedupload' );
+ if ( $descUrl ) {
$sk = $wgUser->getSkin();
- $link = $sk->makeExternalLink( $descUrl, wfMsg('shareduploadwiki-linktext') );
- $s .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
+ $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadwiki-linktext' ) );
+ $msg = ( $descText ) ? 'shareduploadwiki-desc' : 'shareduploadwiki';
+ $msg = wfMsgExt( $msg, array( 'parseinline', 'replaceafter' ), $link );
+ if ( $msg != '-' ) {
+ # Show message only if not voided by local sysops
+ $s .= $msg;
+ }
}
$s .= "</div>";
- $wgOut->addHTML($s);
+ $wgOut->addHTML( $s );
if ( $descText ) {
$this->mExtraDescription = $descText;
}
}
+ /*
+ * Check for files with the same name on the foreign repos.
+ */
+ function checkSharedConflict() {
+ global $wgOut, $wgUser;
+
+ $repoGroup = RepoGroup::singleton();
+ if( !$repoGroup->hasForeignRepos() ) {
+ return;
+ }
+
+ $this->loadFile();
+ if( !$this->img->isLocal() ) {
+ return;
+ }
+
+ $this->dupFile = null;
+ $repoGroup->forEachForeignRepo( array( $this, 'checkSharedConflictCallback' ) );
+
+ if( !$this->dupFile )
+ return;
+ $dupfile = $this->dupFile;
+ $same = (
+ ($this->img->getSha1() == $dupfile->getSha1()) &&
+ ($this->img->getSize() == $dupfile->getSize())
+ );
+
+ $sk = $wgUser->getSkin();
+ $descUrl = $dupfile->getDescriptionUrl();
+ if( $same ) {
+ $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadduplicate-linktext' ) );
+ $wgOut->addHTML( '<div id="shared-image-dup">' . wfMsgWikiHtml( 'shareduploadduplicate', $link ) . '</div>' );
+ } else {
+ $link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadconflict-linktext' ) );
+ $wgOut->addHTML( '<div id="shared-image-conflict">' . wfMsgWikiHtml( 'shareduploadconflict', $link ) . '</div>' );
+ }
+ }
+
+ function checkSharedConflictCallback( $repo ) {
+ $this->loadFile();
+ $dupfile = $repo->newFile( $this->img->getTitle() );
+ if( $dupfile && $dupfile->exists() ) {
+ $this->dupFile = $dupfile;
+ return $dupfile->exists();
+ }
+ return false;
+ }
+
function getUploadUrl() {
+ $this->loadFile();
$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
return $uploadTitle->getFullUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) );
}
@@ -377,23 +561,28 @@ EOT
function uploadLinksBox() {
global $wgUser, $wgOut;
+ $this->loadFile();
if( !$this->img->isLocal() )
return;
$sk = $wgUser->getSkin();
-
+
$wgOut->addHtml( '<br /><ul>' );
-
+
# "Upload a new version of this file" link
if( UploadForm::userCanReUpload($wgUser,$this->img->name) ) {
$ulink = $sk->makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) );
$wgOut->addHtml( "<li><div class='plainlinks'>{$ulink}</div></li>" );
}
-
+
+ # Link to Special:FileDuplicateSearch
+ $dupeLink = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'FileDuplicateSearch', $this->mTitle->getDBkey() ), wfMsgHtml( 'imagepage-searchdupe' ) );
+ $wgOut->addHtml( "<li>{$dupeLink}</li>" );
+
# External editing link
$elink = $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'edit-externally' ), 'action=edit&externaledit=true&mode=file' );
$wgOut->addHtml( '<li>' . $elink . '<div>' . wfMsgWikiHtml( 'edit-externally-help' ) . '</div></li>' );
-
+
$wgOut->addHtml( '</ul>' );
}
@@ -409,29 +598,20 @@ EOT
*/
function imageHistory()
{
- global $wgUser, $wgOut, $wgUseExternalEditor;
-
- $sk = $wgUser->getSkin();
+ global $wgOut, $wgUseExternalEditor;
+ $this->loadFile();
if ( $this->img->exists() ) {
- $list = new ImageHistoryList( $sk, $this->current );
- $file = $this->current;
+ $list = new ImageHistoryList( $this );
+ $file = $this->img;
$dims = $file->getDimensionsString();
- $s = $list->beginImageHistoryList() .
- $list->imageHistoryLine( true, wfTimestamp(TS_MW, $file->getTimestamp()),
- $this->mTitle->getDBkey(), $file->getUser('id'),
- $file->getUser('text'), $file->getSize(), $file->getDescription(),
- $dims
- );
-
+ $s = $list->beginImageHistoryList();
+ $s .= $list->imageHistoryLine( true, $file );
+ // old image versions
$hist = $this->img->getHistory();
foreach( $hist as $file ) {
$dims = $file->getDimensionsString();
- $s .= $list->imageHistoryLine( false, wfTimestamp(TS_MW, $file->getTimestamp()),
- $file->getArchiveName(), $file->getUser('id'),
- $file->getUser('text'), $file->getSize(), $file->getDescription(),
- $dims
- );
+ $s .= $list->imageHistoryLine( false, $file );
}
$s .= $list->endImageHistoryList();
} else { $s=''; }
@@ -451,37 +631,97 @@ EOT
{
global $wgUser, $wgOut;
- $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'filelinks' ), wfMsg( 'imagelinks' ) ) . "\n" );
+ $limit = 100;
$dbr = wfGetDB( DB_SLAVE );
- $page = $dbr->tableName( 'page' );
- $imagelinks = $dbr->tableName( 'imagelinks' );
- $sql = "SELECT page_namespace,page_title FROM $imagelinks,$page WHERE il_to=" .
- $dbr->addQuotes( $this->mTitle->getDBkey() ) . " AND il_from=page_id";
- $sql = $dbr->limitResult($sql, 500, 0);
- $res = $dbr->query( $sql, "ImagePage::imageLinks" );
-
- if ( 0 == $dbr->numRows( $res ) ) {
- $wgOut->addHtml( '<p>' . wfMsg( "nolinkstoimage" ) . "</p>\n" );
+ $res = $dbr->select(
+ array( 'imagelinks', 'page' ),
+ array( 'page_namespace', 'page_title' ),
+ array( 'il_to' => $this->mTitle->getDBkey(), 'il_from = page_id' ),
+ __METHOD__,
+ array( 'LIMIT' => $limit + 1)
+ );
+ $count = $dbr->numRows( $res );
+ if ( $count == 0 ) {
+ $wgOut->addHTML( "<div id='mw-imagepage-nolinkstoimage'>\n" );
+ $wgOut->addWikiMsg( 'nolinkstoimage' );
+ $wgOut->addHTML( "</div>\n" );
return;
}
- $wgOut->addHTML( '<p>' . wfMsg( 'linkstoimage' ) . "</p>\n<ul>" );
+ $wgOut->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
+ $wgOut->addWikiMsg( 'linkstoimage', $count );
+ $wgOut->addHTML( "<ul class='mw-imagepage-linktoimage'>\n" );
+
+ $sk = $wgUser->getSkin();
+ $count = 0;
+ while ( $s = $res->fetchObject() ) {
+ $count++;
+ if ( $count <= $limit ) {
+ // We have not yet reached the extra one that tells us there is more to fetch
+ $name = Title::makeTitle( $s->page_namespace, $s->page_title );
+ $link = $sk->makeKnownLinkObj( $name, "" );
+ $wgOut->addHTML( "<li>{$link}</li>\n" );
+ }
+ }
+ $wgOut->addHTML( "</ul></div>\n" );
+ $res->free();
+
+ // Add a links to [[Special:Whatlinkshere]]
+ if ( $count > $limit )
+ $wgOut->addWikiMsg( 'morelinkstoimage', $this->mTitle->getPrefixedDBkey() );
+ }
+
+ function imageRedirects()
+ {
+ global $wgUser, $wgOut;
+
+ $redirects = $this->getTitle()->getRedirectsHere( NS_IMAGE );
+ if ( count( $redirects ) == 0 ) return;
+
+ $wgOut->addHTML( "<div id='mw-imagepage-section-redirectstofile'>\n" );
+ $wgOut->addWikiMsg( 'redirectstofile', count( $redirects ) );
+ $wgOut->addHTML( "<ul class='mw-imagepage-redirectstofile'>\n" );
+
+ $sk = $wgUser->getSkin();
+ foreach ( $redirects as $title ) {
+ $link = $sk->makeKnownLinkObj( $title, "", "redirect=no" );
+ $wgOut->addHTML( "<li>{$link}</li>\n" );
+ }
+ $wgOut->addHTML( "</ul></div>\n" );
+
+ }
+
+ function imageDupes() {
+ global $wgOut, $wgUser;
+
+ $this->loadFile();
+
+ $dupes = $this->getDuplicates();
+ if ( count( $dupes ) == 0 ) return;
+
+ $wgOut->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
+ $wgOut->addWikiMsg( 'duplicatesoffile', count( $dupes ) );
+ $wgOut->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
$sk = $wgUser->getSkin();
- while ( $s = $dbr->fetchObject( $res ) ) {
- $name = Title::MakeTitle( $s->page_namespace, $s->page_title );
- $link = $sk->makeKnownLinkObj( $name, "" );
+ foreach ( $dupes as $file ) {
+ if ( $file->isLocal() )
+ $link = $sk->makeKnownLinkObj( $file->getTitle(), "" );
+ else
+ $link = $sk->makeExternalLink( $file->getDescriptionUrl(),
+ $file->getTitle()->getPrefixedText() );
$wgOut->addHTML( "<li>{$link}</li>\n" );
}
- $wgOut->addHTML( "</ul>\n" );
+ $wgOut->addHTML( "</ul></div>\n" );
}
/**
* Delete the file, or an earlier version of it
*/
public function delete() {
- if( !$this->img->exists() || !$this->img->isLocal() ) {
+ $this->loadFile();
+ if( !$this->img->exists() || !$this->img->isLocal() || $this->img->getRedirected() ) {
// Standard article deletion
Article::delete();
return;
@@ -494,14 +734,16 @@ EOT
* Revert the file to an earlier version
*/
public function revert() {
+ $this->loadFile();
$reverter = new FileRevertForm( $this->img );
$reverter->execute();
}
-
+
/**
* Override handling of action=purge
*/
function doPurge() {
+ $this->loadFile();
if( $this->img->exists() ) {
wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
$update = new HTMLCacheUpdate( $this->mTitle, 'imagelinks' );
@@ -531,16 +773,31 @@ EOT
/**
* Builds the image revision log shown on image pages
*
- * @addtogroup Media
+ * @ingroup Media
*/
class ImageHistoryList {
- protected $img, $skin, $title, $repo;
+ protected $imagePage, $img, $skin, $title, $repo;
+
+ public function __construct( $imagePage ) {
+ global $wgUser;
+ $this->skin = $wgUser->getSkin();
+ $this->current = $imagePage->getFile();
+ $this->img = $imagePage->getDisplayedFile();
+ $this->title = $imagePage->getTitle();
+ $this->imagePage = $imagePage;
+ }
+
+ function getImagePage() {
+ return $this->imagePage;
+ }
- public function __construct( $skin, $img ) {
- $this->skin = $skin;
- $this->img = $img;
- $this->title = $img->getTitle();
+ function getSkin() {
+ return $this->skin;
+ }
+
+ function getFile() {
+ return $this->img;
}
public function beginImageHistoryList() {
@@ -549,12 +806,11 @@ class ImageHistoryList {
. $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) )
. Xml::openElement( 'table', array( 'class' => 'filehistory' ) ) . "\n"
. '<tr><td></td>'
- . ( $this->img->isLocal() && $wgUser->isAllowed( 'delete' ) ? '<td></td>' : '' )
+ . ( $this->current->isLocal() && ($wgUser->isAllowed('delete') || $wgUser->isAllowed('deleterevision') ) ? '<td></td>' : '' )
. '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
- . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
. '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
- . '<th class="mw-imagepage-filesize">' . wfMsgHtml( 'filehist-filesize' ) . '</th>'
- . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
. "</tr>\n";
}
@@ -562,36 +818,57 @@ class ImageHistoryList {
return "</table>\n";
}
- /**
- * Create one row of file history
- *
- * @param bool $iscur is this the current file version?
- * @param string $timestamp timestamp of file version
- * @param string $img filename
- * @param int $user ID of uploading user
- * @param string $usertext username of uploading user
- * @param int $size size of file version
- * @param string $description description of file version
- * @param string $dims dimensions of file version
- * @return string a HTML formatted table row
- */
- public function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $dims ) {
- global $wgUser, $wgLang, $wgContLang;
- $local = $this->img->isLocal();
- $row = '';
+ public function imageHistoryLine( $iscur, $file ) {
+ global $wgUser, $wgLang, $wgContLang, $wgTitle;
+
+ $timestamp = wfTimestamp(TS_MW, $file->getTimestamp());
+ $img = $iscur ? $file->getName() : $file->getArchiveName();
+ $user = $file->getUser('id');
+ $usertext = $file->getUser('text');
+ $size = $file->getSize();
+ $description = $file->getDescription();
+ $dims = $file->getDimensionsString();
+ $sha1 = $file->getSha1();
+
+ $local = $this->current->isLocal();
+ $row = $css = $selected = '';
// Deletion link
- if( $local && $wgUser->isAllowed( 'delete' ) ) {
+ if( $local && ($wgUser->isAllowed('delete') || $wgUser->isAllowed('deleterevision') ) ) {
$row .= '<td>';
- $q = array();
- $q[] = 'action=delete';
- if( !$iscur )
- $q[] = 'oldimage=' . urlencode( $img );
- $row .= $this->skin->makeKnownLinkObj(
- $this->title,
- wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
- implode( '&', $q )
- );
+ # Link to remove from history
+ if( $wgUser->isAllowed( 'delete' ) ) {
+ $q = array();
+ $q[] = 'action=delete';
+ if( !$iscur )
+ $q[] = 'oldimage=' . urlencode( $img );
+ $row .= $this->skin->makeKnownLinkObj(
+ $this->title,
+ wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
+ implode( '&', $q )
+ );
+ }
+ # Link to hide content
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ if( $wgUser->isAllowed('delete') ) {
+ $row .= '<br/>';
+ }
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ // If file is top revision or locked from this user, don't link
+ if( $iscur || !$file->userCan(File::DELETED_RESTRICTED) ) {
+ $del = wfMsgHtml( 'rev-delundel' );
+ } else {
+ // If the file was hidden, link to sha-1
+ list($ts,$name) = explode('!',$img,2);
+ $del = $this->skin->makeKnownLinkObj( $revdel, wfMsg( 'rev-delundel' ),
+ 'target=' . urlencode( $wgTitle->getPrefixedText() ) .
+ '&oldimage=' . urlencode( $ts ) );
+ // Bolden oversighted content
+ if( $file->isDeleted(File::DELETED_RESTRICTED) )
+ $del = "<strong>$del</strong>";
+ }
+ $row .= "<tt style='white-space: nowrap;'><small>$del</small></tt>";
+ }
$row .= '</td>';
}
@@ -600,47 +877,73 @@ class ImageHistoryList {
if( $iscur ) {
$row .= wfMsgHtml( 'filehist-current' );
} elseif( $local && $wgUser->isLoggedIn() && $this->title->userCan( 'edit' ) ) {
- $q = array();
- $q[] = 'action=revert';
- $q[] = 'oldimage=' . urlencode( $img );
- $q[] = 'wpEditToken=' . urlencode( $wgUser->editToken( $img ) );
- $row .= $this->skin->makeKnownLinkObj(
- $this->title,
- wfMsgHtml( 'filehist-revert' ),
- implode( '&', $q )
- );
+ if( $file->isDeleted(File::DELETED_FILE) ) {
+ $row .= wfMsgHtml('filehist-revert');
+ } else {
+ $q = array();
+ $q[] = 'action=revert';
+ $q[] = 'oldimage=' . urlencode( $img );
+ $q[] = 'wpEditToken=' . urlencode( $wgUser->editToken( $img ) );
+ $row .= $this->skin->makeKnownLinkObj( $this->title,
+ wfMsgHtml( 'filehist-revert' ),
+ implode( '&', $q ) );
+ }
}
$row .= '</td>';
// Date/time and image link
- $row .= '<td>';
- $url = $iscur ? $this->img->getUrl() : $this->img->getArchiveUrl( $img );
- $row .= Xml::element(
- 'a',
- array( 'href' => $url ),
- $wgLang->timeAndDate( $timestamp, true )
- );
- $row .= '</td>';
+ if( $file->getTimestamp() === $this->img->getTimestamp() ) {
+ $selected = "class='filehistory-selected'";
+ }
+ $row .= "<td $selected style='white-space: nowrap;'>";
+ if( !$file->userCan(File::DELETED_FILE) ) {
+ # Don't link to unviewable files
+ $row .= '<span class="history-deleted">' . $wgLang->timeAndDate( $timestamp, true ) . '</span>';
+ } else if( $file->isDeleted(File::DELETED_FILE) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ # Make a link to review the image
+ $url = $this->skin->makeKnownLinkObj( $revdel, $wgLang->timeAndDate( $timestamp, true ),
+ "target=".$wgTitle->getPrefixedText()."&file=$sha1.".$this->current->getExtension() );
+ $row .= '<span class="history-deleted">'.$url.'</span>';
+ } else {
+ $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
+ $row .= Xml::element( 'a', array( 'href' => $url ), $wgLang->timeAndDate( $timestamp, true ) );
+ }
+
+ $row .= "</td><td>";
+
+ // Image dimensions
+ $row .= htmlspecialchars( $dims );
+
+ // File size
+ $row .= " <span style='white-space: nowrap;'>(" . $this->skin->formatSize( $size ) . ')</span>';
// Uploading user
- $row .= '<td>';
+ $row .= '</td><td>';
if( $local ) {
- $row .= $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
+ // Hide deleted usernames
+ if( $file->isDeleted(File::DELETED_USER) ) {
+ $row .= '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+ } else {
+ $row .= $this->skin->userLink( $user, $usertext ) . " <span style='white-space: nowrap;'>" .
+ $this->skin->userToolLinks( $user, $usertext ) . "</span>";
+ }
} else {
$row .= htmlspecialchars( $usertext );
}
- $row .= '</td>';
+ $row .= '</td><td>';
- // Image dimensions
- $row .= '<td>' . htmlspecialchars( $dims ) . '</td>';
-
- // File size
- $row .= '<td class="mw-imagepage-filesize">' . $this->skin->formatSize( $size ) . '</td>';
+ // Don't show deleted descriptions
+ if ( $file->isDeleted(File::DELETED_COMMENT) ) {
+ $row .= '<span class="history-deleted">' . wfMsgHtml('rev-deleted-comment') . '</span>';
+ } else {
+ $row .= $this->skin->commentBlock( $description, $this->title );
+ }
+ $row .= '</td>';
- // Comment
- $row .= '<td>' . $this->skin->formatComment( $description, $this->title ) . '</td>';
+ wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
+ $classAttr = $rowClass ? " class='$rowClass'" : "";
- return "<tr>{$row}</tr>\n";
+ return "<tr{$classAttr}>{$row}</tr>\n";
}
-
}
diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php
index 8948ddc6..da9b6fd6 100644
--- a/includes/ImageQueryPage.php
+++ b/includes/ImageQueryPage.php
@@ -4,8 +4,7 @@
* Variant of QueryPage which uses a gallery to output results, thus
* suited for reports generating images
*
- * @package MediaWiki
- * @addtogroup SpecialPage
+ * @ingroup SpecialPage
* @author Rob Church <robchur@gmail.com>
*/
class ImageQueryPage extends QueryPage {
@@ -64,5 +63,3 @@ class ImageQueryPage extends QueryPage {
}
}
-
-
diff --git a/includes/JobQueue.php b/includes/JobQueue.php
index 5cec3106..8bfd1b3e 100644
--- a/includes/JobQueue.php
+++ b/includes/JobQueue.php
@@ -1,4 +1,7 @@
<?php
+/**
+ * @defgroup JobQueue JobQueue
+ */
if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point\n" );
@@ -6,6 +9,8 @@ if ( !defined( 'MEDIAWIKI' ) ) {
/**
* Class to both describe a background job and handle jobs.
+ *
+ * @ingroup JobQueue
*/
abstract class Job {
var $command,
@@ -37,8 +42,8 @@ abstract class Job {
*/
/**
- * Pop a job of a certain type. This tries less hard than pop() to
- * actually find a job; it may be adversely affected by concurrent job
+ * Pop a job of a certain type. This tries less hard than pop() to
+ * actually find a job; it may be adversely affected by concurrent job
* runners.
*/
static function pop_type($type) {
@@ -78,7 +83,7 @@ abstract class Job {
/**
* Pop a job off the front of the queue
- * @static
+ *
* @param $offset Number of jobs to skip
* @return Job or false if there's no jobs
*/
@@ -87,11 +92,11 @@ abstract class Job {
$dbr = wfGetDB( DB_SLAVE );
- /* Get a job from the slave, start with an offset,
+ /* Get a job from the slave, start with an offset,
scan full set afterwards, avoid hitting purged rows
- NB: If random fetch previously was used, offset
- will always be ahead of few entries
+ NB: If random fetch previously was used, offset
+ will always be ahead of few entries
*/
$row = $dbr->selectRow( 'job', '*', "job_id >= ${offset}", __METHOD__,
@@ -158,7 +163,10 @@ abstract class Job {
$job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
// Remove any duplicates it may have later in the queue
+ // Deadlock prone section
+ $dbw->begin();
$dbw->delete( 'job', $job->insertFields(), __METHOD__ );
+ $dbw->commit();
wfProfileOut( __METHOD__ );
return $job;
@@ -167,10 +175,10 @@ abstract class Job {
/**
* Create the appropriate object to handle a specific job
*
- * @param string $command Job command
- * @param Title $title Associated title
- * @param array $params Job parameters
- * @param int $id Job identifier
+ * @param $command String: Job command
+ * @param $title Title: Associated title
+ * @param $params Array: Job parameters
+ * @param $id Int: Job identifier
* @return Job
*/
static function factory( $command, $title, $params = false, $id = 0 ) {
@@ -181,7 +189,7 @@ abstract class Job {
}
throw new MWException( "Invalid job command `{$command}`" );
}
-
+
static function makeBlob( $params ) {
if ( $params !== false ) {
return serialize( $params );
@@ -208,12 +216,23 @@ abstract class Job {
* @param $jobs array of Job objects
*/
static function batchInsert( $jobs ) {
- if( count( $jobs ) ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
- foreach( $jobs as $job ) {
- $rows[] = $job->insertFields();
+ if( !count( $jobs ) ) {
+ return;
+ }
+ $dbw = wfGetDB( DB_MASTER );
+ $rows = array();
+ foreach( $jobs as $job ) {
+ $rows[] = $job->insertFields();
+ if ( count( $rows ) >= 50 ) {
+ # Do a small transaction to avoid slave lag
+ $dbw->begin();
+ $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
+ $dbw->commit();
+ $rows = array();
}
+ }
+ if ( $rows ) {
+ $dbw->begin();
$dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
$dbw->commit();
}
@@ -283,9 +302,11 @@ abstract class Job {
}
}
+ protected function setLastError( $error ) {
+ $this->error = $error;
+ }
+
function getLastError() {
return $this->error;
}
}
-
-
diff --git a/includes/Licenses.php b/includes/Licenses.php
index 6a034468..e76ac23c 100644
--- a/includes/Licenses.php
+++ b/includes/Licenses.php
@@ -1,8 +1,8 @@
<?php
/**
* A License class for use on Special:Upload
- *
- * @addtogroup SpecialPage
+ *
+ * @ingroup SpecialPage
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -172,4 +172,3 @@ class License {
$this->text = strrev( $text );
}
}
-
diff --git a/includes/LinkBatch.php b/includes/LinkBatch.php
index db1114c9..bdc4b43a 100644
--- a/includes/LinkBatch.php
+++ b/includes/LinkBatch.php
@@ -4,7 +4,7 @@
* Class representing a list of titles
* The execute() method checks them all for existence and adds them to a LinkCache object
*
- * @addtogroup Cache
+ * @ingroup Cache
*/
class LinkBatch {
/**
@@ -18,7 +18,7 @@ class LinkBatch {
}
}
- function addObj( $title ) {
+ public function addObj( $title ) {
if ( is_object( $title ) ) {
$this->add( $title->getNamespace(), $title->getDBkey() );
} else {
@@ -26,7 +26,7 @@ class LinkBatch {
}
}
- function add( $ns, $dbkey ) {
+ public function add( $ns, $dbkey ) {
if ( $ns < 0 ) {
return;
}
@@ -41,21 +41,21 @@ class LinkBatch {
* Set the link list to a given 2-d array
* First key is the namespace, second is the DB key, value arbitrary
*/
- function setArray( $array ) {
+ public function setArray( $array ) {
$this->data = $array;
}
/**
* Returns true if no pages have been added, false otherwise.
*/
- function isEmpty() {
+ public function isEmpty() {
return ($this->getSize() == 0);
}
/**
* Returns the size of the batch.
*/
- function getSize() {
+ public function getSize() {
return count( $this->data );
}
@@ -63,8 +63,8 @@ class LinkBatch {
* Do the query and add the results to the LinkCache object
* Return an array mapping PDBK to ID
*/
- function execute() {
- $linkCache =& LinkCache::singleton();
+ public function execute() {
+ $linkCache = LinkCache::singleton();
return $this->executeInto( $linkCache );
}
@@ -72,13 +72,22 @@ class LinkBatch {
* Do the query and add the results to a given LinkCache object
* Return an array mapping PDBK to ID
*/
- function executeInto( &$cache ) {
- $fname = 'LinkBatch::executeInto';
- wfProfileIn( $fname );
- // Do query
+ protected function executeInto( &$cache ) {
+ wfProfileIn( __METHOD__ );
$res = $this->doQuery();
+ $ids = $this->addResultToCache( $cache, $res );
+ wfProfileOut( __METHOD__ );
+ return $ids;
+ }
+
+ /**
+ * Add a ResultWrapper containing IDs and titles to a LinkCache object.
+ * As normal, titles will go into the static Title cache field.
+ * This function *also* stores extra fields of the title used for link
+ * parsing to avoid extra DB queries.
+ */
+ public function addResultToCache( $cache, $res ) {
if ( !$res ) {
- wfProfileOut( $fname );
return array();
}
@@ -88,11 +97,10 @@ class LinkBatch {
$remaining = $this->data;
while ( $row = $res->fetchObject() ) {
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $cache->addGoodLinkObj( $row->page_id, $title );
+ $cache->addGoodLinkObj( $row->page_id, $title, $row->page_len, $row->page_is_redirect );
$ids[$title->getPrefixedDBkey()] = $row->page_id;
unset( $remaining[$row->page_namespace][$row->page_title] );
}
- $res->free();
// The remaining links in $data are bad links, register them as such
foreach ( $remaining as $ns => $dbkeys ) {
@@ -102,20 +110,17 @@ class LinkBatch {
$ids[$title->getPrefixedDBkey()] = 0;
}
}
- wfProfileOut( $fname );
return $ids;
}
/**
* Perform the existence test query, return a ResultWrapper with page_id fields
*/
- function doQuery() {
- $fname = 'LinkBatch::doQuery';
-
+ public function doQuery() {
if ( $this->isEmpty() ) {
return false;
}
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
// Construct query
// This is very similar to Parser::replaceLinkHolders
@@ -123,26 +128,25 @@ class LinkBatch {
$page = $dbr->tableName( 'page' );
$set = $this->constructSet( 'page', $dbr );
if ( $set === false ) {
- wfProfileOut( $fname );
+ wfProfileOut( __METHOD__ );
return false;
}
- $sql = "SELECT page_id, page_namespace, page_title FROM $page WHERE $set";
+ $sql = "SELECT page_id, page_namespace, page_title, page_len, page_is_redirect FROM $page WHERE $set";
// Do query
- $res = new ResultWrapper( $dbr, $dbr->query( $sql, $fname ) );
- wfProfileOut( $fname );
+ $res = new ResultWrapper( $dbr, $dbr->query( $sql, __METHOD__ ) );
+ wfProfileOut( __METHOD__ );
return $res;
}
/**
* Construct a WHERE clause which will match all the given titles.
- * Give the appropriate table's field name prefix ('page', 'pl', etc).
*
- * @param $prefix String: ??
+ * @param string $prefix the appropriate table's field name prefix ('page', 'pl', etc)
* @return string
* @public
*/
- function constructSet( $prefix, &$db ) {
+ public function constructSet( $prefix, &$db ) {
$first = true;
$firstTitle = true;
$sql = '';
@@ -156,7 +160,7 @@ class LinkBatch {
} else {
$sql .= ' OR ';
}
-
+
if (count($dbkeys)==1) { // avoid multiple-reference syntax if simple equality can be used
$singleKey = array_keys($dbkeys);
$sql .= "({$prefix}_namespace=$ns AND {$prefix}_title=".
@@ -164,7 +168,7 @@ class LinkBatch {
")";
} else {
$sql .= "({$prefix}_namespace=$ns AND {$prefix}_title IN (";
-
+
$firstTitle = true;
foreach( $dbkeys as $dbkey => $unused ) {
if ( $firstTitle ) {
@@ -185,5 +189,3 @@ class LinkBatch {
}
}
}
-
-
diff --git a/includes/LinkCache.php b/includes/LinkCache.php
index 7c49d88e..79727615 100644
--- a/includes/LinkCache.php
+++ b/includes/LinkCache.php
@@ -1,13 +1,13 @@
<?php
/**
* Cache for article titles (prefixed DB keys) and ids linked from one source
- *
- * @addtogroup Cache
+ *
+ * @ingroup Cache
*/
class LinkCache {
// Increment $mClassVer whenever old serialized versions of this class
// becomes incompatible with the new version.
- /* private */ var $mClassVer = 3;
+ /* private */ var $mClassVer = 4;
/* private */ var $mPageLinks;
/* private */ var $mGoodLinks, $mBadLinks;
@@ -28,21 +28,18 @@ class LinkCache {
$this->mForUpdate = false;
$this->mPageLinks = array();
$this->mGoodLinks = array();
+ $this->mGoodLinkFields = array();
$this->mBadLinks = array();
}
- /* private */ function getKey( $title ) {
- return wfMemcKey( 'lc', 'title', $title );
- }
-
/**
* General accessor to get/set whether SELECT FOR UPDATE should be used
*/
- function forUpdate( $update = NULL ) {
+ public function forUpdate( $update = NULL ) {
return wfSetVar( $this->mForUpdate, $update );
}
- function getGoodLinkID( $title ) {
+ public function getGoodLinkID( $title ) {
if ( array_key_exists( $title, $this->mGoodLinks ) ) {
return $this->mGoodLinks[$title];
} else {
@@ -50,17 +47,41 @@ class LinkCache {
}
}
- function isBadLink( $title ) {
+ /**
+ * Get a field of a title object from cache.
+ * If this link is not good, it will return NULL.
+ * @param Title $title
+ * @param string $field ('length','redirect')
+ * @return mixed
+ */
+ public function getGoodLinkFieldObj( $title, $field ) {
+ $dbkey = $title->getPrefixedDbKey();
+ if ( array_key_exists( $dbkey, $this->mGoodLinkFields ) ) {
+ return $this->mGoodLinkFields[$dbkey][$field];
+ } else {
+ return NULL;
+ }
+ }
+
+ public function isBadLink( $title ) {
return array_key_exists( $title, $this->mBadLinks );
}
- function addGoodLinkObj( $id, $title ) {
+ /**
+ * Add a link for the title to the link cache
+ * @param int $id
+ * @param Title $title
+ * @param int $len
+ * @param int $redir
+ */
+ public function addGoodLinkObj( $id, $title, $len = -1, $redir = NULL ) {
$dbkey = $title->getPrefixedDbKey();
$this->mGoodLinks[$dbkey] = $id;
+ $this->mGoodLinkFields[$dbkey] = array( 'length' => $len, 'redirect' => $redir );
$this->mPageLinks[$dbkey] = $title;
}
- function addBadLinkObj( $title ) {
+ public function addBadLinkObj( $title ) {
$dbkey = $title->getPrefixedDbKey();
if ( ! $this->isBadLink( $dbkey ) ) {
$this->mBadLinks[$dbkey] = 1;
@@ -68,30 +89,28 @@ class LinkCache {
}
}
- function clearBadLink( $title ) {
+ public function clearBadLink( $title ) {
unset( $this->mBadLinks[$title] );
- $this->clearLink( $title );
}
- function clearLink( $title ) {
- global $wgMemc, $wgLinkCacheMemcached;
- if( $wgLinkCacheMemcached )
- $wgMemc->delete( $this->getKey( $title ) );
- }
+ /* obsolete, for old $wgLinkCacheMemcached stuff */
+ public function clearLink( $title ) {}
- function getPageLinks() { return $this->mPageLinks; }
- function getGoodLinks() { return $this->mGoodLinks; }
- function getBadLinks() { return array_keys( $this->mBadLinks ); }
+ public function getPageLinks() { return $this->mPageLinks; }
+ public function getGoodLinks() { return $this->mGoodLinks; }
+ public function getBadLinks() { return array_keys( $this->mBadLinks ); }
/**
* Add a title to the link cache, return the page_id or zero if non-existent
* @param $title String: title to add
+ * @param $len int, page size
+ * @param $redir bool, is redirect?
* @return integer
*/
- function addLink( $title ) {
+ public function addLink( $title, $len = -1, $redir = NULL ) {
$nt = Title::newFromDBkey( $title );
if( $nt ) {
- return $this->addLinkObj( $nt );
+ return $this->addLinkObj( $nt, $len, $redir );
} else {
return 0;
}
@@ -100,18 +119,20 @@ class LinkCache {
/**
* Add a title to the link cache, return the page_id or zero if non-existent
* @param $nt Title to add.
+ * @param $len int, page size
+ * @param $redir bool, is redirect?
* @return integer
*/
- function addLinkObj( &$nt ) {
- global $wgMemc, $wgLinkCacheMemcached, $wgAntiLockFlags;
+ public function addLinkObj( &$nt, $len = -1, $redirect = NULL ) {
+ global $wgAntiLockFlags, $wgProfiler;
+
$title = $nt->getPrefixedDBkey();
if ( $this->isBadLink( $title ) ) { return 0; }
$id = $this->getGoodLinkID( $title );
if ( 0 != $id ) { return $id; }
$fname = 'LinkCache::addLinkObj';
- global $wgProfiling, $wgProfiler;
- if ( $wgProfiling && isset( $wgProfiler ) ) {
+ if ( isset( $wgProfiler ) ) {
$fname .= ' (' . $wgProfiler->getCurrentSection() . ')';
}
@@ -125,36 +146,32 @@ class LinkCache {
return 0;
}
- $id = NULL;
- if( $wgLinkCacheMemcached )
- $id = $wgMemc->get( $key = $this->getKey( $title ) );
- if( ! is_integer( $id ) ) {
- if ( $this->mForUpdate ) {
- $db = wfGetDB( DB_MASTER );
- if ( !( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) ) {
- $options = array( 'FOR UPDATE' );
- } else {
- $options = array();
- }
+ # Some fields heavily used for linking...
+ if ( $this->mForUpdate ) {
+ $db = wfGetDB( DB_MASTER );
+ if ( !( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) ) {
+ $options = array( 'FOR UPDATE' );
} else {
- $db = wfGetDB( DB_SLAVE );
$options = array();
}
-
- $id = $db->selectField( 'page', 'page_id',
- array( 'page_namespace' => $ns, 'page_title' => $t ),
- $fname, $options );
- if ( !$id ) {
- $id = 0;
- }
- if( $wgLinkCacheMemcached )
- $wgMemc->add( $key, $id, 3600*24 );
+ } else {
+ $db = wfGetDB( DB_SLAVE );
+ $options = array();
}
+ $s = $db->selectRow( 'page',
+ array( 'page_id', 'page_len', 'page_is_redirect' ),
+ array( 'page_namespace' => $ns, 'page_title' => $t ),
+ $fname, $options );
+ # Set fields...
+ $id = $s ? $s->page_id : 0;
+ $len = $s ? $s->page_len : -1;
+ $redirect = $s ? $s->page_is_redirect : 0;
+
if( 0 == $id ) {
$this->addBadLinkObj( $nt );
} else {
- $this->addGoodLinkObj( $id, $nt );
+ $this->addGoodLinkObj( $id, $nt, $len, $redirect );
}
wfProfileOut( $fname );
return $id;
@@ -163,10 +180,10 @@ class LinkCache {
/**
* Clears cache
*/
- function clear() {
+ public function clear() {
$this->mPageLinks = array();
$this->mGoodLinks = array();
+ $this->mGoodLinkFields = array();
$this->mBadLinks = array();
}
}
-
diff --git a/includes/LinkFilter.php b/includes/LinkFilter.php
index ced76d75..dc4c1256 100644
--- a/includes/LinkFilter.php
+++ b/includes/LinkFilter.php
@@ -106,4 +106,3 @@ class LinkFilter {
return $like;
}
}
-
diff --git a/includes/Linker.php b/includes/Linker.php
index 4b092cf9..32c506a4 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -1,15 +1,12 @@
<?php
/**
- * Split off some of the internal bits from Skin.php.
- * These functions are used for primarily page content:
- * links, embedded images, table of contents. Links are
- * also used in the skin.
- * For the moment, Skin is a descendent class of Linker.
- * In the future, it should probably be further split
- * so that ever other bit of the wiki doesn't have to
- * go loading up Skin to get at it.
+ * Split off some of the internal bits from Skin.php. These functions are used
+ * for primarily page content: links, embedded images, table of contents. Links
+ * are also used in the skin. For the moment, Skin is a descendent class of
+ * Linker. In the future, it should probably be further split so that every
+ * other bit of the wiki doesn't have to go loading up Skin to get at it.
*
- * @addtogroup Skins
+ * @ingroup Skins
*/
class Linker {
@@ -23,72 +20,110 @@ class Linker {
/**
* @deprecated
*/
- function postParseLinkColour( $s = NULL ) {
- return NULL;
+ function postParseLinkColour( $s = null ) {
+ return null;
}
- /** @todo document */
- function getExternalLinkAttributes( $link, $text, $class='' ) {
- $link = htmlspecialchars( $link );
-
- $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
-
- $r .= " title=\"{$link}\"";
- return $r;
+ /**
+ * Get the appropriate HTML attributes to add to the "a" element of an ex-
+ * ternal link, as created by [wikisyntax].
+ *
+ * @param string $title The (unescaped) title text for the link
+ * @param string $unused Unused
+ * @param string $class The contents of the class attribute; if an empty
+ * string is passed, which is the default value, defaults to 'external'.
+ */
+ function getExternalLinkAttributes( $title, $unused = null, $class='' ) {
+ return $this->getLinkAttributesInternal( $title, $class, 'external' );
}
- function getInterwikiLinkAttributes( $link, $text, $class='' ) {
+ /**
+ * Get the appropriate HTML attributes to add to the "a" element of an in-
+ * terwiki link.
+ *
+ * @param string $title The title text for the link, URL-encoded (???) but
+ * not HTML-escaped
+ * @param string $unused Unused
+ * @param string $class The contents of the class attribute; if an empty
+ * string is passed, which is the default value, defaults to 'external'.
+ */
+ function getInterwikiLinkAttributes( $title, $unused = null, $class='' ) {
global $wgContLang;
- $link = urldecode( $link );
- $link = $wgContLang->checkTitleEncoding( $link );
- $link = preg_replace( '/[\\x00-\\x1f]/', ' ', $link );
- $link = htmlspecialchars( $link );
+ # FIXME: We have a whole bunch of handling here that doesn't happen in
+ # getExternalLinkAttributes, why?
+ $title = urldecode( $title );
+ $title = $wgContLang->checkTitleEncoding( $title );
+ $title = preg_replace( '/[\\x00-\\x1f]/', ' ', $title );
- $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
+ return $this->getLinkAttributesInternal( $title, $class, 'external' );
+ }
- $r .= " title=\"{$link}\"";
- return $r;
+ /**
+ * Get the appropriate HTML attributes to add to the "a" element of an in-
+ * ternal link.
+ *
+ * @param string $title The title text for the link, URL-encoded (???) but
+ * not HTML-escaped
+ * @param string $unused Unused
+ * @param string $class The contents of the class attribute, default none
+ */
+ function getInternalLinkAttributes( $title, $unused = null, $class='' ) {
+ $title = urldecode( $title );
+ $title = str_replace( '_', ' ', $title );
+ return $this->getLinkAttributesInternal( $title, $class );
}
- /** @todo document */
- function getInternalLinkAttributes( $link, $text, $class='' ) {
- $link = urldecode( $link );
- $link = str_replace( '_', ' ', $link );
- $link = htmlspecialchars( $link );
- $r = ($class != '') ? ' class="' . htmlspecialchars( $class ) . '"' : '';
- $r .= " title=\"{$link}\"";
- return $r;
+ /**
+ * Get the appropriate HTML attributes to add to the "a" element of an in-
+ * ternal link, given the Title object for the page we want to link to.
+ *
+ * @param Title $nt The Title object
+ * @param string $unused Unused
+ * @param string $class The contents of the class attribute, default none
+ * @param mixed $title Optional (unescaped) string to use in the title
+ * attribute; if false, default to the name of the page we're linking to
+ */
+ function getInternalLinkAttributesObj( $nt, $unused = null, $class = '', $title = false ) {
+ if( $title === false ) {
+ $title = $nt->getPrefixedText();
+ }
+ return $this->getLinkAttributesInternal( $title, $class );
}
/**
- * @param $nt Title object.
- * @param $text String: FIXME
- * @param $class String: CSS class of the link, default ''.
+ * Common code for getLinkAttributesX functions
*/
- function getInternalLinkAttributesObj( &$nt, $text, $class='' ) {
- $r = ($class != '') ? ' class="' . htmlspecialchars( $class ) . '"' : '';
- $r .= ' title="' . $nt->getEscapedText() . '"';
+ private function getLinkAttributesInternal( $title, $class, $classDefault = false ) {
+ $title = htmlspecialchars( $title );
+ if( $class === '' and $classDefault !== false ) {
+ # FIXME: Parameter defaults the hard way! We should just have
+ # $class = 'external' or whatever as the default in the externally-
+ # exposed functions, not $class = ''.
+ $class = $classDefault;
+ }
+ $class = htmlspecialchars( $class );
+ $r = '';
+ if( $class !== '' ) {
+ $r .= " class=\"$class\"";
+ }
+ $r .= " title=\"$title\"";
return $r;
}
/**
* Return the CSS colour of a known link
*
- * @param mixed $s
+ * @param Title $t
* @param integer $threshold user defined threshold
* @return string CSS class
*/
- function getLinkColour( $s, $threshold ) {
- if( $s === false ) {
- return '';
- }
-
+ function getLinkColour( $t, $threshold ) {
$colour = '';
- if ( !empty( $s->page_is_redirect ) ) {
+ if ( $t->isRedirect() ) {
# Page is a redirect
$colour = 'mw-redirect';
- } elseif ( $threshold > 0 && $s->page_len < $threshold && Namespace::isContent( $s->page_namespace ) ) {
+ } elseif ( $threshold > 0 && $t->getLength() < $threshold && MWNamespace::isContent( $t->getNamespace() ) ) {
# Page is a stub
$colour = 'stub';
}
@@ -123,7 +158,7 @@ class Linker {
/**
* This function is a shortcut to makeKnownLinkObj(Title::newFromText($title),...). Do not call
* it if you already have a title object handy. See makeKnownLinkObj for further documentation.
- *
+ *
* @param $title String: the text of the title
* @param $text String: link text
* @param $query String: optional query part
@@ -144,7 +179,7 @@ class Linker {
/**
* This function is a shortcut to makeBrokenLinkObj(Title::newFromText($title),...). Do not call
* it if you already have a title object handy. See makeBrokenLinkObj for further documentation.
- *
+ *
* @param string $title The text of the title
* @param string $text Link text
* @param string $query Optional query part
@@ -164,10 +199,10 @@ class Linker {
/**
* @deprecated use makeColouredLinkObj
- *
+ *
* This function is a shortcut to makeStubLinkObj(Title::newFromText($title),...). Do not call
* it if you already have a title object handy. See makeStubLinkObj for further documentation.
- *
+ *
* @param $title String: the text of the title
* @param $text String: link text
* @param $query String: optional query part
@@ -189,7 +224,7 @@ class Linker {
* Make a link for a title which may or may not be in the database. If you need to
* call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each
* call to this will result in a DB query.
- *
+ *
* @param $nt Title: the title object to make the link from, e.g. from
* Title::newFromText.
* @param $text String: link text
@@ -228,7 +263,7 @@ class Linker {
wfProfileOut( __METHOD__ );
return $t;
} elseif ( $nt->isAlwaysKnown() ) {
- # Image links, special page links and self-links with fragements are always known.
+ # Image links, special page links and self-links with fragments are always known.
$retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
} else {
wfProfileIn( __METHOD__.'-immediate' );
@@ -252,15 +287,8 @@ class Linker {
} else {
$colour = '';
if ( $nt->isContentPage() ) {
- # FIXME: This is stupid, we should combine this query with
- # the Title::getArticleID() query above.
$threshold = $wgUser->getOption('stubthreshold');
- $dbr = wfGetDB( DB_SLAVE );
- $s = $dbr->selectRow(
- array( 'page' ),
- array( 'page_len', 'page_is_redirect', 'page_namespace' ),
- array( 'page_id' => $aid ), __METHOD__ ) ;
- $colour = $this->getLinkColour( $s, $threshold );
+ $colour = $this->getLinkColour( $nt, $threshold );
}
$retVal = $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix );
}
@@ -284,15 +312,17 @@ class Linker {
* @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead
* @return the a-element
*/
- function makeKnownLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
+ function makeKnownLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
wfProfileIn( __METHOD__ );
- if ( !$nt instanceof Title ) {
+ if ( !$title instanceof Title ) {
# Fail gracefully
wfProfileOut( __METHOD__ );
return "<!-- ERROR -->{$prefix}{$text}{$trail}";
}
+ $nt = $this->normaliseSpecialPage( $title );
+
$u = $nt->escapeLocalURL( $query );
if ( $nt->getFragment() != '' ) {
if( $nt->getPrefixedDbkey() == '' ) {
@@ -320,7 +350,7 @@ class Linker {
/**
* Make a red link to the edit page of a given title.
- *
+ *
* @param $nt Title object of the target page
* @param $text String: Link text
* @param $query String: Optional query part
@@ -328,30 +358,35 @@ class Linker {
* be included in the link text. Other characters will be appended after
* the end of the link.
*/
- function makeBrokenLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
+ function makeBrokenLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' ) {
wfProfileIn( __METHOD__ );
- if ( !$nt instanceof Title ) {
+ if ( !$title instanceof Title ) {
# Fail gracefully
wfProfileOut( __METHOD__ );
return "<!-- ERROR -->{$prefix}{$text}{$trail}";
}
+ $nt = $this->normaliseSpecialPage( $title );
+
if( $nt->getNamespace() == NS_SPECIAL ) {
$q = $query;
} else if ( '' == $query ) {
- $q = 'action=edit';
+ $q = 'action=edit&redlink=1';
} else {
- $q = 'action=edit&'.$query;
+ $q = 'action=edit&redlink=1&'.$query;
}
$u = $nt->escapeLocalURL( $q );
+ $titleText = $nt->getPrefixedText();
if ( '' == $text ) {
- $text = htmlspecialchars( $nt->getPrefixedText() );
+ $text = htmlspecialchars( $titleText );
}
- $style = $this->getInternalLinkAttributesObj( $nt, $text, 'new' );
-
+ $titleAttr = wfMsg( 'red-link-title', $titleText );
+ $style = $this->getInternalLinkAttributesObj( $nt, $text, 'new', $titleAttr );
list( $inside, $trail ) = Linker::splitTrail( $trail );
+
+ wfRunHooks( 'BrokenLink', array( &$this, $nt, $query, &$u, &$style, &$prefix, &$text, &$inside, &$trail ) );
$s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
wfProfileOut( __METHOD__ );
@@ -360,9 +395,9 @@ class Linker {
/**
* @deprecated use makeColouredLinkObj
- *
+ *
* Make a brown link to a short article.
- *
+ *
* @param $nt Title object of the target page
* @param $text String: link text
* @param $query String: optional query part
@@ -376,7 +411,7 @@ class Linker {
/**
* Make a coloured link.
- *
+ *
* @param $nt Title object of the target page
* @param $colour Integer: colour of the link
* @param $text String: link text
@@ -412,7 +447,7 @@ class Linker {
return $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix );
}
- /**
+ /**
* Make appropriate markup for a link to the current article. This is currently rendered
* as the bold link text. The calling sequence is the same as the other make*LinkObj functions,
* despite $query not being used.
@@ -425,6 +460,16 @@ class Linker {
return "<strong class=\"selflink\">{$prefix}{$text}{$inside}</strong>{$trail}";
}
+ function normaliseSpecialPage( Title $title ) {
+ if ( $title->getNamespace() == NS_SPECIAL ) {
+ list( $name, $subpage ) = SpecialPage::resolveAliasWithSubpage( $title->getDBkey() );
+ if ( !$name ) return $title;
+ return SpecialPage::getTitleFor( $name, $subpage );
+ } else {
+ return $title;
+ }
+ }
+
/** @todo document */
function fnamePart( $url ) {
$basename = strrchr( $url, '/' );
@@ -433,7 +478,7 @@ class Linker {
} else {
$basename = substr( $basename, 1 );
}
- return htmlspecialchars( $basename );
+ return $basename;
}
/** Obsolete alias */
@@ -446,11 +491,19 @@ class Linker {
if ( '' == $alt ) {
$alt = $this->fnamePart( $url );
}
- $s = '<img src="'.$url.'" alt="'.$alt.'" />';
- return $s;
+ $img = '';
+ $success = wfRunHooks('LinkerMakeExternalImage', array( &$url, &$alt, &$img ) );
+ if(!$success) {
+ wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}", true);
+ return $img;
+ }
+ return Xml::element( 'img',
+ array(
+ 'src' => $url,
+ 'alt' => $alt ) );
}
- /**
+ /**
* Creates the HTML source for images
* @deprecated use makeImageLink2
*
@@ -490,12 +543,14 @@ class Linker {
}
/**
- * Make an image link
+ * Given parameters derived from [[Image:Foo|options...]], generate the
+ * HTML that that syntax inserts in the page.
+ *
* @param Title $title Title object
* @param File $file File object, or false if it doesn't exist
*
* @param array $frameParams Associative array of parameters external to the media handler.
- * Boolean parameters are indicated by presence or absence, the value is arbitrary and
+ * Boolean parameters are indicated by presence or absence, the value is arbitrary and
* will often be false.
* thumbnail If present, downscale and frame
* manualthumb Image name to use as a thumbnail, instead of automatic scaling
@@ -505,16 +560,24 @@ class Linker {
* upright_factor Fudge factor for "upright" tweak (default 0.75)
* border If present, show a border around the image
* align Horizontal alignment (left, right, center, none)
- * valign Vertical alignment (baseline, sub, super, top, text-top, middle,
+ * valign Vertical alignment (baseline, sub, super, top, text-top, middle,
* bottom, text-bottom)
* alt Alternate text for image (i.e. alt attribute). Plain text.
* caption HTML for image caption.
*
- * @param array $handlerParams Associative array of media handler parameters, to be passed
- * to transform(). Typical keys are "width" and "page".
+ * @param array $handlerParams Associative array of media handler parameters, to be passed
+ * to transform(). Typical keys are "width" and "page".
* @param string $time, timestamp of the file, set as false for current
+ * @param string $query, query params for desc url
+ * @return string HTML for an image, with links, wrappers, etc.
*/
- function makeImageLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false ) {
+ function makeImageLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false, $query = "" ) {
+ $res = null;
+ if( !wfRunHooks( 'ImageBeforeProduceHTML', array( &$this, &$title,
+ &$file, &$frameParams, &$handlerParams, &$time, &$res ) ) ) {
+ return $res;
+ }
+
global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright;
if ( $file && !$file->allowInlineDisplay() ) {
wfDebug( __METHOD__.': '.$title->getPrefixedDBkey()." does not allow inline display\n" );
@@ -554,8 +617,8 @@ class Linker {
}
// Use width which is smaller: real image width or user preference width
// For caching health: If width scaled down due to upright parameter, round to full __0 pixel to avoid the creation of a lot of odd thumbs
- $prefWidth = isset( $fp['upright'] ) ?
- round( $wgThumbLimits[$wopt] * $fp['upright'], -1 ) :
+ $prefWidth = isset( $fp['upright'] ) ?
+ round( $wgThumbLimits[$wopt] * $fp['upright'], -1 ) :
$wgThumbLimits[$wopt];
if ( $hp['width'] <= 0 || $prefWidth < $hp['width'] ) {
$hp['width'] = $prefWidth;
@@ -575,7 +638,7 @@ class Linker {
if ( $fp['align'] == '' ) {
$fp['align'] = $wgContLang->isRTL() ? 'left' : 'right';
}
- return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp, $time ).$postfix;
+ return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp, $time, $query ).$postfix;
}
if ( $file && isset( $fp['frameless'] ) ) {
@@ -599,6 +662,7 @@ class Linker {
} else {
$s = $thumb->toHtml( array(
'desc-link' => true,
+ 'desc-query' => $query,
'alt' => $fp['alt'],
'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false ,
'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ) );
@@ -611,11 +675,11 @@ class Linker {
/**
* Make HTML for a thumbnail including image, border and caption
- * @param Title $title
+ * @param Title $title
* @param File $file File object or false if it doesn't exist
*/
function makeThumbLinkObj( Title $title, $file, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manualthumb = "" ) {
- $frameParams = array(
+ $frameParams = array(
'alt' => $alt,
'caption' => $label,
'align' => $align
@@ -625,7 +689,7 @@ class Linker {
return $this->makeThumbLink2( $title, $file, $frameParams, $params );
}
- function makeThumbLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false ) {
+ function makeThumbLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false, $query = "" ) {
global $wgStylePath, $wgContLang;
$exists = $file && $file->exists();
@@ -639,7 +703,7 @@ class Linker {
if ( !isset( $fp['caption'] ) ) $fp['caption'] = '';
if ( empty( $hp['width'] ) ) {
- // Reduce width for upright images when parameter 'upright' is used
+ // Reduce width for upright images when parameter 'upright' is used
$hp['width'] = isset( $fp['upright'] ) ? 130 : 180;
}
$thumb = false;
@@ -678,7 +742,9 @@ class Linker {
}
}
- $query = $page ? 'page=' . urlencode( $page ) : '';
+ if( $page ) {
+ $query = $query ? '&page=' . urlencode( $page ) : 'page=' . urlencode( $page );
+ }
$url = $title->getLocalURL( $query );
$more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
@@ -694,7 +760,8 @@ class Linker {
$s .= $thumb->toHtml( array(
'alt' => $fp['alt'],
'img-class' => 'thumbimage',
- 'desc-link' => true ) );
+ 'desc-link' => true,
+ 'desc-query' => $query ) );
if ( isset( $fp['framed'] ) ) {
$zoomicon="";
} else {
@@ -729,9 +796,9 @@ class Linker {
if( $text == '' )
$text = htmlspecialchars( $title->getPrefixedText() );
$redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
- if( $redir ) {
+ if( $redir ) {
return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix );
- }
+ }
$q = 'wpDestFile=' . $title->getPartialUrl();
if( $query != '' )
$q .= '&' . $query;
@@ -750,27 +817,28 @@ class Linker {
}
/** @deprecated use Linker::makeMediaLinkObj() */
- function makeMediaLink( $name, $unused = '', $text = '' ) {
+ function makeMediaLink( $name, $unused = '', $text = '', $time = false ) {
$nt = Title::makeTitleSafe( NS_IMAGE, $name );
- return $this->makeMediaLinkObj( $nt, $text );
+ return $this->makeMediaLinkObj( $nt, $text, $time );
}
/**
* Create a direct link to a given uploaded file.
*
* @param $title Title object.
- * @param $text String: pre-sanitized HTML
+ * @param $text String: pre-sanitized HTML
+ * @param $time string: time image was created
* @return string HTML
*
* @public
* @todo Handle invalid or missing images better.
*/
- function makeMediaLinkObj( $title, $text = '' ) {
+ function makeMediaLinkObj( $title, $text = '', $time = false ) {
if( is_null( $title ) ) {
### HOTFIX. Instead of breaking, return empty string.
return $text;
} else {
- $img = wfFindFile( $title );
+ $img = wfFindFile( $title, $time );
if( $img ) {
$url = $img->getURL();
$class = 'internal';
@@ -809,6 +877,12 @@ class Linker {
if( $escape ) {
$text = htmlspecialchars( $text );
}
+ $link = '';
+ $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link ) );
+ if(!$success) {
+ wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}", true);
+ return $link;
+ }
return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
}
@@ -838,9 +912,10 @@ class Linker {
* @param string $userText User name or IP address
* @param bool $redContribsWhenNoEdits Should the contributions link be red if the user has no edits?
* @param int $flags Customisation flags (e.g. self::TOOL_LINKS_NOBLOCK)
+ * @param int $edits, user edit count (optional, for performance)
* @return string
*/
- public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0 ) {
+ public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits=null ) {
global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
$talkable = !( $wgDisableAnonTalk && 0 == $userId );
$blockable = ( $wgSysopUserBans || 0 == $userId ) && !$flags & self::TOOL_LINKS_NOBLOCK;