summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2009-02-22 13:37:51 +0100
committerPierre Schmitz <pierre@archlinux.de>2009-02-22 13:37:51 +0100
commitb9b85843572bf283f48285001e276ba7e61b63f6 (patch)
tree4c6f4571552ada9ccfb4030481dcf77308f8b254 /includes
parentd9a20acc4e789cca747ad360d87ee3f3e7aa58c1 (diff)
updated to MediaWiki 1.14.0
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxFunctions.php126
-rw-r--r--includes/AjaxResponse.php6
-rw-r--r--includes/Article.php1829
-rw-r--r--includes/AuthPlugin.php82
-rw-r--r--includes/AutoLoader.php982
-rw-r--r--includes/Autopromote.php9
-rw-r--r--includes/BagOStuff.php13
-rw-r--r--includes/Block.php480
-rw-r--r--includes/Category.php41
-rw-r--r--includes/CategoryPage.php82
-rw-r--r--includes/Categoryfinder.php10
-rw-r--r--includes/ChangesFeed.php7
-rw-r--r--includes/ChangesList.php381
-rw-r--r--includes/Credits.php300
-rw-r--r--includes/DatabaseFunctions.php26
-rw-r--r--includes/DefaultSettings.php563
-rw-r--r--includes/Defines.php20
-rw-r--r--includes/EditPage.php958
-rw-r--r--includes/Exception.php34
-rw-r--r--includes/Exif.php25
-rw-r--r--includes/Export.php288
-rw-r--r--includes/ExternalStore.php47
-rw-r--r--includes/ExternalStoreDB.php11
-rw-r--r--includes/FakeTitle.php23
-rw-r--r--includes/Feed.php44
-rw-r--r--includes/FeedUtils.php17
-rw-r--r--includes/FileDeleteForm.php66
-rw-r--r--includes/FileRevertForm.php6
-rw-r--r--includes/FileStore.php27
-rw-r--r--includes/FormOptions.php4
-rw-r--r--includes/GlobalFunctions.php445
-rw-r--r--includes/HTMLCacheUpdate.php28
-rw-r--r--includes/HTMLFileCache.php116
-rw-r--r--includes/HistoryBlob.php444
-rw-r--r--includes/HttpFunctions.php86
-rw-r--r--includes/IEContentAnalyzer.php7
-rw-r--r--includes/IP.php53
-rw-r--r--includes/ImageFunctions.php100
-rw-r--r--includes/ImageGallery.php2
-rw-r--r--includes/ImagePage.php395
-rw-r--r--includes/ImageQueryPage.php6
-rw-r--r--includes/Import.php1133
-rw-r--r--includes/Interwiki.php207
-rw-r--r--includes/JobQueue.php2
-rw-r--r--includes/Licenses.php2
-rw-r--r--includes/LinkBatch.php2
-rw-r--r--includes/LinkCache.php70
-rw-r--r--includes/Linker.php783
-rw-r--r--includes/LinksUpdate.php73
-rw-r--r--includes/LogEventsList.php468
-rw-r--r--includes/LogPage.php140
-rw-r--r--includes/MagicWord.php8
-rw-r--r--includes/Math.php48
-rw-r--r--includes/MediaTransformOutput.php20
-rw-r--r--includes/MessageCache.php93
-rw-r--r--includes/Metadata.php528
-rw-r--r--includes/MimeMagic.php90
-rw-r--r--includes/Namespace.php14
-rw-r--r--includes/ObjectCache.php23
-rw-r--r--includes/OutputPage.php575
-rw-r--r--includes/PageHistory.php374
-rw-r--r--includes/PageQueryPage.php6
-rw-r--r--includes/Pager.php82
-rw-r--r--includes/PrefixSearch.php19
-rw-r--r--includes/Profiler.php5
-rw-r--r--includes/ProtectionForm.php409
-rw-r--r--includes/ProxyTools.php58
-rw-r--r--includes/QueryPage.php23
-rw-r--r--includes/RawPage.php62
-rw-r--r--includes/RecentChange.php327
-rw-r--r--includes/RefreshLinksJob.php84
-rw-r--r--includes/Revision.php174
-rw-r--r--includes/Sanitizer.php121
-rw-r--r--includes/SearchEngine.php138
-rw-r--r--includes/SearchMySQL.php16
-rw-r--r--includes/SearchOracle.php14
-rw-r--r--includes/SearchPostgres.php10
-rw-r--r--includes/Setup.php42
-rw-r--r--includes/SiteConfiguration.php341
-rw-r--r--includes/SiteStats.php88
-rw-r--r--includes/Skin.php483
-rw-r--r--includes/SkinTemplate.php242
-rw-r--r--includes/SpecialPage.php33
-rw-r--r--includes/SquidUpdate.php2
-rw-r--r--includes/StringUtils.php99
-rw-r--r--includes/StubObject.php2
-rw-r--r--includes/Title.php1366
-rw-r--r--includes/TitleArray.php81
-rw-r--r--includes/UploadBase.php867
-rw-r--r--includes/UploadFromStash.php58
-rw-r--r--includes/UploadFromUpload.php20
-rw-r--r--includes/UploadFromUrl.php92
-rw-r--r--includes/User.php1059
-rw-r--r--includes/UserArray.php4
-rw-r--r--includes/UserMailer.php57
-rw-r--r--includes/WatchedItem.php37
-rw-r--r--includes/WatchlistEditor.php76
-rw-r--r--includes/WebRequest.php52
-rw-r--r--includes/WebResponse.php54
-rw-r--r--includes/WebStart.php55
-rw-r--r--includes/Wiki.php139
-rw-r--r--includes/WikiError.php3
-rw-r--r--includes/Xml.php70
-rw-r--r--includes/XmlFunctions.php24
-rw-r--r--includes/XmlTypeCheck.php7
-rw-r--r--includes/ZhConversion.php196
-rw-r--r--includes/api/ApiBase.php148
-rw-r--r--includes/api/ApiBlock.php22
-rw-r--r--includes/api/ApiDelete.php46
-rw-r--r--includes/api/ApiDisabled.php72
-rw-r--r--includes/api/ApiEditPage.php39
-rw-r--r--includes/api/ApiEmailUser.php14
-rw-r--r--includes/api/ApiExpandTemplates.php17
-rw-r--r--includes/api/ApiFormatBase.php10
-rw-r--r--includes/api/ApiFormatJson.php7
-rw-r--r--includes/api/ApiFormatJson_json.php76
-rw-r--r--includes/api/ApiFormatWddx.php58
-rw-r--r--includes/api/ApiFormatXml.php31
-rw-r--r--includes/api/ApiFormatYaml_spyc.php1115
-rw-r--r--includes/api/ApiLogin.php135
-rw-r--r--includes/api/ApiLogout.php5
-rw-r--r--includes/api/ApiMain.php72
-rw-r--r--includes/api/ApiMove.php51
-rw-r--r--includes/api/ApiPageSet.php54
-rw-r--r--includes/api/ApiParamInfo.php16
-rw-r--r--includes/api/ApiParse.php60
-rw-r--r--includes/api/ApiPatrol.php99
-rw-r--r--includes/api/ApiProtect.php83
-rw-r--r--includes/api/ApiPurge.php106
-rw-r--r--includes/api/ApiQuery.php29
-rw-r--r--includes/api/ApiQueryAllCategories.php23
-rw-r--r--includes/api/ApiQueryAllLinks.php29
-rw-r--r--includes/api/ApiQueryAllUsers.php4
-rw-r--r--includes/api/ApiQueryAllimages.php18
-rw-r--r--includes/api/ApiQueryAllpages.php50
-rw-r--r--includes/api/ApiQueryBacklinks.php56
-rw-r--r--includes/api/ApiQueryBase.php48
-rw-r--r--includes/api/ApiQueryBlocks.php42
-rw-r--r--includes/api/ApiQueryCategories.php35
-rw-r--r--includes/api/ApiQueryCategoryInfo.php21
-rw-r--r--includes/api/ApiQueryCategoryMembers.php36
-rw-r--r--includes/api/ApiQueryDeletedrevs.php4
-rw-r--r--includes/api/ApiQueryDisabled.php72
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php164
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php6
-rw-r--r--includes/api/ApiQueryImageInfo.php30
-rw-r--r--includes/api/ApiQueryImages.php8
-rw-r--r--includes/api/ApiQueryInfo.php149
-rw-r--r--includes/api/ApiQueryLangLinks.php4
-rw-r--r--includes/api/ApiQueryLinks.php10
-rw-r--r--includes/api/ApiQueryLogEvents.php103
-rw-r--r--includes/api/ApiQueryRandom.php13
-rw-r--r--includes/api/ApiQueryRecentChanges.php129
-rw-r--r--includes/api/ApiQueryRevisions.php90
-rw-r--r--includes/api/ApiQuerySearch.php37
-rw-r--r--includes/api/ApiQuerySiteinfo.php82
-rw-r--r--includes/api/ApiQueryUserContributions.php39
-rw-r--r--includes/api/ApiQueryUserInfo.php10
-rw-r--r--includes/api/ApiQueryUsers.php39
-rw-r--r--includes/api/ApiQueryWatchlist.php51
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php179
-rw-r--r--includes/api/ApiResult.php17
-rw-r--r--includes/api/ApiRollback.php27
-rw-r--r--includes/api/ApiUnblock.php8
-rw-r--r--includes/api/ApiUndelete.php6
-rw-r--r--includes/api/ApiWatch.php99
-rw-r--r--includes/db/Database.php308
-rw-r--r--includes/db/DatabaseMssql.php59
-rw-r--r--includes/db/DatabaseOracle.php14
-rw-r--r--includes/db/DatabasePostgres.php89
-rw-r--r--includes/db/DatabaseSqlite.php43
-rw-r--r--includes/db/LBFactory_Multi.php14
-rw-r--r--includes/db/LoadBalancer.php77
-rw-r--r--includes/db/LoadMonitor.php3
-rw-r--r--includes/diff/Diff.php580
-rw-r--r--includes/diff/DifferenceEngine.php (renamed from includes/DifferenceEngine.php)666
-rw-r--r--includes/diff/HTMLDiff.php1005
-rw-r--r--includes/diff/Nodes.php439
-rw-r--r--includes/filerepo/ArchivedFile.php44
-rw-r--r--includes/filerepo/FSRepo.php10
-rw-r--r--includes/filerepo/File.php26
-rw-r--r--includes/filerepo/FileCache.php156
-rw-r--r--includes/filerepo/FileRepo.php80
-rw-r--r--includes/filerepo/ForeignAPIFile.php83
-rw-r--r--includes/filerepo/ForeignAPIRepo.php77
-rw-r--r--includes/filerepo/ForeignDBFile.php2
-rw-r--r--includes/filerepo/Image.php2
-rw-r--r--includes/filerepo/LocalFile.php99
-rw-r--r--includes/filerepo/LocalRepo.php7
-rw-r--r--includes/filerepo/OldLocalFile.php3
-rw-r--r--includes/filerepo/RepoGroup.php24
-rw-r--r--includes/filerepo/UnregisteredLocalFile.php2
-rw-r--r--includes/media/BMP.php9
-rw-r--r--includes/media/Bitmap.php36
-rw-r--r--includes/media/Bitmap_ClientOnly.php15
-rw-r--r--includes/media/Generic.php15
-rw-r--r--includes/media/SVG.php40
-rw-r--r--includes/memcached-client.php6
-rw-r--r--includes/mime.types1
-rw-r--r--includes/parser/CoreLinkFunctions.php47
-rw-r--r--includes/parser/CoreParserFunctions.php75
-rw-r--r--includes/parser/LinkHolderArray.php438
-rw-r--r--includes/parser/Parser.php1471
-rw-r--r--includes/parser/ParserCache.php4
-rw-r--r--includes/parser/ParserOptions.php16
-rw-r--r--includes/parser/ParserOutput.php63
-rw-r--r--includes/parser/Parser_DiffTest.php34
-rw-r--r--includes/parser/Parser_LinkHooks.php315
-rw-r--r--includes/parser/Parser_OldPP.php4944
-rw-r--r--includes/parser/Preprocessor_DOM.php41
-rw-r--r--includes/parser/Preprocessor_Hash.php37
-rw-r--r--includes/specials/SpecialAllmessages.php85
-rw-r--r--includes/specials/SpecialAllpages.php717
-rw-r--r--includes/specials/SpecialBlockip.php195
-rw-r--r--includes/specials/SpecialBooksources.php56
-rw-r--r--includes/specials/SpecialCategories.php4
-rw-r--r--includes/specials/SpecialConfirmemail.php22
-rw-r--r--includes/specials/SpecialContributions.php682
-rw-r--r--includes/specials/SpecialDeletedContributions.php369
-rw-r--r--includes/specials/SpecialDisambiguations.php8
-rw-r--r--includes/specials/SpecialEmailuser.php142
-rw-r--r--includes/specials/SpecialExport.php38
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php6
-rw-r--r--includes/specials/SpecialFilepath.php4
-rw-r--r--includes/specials/SpecialImport.php1202
-rw-r--r--includes/specials/SpecialIpblocklist.php118
-rw-r--r--includes/specials/SpecialLinkSearch.php185
-rw-r--r--includes/specials/SpecialListUserRestrictions.php161
-rw-r--r--includes/specials/SpecialListfiles.php (renamed from includes/specials/SpecialImagelist.php)51
-rw-r--r--includes/specials/SpecialListgrouprights.php25
-rw-r--r--includes/specials/SpecialListredirects.php3
-rw-r--r--includes/specials/SpecialListusers.php68
-rw-r--r--includes/specials/SpecialLockdb.php2
-rw-r--r--includes/specials/SpecialLog.php22
-rw-r--r--includes/specials/SpecialLonelypages.php7
-rw-r--r--includes/specials/SpecialMIMEsearch.php2
-rw-r--r--includes/specials/SpecialMergeHistory.php22
-rw-r--r--includes/specials/SpecialMostcategories.php4
-rw-r--r--includes/specials/SpecialMostimages.php2
-rw-r--r--includes/specials/SpecialMostlinkedtemplates.php19
-rw-r--r--includes/specials/SpecialMovepage.php136
-rw-r--r--includes/specials/SpecialNewimages.php127
-rw-r--r--includes/specials/SpecialNewpages.php57
-rw-r--r--includes/specials/SpecialPreferences.php461
-rw-r--r--includes/specials/SpecialPrefixindex.php139
-rw-r--r--includes/specials/SpecialProtectedpages.php32
-rw-r--r--includes/specials/SpecialProtectedtitles.php13
-rw-r--r--includes/specials/SpecialRandompage.php22
-rw-r--r--includes/specials/SpecialRecentchanges.php263
-rw-r--r--includes/specials/SpecialRecentchangeslinked.php30
-rw-r--r--includes/specials/SpecialRemoveRestrictions.php60
-rw-r--r--includes/specials/SpecialResetpass.php178
-rw-r--r--includes/specials/SpecialRestrictUser.php189
-rw-r--r--includes/specials/SpecialRevisiondelete.php72
-rw-r--r--includes/specials/SpecialSearch.php984
-rw-r--r--includes/specials/SpecialSpecialpages.php2
-rw-r--r--includes/specials/SpecialStatistics.php277
-rw-r--r--includes/specials/SpecialUncategorizedimages.php2
-rw-r--r--includes/specials/SpecialUndelete.php197
-rw-r--r--includes/specials/SpecialUnusedimages.php2
-rw-r--r--includes/specials/SpecialUpload.php69
-rw-r--r--includes/specials/SpecialUserlogin.php249
-rw-r--r--includes/specials/SpecialUserlogout.php2
-rw-r--r--includes/specials/SpecialUserrights.php88
-rw-r--r--includes/specials/SpecialVersion.php74
-rw-r--r--includes/specials/SpecialWantedfiles.php90
-rw-r--r--includes/specials/SpecialWantedtemplates.php110
-rw-r--r--includes/specials/SpecialWatchlist.php266
-rw-r--r--includes/specials/SpecialWhatlinkshere.php20
-rw-r--r--includes/templates/NoLocalSettings.php29
-rw-r--r--includes/templates/PHP4.php100
-rw-r--r--includes/templates/Userlogin.php14
-rw-r--r--includes/zhtable/simpphrases.manual18
-rw-r--r--includes/zhtable/toCN.manual73
-rw-r--r--includes/zhtable/toHK.manual80
-rw-r--r--includes/zhtable/toSG.manual2
-rw-r--r--includes/zhtable/toTW.manual15
-rw-r--r--includes/zhtable/tradphrases.manual7
-rw-r--r--includes/zhtable/tradphrases_exclude.manual2
279 files changed, 26138 insertions, 18295 deletions
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index 9daca9e5..1a9adbca 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -14,7 +14,8 @@ if( !defined( 'MEDIAWIKI' ) ) {
* Modified function from http://pure-essence.net/stuff/code/utf8RawUrlDecode.phps
*
* @param $source String escaped with Javascript's escape() function
- * @param $iconv_to String destination character set will be used as second paramether in the iconv function. Default is UTF-8.
+ * @param $iconv_to String destination character set will be used as second parameter
+ * in the iconv function. Default is UTF-8.
* @return string
*/
function js_unescape($source, $iconv_to = 'UTF-8') {
@@ -72,91 +73,6 @@ function code2utf($num){
return '';
}
-define( 'AJAX_SEARCH_VERSION', 2 ); //AJAX search cache version
-
-function wfSajaxSearch( $term ) {
- global $wgContLang, $wgUser, $wgCapitalLinks, $wgMemc;
- $limit = 16;
- $sk = $wgUser->getSkin();
- $output = '';
-
- $term = trim( $term );
- $term = $wgContLang->checkTitleEncoding( $wgContLang->recodeInput( js_unescape( $term ) ) );
- if ( $wgCapitalLinks )
- $term = $wgContLang->ucfirst( $term );
- $term_title = Title::newFromText( $term );
-
- $memckey = $term_title ? wfMemcKey( 'ajaxsearch', md5( $term_title->getFullText() ) ) : wfMemcKey( 'ajaxsearch', md5( $term ) );
- $cached = $wgMemc->get($memckey);
- if( is_array( $cached ) && $cached['version'] == AJAX_SEARCH_VERSION ) {
- $response = new AjaxResponse( $cached['html'] );
- $response->setCacheDuration( 30*60 );
- return $response;
- }
-
- $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] );
- if( $t && $t->getNamespace() == NS_SPECIAL ) {
- $canSearch = false;
- if( count( $results ) > $limit ) {
- $more = '<i>' .
- $sk->makeKnownLinkObj(
- SpecialPage::getTitleFor( 'Specialpages' ),
- wfMsgHtml( 'moredotdotdot' ) ) .
- '</i>';
- }
- } else {
- if( count( $results ) > $limit ) {
- $more = '<i>' .
- $sk->makeKnownLinkObj(
- SpecialPage::getTitleFor( "Allpages", $term ),
- wfMsgHtml( 'moredotdotdot' ) ) .
- '</i>';
- }
- }
- }
-
- $valid = (bool) $term_title;
- $term_url = urlencode( $term );
- $term_normalized = $valid ? $term_title->getFullText() : $term;
- $term_display = htmlspecialchars( $term );
- $subtitlemsg = ( $valid ? 'searchsubtitle' : 'searchsubtitleinvalid' );
- $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')
- . '</h1><div id="contentSub">'. $subtitle . '</div>';
- if( $canSearch ) {
- $html .= '<ul><li>'
- . $sk->makeKnownLink( $wgContLang->specialPage( 'Search' ),
- wfMsgHtml( 'searchcontaining', $term_display ),
- "search={$term_url}&fulltext=Search" )
- . '</li><li>' . $sk->makeKnownLink( $wgContLang->specialPage( 'Search' ),
- wfMsgHtml( 'searchnamed', $term_display ) ,
- "search={$term_url}&go=Go" )
- . "</li></ul>";
- }
- if( $r ) {
- $html .= "<h2>" . wfMsgHtml( 'articletitles', $term_display ) . "</h2>"
- . '<ul>' .$r .'</ul>' . $more;
- }
-
- $wgMemc->set( $memckey, array( 'version' => AJAX_SEARCH_VERSION, 'html' => $html ), 30 * 60 );
-
- $response = new AjaxResponse( $html );
- $response->setCacheDuration( 30*60 );
- return $response;
-}
-
/**
* Called for AJAX watch/unwatch requests.
* @param $pagename Prefixed title string for page to watch/unwatch
@@ -189,20 +105,54 @@ function wfAjaxWatch($pagename = "", $watch = "") {
if(!$watching) {
$dbw = wfGetDB(DB_MASTER);
$dbw->begin();
- $article->doWatch();
+ $ok = $article->doWatch();
$dbw->commit();
}
} else {
if($watching) {
$dbw = wfGetDB(DB_MASTER);
$dbw->begin();
- $article->doUnwatch();
+ $ok = $article->doUnwatch();
$dbw->commit();
}
}
+ // Something stopped the change
+ if( isset($ok) && !$ok ) {
+ return '<err#>';
+ }
if( $watch ) {
return '<w#>'.wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
} else {
return '<u#>'.wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
}
}
+
+/**
+ * Called in some places (currently just extensions)
+ * to get the thumbnail URL for a given file at a given resolution.
+ */
+function wfAjaxGetThumbnailUrl( $file, $width, $height ) {
+ $file = wfFindFile( $file );
+
+ if ( !$file || !$file->exists() )
+ return null;
+
+ $url = $file->getThumbnail( $width, $height )->url;
+
+ return $url;
+}
+
+/**
+ * Called in some places (currently just extensions)
+ * to get the URL for a given file.
+ */
+function wfAjaxGetFileUrl( $file ) {
+ $file = wfFindFile( $file );
+
+ if ( !$file || !$file->exists() )
+ return null;
+
+ $url = $file->getUrl();
+
+ return $url;
+} \ No newline at end of file
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index c79e928b..63468a14 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -9,7 +9,9 @@ if( !defined( 'MEDIAWIKI' ) ) {
}
/**
- * @todo document
+ * Handle responses for Ajax requests (send headers, print
+ * content, that sort of thing)
+ *
* @ingroup Ajax
*/
class AjaxResponse {
@@ -20,7 +22,7 @@ class AjaxResponse {
/** HTTP header Content-Type */
private $mContentType;
- /** @todo document */
+ /** Disables output. Can be set by calling $AjaxResponse->disable() */
private $mDisabled;
/** Date for the HTTP header Last-modified */
diff --git a/includes/Article.php b/includes/Article.php
index 4d8277bb..3d9c2147 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -16,27 +16,30 @@ class Article {
/**@{{
* @private
*/
- var $mComment; //!<
- var $mContent; //!<
- var $mContentLoaded; //!<
- var $mCounter; //!<
- var $mForUpdate; //!<
- var $mGoodAdjustment; //!<
- var $mLatest; //!<
- var $mMinorEdit; //!<
- var $mOldId; //!<
- var $mRedirectedFrom; //!<
- var $mRedirectUrl; //!<
- var $mRevIdFetched; //!<
- var $mRevision; //!<
- var $mTimestamp; //!<
- var $mTitle; //!<
- var $mTotalAdjustment; //!<
- var $mTouched; //!<
- var $mUser; //!<
- var $mUserText; //!<
- var $mRedirectTarget; //!<
- var $mIsRedirect;
+ var $mComment = ''; //!<
+ var $mContent; //!<
+ var $mContentLoaded = false; //!<
+ var $mCounter = -1; //!< Not loaded
+ var $mCurID = -1; //!< Not loaded
+ var $mDataLoaded = false; //!<
+ var $mForUpdate = false; //!<
+ var $mGoodAdjustment = 0; //!<
+ var $mIsRedirect = false; //!<
+ var $mLatest = false; //!<
+ var $mMinorEdit; //!<
+ var $mOldId; //!<
+ var $mPreparedEdit = false; //!< Title object if set
+ var $mRedirectedFrom = null; //!< Title object if set
+ var $mRedirectTarget = null; //!< Title object if set
+ var $mRedirectUrl = false; //!<
+ var $mRevIdFetched = 0; //!<
+ var $mRevision; //!<
+ var $mTimestamp = ''; //!<
+ var $mTitle; //!<
+ var $mTotalAdjustment = 0; //!<
+ var $mTouched = '19700101000000'; //!<
+ var $mUser = -1; //!< Not loaded
+ var $mUserText = ''; //!<
/**@}}*/
/**
@@ -44,10 +47,18 @@ class Article {
* @param $title Reference to a Title object.
* @param $oldId Integer revision ID, null to fetch from request, zero for current
*/
- function __construct( Title $title, $oldId = null ) {
+ public function __construct( Title $title, $oldId = null ) {
$this->mTitle =& $title;
$this->mOldId = $oldId;
- $this->clear();
+ }
+
+ /**
+ * Constructor from an article article
+ * @param $id The article ID to load
+ */
+ public static function newFromID( $id ) {
+ $t = Title::newFromID( $id );
+ return $t == null ? null : new Article( $t );
}
/**
@@ -55,7 +66,7 @@ class Article {
* from another page on the wiki.
* @param $from Title object.
*/
- function setRedirectedFrom( $from ) {
+ public function setRedirectedFrom( $from ) {
$this->mRedirectedFrom = $from;
}
@@ -67,22 +78,20 @@ class Article {
* @return mixed Title object, or null if this page is not a redirect
*/
public function getRedirectTarget() {
- if(!$this->mTitle || !$this->mTitle->isRedirect())
+ if( !$this->mTitle || !$this->mTitle->isRedirect() )
return null;
- if(!is_null($this->mRedirectTarget))
+ if( !is_null($this->mRedirectTarget) )
return $this->mRedirectTarget;
-
# Query the redirect table
- $dbr = wfGetDB(DB_SLAVE);
- $res = $dbr->select('redirect',
- array('rd_namespace', 'rd_title'),
- array('rd_from' => $this->getID()),
- __METHOD__
+ $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)
+ if( $row = $dbr->fetchObject($res) ) {
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();
}
@@ -94,15 +103,19 @@ class Article {
* @return Title object
*/
public function insertRedirect() {
- $retval = Title::newFromRedirect($this->getContent());
- if(!$retval)
+ $retval = Title::newFromRedirect( $this->getContent() );
+ if( !$retval ) {
return null;
- $dbw = wfGetDB(DB_MASTER);
- $dbw->replace('redirect', array('rd_from'), array(
+ }
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->replace( 'redirect', array('rd_from'),
+ array(
'rd_from' => $this->getID(),
'rd_namespace' => $retval->getNamespace(),
'rd_title' => $retval->getDBKey()
- ), __METHOD__);
+ ),
+ __METHOD__
+ );
return $retval;
}
@@ -113,9 +126,9 @@ class Article {
*/
public function followRedirect() {
$text = $this->getContent();
- return self::followRedirectText( $text );
+ return $this->followRedirectText( $text );
}
-
+
/**
* Get the Title object this text redirects to
*
@@ -131,7 +144,6 @@ class Article {
//
// This can be hard to reverse and may produce loops,
// so they may be disabled in the site configuration.
-
$source = $this->mTitle->getFullURL( 'redirect=no' );
return $rt->getFullURL( 'rdfrom=' . urlencode( $source ) );
}
@@ -142,7 +154,6 @@ class Article {
// the rest of the page we're on.
//
// This can be hard to reverse, so they may be disabled.
-
if( $rt->isSpecial( 'Userlogout' ) ) {
// rolleyes
} else {
@@ -159,15 +170,15 @@ class Article {
/**
* get the title object of the article
*/
- function getTitle() {
+ public function getTitle() {
return $this->mTitle;
}
/**
- * Clear the object
- * @private
- */
- function clear() {
+ * Clear the object
+ * @private
+ */
+ public function clear() {
$this->mDataLoaded = false;
$this->mContentLoaded = false;
@@ -190,30 +201,27 @@ class Article {
* Note that getContent/loadContent do not follow redirects anymore.
* If you need to fetch redirectable content easily, try
* the shortcut in Article::followContent()
- * FIXME
- * @todo There are still side-effects in this!
- * In general, you should use the Revision class, not Article,
- * to fetch text for purposes other than page views.
*
* @return Return the text of this revision
*/
- function getContent() {
- global $wgUser, $wgOut, $wgMessageCache;
-
+ public function getContent() {
+ global $wgUser, $wgContLang, $wgOut, $wgMessageCache;
wfProfileIn( __METHOD__ );
-
- if ( 0 == $this->getID() ) {
- wfProfileOut( __METHOD__ );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
-
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- $wgMessageCache->loadAllMessages();
- $ret = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
+ if( $this->getID() === 0 ) {
+ # If this is a MediaWiki:x message, then load the messages
+ # and return the message value for x.
+ if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ # If this is a system message, get the default text.
+ list( $message, $lang ) = $wgMessageCache->figureMessage( $wgContLang->lcfirst( $this->mTitle->getText() ) );
+ $wgMessageCache->loadAllMessages( $lang );
+ $text = wfMsgGetKey( $message, false, $lang, false );
+ if( wfEmptyMsg( $message, $text ) )
+ $text = '';
} else {
- $ret = wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' );
+ $text = wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' );
}
-
- return "<div class='noarticletext'>\n$ret\n</div>";
+ wfProfileOut( __METHOD__ );
+ return $text;
} else {
$this->loadContent();
wfProfileOut( __METHOD__ );
@@ -233,7 +241,7 @@ class Article {
* @return string text of the requested section
* @deprecated
*/
- function getSection($text,$section) {
+ public function getSection( $text, $section ) {
global $wgParser;
return $wgParser->getSection( $text, $section );
}
@@ -242,8 +250,8 @@ class Article {
* @return int The oldid of the article that is to be shown, 0 for the
* current revision
*/
- function getOldID() {
- if ( is_null( $this->mOldId ) ) {
+ public function getOldID() {
+ if( is_null( $this->mOldId ) ) {
$this->mOldId = $this->getOldIDFromRequest();
}
return $this->mOldId;
@@ -254,32 +262,27 @@ class Article {
*
* @return int The old id for the request
*/
- function getOldIDFromRequest() {
+ public function getOldIDFromRequest() {
global $wgRequest;
$this->mRedirectUrl = false;
$oldid = $wgRequest->getVal( 'oldid' );
- if ( isset( $oldid ) ) {
+ if( isset( $oldid ) ) {
$oldid = intval( $oldid );
- if ( $wgRequest->getVal( 'direction' ) == 'next' ) {
+ if( $wgRequest->getVal( 'direction' ) == 'next' ) {
$nextid = $this->mTitle->getNextRevisionID( $oldid );
- if ( $nextid ) {
+ if( $nextid ) {
$oldid = $nextid;
} else {
$this->mRedirectUrl = $this->mTitle->getFullURL( 'redirect=no' );
}
- } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) {
+ } elseif( $wgRequest->getVal( 'direction' ) == 'prev' ) {
$previd = $this->mTitle->getPreviousRevisionID( $oldid );
- if ( $previd ) {
+ if( $previd ) {
$oldid = $previd;
- } else {
- # TODO
}
}
- # unused:
- # $lastid = $oldid;
}
-
- if ( !$oldid ) {
+ if( !$oldid ) {
$oldid = 0;
}
return $oldid;
@@ -289,25 +292,24 @@ class Article {
* Load the revision (including text) into this object
*/
function loadContent() {
- if ( $this->mContentLoaded ) return;
-
+ if( $this->mContentLoaded ) return;
+ wfProfileIn( __METHOD__ );
# Query variables :P
$oldid = $this->getOldID();
-
# Pre-fill content with error message so that if something
# fails we'll have something telling us what we intended.
$this->mOldId = $oldid;
$this->fetchContent( $oldid );
+ wfProfileOut( __METHOD__ );
}
/**
* Fetch a page record with the given conditions
- * @param Database $dbr
- * @param array $conditions
- * @private
+ * @param $dbr Database object
+ * @param $conditions Array
*/
- function pageData( $dbr, $conditions ) {
+ protected function pageData( $dbr, $conditions ) {
$fields = array(
'page_id',
'page_namespace',
@@ -333,20 +335,20 @@ class Article {
}
/**
- * @param Database $dbr
- * @param Title $title
+ * @param $dbr Database object
+ * @param $title Title object
*/
- function pageDataFromTitle( $dbr, $title ) {
+ public function pageDataFromTitle( $dbr, $title ) {
return $this->pageData( $dbr, array(
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey() ) );
}
/**
- * @param Database $dbr
- * @param int $id
+ * @param $dbr Database
+ * @param $id Integer
*/
- function pageDataFromId( $dbr, $id ) {
+ protected function pageDataFromId( $dbr, $id ) {
return $this->pageData( $dbr, array( 'page_id' => $id ) );
}
@@ -354,22 +356,21 @@ class Article {
* Set the general counter, title etc data loaded from
* some source.
*
- * @param object $data
- * @private
+ * @param $data Database row object or "fromdb"
*/
- function loadPageData( $data = 'fromdb' ) {
- if ( $data === 'fromdb' ) {
+ public function loadPageData( $data = 'fromdb' ) {
+ if( $data === 'fromdb' ) {
$dbr = wfGetDB( DB_MASTER );
$data = $this->pageDataFromId( $dbr, $this->getId() );
}
$lc = LinkCache::singleton();
- if ( $data ) {
+ if( $data ) {
$lc->addGoodLinkObj( $data->page_id, $this->mTitle, $data->page_len, $data->page_is_redirect );
$this->mTitle->mArticleID = $data->page_id;
- # Old-fashioned restrictions.
+ # Old-fashioned restrictions
$this->mTitle->loadRestrictions( $data->page_restrictions );
$this->mCounter = $data->page_counter;
@@ -377,7 +378,7 @@ class Article {
$this->mIsRedirect = $data->page_is_redirect;
$this->mLatest = $data->page_latest;
} else {
- if ( is_object( $this->mTitle ) ) {
+ if( is_object( $this->mTitle ) ) {
$lc->addBadLinkObj( $this->mTitle );
}
$this->mTitle->mArticleID = 0;
@@ -389,11 +390,11 @@ class Article {
/**
* Get text of an article from database
* Does *NOT* follow redirects.
- * @param int $oldid 0 for whatever the latest revision is
+ * @param $oldid Int: 0 for whatever the latest revision is
* @return string
*/
function fetchContent( $oldid = 0 ) {
- if ( $this->mContentLoaded ) {
+ if( $this->mContentLoaded ) {
return $this->mContent;
}
@@ -429,14 +430,14 @@ class Article {
}
$revision = Revision::newFromId( $this->mLatest );
if( is_null( $revision ) ) {
- wfDebug( __METHOD__." failed to retrieve current page, rev_id {$data->page_latest}\n" );
+ wfDebug( __METHOD__." failed to retrieve current page, rev_id {$this->mLatest}\n" );
return false;
}
}
// 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->revText(); // Loads if user is allowed
+ $this->mContent = $revision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed
$this->mUser = $revision->getUser();
$this->mUserText = $revision->getUserText();
@@ -457,7 +458,7 @@ class Article {
*
* @param $x Mixed: FIXME
*/
- function forUpdate( $x = NULL ) {
+ public function forUpdate( $x = NULL ) {
return wfSetVar( $this->mForUpdate, $x );
}
@@ -479,9 +480,9 @@ class Article {
* the default
* @return Array: options
*/
- function getSelectOptions( $options = '' ) {
- if ( $this->mForUpdate ) {
- if ( is_array( $options ) ) {
+ protected function getSelectOptions( $options = '' ) {
+ if( $this->mForUpdate ) {
+ if( is_array( $options ) ) {
$options[] = 'FOR UPDATE';
} else {
$options = 'FOR UPDATE';
@@ -493,7 +494,7 @@ class Article {
/**
* @return int Page ID
*/
- function getID() {
+ public function getID() {
if( $this->mTitle ) {
return $this->mTitle->getArticleID();
} else {
@@ -504,22 +505,38 @@ class Article {
/**
* @return bool Whether or not the page exists in the database
*/
- function exists() {
- return $this->getId() != 0;
+ public function exists() {
+ return $this->getId() > 0;
+ }
+
+ /**
+ * Check if this page is something we're going to be showing
+ * some sort of sensible content for. If we return false, page
+ * views (plain action=view) will return an HTTP 404 response,
+ * so spiders and robots can know they're following a bad link.
+ *
+ * @return bool
+ */
+ public function hasViewableContent() {
+ return $this->exists() || $this->mTitle->isAlwaysKnown();
}
/**
* @return int The view count for the page
*/
- function getCount() {
- if ( -1 == $this->mCounter ) {
+ public function getCount() {
+ if( -1 == $this->mCounter ) {
$id = $this->getID();
- if ( $id == 0 ) {
+ if( $id == 0 ) {
$this->mCounter = 0;
} else {
$dbr = wfGetDB( DB_SLAVE );
- $this->mCounter = $dbr->selectField( 'page', 'page_counter', array( 'page_id' => $id ),
- 'Article::getCount', $this->getSelectOptions() );
+ $this->mCounter = $dbr->selectField( 'page',
+ 'page_counter',
+ array( 'page_id' => $id ),
+ __METHOD__,
+ $this->getSelectOptions()
+ );
}
}
return $this->mCounter;
@@ -532,14 +549,11 @@ class Article {
* @param $text String: text to analyze
* @return bool
*/
- function isCountable( $text ) {
+ public function isCountable( $text ) {
global $wgUseCommaCount;
$token = $wgUseCommaCount ? ',' : '[[';
- return
- $this->mTitle->isContentPage()
- && !$this->isRedirect( $text )
- && in_string( $token, $text );
+ return $this->mTitle->isContentPage() && !$this->isRedirect($text) && in_string($token,$text);
}
/**
@@ -548,11 +562,11 @@ class Article {
* @param $text String: FIXME
* @return bool
*/
- function isRedirect( $text = false ) {
- if ( $text === false ) {
- if ( $this->mDataLoaded )
+ public 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() );
@@ -567,28 +581,25 @@ class Article {
* to this page (and it exists).
* @return bool
*/
- function isCurrent() {
+ public function isCurrent() {
# If no oldid, this is the current version.
- if ($this->getOldID() == 0)
+ if( $this->getOldID() == 0 ) {
return true;
-
- return $this->exists() &&
- isset( $this->mRevision ) &&
- $this->mRevision->isCurrent();
+ }
+ return $this->exists() && isset($this->mRevision) && $this->mRevision->isCurrent();
}
/**
* Loads everything except the text
* This isn't necessary for all uses, so it's only done if needed.
- * @private
*/
- function loadLastEdit() {
- if ( -1 != $this->mUser )
+ protected function loadLastEdit() {
+ if( -1 != $this->mUser )
return;
# New or non-existent articles have no user information
$id = $this->getID();
- if ( 0 == $id ) return;
+ if( 0 == $id ) return;
$this->mLastRevision = Revision::loadFromPageId( wfGetDB( DB_MASTER ), $id );
if( !is_null( $this->mLastRevision ) ) {
@@ -601,35 +612,36 @@ class Article {
}
}
- function getTimestamp() {
+ public function getTimestamp() {
// Check if the field has been filled by ParserCache::get()
- if ( !$this->mTimestamp ) {
+ if( !$this->mTimestamp ) {
$this->loadLastEdit();
}
return wfTimestamp(TS_MW, $this->mTimestamp);
}
- function getUser() {
+ public function getUser() {
$this->loadLastEdit();
return $this->mUser;
}
- function getUserText() {
+ public function getUserText() {
$this->loadLastEdit();
return $this->mUserText;
}
- function getComment() {
+ public function getComment() {
$this->loadLastEdit();
return $this->mComment;
}
- function getMinorEdit() {
+ public function getMinorEdit() {
$this->loadLastEdit();
return $this->mMinorEdit;
}
- function getRevIdFetched() {
+ /* Use this to fetch the rev ID used on page views */
+ public function getRevIdFetched() {
$this->loadLastEdit();
return $this->mRevIdFetched;
}
@@ -638,7 +650,7 @@ class Article {
* @param $limit Integer: default 0.
* @param $offset Integer: default 0.
*/
- function getContributors($limit = 0, $offset = 0) {
+ public function getContributors($limit = 0, $offset = 0) {
# XXX: this is expensive; cache this info somewhere.
$contribs = array();
@@ -648,49 +660,62 @@ class Article {
$user = $this->getUser();
$pageId = $this->getId();
- $sql = "SELECT rev_user, rev_user_text, user_real_name, MAX(rev_timestamp) as timestamp
+ $sql = "SELECT {$userTable}.*, MAX(rev_timestamp) as timestamp
FROM $revTable LEFT JOIN $userTable ON rev_user = user_id
WHERE rev_page = $pageId
AND rev_user != $user
GROUP BY rev_user, rev_user_text, user_real_name
ORDER BY timestamp DESC";
- if ($limit > 0) { $sql .= ' LIMIT '.$limit; }
- if ($offset > 0) { $sql .= ' OFFSET '.$offset; }
-
- $sql .= ' '. $this->getSelectOptions();
+ if($limit > 0) { $sql .= ' LIMIT '.$limit; }
+ if($offset > 0) { $sql .= ' OFFSET '.$offset; }
- $res = $dbr->query($sql, __METHOD__);
+ $sql .= ' '. $this->getSelectOptions();
- while ( $line = $dbr->fetchObject( $res ) ) {
- $contribs[] = array($line->rev_user, $line->rev_user_text, $line->user_real_name);
- }
+ $res = $dbr->query($sql, __METHOD__ );
- $dbr->freeResult($res);
- return $contribs;
+ return new UserArrayFromResult( $res );
}
/**
* This is the default action of the script: just view the page of
* the given title.
*/
- function view() {
+ public function view() {
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgParser;
global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
global $wgDefaultRobotPolicy;
- $sk = $wgUser->getSkin();
wfProfileIn( __METHOD__ );
- $parserCache = ParserCache::singleton();
- $ns = $this->mTitle->getNamespace(); # shortcut
-
# Get variables from query string
$oldid = $this->getOldID();
+ # Try file cache
+ if( $oldid === 0 && $this->checkTouched() ) {
+ global $wgUseETag;
+ if( $wgUseETag ) {
+ $parserCache = ParserCache::singleton();
+ $wgOut->setETag( $parserCache->getETag($this,$wgUser) );
+ }
+ if( $wgOut->checkLastModified( $this->getTouched() ) ) {
+ wfProfileOut( __METHOD__ );
+ return;
+ } else if( $this->tryFileCache() ) {
+ # tell wgOut that output is taken care of
+ $wgOut->disable();
+ $this->viewUpdates();
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+ }
+
+ $ns = $this->mTitle->getNamespace(); # shortcut
+ $sk = $wgUser->getSkin();
+
# getOldID may want us to redirect somewhere else
- if ( $this->mRedirectUrl ) {
+ if( $this->mRedirectUrl ) {
$wgOut->redirect( $this->mRedirectUrl );
wfProfileOut( __METHOD__ );
return;
@@ -701,13 +726,14 @@ class Article {
$rdfrom = $wgRequest->getVal( 'rdfrom' );
$diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
$purge = $wgRequest->getVal( 'action' ) == 'purge';
+ $return404 = false;
$wgOut->setArticleFlag( true );
# Discourage indexing of printable versions, but encourage following
if( $wgOut->isPrintable() ) {
$policy = 'noindex,follow';
- } elseif ( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
+ } elseif( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
$policy = $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()];
} elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
# Honour customised robot policies for this namespace
@@ -720,10 +746,12 @@ class Article {
# If we got diff and oldid in the query, we want to see a
# diff page instead of the article.
- if ( !is_null( $diff ) ) {
+ if( !is_null( $diff ) ) {
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
- $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge );
+ $diff = $wgRequest->getVal( 'diff' );
+ $htmldiff = $wgRequest->getVal( 'htmldiff' , false);
+ $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $htmldiff);
// DifferenceEngine directly fetched the revision:
$this->mRevIdFetched = $de->mNewid;
$de->showDiffPage( $diffOnly );
@@ -738,51 +766,36 @@ class Article {
return;
}
- if ( empty( $oldid ) && $this->checkTouched() ) {
- $wgOut->setETag($parserCache->getETag($this, $wgUser));
-
- if( $wgOut->checkLastModified( $this->mTouched ) ){
- wfProfileOut( __METHOD__ );
- return;
- } else if ( $this->tryFileCache() ) {
- # tell wgOut that output is taken care of
- $wgOut->disable();
- $this->viewUpdates();
- wfProfileOut( __METHOD__ );
- return;
- }
- }
-
# Should the parser cache be used?
$pcache = $this->useParserCache( $oldid );
wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" );
- if ( $wgUser->getOption( 'stubthreshold' ) ) {
+ if( $wgUser->getOption( 'stubthreshold' ) ) {
wfIncrStats( 'pcache_miss_stub' );
}
$wasRedirected = false;
- if ( isset( $this->mRedirectedFrom ) ) {
+ if( isset( $this->mRedirectedFrom ) ) {
// This is an internally redirected page view.
// We'll need a backlink to the source page for navigation.
- if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
+ if( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
$redir = $sk->makeKnownLinkObj( $this->mRedirectedFrom, '', 'redirect=no' );
- $s = wfMsg( 'redirectedfrom', $redir );
+ $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
$wgOut->setSubtitle( $s );
// Set the fragment if one was specified in the redirect
- if ( strval( $this->mTitle->getFragment() ) != '' ) {
+ if( strval( $this->mTitle->getFragment() ) != '' ) {
$fragment = Xml::escapeJsString( $this->mTitle->getFragmentForURL() );
$wgOut->addInlineScript( "redirectToFragment(\"$fragment\");" );
}
$wasRedirected = true;
}
- } elseif ( !empty( $rdfrom ) ) {
+ } elseif( !empty( $rdfrom ) ) {
// This is an externally redirected view, from some other wiki.
// If it was reported from a trusted site, supply a backlink.
global $wgRedirectSources;
if( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
$redir = $sk->makeExternalLink( $rdfrom, $rdfrom );
- $s = wfMsg( 'redirectedfrom', $redir );
+ $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
$wgOut->setSubtitle( $s );
$wasRedirected = true;
}
@@ -790,18 +803,20 @@ class Article {
$outputDone = false;
wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$pcache ) );
- if ( $pcache ) {
- if ( $wgOut->tryParserCache( $this, $wgUser ) ) {
- // Ensure that UI elements requiring revision ID have
- // the correct version information.
- $wgOut->setRevisionId( $this->mLatest );
- $outputDone = true;
- }
+ if( $pcache && $wgOut->tryParserCache( $this, $wgUser ) ) {
+ // Ensure that UI elements requiring revision ID have
+ // the correct version information.
+ $wgOut->setRevisionId( $this->mLatest );
+ $outputDone = true;
}
# Fetch content and check for errors
- if ( !$outputDone ) {
+ if( !$outputDone ) {
+ # If the article does not exist and was deleted, show the log
+ if( $this->getID() == 0 ) {
+ $this->showDeletionLog();
+ }
$text = $this->getContent();
- if ( $text === false ) {
+ if( $text === false ) {
# Failed to load, replace text with error message
$t = $this->mTitle->getPrefixedText();
if( $oldid ) {
@@ -811,18 +826,38 @@ class Article {
$text = wfMsg( 'noarticletext' );
}
}
+
+ # Non-existent pages
+ if( $this->getID() === 0 ) {
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $text = "<div class='noarticletext'>\n$text\n</div>";
+ if( !$this->hasViewableContent() ) {
+ // If there's no backing content, send a 404 Not Found
+ // for better machine handling of broken links.
+ $return404 = true;
+ }
+ }
+
+ if( $return404 ) {
+ $wgRequest->response()->header( "HTTP/1.x 404 Not Found" );
+ }
# Another whitelist check in case oldid is altering the title
- if ( !$this->mTitle->userCanRead() ) {
+ if( !$this->mTitle->userCanRead() ) {
$wgOut->loginToUse();
$wgOut->output();
+ $wgOut->disable();
wfProfileOut( __METHOD__ );
- exit;
+ return;
}
+
+ # For ?curid=x urls, disallow indexing
+ if( $wgRequest->getInt('curid') )
+ $wgOut->setRobotPolicy( 'noindex,follow' );
# We're looking at an old revision
- if ( !empty( $oldid ) ) {
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ if( !empty( $oldid ) ) {
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
if( is_null( $this->mRevision ) ) {
// FIXME: This would be a nice place to load the 'no such page' text.
} else {
@@ -840,27 +875,27 @@ class Article {
}
}
}
-
+
$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' ) );
+ $wgOut->addHTML( wfMsgExt( 'clearyourcache', 'parse' ) );
// Give hooks a chance to customise the output
if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
// Wrap the whole lot in a <pre> and don't parse
$m = array();
preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
- $wgOut->addHtml( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
- $wgOut->addHtml( htmlspecialchars( $this->mContent ) );
- $wgOut->addHtml( "\n</pre>\n" );
+ $wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
+ $wgOut->addHTML( htmlspecialchars( $this->mContent ) );
+ $wgOut->addHTML( "\n</pre>\n" );
}
- } else if ( $rt = Title::newFromRedirect( $text ) ) {
+ } else if( $rt = Title::newFromRedirect( $text ) ) {
# Don't append the subtitle if this was an old revision
- $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() );
+ $wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
$parseout = $wgParser->parse($text, $this->mTitle, ParserOptions::newFromUser($wgUser));
$wgOut->addParserOutputNoText( $parseout );
- } else if ( $pcache ) {
+ } else if( $pcache ) {
# Display content and save to parser cache
$this->outputWikiText( $text );
} else {
@@ -876,7 +911,7 @@ class Article {
$time += wfTime();
# Timing hack
- if ( $time > 3 ) {
+ if( $time > 3 ) {
wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
$this->mTitle->getPrefixedDBkey()));
}
@@ -890,6 +925,14 @@ class Article {
$t = $wgOut->getPageTitle();
if( empty( $t ) ) {
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+
+ # For the main page, overwrite the <title> element with the con-
+ # tents of 'pagetitle-view-mainpage' instead of the default (if
+ # that's not empty).
+ if( $this->mTitle->equals( Title::newMainPage() ) &&
+ wfMsgForContent( 'pagetitle-view-mainpage' ) !== '' ) {
+ $wgOut->setHTMLTitle( wfMsgForContent( 'pagetitle-view-mainpage' ) );
+ }
}
# check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
@@ -899,7 +942,7 @@ class Article {
# If we have been passed an &rcid= parameter, we want to give the user a
# chance to mark this new article as patrolled.
- if( !is_null( $rcid ) && $rcid != 0 && $wgUser->isAllowed( 'patrol' ) && $this->mTitle->exists() ) {
+ if( !empty($rcid) && $this->mTitle->exists() && $this->mTitle->userCan('patrol') ) {
$wgOut->addHTML(
"<div class='patrollink'>" .
wfMsgHtml( 'markaspatrolledlink',
@@ -911,19 +954,45 @@ class Article {
}
# Trackbacks
- if ($wgUseTrackbacks)
+ if( $wgUseTrackbacks ) {
$this->addTrackbacks();
+ }
$this->viewUpdates();
wfProfileOut( __METHOD__ );
}
- /*
+ protected function showDeletionLog() {
+ global $wgUser, $wgOut;
+ $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut );
+ $pager = new LogPager( $loglist, 'delete', false, $this->mTitle->getPrefixedText() );
+ if( $pager->getNumRows() > 0 ) {
+ $pager->mLimit = 10;
+ $wgOut->addHTML( '<div class="mw-warning-with-logexcerpt">' );
+ $wgOut->addWikiMsg( 'deleted-notice' );
+ $wgOut->addHTML(
+ $loglist->beginLogEventsList() .
+ $pager->getBody() .
+ $loglist->endLogEventsList()
+ );
+ if( $pager->getNumRows() > 10 ) {
+ $wgOut->addHTML( $wgUser->getSkin()->link(
+ SpecialPage::getTitleFor( 'Log' ),
+ wfMsgHtml( 'deletelog-fulllog' ),
+ array(),
+ array( 'type' => 'delete', 'page' => $this->mTitle->getPrefixedText() )
+ ) );
+ }
+ $wgOut->addHTML( '</div>' );
+ }
+ }
+
+ /*
* Should the parser cache be used?
*/
protected function useParserCache( $oldid ) {
global $wgUser, $wgEnableParserCache;
-
+
return $wgEnableParserCache
&& intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
&& $this->exists()
@@ -931,47 +1000,48 @@ class Article {
&& !$this->mTitle->isCssOrJsPage()
&& !$this->mTitle->isCssJsSubpage();
}
-
- protected function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
+
+ /**
+ * View redirect
+ * @param $target Title object of destination to redirect
+ * @param $appendSubtitle Boolean [optional]
+ * @param $forceKnown Boolean: should the image be shown as a bluelink regardless of existence?
+ */
+ public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
global $wgParser, $wgOut, $wgContLang, $wgStylePath, $wgUser;
-
# 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 )
+ if( $forceKnown ) {
$link = $sk->makeKnownLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
- else
+ } else {
$link = $sk->makeLinkObj( $target, htmlspecialchars( $target->getFullText() ) );
+ }
+ return '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
+ '<span class="redirectText">'.$link.'</span>';
- $wgOut->addHTML( '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
- '<span class="redirectText">'.$link.'</span>' );
-
}
- function addTrackbacks() {
+ public function addTrackbacks() {
global $wgOut, $wgUser;
-
- $dbr = wfGetDB(DB_SLAVE);
- $tbs = $dbr->select(
- /* FROM */ 'trackbacks',
- /* SELECT */ array('tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name'),
- /* WHERE */ array('tb_page' => $this->getID())
+ $dbr = wfGetDB( DB_SLAVE );
+ $tbs = $dbr->select( 'trackbacks',
+ array('tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name'),
+ array('tb_page' => $this->getID() )
);
-
- if (!$dbr->numrows($tbs))
- return;
+ if( !$dbr->numRows($tbs) ) return;
$tbtext = "";
- while ($o = $dbr->fetchObject($tbs)) {
+ while( $o = $dbr->fetchObject($tbs) ) {
$rmvtxt = "";
- if ($wgUser->isAllowed( 'trackback' )) {
- $delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid="
- . $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
+ if( $wgUser->isAllowed( 'trackback' ) ) {
+ $delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid=" .
+ $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
$rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
$tbtext .= "\n";
@@ -983,33 +1053,31 @@ class Article {
$rmvtxt);
}
$wgOut->addWikiMsg( 'trackbackbox', $tbtext );
+ $this->mTitle->invalidateCache();
}
- function deletetrackback() {
+ public function deletetrackback() {
global $wgUser, $wgRequest, $wgOut, $wgTitle;
-
- if (!$wgUser->matchEditToken($wgRequest->getVal('token'))) {
+ if( !$wgUser->matchEditToken($wgRequest->getVal('token')) ) {
$wgOut->addWikiMsg( 'sessionfailure' );
return;
}
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
-
- if (count($permission_errors)>0)
- {
+ if( count($permission_errors) ) {
$wgOut->showPermissionsErrorPage( $permission_errors );
return;
}
- $db = wfGetDB(DB_MASTER);
- $db->delete('trackbacks', array('tb_id' => $wgRequest->getInt('tbid')));
- $wgTitle->invalidateCache();
- $wgOut->addWikiMsg('trackbackdeleteok');
+ $db = wfGetDB( DB_MASTER );
+ $db->delete( 'trackbacks', array('tb_id' => $wgRequest->getInt('tbid')) );
+
+ $wgOut->addWikiMsg( 'trackbackdeleteok' );
+ $this->mTitle->invalidateCache();
}
- function render() {
+ public function render() {
global $wgOut;
-
$wgOut->setArticleBodyOnly(true);
$this->view();
}
@@ -1017,37 +1085,36 @@ class Article {
/**
* Handle action=purge
*/
- function purge() {
+ public function purge() {
global $wgUser, $wgRequest, $wgOut;
-
- if ( $wgUser->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) {
+ if( $wgUser->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) {
if( wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
$this->doPurge();
+ $this->view();
}
} else {
- $msg = $wgOut->parse( wfMsg( 'confirm_purge' ) );
- $action = htmlspecialchars( $_SERVER['REQUEST_URI'] );
- $button = htmlspecialchars( wfMsg( 'confirm_purge_button' ) );
- $msg = str_replace( '$1',
- "<form method=\"post\" action=\"$action\">\n" .
- "<input type=\"submit\" name=\"submit\" value=\"$button\" />\n" .
- "</form>\n", $msg );
-
+ $action = htmlspecialchars( $wgRequest->getRequestURL() );
+ $button = wfMsgExt( 'confirm_purge_button', array('escapenoentities') );
+ $form = "<form method=\"post\" action=\"$action\">\n" .
+ "<input type=\"submit\" name=\"submit\" value=\"$button\" />\n" .
+ "</form>\n";
+ $top = wfMsgExt( 'confirm-purge-top', array('parse') );
+ $bottom = wfMsgExt( 'confirm-purge-bottom', array('parse') );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
- $wgOut->addHTML( $msg );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->addHTML( $top . $form . $bottom );
}
}
/**
* Perform the actions of a page purging
*/
- function doPurge() {
+ public function doPurge() {
global $wgUseSquid;
// Invalidate the cache
$this->mTitle->invalidateCache();
- if ( $wgUseSquid ) {
+ if( $wgUseSquid ) {
// Commit the transaction before the purge is sent
$dbw = wfGetDB( DB_MASTER );
$dbw->immediateCommit();
@@ -1056,16 +1123,15 @@ class Article {
$update = SquidUpdate::newSimplePurge( $this->mTitle );
$update->doUpdate();
}
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
global $wgMessageCache;
- if ( $this->getID() == 0 ) {
+ if( $this->getID() == 0 ) {
$text = false;
} else {
$text = $this->getContent();
}
$wgMessageCache->replace( $this->mTitle->getDBkey(), $text );
}
- $this->view();
}
/**
@@ -1075,11 +1141,11 @@ class Article {
* or else the record will be left in a funky state.
* Best if all done inside a transaction.
*
- * @param Database $dbw
- * @return int The newly created page_id key
+ * @param $dbw Database
+ * @return int The newly created page_id key, or false if the title already existed
* @private
*/
- function insertOn( $dbw ) {
+ public function insertOn( $dbw ) {
wfProfileIn( __METHOD__ );
$page_id = $dbw->nextSequenceValue( 'page_page_id_seq' );
@@ -1095,31 +1161,33 @@ class Article {
'page_touched' => $dbw->timestamp(),
'page_latest' => 0, # Fill this in shortly...
'page_len' => 0, # Fill this in shortly...
- ), __METHOD__ );
- $newid = $dbw->insertId();
-
- $this->mTitle->resetArticleId( $newid );
+ ), __METHOD__, 'IGNORE' );
+ $affected = $dbw->affectedRows();
+ if( $affected ) {
+ $newid = $dbw->insertId();
+ $this->mTitle->resetArticleId( $newid );
+ }
wfProfileOut( __METHOD__ );
- return $newid;
+ return $affected ? $newid : false;
}
/**
* Update the page record to point to a newly saved revision.
*
- * @param Database $dbw
- * @param Revision $revision For ID number, and text used to set
- length and redirect status fields
- * @param int $lastRevision If given, will not overwrite the page field
- * when different from the currently set value.
- * Giving 0 indicates the new page flag should
- * be set on.
- * @param bool $lastRevIsRedirect If given, will optimize adding and
- * removing rows in redirect table.
+ * @param $dbw Database object
+ * @param $revision Revision: For ID number, and text used to set
+ length and redirect status fields
+ * @param $lastRevision Integer: if given, will not overwrite the page field
+ * when different from the currently set value.
+ * Giving 0 indicates the new page flag should be set
+ * on.
+ * @param $lastRevIsRedirect Boolean: if given, will optimize adding and
+ * removing rows in redirect table.
* @return bool true on success, false on failure
* @private
*/
- function updateRevisionOn( &$dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) {
+ public function updateRevisionOn( &$dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) {
wfProfileIn( __METHOD__ );
$text = $revision->getText();
@@ -1143,8 +1211,7 @@ class Article {
__METHOD__ );
$result = $dbw->affectedRows() != 0;
-
- if ($result) {
+ if( $result ) {
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
}
@@ -1155,46 +1222,40 @@ class Article {
/**
* Add row to the redirect table if this is a redirect, remove otherwise.
*
- * @param Database $dbw
+ * @param $dbw Database
* @param $redirectTitle a title object pointing to the redirect target,
- * or NULL if this is not a redirect
- * @param bool $lastRevIsRedirect If given, will optimize adding and
- * removing rows in redirect table.
+ * or NULL if this is not a redirect
+ * @param $lastRevIsRedirect If given, will optimize adding and
+ * removing rows in redirect table.
* @return bool true on success, false on failure
* @private
*/
- function updateRedirectOn( &$dbw, $redirectTitle, $lastRevIsRedirect = null ) {
-
+ public function updateRedirectOn( &$dbw, $redirectTitle, $lastRevIsRedirect = null ) {
// Always update redirects (target link might have changed)
// Update/Insert if we don't know if the last revision was a redirect or not
// Delete if changing from redirect to non-redirect
$isRedirect = !is_null($redirectTitle);
- if ($isRedirect || is_null($lastRevIsRedirect) || $lastRevIsRedirect !== $isRedirect) {
-
+ if($isRedirect || is_null($lastRevIsRedirect) || $lastRevIsRedirect !== $isRedirect) {
wfProfileIn( __METHOD__ );
-
- if ($isRedirect) {
-
+ if( $isRedirect ) {
// This title is a redirect, Add/Update row in the redirect table
$set = array( /* SET */
'rd_namespace' => $redirectTitle->getNamespace(),
'rd_title' => $redirectTitle->getDBkey(),
'rd_from' => $this->getId(),
);
-
$dbw->replace( 'redirect', array( 'rd_from' ), $set, __METHOD__ );
} else {
// This is not a redirect, remove row from redirect table
$where = array( 'rd_from' => $this->getId() );
$dbw->delete( 'redirect', $where, __METHOD__);
}
-
- if( $this->getTitle()->getNamespace() == NS_IMAGE )
+ if( $this->getTitle()->getNamespace() == NS_FILE ) {
RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() );
+ }
wfProfileOut( __METHOD__ );
return ( $dbw->affectedRows() != 0 );
}
-
return true;
}
@@ -1202,12 +1263,11 @@ class Article {
* If the given revision is newer than the currently set page_latest,
* update the page record. Otherwise, do nothing.
*
- * @param Database $dbw
- * @param Revision $revision
+ * @param $dbw Database object
+ * @param $revision Revision object
*/
- function updateIfNewerOn( &$dbw, $revision ) {
+ public function updateIfNewerOn( &$dbw, $revision ) {
wfProfileIn( __METHOD__ );
-
$row = $dbw->selectRow(
array( 'revision', 'page' ),
array( 'rev_id', 'rev_timestamp', 'page_is_redirect' ),
@@ -1227,28 +1287,27 @@ class Article {
$prev = 0;
$lastRevIsRedirect = null;
}
-
$ret = $this->updateRevisionOn( $dbw, $revision, $prev, $lastRevIsRedirect );
wfProfileOut( __METHOD__ );
return $ret;
}
/**
+ * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...)
* @return string Complete article text, or null if error
*/
- function replaceSection($section, $text, $summary = '', $edittime = NULL) {
+ public function replaceSection( $section, $text, $summary = '', $edittime = NULL ) {
wfProfileIn( __METHOD__ );
-
- if( $section == '' ) {
- // Whole-page edit; let the text through unmolested.
+ if( strval( $section ) == '' ) {
+ // Whole-page edit; let the whole text through
} else {
- if( is_null( $edittime ) ) {
+ if( is_null($edittime) ) {
$rev = Revision::newFromTitle( $this->mTitle );
} else {
$dbw = wfGetDB( DB_MASTER );
$rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
}
- if( is_null( $rev ) ) {
+ if( !$rev ) {
wfDebug( "Article::replaceSection asked for bogus section (page: " .
$this->getId() . "; section: $section; edittime: $edittime)\n" );
return null;
@@ -1266,9 +1325,7 @@ class Article {
global $wgParser;
$text = $wgParser->replaceSection( $oldtext, $section, $text );
}
-
}
-
wfProfileOut( __METHOD__ );
return $text;
}
@@ -1277,27 +1334,28 @@ class Article {
* @deprecated use Article::doEdit()
*/
function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC=false, $comment=false, $bot=false ) {
+ wfDeprecated( __METHOD__ );
$flags = EDIT_NEW | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
( $isminor ? EDIT_MINOR : 0 ) |
( $suppressRC ? EDIT_SUPPRESS_RC : 0 ) |
( $bot ? EDIT_FORCE_BOT : 0 );
# If this is a comment, add the summary as headline
- if ( $comment && $summary != "" ) {
+ if( $comment && $summary != "" ) {
$text = wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n".$text;
}
$this->doEdit( $text, $summary, $flags );
$dbw = wfGetDB( DB_MASTER );
- if ($watchthis) {
- if (!$this->mTitle->userIsWatching()) {
+ if($watchthis) {
+ if(!$this->mTitle->userIsWatching()) {
$dbw->begin();
$this->doWatch();
$dbw->commit();
}
} else {
- if ( $this->mTitle->userIsWatching() ) {
+ if( $this->mTitle->userIsWatching() ) {
$dbw->begin();
$this->doUnwatch();
$dbw->commit();
@@ -1310,33 +1368,36 @@ class Article {
* @deprecated use Article::doEdit()
*/
function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) {
+ wfDeprecated( __METHOD__ );
$flags = EDIT_UPDATE | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
( $minor ? EDIT_MINOR : 0 ) |
( $forceBot ? EDIT_FORCE_BOT : 0 );
- $good = $this->doEdit( $text, $summary, $flags );
- if ( $good ) {
- $dbw = wfGetDB( DB_MASTER );
- if ($watchthis) {
- if (!$this->mTitle->userIsWatching()) {
- $dbw->begin();
- $this->doWatch();
- $dbw->commit();
- }
- } else {
- if ( $this->mTitle->userIsWatching() ) {
- $dbw->begin();
- $this->doUnwatch();
- $dbw->commit();
- }
+ $status = $this->doEdit( $text, $summary, $flags );
+ if( !$status->isOK() ) {
+ return false;
+ }
+
+ $dbw = wfGetDB( DB_MASTER );
+ if( $watchthis ) {
+ if(!$this->mTitle->userIsWatching()) {
+ $dbw->begin();
+ $this->doWatch();
+ $dbw->commit();
}
+ } else {
+ if( $this->mTitle->userIsWatching() ) {
+ $dbw->begin();
+ $this->doUnwatch();
+ $dbw->commit();
+ }
+ }
- $extraQuery = ''; // Give extensions a chance to modify URL query on update
- wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraQuery ) );
+ $extraQuery = ''; // Give extensions a chance to modify URL query on update
+ wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraQuery ) );
- $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraQuery );
- }
- return $good;
+ $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraQuery );
+ return true;
}
/**
@@ -1347,9 +1408,9 @@ class Article {
*
* $wgUser must be set before calling this function.
*
- * @param string $text New text
- * @param string $summary Edit summary
- * @param integer $flags bitfield:
+ * @param $text String: new text
+ * @param $summary String: edit summary
+ * @param $flags Integer bitfield:
* EDIT_NEW
* Article is known or assumed to be non-existent, create a new one
* EDIT_UPDATE
@@ -1366,40 +1427,67 @@ class Article {
* Fill in blank summaries with generated text where possible
*
* If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
- * If EDIT_UPDATE is specified and the article doesn't exist, the function will return false. If
- * EDIT_NEW is specified and the article does exist, a duplicate key error will cause an exception
- * to be thrown from the Database. These two conditions are also possible with auto-detection due
- * to MediaWiki's performance-optimised locking strategy.
- * @param $baseRevId, the revision ID this edit was based off, if any
+ * If EDIT_UPDATE is specified and the article doesn't exist, the function will an
+ * edit-gone-missing error. If EDIT_NEW is specified and the article does exist, an
+ * edit-already-exists error will be returned. These two conditions are also possible with
+ * auto-detection due to MediaWiki's performance-optimised locking strategy.
+ *
+ * @param $baseRevId the revision ID this edit was based off, if any
+ * @param $user Optional user object, $wgUser will be used if not passed
*
- * @return bool success
+ * @return Status object. Possible errors:
+ * edit-hook-aborted: The ArticleSave hook aborted the edit but didn't set the fatal flag of $status
+ * edit-gone-missing: In update mode, but the article didn't exist
+ * edit-conflict: In update mode, the article changed unexpectedly
+ * edit-no-change: Warning that the text was the same as before
+ * edit-already-exists: In creation mode, but the article already exists
+ *
+ * Extensions may define additional errors.
+ *
+ * $return->value will contain an associative array with members as follows:
+ * new: Boolean indicating if the function attempted to create a new article
+ * revision: The revision object for the inserted revision, or null
+ *
+ * Compatibility note: this function previously returned a boolean value indicating success/failure
*/
- function doEdit( $text, $summary, $flags = 0, $baseRevId = false ) {
+ public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
+ # Low-level sanity check
+ if( $this->mTitle->getText() == '' ) {
+ throw new MWException( 'Something is trying to edit an article with an empty title' );
+ }
+
wfProfileIn( __METHOD__ );
- $good = true;
- if ( !($flags & EDIT_NEW) && !($flags & EDIT_UPDATE) ) {
- $aid = $this->mTitle->getArticleID( GAID_FOR_UPDATE );
- if ( $aid ) {
+ $user = is_null($user) ? $wgUser : $user;
+ $status = Status::newGood( array() );
+
+ # Load $this->mTitle->getArticleID() and $this->mLatest if it's not already
+ $this->loadPageData();
+
+ if( !($flags & EDIT_NEW) && !($flags & EDIT_UPDATE) ) {
+ $aid = $this->mTitle->getArticleID();
+ if( $aid ) {
$flags |= EDIT_UPDATE;
} else {
$flags |= EDIT_NEW;
}
}
- if( !wfRunHooks( 'ArticleSave', array( &$this, &$wgUser, &$text,
- &$summary, $flags & EDIT_MINOR,
- null, null, &$flags ) ) )
+ if( !wfRunHooks( 'ArticleSave', array( &$this, &$user, &$text, &$summary,
+ $flags & EDIT_MINOR, null, null, &$flags, &$status ) ) )
{
wfDebug( __METHOD__ . ": ArticleSave hook aborted save!\n" );
wfProfileOut( __METHOD__ );
- return false;
+ if( $status->isOK() ) {
+ $status->fatal( 'edit-hook-aborted');
+ }
+ return $status;
}
# Silently ignore EDIT_MINOR if not allowed
- $isminor = ( $flags & EDIT_MINOR ) && $wgUser->isAllowed('minoredit');
+ $isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed('minoredit');
$bot = $flags & EDIT_FORCE_BOT;
$oldtext = $this->getContent();
@@ -1417,32 +1505,29 @@ class Article {
$dbw = wfGetDB( DB_MASTER );
$now = wfTimestampNow();
- if ( $flags & EDIT_UPDATE ) {
+ if( $flags & EDIT_UPDATE ) {
# Update article, but only if changed.
-
+ $status->value['new'] = false;
# Make sure the revision is either completely inserted or not inserted at all
if( !$wgDBtransactions ) {
$userAbort = ignore_user_abort( true );
}
- $lastRevision = 0;
$revisionId = 0;
$changed = ( strcmp( $text, $oldtext ) != 0 );
- if ( $changed ) {
+ if( $changed ) {
$this->mGoodAdjustment = (int)$this->isCountable( $text )
- (int)$this->isCountable( $oldtext );
$this->mTotalAdjustment = 0;
- $lastRevision = $dbw->selectField(
- 'page', 'page_latest', array( 'page_id' => $this->getId() ) );
-
- if ( !$lastRevision ) {
+ if( !$this->mLatest ) {
# Article gone missing
wfDebug( __METHOD__.": EDIT_UPDATE specified but article doesn't exist\n" );
+ $status->fatal( 'edit-gone-missing' );
wfProfileOut( __METHOD__ );
- return false;
+ return $status;
}
$revision = new Revision( array(
@@ -1450,38 +1535,54 @@ class Article {
'comment' => $summary,
'minor_edit' => $isminor,
'text' => $text,
- 'parent_id' => $lastRevision
+ 'parent_id' => $this->mLatest,
+ 'user' => $user->getId(),
+ 'user_text' => $user->getName(),
) );
$dbw->begin();
$revisionId = $revision->insertOn( $dbw );
# Update page
- $ok = $this->updateRevisionOn( $dbw, $revision, $lastRevision );
+ #
+ # Note that we use $this->mLatest instead of fetching a value from the master DB
+ # during the course of this function. This makes sure that EditPage can detect
+ # edit conflicts reliably, either by $ok here, or by $article->getTimestamp()
+ # before this function is called. A previous function used a separate query, this
+ # creates a window where concurrent edits can cause an ignored edit conflict.
+ $ok = $this->updateRevisionOn( $dbw, $revision, $this->mLatest );
if( !$ok ) {
/* Belated edit conflict! Run away!! */
- $good = false;
+ $status->fatal( 'edit-conflict' );
+ # Delete the invalid revision if the DB is not transactional
+ if( !$wgDBtransactions ) {
+ $dbw->delete( 'revision', array( 'rev_id' => $revisionId ), __METHOD__ );
+ }
+ $revisionId = 0;
$dbw->rollback();
} else {
- wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId ) );
-
+ global $wgUseRCPatrol;
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, $baseRevId, $user) );
# Update recentchanges
if( !( $flags & EDIT_SUPPRESS_RC ) ) {
- $rcid = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $wgUser, $summary,
- $lastRevision, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
- $revisionId );
-
# Mark as patrolled if the user can do so
- if( $GLOBALS['wgUseRCPatrol'] && $wgUser->isAllowed( 'autopatrol' ) ) {
- RecentChange::markPatrolled( $rcid );
- PatrolLog::record( $rcid, true );
+ $patrolled = $wgUseRCPatrol && $this->mTitle->userCan('autopatrol');
+ # Add RC row to the DB
+ $rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
+ $this->mLatest, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
+ $revisionId, $patrolled
+ );
+ # Log auto-patrolled edits
+ if( $patrolled ) {
+ PatrolLog::record( $rc, true );
}
}
- $wgUser->incEditCount();
+ $user->incEditCount();
$dbw->commit();
}
} else {
+ $status->warning( 'edit-no-change' );
$revision = null;
// Keep the same revision ID, but do some updates on it
$revisionId = $this->getRevIdFetched();
@@ -1493,17 +1594,20 @@ class Article {
if( !$wgDBtransactions ) {
ignore_user_abort( $userAbort );
}
-
- if ( $good ) {
- # 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 );
+ // Now that ignore_user_abort is restored, we can respond to fatal errors
+ if( !$status->isOK() ) {
+ wfProfileOut( __METHOD__ );
+ return $status;
}
+
+ # Invalidate cache of this article and all pages using this article
+ # as a template. Partly deferred. Leave templatelinks for editUpdates().
+ Article::onArticleEdit( $this->mTitle, 'skiptransclusions' );
+ # Update links tables, site stats, etc.
+ $this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
} else {
# Create new article
+ $status->value['new'] = true;
# Set statistics members
# We work out if it's countable after PST to avoid counter drift
@@ -1514,15 +1618,24 @@ class Article {
$dbw->begin();
# Add the page record; stake our claim on this title!
- # This will fail with a database query exception if the article already exists
+ # This will return false if the article already exists
$newid = $this->insertOn( $dbw );
+ if( $newid === false ) {
+ $dbw->rollback();
+ $status->fatal( 'edit-already-exists' );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
# Save the revision text...
$revision = new Revision( array(
'page' => $newid,
'comment' => $summary,
'minor_edit' => $isminor,
- 'text' => $text
+ 'text' => $text,
+ 'user' => $user->getId(),
+ 'user_text' => $user->getName(),
) );
$revisionId = $revision->insertOn( $dbw );
@@ -1530,19 +1643,22 @@ class Article {
# Update the page record with revision data
$this->updateRevisionOn( $dbw, $revision, 0 );
-
- wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false, $user) );
+ # Update recentchanges
if( !( $flags & EDIT_SUPPRESS_RC ) ) {
- $rcid = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary, $bot,
- '', strlen( $text ), $revisionId );
- # Mark as patrolled if the user can
- if( ($GLOBALS['wgUseRCPatrol'] || $GLOBALS['wgUseNPPatrol']) && $wgUser->isAllowed( 'autopatrol' ) ) {
- RecentChange::markPatrolled( $rcid );
- PatrolLog::record( $rcid, true );
+ global $wgUseRCPatrol, $wgUseNPPatrol;
+ # Mark as patrolled if the user can do so
+ $patrolled = ($wgUseRCPatrol || $wgUseNPPatrol) && $this->mTitle->userCan('autopatrol');
+ # Add RC row to the DB
+ $rc = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot,
+ '', strlen($text), $revisionId, $patrolled );
+ # Log auto-patrolled edits
+ if( $patrolled ) {
+ PatrolLog::record( $rc, true );
}
}
- $wgUser->incEditCount();
+ $user->incEditCount();
$dbw->commit();
# Update links, etc.
@@ -1551,27 +1667,30 @@ class Article {
# Clear caches
Article::onArticleCreate( $this->mTitle );
- wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+ wfRunHooks( 'ArticleInsertComplete', array( &$this, &$user, $text, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
}
- if ( $good && !( $flags & EDIT_DEFER_UPDATES ) ) {
+ # Do updates right now unless deferral was requested
+ if( !( $flags & EDIT_DEFER_UPDATES ) ) {
wfDoUpdates();
}
- if ( $good ) {
- wfRunHooks( 'ArticleSaveComplete', array( &$this, &$wgUser, $text, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
- }
+ // Return the new revision (or null) to the caller
+ $status->value['revision'] = $revision;
+
+ wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $text, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status ) );
wfProfileOut( __METHOD__ );
- return $good;
+ return $status;
}
/**
* @deprecated wrapper for doRedirect
*/
- function showArticle( $text, $subtitle , $sectionanchor = '', $me2, $now, $summary, $oldid ) {
+ public function showArticle( $text, $subtitle , $sectionanchor = '', $me2, $now, $summary, $oldid ) {
+ wfDeprecated( __METHOD__ );
$this->doRedirect( $this->isRedirect( $text ), $sectionanchor );
}
@@ -1579,13 +1698,13 @@ class Article {
* Output a redirect back to the article.
* This is typically used after an edit.
*
- * @param boolean $noRedir Add redirect=no
- * @param string $sectionAnchor section to redirect to, including "#"
- * @param string $extraQuery, extra query params
+ * @param $noRedir Boolean: add redirect=no
+ * @param $sectionAnchor String: section to redirect to, including "#"
+ * @param $extraQuery String: extra query params
*/
- function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
+ public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
global $wgOut;
- if ( $noRedir ) {
+ if( $noRedir ) {
$query = 'redirect=no';
if( $extraQuery )
$query .= "&$query";
@@ -1598,77 +1717,45 @@ class Article {
/**
* Mark this particular edit/page as patrolled
*/
- function markpatrolled() {
+ public function markpatrolled() {
global $wgOut, $wgRequest, $wgUseRCPatrol, $wgUseNPPatrol, $wgUser;
$wgOut->setRobotPolicy( 'noindex,nofollow' );
- # Check patrol config options
-
- if ( !($wgUseNPPatrol || $wgUseRCPatrol)) {
- $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
- return;
- }
-
# If we haven't been given an rc_id value, we can't do anything
$rcid = (int) $wgRequest->getVal('rcid');
- $rc = $rcid ? RecentChange::newFromId($rcid) : null;
- if ( is_null ( $rc ) )
- {
+ $rc = RecentChange::newFromId($rcid);
+ if( is_null($rc) ) {
$wgOut->showErrorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
return;
}
- 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->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
- return;
- }
+ #It would be nice to see where the user had actually come from, but for now just guess
+ $returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
+ $return = Title::makeTitle( NS_SPECIAL, $returnto );
- # Check permissions
- $permission_errors = $this->mTitle->getUserPermissionsErrors( 'patrol', $wgUser );
+ $dbw = wfGetDB( DB_MASTER );
+ $errors = $rc->doMarkPatrolled();
- if (count($permission_errors)>0)
- {
- $wgOut->showPermissionsErrorPage( $permission_errors );
+ if( in_array(array('rcpatroldisabled'), $errors) ) {
+ $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
return;
}
-
- # Handle the 'MarkPatrolled' hook
- if( !wfRunHooks( 'MarkPatrolled', array( $rcid, &$wgUser, false ) ) ) {
+
+ if( in_array(array('hookaborted'), $errors) ) {
+ // The hook itself has handled any output
return;
}
-
- #It would be nice to see where the user had actually come from, but for now just guess
- $returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
- $return = Title::makeTitle( NS_SPECIAL, $returnto );
-
- # If it's left up to us, check that the user is allowed to patrol this edit
- # If the user has the "autopatrol" right, then we'll assume there are no
- # other conditions stopping them doing so
- if( !$wgUser->isAllowed( 'autopatrol' ) ) {
- $rc = RecentChange::newFromId( $rcid );
- # Graceful error handling, as we've done before here...
- # (If the recent change doesn't exist, then it doesn't matter whether
- # the user is allowed to patrol it or not; nothing is going to happen
- if( is_object( $rc ) && $wgUser->getName() == $rc->getAttribute( 'rc_user_text' ) ) {
- # The user made this edit, and can't patrol it
- # Tell them so, and then back off
- $wgOut->setPageTitle( wfMsg( 'markedaspatrollederror' ) );
- $wgOut->addWikiMsg( 'markedaspatrollederror-noautopatrol' );
- $wgOut->returnToMain( false, $return );
- return;
- }
+
+ if( in_array(array('markedaspatrollederror-noautopatrol'), $errors) ) {
+ $wgOut->setPageTitle( wfMsg( 'markedaspatrollederror' ) );
+ $wgOut->addWikiMsg( 'markedaspatrollederror-noautopatrol' );
+ $wgOut->returnToMain( false, $return );
+ return;
}
- # 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 ) );
+ if( !empty($errors) ) {
+ $wgOut->showPermissionsErrorPage( $errors );
+ return;
}
# Inform the user
@@ -1681,26 +1768,21 @@ class Article {
* User-interface handler for the "watch" action
*/
- function watch() {
-
+ public function watch() {
global $wgUser, $wgOut;
-
- if ( $wgUser->isAnon() ) {
+ if( $wgUser->isAnon() ) {
$wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
return;
}
- if ( wfReadOnly() ) {
+ if( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
-
if( $this->doWatch() ) {
$wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
-
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'addedwatchtext', $this->mTitle->getPrefixedText() );
}
-
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
}
@@ -1708,44 +1790,36 @@ class Article {
* Add this page to $wgUser's watchlist
* @return bool true on successful watch operation
*/
- function doWatch() {
+ public function doWatch() {
global $wgUser;
if( $wgUser->isAnon() ) {
return false;
}
-
- if (wfRunHooks('WatchArticle', array(&$wgUser, &$this))) {
+ if( wfRunHooks('WatchArticle', array(&$wgUser, &$this)) ) {
$wgUser->addWatch( $this->mTitle );
-
return wfRunHooks('WatchArticleComplete', array(&$wgUser, &$this));
}
-
return false;
}
/**
* User interface handler for the "unwatch" action.
*/
- function unwatch() {
-
+ public function unwatch() {
global $wgUser, $wgOut;
-
- if ( $wgUser->isAnon() ) {
+ if( $wgUser->isAnon() ) {
$wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
return;
}
- if ( wfReadOnly() ) {
+ if( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
-
if( $this->doUnwatch() ) {
$wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
-
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'removedwatchtext', $this->mTitle->getPrefixedText() );
}
-
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
}
@@ -1753,25 +1827,22 @@ class Article {
* Stop watching a page
* @return bool true on successful unwatch
*/
- function doUnwatch() {
+ public function doUnwatch() {
global $wgUser;
if( $wgUser->isAnon() ) {
return false;
}
-
- if (wfRunHooks('UnwatchArticle', array(&$wgUser, &$this))) {
+ if( wfRunHooks('UnwatchArticle', array(&$wgUser, &$this)) ) {
$wgUser->removeWatch( $this->mTitle );
-
return wfRunHooks('UnwatchArticleComplete', array(&$wgUser, &$this));
}
-
return false;
}
/**
* action=protect handler
*/
- function protect() {
+ public function protect() {
$form = new ProtectionForm( $this );
$form->execute();
}
@@ -1779,26 +1850,28 @@ class Article {
/**
* action=unprotect handler (alias)
*/
- function unprotect() {
+ public function unprotect() {
$this->protect();
}
/**
* Update the article's restriction field, and leave a log entry.
*
- * @param array $limit set of restriction keys
- * @param string $reason
+ * @param $limit Array: set of restriction keys
+ * @param $reason String
+ * @param &$cascade Integer. Set to false if cascading protection isn't allowed.
+ * @param $expiry Array: per restriction type expiration
* @return bool true on success
*/
- function updateRestrictions( $limit = array(), $reason = '', $cascade = 0, $expiry = null ) {
+ public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
global $wgUser, $wgRestrictionTypes, $wgContLang;
$id = $this->mTitle->getArticleID();
- if( array() != $this->mTitle->getUserPermissionsErrors( 'protect', $wgUser ) || wfReadOnly() || $id == 0 ) {
+ if( $id <= 0 || wfReadOnly() || !$this->mTitle->userCan('protect') ) {
return false;
}
- if (!$cascade) {
+ if( !$cascade ) {
$cascade = false;
}
@@ -1808,34 +1881,39 @@ class Article {
# FIXME: Same limitations as described in ProtectionForm.php (line 37);
# we expect a single selection, but the schema allows otherwise.
$current = array();
- foreach( $wgRestrictionTypes as $action )
- $current[$action] = implode( '', $this->mTitle->getRestrictions( $action ) );
+ $updated = Article::flattenRestrictions( $limit );
+ $changed = false;
+ foreach( $wgRestrictionTypes as $action ) {
+ if( isset( $expiry[$action] ) ) {
+ # Get current restrictions on $action
+ $aLimits = $this->mTitle->getRestrictions( $action );
+ $current[$action] = implode( '', $aLimits );
+ # Are any actual restrictions being dealt with here?
+ $aRChanged = count($aLimits) || !empty($limit[$action]);
+ # If something changed, we need to log it. Checking $aRChanged
+ # assures that "unprotecting" a page that is not protected does
+ # not log just because the expiry was "changed".
+ if( $aRChanged && $this->mTitle->mRestrictionsExpiry[$action] != $expiry[$action] ) {
+ $changed = true;
+ }
+ }
+ }
$current = Article::flattenRestrictions( $current );
- $updated = Article::flattenRestrictions( $limit );
- $changed = ( $current != $updated );
+ $changed = ($changed || $current != $updated );
$changed = $changed || ($updated && $this->mTitle->areRestrictionsCascading() != $cascade);
- $changed = $changed || ($updated && $this->mTitle->mRestrictionsExpiry != $expiry);
$protect = ( $updated != '' );
# If nothing's changed, do nothing
if( $changed ) {
- global $wgGroupPermissions;
if( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
$dbw = wfGetDB( DB_MASTER );
-
- $encodedExpiry = Block::encodeExpiry($expiry, $dbw );
-
- $expiry_description = '';
- if ( $encodedExpiry != 'infinity' ) {
- $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry, false, false ) ).')';
- }
-
+
# Prepare a null revision to be added to the history
$modified = $current != '' && $protect;
- if ( $protect ) {
+ if( $protect ) {
$comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle';
} else {
$comment_type = 'unprotectedarticle';
@@ -1844,35 +1922,51 @@ class Article {
# 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').']';
+ $editrestriction = isset( $limit['edit'] ) ? array( $limit['edit'] ) : $this->mTitle->getRestrictions( 'edit' );
+ # The schema allows multiple restrictions
+ if(!in_array('protect', $editrestriction) && !in_array('sysop', $editrestriction))
+ $cascade = false;
+ $cascade_description = '';
+ if( $cascade ) {
+ $cascade_description = ' ['.wfMsgForContent('protect-summary-cascade').']';
}
if( $reason )
$comment .= ": $reason";
- if( $protect )
- $comment .= " [$updated]";
- if ( $expiry_description && $protect )
- $comment .= "$expiry_description";
- if ( $cascade )
- $comment .= "$cascade_description";
+ $editComment = $comment;
+ $encodedExpiry = array();
+ $protect_description = '';
+ foreach( $limit as $action => $restrictions ) {
+ $encodedExpiry[$action] = Block::encodeExpiry($expiry[$action], $dbw );
+ if( $restrictions != '' ) {
+ $protect_description .= "[$action=$restrictions] (";
+ if( $encodedExpiry[$action] != 'infinity' ) {
+ $protect_description .= wfMsgForContent( 'protect-expiring',
+ $wgContLang->timeanddate( $expiry[$action], false, false ) ,
+ $wgContLang->date( $expiry[$action], false, false ) ,
+ $wgContLang->time( $expiry[$action], false, false ) );
+ } else {
+ $protect_description .= wfMsgForContent( 'protect-expiry-indefinite' );
+ }
+ $protect_description .= ') ';
+ }
+ }
+ $protect_description = trim($protect_description);
+
+ if( $protect_description && $protect )
+ $editComment .= " ($protect_description)";
+ if( $cascade )
+ $editComment .= "$cascade_description";
# Update restrictions table
foreach( $limit as $action => $restrictions ) {
- if ($restrictions != '' ) {
+ if($restrictions != '' ) {
$dbw->replace( 'page_restrictions', array(array('pr_page', 'pr_type')),
- array( 'pr_page' => $id, 'pr_type' => $action
- , 'pr_level' => $restrictions, 'pr_cascade' => $cascade ? 1 : 0
- , 'pr_expiry' => $encodedExpiry ), __METHOD__ );
+ array( 'pr_page' => $id,
+ 'pr_type' => $action,
+ 'pr_level' => $restrictions,
+ 'pr_cascade' => ($cascade && $action == 'edit') ? 1 : 0,
+ 'pr_expiry' => $encodedExpiry[$action] ), __METHOD__ );
} else {
$dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
'pr_type' => $action ), __METHOD__ );
@@ -1880,9 +1974,10 @@ class Article {
}
# Insert a null revision
- $nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
+ $nullRevision = Revision::newNullRevision( $dbw, $id, $editComment, true );
$nullRevId = $nullRevision->insertOn( $dbw );
+ $latest = $this->getLatest();
# Update page record
$dbw->update( 'page',
array( /* SET */
@@ -1893,15 +1988,15 @@ class Article {
'page_id' => $id
), 'Article::protect'
);
-
- wfRunHooks( 'NewRevisionFromEditComplete', array($this, $nullRevision, false) );
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $nullRevision, $latest, $wgUser) );
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" ) );
+ $params = array($protect_description,$cascade ? 'cascade' : '');
+ $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason), $params );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
@@ -1915,11 +2010,10 @@ class Article {
/**
* Take an array of page restrictions and flatten it to a string
* suitable for insertion into the page_restrictions field.
- * @param array $limit
- * @return string
- * @private
+ * @param $limit Array
+ * @return String
*/
- function flattenRestrictions( $limit ) {
+ protected static function flattenRestrictions( $limit ) {
if( !is_array( $limit ) ) {
throw new MWException( 'Article::flattenRestrictions given non-array restriction set' );
}
@@ -1935,26 +2029,24 @@ class Article {
/**
* Auto-generates a deletion reason
- * @param bool &$hasHistory Whether the page has a history
+ * @param &$hasHistory Boolean: whether the page has a history
*/
- public function generateReason(&$hasHistory)
- {
+ public function generateReason( &$hasHistory ) {
global $wgContLang;
- $dbw = wfGetDB(DB_MASTER);
+ $dbw = wfGetDB( DB_MASTER );
// Get the last revision
- $rev = Revision::newFromTitle($this->mTitle);
- if(is_null($rev))
+ $rev = Revision::newFromTitle( $this->mTitle );
+ if( is_null( $rev ) )
return false;
+
// Get the article's contents
$contents = $rev->getText();
$blank = false;
// If the page is blank, use the text from the previous revision,
// which can only be blank if there's a move/import/protect dummy revision involved
- if($contents == '')
- {
+ if( $contents == '' ) {
$prev = $rev->getPrevious();
- if($prev)
- {
+ if( $prev ) {
$contents = $prev->getText();
$blank = true;
}
@@ -1963,44 +2055,51 @@ class Article {
// Find out if there was only one contributor
// Only scan the last 20 revisions
$limit = 20;
- $res = $dbw->select('revision', 'rev_user_text', array('rev_page' => $this->getID()), __METHOD__,
- array('LIMIT' => $limit));
- if($res === false)
+ $res = $dbw->select( 'revision', 'rev_user_text',
+ array( 'rev_page' => $this->getID() ), __METHOD__,
+ array( 'LIMIT' => $limit )
+ );
+ if( $res === false )
// This page has no revisions, which is very weird
return false;
- if($res->numRows() > 1)
+ if( $res->numRows() > 1 )
$hasHistory = true;
else
$hasHistory = false;
- $row = $dbw->fetchObject($res);
+ $row = $dbw->fetchObject( $res );
$onlyAuthor = $row->rev_user_text;
// Try to find a second contributor
- while( $row = $dbw->fetchObject($res) ) {
- if($row->rev_user_text != $onlyAuthor) {
+ foreach( $res as $row ) {
+ if( $row->rev_user_text != $onlyAuthor ) {
$onlyAuthor = false;
break;
}
}
- $dbw->freeResult($res);
+ $dbw->freeResult( $res );
// Generate the summary with a '$1' placeholder
- if($blank) {
+ if( $blank ) {
// The current revision is blank and the one before is also
// blank. It's just not our lucky day
- $reason = wfMsgForContent('exbeforeblank', '$1');
+ $reason = wfMsgForContent( 'exbeforeblank', '$1' );
} else {
- if($onlyAuthor)
- $reason = wfMsgForContent('excontentauthor', '$1', $onlyAuthor);
+ if( $onlyAuthor )
+ $reason = wfMsgForContent( 'excontentauthor', '$1', $onlyAuthor );
else
- $reason = wfMsgForContent('excontent', '$1');
+ $reason = wfMsgForContent( 'excontent', '$1' );
+ }
+
+ if( $reason == '-' ) {
+ // Allow these UI messages to be blanked out cleanly
+ return '';
}
// Replace newlines with spaces to prevent uglyness
- $contents = preg_replace("/[\n\r]/", ' ', $contents);
+ $contents = preg_replace( "/[\n\r]/", ' ', $contents );
// Calculate the maximum amount of chars to get
// Max content length = max comment length - length of the comment (excl. $1) - '...'
- $maxLength = 255 - (strlen($reason) - 2) - 3;
- $contents = $wgContLang->truncate($contents, $maxLength, '...');
+ $maxLength = 255 - (strlen( $reason ) - 2) - 3;
+ $contents = $wgContLang->truncate( $contents, $maxLength, '...' );
// Remove possible unfinished links
$contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
// Now replace the '$1' placeholder
@@ -2012,7 +2111,7 @@ class Article {
/*
* UI entry point for page deletion
*/
- function delete() {
+ public function delete() {
global $wgUser, $wgOut, $wgRequest;
$confirm = $wgRequest->wasPosted() &&
@@ -2023,19 +2122,19 @@ class Article {
$reason = $this->DeleteReasonList;
- if ( $reason != 'other' && $this->DeleteReason != '') {
+ if( $reason != 'other' && $this->DeleteReason != '' ) {
// Entry from drop down menu + additional comment
$reason .= ': ' . $this->DeleteReason;
- } elseif ( $reason == 'other' ) {
+ } elseif( $reason == 'other' ) {
$reason = $this->DeleteReason;
}
# Flag to hide all contents of the archived revisions
- $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('suppressrevision');
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed( 'suppressrevision' );
# This code desperately needs to be totally rewritten
# Read-only check...
- if ( wfReadOnly() ) {
+ if( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
@@ -2043,7 +2142,7 @@ class Article {
# Check permissions
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
- if (count($permission_errors)>0) {
+ if( count( $permission_errors ) > 0 ) {
$wgOut->showPermissionsErrorPage( $permission_errors );
return;
}
@@ -2054,8 +2153,10 @@ class Article {
$dbw = wfGetDB( DB_MASTER );
$conds = $this->mTitle->pageCond();
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
- if ( $latest === false ) {
- $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
+ if( $latest === false ) {
+ $wgOut->showFatalError( wfMsgExt( 'cannotdelete', array( 'parse' ) ) );
+ $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
+ LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
return;
}
@@ -2080,12 +2181,12 @@ class Article {
// Generate deletion reason
$hasHistory = false;
- if ( !$reason ) $reason = $this->generateReason($hasHistory);
+ if( !$reason ) $reason = $this->generateReason($hasHistory);
// If the page has a history, insert a warning
if( $hasHistory && !$confirm ) {
- $skin=$wgUser->getSkin();
- $wgOut->addHTML( '<strong>' . wfMsg( 'historywarning' ) . ' ' . $skin->historyLink() . '</strong>' );
+ $skin = $wgUser->getSkin();
+ $wgOut->addHTML( '<strong>' . wfMsgExt( 'historywarning', array( 'parseinline' ) ) . ' ' . $skin->historyLink() . '</strong>' );
if( $bigHistory ) {
global $wgLang, $wgDeleteRevisionsLimit;
$wgOut->wrapWikiMsg( "<div class='error'>\n$1</div>\n",
@@ -2099,7 +2200,7 @@ class Article {
/**
* @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
*/
- function isBigDeletion() {
+ public function isBigDeletion() {
global $wgDeleteRevisionsLimit;
if( $wgDeleteRevisionsLimit ) {
$revCount = $this->estimateRevisionCount();
@@ -2111,8 +2212,8 @@ class Article {
/**
* @return int approximate revision count
*/
- function estimateRevisionCount() {
- $dbr = wfGetDB();
+ public function estimateRevisionCount() {
+ $dbr = wfGetDB( DB_SLAVE );
// For an exact count...
//return $dbr->selectField( 'revision', 'COUNT(*)',
// array( 'rev_page' => $this->getId() ), __METHOD__ );
@@ -2122,13 +2223,12 @@ class Article {
/**
* Get the last N authors
- * @param int $num Number of revisions to get
- * @param string $revLatest The latest rev_id, selected from the master (optional)
+ * @param $num Integer: number of revisions to get
+ * @param $revLatest String: the latest rev_id, selected from the master (optional)
* @return array Array of authors, duplicates not removed
*/
- function getLastNAuthors( $num, $revLatest = 0 ) {
+ public function getLastNAuthors( $num, $revLatest = 0 ) {
wfProfileIn( __METHOD__ );
-
// First try the slave
// If that doesn't have the latest revision, try the master
$continue = 2;
@@ -2145,12 +2245,12 @@ class Article {
'LIMIT' => $num
) )
);
- if ( !$res ) {
+ if( !$res ) {
wfProfileOut( __METHOD__ );
return array();
}
$row = $db->fetchObject( $res );
- if ( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
+ if( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
$db = wfGetDB( DB_MASTER );
$continue--;
} else {
@@ -2168,59 +2268,67 @@ class Article {
/**
* Output deletion confirmation dialog
- * @param $reason string Prefilled reason
+ * @param $reason String: prefilled reason
*/
- function confirmDelete( $reason ) {
- global $wgOut, $wgUser, $wgContLang;
- $align = $wgContLang->isRtl() ? 'left' : 'right';
+ public function confirmDelete( $reason ) {
+ global $wgOut, $wgUser;
wfDebug( "Article::confirmDelete\n" );
- $wgOut->setSubtitle( wfMsg( 'delete-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->mTitle ) ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ $wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->mTitle ) ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'confirmdeletetext' );
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>";
+ $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\">
+ <td></td>
+ <td class='mw-input'>" .
+ Xml::checkLabel( wfMsg( 'revdelete-suppress' ),
+ 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) .
+ "</td>
+ </tr>";
} else {
$suppress = '';
}
+ $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching();
- $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->mTitle->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
+ $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' ) .
+ Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) .
"<tr id=\"wpDeleteReasonListRow\">
- <td align='$align'>" .
+ <td class='mw-label'>" .
Xml::label( wfMsg( 'deletecomment' ), 'wpDeleteReasonList' ) .
"</td>
- <td>" .
+ <td class='mw-input'>" .
Xml::listDropDown( 'wpDeleteReasonList',
wfMsgForContent( 'deletereason-dropdown' ),
wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) .
"</td>
</tr>
<tr id=\"wpDeleteReasonRow\">
- <td align='$align'>" .
+ <td class='mw-label'>" .
Xml::label( wfMsg( 'deleteotherreason' ), 'wpReason' ) .
"</td>
- <td>" .
- Xml::input( 'wpReason', 60, $reason, array( 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ) ) .
+ <td class='mw-input'>" .
+ Xml::input( 'wpReason', 60, $reason, array( 'type' => 'text', 'maxlength' => '255',
+ 'tabindex' => '2', 'id' => 'wpReason' ) ) .
"</td>
</tr>
<tr>
<td></td>
- <td>" .
- Xml::checkLabel( wfMsg( 'watchthis' ), 'wpWatch', 'wpWatch', $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching(), array( 'tabindex' => '3' ) ) .
+ <td class='mw-input'>" .
+ Xml::checkLabel( wfMsg( 'watchthis' ),
+ 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
"</td>
</tr>
$suppress
<tr>
<td></td>
- <td>" .
- Xml::submitButton( wfMsg( 'deletepage' ), array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '4' ) ) .
+ <td class='mw-submit'>" .
+ Xml::submitButton( wfMsg( 'deletepage' ),
+ array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '5' ) ) .
"</td>
</tr>" .
Xml::closeElement( 'table' ) .
@@ -2228,43 +2336,30 @@ class Article {
Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
Xml::closeElement( 'form' );
- if ( $wgUser->isAllowed( 'editinterface' ) ) {
+ if( $wgUser->isAllowed( 'editinterface' ) ) {
$skin = $wgUser->getSkin();
$link = $skin->makeLink ( 'MediaWiki:Deletereason-dropdown', wfMsgHtml( 'delete-edit-reasonlist' ) );
$form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
}
$wgOut->addHTML( $form );
- $this->showLogExtract( $wgOut );
- }
-
-
- /**
- * Show relevant lines from the deletion log
- */
- function showLogExtract( $out ) {
- $out->addHtml( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
- LogEventsList::showLogExtract( $out, 'delete', $this->mTitle->getPrefixedText() );
+ LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
}
-
/**
* Perform a deletion and output success or failure messages
*/
- function doDelete( $reason, $suppress = false ) {
+ public function doDelete( $reason, $suppress = false ) {
global $wgOut, $wgUser;
- wfDebug( __METHOD__."\n" );
-
- $id = $this->getId();
-
- $error = '';
+ $id = $this->mTitle->getArticleID( GAID_FOR_UPDATE );
- if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason, &$error))) {
- if ( $this->doDeleteArticle( $reason, $suppress ) ) {
+ $error = '';
+ if( wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason, &$error)) ) {
+ if( $this->doDeleteArticle( $reason, $suppress, $id ) ) {
$deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
$loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]';
@@ -2272,10 +2367,13 @@ class Article {
$wgOut->returnToMain( false );
wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason, $id));
} else {
- if ($error = '')
- $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
- else
+ if( $error == '' ) {
+ $wgOut->showFatalError( wfMsgExt( 'cannotdelete', array( 'parse' ) ) );
+ $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
+ LogEventsList::showLogExtract( $wgOut, 'delete', $this->mTitle->getPrefixedText() );
+ } else {
$wgOut->showFatalError( $error );
+ }
}
}
}
@@ -2285,7 +2383,7 @@ class Article {
* Deletes the article with database consistency, writes logs, purges caches
* Returns success
*/
- function doDeleteArticle( $reason, $suppress = false ) {
+ public function doDeleteArticle( $reason, $suppress = false, $id = 0 ) {
global $wgUseSquid, $wgDeferredUpdateList;
global $wgUseTrackbacks;
@@ -2294,9 +2392,9 @@ class Article {
$dbw = wfGetDB( DB_MASTER );
$ns = $this->mTitle->getNamespace();
$t = $this->mTitle->getDBkey();
- $id = $this->mTitle->getArticleID();
+ $id = $id ? $id : $this->mTitle->getArticleID( GAID_FOR_UPDATE );
- if ( $t == '' || $id == 0 ) {
+ if( $t == '' || $id == 0 ) {
return false;
}
@@ -2304,7 +2402,7 @@ class Article {
array_push( $wgDeferredUpdateList, $u );
// Bitfields to further suppress the content
- if ( $suppress ) {
+ if( $suppress ) {
$bitfield = 0;
// This should be 15...
$bitfield |= Revision::DELETED_TEXT;
@@ -2351,15 +2449,6 @@ 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
@@ -2367,12 +2456,20 @@ class Article {
$dbw->rollback();
return false;
}
+
+ # Fix category table counts
+ $cats = array();
+ $res = $dbw->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
+ foreach( $res as $row ) {
+ $cats []= $row->cl_to;
+ }
+ $this->updateCategoryCounts( array(), $cats );
# If using cascading deletes, we can skip some explicit deletes
- if ( !$dbw->cascadingDeletes() ) {
+ if( !$dbw->cascadingDeletes() ) {
$dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
- if ($wgUseTrackbacks)
+ if($wgUseTrackbacks)
$dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
# Delete outgoing links
@@ -2386,14 +2483,17 @@ class Article {
}
# If using cleanup triggers, we can skip some manual deletes
- if ( !$dbw->cleanupTriggers() ) {
-
+ if( !$dbw->cleanupTriggers() ) {
# Clean up recentchanges entries...
$dbw->delete( 'recentchanges',
- array( 'rc_namespace' => $ns, 'rc_title' => $t, 'rc_type != '.RC_LOG ),
+ array( 'rc_type != '.RC_LOG,
+ 'rc_namespace' => $this->mTitle->getNamespace(),
+ 'rc_title' => $this->mTitle->getDBKey() ),
+ __METHOD__ );
+ $dbw->delete( 'recentchanges',
+ array( 'rc_type != '.RC_LOG, 'rc_cur_id' => $id ),
__METHOD__ );
}
- $dbw->commit();
# Clear caches
Article::onArticleDelete( $this->mTitle );
@@ -2409,6 +2509,8 @@ class Article {
# Make sure logging got through
$log->addEntry( 'delete', $this->mTitle, $reason, array() );
+ $dbw->commit();
+
return true;
}
@@ -2419,12 +2521,12 @@ 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 $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 $fromP String: Name of the user whose edits to rollback.
+ * @param $summary String: Custom summary. Set to default summary if empty.
+ * @param $token String: Rollback token.
+ * @param $bot Boolean: If true, mark all reverted edits as bot.
*
- * @param array $resultDetails contains result-specific array of additional values
+ * @param $resultDetails Array: contains result-specific array of additional values
* 'alreadyrolled' : 'current' (rev)
* success : 'summary' (str), 'current' (rev), 'target' (rev)
*
@@ -2438,16 +2540,18 @@ class Article {
$resultDetails = null;
# Check permissions
- $errors = array_merge( $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ),
- $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser ) );
+ $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
+ $rollbackErrors = $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser );
+ $errors = array_merge( $editErrors, wfArrayDiff2( $rollbackErrors, $editErrors ) );
+
if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
$errors[] = array( 'sessionfailure' );
- if ( $wgUser->pingLimiter('rollback') || $wgUser->pingLimiter() ) {
+ if( $wgUser->pingLimiter( 'rollback' ) || $wgUser->pingLimiter() ) {
$errors[] = array( 'actionthrottledtext' );
}
# If there were errors, bail out now
- if(!empty($errors))
+ if( !empty( $errors ) )
return $errors;
return $this->commitRollback($fromP, $summary, $bot, $resultDetails);
@@ -2493,7 +2597,7 @@ class Article {
$s = $dbw->selectRow( 'revision',
array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
array( 'rev_page' => $current->getPage(),
- "rev_user <> {$user} OR rev_user_text <> {$user_text}"
+ "rev_user != {$user} OR rev_user_text != {$user_text}"
), __METHOD__,
array( 'USE INDEX' => 'page_timestamp',
'ORDER BY' => 'rev_timestamp DESC' )
@@ -2507,16 +2611,16 @@ class Article {
}
$set = array();
- if ( $bot && $wgUser->isAllowed('markbotedits') ) {
+ if( $bot && $wgUser->isAllowed('markbotedits') ) {
# Mark all reverted edits as bot
$set['rc_bot'] = 1;
}
- if ( $wgUseRCPatrol ) {
+ if( $wgUseRCPatrol ) {
# Mark all reverted edits as patrolled
$set['rc_patrolled'] = 1;
}
- if ( $set ) {
+ if( $set ) {
$dbw->update( 'recentchanges', $set,
array( /* WHERE */
'rc_cur_id' => $current->getPage(),
@@ -2531,31 +2635,38 @@ class Article {
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 );
+ $summary = wfMsgReplaceArgs( $summary, $args );
# Save
$flags = EDIT_UPDATE;
- if ($wgUser->isAllowed('minoredit'))
+ if( $wgUser->isAllowed('minoredit') )
$flags |= EDIT_MINOR;
if( $bot && ($wgUser->isAllowed('markbotedits') || $wgUser->isAllowed('bot')) )
$flags |= EDIT_FORCE_BOT;
- $this->doEdit( $target->getText(), $summary, $flags, $target->getId() );
+ # Actually store the edit
+ $status = $this->doEdit( $target->getText(), $summary, $flags, $target->getId() );
+ if( !empty( $status->value['revision'] ) ) {
+ $revId = $status->value['revision']->getId();
+ } else {
+ $revId = false;
+ }
- wfRunHooks( 'ArticleRollbackComplete', array( $this, $wgUser, $target ) );
+ wfRunHooks( 'ArticleRollbackComplete', array( $this, $wgUser, $target, $current ) );
$resultDetails = array(
'summary' => $summary,
'current' => $current,
'target' => $target,
+ 'newid' => $revId
);
return array();
}
@@ -2563,7 +2674,7 @@ class Article {
/**
* User interface for rollback operations
*/
- function rollback() {
+ public function rollback() {
global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
$details = null;
@@ -2575,15 +2686,11 @@ class Article {
$details
);
- if( in_array( array( 'blocked' ), $result ) ) {
- $wgOut->blockedPage();
- return;
- }
if( in_array( array( 'actionthrottledtext' ), $result ) ) {
$wgOut->rateLimited();
return;
}
- if( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ){
+ if( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ) {
$wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
$errArray = $result[0];
$errMsg = array_shift( $errArray );
@@ -2591,7 +2698,8 @@ class Article {
if( isset( $details['current'] ) ){
$current = $details['current'];
if( $current->getComment() != '' ) {
- $wgOut->addWikiMsgArray( 'editcomment', array( $wgUser->getSkin()->formatComment( $current->getComment() ) ), array( 'replaceafter' ) );
+ $wgOut->addWikiMsgArray( 'editcomment', array(
+ $wgUser->getSkin()->formatComment( $current->getComment() ) ), array( 'replaceafter' ) );
}
}
return;
@@ -2599,7 +2707,7 @@ class Article {
# Display permissions errors before read-only message -- there's no
# point in misleading the user into thinking the inability to rollback
# is only temporary.
- if( !empty($result) && $result !== array( array('readonlytext') ) ) {
+ if( !empty( $result ) && $result !== array( array( 'readonlytext' ) ) ) {
# array_diff is completely broken for arrays of arrays, sigh. Re-
# move any 'readonlytext' error manually.
$out = array();
@@ -2611,24 +2719,25 @@ class Article {
$wgOut->showPermissionsErrorPage( $out );
return;
}
- if( $result == array( array('readonlytext') ) ) {
+ if( $result == array( array( 'readonlytext' ) ) ) {
$wgOut->readOnlyPage();
return;
}
$current = $details['current'];
$target = $details['target'];
+ $newId = $details['newid'];
$wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
. $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
$new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
. $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
- $wgOut->addHtml( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
+ $wgOut->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 );
+
+ if( !$wgRequest->getBool( 'hidediff', false ) && !$wgUser->getBoolOption( 'norollbackdiff', false ) ) {
+ $de = new DifferenceEngine( $this->mTitle, $current->getId(), $newId, false, true );
$de->showDiff( '', '' );
}
}
@@ -2636,21 +2745,15 @@ class Article {
/**
* Do standard deferred updates after page view
- * @private
*/
- function viewUpdates() {
- global $wgDeferredUpdateList, $wgUser;
-
- if ( 0 != $this->getID() ) {
- # Don't update page view counters on views from bot users (bug 14044)
- global $wgDisableCounters;
- if( !$wgDisableCounters && !$wgUser->isAllowed( 'bot' ) ) {
- Article::incViewCount( $this->getID() );
- $u = new SiteStatsUpdate( 1, 0, 0 );
- array_push( $wgDeferredUpdateList, $u );
- }
+ public function viewUpdates() {
+ global $wgDeferredUpdateList, $wgDisableCounters, $wgUser;
+ # Don't update page view counters on views from bot users (bug 14044)
+ if( !$wgDisableCounters && !$wgUser->isAllowed('bot') && $this->getID() ) {
+ Article::incViewCount( $this->getID() );
+ $u = new SiteStatsUpdate( 1, 0, 0 );
+ array_push( $wgDeferredUpdateList, $u );
}
-
# Update newtalk / watchlist notification status
$wgUser->clearNotification( $this->mTitle );
}
@@ -2659,8 +2762,8 @@ class Article {
* Prepare text which is about to be saved.
* Returns a stdclass with source, pst and output members
*/
- function prepareTextForEdit( $text, $revid=null ) {
- if ( $this->mPreparedEdit && $this->mPreparedEdit->newText == $text && $this->mPreparedEdit->revid == $revid) {
+ public function prepareTextForEdit( $text, $revid=null ) {
+ if( $this->mPreparedEdit && $this->mPreparedEdit->newText == $text && $this->mPreparedEdit->revid == $revid) {
// Already prepared
return $this->mPreparedEdit;
}
@@ -2681,6 +2784,7 @@ class Article {
/**
* Do standard deferred updates after page edit.
* Update links tables, site stats, search index and message cache.
+ * Purges pages that include this page if the text was changed here.
* Every 100th edit, prune the recent changes table.
*
* @private
@@ -2691,14 +2795,14 @@ class Article {
* @param $newid rev_id value of the new revision
* @param $changed Whether or not the content actually changed
*/
- function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange, $newid, $changed = true ) {
+ public function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange, $newid, $changed = true ) {
global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgParser, $wgEnableParserCache;
wfProfileIn( __METHOD__ );
# Parse the text
# Be careful not to double-PST: $text is usually already PST-ed once
- if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
+ if( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
$editInfo = $this->prepareTextForEdit( $text, $newid );
} else {
@@ -2707,17 +2811,20 @@ class Article {
}
# Save it to the parser cache
- if ( $wgEnableParserCache ) {
+ if( $wgEnableParserCache ) {
$parserCache = ParserCache::singleton();
$parserCache->save( $editInfo->output, $this, $wgUser );
}
# Update the links tables
- $u = new LinksUpdate( $this->mTitle, $editInfo->output );
+ $u = new LinksUpdate( $this->mTitle, $editInfo->output, false );
+ $u->setRecursiveTouch( $changed ); // refresh/invalidate including pages too
$u->doUpdate();
+
+ wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $changed ) );
if( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
- if ( 0 == mt_rand( 0, 99 ) ) {
+ if( 0 == mt_rand( 0, 99 ) ) {
// Flush old entries from the `recentchanges` table; we do this on
// random requests so as to avoid an increase in writes for no good reason
global $wgRCMaxAge;
@@ -2733,7 +2840,7 @@ class Article {
$title = $this->mTitle->getPrefixedDBkey();
$shortTitle = $this->mTitle->getDBkey();
- if ( 0 == $id ) {
+ if( 0 == $id ) {
wfProfileOut( __METHOD__ );
return;
}
@@ -2748,21 +2855,23 @@ class Article {
# load of user talk pages and piss people off, nor if it's a minor edit
# by a properly-flagged bot.
if( $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getTitleKey() && $changed
- && !($minoredit && $wgUser->isAllowed('nominornewtalk') ) ) {
- if (wfRunHooks('ArticleEditUpdateNewTalk', array(&$this)) ) {
- $other = User::newFromName( $shortTitle );
- if( is_null( $other ) && User::isIP( $shortTitle ) ) {
+ && !( $minoredit && $wgUser->isAllowed( 'nominornewtalk' ) ) ) {
+ if( wfRunHooks('ArticleEditUpdateNewTalk', array( &$this ) ) ) {
+ $other = User::newFromName( $shortTitle, false );
+ if( !$other ) {
+ wfDebug( __METHOD__.": invalid username\n" );
+ } elseif( User::isIP( $shortTitle ) ) {
// An anonymous user
- $other = new User();
- $other->setName( $shortTitle );
- }
- if( $other ) {
$other->setNewtalk( true );
+ } elseif( $other->isLoggedIn() ) {
+ $other->setNewtalk( true );
+ } else {
+ wfDebug( __METHOD__. ": don't need to notify a nonexistent user\n" );
}
}
}
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
$wgMessageCache->replace( $shortTitle, $text );
}
@@ -2772,13 +2881,13 @@ class Article {
/**
* Perform article updates on a special page creation.
*
- * @param Revision $rev
+ * @param $rev Revision object
*
* @todo This is a shitty interface function. Kill it and replace the
* other shitty functions like editUpdates and such so it's not needed
* anymore.
*/
- function createUpdates( $rev ) {
+ public function createUpdates( $rev ) {
$this->mGoodAdjustment = $this->isCountable( $rev->getText() );
$this->mTotalAdjustment = 1;
$this->editUpdates( $rev->getText(), $rev->getComment(),
@@ -2791,14 +2900,13 @@ class Article {
* Revision as of \<date\>; view current revision
* \<- Previous version | Next Version -\>
*
- * @private
- * @param string $oldid Revision ID of this article revision
+ * @param $oldid String: revision ID of this article revision
*/
- function setOldSubtitle( $oldid=0 ) {
+ public function setOldSubtitle( $oldid = 0 ) {
global $wgLang, $wgOut, $wgUser;
- if ( !wfRunHooks( 'DisplayOldSubtitle', array(&$this, &$oldid) ) ) {
- return;
+ if( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
+ return;
}
$revision = Revision::newFromId( $oldid );
@@ -2807,34 +2915,34 @@ class Article {
$td = $wgLang->timeanddate( $this->mTimestamp, true );
$sk = $wgUser->getSkin();
$lnk = $current
- ? wfMsg( 'currentrevisionlink' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
+ ? wfMsgHtml( 'currentrevisionlink' )
+ : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'currentrevisionlink' ) );
$curdiff = $current
- ? wfMsg( 'diff' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=cur&oldid='.$oldid );
+ ? wfMsgHtml( 'diff' )
+ : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'diff' ), 'diff=cur&oldid='.$oldid );
$prev = $this->mTitle->getPreviousRevisionID( $oldid ) ;
$prevlink = $prev
- ? $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'previousrevision' ), 'direction=prev&oldid='.$oldid )
- : wfMsg( 'previousrevision' );
+ ? $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'previousrevision' ), 'direction=prev&oldid='.$oldid )
+ : wfMsgHtml( 'previousrevision' );
$prevdiff = $prev
- ? $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=prev&oldid='.$oldid )
- : wfMsg( 'diff' );
+ ? $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'diff' ), 'diff=prev&oldid='.$oldid )
+ : wfMsgHtml( 'diff' );
$nextlink = $current
- ? wfMsg( 'nextrevision' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'nextrevision' ), 'direction=next&oldid='.$oldid );
+ ? wfMsgHtml( 'nextrevision' )
+ : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'nextrevision' ), 'direction=next&oldid='.$oldid );
$nextdiff = $current
- ? wfMsg( 'diff' )
- : $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
+ ? wfMsgHtml( 'diff' )
+ : $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'diff' ), 'diff=next&oldid='.$oldid );
$cdel='';
if( $wgUser->isAllowed( 'deleterevision' ) ) {
$revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
if( $revision->isCurrent() ) {
// We don't handle top deleted edits too well
- $cdel = wfMsgHtml('rev-delundel');
+ $cdel = wfMsgHtml( 'rev-delundel' );
} else if( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
// If revision was hidden from sysops
- $cdel = wfMsgHtml('rev-delundel');
+ $cdel = wfMsgHtml( 'rev-delundel' );
} else {
$cdel = $sk->makeKnownLinkObj( $revdel,
wfMsgHtml('rev-delundel'),
@@ -2855,9 +2963,10 @@ class Article {
? 'revision-info-current'
: 'revision-info';
- $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsg( $infomsg, $td, $userlinks ) . "</div>\n" .
+ $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsgExt( $infomsg, array( 'parseinline', 'replaceafter' ), $td, $userlinks, $revision->getID() ) . "</div>\n" .
- "\n\t\t\t\t<div id=\"mw-revision-nav\">" . $cdel . 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 . wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ),
+ $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
@@ -2865,9 +2974,9 @@ class Article {
* This function is called right before saving the wikitext,
* so we can do things like signatures and links-in-context.
*
- * @param string $text
+ * @param $text String
*/
- function preSaveTransform( $text ) {
+ public function preSaveTransform( $text ) {
global $wgParser, $wgUser;
return $wgParser->preSaveTransform( $text, $this->mTitle, $wgUser, ParserOptions::newFromUser( $wgUser ) );
}
@@ -2879,17 +2988,16 @@ class Article {
* output to the client that is necessary for this request.
* (that is, it has sent a cached version of the page)
*/
- function tryFileCache() {
+ protected function tryFileCache() {
static $called = false;
if( $called ) {
wfDebug( "Article::tryFileCache(): called twice!?\n" );
- return;
+ return false;
}
$called = true;
- if($this->isFileCacheable()) {
- $touched = $this->mTouched;
+ if( $this->isFileCacheable() ) {
$cache = new HTMLFileCache( $this->mTitle );
- if($cache->isFileCacheGood( $touched )) {
+ if( $cache->isFileCacheGood( $this->mTouched ) ) {
wfDebug( "Article::tryFileCache(): about to load file\n" );
$cache->loadFromFileCache();
return true;
@@ -2900,46 +3008,22 @@ class Article {
} else {
wfDebug( "Article::tryFileCache(): not cacheable\n" );
}
+ return false;
}
/**
* Check if the page can be cached
* @return bool
*/
- function isFileCacheable() {
- global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest, $wgLang, $wgContLang;
- $action = $wgRequest->getVal( 'action' );
- $oldid = $wgRequest->getVal( 'oldid' );
- $diff = $wgRequest->getVal( 'diff' );
- $redirect = $wgRequest->getVal( 'redirect' );
- $printable = $wgRequest->getVal( 'printable' );
- $page = $wgRequest->getVal( 'page' );
-
- //check for non-standard user language; this covers uselang,
- //and extensions for auto-detecting user language.
- $ulang = $wgLang->getCode();
- $clang = $wgContLang->getCode();
-
- $cacheable = $wgUseFileCache
- && (!$wgShowIPinHeader)
- && ($this->getID() != 0)
- && ($wgUser->isAnon())
- && (!$wgUser->getNewtalk())
- && ($this->mTitle->getNamespace() != NS_SPECIAL )
- && (empty( $action ) || $action == 'view')
- && (!isset($oldid))
- && (!isset($diff))
- && (!isset($redirect))
- && (!isset($printable))
- && !isset($page)
- && (!$this->mRedirectedFrom)
- && ($ulang === $clang);
-
- if ( $cacheable ) {
- //extension may have reason to disable file caching on some pages.
- $cacheable = wfRunHooks( 'IsFileCacheable', array( $this ) );
+ public function isFileCacheable() {
+ $cacheable = false;
+ if( HTMLFileCache::useFileCache() ) {
+ $cacheable = $this->getID() && !$this->mRedirectedFrom;
+ // Extension may have reason to disable file caching on some pages.
+ if( $cacheable ) {
+ $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) );
+ }
}
-
return $cacheable;
}
@@ -2947,7 +3031,7 @@ class Article {
* Loads page_touched and returns a value indicating if it should be used
*
*/
- function checkTouched() {
+ public function checkTouched() {
if( !$this->mDataLoaded ) {
$this->loadPageData();
}
@@ -2957,7 +3041,7 @@ class Article {
/**
* Get the page_touched field
*/
- function getTouched() {
+ public function getTouched() {
# Ensure that page data has been loaded
if( !$this->mDataLoaded ) {
$this->loadPageData();
@@ -2968,8 +3052,8 @@ class Article {
/**
* Get the page_latest field
*/
- function getLatest() {
- if ( !$this->mDataLoaded ) {
+ public function getLatest() {
+ if( !$this->mDataLoaded ) {
$this->loadPageData();
}
return $this->mLatest;
@@ -2980,15 +3064,14 @@ class Article {
* The article must already exist; link tables etc
* are not updated, caches are not flushed.
*
- * @param string $text text submitted
- * @param string $comment comment submitted
- * @param bool $minor whereas it's a minor modification
+ * @param $text String: text submitted
+ * @param $comment String: comment submitted
+ * @param $minor Boolean: whereas it's a minor modification
*/
- function quickEdit( $text, $comment = '', $minor = 0 ) {
+ public function quickEdit( $text, $comment = '', $minor = 0 ) {
wfProfileIn( __METHOD__ );
$dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
$revision = new Revision( array(
'page' => $this->getId(),
'text' => $text,
@@ -2997,9 +3080,8 @@ class Article {
) );
$revision->insertOn( $dbw );
$this->updateRevisionOn( $dbw, $revision );
- $dbw->commit();
-
- wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false) );
+
+ wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false, $wgUser) );
wfProfileOut( __METHOD__ );
}
@@ -3007,10 +3089,9 @@ class Article {
/**
* Used to increment the view counter
*
- * @static
- * @param integer $id article id
+ * @param $id Integer: article id
*/
- function incViewCount( $id ) {
+ public static function incViewCount( $id ) {
$id = intval( $id );
global $wgHitcounterUpdateFreq, $wgDBtype;
@@ -3043,14 +3124,14 @@ class Article {
wfProfileIn( 'Article::incViewCount-collect' );
$old_user_abort = ignore_user_abort( true );
- if ($wgDBtype == 'mysql')
+ if($wgDBtype == 'mysql')
$dbw->query("LOCK TABLES $hitcounterTable WRITE");
$tabletype = $wgDBtype == 'mysql' ? "ENGINE=HEAP " : '';
$dbw->query("CREATE TEMPORARY TABLE $acchitsTable $tabletype AS ".
"SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable ".
'GROUP BY hc_id');
$dbw->query("DELETE FROM $hitcounterTable");
- if ($wgDBtype == 'mysql') {
+ if($wgDBtype == 'mysql') {
$dbw->query('UNLOCK TABLES');
$dbw->query("UPDATE $pageTable,$acchitsTable SET page_counter=page_counter + hc_n ".
'WHERE page_id = hc_id');
@@ -3075,13 +3156,13 @@ class Article {
* This is a good place to put code to clear caches, for instance.
*
* This is called on page move and undelete, as well as edit
- * @static
- * @param $title_obj a title object
+ *
+ * @param $title a title object
*/
- static function onArticleCreate($title) {
- # The talk page isn't in the regular link tables, so we need to update manually:
- if ( $title->isTalkPage() ) {
+ public static function onArticleCreate( $title ) {
+ # Update existence markers on article/talk tabs...
+ if( $title->isTalkPage() ) {
$other = $title->getSubjectPage();
} else {
$other = $title->getTalkPage();
@@ -3094,10 +3175,9 @@ class Article {
$title->deleteTitleProtection();
}
- static function onArticleDelete( $title ) {
- global $wgUseFileCache, $wgMessageCache;
-
- // Update existence markers on article/talk tabs...
+ public static function onArticleDelete( $title ) {
+ global $wgMessageCache;
+ # Update existence markers on article/talk tabs...
if( $title->isTalkPage() ) {
$other = $title->getSubjectPage();
} else {
@@ -3110,17 +3190,14 @@ class Article {
$title->purgeSquid();
# File cache
- if ( $wgUseFileCache ) {
- $cm = new HTMLFileCache( $title );
- @unlink( $cm->fileCacheName() );
- }
+ HTMLFileCache::clearFileCache( $title );
# Messages
if( $title->getNamespace() == NS_MEDIAWIKI ) {
$wgMessageCache->replace( $title->getDBkey(), false );
}
# Images
- if( $title->getNamespace() == NS_IMAGE ) {
+ if( $title->getNamespace() == NS_FILE ) {
$update = new HTMLCacheUpdate( $title, 'imagelinks' );
$update->doUpdate();
}
@@ -3134,11 +3211,12 @@ class Article {
/**
* Purge caches on page update etc
*/
- static function onArticleEdit( $title ) {
- global $wgDeferredUpdateList, $wgUseFileCache;
+ public static function onArticleEdit( $title, $transclusions = 'transclusions' ) {
+ global $wgDeferredUpdateList;
// Invalidate caches of articles which include this page
- $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'templatelinks' );
+ if( $transclusions !== 'skiptransclusions' )
+ $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'templatelinks' );
// Invalidate the caches of all pages which redirect here
$wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'redirect' );
@@ -3146,11 +3224,8 @@ class Article {
# Purge squid for this page only
$title->purgeSquid();
- # Clear file cache
- if ( $wgUseFileCache ) {
- $cm = new HTMLFileCache( $title );
- @unlink( $cm->fileCacheName() );
- }
+ # Clear file cache for this page only
+ HTMLFileCache::clearFileCache( $title );
}
/**#@-*/
@@ -3159,7 +3234,7 @@ class Article {
* Overriden by ImagePage class, only present here to avoid a fatal error
* Called for ?action=revert
*/
- public function revert(){
+ public function revert() {
global $wgOut;
$wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
}
@@ -3167,13 +3242,11 @@ class Article {
/**
* Info about this page
* Called for ?action=info when $wgAllowPageInfo is on.
- *
- * @public
*/
- function info() {
+ public function info() {
global $wgLang, $wgOut, $wgAllowPageInfo, $wgUser;
- if ( !$wgAllowPageInfo ) {
+ if( !$wgAllowPageInfo ) {
$wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
return;
}
@@ -3182,21 +3255,21 @@ class Article {
$wgOut->setPagetitle( $page->getPrefixedText() );
$wgOut->setPageTitleActionText( wfMsg( 'info_short' ) );
- $wgOut->setSubtitle( wfMsg( 'infosubtitle' ) );
+ $wgOut->setSubtitle( wfMsgHtml( 'infosubtitle' ) );
if( !$this->mTitle->exists() ) {
- $wgOut->addHtml( '<div class="noarticletext">' );
+ $wgOut->addHTML( '<div class="noarticletext">' );
if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
// This doesn't quite make sense; the user is asking for
// information about the _page_, not the message... -- RC
- $wgOut->addHtml( htmlspecialchars( wfMsgWeirdKey( $this->mTitle->getText() ) ) );
+ $wgOut->addHTML( htmlspecialchars( wfMsgWeirdKey( $this->mTitle->getText() ) ) );
} else {
$msg = $wgUser->isLoggedIn()
? 'noarticletext'
: 'noarticletextanon';
- $wgOut->addHtml( wfMsgExt( $msg, 'parse' ) );
+ $wgOut->addHTML( wfMsgExt( $msg, 'parse' ) );
}
- $wgOut->addHtml( '</div>' );
+ $wgOut->addHTML( '</div>' );
} else {
$dbr = wfGetDB( DB_SLAVE );
$wl_clause = array(
@@ -3222,7 +3295,6 @@ class Article {
$wgOut->addHTML( '<li>' . wfMsg('numtalkauthors', $wgLang->formatNum( $talkInfo['authors'] ) ) . '</li>' );
}
$wgOut->addHTML( '</ul>' );
-
}
}
@@ -3230,34 +3302,30 @@ class Article {
* Return the total number of edits and number of unique editors
* on a given page. If page does not exist, returns false.
*
- * @param Title $title
+ * @param $title Title object
* @return array
- * @private
*/
- function pageCountInfo( $title ) {
+ protected function pageCountInfo( $title ) {
$id = $title->getArticleId();
if( $id == 0 ) {
return false;
}
-
$dbr = wfGetDB( DB_SLAVE );
-
$rev_clause = array( 'rev_page' => $id );
-
$edits = $dbr->selectField(
'revision',
'COUNT(rev_page)',
$rev_clause,
__METHOD__,
- $this->getSelectOptions() );
-
+ $this->getSelectOptions()
+ );
$authors = $dbr->selectField(
'revision',
'COUNT(DISTINCT rev_user_text)',
$rev_clause,
__METHOD__,
- $this->getSelectOptions() );
-
+ $this->getSelectOptions()
+ );
return array( 'edits' => $edits, 'authors' => $authors );
}
@@ -3265,25 +3333,22 @@ class Article {
* Return a list of templates used by this article.
* Uses the templatelinks table
*
- * @return array Array of Title objects
+ * @return Array of Title objects
*/
- function getUsedTemplates() {
+ public function getUsedTemplates() {
$result = array();
$id = $this->mTitle->getArticleID();
if( $id == 0 ) {
return array();
}
-
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'templatelinks' ),
array( 'tl_namespace', 'tl_title' ),
array( 'tl_from' => $id ),
- 'Article:getUsedTemplates' );
- if ( false !== $res ) {
- if ( $dbr->numRows( $res ) ) {
- while ( $row = $dbr->fetchObject( $res ) ) {
- $result[] = Title::makeTitle( $row->tl_namespace, $row->tl_title );
- }
+ __METHOD__ );
+ if( $res !== false ) {
+ foreach( $res as $row ) {
+ $result[] = Title::makeTitle( $row->tl_namespace, $row->tl_title );
}
}
$dbr->freeResult( $res );
@@ -3294,26 +3359,23 @@ class Article {
* Returns a list of hidden categories this page is a member of.
* Uses the page_props and categorylinks tables.
*
- * @return array Array of Title objects
+ * @return Array of Title objects
*/
- function getHiddenCategories() {
+ public function getHiddenCategories() {
$result = array();
$id = $this->mTitle->getArticleID();
if( $id == 0 ) {
return array();
}
-
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ),
array( 'cl_to' ),
array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
'page_namespace' => NS_CATEGORY, 'page_title=cl_to'),
- 'Article:getHiddenCategories' );
- if ( false !== $res ) {
- if ( $dbr->numRows( $res ) ) {
- while ( $row = $dbr->fetchObject( $res ) ) {
- $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
- }
+ __METHOD__ );
+ if( $res !== false ) {
+ foreach( $res as $row ) {
+ $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
}
}
$dbr->freeResult( $res );
@@ -3322,17 +3384,18 @@ class Article {
/**
* Return an applicable autosummary if one exists for the given edit.
- * @param string $oldtext The previous text of the page.
- * @param string $newtext The submitted text of the page.
- * @param bitmask $flags A bitmask of flags submitted for the edit.
+ * @param $oldtext String: the previous text of the page.
+ * @param $newtext String: The submitted text of the page.
+ * @param $flags Bitmask: a bitmask of flags submitted for the edit.
* @return string An appropriate autosummary, or an empty string.
*/
public static function getAutosummary( $oldtext, $newtext, $flags ) {
# Decide what kind of autosummary is needed.
# Redirect autosummaries
+ $ot = Title::newFromRedirect( $oldtext );
$rt = Title::newFromRedirect( $newtext );
- if( is_object( $rt ) ) {
+ if( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) {
return wfMsgForContent( 'autoredircomment', $rt->getFullText() );
}
@@ -3342,14 +3405,14 @@ class Article {
global $wgContLang;
$truncatedtext = $wgContLang->truncate(
str_replace("\n", ' ', $newtext),
- max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new') ) ),
+ max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ),
'...' );
return wfMsgForContent( 'autosumm-new', $truncatedtext );
}
# Blanking autosummaries
if( $oldtext != '' && $newtext == '' ) {
- return wfMsgForContent('autosumm-blank');
+ return wfMsgForContent( 'autosumm-blank' );
} elseif( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500) {
# Removing more than 90% of the article
global $wgContLang;
@@ -3371,11 +3434,11 @@ class Article {
* Saves the text into the parser cache if possible.
* Updates templatelinks if it is out of date.
*
- * @param string $text
- * @param bool $cache
+ * @param $text String
+ * @param $cache Boolean
*/
public function outputWikiText( $text, $cache = true ) {
- global $wgParser, $wgUser, $wgOut, $wgEnableParserCache;
+ global $wgParser, $wgUser, $wgOut, $wgEnableParserCache, $wgUseFileCache;
$popts = $wgOut->parserOptions();
$popts->setTidy(true);
@@ -3384,12 +3447,18 @@ class Article {
$popts, true, true, $this->getRevIdFetched() );
$popts->setTidy(false);
$popts->enableLimitReport( false );
- if ( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
+ if( $wgEnableParserCache && $cache && $this && $parserOutput->getCacheTime() != -1 ) {
$parserCache = ParserCache::singleton();
$parserCache->save( $parserOutput, $this, $wgUser );
}
+ // Make sure file cache is not used on uncacheable content.
+ // Output that has magic words in it can still use the parser cache
+ // (if enabled), though it will generally expire sooner.
+ if( $parserOutput->getCacheTime() == -1 || $parserOutput->containsOldMagic() ) {
+ $wgUseFileCache = false;
+ }
- if ( !wfReadOnly() && $this->mTitle->areRestrictionsCascading() ) {
+ if( $this->isCurrent() && !wfReadOnly() && $this->mTitle->areRestrictionsCascading() ) {
// templatelinks table may have become out of sync,
// especially if using variable-based transclusions.
// For paranoia, check if things have changed and if
@@ -3406,15 +3475,13 @@ class Article {
$res = $dbr->select( array( 'templatelinks' ),
array( 'tl_namespace', 'tl_title' ),
array( 'tl_from' => $id ),
- 'Article:getUsedTemplates' );
+ __METHOD__ );
global $wgContLang;
- if ( false !== $res ) {
- if ( $dbr->numRows( $res ) ) {
- while ( $row = $dbr->fetchObject( $res ) ) {
- $tlTemplates[] = $wgContLang->getNsText( $row->tl_namespace ) . ':' . $row->tl_title ;
- }
+ if( $res !== false ) {
+ foreach( $res as $row ) {
+ $tlTemplates[] = $wgContLang->getNsText( $row->tl_namespace ) . ':' . $row->tl_title ;
}
}
@@ -3429,16 +3496,10 @@ class Article {
# Get the diff
$templates_diff = array_diff( $poTemplates, $tlTemplates );
- if ( count( $templates_diff ) > 0 ) {
+ if( count( $templates_diff ) > 0 ) {
# Whee, link updates time.
$u = new LinksUpdate( $this->mTitle, $parserOutput );
-
- $dbw = wfGetDb( DB_MASTER );
- $dbw->begin();
-
$u->doUpdate();
-
- $dbw->commit();
}
}
@@ -3479,12 +3540,12 @@ class Article {
if( $ns == NS_CATEGORY ) {
$addFields[] = 'cat_subcats = cat_subcats + 1';
$removeFields[] = 'cat_subcats = cat_subcats - 1';
- } elseif( $ns == NS_IMAGE ) {
+ } elseif( $ns == NS_FILE ) {
$addFields[] = 'cat_files = cat_files + 1';
$removeFields[] = 'cat_files = cat_files - 1';
}
- if ( $added ) {
+ if( $added ) {
$dbw->update(
'category',
$addFields,
@@ -3492,7 +3553,7 @@ class Article {
__METHOD__
);
}
- if ( $deleted ) {
+ if( $deleted ) {
$dbw->update(
'category',
$removeFields,
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index 7717e001..b29e13f2 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -38,9 +38,8 @@ class AuthPlugin {
*
* @param $username String: username.
* @return bool
- * @public
*/
- function userExists( $username ) {
+ public function userExists( $username ) {
# Override this!
return false;
}
@@ -54,9 +53,8 @@ class AuthPlugin {
* @param $username String: username.
* @param $password String: user password.
* @return bool
- * @public
*/
- function authenticate( $username, $password ) {
+ public function authenticate( $username, $password ) {
# Override this!
return false;
}
@@ -65,9 +63,8 @@ class AuthPlugin {
* Modify options in the login template.
*
* @param $template UserLoginTemplate object.
- * @public
*/
- function modifyUITemplate( &$template ) {
+ public function modifyUITemplate( &$template ) {
# Override this!
$template->set( 'usedomain', false );
}
@@ -76,9 +73,8 @@ class AuthPlugin {
* Set the domain this plugin is supposed to use when authenticating.
*
* @param $domain String: authentication domain.
- * @public
*/
- function setDomain( $domain ) {
+ public function setDomain( $domain ) {
$this->domain = $domain;
}
@@ -87,9 +83,8 @@ class AuthPlugin {
*
* @param $domain String: authentication domain.
* @return bool
- * @public
*/
- function validDomain( $domain ) {
+ public function validDomain( $domain ) {
# Override this!
return true;
}
@@ -103,9 +98,8 @@ class AuthPlugin {
* forget the & on your function declaration.
*
* @param User $user
- * @public
*/
- function updateUser( &$user ) {
+ public function updateUser( &$user ) {
# Override this and do something
return true;
}
@@ -123,9 +117,8 @@ class AuthPlugin {
* This is just a question, and shouldn't perform any actions.
*
* @return bool
- * @public
*/
- function autoCreate() {
+ public function autoCreate() {
return false;
}
@@ -134,7 +127,7 @@ class AuthPlugin {
*
* @return bool
*/
- function allowPasswordChange() {
+ public function allowPasswordChange() {
return true;
}
@@ -149,9 +142,8 @@ class AuthPlugin {
* @param $user User object.
* @param $password String: password.
* @return bool
- * @public
*/
- function setPassword( $user, $password ) {
+ public function setPassword( $user, $password ) {
return true;
}
@@ -161,9 +153,8 @@ class AuthPlugin {
*
* @param $user User object.
* @return bool
- * @public
*/
- function updateExternalDB( $user ) {
+ public function updateExternalDB( $user ) {
return true;
}
@@ -171,9 +162,8 @@ class AuthPlugin {
* Check to see if external accounts can be created.
* Return true if external accounts can be created.
* @return bool
- * @public
*/
- function canCreateAccounts() {
+ public function canCreateAccounts() {
return false;
}
@@ -186,9 +176,8 @@ class AuthPlugin {
* @param string $email
* @param string $realname
* @return bool
- * @public
*/
- function addUser( $user, $password, $email='', $realname='' ) {
+ public function addUser( $user, $password, $email='', $realname='' ) {
return true;
}
@@ -200,9 +189,8 @@ class AuthPlugin {
* This is just a question, and shouldn't perform any actions.
*
* @return bool
- * @public
*/
- function strict() {
+ public function strict() {
return false;
}
@@ -212,9 +200,8 @@ class AuthPlugin {
*
* @param $username String: username.
* @return bool
- * @public
*/
- function strictUserAuth( $username ) {
+ public function strictUserAuth( $username ) {
return false;
}
@@ -228,9 +215,8 @@ class AuthPlugin {
*
* @param $user User object.
* @param $autocreate bool True if user is being autocreated on login
- * @public
*/
- function initUser( &$user, $autocreate=false ) {
+ public function initUser( &$user, $autocreate=false ) {
# Override this to do something.
}
@@ -238,7 +224,43 @@ class AuthPlugin {
* If you want to munge the case of an account name before the final
* check, now is your chance.
*/
- function getCanonicalName( $username ) {
+ public function getCanonicalName( $username ) {
return $username;
}
+
+ /**
+ * Get an instance of a User object
+ *
+ * @param $user User
+ * @public
+ */
+ public function getUserInstance( User &$user ) {
+ return new AuthPluginUser( $user );
+ }
+}
+
+class AuthPluginUser {
+ function __construct( $user ) {
+ # Override this!
+ }
+
+ public function getId() {
+ # Override this!
+ return -1;
+ }
+
+ public function isLocked() {
+ # Override this!
+ return false;
+ }
+
+ public function isHidden() {
+ # Override this!
+ return false;
+ }
+
+ public function resetAuthToken() {
+ # Override this!
+ return true;
+ }
}
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index de75b41d..ce1912ea 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -4,483 +4,544 @@
ini_set('unserialize_callback_func', '__autoload' );
-class AutoLoader {
- # Locations of core classes
- # Extension classes are specified with $wgAutoloadClasses
- static $localClasses = array(
- # Includes
- 'AjaxDispatcher' => 'includes/AjaxDispatcher.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',
- 'Block' => 'includes/Block.php',
- 'CacheDependency' => 'includes/CacheDependency.php',
- 'Category' => 'includes/Category.php',
- 'Categoryfinder' => 'includes/Categoryfinder.php',
- 'CategoryPage' => 'includes/CategoryPage.php',
- 'CategoryViewer' => 'includes/CategoryPage.php',
- 'ChangesList' => 'includes/ChangesList.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',
- 'DiffFormatter' => 'includes/DifferenceEngine.php',
- 'Diff' => 'includes/DifferenceEngine.php',
- '_DiffOp_Add' => 'includes/DifferenceEngine.php',
- '_DiffOp_Change' => 'includes/DifferenceEngine.php',
- '_DiffOp_Copy' => 'includes/DifferenceEngine.php',
- '_DiffOp_Delete' => 'includes/DifferenceEngine.php',
- '_DiffOp' => 'includes/DifferenceEngine.php',
- 'DjVuImage' => 'includes/DjVuImage.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',
- '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',
- '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',
- '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',
- 'HistoryBlobStub' => 'includes/HistoryBlob.php',
- 'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php',
- 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
- 'HTMLFileCache' => 'includes/HTMLFileCache.php',
- 'Http' => 'includes/HttpFunctions.php',
- '_HWLDF_WordAccumulator' => 'includes/DifferenceEngine.php',
- 'IEContentAnalyzer' => 'includes/IEContentAnalyzer.php',
- 'ImageGallery' => 'includes/ImageGallery.php',
- 'ImageHistoryList' => 'includes/ImagePage.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',
- 'License' => 'includes/Licenses.php',
- 'Licenses' => 'includes/Licenses.php',
- 'LinkBatch' => 'includes/LinkBatch.php',
- 'LinkCache' => 'includes/LinkCache.php',
- 'Linker' => 'includes/Linker.php',
- 'LinkFilter' => 'includes/LinkFilter.php',
- 'LinksUpdate' => 'includes/LinksUpdate.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',
- 'MagicWordArray' => 'includes/MagicWord.php',
- 'MagicWord' => 'includes/MagicWord.php',
- 'MailAddress' => 'includes/UserMailer.php',
- 'MappedDiff' => 'includes/DifferenceEngine.php',
- 'MathRenderer' => 'includes/Math.php',
- 'MediaTransformError' => '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',
- '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',
- 'PageHistoryPager' => 'includes/PageHistory.php',
- 'PageQueryPage' => 'includes/PageQueryPage.php',
- 'Pager' => 'includes/Pager.php',
- 'PasswordError' => 'includes/User.php',
- 'PatrolLog' => 'includes/PatrolLog.php',
- 'PostgresSearchResult' => 'includes/SearchPostgres.php',
- 'PostgresSearchResultSet' => 'includes/SearchPostgres.php',
- 'PrefixSearch' => 'includes/PrefixSearch.php',
- 'Profiler' => 'includes/Profiler.php',
- 'ProfilerSimple' => 'includes/ProfilerSimple.php',
- 'ProfilerSimpleText' => 'includes/ProfilerSimpleText.php',
- 'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
- 'ProtectionForm' => 'includes/ProtectionForm.php',
- 'QueryPage' => 'includes/QueryPage.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',
- 'SearchEngineDummy' => 'includes/SearchEngine.php',
- 'SearchEngine' => 'includes/SearchEngine.php',
- 'SearchHighlighter' => 'includes/SearchEngine.php',
- 'SearchMySQL4' => 'includes/SearchMySQL4.php',
- 'SearchMySQL' => 'includes/SearchMySQL.php',
- 'SearchOracle' => 'includes/SearchOracle.php',
- 'SearchPostgres' => 'includes/SearchPostgres.php',
- 'SearchResult' => 'includes/SearchEngine.php',
- 'SearchResultSet' => 'includes/SearchEngine.php',
- 'SearchResultTooMany' => 'includes/SearchEngine.php',
- 'SearchUpdate' => 'includes/SearchUpdate.php',
- 'SearchUpdateMyISAM' => 'includes/SearchUpdate.php',
- 'SiteConfiguration' => 'includes/SiteConfiguration.php',
- 'SiteStats' => 'includes/SiteStats.php',
- 'SiteStatsUpdate' => 'includes/SiteStats.php',
- 'Skin' => 'includes/Skin.php',
- 'SkinTemplate' => 'includes/SkinTemplate.php',
- 'SpecialMycontributions' => 'includes/SpecialPage.php',
- 'SpecialMypage' => 'includes/SpecialPage.php',
- 'SpecialMytalk' => 'includes/SpecialPage.php',
- 'SpecialPage' => 'includes/SpecialPage.php',
- 'SpecialRedirectToSpecial' => 'includes/SpecialPage.php',
- 'SqlBagOStuff' => 'includes/BagOStuff.php',
- 'SquidUpdate' => 'includes/SquidUpdate.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',
- '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',
- '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',
+# Locations of core classes
+# Extension classes are specified with $wgAutoloadClasses
+# This array is a global instead of a static member of AutoLoader to work around a bug in APC
+global $wgAutoloadLocalClasses;
+$wgAutoloadLocalClasses = array(
+ # Includes
+ 'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
+ 'AjaxResponse' => 'includes/AjaxResponse.php',
+ 'AlphabeticPager' => 'includes/Pager.php',
+ 'APCBagOStuff' => 'includes/BagOStuff.php',
+ 'Article' => 'includes/Article.php',
+ 'AtomFeed' => 'includes/Feed.php',
+ 'AuthPlugin' => 'includes/AuthPlugin.php',
+ 'AuthPluginUser' => 'includes/AuthPlugin.php',
+ 'Autopromote' => 'includes/Autopromote.php',
+ 'BagOStuff' => 'includes/BagOStuff.php',
+ 'Block' => 'includes/Block.php',
+ 'CacheDependency' => 'includes/CacheDependency.php',
+ 'Category' => 'includes/Category.php',
+ 'Categoryfinder' => 'includes/Categoryfinder.php',
+ 'CategoryPage' => 'includes/CategoryPage.php',
+ 'CategoryViewer' => 'includes/CategoryPage.php',
+ 'ChangesList' => 'includes/ChangesList.php',
+ 'ChangesFeed' => 'includes/ChangesFeed.php',
+ 'ChannelFeed' => 'includes/Feed.php',
+ 'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
+ 'ConstantDependency' => 'includes/CacheDependency.php',
+ 'CreativeCommonsRdf' => 'includes/Metadata.php',
+ 'Credits' => 'includes/Credits.php',
+ 'DBABagOStuff' => 'includes/BagOStuff.php',
+ 'DependencyWrapper' => 'includes/CacheDependency.php',
+ 'DiffHistoryBlob' => 'includes/HistoryBlob.php',
+ 'DjVuImage' => 'includes/DjVuImage.php',
+ 'DoubleReplacer' => 'includes/StringUtils.php',
+ 'DoubleRedirectJob' => 'includes/DoubleRedirectJob.php',
+ 'DublinCoreRdf' => 'includes/Metadata.php',
+ 'Dump7ZipOutput' => 'includes/Export.php',
+ 'DumpBZip2Output' => 'includes/Export.php',
+ 'DumpFileOutput' => 'includes/Export.php',
+ 'DumpFilter' => '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',
+ 'ExplodeIterator' => 'includes/StringUtils.php',
+ 'ExternalEdit' => 'includes/ExternalEdit.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',
+ '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',
+ 'HistoryBlobStub' => 'includes/HistoryBlob.php',
+ 'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php',
+ 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
+ 'HTMLFileCache' => 'includes/HTMLFileCache.php',
+ 'Http' => 'includes/HttpFunctions.php',
+ 'IEContentAnalyzer' => 'includes/IEContentAnalyzer.php',
+ 'ImageGallery' => 'includes/ImageGallery.php',
+ 'ImageHistoryList' => 'includes/ImagePage.php',
+ 'ImagePage' => 'includes/ImagePage.php',
+ 'ImageQueryPage' => 'includes/ImageQueryPage.php',
+ 'IncludableSpecialPage' => 'includes/SpecialPage.php',
+ 'IndexPager' => 'includes/Pager.php',
+ 'Interwiki' => 'includes/Interwiki.php',
+ 'IP' => 'includes/IP.php',
+ 'Job' => 'includes/JobQueue.php',
+ 'License' => 'includes/Licenses.php',
+ 'Licenses' => 'includes/Licenses.php',
+ 'LinkBatch' => 'includes/LinkBatch.php',
+ 'LinkCache' => 'includes/LinkCache.php',
+ 'Linker' => 'includes/Linker.php',
+ 'LinkFilter' => 'includes/LinkFilter.php',
+ 'LinksUpdate' => 'includes/LinksUpdate.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',
+ 'MagicWordArray' => 'includes/MagicWord.php',
+ 'MagicWord' => 'includes/MagicWord.php',
+ 'MailAddress' => 'includes/UserMailer.php',
+ 'MathRenderer' => 'includes/Math.php',
+ 'MediaTransformError' => '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',
+ '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',
+ 'PageHistoryPager' => 'includes/PageHistory.php',
+ 'PageQueryPage' => 'includes/PageQueryPage.php',
+ 'Pager' => 'includes/Pager.php',
+ 'PasswordError' => 'includes/User.php',
+ 'PatrolLog' => 'includes/PatrolLog.php',
+ 'PostgresSearchResult' => 'includes/SearchPostgres.php',
+ 'PostgresSearchResultSet' => 'includes/SearchPostgres.php',
+ 'PrefixSearch' => 'includes/PrefixSearch.php',
+ 'Profiler' => 'includes/Profiler.php',
+ 'ProfilerSimple' => 'includes/ProfilerSimple.php',
+ 'ProfilerSimpleText' => 'includes/ProfilerSimpleText.php',
+ 'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
+ 'ProtectionForm' => 'includes/ProtectionForm.php',
+ 'QueryPage' => 'includes/QueryPage.php',
+ 'QuickTemplate' => 'includes/SkinTemplate.php',
+ 'RawPage' => 'includes/RawPage.php',
+ 'RCCacheEntry' => 'includes/ChangesList.php',
+ 'RdfMetaData' => 'includes/Metadata.php',
+ 'RecentChange' => 'includes/RecentChange.php',
+ 'RefreshLinksJob' => 'includes/RefreshLinksJob.php',
+ 'RefreshLinksJob2' => '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',
+ 'SearchEngineDummy' => 'includes/SearchEngine.php',
+ 'SearchEngine' => 'includes/SearchEngine.php',
+ 'SearchHighlighter' => 'includes/SearchEngine.php',
+ 'SearchMySQL4' => 'includes/SearchMySQL4.php',
+ 'SearchMySQL' => 'includes/SearchMySQL.php',
+ 'SearchOracle' => 'includes/SearchOracle.php',
+ 'SearchPostgres' => 'includes/SearchPostgres.php',
+ 'SearchResult' => 'includes/SearchEngine.php',
+ 'SearchResultSet' => 'includes/SearchEngine.php',
+ 'SearchResultTooMany' => 'includes/SearchEngine.php',
+ 'SearchUpdate' => 'includes/SearchUpdate.php',
+ 'SearchUpdateMyISAM' => 'includes/SearchUpdate.php',
+ 'SiteConfiguration' => 'includes/SiteConfiguration.php',
+ 'SiteStats' => 'includes/SiteStats.php',
+ 'SiteStatsUpdate' => 'includes/SiteStats.php',
+ 'Skin' => 'includes/Skin.php',
+ 'SkinTemplate' => 'includes/SkinTemplate.php',
+ 'SpecialMycontributions' => 'includes/SpecialPage.php',
+ 'SpecialMypage' => 'includes/SpecialPage.php',
+ 'SpecialMytalk' => 'includes/SpecialPage.php',
+ 'SpecialPage' => 'includes/SpecialPage.php',
+ 'SpecialRedirectToSpecial' => 'includes/SpecialPage.php',
+ 'SqlBagOStuff' => 'includes/BagOStuff.php',
+ 'SquidUpdate' => 'includes/SquidUpdate.php',
+ 'Status' => 'includes/Status.php',
+ 'StringUtils' => 'includes/StringUtils.php',
+ 'TablePager' => 'includes/Pager.php',
+ 'ThumbnailImage' => 'includes/MediaTransformOutput.php',
+ 'TitleDependency' => 'includes/CacheDependency.php',
+ 'Title' => 'includes/Title.php',
+ 'TitleArray' => 'includes/TitleArray.php',
+ 'TitleListDependency' => 'includes/CacheDependency.php',
+ 'TransformParameterError' => 'includes/MediaTransformOutput.php',
+ 'TurckBagOStuff' => 'includes/BagOStuff.php',
+ 'UnlistedSpecialPage' => 'includes/SpecialPage.php',
+ 'UploadBase' => 'includes/UploadBase.php',
+ 'UploadFromStash' => 'includes/UploadFromStash.php',
+ 'UploadFromUpload' => 'includes/UploadFromUpload.php',
+ 'UploadFromUrl' => 'includes/UploadFromUrl.php',
+ 'User' => 'includes/User.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',
+ 'WikiError' => 'includes/WikiError.php',
+ 'WikiErrorMsg' => 'includes/WikiError.php',
+ 'WikiExporter' => 'includes/Export.php',
+ 'WikiXmlError' => 'includes/WikiError.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',
+
+ # includes/api
+ 'ApiBase' => 'includes/api/ApiBase.php',
+ 'ApiBlock' => 'includes/api/ApiBlock.php',
+ 'ApiDelete' => 'includes/api/ApiDelete.php',
+ 'ApiDisabled' => 'includes/api/ApiDisabled.php',
+ 'ApiEditPage' => 'includes/api/ApiEditPage.php',
+ 'ApiEmailUser' => 'includes/api/ApiEmailUser.php',
+ 'ApiExpandTemplates' => 'includes/api/ApiExpandTemplates.php',
+ 'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
+ 'ApiFormatBase' => 'includes/api/ApiFormatBase.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',
+ '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',
+ 'ApiPatrol' => 'includes/api/ApiPatrol.php',
+ 'ApiProtect' => 'includes/api/ApiProtect.php',
+ 'ApiPurge' => 'includes/api/ApiPurge.php',
+ 'ApiQuery' => 'includes/api/ApiQuery.php',
+ 'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php',
+ 'ApiQueryAllimages' => 'includes/api/ApiQueryAllimages.php',
+ 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
+ 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.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',
+ 'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
+ 'ApiQueryDisabled' => 'includes/api/ApiQueryDisabled.php',
+ 'ApiQueryDuplicateFiles' => 'includes/api/ApiQueryDuplicateFiles.php',
+ 'ApiQueryExtLinksUsage' => 'includes/api/ApiQueryExtLinksUsage.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',
+ 'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
+ 'ApiQueryRandom' => 'includes/api/ApiQueryRandom.php',
+ 'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php',
+ 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
+ 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
+ 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
+ 'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php',
+ 'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php',
+ 'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
+ 'ApiQueryWatchlistRaw' => 'includes/api/ApiQueryWatchlistRaw.php',
+ 'ApiResult' => 'includes/api/ApiResult.php',
+ 'ApiRollback' => 'includes/api/ApiRollback.php',
+ 'ApiUnblock' => 'includes/api/ApiUnblock.php',
+ 'ApiUndelete' => 'includes/api/ApiUndelete.php',
+ 'ApiWatch' => 'includes/api/ApiWatch.php',
+ 'Services_JSON' => 'includes/api/ApiFormatJson_json.php',
+ 'Services_JSON_Error' => 'includes/api/ApiFormatJson_json.php',
+ 'Spyc' => 'includes/api/ApiFormatYaml_spyc.php',
+ 'UsageException' => 'includes/api/ApiMain.php',
- # includes/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',
- 'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
- 'ApiFormatBase' => 'includes/api/ApiFormatBase.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',
- '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',
- 'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php',
- 'ApiQueryAllimages' => 'includes/api/ApiQueryAllimages.php',
- 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
- 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.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',
- 'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
- 'ApiQueryExtLinksUsage' => 'includes/api/ApiQueryExtLinksUsage.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',
- 'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
- 'ApiQueryRandom' => 'includes/api/ApiQueryRandom.php',
- 'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php',
- 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
- 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
- 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
- 'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php',
- 'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php',
- 'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
- 'ApiResult' => 'includes/api/ApiResult.php',
- 'ApiRollback' => 'includes/api/ApiRollback.php',
- 'ApiUnblock' => 'includes/api/ApiUnblock.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/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/diff
+ 'AncestorComparator' => 'includes/diff/HTMLDiff.php',
+ 'AnchorToString' => 'includes/diff/HTMLDiff.php',
+ 'ArrayDiffFormatter' => 'includes/diff/DifferenceEngine.php',
+ 'BodyNode' => 'includes/diff/Nodes.php',
+ 'ChangeText' => 'includes/diff/HTMLDiff.php',
+ 'ChangeTextGenerator' => 'includes/diff/HTMLDiff.php',
+ 'DelegatingContentHandler' => 'includes/diff/HTMLDiff.php',
+ '_DiffEngine' => 'includes/diff/DifferenceEngine.php',
+ 'DifferenceEngine' => 'includes/diff/DifferenceEngine.php',
+ 'DiffFormatter' => 'includes/diff/DifferenceEngine.php',
+ 'Diff' => 'includes/diff/DifferenceEngine.php',
+ '_DiffOp_Add' => 'includes/diff/DifferenceEngine.php',
+ '_DiffOp_Change' => 'includes/diff/DifferenceEngine.php',
+ '_DiffOp_Copy' => 'includes/diff/DifferenceEngine.php',
+ '_DiffOp_Delete' => 'includes/diff/DifferenceEngine.php',
+ '_DiffOp' => 'includes/diff/DifferenceEngine.php',
+ 'DomTreeBuilder' => 'includes/diff/HTMLDiff.php',
+ 'DummyNode' => 'includes/diff/Nodes.php',
+ 'HTMLDiffer' => 'includes/diff/HTMLDiff.php',
+ 'HTMLOutput' => 'includes/diff/HTMLDiff.php',
+ '_HWLDF_WordAccumulator' => 'includes/diff/DifferenceEngine.php',
+ 'ImageNode' => 'includes/diff/Nodes.php',
+ 'LastCommonParentResult' => 'includes/diff/HTMLDiff.php',
+ 'MappedDiff' => 'includes/diff/DifferenceEngine.php',
+ 'Modification' => 'includes/diff/HTMLDiff.php',
+ 'NoContentTagToString' => 'includes/diff/HTMLDiff.php',
+ 'Node' => 'includes/diff/Nodes.php',
+ 'RangeDifference' => 'includes/diff/Diff.php',
+ 'TableDiffFormatter' => 'includes/diff/DifferenceEngine.php',
+ 'TagNode' => 'includes/diff/Nodes.php',
+ 'TagToString' => 'includes/diff/HTMLDiff.php',
+ 'TagToStringFactory' => 'includes/diff/HTMLDiff.php',
+ 'TextNode' => 'includes/diff/Nodes.php',
+ 'TextNodeDiffer' => 'includes/diff/HTMLDiff.php',
+ 'TextOnlyComparator' => 'includes/diff/HTMLDiff.php',
+ 'UnifiedDiffFormatter' => 'includes/diff/DifferenceEngine.php',
+ 'WhiteSpaceNode' => 'includes/diff/Nodes.php',
+ 'WikiDiff3' => 'includes/diff/Diff.php',
+ 'WordLevelDiff' => 'includes/diff/DifferenceEngine.php',
- # includes/filerepo
- 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
- 'File' => 'includes/filerepo/File.php',
- '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/filerepo
+ 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
+ 'File' => 'includes/filerepo/File.php',
+ 'FileCache' => 'includes/filerepo/FileCache.php',
+ 'FileRepo' => 'includes/filerepo/FileRepo.php',
+ 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
+ 'ForeignAPIFile' => 'includes/filerepo/ForeignAPIFile.php',
+ '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/media
+ 'BitmapHandler' => 'includes/media/Bitmap.php',
+ 'BitmapHandler_ClientOnly' => 'includes/media/Bitmap_ClientOnly.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/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/parser
+ 'CoreLinkFunctions' => 'includes/parser/CoreLinkFunctions.php',
+ 'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php',
+ 'DateFormatter' => 'includes/parser/DateFormatter.php',
+ 'LinkHolderArray' => 'includes/parser/LinkHolderArray.php',
+ 'LinkMarkerReplacer' => 'includes/parser/LinkMarkerReplacer.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_LinkHooks' => 'includes/parser/Parser_LinkHooks.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/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',
+ 'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php',
+ 'DeletedContribsPager' => 'includes/specials/SpecialDeletedContributions.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/Import.php',
+ 'ImportStringSource' => 'includes/Import.php',
+ 'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.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',
+ 'SpecialContributions' => 'includes/specials/SpecialContributions.php',
+ 'NewPagesPager' => 'includes/specials/SpecialNewpages.php',
+ 'PageArchive' => 'includes/specials/SpecialUndelete.php',
+ 'SpecialResetpass' => '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',
+ 'SpecialImport' => 'includes/specials/SpecialImport.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',
+ 'SpecialSearchOld' => 'includes/specials/SpecialSearch.php',
+ 'SpecialStatistics' => 'includes/specials/SpecialStatistics.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',
+ 'WantedFilesPage' => 'includes/specials/SpecialWantedfiles.php',
+ 'WantedPagesPage' => 'includes/specials/SpecialWantedpages.php',
+ 'WantedTemplatesPage' => 'includes/specials/SpecialWantedtemplates.php',
+ 'WhatLinksHerePage' => 'includes/specials/SpecialWhatlinkshere.php',
+ 'WikiImporter' => 'includes/Import.php',
+ 'WikiRevision' => 'includes/Import.php',
+ 'WithoutInterwikiPage' => 'includes/specials/SpecialWithoutinterwiki.php',
- # includes/templates
- 'UsercreateTemplate' => 'includes/templates/Userlogin.php',
- 'UserloginTemplate' => 'includes/templates/Userlogin.php',
+ # includes/templates
+ 'UsercreateTemplate' => 'includes/templates/Userlogin.php',
+ 'UserloginTemplate' => 'includes/templates/Userlogin.php',
- # languages
- 'Language' => 'languages/Language.php',
- 'FakeConverter' => 'languages/Language.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',
+ # 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',
- );
+);
+class AutoLoader {
/**
* 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.
+ * as well.
*/
static function autoload( $className ) {
- global $wgAutoloadClasses;
+ global $wgAutoloadClasses, $wgAutoloadLocalClasses;
- wfProfileIn( __METHOD__ );
- if ( isset( self::$localClasses[$className] ) ) {
- $filename = self::$localClasses[$className];
+ if ( isset( $wgAutoloadLocalClasses[$className] ) ) {
+ $filename = $wgAutoloadLocalClasses[$className];
} elseif ( isset( $wgAutoloadClasses[$className] ) ) {
$filename = $wgAutoloadClasses[$className];
} else {
@@ -488,14 +549,15 @@ class AutoLoader {
# The case can sometimes be wrong when unserializing PHP 4 objects
$filename = false;
$lowerClass = strtolower( $className );
- foreach ( self::$localClasses as $class2 => $file2 ) {
+ foreach ( $wgAutoloadLocalClasses as $class2 => $file2 ) {
if ( strtolower( $class2 ) == $lowerClass ) {
$filename = $file2;
}
}
if ( !$filename ) {
+ if( function_exists( 'wfDebug' ) )
+ wfDebug( "Class {$className} not found; skipped loading" );
# Give up
- wfProfileOut( __METHOD__ );
return false;
}
}
@@ -506,7 +568,6 @@ class AutoLoader {
$filename = "$IP/$filename";
}
require( $filename );
- wfProfileOut( __METHOD__ );
return true;
}
@@ -532,4 +593,3 @@ if ( function_exists( 'spl_autoload_register' ) ) {
AutoLoader::autoload( $class );
}
}
-
diff --git a/includes/Autopromote.php b/includes/Autopromote.php
index 68fe6636..c8a4c03b 100644
--- a/includes/Autopromote.php
+++ b/includes/Autopromote.php
@@ -19,7 +19,7 @@ class Autopromote {
$promote[] = $group;
}
- wfRunHooks( 'GetAutoPromoteGroups', array($user, &$promote) );
+ wfRunHooks( 'GetAutoPromoteGroups', array( $user, &$promote ) );
return $promote;
}
@@ -106,9 +106,16 @@ class Autopromote {
case APCOND_AGE:
$age = time() - wfTimestampOrNull( TS_UNIX, $user->getRegistration() );
return $age >= $cond[1];
+ case APCOND_AGE_FROM_EDIT:
+ $age = time() - wfTimestampOrNull( TS_UNIX, $user->getFirstEditTimestamp() );
+ return $age >= $cond[1];
case APCOND_INGROUPS:
$groups = array_slice( $cond, 1 );
return count( array_intersect( $groups, $user->getGroups() ) ) == count( $groups );
+ case APCOND_ISIP:
+ return $cond[1] == wfGetIP();
+ case APCOND_IPINRANGE:
+ return IP::isInRange( wfGetIP(), $cond[1] );
default:
$result = null;
wfRunHooks( 'AutopromoteCondition', array( $cond[0], array_slice( $cond, 1 ), $user, &$result ) );
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index 92311329..572dca6c 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -475,8 +475,19 @@ class MediaWikiBagOStuff extends SqlBagOStuff {
function _fromunixtime($ts) {
return $this->_getDB()->timestamp($ts);
}
+ /***
+ * Note -- this should *not* check wfReadOnly().
+ * Read-only mode has been repurposed from the original
+ * "nothing must write to the database" to "users should not
+ * be able to edit or alter anything user-visible".
+ *
+ * Backend bits like the object cache should continue
+ * to work in this mode, otherwise things will blow up
+ * like the message cache failing to save its state,
+ * causing long delays (bug 11533).
+ */
function _readonly(){
- return wfReadOnly();
+ return false;
}
function _strencode($s) {
return $this->_getDB()->strencode($s);
diff --git a/includes/Block.php b/includes/Block.php
index b208fa8a..2c2227e2 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -13,11 +13,10 @@
*
* @todo This could be used everywhere, but it isn't.
*/
-class Block
-{
+class Block {
/* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
$mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName,
- $mBlockEmail, $mByName, $mAngryAutoblock;
+ $mBlockEmail, $mByName, $mAngryAutoblock, $mAllowUsertalk;
/* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster;
const EB_KEEP_EXPIRED = 1;
@@ -26,7 +25,7 @@ class Block
function __construct( $address = '', $user = 0, $by = 0, $reason = '',
$timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
- $hideName = 0, $blockEmail = 0 )
+ $hideName = 0, $blockEmail = 0, $allowUsertalk = 0 )
{
$this->mId = 0;
# Expand valid IPv6 addresses
@@ -43,6 +42,7 @@ class Block
$this->mEnableAutoblock = $enableAutoblock;
$this->mHideName = $hideName;
$this->mBlockEmail = $blockEmail;
+ $this->mAllowUsertalk = $allowUsertalk;
$this->mForUpdate = false;
$this->mFromMaster = false;
$this->mByName = false;
@@ -50,9 +50,18 @@ class Block
$this->initialiseRange();
}
- static function newFromDB( $address, $user = 0, $killExpired = true )
- {
- $block = new Block();
+ /**
+ * Load a block from the database, using either the IP address or
+ * user ID. Tries the user ID first, and if that doesn't work, tries
+ * the address.
+ *
+ * @param $address String: IP address of user/anon
+ * @param $user Integer: user id of user
+ * @param $killExpired Boolean: delete expired blocks on load
+ * @return Block Object
+ */
+ public static function newFromDB( $address, $user = 0, $killExpired = true ) {
+ $block = new Block;
$block->load( $address, $user, $killExpired );
if ( $block->isValid() ) {
return $block;
@@ -61,8 +70,13 @@ class Block
}
}
- static function newFromID( $id )
- {
+ /**
+ * Load a blocked user from their block id.
+ *
+ * @param $id Integer: Block id to search for
+ * @return Block object
+ */
+ public static function newFromID( $id ) {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->resultObject( $dbr->select( 'ipblocks', '*',
array( 'ipb_id' => $id ), __METHOD__ ) );
@@ -73,21 +87,47 @@ class Block
return null;
}
}
+
+ /**
+ * Check if two blocks are effectively equal
+ *
+ * @return Boolean
+ */
+ public function equals( Block $block ) {
+ return (
+ $this->mAddress == $block->mAddress
+ && $this->mUser == $block->mUser
+ && $this->mAuto == $block->mAuto
+ && $this->mAnonOnly == $block->mAnonOnly
+ && $this->mCreateAccount == $block->mCreateAccount
+ && $this->mExpiry == $block->mExpiry
+ && $this->mEnableAutoblock == $block->mEnableAutoblock
+ && $this->mHideName == $block->mHideName
+ && $this->mBlockEmail == $block->mBlockEmail
+ && $this->mAllowUsertalk == $block->mAllowUsertalk
+ );
+ }
- function clear()
- {
+ /**
+ * Clear all member variables in the current object. Does not clear
+ * the block from the DB.
+ */
+ public function clear() {
$this->mAddress = $this->mReason = $this->mTimestamp = '';
$this->mId = $this->mAnonOnly = $this->mCreateAccount =
$this->mEnableAutoblock = $this->mAuto = $this->mUser =
- $this->mBy = $this->mHideName = $this->mBlockEmail = 0;
+ $this->mBy = $this->mHideName = $this->mBlockEmail = $this->mAllowUsertalk = 0;
$this->mByName = false;
}
/**
- * Get the DB object and set the reference parameter to the query options
+ * Get the DB object and set the reference parameter to the select options.
+ * The options array will contain FOR UPDATE if appropriate.
+ *
+ * @param $options Array
+ * @return Database
*/
- function &getDBOptions( &$options )
- {
+ protected function &getDBOptions( &$options ) {
global $wgAntiLockFlags;
if ( $this->mForUpdate || $this->mFromMaster ) {
$db = wfGetDB( DB_MASTER );
@@ -104,15 +144,15 @@ class Block
}
/**
- * Get a ban from the DB, with either the given address or the given username
+ * Get a block from the DB, with either the given address or the given username
*
- * @param string $address The IP address of the user, or blank to skip IP blocks
- * @param integer $user The user ID, or zero for anonymous users
- * @param bool $killExpired Whether to delete expired rows while loading
+ * @param $address string The IP address of the user, or blank to skip IP blocks
+ * @param $user int The user ID, or zero for anonymous users
+ * @param $killExpired bool Whether to delete expired rows while loading
+ * @return Boolean: the user is blocked from editing
*
*/
- function load( $address = '', $user = 0, $killExpired = true )
- {
+ public function load( $address = '', $user = 0, $killExpired = true ) {
wfDebug( "Block::load: '$address', '$user', $killExpired\n" );
$options = array();
@@ -143,7 +183,10 @@ class Block
if ( $user && $this->mAnonOnly ) {
# Block is marked anon-only
# Whitelist this IP address against autoblocks and range blocks
- $this->clear();
+ # (but not account creation blocks -- bug 13611)
+ if( !$this->mCreateAccount ) {
+ $this->clear();
+ }
return false;
} else {
return true;
@@ -154,7 +197,10 @@ class Block
# Try range block
if ( $this->loadRange( $address, $killExpired, $user ) ) {
if ( $user && $this->mAnonOnly ) {
- $this->clear();
+ # Respect account creation blocks on logged-in users -- bug 13611
+ if( !$this->mCreateAccount ) {
+ $this->clear();
+ }
return false;
} else {
return true;
@@ -180,9 +226,12 @@ class Block
/**
* Fill in member variables from a result wrapper
+ *
+ * @param $res ResultWrapper: row from the ipblocks table
+ * @param $killExpired Boolean: whether to delete expired rows while loading
+ * @return Boolean
*/
- function loadFromResult( ResultWrapper $res, $killExpired = true )
- {
+ protected function loadFromResult( ResultWrapper $res, $killExpired = true ) {
$ret = false;
if ( 0 != $res->numRows() ) {
# Get first block
@@ -216,9 +265,13 @@ class Block
/**
* Search the database for any range blocks matching the given address, and
* load the row if one is found.
+ *
+ * @param $address String: IP address range
+ * @param $killExpired Boolean: whether to delete expired rows while loading
+ * @param $userid Integer: if not 0, then sets ipb_anon_only
+ * @return Boolean
*/
- function loadRange( $address, $killExpired = true, $user = 0 )
- {
+ public function loadRange( $address, $killExpired = true, $user = 0 ) {
$iaddr = IP::toHex( $address );
if ( $iaddr === false ) {
# Invalid address
@@ -247,15 +300,12 @@ class Block
}
/**
- * Determine if a given integer IPv4 address is in a given CIDR network
- * @deprecated Use IP::isInRange
+ * Given a database row from the ipblocks table, initialize
+ * member variables
+ *
+ * @param $row ResultWrapper: a row from the ipblocks table
*/
- function isAddressInRange( $addr, $range ) {
- return IP::isInRange( $addr, $range );
- }
-
- function initFromRow( $row )
- {
+ public function initFromRow( $row ) {
$this->mAddress = $row->ipb_address;
$this->mReason = $row->ipb_reason;
$this->mTimestamp = wfTimestamp(TS_MW,$row->ipb_timestamp);
@@ -266,6 +316,7 @@ class Block
$this->mCreateAccount = $row->ipb_create_account;
$this->mEnableAutoblock = $row->ipb_enable_autoblock;
$this->mBlockEmail = $row->ipb_block_email;
+ $this->mAllowUsertalk = $row->ipb_allow_usertalk;
$this->mHideName = $row->ipb_deleted;
$this->mId = $row->ipb_id;
$this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
@@ -278,8 +329,11 @@ class Block
$this->mRangeEnd = $row->ipb_range_end;
}
- function initialiseRange()
- {
+ /**
+ * Once $mAddress has been set, get the range they came from.
+ * Wrapper for IP::parseRange
+ */
+ protected function initialiseRange() {
$this->mRangeStart = '';
$this->mRangeEnd = '';
@@ -289,64 +343,12 @@ class Block
}
/**
- * Callback with a Block object for every block
- * @return integer number of blocks;
+ * Delete the row from the IP blocks table.
+ *
+ * @return Boolean
*/
- /*static*/ function enumBlocks( $callback, $tag, $flags = 0 )
- {
- global $wgAntiLockFlags;
-
- $block = new Block();
- if ( $flags & Block::EB_FOR_UPDATE ) {
- $db = wfGetDB( DB_MASTER );
- if ( $wgAntiLockFlags & ALF_NO_BLOCK_LOCK ) {
- $options = '';
- } else {
- $options = 'FOR UPDATE';
- }
- $block->forUpdate( true );
- } else {
- $db = wfGetDB( DB_SLAVE );
- $options = '';
- }
- if ( $flags & Block::EB_RANGE_ONLY ) {
- $cond = " AND ipb_range_start <> ''";
- } else {
- $cond = '';
- }
-
- $now = wfTimestampNow();
-
- list( $ipblocks, $user ) = $db->tableNamesN( 'ipblocks', 'user' );
-
- $sql = "SELECT $ipblocks.*,user_name FROM $ipblocks,$user " .
- "WHERE user_id=ipb_by $cond ORDER BY ipb_timestamp DESC $options";
- $res = $db->query( $sql, 'Block::enumBlocks' );
- $num_rows = $db->numRows( $res );
-
- while ( $row = $db->fetchObject( $res ) ) {
- $block->initFromRow( $row );
- if ( ( $flags & Block::EB_RANGE_ONLY ) && $block->mRangeStart == '' ) {
- continue;
- }
-
- if ( !( $flags & Block::EB_KEEP_EXPIRED ) ) {
- if ( $block->mExpiry && $now > $block->mExpiry ) {
- $block->delete();
- } else {
- call_user_func( $callback, $block, $tag );
- }
- } else {
- call_user_func( $callback, $block, $tag );
- }
- }
- $db->freeResult( $res );
- return $num_rows;
- }
-
- function delete()
- {
- if (wfReadOnly()) {
+ public function delete() {
+ if ( wfReadOnly() ) {
return false;
}
if ( !$this->mId ) {
@@ -359,33 +361,17 @@ class Block
}
/**
- * Insert a block into the block table.
- * @return Whether or not the insertion was successful.
- */
- function insert()
- {
+ * Insert a block into the block table. Will fail if there is a conflicting
+ * block (same name and options) already in the database.
+ *
+ * @return Boolean: whether or not the insertion was successful.
+ */
+ public function insert() {
wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
$dbw = wfGetDB( DB_MASTER );
- # Unset ipb_anon_only for user blocks, makes no sense
- if ( $this->mUser ) {
- $this->mAnonOnly = 0;
- }
-
- # Unset ipb_enable_autoblock for IP blocks, makes no sense
- if ( !$this->mUser ) {
- $this->mEnableAutoblock = 0;
- $this->mBlockEmail = 0; //Same goes for email...
- }
-
- if( !$this->mByName ) {
- if( $this->mBy ) {
- $this->mByName = User::whoIs( $this->mBy );
- } else {
- global $wgUser;
- $this->mByName = $wgUser->getName();
- }
- }
+ $this->validateBlockParams();
+ $this->initialiseRange();
# Don't collide with expired blocks
Block::purgeExpired();
@@ -408,7 +394,8 @@ class Block
'ipb_range_start' => $this->mRangeStart,
'ipb_range_end' => $this->mRangeEnd,
'ipb_deleted' => $this->mHideName,
- 'ipb_block_email' => $this->mBlockEmail
+ 'ipb_block_email' => $this->mBlockEmail,
+ 'ipb_allow_usertalk' => $this->mAllowUsertalk
), 'Block::insert', array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
@@ -416,15 +403,76 @@ class Block
if ($affected)
$this->doRetroactiveAutoblock();
- return $affected;
+ return (bool)$affected;
+ }
+
+ /**
+ * Update a block in the DB with new parameters.
+ * The ID field needs to be loaded first.
+ */
+ public function update() {
+ wfDebug( "Block::update; timestamp {$this->mTimestamp}\n" );
+ $dbw = wfGetDB( DB_MASTER );
+
+ $this->validateBlockParams();
+
+ $dbw->update( 'ipblocks',
+ array(
+ 'ipb_user' => $this->mUser,
+ 'ipb_by' => $this->mBy,
+ 'ipb_by_text' => $this->mByName,
+ 'ipb_reason' => $this->mReason,
+ 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
+ 'ipb_auto' => $this->mAuto,
+ 'ipb_anon_only' => $this->mAnonOnly,
+ 'ipb_create_account' => $this->mCreateAccount,
+ 'ipb_enable_autoblock' => $this->mEnableAutoblock,
+ 'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
+ 'ipb_range_start' => $this->mRangeStart,
+ 'ipb_range_end' => $this->mRangeEnd,
+ 'ipb_deleted' => $this->mHideName,
+ 'ipb_block_email' => $this->mBlockEmail,
+ 'ipb_allow_usertalk' => $this->mAllowUsertalk ),
+ array( 'ipb_id' => $this->mId ),
+ 'Block::update' );
+
+ return $dbw->affectedRows();
}
+
+ /**
+ * Make sure all the proper members are set to sane values
+ * before adding/updating a block
+ */
+ protected function validateBlockParams() {
+ # Unset ipb_anon_only for user blocks, makes no sense
+ if ( $this->mUser ) {
+ $this->mAnonOnly = 0;
+ }
+
+ # Unset ipb_enable_autoblock for IP blocks, makes no sense
+ if ( !$this->mUser ) {
+ $this->mEnableAutoblock = 0;
+ $this->mBlockEmail = 0; //Same goes for email...
+ }
+ if( !$this->mByName ) {
+ if( $this->mBy ) {
+ $this->mByName = User::whoIs( $this->mBy );
+ } else {
+ global $wgUser;
+ $this->mByName = $wgUser->getName();
+ }
+ }
+ }
+
+
/**
* Retroactively autoblocks the last IP used by the user (if it is a user)
* blocked by this Block.
- *@return Whether or not a retroactive autoblock was made.
+ *
+ * @return Boolean: whether or not a retroactive autoblock was made.
*/
- function doRetroactiveAutoblock() {
+ public function doRetroactiveAutoblock() {
$dbr = wfGetDB( DB_SLAVE );
#If autoblock is enabled, autoblock the LAST IP used
# - stolen shamelessly from CheckUser_body.php
@@ -458,25 +506,25 @@ class Block
}
}
}
-
+
/**
- * Autoblocks the given IP, referring to this Block.
- * @param string $autoblockip The IP to autoblock.
- * @param bool $justInserted The main block was just inserted
- * @return bool Whether or not an autoblock was inserted.
- */
- function doAutoblock( $autoblockip, $justInserted = false ) {
- # If autoblocks are disabled, go away.
- if ( !$this->mEnableAutoblock ) {
- return;
+ * Checks whether a given IP is on the autoblock whitelist.
+ *
+ * @param $ip String: The IP to check
+ * @return Boolean
+ */
+ public static function isWhitelistedFromAutoblocks( $ip ) {
+ global $wgMemc;
+
+ // Try to get the autoblock_whitelist from the cache, as it's faster
+ // than getting the msg raw and explode()'ing it.
+ $key = wfMemcKey( 'ipb', 'autoblock', 'whitelist' );
+ $lines = $wgMemc->get( $key );
+ if ( !$lines ) {
+ $lines = explode( "\n", wfMsgForContentNoTrans( 'autoblock_whitelist' ) );
+ $wgMemc->set( $key, $lines, 3600 * 24 );
}
- # Check for presence on the autoblock whitelist
- # TODO cache this?
- $lines = explode( "\n", wfMsgForContentNoTrans( 'autoblock_whitelist' ) );
-
- $ip = $autoblockip;
-
wfDebug("Checking the autoblock whitelist..\n");
foreach( $lines as $line ) {
@@ -493,23 +541,42 @@ class Block
# Is the IP in this range?
if (IP::isInRange( $ip, $wlEntry )) {
wfDebug(" IP $ip matches $wlEntry, not autoblocking\n");
- #$autoblockip = null; # Don't autoblock a whitelisted IP.
- return; #This /SHOULD/ introduce a dummy block - but
- # I don't know a safe way to do so. -werdna
+ return true;
} else {
wfDebug( " No match\n" );
}
}
+ return false;
+ }
+
+ /**
+ * Autoblocks the given IP, referring to this Block.
+ *
+ * @param $autoblockIP String: the IP to autoblock.
+ * @param $justInserted Boolean: the main block was just inserted
+ * @return Boolean: whether or not an autoblock was inserted.
+ */
+ public function doAutoblock( $autoblockIP, $justInserted = false ) {
+ # If autoblocks are disabled, go away.
+ if ( !$this->mEnableAutoblock ) {
+ return;
+ }
+
+ # Check for presence on the autoblock whitelist
+ if (Block::isWhitelistedFromAutoblocks($autoblockIP)) {
+ return;
+ }
+
## Allow hooks to cancel the autoblock.
- if (!wfRunHooks( 'AbortAutoblock', array( $autoblockip, &$this ) )) {
+ 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.
- $ipblock = Block::newFromDB( $autoblockip );
+ $ipblock = Block::newFromDB( $autoblockIP );
if ( $ipblock ) {
# If the user is already blocked. Then check if the autoblock would
# exceed the user block. If it would exceed, then do nothing, else
@@ -528,8 +595,8 @@ class Block
}
# Make a new block object with the desired properties
- wfDebug( "Autoblocking {$this->mAddress}@" . $autoblockip . "\n" );
- $ipblock->mAddress = $autoblockip;
+ wfDebug( "Autoblocking {$this->mAddress}@" . $autoblockIP . "\n" );
+ $ipblock->mAddress = $autoblockIP;
$ipblock->mUser = 0;
$ipblock->mBy = $this->mBy;
$ipblock->mByName = $this->mByName;
@@ -539,7 +606,7 @@ class Block
$ipblock->mCreateAccount = $this->mCreateAccount;
# Continue suppressing the name if needed
$ipblock->mHideName = $this->mHideName;
-
+ $ipblock->mAllowUsertalk = $this->mAllowUsertalk;
# If the user is already blocked with an expiry date, we don't
# want to pile on top of that!
if($this->mExpiry) {
@@ -551,8 +618,11 @@ class Block
return $ipblock->insert();
}
- function deleteIfExpired()
- {
+ /**
+ * Check if a block has expired. Delete it if it is.
+ * @return Boolean
+ */
+ public function deleteIfExpired() {
$fname = 'Block::deleteIfExpired';
wfProfileIn( $fname );
if ( $this->isExpired() ) {
@@ -567,8 +637,11 @@ class Block
return $retVal;
}
- function isExpired()
- {
+ /**
+ * Has the block expired?
+ * @return Boolean
+ */
+ public function isExpired() {
wfDebug( "Block::isExpired() checking current " . wfTimestampNow() . " vs $this->mExpiry\n" );
if ( !$this->mExpiry ) {
return false;
@@ -577,13 +650,18 @@ class Block
}
}
- function isValid()
- {
+ /**
+ * Is the block address valid (i.e. not a null string?)
+ * @return Boolean
+ */
+ public function isValid() {
return $this->mAddress != '';
}
- function updateTimestamp()
- {
+ /**
+ * Update the timestamp on autoblocks.
+ */
+ public function updateTimestamp() {
if ( $this->mAuto ) {
$this->mTimestamp = wfTimestamp();
$this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
@@ -600,41 +678,43 @@ class Block
}
}
- /*
- function getIntegerAddr()
- {
- return $this->mIntegerAddr;
- }
-
- function getNetworkBits()
- {
- return $this->mNetworkBits;
- }*/
-
/**
- * @return The blocker user ID.
+ * Get the user id of the blocking sysop
+ *
+ * @return Integer
*/
public function getBy() {
return $this->mBy;
}
/**
- * @return The blocker user name.
+ * Get the username of the blocking sysop
+ *
+ * @return String
*/
- function getByName()
- {
+ public function getByName() {
return $this->mByName;
}
- function forUpdate( $x = NULL ) {
+ /**
+ * Get/set the SELECT ... FOR UPDATE flag
+ */
+ public function forUpdate( $x = NULL ) {
return wfSetVar( $this->mForUpdate, $x );
}
- function fromMaster( $x = NULL ) {
+ /**
+ * Get/set a flag determining whether the master is used for reads
+ */
+ public function fromMaster( $x = NULL ) {
return wfSetVar( $this->mFromMaster, $x );
}
- function getRedactedName() {
+ /**
+ * Get the block name, but with autoblocked IPs hidden as per standard privacy policy
+ * @return String
+ */
+ public function getRedactedName() {
if ( $this->mAuto ) {
return '#' . $this->mId;
} else {
@@ -644,8 +724,12 @@ class Block
/**
* Encode expiry for DB
+ *
+ * @param $expiry String: timestamp for expiry, or
+ * @param $db Database object
+ * @return String
*/
- static function encodeExpiry( $expiry, $db ) {
+ public static function encodeExpiry( $expiry, $db ) {
if ( $expiry == '' || $expiry == Block::infinity() ) {
return Block::infinity();
} else {
@@ -655,8 +739,12 @@ class Block
/**
* Decode expiry which has come from the DB
+ *
+ * @param $expiry String: Database expiry format
+ * @param $timestampType Requested timestamp format
+ * @return String
*/
- static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
+ public static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
if ( $expiry == '' || $expiry == Block::infinity() ) {
return Block::infinity();
} else {
@@ -664,8 +752,12 @@ class Block
}
}
- static function getAutoblockExpiry( $timestamp )
- {
+ /**
+ * Get a timestamp of the expiry for autoblocks
+ *
+ * @return String
+ */
+ public static function getAutoblockExpiry( $timestamp ) {
global $wgAutoblockExpiry;
return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
}
@@ -673,8 +765,10 @@ class Block
/**
* Gets rid of uneeded numbers in quad-dotted/octet IP strings
* For example, 127.111.113.151/24 -> 127.111.113.0/24
+ * @param $range String: IP address to normalize
+ * @return string
*/
- static function normaliseRange( $range ) {
+ public static function normaliseRange( $range ) {
$parts = explode( '/', $range );
if ( count( $parts ) == 2 ) {
// IPv6
@@ -706,31 +800,31 @@ class Block
/**
* Purge expired blocks from the ipblocks table
*/
- static function purgeExpired() {
+ public static function purgeExpired() {
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'ipblocks', array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ );
}
- static function infinity() {
+ /**
+ * Get a value to insert into expiry field of the database when infinite expiry
+ * is desired. In principle this could be DBMS-dependant, but currently all
+ * supported DBMS's support the string "infinity", so we just use that.
+ *
+ * @return String
+ */
+ public static function infinity() {
# This is a special keyword for timestamps in PostgreSQL, and
# works with CHAR(14) as well because "i" sorts after all numbers.
return 'infinity';
-
- /*
- static $infinity;
- if ( !isset( $infinity ) ) {
- $dbr = wfGetDB( DB_SLAVE );
- $infinity = $dbr->bigTimestamp();
- }
- return $infinity;
- */
}
/**
* Convert a DB-encoded expiry into a real string that humans can read.
+ *
+ * @param $encoded_expiry String: Database encoded expiry time
+ * @return String
*/
- static function formatExpiry( $encoded_expiry ) {
-
+ public static function formatExpiry( $encoded_expiry ) {
static $msg = null;
if( is_null( $msg ) ) {
@@ -749,14 +843,15 @@ class Block
$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.
+ * @param $expiry_input String: whatever was typed into the form
+ * @return String: more database friendly
*/
- static function parseExpiryInput( $expiry_input ) {
+ public static function parseExpiryInput( $expiry_input ) {
if ( $expiry_input == 'infinite' || $expiry_input == 'indefinite' ) {
$expiry = 'infinity';
} else {
@@ -765,7 +860,6 @@ class Block
return false;
}
}
-
return $expiry;
}
diff --git a/includes/Category.php b/includes/Category.php
index acafc47a..78567add 100644
--- a/includes/Category.php
+++ b/includes/Category.php
@@ -1,6 +1,8 @@
<?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.
+ * 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.
*
@@ -79,7 +81,7 @@ class Category {
/**
* Factory function.
*
- * @param array $name A category name (no "Category:" prefix). It need
+ * @param $name Array: 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
*/
@@ -99,8 +101,8 @@ class Category {
/**
* Factory function.
*
- * @param array $title Title for the category page
- * @return mixed Category, or false on a totally invalid name
+ * @param $title Title for the category page
+ * @return Mixed: category, or false on a totally invalid name
*/
public static function newFromTitle( $title ) {
$cat = new self();
@@ -114,7 +116,7 @@ class Category {
/**
* Factory function.
*
- * @param array $id A category id
+ * @param $id Integer: a category id
* @return Category
*/
public static function newFromID( $id ) {
@@ -192,6 +194,33 @@ class Category {
return $this->mTitle;
}
+ /**
+ * Fetch a TitleArray of up to $limit category members, beginning after the
+ * category sort key $offset.
+ * @param $limit integer
+ * @param $offset string
+ * @return TitleArray object for category members.
+ */
+ public function getMembers( $limit = false, $offset = '' ) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ $conds = array( 'cl_to' => $this->getName(), 'cl_from = page_id' );
+ $options = array( 'ORDER BY' => 'cl_sortkey' );
+ if( $limit ) $options[ 'LIMIT' ] = $limit;
+ if( $offset !== '' ) $conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset );
+
+ return TitleArray::newFromResult(
+ $dbr->select(
+ array( 'page', 'categorylinks' ),
+ array( 'page_id', 'page_namespace','page_title', 'page_len',
+ 'page_is_redirect', 'page_latest' ),
+ $conds,
+ __METHOD__,
+ $options
+ )
+ );
+ }
+
/** Generic accessor */
private function getX( $key ) {
if( !$this->initialize() ) {
@@ -228,7 +257,7 @@ class Category {
}
$cond1 = $dbw->conditional( 'page_namespace='.NS_CATEGORY, 1, 'NULL' );
- $cond2 = $dbw->conditional( 'page_namespace='.NS_IMAGE, 1, 'NULL' );
+ $cond2 = $dbw->conditional( 'page_namespace='.NS_FILE, 1, 'NULL' );
$result = $dbw->selectRow(
array( 'categorylinks', 'page' ),
array( 'COUNT(*) AS pages',
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 92e4e279..4ac24b5f 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -36,18 +36,18 @@ class CategoryPage extends Article {
$this->closeShowCategory();
}
}
-
+
/**
- * This page should not be cached if 'from' or 'until' has been used
- * @return bool
+ * Don't return a 404 for categories in use.
*/
- function isFileCacheable() {
- global $wgRequest;
-
- return ( ! Article::isFileCacheable()
- || $wgRequest->getVal( 'from' )
- || $wgRequest->getVal( 'until' )
- ) ? false : true;
+ function hasViewableContent() {
+ if( parent::hasViewableContent() ) {
+ return true;
+ } else {
+ $cat = Category::newFromTitle( $this->mTitle );
+ return $cat->getId() != 0;
+ }
+
}
function openShowCategory() {
@@ -85,8 +85,6 @@ class CategoryViewer {
/**
* Format the category data list.
*
- * @param string $from -- return only sort keys from this item on
- * @param string $until -- don't return keys after this point.
* @return string HTML output
* @private
*/
@@ -144,7 +142,7 @@ class CategoryViewer {
/**
* Add a subcategory to the internal lists, using a title object
- * @deprectated kept for compatibility, please use addSubcategoryObject instead
+ * @deprecated kept for compatibility, please use addSubcategoryObject instead
*/
function addSubcategory( $title, $sortkey, $pageLength ) {
global $wgContLang;
@@ -225,14 +223,14 @@ class CategoryViewer {
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_to' => $this->title->getDBkey() ),
+ array( $pageCondition, 'cl_to' => $this->title->getDBkey() ),
__METHOD__,
array( 'ORDER BY' => $this->flip ? 'cl_sortkey DESC' : 'cl_sortkey',
- 'USE INDEX' => array( 'categorylinks' => '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 ) ) );
+ 'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY ) )
+ );
$count = 0;
$this->nextPage = null;
@@ -249,7 +247,7 @@ class CategoryViewer {
if( $title->getNamespace() == NS_CATEGORY ) {
$cat = Category::newFromRow( $x, $title );
$this->addSubcategoryObject( $cat, $x->cl_sortkey, $x->page_len );
- } elseif( $this->showGallery && $title->getNamespace() == NS_IMAGE ) {
+ } elseif( $this->showGallery && $title->getNamespace() == NS_FILE ) {
$this->addImage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
} else {
$this->addPage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
@@ -339,10 +337,10 @@ class CategoryViewer {
* Format a list of articles chunked by letter, either as a
* bullet list or a columnar format, depending on the length.
*
- * @param array $articles
- * @param array $articles_start_char
- * @param int $cutoff
- * @return string
+ * @param $articles Array
+ * @param $articles_start_char Array
+ * @param $cutoff Int
+ * @return String
* @private
*/
function formatList( $articles, $articles_start_char, $cutoff = 6 ) {
@@ -359,9 +357,9 @@ class CategoryViewer {
* Format a list of articles chunked by letter in a three-column
* list, ordered vertically.
*
- * @param array $articles
- * @param array $articles_start_char
- * @return string
+ * @param $articles Array
+ * @param $articles_start_char Array
+ * @return String
* @private
*/
function columnList( $articles, $articles_start_char ) {
@@ -418,9 +416,9 @@ class CategoryViewer {
/**
* Format a list of articles chunked by letter in a bullet list.
- * @param array $articles
- * @param array $articles_start_char
- * @return string
+ * @param $articles Array
+ * @param $articles_start_char Array
+ * @return String
* @private
*/
function shortList( $articles, $articles_start_char ) {
@@ -440,12 +438,12 @@ class CategoryViewer {
}
/**
- * @param Title $title
- * @param string $first
- * @param string $last
- * @param int $limit
- * @param array $query - additional query options to pass
- * @return string
+ * @param $title Title object
+ * @param $first String
+ * @param $last String
+ * @param $limit Int
+ * @param $query Array: additional query options to pass
+ * @return String
* @private
*/
function pagingLinks( $title, $first, $last, $limit, $query = array() ) {
@@ -477,10 +475,10 @@ class CategoryViewer {
* 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.
+ * @param $rescnt Int: The number of items returned by our database query.
+ * @param $dbcnt Int: The number of items according to the category table.
+ * @param $type String: '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;
@@ -500,8 +498,12 @@ class CategoryViewer {
# Case 1: seems sane.
$totalcnt = $dbcnt;
} elseif($totalrescnt < $this->limit && !$this->from && !$this->until){
- # Case 2: not sane, but salvageable.
+ # Case 2: not sane, but salvageable. Use the number of results.
+ # Since there are fewer than 200, we can also take this opportunity
+ # to refresh the incorrect category table entry -- which should be
+ # quick due to the small number of entries.
$totalcnt = $rescnt;
+ $this->cat->refreshCounts();
} else {
# Case 3: hopeless. Don't give a total count at all.
return wfMsgExt("category-$type-count-limited", 'parse',
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index d28f2eeb..4413bd1a 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -86,9 +86,15 @@ class Categoryfinder {
* This functions recurses through the parent representation, trying to match the conditions
* @param $id The article/category to check
* @param $conds The array of categories to match
+ * @param $path used to check for recursion loops
* @return bool Does this match the conditions?
*/
- function check ( $id , &$conds ) {
+ function check ( $id , &$conds, $path=array() ) {
+ // Check for loops and stop!
+ if( in_array( $id, $path ) )
+ return false;
+ $path[] = $id;
+
# Shortcut (runtime paranoia): No contitions=all matched
if ( count ( $conds ) == 0 ) return true ;
@@ -120,7 +126,7 @@ class Categoryfinder {
# No sub-parent
continue ;
}
- $done = $this->check ( $this->name2id[$pname] , $conds ) ;
+ $done = $this->check ( $this->name2id[$pname] , $conds, $path );
if ( $done OR count ( $conds ) == 0 ) {
# Subparents have done it!
return true ;
diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php
index 9bee1790..f3c3e429 100644
--- a/includes/ChangesFeed.php
+++ b/includes/ChangesFeed.php
@@ -12,14 +12,15 @@ class ChangesFeed {
public function getFeedObject( $title, $description ) {
global $wgSitename, $wgContLanguageCode, $wgFeedClasses, $wgTitle;
$feedTitle = "$wgSitename - {$title} [$wgContLanguageCode]";
-
+ if( !isset($wgFeedClasses[$this->format] ) )
+ return false;
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;
+ global $wgFeedClasses, $wgSitename, $wgContLanguageCode;
if ( !FeedUtils::checkFeedOutput( $this->format ) ) {
return;
@@ -85,7 +86,7 @@ class ChangesFeed {
}
/**
- * @todo document
+ * Generate the feed items given a row from the database.
* @param $rows Database resource with recentchanges rows
* @param $feed Feed object
*/
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index 436f006e..a8f5fff0 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -3,10 +3,9 @@
/**
* @todo document
*/
-class RCCacheEntry extends RecentChange
-{
+class RCCacheEntry extends RecentChange {
var $secureName, $link;
- var $curlink , $difflink, $lastlink , $usertalklink , $versionlink ;
+ var $curlink , $difflink, $lastlink, $usertalklink, $versionlink;
var $userlink, $timestamp, $watched;
static function newFromParent( $rc ) {
@@ -15,7 +14,7 @@ class RCCacheEntry extends RecentChange
$rc2->mExtra = $rc->mExtra;
return $rc2;
}
-} ;
+}
/**
* Class to show various lists of changes:
@@ -25,13 +24,13 @@ class RCCacheEntry extends RecentChange
*/
class ChangesList {
# Called by history lists and recent changes
- #
+ public $skin;
/**
* Changeslist contructor
* @param Skin $skin
*/
- function __construct( &$skin ) {
+ public function __construct( &$skin ) {
$this->skin =& $skin;
$this->preCacheMessages();
}
@@ -47,7 +46,8 @@ class ChangesList {
$sk = $user->getSkin();
$list = NULL;
if( wfRunHooks( 'FetchChangesList', array( &$user, &$sk, &$list ) ) ) {
- return $user->getOption( 'usenewrc' ) ? new EnhancedChangesList( $sk ) : new OldChangesList( $sk );
+ return $user->getOption( 'usenewrc' ) ?
+ new EnhancedChangesList( $sk ) : new OldChangesList( $sk );
} else {
return $list;
}
@@ -58,7 +58,6 @@ class ChangesList {
* they are called often, we call them once and save them in $this->message
*/
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 ) {
@@ -78,10 +77,10 @@ class ChangesList {
* @return string
*/
protected function recentChangesFlags( $new, $minor, $patrolled, $nothing = '&nbsp;', $bot = false ) {
- $f = $new ? '<span class="newpage">' . $this->message['newpageletter'] . '</span>'
- : $nothing;
- $f .= $minor ? '<span class="minor">' . $this->message['minoreditletter'] . '</span>'
- : $nothing;
+ $f = $new ?
+ '<span class="newpage">' . $this->message['newpageletter'] . '</span>' : $nothing;
+ $f .= $minor ?
+ '<span class="minor">' . $this->message['minoreditletter'] . '</span>' : $nothing;
$f .= $bot ? '<span class="bot">' . $this->message['boteditletter'] . '</span>' : $nothing;
$f .= $patrolled ? '<span class="unpatrolled">!</span>' : $nothing;
return $f;
@@ -99,6 +98,30 @@ class ChangesList {
$this->rclistOpen = false;
return '';
}
+
+ /**
+ * Show formatted char difference
+ * @param int $old bytes
+ * @param int $new bytes
+ * @returns string
+ */
+ public static function showCharacterDifference( $old, $new ) {
+ global $wgRCChangedSizeThreshold, $wgLang;
+ $szdiff = $new - $old;
+ $formatedSize = wfMsgExt( 'rc-change-size', array( 'parsemag', 'escape'), $wgLang->formatNum($szdiff) );
+ if( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
+ $tag = 'strong';
+ } else {
+ $tag = 'span';
+ }
+ if( $szdiff === 0 ) {
+ return "<$tag class='mw-plusminus-null'>($formatedSize)</$tag>";
+ } elseif( $szdiff > 0 ) {
+ return "<$tag class='mw-plusminus-pos'>(+$formatedSize)</$tag>";
+ } else {
+ return "<$tag class='mw-plusminus-neg'>($formatedSize)</$tag>";
+ }
+ }
/**
* Returns text for the end of RC
@@ -116,21 +139,18 @@ class ChangesList {
# Diff
$s .= '(' . $this->message['diff'] . ') (';
# Hist
- $s .= $this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), $this->message['hist'], 'action=history' ) .
- ') . . ';
-
+ $s .= $this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), $this->message['hist'],
+ 'action=history' ) . ') . . ';
# "[[x]] moved to [[y]]"
$msg = ( $rc->mAttribs['rc_type'] == RC_MOVE ) ? '1movedto2' : '1movedto2_redir';
$s .= wfMsg( $msg, $this->skin->makeKnownLinkObj( $rc->getTitle(), '', 'redirect=no' ),
$this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), '' ) );
}
- protected function insertDateHeader(&$s, $rc_timestamp) {
+ protected function insertDateHeader( &$s, $rc_timestamp ) {
global $wgLang;
-
# Make date header if necessary
$date = $wgLang->date( $rc_timestamp, true, true );
- $s = '';
if( $date != $this->lastdate ) {
if( '' != $this->lastdate ) {
$s .= "</ul>\n";
@@ -141,21 +161,19 @@ class ChangesList {
}
}
- protected function insertLog(&$s, $title, $logtype) {
+ protected function insertLog( &$s, $title, $logtype ) {
$logname = LogPage::logName( $logtype );
$s .= '(' . $this->skin->makeKnownLinkObj($title, $logname ) . ')';
}
- protected function insertDiffHist(&$s, &$rc, $unpatrolled) {
+ protected function insertDiffHist( &$s, &$rc, $unpatrolled ) {
# Diff link
- if( !$this->userCan($rc,Revision::DELETED_TEXT) ) {
+ if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
$diffLink = $this->message['diff'];
- } else if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
+ } else if( !$this->userCan($rc,Revision::DELETED_TEXT) ) {
$diffLink = $this->message['diff'];
} else {
- $rcidparam = $unpatrolled
- ? array( 'rcid' => $rc->mAttribs['rc_id'] )
- : array();
+ $rcidparam = $unpatrolled ? array( 'rcid' => $rc->mAttribs['rc_id'] ) : array();
$diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['diff'],
wfArrayToCGI( array(
'curid' => $rc->mAttribs['rc_cur_id'],
@@ -165,7 +183,6 @@ class ChangesList {
'', '', ' tabindex="'.$rc->counter.'"');
}
$s .= '('.$diffLink.') (';
-
# History link
$s .= $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['hist'],
wfArrayToCGI( array(
@@ -174,39 +191,40 @@ class ChangesList {
$s .= ') . . ';
}
- protected function insertArticleLink(&$s, &$rc, $unpatrolled, $watched) {
- # Article link
+ protected function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
+ global $wgContLang;
# If it's a new article, there is no diff link, but if it hasn't been
# patrolled yet, we need to give users a way to do so
- $params = ( $unpatrolled && $rc->mAttribs['rc_type'] == RC_NEW )
- ? 'rcid='.$rc->mAttribs['rc_id']
- : '';
+ $params = ( $unpatrolled && $rc->mAttribs['rc_type'] == RC_NEW ) ?
+ 'rcid='.$rc->mAttribs['rc_id'] : '';
if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
$articlelink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
$articlelink = '<span class="history-deleted">'.$articlelink.'</span>';
} else {
$articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
}
- if( $watched )
+ # Bolden pages watched by this user
+ if( $watched ) {
$articlelink = "<strong class=\"mw-watched\">{$articlelink}</strong>";
- global $wgContLang;
+ }
+ # RTL/LTR marker
$articlelink .= $wgContLang->getDirMark();
- wfRunHooks('ChangesListInsertArticleLink',
- array(&$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched));
+ wfRunHooks( 'ChangesListInsertArticleLink',
+ array(&$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched) );
- $s .= ' '.$articlelink;
+ $s .= " $articlelink";
}
- protected 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 ) . ' . . ';
+ $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 */
- protected function insertUserRelatedLinks(&$s, &$rc) {
- if ( $this->isDeleted($rc,Revision::DELETED_USER) ) {
+ public 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'] );
@@ -216,13 +234,11 @@ class ChangesList {
/** 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) ) {
+ 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'],
+ $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 );
}
}
@@ -230,10 +246,8 @@ class ChangesList {
/** insert a formatted comment */
protected function insertComment(&$s, &$rc) {
- # Add comment
if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
- // log comment
- if ( $this->isDeleted($rc,Revision::DELETED_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() );
@@ -256,8 +270,8 @@ class ChangesList {
protected function numberofWatchingusers( $count ) {
global $wgLang;
static $cache = array();
- if ( $count > 0 ) {
- if ( !isset( $cache[$count] ) ) {
+ if( $count > 0 ) {
+ if( !isset( $cache[$count] ) ) {
$cache[$count] = wfMsgExt('number_of_watching_users_RCview',
array('parsemag', 'escape'), $wgLang->formatNum($count));
}
@@ -290,12 +304,20 @@ class ChangesList {
$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" );
+ wfDebug( "Checking for $permission due to $field match on {$rc->mAttribs['rc_deleted']}\n" );
return $wgUser->isAllowed( $permission );
} else {
return true;
}
}
+
+ protected function maybeWatchedLink( $link, $watched=false ) {
+ if( $watched ) {
+ return '<strong class="mw-watched">' . $link . '</strong>';
+ } else {
+ return '<span class="mw-rc-unwatched">' . $link . '</span>';
+ }
+ }
}
@@ -308,55 +330,43 @@ class OldChangesList extends ChangesList {
*/
public function recentChangesLine( &$rc, $watched = false ) {
global $wgContLang, $wgRCShowChangedSize, $wgUser;
-
- $fname = 'ChangesList::recentChangesLineOld';
- wfProfileIn( $fname );
-
- # Extract DB fields into local scope
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
- extract( $rc->mAttribs );
-
+ wfProfileIn( __METHOD__ );
# Should patrol-related stuff be shown?
- $unpatrolled = $wgUser->useRCPatrol() && $rc_patrolled == 0;
+ $unpatrolled = $wgUser->useRCPatrol() && !$rc->mAttribs['rc_patrolled'];
- $this->insertDateHeader($s,$rc_timestamp);
-
- $s .= '<li>';
+ $dateheader = ''; // $s now contains only <li>...</li>, for hooks' convenience.
+ $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
+ $s = '';
// Moved pages
- if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ if( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
$this->insertMove( $s, $rc );
// Log entries
- } elseif( $rc_log_type ) {
- $logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
- $this->insertLog( $s, $logtitle, $rc_log_type );
+ } elseif( $rc->mAttribs['rc_log_type'] ) {
+ $logtitle = Title::newFromText( 'Log/'.$rc->mAttribs['rc_log_type'], NS_SPECIAL );
+ $this->insertLog( $s, $logtitle, $rc->mAttribs['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" );
+ } elseif( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
+ list( $name, $subpage ) = SpecialPage::resolveAliasWithSubpage( $rc->mAttribs['rc_title'] );
+ if( $name == 'Log' ) {
+ $this->insertLog( $s, $rc->getTitle(), $subpage );
}
// Regular entries
} else {
- wfProfileIn($fname.'-page');
-
- $this->insertDiffHist($s, $rc, $unpatrolled);
-
+ $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 );
- $this->insertArticleLink($s, $rc, $unpatrolled, $watched);
-
- wfProfileOut($fname.'-page');
+ $s .= $this->recentChangesFlags( $rc->mAttribs['rc_new'], $rc->mAttribs['rc_minor'],
+ $unpatrolled, '', $rc->mAttribs['rc_bot'] );
+ $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
}
-
- wfProfileIn( $fname.'-rest' );
-
- $this->insertTimestamp($s,$rc);
-
+ # Edit/log timestamp
+ $this->insertTimestamp( $s, $rc );
+ # Bytes added or removed
if( $wgRCShowChangedSize ) {
- $s .= ( $rc->getCharacterDifference() == '' ? '' : $rc->getCharacterDifference() . ' . . ' );
+ $cd = $rc->getCharacterDifference();
+ if( $cd != '' ) {
+ $s .= "$cd . . ";
+ }
}
# User tool links
$this->insertUserRelatedLinks($s,$rc);
@@ -364,29 +374,45 @@ class OldChangesList extends ChangesList {
$this->insertAction($s, $rc);
# Edit or log comment
$this->insertComment($s, $rc);
-
# Mark revision as deleted if so
- if ( !$rc_log_type && $this->isDeleted($rc,Revision::DELETED_TEXT) )
+ if( !$rc->mAttribs['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));
+ }
+ # How many users watch this page
+ if( $rc->numberofWatchingusers > 0 ) {
+ $s .= ' ' . wfMsg( 'number_of_watching_users_RCview',
+ $wgContLang->formatNum($rc->numberofWatchingusers) );
}
- $s .= "</li>\n";
-
- wfProfileOut( $fname.'-rest' );
+ wfRunHooks( 'OldChangesListRecentChangesLine', array(&$this, &$s, $rc) );
- wfProfileOut( $fname );
- return $s;
+ wfProfileOut( __METHOD__ );
+ return "$dateheader<li>$s</li>\n";
}
}
/**
- * Generate a list of changes using an Enhanced system (use javascript).
+ * Generate a list of changes using an Enhanced system (uses javascript).
*/
class EnhancedChangesList extends ChangesList {
/**
+ * Add the JavaScript file for enhanced changeslist
+ * @ return string
+ */
+ public function beginRecentChangesList() {
+ global $wgStylePath, $wgJsMimeType, $wgStyleVersion;
+ $this->rc_cache = array();
+ $this->rcMoveIndex = 0;
+ $this->rcCacheIndex = 0;
+ $this->lastdate = '';
+ $this->rclistOpen = false;
+ $script = Xml::tags( 'script', array(
+ 'type' => $wgJsMimeType,
+ 'src' => $wgStylePath . "/common/enhancedchanges.js?$wgStyleVersion" ), '' );
+ return $script;
+ }
+ /**
* Format a line for enhanced recentchange (aka with javascript and block of lines).
*/
public function recentChangesLine( &$baseRC, $watched = false ) {
@@ -396,12 +422,13 @@ class EnhancedChangesList extends ChangesList {
$rc = RCCacheEntry::newFromParent( $baseRC );
# Extract fields from DB into the function scope (rc_xxxx variables)
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
+ // FIXME: Would be good to replace this extract() call with something
+ // that explicitly initializes variables.
extract( $rc->mAttribs );
$curIdEq = 'curid=' . $rc_cur_id;
# If it's a new day, add the headline and flush the cache
- $date = $wgLang->date( $rc_timestamp, true);
+ $date = $wgLang->date( $rc_timestamp, true );
$ret = '';
if( $date != $this->lastdate ) {
# Process current cache
@@ -425,17 +452,6 @@ class EnhancedChangesList extends ChangesList {
$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' ) {
- # Log updates, etc
- $logname = LogPage::logName( $logtype );
- $clink = '(' . $this->skin->makeKnownLinkObj( $rc->getTitle(), $logname ) . ')';
- } else {
- wfDebug( "Unexpected special page in recentchanges\n" );
- $clink = '';
- }
// New unpatrolled pages
} else if( $rc->unpatrolled && $rc_type == RC_NEW ) {
$clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', "rcid={$rc_id}" );
@@ -443,11 +459,23 @@ class EnhancedChangesList extends ChangesList {
} 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) ) . ')';
+ $clink = '(' . $this->skin->makeKnownLinkObj( $logtitle,
+ LogPage::logName($rc_log_type) ) . ')';
} else {
$clink = $this->skin->makeLinkObj( $rc->getTitle(), '' );
}
$watched = false;
+ // Log entries (old format) and special pages
+ } elseif( $rc_namespace == NS_SPECIAL ) {
+ list( $specialName, $logtype ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
+ if ( $specialName == 'Log' ) {
+ # Log updates, etc
+ $logname = LogPage::logName( $logtype );
+ $clink = '(' . $this->skin->makeKnownLinkObj( $rc->getTitle(), $logname ) . ')';
+ } else {
+ wfDebug( "Unexpected special page in recentchanges\n" );
+ $clink = '';
+ }
// Edits
} else {
$clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '' );
@@ -473,7 +501,8 @@ class EnhancedChangesList extends ChangesList {
$querycur = $curIdEq."&diff=0&oldid=$rc_this_oldid";
$querydiff = $curIdEq."&diff=$rc_this_oldid&oldid=$rc_last_oldid$rcIdQuery";
$aprops = ' tabindex="'.$baseRC->counter.'"';
- $curLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['cur'], $querycur, '' ,'', $aprops );
+ $curLink = $this->skin->makeKnownLinkObj( $rc->getTitle(),
+ $this->message['cur'], $querycur, '' ,'', $aprops );
# Make "diff" an "cur" links
if( !$showdifflinks ) {
@@ -485,7 +514,8 @@ class EnhancedChangesList extends ChangesList {
}
$diffLink = $this->message['diff'];
} else {
- $diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['diff'], $querydiff, '' ,'', $aprops );
+ $diffLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['diff'],
+ $querydiff, '' ,'', $aprops );
}
# Make "last" link
@@ -545,7 +575,7 @@ class EnhancedChangesList extends ChangesList {
$curId = $currentRevision = 0;
# Some catalyst variables...
$namehidden = true;
- $alllogs = true;
+ $allLogs = true;
foreach( $block as $rcObj ) {
$oldid = $rcObj->mAttribs['rc_last_oldid'];
if( $rcObj->mAttribs['rc_new'] ) {
@@ -564,7 +594,7 @@ class EnhancedChangesList extends ChangesList {
$unpatrolled = true;
}
if( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
- $alllogs = false;
+ $allLogs = false;
}
# Get the latest entry with a page_id and oldid
# since logs may not have these.
@@ -587,20 +617,24 @@ class EnhancedChangesList extends ChangesList {
$text = $userlink;
$text .= $wgContLang->getDirMark();
if( $count > 1 ) {
- $text .= ' ('.$count.'&times;)';
+ $text .= ' (' . $wgLang->formatNum( $count ) . '×)';
}
array_push( $users, $text );
}
- $users = ' <span class="changedby">[' . implode( $this->message['semicolon-separator'] . ' ', $users ) . ']</span>';
+ $users = ' <span class="changedby">[' .
+ implode( $this->message['semicolon-separator'], $users ) . ']</span>';
- # Arrow
- $rci = 'RCI'.$this->rcCacheIndex;
- $rcl = 'RCL'.$this->rcCacheIndex;
- $rcm = 'RCM'.$this->rcCacheIndex;
- $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>';
+ # ID for JS visibility toggle
+ $jsid = $this->rcCacheIndex;
+ # onclick handler to toggle hidden/expanded
+ $toggleLink = "onclick='toggleVisibility($jsid); return false'";
+ # Title for <a> tags
+ $expandTitle = htmlspecialchars( wfMsg('rc-enhanced-expand') );
+ $closeTitle = htmlspecialchars( wfMsg('rc-enhanced-hide') );
+
+ $tl = "<span id='mw-rc-openarrow-$jsid' class='mw-changeslist-expanded' style='visibility:hidden'><a href='#' $toggleLink title='$expandTitle'>" . $this->sideArrow() . "</a></span>";
+ $tl .= "<span id='mw-rc-closearrow-$jsid' class='mw-changeslist-hidden' style='display:none'><a href='#' $toggleLink title='$closeTitle'>" . $this->downArrow() . "</a></span>";
$r .= '<td valign="top" style="white-space: nowrap"><tt>'.$tl.'&nbsp;';
# Main line
@@ -612,8 +646,10 @@ class EnhancedChangesList extends ChangesList {
# Article link
if( $namehidden ) {
$r .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
- } else {
+ } else if( $allLogs ) {
$r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+ } else {
+ $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
}
$r .= $wgContLang->getDirMark();
@@ -627,7 +663,7 @@ class EnhancedChangesList extends ChangesList {
}
# Total change link
$r .= ' ';
- if( !$alllogs ) {
+ if( !$allLogs ) {
$r .= '(';
if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
$r .= $nchanges[$n];
@@ -637,11 +673,21 @@ class EnhancedChangesList extends ChangesList {
$r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
$nchanges[$n], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
}
- $r .= ') . . ';
}
+ # History
+ if( $allLogs ) {
+ // don't show history link for logs
+ } else if( $namehidden || !$block[0]->getTitle()->exists() ) {
+ $r .= $this->message['semicolon-separator'] . $this->message['hist'] . ')';
+ } else {
+ $r .= $this->message['semicolon-separator'] . $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
+ $this->message['hist'], $curIdEq . '&action=history' ) . ')';
+ }
+ $r .= ' . . ';
+
# Character difference (does not apply if only log items)
- if( $wgRCShowChangedSize && !$alllogs ) {
+ if( $wgRCShowChangedSize && !$allLogs ) {
$last = 0;
$first = count($block) - 1;
# Some events (like logs) have an "empty" size, so we need to skip those...
@@ -662,26 +708,18 @@ class EnhancedChangesList extends ChangesList {
}
}
- # 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 .= $users;
$r .= $this->numberofWatchingusers($block[0]->numberofWatchingusers);
$r .= "</td></tr></table>\n";
# Sub-entries
- $r .= '<div id="'.$rci.'" style="display:none;"><table cellpadding="0" cellspacing="0" border="0" style="background: none">';
+ $r .= '<div id="mw-rc-subentries-'.$jsid.'" class="mw-changeslist-hidden">';
+ $r .= '<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 fields from DB into the function scope (rc_xxxx variables)
+ // FIXME: Would be good to replace this extract() call with something
+ // that explicitly initializes variables.
extract( $rcObj->mAttribs );
#$r .= '<tr><td valign="top">'.$this->spacerArrow();
@@ -701,9 +739,10 @@ class EnhancedChangesList extends ChangesList {
} else if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
$link = '<span class="history-deleted"><tt>'.$rcObj->timestamp.'</tt></span> ';
} else {
- $rcIdEq = ($rcObj->unpatrolled && $rc_type == RC_NEW) ? '&rcid='.$rcObj->mAttribs['rc_id'] : '';
-
- $link = '<tt>'.$this->skin->makeKnownLinkObj( $rcObj->getTitle(), $rcObj->timestamp, $curIdEq.'&'.$o.$rcIdEq ).'</tt>';
+ $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> ';
}
@@ -712,7 +751,7 @@ class EnhancedChangesList extends ChangesList {
if ( !$rc_type == RC_LOG || $rc_type == RC_NEW ) {
$r .= ' (';
$r .= $rcObj->curlink;
- $r .= $this->message['semicolon-separator'] . ' ';
+ $r .= $this->message['semicolon-separator'];
$r .= $rcObj->lastlink;
$r .= ')';
}
@@ -742,26 +781,19 @@ class EnhancedChangesList extends ChangesList {
return $r;
}
- protected function maybeWatchedLink( $link, $watched=false ) {
- if( $watched ) {
- // FIXME: css style might be more appropriate
- return '<strong class="mw-watched">' . $link . '</strong>';
- } else {
- return $link;
- }
- }
-
/**
* Generate HTML for an arrow or placeholder graphic
* @param string $dir one of '', 'd', 'l', 'r'
* @param string $alt text
+ * @param string $title text
* @return string HTML <img> tag
*/
- protected function arrow( $dir, $alt='' ) {
+ protected function arrow( $dir, $alt='', $title='' ) {
global $wgStylePath;
$encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' );
$encAlt = htmlspecialchars( $alt );
- return "<img src=\"$encUrl\" width=\"12\" height=\"12\" alt=\"$encAlt\" />";
+ $encTitle = htmlspecialchars( $title );
+ return "<img src=\"$encUrl\" width=\"12\" height=\"12\" alt=\"$encAlt\" title=\"$encTitle\" />";
}
/**
@@ -772,7 +804,7 @@ class EnhancedChangesList extends ChangesList {
protected function sideArrow() {
global $wgContLang;
$dir = $wgContLang->isRTL() ? 'l' : 'r';
- return $this->arrow( $dir, '+' );
+ return $this->arrow( $dir, '+', wfMsg('rc-enhanced-expand') );
}
/**
@@ -781,7 +813,7 @@ class EnhancedChangesList extends ChangesList {
* @return string HTML <img> tag
*/
protected function downArrow() {
- return $this->arrow( 'd', '-' );
+ return $this->arrow( 'd', '-', wfMsg('rc-enhanced-hide') );
}
/**
@@ -789,7 +821,7 @@ class EnhancedChangesList extends ChangesList {
* @return string HTML <img> tag
*/
protected function spacerArrow() {
- return $this->arrow( '', ' ' );
+ return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
}
/**
@@ -806,16 +838,14 @@ class EnhancedChangesList extends ChangesList {
*/
protected function recentChangesBlockLine( $rcObj ) {
global $wgContLang, $wgRCShowChangedSize;
-
- # Get rc_xxxx variables
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
+ # Extract fields from DB into the function scope (rc_xxxx variables)
+ // FIXME: Would be good to replace this extract() call with something
+ // that explicitly initializes variables.
extract( $rcObj->mAttribs );
- $curIdEq = 'curid='.$rc_cur_id;
+ $curIdEq = "curid={$rc_cur_id}";
$r = '<table cellspacing="0" cellpadding="0" border="0" style="background: none"><tr>';
-
$r .= '<td valign="top" style="white-space: nowrap"><tt>' . $this->spacerArrow() . '&nbsp;';
-
# Flag and Timestamp
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$r .= '&nbsp;&nbsp;&nbsp;&nbsp;'; // 4 flags -> 4 spaces
@@ -823,33 +853,27 @@ class EnhancedChangesList extends ChangesList {
$r .= $this->recentChangesFlags( $rc_type == RC_NEW, $rc_minor, $rcObj->unpatrolled, '&nbsp;', $rc_bot );
}
$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 );
+ $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
}
-
# Diff and hist links
if ( $rc_type != RC_LOG ) {
- $r .= ' ('. $rcObj->difflink . $this->message['semicolon-separator'] . ' ';
- $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), wfMsg( 'hist' ), $curIdEq.'&action=history' ) . ')';
+ $r .= ' ('. $rcObj->difflink . $this->message['semicolon-separator'];
+ $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), wfMsg( 'hist' ),
+ $curIdEq.'&action=history' ) . ')';
}
$r .= ' . . ';
-
# Character diff
- if( $wgRCShowChangedSize ) {
- $r .= ( $rcObj->getCharacterDifference() == '' ? '' : '&nbsp;' . $rcObj->getCharacterDifference() . ' . . ' ) ;
+ if( $wgRCShowChangedSize && ($cd = $rcObj->getCharacterDifference()) ) {
+ $r .= "$cd . . ";
}
-
# User/talk
$r .= ' '.$rcObj->userlink . $rcObj->usertalklink;
-
# Log action (if any)
if( $rc_log_type ) {
if( $this->isDeleted($rcObj,LogPage::DELETED_ACTION) ) {
@@ -859,7 +883,6 @@ class EnhancedChangesList extends ChangesList {
$this->skin, LogPage::extractParams($rc_params), true, true );
}
}
-
# Edit or log comment
if( $rc_type != RC_MOVE && $rc_type != RC_MOVE_OVER_REDIRECT ) {
// log comment
@@ -869,7 +892,6 @@ class EnhancedChangesList extends ChangesList {
$r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
}
}
-
# Show how many people are watching this if enabled
$r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers);
@@ -893,7 +915,6 @@ class EnhancedChangesList extends ChangesList {
$blockOut .= $this->recentChangesBlockGroup( $block );
}
}
-
return '<div>'.$blockOut.'</div>';
}
diff --git a/includes/Credits.php b/includes/Credits.php
index 6326e3a2..ae9377f2 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -20,167 +20,187 @@
* @author <evan@wikitravel.org>
*/
-/**
- * This is largely cadged from PageHistory::history
- */
-function showCreditsPage($article) {
- global $wgOut;
-
- $fname = 'showCreditsPage';
-
- wfProfileIn( $fname );
-
- $wgOut->setPageTitle( $article->mTitle->getPrefixedText() );
- $wgOut->setSubtitle( wfMsg( 'creditspage' ) );
- $wgOut->setArticleFlag( false );
- $wgOut->setArticleRelated( true );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
-
- if( $article->mTitle->getArticleID() == 0 ) {
- $s = wfMsg( 'nocredits' );
- } else {
- $s = getCredits($article, -1);
- }
-
- $wgOut->addHTML( $s );
-
- wfProfileOut( $fname );
-}
-
-function getCredits($article, $cnt, $showIfMax=true) {
- $fname = 'getCredits';
- wfProfileIn( $fname );
- $s = '';
-
- if (isset($cnt) && $cnt != 0) {
- $s = getAuthorCredits($article);
- if ($cnt > 1 || $cnt < 0) {
- $s .= ' ' . getContributorCredits($article, $cnt - 1, $showIfMax);
+class Credits {
+
+ /**
+ * This is largely cadged from PageHistory::history
+ * @param $article Article object
+ */
+ public static function showPage( Article $article ) {
+ global $wgOut;
+
+ wfProfileIn( __METHOD__ );
+
+ $wgOut->setPageTitle( $article->mTitle->getPrefixedText() );
+ $wgOut->setSubtitle( wfMsg( 'creditspage' ) );
+ $wgOut->setArticleFlag( false );
+ $wgOut->setArticleRelated( true );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+
+ if( $article->mTitle->getArticleID() == 0 ) {
+ $s = wfMsg( 'nocredits' );
+ } else {
+ $s = self::getCredits($article, -1 );
}
+
+ $wgOut->addHTML( $s );
+
+ wfProfileOut( __METHOD__ );
}
- wfProfileOut( $fname );
- return $s;
-}
-
-/**
- *
- */
-function getAuthorCredits($article) {
- global $wgLang, $wgAllowRealName;
-
- $last_author = $article->getUser();
-
- if ($last_author == 0) {
- $author_credit = wfMsg('anonymous');
- } else {
- if($wgAllowRealName) { $real_name = User::whoIsReal($last_author); }
- $user_name = User::whoIs($last_author);
-
- if (!empty($real_name)) {
- $author_credit = creditLink($user_name, $real_name);
- } else {
- $author_credit = wfMsg('siteuser', creditLink($user_name));
+ /**
+ * Get a list of contributors of $article
+ * @param $article Article object
+ * @param $cnt Int: maximum list of contributors to show
+ * @param $showIfMax Bool: whether to contributors if there more than $cnt
+ * @return String: html
+ */
+ public static function getCredits($article, $cnt, $showIfMax=true) {
+ wfProfileIn( __METHOD__ );
+ $s = '';
+
+ if( isset( $cnt ) && $cnt != 0 ){
+ $s = self::getAuthor( $article );
+ if ($cnt > 1 || $cnt < 0) {
+ $s .= ' ' . self::getContributors( $article, $cnt - 1, $showIfMax );
+ }
}
- }
- $timestamp = $article->getTimestamp();
- if ($timestamp) {
- $d = $wgLang->date($article->getTimestamp(), true);
- $t = $wgLang->time($article->getTimestamp(), true);
- } else {
- $d = '';
- $t = '';
+ wfProfileOut( __METHOD__ );
+ return $s;
}
- return wfMsg('lastmodifiedatby', $d, $t, $author_credit);
-}
-
-/**
- *
- */
-function getContributorCredits($article, $cnt, $showIfMax) {
-
- global $wgLang, $wgAllowRealName;
- $contributors = $article->getContributors();
+ /**
+ * Get the last author with the last modification time
+ * @param $article Article object
+ */
+ protected static function getAuthor( Article $article ){
+ global $wgLang, $wgAllowRealName;
- $others_link = '';
+ $user = User::newFromId( $article->getUser() );
- # Hmm... too many to fit!
-
- if ($cnt > 0 && count($contributors) > $cnt) {
- $others_link = creditOthersLink($article);
- if (!$showIfMax) {
- return wfMsg('othercontribs', $others_link);
+ $timestamp = $article->getTimestamp();
+ if( $timestamp ){
+ $d = $wgLang->date( $article->getTimestamp(), true );
+ $t = $wgLang->time( $article->getTimestamp(), true );
} else {
- $contributors = array_slice($contributors, 0, $cnt);
+ $d = '';
+ $t = '';
}
+ return wfMsg( 'lastmodifiedatby', $d, $t, self::userLink( $user ) );
}
- $real_names = array();
- $user_names = array();
-
- $anon = '';
-
- # Sift for real versus user names
-
- foreach ($contributors as $user_parts) {
- if ($user_parts[0] != 0) {
- if ($wgAllowRealName && !empty($user_parts[2])) {
- $real_names[] = creditLink($user_parts[1], $user_parts[2]);
+ /**
+ * Get a list of contributors of $article
+ * @param $article Article object
+ * @param $cnt Int: maximum list of contributors to show
+ * @param $showIfMax Bool: whether to contributors if there more than $cnt
+ * @return String: html
+ */
+ protected static function getContributors( Article $article, $cnt, $showIfMax ) {
+ global $wgLang, $wgAllowRealName;
+
+ $contributors = $article->getContributors();
+
+ $others_link = '';
+
+ # Hmm... too many to fit!
+ if( $cnt > 0 && $contributors->count() > $cnt ){
+ $others_link = self::othersLink( $article );
+ if( !$showIfMax )
+ return wfMsg( 'othercontribs', $others_link );
+ }
+
+ $real_names = array();
+ $user_names = array();
+ $anon = 0;
+
+ # Sift for real versus user names
+ foreach( $contributors as $user ) {
+ $cnt--;
+ if( $user->isLoggedIn() ){
+ $link = self::link( $user );
+ if( $wgAllowRealName && $user->getRealName() )
+ $real_names[] = $link;
+ else
+ $user_names[] = $link;
} else {
- $user_names[] = creditLink($user_parts[1]);
+ $anon++;
+ }
+ if( $cnt == 0 ) break;
+ }
+
+ # Two strings: real names, and user names
+ $real = $wgLang->listToText( $real_names );
+ $user = $wgLang->listToText( $user_names );
+ if( $anon )
+ $anon = wfMsgExt( 'anonymous', array( 'parseinline' ), $anon );
+
+ # "ThisSite user(s) A, B and C"
+ if( !empty( $user ) ){
+ $user = wfMsgExt( 'siteusers', array( 'parsemag' ), $user, count( $user_names ) );
+ }
+
+ # This is the big list, all mooshed together. We sift for blank strings
+ $fulllist = array();
+ foreach( array( $real, $user, $anon, $others_link ) as $s ){
+ if( !empty( $s ) ){
+ array_push( $fulllist, $s );
}
- } else {
- $anon = wfMsg('anonymous');
}
- }
-
- # Two strings: real names, and user names
-
- $real = $wgLang->listToText($real_names);
- $user = $wgLang->listToText($user_names);
- # "ThisSite user(s) A, B and C"
+ # Make the list into text...
+ $creds = $wgLang->listToText( $fulllist );
- if (!empty($user)) {
- $user = wfMsg('siteusers', $user);
+ # "Based on work by ..."
+ return empty( $creds ) ? '' : wfMsg( 'othercontribs', $creds );
}
- # This is the big list, all mooshed together. We sift for blank strings
-
- $fulllist = array();
+ /**
+ * Get a link to $user_name page
+ * @param $user User object
+ * @return String: html
+ */
+ protected static function link( User $user ) {
+ global $wgUser, $wgAllowRealName;
+ if( $wgAllowRealName )
+ $real = $user->getRealName();
+ else
+ $real = false;
+
+ $skin = $wgUser->getSkin();
+ $page = $user->getUserPage();
+
+ return $skin->link( $page, htmlspecialchars( $real ? $real : $user->getName() ) );
+ }
- foreach (array($real, $user, $anon, $others_link) as $s) {
- if (!empty($s)) {
- array_push($fulllist, $s);
+ /**
+ * Get a link to $user_name page
+ * @param $user_name String: user name
+ * @param $linkText String: optional display
+ * @return String: html
+ */
+ protected static function userLink( User $user ) {
+ global $wgUser, $wgAllowRealName;
+ if( $user->isAnon() ){
+ return wfMsgExt( 'anonymous', array( 'parseinline' ), 1 );
+ } else {
+ $link = self::link( $user );
+ if( $wgAllowRealName && $user->getRealName() )
+ return $link;
+ else
+ return wfMsgExt( 'siteuser', array( 'parseinline', 'replaceafter' ), $link );
}
}
- # Make the list into text...
-
- $creds = $wgLang->listToText($fulllist);
-
- # "Based on work by ..."
-
- return (empty($creds)) ? '' : wfMsg('othercontribs', $creds);
-}
-
-/**
- *
- */
-function creditLink($user_name, $link_text = '') {
- global $wgUser, $wgContLang;
- $skin = $wgUser->getSkin();
- return $skin->makeLink($wgContLang->getNsText(NS_USER) . ':' . $user_name,
- htmlspecialchars( (empty($link_text)) ? $user_name : $link_text ));
-}
-
-/**
- *
- */
-function creditOthersLink($article) {
- global $wgUser;
- $skin = $wgUser->getSkin();
- return $skin->makeKnownLink($article->mTitle->getPrefixedText(), wfMsg('others'), 'action=credits');
-}
+ /**
+ * Get a link to action=credits of $article page
+ * @param $article Article object
+ * @return String: html
+ */
+ protected static function othersLink( Article $article ) {
+ global $wgUser;
+ $skin = $wgUser->getSkin();
+ return $skin->link( $article->getTitle(), wfMsgHtml( 'others' ), array(), array( 'action' => 'credits' ), array( 'known' ) );
+ }
+} \ No newline at end of file
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
index ad6e7f6c..52e9a8c8 100644
--- a/includes/DatabaseFunctions.php
+++ b/includes/DatabaseFunctions.php
@@ -154,6 +154,7 @@ function wfFieldName( $res, $n, $dbi = DB_LAST )
/**
* @todo document function
+ * @see Database::insertId()
*/
function wfInsertId( $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -166,6 +167,7 @@ function wfInsertId( $dbi = DB_LAST ) {
/**
* @todo document function
+ * @see Database::dataSeek()
*/
function wfDataSeek( $res, $row, $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -177,7 +179,8 @@ function wfDataSeek( $res, $row, $dbi = DB_LAST ) {
}
/**
- * @todo document function
+ * Get the last error number
+ * @see Database::lastErrno()
*/
function wfLastErrno( $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -189,7 +192,8 @@ function wfLastErrno( $dbi = DB_LAST ) {
}
/**
- * @todo document function
+ * Get the last error
+ * @see Database::lastError()
*/
function wfLastError( $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -201,7 +205,8 @@ function wfLastError( $dbi = DB_LAST ) {
}
/**
- * @todo document function
+ * Get the number of affected rows
+ * @see Database::affectedRows()
*/
function wfAffectedRows( $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -213,7 +218,8 @@ function wfAffectedRows( $dbi = DB_LAST ) {
}
/**
- * @todo document function
+ * Get the last query ran
+ * @see Database::lastQuery
*/
function wfLastDBquery( $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -245,8 +251,8 @@ function wfSetSQL( $table, $var, $value, $cond, $dbi = DB_MASTER )
/**
+ * Simple select wrapper, return one field
* @see Database::selectField()
- * @todo document function
* @param $table
* @param $var
* @param $cond Default ''
@@ -263,8 +269,8 @@ function wfGetSQL( $table, $var, $cond='', $dbi = DB_LAST )
}
/**
+ * Does a given field exist on the specified table?
* @see Database::fieldExists()
- * @todo document function
* @param $table
* @param $field
* @param $dbi Default DB_LAST
@@ -280,8 +286,8 @@ function wfFieldExists( $table, $field, $dbi = DB_LAST ) {
}
/**
+ * Does the requested index exist on the specified table?
* @see Database::indexExists()
- * @todo document function
* @param $table String
* @param $index
* @param $dbi Default DB_LAST
@@ -354,7 +360,8 @@ function wfUpdateArray( $table, $values, $conds, $fname = 'wfUpdateArray', $dbi
}
/**
- * @todo document function
+ * Get fully usable table name
+ * @see Database::tableName()
*/
function wfTableName( $name, $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -367,6 +374,7 @@ function wfTableName( $name, $dbi = DB_LAST ) {
/**
* @todo document function
+ * @see Database::strencode()
*/
function wfStrencode( $s, $dbi = DB_LAST ) {
$db = wfGetDB( $dbi );
@@ -379,6 +387,7 @@ function wfStrencode( $s, $dbi = DB_LAST ) {
/**
* @todo document function
+ * @see Database::nextSequenceValue()
*/
function wfNextSequenceValue( $seqName, $dbi = DB_MASTER ) {
$db = wfGetDB( $dbi );
@@ -391,6 +400,7 @@ function wfNextSequenceValue( $seqName, $dbi = DB_MASTER ) {
/**
* @todo document function
+ * @see Database::useIndexClause()
*/
function wfUseIndexClause( $index, $dbi = DB_SLAVE ) {
$db = wfGetDB( $dbi );
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index aaf934f5..ed68fe7a 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -27,11 +27,13 @@ if( !defined( 'MEDIAWIKI' ) ) {
* Create a site configuration object
* Not used for much in a default install
*/
-require_once( "$IP/includes/SiteConfiguration.php" );
-$wgConf = new SiteConfiguration;
+if ( !defined( 'MW_PHP4' ) ) {
+ require_once( "$IP/includes/SiteConfiguration.php" );
+ $wgConf = new SiteConfiguration;
+}
/** MediaWiki version number */
-$wgVersion = '1.13.4';
+$wgVersion = '1.14.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
@@ -539,10 +541,10 @@ $wgSMTP = false;
*/
/** database host name or ip address */
$wgDBserver = 'localhost';
-/** database port number */
-$wgDBport = '';
+/** database port number (for PostgreSQL) */
+$wgDBport = 5432;
/** name of the database */
-$wgDBname = 'wikidb';
+$wgDBname = 'my_wiki';
/** */
$wgDBconnection = '';
/** Database username */
@@ -572,6 +574,12 @@ $wgDBts2schema = 'public';
/** To override default SQLite data directory ($docroot/../data) */
$wgSQLiteDataDir = '';
+/** Default directory mode for SQLite data directory on creation.
+ * Note that this is different from the default directory mode used
+ * elsewhere.
+ */
+$wgSQLiteDataDirMode = 0700;
+
/**
* Make all database connections secretly go to localhost. Fool the load balancer
* thinking there is an arbitrarily large cluster of servers to connect to.
@@ -672,14 +680,6 @@ $wgDBClusterTimeout = 10;
*/
$wgDBAvgStatusPoll = 2000;
-/**
- * wgDBminWordLen :
- * MySQL 3.x : used to discard words that MySQL will not return any results for
- * shorter values configure mysql directly.
- * MySQL 4.x : ignore it and configure mySQL
- * See: http://dev.mysql.com/doc/mysql/en/Fulltext_Fine-tuning.html
- */
-$wgDBminWordLen = 4;
/** Set to true if using InnoDB tables */
$wgDBtransactions = false;
/** Set to true for compatibility with extensions that might be checking.
@@ -745,12 +745,6 @@ $wgLocalMessageCache = false;
*/
$wgLocalMessageCacheSerialized = true;
-/**
- * Directory for compiled constant message array databases
- * WARNING: turning anything on will just break things, aaaaaah!!!!
- */
-$wgCachedMessageArrays = false;
-
# Language settings
#
/** Site language code, should be one of ./languages/Language(.*).php */
@@ -844,7 +838,6 @@ $wgTranslateNumerals = true;
/**
* Translation using MediaWiki: namespace.
- * This will increase load times by 25-60% unless memcached is installed.
* Interface messages will be loaded from the database.
*/
$wgUseDatabaseMessages = true;
@@ -860,6 +853,14 @@ $wgMsgCacheExpiry = 86400;
$wgMaxMsgCacheEntrySize = 10000;
/**
+ * If true, serialized versions of the messages arrays will be
+ * read from the 'serialized' subdirectory if they are present.
+ * Set to false to always use the Messages files, regardless of
+ * whether they are up to date or not.
+ */
+$wgEnableSerializedMessages = true;
+
+/**
* Set to false if you are thorough system admin who always remembers to keep
* serialized files up to date to save few mtime calls.
*/
@@ -868,6 +869,9 @@ $wgCheckSerialized = true;
/** Whether to enable language variant conversion. */
$wgDisableLangConversion = false;
+/** Whether to enable language variant conversion for links. */
+$wgDisableTitleConversion = false;
+
/** Default variant code, if false, the default will be the language code */
$wgDefaultLanguageVariant = false;
@@ -947,26 +951,68 @@ $wgMaxPPNodeCount = 1000000; # A complexity limit on template expansion
$wgMaxTemplateDepth = 40;
$wgMaxPPExpandDepth = 40;
+/**
+ * If true, removes (substitutes) templates in "~~~~" signatures.
+ */
+$wgCleanSignatures = true;
+
$wgExtraSubtitle = '';
$wgSiteSupportPage = ''; # A page where you users can receive donations
+/**
+ * Set this to a string to put the wiki into read-only mode. The text will be
+ * used as an explanation to users.
+ *
+ * This prevents most write operations via the web interface. Cache updates may
+ * still be possible. To prevent database writes completely, use the read_only
+ * option in MySQL.
+ */
+$wgReadOnly = null;
+
/***
- * If this lock file exists, the wiki will be forced into read-only mode.
+ * If this lock file exists (size > 0), the wiki will be forced into read-only mode.
* Its contents will be shown to users as part of the read-only warning
* message.
*/
$wgReadOnlyFile = false; ///< defaults to "{$wgUploadDirectory}/lock_yBgMBwiR";
/**
+ * Filename for debug logging.
* The debug log file should be not be publicly accessible if it is used, as it
- * may contain private data. */
+ * may contain private data.
+ */
$wgDebugLogFile = '';
+/**
+ * Prefix for debug log lines
+ */
+$wgDebugLogPrefix = '';
+
+/**
+ * If true, instead of redirecting, show a page with a link to the redirect
+ * destination. This allows for the inspection of PHP error messages, and easy
+ * resubmission of form data. For developer use only.
+ */
$wgDebugRedirects = false;
-$wgDebugRawPage = false; # Avoid overlapping debug entries by leaving out CSS
+/**
+ * If true, log debugging data from action=raw.
+ * This is normally false to avoid overlapping debug entries due to gen=css and
+ * gen=js requests.
+ */
+$wgDebugRawPage = false;
+
+/**
+ * Send debug data to an HTML comment in the output.
+ *
+ * This may occasionally be useful when supporting a non-technical end-user. It's
+ * more secure than exposing the debug log file to the web, since the output only
+ * contains private data for the current user. But it's not ideal for development
+ * use since data is lost on fatal errors and redirects.
+ */
$wgDebugComments = false;
-$wgReadOnly = null;
+
+/** Does nothing. Obsolete? */
$wgLogQueries = false;
/**
@@ -1025,11 +1071,18 @@ $wgUseCategoryBrowser = false;
* same options.
*
* This can provide a significant speedup for medium to large pages,
- * so you probably want to keep it on.
+ * so you probably want to keep it on. Extensions that conflict with the
+ * parser cache should disable the cache on a per-page basis instead.
*/
$wgEnableParserCache = true;
/**
+ * Append a configured value to the parser cache and the sitenotice key so
+ * that they can be kept separate for some class of activity.
+ */
+$wgRenderHashAppend = '';
+
+/**
* If on, the sidebar navigation links are cached for users with the
* current language set. This can save a touch of load on a busy site
* by shaving off extra message lookups.
@@ -1070,7 +1123,7 @@ $wgHitcounterUpdateFreq = 1;
$wgSysopUserBans = true; # Allow sysops to ban logged-in users
$wgSysopRangeBans = true; # Allow sysops to ban IP ranges
$wgAutoblockExpiry = 86400; # Number of seconds before autoblock entries expire
-$wgBlockAllowsUTEdit = false; # Blocks allow users to edit their own user talk page
+$wgBlockAllowsUTEdit = false; # Default setting for option on block form to allow self talkpage editing whilst blocked
$wgSysopEmailBans = true; # Allow sysops to ban users from accessing Emailuser
# Pages anonymous user may see as an array, e.g.:
@@ -1110,40 +1163,42 @@ $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['*' ]['writeapi'] = 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' ]['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"
+$wgGroupPermissions['user']['move'] = true;
+$wgGroupPermissions['user']['move-subpages'] = true;
+$wgGroupPermissions['user']['move-rootuserpages'] = true; // can move root userpages
+//$wgGroupPermissions['user']['movefile'] = true; // Disabled for now due to possible bugs and security concerns
+$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;
// 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' ]['suppressredirect'] = true;
-$wgGroupPermissions['bot' ]['apihighlimits'] = true;
-$wgGroupPermissions['bot' ]['writeapi'] = true;
-#$wgGroupPermissions['bot' ]['editprotected'] = true; // can edit all protected pages without cascade protection enabled
+$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']['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;
@@ -1158,6 +1213,7 @@ $wgGroupPermissions['sysop']['import'] = true;
$wgGroupPermissions['sysop']['importupload'] = true;
$wgGroupPermissions['sysop']['move'] = true;
$wgGroupPermissions['sysop']['move-subpages'] = true;
+$wgGroupPermissions['sysop']['move-rootuserpages'] = true;
$wgGroupPermissions['sysop']['patrol'] = true;
$wgGroupPermissions['sysop']['autopatrol'] = true;
$wgGroupPermissions['sysop']['protect'] = true;
@@ -1173,10 +1229,10 @@ $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']['browsearchive'] = true;
$wgGroupPermissions['sysop']['noratelimit'] = true;
+$wgGroupPermissions['sysop']['movefile'] = true;
#$wgGroupPermissions['sysop']['mergehistory'] = true;
// Permission to change users' group assignments
@@ -1208,8 +1264,22 @@ $wgGroupPermissions['bureaucrat']['noratelimit'] = true;
$wgImplicitGroups = array( '*', 'user', 'autoconfirmed' );
/**
- * These are the groups that users are allowed to add to or remove from
- * their own account via Special:Userrights.
+ * A map of group names that the user is in, to group names that those users
+ * are allowed to add or revoke.
+ *
+ * Setting the list of groups to add or revoke to true is equivalent to "any group".
+ *
+ * For example, to allow sysops to add themselves to the "bot" group:
+ *
+ * $wgGroupsAddToSelf = array( 'sysop' => array( 'bot' ) );
+ *
+ * Implicit groups may be used for the source group, for instance:
+ *
+ * $wgGroupsRemoveFromSelf = array( '*' => true );
+ *
+ * This allows users in the '*' group (i.e. any user) to remove themselves from
+ * any group that they happen to be in.
+ *
*/
$wgGroupsAddToSelf = array();
$wgGroupsRemoveFromSelf = array();
@@ -1237,9 +1307,10 @@ $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
* Set the minimum permissions required to edit pages in each
* namespace. If you list more than one permission, a user must
* have all of them to edit pages in that namespace.
+ *
+ * Note: NS_MEDIAWIKI is implicitly restricted to editinterface.
*/
$wgNamespaceProtection = array();
-$wgNamespaceProtection[ NS_MEDIAWIKI ] = array( 'editinterface' );
/**
* Pages in namespaces in this array can not be used as templates.
@@ -1303,8 +1374,8 @@ $wgAutopromote = array(
* // Sysops can disable other sysops in an emergency, and disable bots
* $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' );
*/
-$wgAddGroups = $wgRemoveGroups = array();
-
+$wgAddGroups = array();
+$wgRemoveGroups = array();
/**
* A list of available rights, in addition to the ones defined by the core.
@@ -1375,7 +1446,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '164';
+$wgStyleVersion = '195';
# Server-side caching:
@@ -1439,6 +1510,9 @@ $wgEnotifMaxRecips = 500;
# Send mails via the job queue.
$wgEnotifUseJobQ = false;
+# Use real name instead of username in e-mail "from" field
+$wgEnotifUseRealName = false;
+
/**
* Array of usernames who will be sent a notification email for every change which occurs on a wiki
*/
@@ -1456,14 +1530,17 @@ $wgRCShowChangedSize = true;
* before and after the edit is below that value, the value will be
* highlighted on the RC page.
*/
-$wgRCChangedSizeThreshold = -500;
+$wgRCChangedSizeThreshold = 500;
/**
* Show "Updated (since my last visit)" marker in RC view, watchlist and history
* view for watched pages with new changes */
$wgShowUpdatedMarker = true;
-$wgCookieExpiration = 2592000;
+/**
+ * Default cookie expiration time. Setting to 0 makes all cookies session-only.
+ */
+$wgCookieExpiration = 30*86400;
/** Clock skew or the one-second resolution of time() can occasionally cause cache
* problems when the user requests two pages within a short period of time. This
@@ -1523,6 +1600,9 @@ $wgHTCPMulticastTTL = 1;
# $wgHTCPMulticastAddress = "224.0.0.85";
$wgHTCPMulticastAddress = false;
+/** Should forwarded Private IPs be accepted? */
+$wgUsePrivateIPs = false;
+
# Cookie settings:
#
/**
@@ -1572,14 +1652,26 @@ $wgAllowExternalImages = false;
/** If the above is false, you can specify an exception here. Image URLs
* that start with this string are then rendered, while all others are not.
* You can use this to set up a trusted, simple repository of images.
+ * You may also specify an array of strings to allow multiple sites
*
- * Example:
+ * Examples:
* $wgAllowExternalImagesFrom = 'http://127.0.0.1/';
+ * $wgAllowExternalImagesFrom = array( 'http://127.0.0.1/', 'http://example.com' );
*/
$wgAllowExternalImagesFrom = '';
-/** Allows to move images and other media files. Experemintal, not sure if it always works */
-$wgAllowImageMoving = false;
+/** If $wgAllowExternalImages is false, you can allow an on-wiki
+ * whitelist of regular expression fragments to match the image URL
+ * against. If the image matches one of the regular expression fragments,
+ * The image will be displayed.
+ *
+ * Set this to true to enable the on-wiki whitelist (MediaWiki:External image whitelist)
+ * Or false to disable it
+ */
+$wgEnableImageWhitelist = true;
+
+/** Allows to move images and other media files */
+$wgAllowImageMoving = true;
/** Disable database-intensive features */
$wgMiserMode = false;
@@ -1598,6 +1690,7 @@ $wgAllowSlowParserFunctions = false;
*/
$wgJobClasses = array(
'refreshLinks' => 'RefreshLinksJob',
+ 'refreshLinks2' => 'RefreshLinksJob2',
'htmlCacheUpdate' => 'HTMLCacheUpdateJob',
'html_cache_update' => 'HTMLCacheUpdateJob', // backwards-compatible
'sendMail' => 'EmaillingJob',
@@ -1606,6 +1699,14 @@ $wgJobClasses = array(
);
/**
+ * Additional functions to be performed with updateSpecialPages.
+ * Expensive Querypages are already updated.
+ */
+$wgSpecialPageCacheUpdates = array(
+ 'Statistics' => array('SiteStatsUpdate','cacheUpdate')
+);
+
+/**
* To use inline TeX, you need to compile 'texvc' (in the 'math' subdirectory of
* the MediaWiki package and have latex, dvips, gs (ghostscript), andconvert
* (ImageMagick) installed and available in the PATH.
@@ -1797,7 +1898,10 @@ $wgMimeTypeBlacklist= array(
# Client-side hazards on Internet Explorer
'text/scriptlet', 'application/x-msdownload',
# Windows metafile, client-side vulnerability on some systems
- 'application/x-msmetafile'
+ 'application/x-msmetafile',
+ # A ZIP file may be a valid Java archive containing an applet which exploits the
+ # same-origin policy to steal cookies
+ 'application/zip',
);
/** This is a flag to determine whether or not to check file extensions on upload. */
@@ -1823,7 +1927,7 @@ $wgNamespacesWithSubpages = array(
NS_USER => true,
NS_USER_TALK => true,
NS_PROJECT_TALK => true,
- NS_IMAGE_TALK => true,
+ NS_FILE_TALK => true,
NS_MEDIAWIKI_TALK => true,
NS_TEMPLATE_TALK => true,
NS_HELP_TALK => true,
@@ -1835,6 +1939,21 @@ $wgNamespacesToBeSearchedDefault = array(
);
/**
+ * Additional namespaces to those in $wgNamespacesToBeSearchedDefault that
+ * will be added to default search for "project" page inclusive searches
+ *
+ * Same format as $wgNamespacesToBeSearchedDefault
+ */
+$wgNamespacesToBeSearchedProject = array(
+ NS_USER => true,
+ NS_PROJECT => true,
+ NS_HELP => true,
+ NS_CATEGORY => true,
+);
+
+$wgUseOldSearchUI = true; // temp testing variable
+
+/**
* Site notice shown at the top of each page
*
* This message can contain wiki text, and can also be set through the
@@ -1883,6 +2002,12 @@ $wgSharpenParameter = '0x0.4';
/** Reduction in linear dimensions below which sharpening will be enabled */
$wgSharpenReductionThreshold = 0.85;
+/**
+ * Temporary directory used for ImageMagick. The directory must exist. Leave
+ * this set to false to let ImageMagick decide for itself.
+ */
+$wgImageMagickTempDir = false;
+
/**
* Use another resizing converter, e.g. GraphicMagick
* %s will be replaced with the source path, %d with the destination
@@ -1900,7 +2025,7 @@ $wgCustomConvertCommand = false;
#
# An external program is required to perform this conversion:
$wgSVGConverters = array(
- 'ImageMagick' => '$path/convert -background white -geometry $width $input PNG:$output',
+ 'ImageMagick' => '$path/convert -background white -thumbnail $widthx$height\! $input PNG:$output',
'sodipodi' => '$path/sodipodi -z -w $width -f $input -e $output',
'inkscape' => '$path/inkscape -z -w $width -f $input -e $output',
'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input',
@@ -1920,6 +2045,13 @@ $wgSVGMaxSize = 2048;
*/
$wgMaxImageArea = 1.25e7;
/**
+ * Force thumbnailing of animated GIFs above this size to a single
+ * frame instead of an animated thumbnail. ImageMagick seems to
+ * get real unhappy and doesn't play well with resource limits. :P
+ * Defaulting to 1 megapixel (1000x1000)
+ */
+$wgMaxAnimatedGifArea = 1.0e6;
+/**
* If rendered thumbnail files are older than this timestamp, they
* will be rerendered on demand as if the file didn't already exist.
* Update if there is some need to force thumbs and SVG rasterizations
@@ -1988,17 +2120,54 @@ $wgRCFilterByAge = false;
$wgRCLinkLimits = array( 50, 100, 250, 500 );
$wgRCLinkDays = array( 1, 3, 7, 14, 30 );
-# Send RC updates via UDP
+/**
+ * Send recent changes updates via UDP. The updates will be formatted for IRC.
+ * Set this to the IP address of the receiver.
+ */
$wgRC2UDPAddress = false;
+
+/**
+ * Port number for RC updates
+ */
$wgRC2UDPPort = false;
+
+/**
+ * Prefix to prepend to each UDP packet.
+ * This can be used to identify the wiki. A script is available called
+ * mxircecho.py which listens on a UDP port, and uses a prefix ending in a
+ * tab to identify the IRC channel to send the log line to.
+ */
$wgRC2UDPPrefix = '';
+
+/**
+ * If this is set to true, $wgLocalInterwiki will be prepended to links in the
+ * IRC feed. If this is set to a string, that string will be used as the prefix.
+ */
+$wgRC2UDPInterwikiPrefix = false;
+
+/**
+ * Set to true to omit "bot" edits (by users with the bot permission) from the
+ * UDP feed.
+ */
$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.
+/**
+ * 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;
+/**
+ * Whether to use metadata edition
+ * This will put categories, language links and allowed templates in a separate text box
+ * while editing pages
+ * EXPERIMENTAL
+ */
+$wgUseMetadataEdit = false;
+/** Full name (including namespace) of the page containing templates names that will be allowed as metadata */
+$wgMetadataWhitelist = '';
+
#
# Copyright and credits settings
#
@@ -2084,9 +2253,17 @@ $wgExportMaxHistory = 0;
$wgExportAllowListContributors = false ;
-/** Text matching this regular expression will be recognised as spam
- * See http://en.wikipedia.org/wiki/Regular_expression */
-$wgSpamRegex = false;
+/**
+ * Edits matching these regular expressions in body text or edit summary
+ * will be recognised as spam and rejected automatically.
+ *
+ * There's no administrator override on-wiki, so be careful what you set. :)
+ * May be an array of regexes or a single string for backwards compatibility.
+ *
+ * See http://en.wikipedia.org/wiki/Regular_expression
+ */
+$wgSpamRegex = array();
+
/** Similarly you can get a function to do the job. The function will be given
* the following args:
* - a Title object for the article the edit is made on
@@ -2145,6 +2322,35 @@ $wgValidateAllHtml = false;
/** See list of skins and their symbolic names in languages/Language.php */
$wgDefaultSkin = 'monobook';
+/** Should we allow the user's to select their own skin that will override the default? */
+$wgAllowUserSkin = true;
+
+/**
+ * Optionally, we can specify a stylesheet to use for media="handheld".
+ * This is recognized by some, but not all, handheld/mobile/PDA browsers.
+ * If left empty, compliant handheld browsers won't pick up the skin
+ * stylesheet, which is specified for 'screen' media.
+ *
+ * Can be a complete URL, base-relative path, or $wgStylePath-relative path.
+ * Try 'chick/main.css' to apply the Chick styles to the MonoBook HTML.
+ *
+ * Will also be switched in when 'handheld=yes' is added to the URL, like
+ * the 'printable=yes' mode for print media.
+ */
+$wgHandheldStyle = false;
+
+/**
+ * If set, 'screen' and 'handheld' media specifiers for stylesheets are
+ * transformed such that they apply to the iPhone/iPod Touch Mobile Safari,
+ * which doesn't recognize 'handheld' but does support media queries on its
+ * screen size.
+ *
+ * Consider only using this if you have a *really good* handheld stylesheet,
+ * as iPhone users won't have any way to disable it and use the "grown-up"
+ * styles instead.
+ */
+$wgHandheldForIPhone = false;
+
/**
* Settings added to this array will override the default globals for the user
* preferences used by anonymous visitors and newly created accounts.
@@ -2161,7 +2367,6 @@ $wgDefaultUserOptions = array(
'contextlines' => 5,
'contextchars' => 50,
'disablesuggest' => 0,
- 'ajaxsearch' => 0,
'skin' => false,
'math' => 1,
'usenewrc' => 0,
@@ -2184,6 +2389,10 @@ $wgDefaultUserOptions = array(
'imagesize' => 2,
'thumbsize' => 2,
'rememberpassword' => 0,
+ 'nocache' => 0,
+ 'diffonly' => 0,
+ 'showhiddencats' => 0,
+ 'norollbackdiff' => 0,
'enotifwatchlistpages' => 0,
'enotifusertalkpages' => 1,
'enotifminoredits' => 0,
@@ -2192,7 +2401,9 @@ $wgDefaultUserOptions = array(
'fancysig' => 0,
'externaleditor' => 0,
'externaldiff' => 0,
+ 'forceeditsummary' => 0,
'showjumplinks' => 1,
+ 'justify' => 0,
'numberheadings' => 0,
'uselivepreview' => 0,
'watchlistdays' => 3.0,
@@ -2200,10 +2411,13 @@ $wgDefaultUserOptions = array(
'watchlisthideminor' => 0,
'watchlisthidebots' => 0,
'watchlisthideown' => 0,
+ 'watchlisthideanons' => 0,
+ 'watchlisthideliu' => 0,
'watchcreations' => 0,
'watchdefault' => 0,
'watchmoves' => 0,
'watchdeletion' => 0,
+ 'noconvertlink' => 0,
);
/** Whether or not to allow and use real name fields. Defaults to true. */
@@ -2290,7 +2504,7 @@ $wgAutoloadClasses = array();
* $wgExtensionCredits[$type][] = array(
* 'name' => 'Example extension',
* 'version' => 1.9,
- * 'svn-revision' => '$LastChangedRevision: 46957 $',
+ * 'svn-revision' => '$LastChangedRevision: 47653 $',
* 'author' => 'Foo Barstein',
* 'url' => 'http://wwww.example.com/Example%20Extension/',
* 'description' => 'An example extension',
@@ -2337,6 +2551,9 @@ $wgMaxTocLevel = 999;
/** Name of the external diff engine to use */
$wgExternalDiffEngine = false;
+/** Whether to use inline diff */
+$wgEnableHtmlDiff = false;
+
/** Use RC Patrolling to check for vandalism */
$wgUseRCPatrol = true;
@@ -2363,6 +2580,13 @@ $wgFeedCacheTimeout = 60;
* pages larger than this size. */
$wgFeedDiffCutoff = 32768;
+/** Override the site's default RSS/ATOM feed for recentchanges that appears on
+ * every page. Some sites might have a different feed they'd like to promote
+ * instead of the RC feed (maybe like a "Recent New Articles" or "Breaking news" one).
+ * Ex: $wgSiteFeed['format'] = "http://example.com/somefeed.xml"; Format can be one
+ * of either 'rss' or 'atom'.
+ */
+$wgOverrideSiteFeed = array();
/**
* Additional namespaces. If the namespaces defined in Language.php and
@@ -2448,6 +2672,12 @@ $wgCategoryMagicGallery = true;
$wgCategoryPagingLimit = 200;
/**
+ * Should the default category sortkey be the prefixed title?
+ * Run maintenance/refreshLinks.php after changing this.
+ */
+$wgCategoryPrefixedDefaultSortkey = true;
+
+/**
* Browser Blacklist for unicode non compliant browsers
* Contains a list of regexps : "/regexp/" matching problematic browsers
*/
@@ -2587,6 +2817,30 @@ $wgLogRestrictions = array(
);
/**
+ * Show/hide links on Special:Log will be shown for these log types.
+ *
+ * This is associative array of log type => boolean "hide by default"
+ *
+ * See $wgLogTypes for a list of available log types.
+ *
+ * For example:
+ * $wgFilterLogTypes => array(
+ * 'move' => true,
+ * 'import' => false,
+ * );
+ *
+ * Will display show/hide links for the move and import logs. Move logs will be
+ * hidden by default unless the link is clicked. Import logs will be shown by
+ * default, and hidden when the link is clicked.
+ *
+ * A message of the form log-show-hide-<type> should be added, and will be used
+ * for the link text.
+ */
+$wgFilterLogTypes = array(
+ 'patrol' => true
+);
+
+/**
* Lists the message key string for each log type. The localized messages
* will be listed in the user interface.
*
@@ -2635,9 +2889,11 @@ $wgLogHeaders = array(
$wgLogActions = array(
'block/block' => 'blocklogentry',
'block/unblock' => 'unblocklogentry',
+ 'block/reblock' => 'reblock-logentry',
'protect/protect' => 'protectedarticle',
'protect/modify' => 'modifiedarticleprotection',
'protect/unprotect' => 'unprotectedarticle',
+ 'protect/move_prot' => 'movedarticleprotection',
'rights/rights' => 'rightslogentry',
'delete/delete' => 'deletedarticle',
'delete/restore' => 'undeletedarticle',
@@ -2656,6 +2912,7 @@ $wgLogActions = array(
'suppress/event' => 'logdelete-logentry',
'suppress/delete' => 'suppressedarticle',
'suppress/block' => 'blocklogentry',
+ 'suppress/reblock' => 'reblock-logentry',
);
/**
@@ -2665,6 +2922,11 @@ $wgLogActions = array(
$wgLogActionsHandlers = array();
/**
+ * Maintain a log of newusers at Log/newusers?
+ */
+$wgNewUserLog = true;
+
+/**
* List of special pages, followed by what subtitle they should go under
* at Special:SpecialPages
*/
@@ -2688,6 +2950,8 @@ $wgSpecialPageGroups = array(
'Deadendpages' => 'maintenance',
'Wantedpages' => 'maintenance',
'Wantedcategories' => 'maintenance',
+ 'Wantedfiles' => 'maintenance',
+ 'Wantedtemplates' => 'maintenance',
'Unwatchedpages' => 'maintenance',
'Fewestrevisions' => 'maintenance',
@@ -2703,7 +2967,7 @@ $wgSpecialPageGroups = array(
'Log' => 'changes',
'Upload' => 'media',
- 'Imagelist' => 'media',
+ 'Listfiles' => 'media',
'MIMEsearch' => 'media',
'FileDuplicateSearch' => 'media',
'Filepath' => 'media',
@@ -2719,6 +2983,7 @@ $wgSpecialPageGroups = array(
'Blockip' => 'users',
'Preferences' => 'users',
'Resetpass' => 'users',
+ 'DeletedContributions' => 'users',
'Mostlinked' => 'highuse',
'Mostlinkedcategories' => 'highuse',
@@ -2739,6 +3004,7 @@ $wgSpecialPageGroups = array(
'Mytalk' => 'redirects',
'Mycontributions' => 'redirects',
'Search' => 'redirects',
+ 'LinkSearch' => 'redirects',
'Movepage' => 'pagetools',
'MergeHistory' => 'pagetools',
@@ -2788,6 +3054,11 @@ $wgDisableInternalSearch = false;
$wgSearchForwardUrl = null;
/**
+ * Set a default target for external links, e.g. _blank to pop up a new window
+ */
+$wgExternalLinkTarget = false;
+
+/**
* If true, external URL links in wiki text will be given the
* rel="nofollow" attribute as a hint to search engines that
* they should not be followed for ranking purposes as they
@@ -2802,34 +3073,57 @@ $wgNoFollowLinks = true;
$wgNoFollowNsExceptions = array();
/**
- * Default robot policy.
- * The default policy is to encourage indexing and following of links.
- * It may be overridden on a per-namespace and/or per-page basis.
+ * Default robot policy. The default policy is to encourage indexing and fol-
+ * lowing of links. It may be overridden on a per-namespace and/or per-page
+ * basis.
*/
$wgDefaultRobotPolicy = 'index,follow';
/**
- * Robot policies per namespaces.
- * The default policy is given above, the array is made of namespace
- * constants as defined in includes/Defines.php
+ * Robot policies per namespaces. The default policy is given above, the array
+ * is made of namespace constants as defined in includes/Defines.php. You can-
+ * not specify a different default policy for NS_SPECIAL: it is always noindex,
+ * nofollow. This is because a number of special pages (e.g., ListPages) have
+ * many permutations of options that display the same data under redundant
+ * URLs, so search engine spiders risk getting lost in a maze of twisty special
+ * pages, all alike, and never reaching your actual content.
+ *
* Example:
* $wgNamespaceRobotPolicies = array( NS_TALK => 'noindex' );
*/
$wgNamespaceRobotPolicies = array();
/**
- * Robot policies per article.
- * These override the per-namespace robot policies.
- * Must be in the form of an array where the key part is a properly
- * canonicalised text form title and the value is a robot policy.
+ * 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 canonical-
+ * ised text form title and the value is a robot policy.
* Example:
- * $wgArticleRobotPolicies = array( 'Main Page' => 'noindex' );
+ * $wgArticleRobotPolicies = array( 'Main Page' => 'noindex,follow',
+ * 'User:Bob' => 'index,follow' );
+ * Example that DOES NOT WORK because the names are not canonical text forms:
+ * $wgArticleRobotPolicies = array(
+ * # Underscore, not space!
+ * 'Main_Page' => 'noindex,follow',
+ * # "Project", not the actual project name!
+ * 'Project:X' => 'index,follow',
+ * # Needs to be "Abc", not "abc" (unless $wgCapitalLinks is false)!
+ * 'abc' => 'noindex,nofollow'
+ * );
*/
$wgArticleRobotPolicies = array();
/**
- * Specifies the minimal length of a user password. If set to
- * 0, empty passwords are allowed.
+ * An array of namespace keys in which the __INDEX__/__NOINDEX__ magic words
+ * will not function, so users can't decide whether pages in that namespace are
+ * indexed by search engines. If set to null, default to $wgContentNamespaces.
+ * Example:
+ * $wgExemptFromUserRobotsControl = array( NS_MAIN, NS_TALK, NS_PROJECT );
+ */
+$wgExemptFromUserRobotsControl = null;
+
+/**
+ * Specifies the minimal length of a user password. If set to 0, empty pass-
+ * words are allowed.
*/
$wgMinimalPasswordLength = 0;
@@ -2844,9 +3138,8 @@ $wgUseExternalEditor = true;
$wgSortSpecialPages = true;
/**
- * Specify the name of a skin that should not be presented in the
- * list of available skins.
- * Use for blacklisting a skin which you do not want to remove
+ * Specify the name of a skin that should not be presented in the list of a-
+ * vailable skins. Use for blacklisting a skin which you do not want to remove
* from the .../skins/ directory
*/
$wgSkipSkin = '';
@@ -2858,7 +3151,8 @@ $wgSkipSkins = array(); # More of the same
$wgDisabledActions = array();
/**
- * Disable redirects to special pages and interwiki redirects, which use a 302 and have no "redirected from" link
+ * Disable redirects to special pages and interwiki redirects, which use a 302
+ * and have no "redirected from" link.
*/
$wgDisableHardRedirects = false;
@@ -2869,21 +3163,19 @@ $wgEnableSorbs = false;
$wgSorbsUrl = 'http.dnsbl.sorbs.net.';
/**
- * Proxy whitelist, list of addresses that are assumed to be non-proxy despite what the other
- * methods might say
+ * Proxy whitelist, list of addresses that are assumed to be non-proxy despite
+ * what the other methods might say.
*/
$wgProxyWhitelist = array();
/**
- * Simple rate limiter options to brake edit floods.
- * Maximum number actions allowed in the given number of seconds;
- * after that the violating client receives HTTP 500 error pages
- * until the period elapses.
+ * Simple rate limiter options to brake edit floods. Maximum number actions
+ * allowed in the given number of seconds; after that the violating client re-
+ * ceives HTTP 500 error pages until the period elapses.
*
* array( 4, 60 ) for a maximum of 4 hits in 60 seconds.
*
- * This option set is experimental and likely to change.
- * Requires memcached.
+ * This option set is experimental and likely to change. Requires memcached.
*/
$wgRateLimits = array(
'edit' => array(
@@ -3045,17 +3337,10 @@ $wgUpdateRowsPerQuery = 10;
$wgUseAjax = true;
/**
- * 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.
* Extensions acting as Ajax callbacks must register here
*/
-$wgAjaxExportList = array( );
+$wgAjaxExportList = array( 'wfAjaxGetThumbnailUrl', 'wfAjaxGetFileUrl' );
/**
* Enable watching/unwatching pages using AJAX.
@@ -3080,6 +3365,11 @@ $wgAjaxLicensePreview = true;
$wgAllowDisplayTitle = true;
/**
+ * for consistency, restrict DISPLAYTITLE to titles that normalize to the same canonical DB key
+ */
+$wgRestrictDisplayTitle = true;
+
+/**
* Array of usernames which may not be registered or logged in from
* Maintenance scripts can still use these
*/
@@ -3120,6 +3410,16 @@ $wgMaxShellMemory = 102400;
$wgMaxShellFileSize = 102400;
/**
+ * Maximum CPU time in seconds for shell processes under linux
+ */
+$wgMaxShellTime = 180;
+
+/**
+* Executable name of PHP cli client (php/php5)
+*/
+$wgPhpCli = 'php';
+
+/**
* DJVU settings
* Path of the djvudump executable
* Enable this and $wgDjvuRenderer to enable djvu rendering
@@ -3171,7 +3471,7 @@ $wgEnableAPI = true;
* (page edits, rollback, etc.) when an authorised user
* accesses it
*/
-$wgEnableWriteAPI = false;
+$wgEnableWriteAPI = true;
/**
* API module extensions
@@ -3241,8 +3541,6 @@ $wgSlaveLagCritical = 30;
* 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
@@ -3256,6 +3554,12 @@ $wgParserConf = array(
);
/**
+ * LinkHolderArray batch size
+ * For debugging
+ */
+$wgLinkHolderBatchSize = 1000;
+
+/**
* Hooks that are used for outputting exceptions. Format is:
* $wgExceptionHooks[] = $funcname
* or:
@@ -3290,6 +3594,12 @@ $wgExpensiveParserFunctionLimit = 100;
$wgMaximumMovedPages = 100;
/**
+ * Fix double redirects after a page move.
+ * Tends to conflict with page move vandalism, use only on a private wiki.
+ */
+$wgFixDoubleRedirects = false;
+
+/**
* 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.
@@ -3303,3 +3613,28 @@ $wgSitemapNamespaces = false;
* ting this variable false.
*/
$wgUseAutomaticEditSummaries = true;
+
+/**
+ * Limit password attempts to X attempts per Y seconds per IP per account.
+ * Requires memcached.
+ */
+$wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
+
+/**
+ * Display user edit counts in various prominent places.
+ */
+$wgEdititis = false;
+
+/**
+* Enable the UniversalEditButton for browsers that support it
+* (currently only Firefox with an extension)
+* See http://universaleditbutton.org for more background information
+*/
+$wgUniversalEditButton = true;
+
+/**
+ * Allow id's that don't conform to HTML4 backward compatibility requirements.
+ * This is currently for testing; if all goes well, this option will be removed
+ * and the functionality will be enabled universally.
+ */
+$wgEnforceHtmlIds = true;
diff --git a/includes/Defines.php b/includes/Defines.php
index 98cee57d..8de6c5a1 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -52,8 +52,8 @@ define('NS_USER', 2);
define('NS_USER_TALK', 3);
define('NS_PROJECT', 4);
define('NS_PROJECT_TALK', 5);
-define('NS_IMAGE', 6);
-define('NS_IMAGE_TALK', 7);
+define('NS_FILE', 6);
+define('NS_FILE_TALK', 7);
define('NS_MEDIAWIKI', 8);
define('NS_MEDIAWIKI_TALK', 9);
define('NS_TEMPLATE', 10);
@@ -62,6 +62,16 @@ define('NS_HELP', 12);
define('NS_HELP_TALK', 13);
define('NS_CATEGORY', 14);
define('NS_CATEGORY_TALK', 15);
+/**
+ * NS_IMAGE and NS_IMAGE_TALK are the pre-v1.14 names for NS_FILE and
+ * NS_FILE_TALK respectively, and are kept for compatibility.
+ *
+ * When writing code that should be compatible with older MediaWiki
+ * versions, either stick to the old names or define the new constants
+ * yourself, if they're not defined already.
+ */
+define('NS_IMAGE', NS_FILE);
+define('NS_IMAGE_TALK', NS_FILE_TALK);
/**#@-*/
/**
@@ -202,6 +212,9 @@ define( 'OT_MSG' , 3 ); // b/c alias for OT_PREPROCESS
define( 'SFH_NO_HASH', 1 );
define( 'SFH_OBJECT_ARGS', 2 );
+# Flags for Parser::setLinkHook
+define( 'SLH_PATTERN', 1 );
+
# Flags for Parser::replaceLinkHolders
define( 'RLH_FOR_UPDATE', 1 );
@@ -211,3 +224,6 @@ define( 'APCOND_EDITCOUNT', 1 );
define( 'APCOND_AGE', 2 );
define( 'APCOND_EMAILCONFIRMED', 3 );
define( 'APCOND_INGROUPS', 4 );
+define( 'APCOND_ISIP', 5 );
+define( 'APCOND_IPINRANGE', 6 );
+define( 'APCOND_AGE_FROM_EDIT', 7 );
diff --git a/includes/EditPage.php b/includes/EditPage.php
index a34964bc..0193dc38 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -44,6 +44,7 @@ class EditPage {
var $mArticle;
var $mTitle;
+ var $action;
var $mMetaData = '';
var $isConflict = false;
var $isCssJsSubpage = false;
@@ -61,7 +62,8 @@ class EditPage {
var $allowBlankSummary = false;
var $autoSumm = '';
var $hookError = '';
- var $mPreviewTemplates;
+ #var $mPreviewTemplates;
+ var $mParserOutput;
var $mBaseRevision = false;
# Form values
@@ -92,6 +94,7 @@ class EditPage {
function EditPage( $article ) {
$this->mArticle =& $article;
$this->mTitle = $article->getTitle();
+ $this->action = 'submit';
# Placeholders for text injection by hooks (empty per default)
$this->editFormPageTop =
@@ -101,49 +104,47 @@ class EditPage {
$this->editFormTextAfterTools =
$this->editFormTextBottom = "";
}
+
+ function getArticle() {
+ return $this->mArticle;
+ }
/**
* Fetch initial editing page content.
* @private
*/
function getContent( $def_text = '' ) {
- global $wgOut, $wgRequest, $wgParser, $wgMessageCache;
+ global $wgOut, $wgRequest, $wgParser, $wgContLang, $wgMessageCache;
+ wfProfileIn( __METHOD__ );
# Get variables from query string :P
$section = $wgRequest->getVal( 'section' );
$preload = $wgRequest->getVal( 'preload' );
$undoafter = $wgRequest->getVal( 'undoafter' );
$undo = $wgRequest->getVal( 'undo' );
- wfProfileIn( __METHOD__ );
-
$text = '';
- if( !$this->mTitle->exists() ) {
+ // For message page not locally set, use the i18n message.
+ // For other non-existent articles, use preload text if any.
+ if ( !$this->mTitle->exists() ) {
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- $wgMessageCache->loadAllMessages();
# If this is a system message, get the default text.
- $text = wfMsgWeirdKey ( $this->mTitle->getText() ) ;
+ list( $message, $lang ) = $wgMessageCache->figureMessage( $wgContLang->lcfirst( $this->mTitle->getText() ) );
+ $wgMessageCache->loadAllMessages( $lang );
+ $text = wfMsgGetKey( $message, false, $lang, false );
+ if( wfEmptyMsg( $message, $text ) )
+ $text = '';
} else {
# If requested, preload some text.
$text = $this->getPreloadedText( $preload );
}
- # We used to put MediaWiki:Newarticletext here if
- # $text was empty at this point.
- # This is now shown above the edit box instead.
+ // For existing pages, get text based on "undo" or section parameters.
} else {
- // FIXME: may be better to use Revision class directly
- // But don't mess with it just yet. Article knows how to
- // fetch the page record from the high-priority server,
- // which is needed to guarantee we don't pick up lagged
- // information.
-
$text = $this->mArticle->getContent();
-
- if ($undo > 0 && $undoafter > 0 && $undo < $undoafter) {
+ if ( $undo > 0 && $undoafter > 0 && $undo < $undoafter ) {
# If they got undoafter and undo round the wrong way, switch them
list( $undo, $undoafter ) = array( $undoafter, $undo );
}
-
if ( $undo > 0 && $undo > $undoafter ) {
# Undoing a specific edit overrides section editing; section-editing
# doesn't work with undoing.
@@ -158,7 +159,7 @@ class EditPage {
# 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 ) &&
+ if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
$undorev->getPage() == $oldrev->getPage() &&
$undorev->getPage() == $this->mArticle->getID() &&
!$undorev->isDeleted( Revision::DELETED_TEXT ) &&
@@ -174,12 +175,12 @@ class EditPage {
$text = $oldrev_text;
$result = true;
}
- if( $result ) {
+ 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 ) {
+ if ( $firstrev->mId == $undo ) {
$this->summary = wfMsgForContent('undo-summary', $undo, $undorev->getUserText());
}
$this->formtype = 'diff';
@@ -193,8 +194,8 @@ class EditPage {
// was created, or we may simply have got bogus input.
$this->editFormPageTop .= $wgOut->parse( wfMsgNoTrans( 'undo-norev' ) );
}
- } else if( $section != '' ) {
- if( $section == 'new' ) {
+ } else if ( $section != '' ) {
+ if ( $section == 'new' ) {
$text = $this->getPreloadedText( $preload );
} else {
$text = $wgParser->getSection( $text, $section, $def_text );
@@ -212,13 +213,13 @@ class EditPage {
* @param $preload String: the title of the page.
* @return string The contents of the page.
*/
- protected function getPreloadedText($preload) {
- if ( $preload === '' )
+ protected function getPreloadedText( $preload ) {
+ if ( $preload === '' ) {
return '';
- else {
+ } else {
$preloadTitle = Title::newFromText( $preload );
if ( isset( $preloadTitle ) && $preloadTitle->userCanRead() ) {
- $rev=Revision::newFromTitle($preloadTitle);
+ $rev = Revision::newFromTitle($preloadTitle);
if ( is_object( $rev ) ) {
$text = $rev->getText();
// TODO FIXME: AAAAAAAAAAA, this shouldn't be implementing
@@ -237,107 +238,103 @@ class EditPage {
* and set $wgMetadataWhitelist to the *full* title of the template whitelist
*/
function extractMetaDataFromArticle () {
- global $wgUseMetadataEdit , $wgMetadataWhitelist , $wgLang ;
- $this->mMetaData = '' ;
- if ( !$wgUseMetadataEdit ) return ;
- if ( $wgMetadataWhitelist == '' ) return ;
- $s = '' ;
+ global $wgUseMetadataEdit, $wgMetadataWhitelist, $wgContLang;
+ $this->mMetaData = '';
+ if ( !$wgUseMetadataEdit ) return;
+ if ( $wgMetadataWhitelist == '' ) return;
+ $s = '';
$t = $this->getContent();
# MISSING : <nowiki> filtering
# Categories and language links
- $t = explode ( "\n" , $t ) ;
- $catlow = strtolower ( $wgLang->getNsText ( NS_CATEGORY ) ) ;
- $cat = $ll = array() ;
- foreach ( $t AS $key => $x )
- {
- $y = trim ( strtolower ( $x ) ) ;
- while ( substr ( $y , 0 , 2 ) == '[[' )
- {
- $y = explode ( ']]' , trim ( $x ) ) ;
- $first = array_shift ( $y ) ;
- $first = explode ( ':' , $first ) ;
- $ns = array_shift ( $first ) ;
- $ns = trim ( str_replace ( '[' , '' , $ns ) ) ;
- if ( strlen ( $ns ) == 2 OR strtolower ( $ns ) == $catlow )
- {
- $add = '[[' . $ns . ':' . implode ( ':' , $first ) . ']]' ;
- if ( strtolower ( $ns ) == $catlow ) $cat[] = $add ;
- else $ll[] = $add ;
- $x = implode ( ']]' , $y ) ;
- $t[$key] = $x ;
- $y = trim ( strtolower ( $x ) ) ;
+ $t = explode ( "\n" , $t );
+ $catlow = strtolower ( $wgContLang->getNsText( NS_CATEGORY ) );
+ $cat = $ll = array();
+ foreach ( $t AS $key => $x ) {
+ $y = trim ( strtolower ( $x ) );
+ while ( substr ( $y , 0 , 2 ) == '[[' ) {
+ $y = explode ( ']]' , trim ( $x ) );
+ $first = array_shift ( $y );
+ $first = explode ( ':' , $first );
+ $ns = array_shift ( $first );
+ $ns = trim ( str_replace ( '[' , '' , $ns ) );
+ if ( $wgContLang->getLanguageName( $ns ) || strtolower ( $ns ) == $catlow ) {
+ $add = '[[' . $ns . ':' . implode ( ':' , $first ) . ']]';
+ if ( strtolower ( $ns ) == $catlow ) $cat[] = $add;
+ else $ll[] = $add;
+ $x = implode ( ']]' , $y );
+ $t[$key] = $x;
+ $y = trim ( strtolower ( $x ) );
+ } else {
+ $x = implode ( ']]' , $y );
+ $y = trim ( strtolower ( $x ) );
}
}
}
- if ( count ( $cat ) ) $s .= implode ( ' ' , $cat ) . "\n" ;
- if ( count ( $ll ) ) $s .= implode ( ' ' , $ll ) . "\n" ;
- $t = implode ( "\n" , $t ) ;
+ if ( count ( $cat ) ) $s .= implode ( ' ' , $cat ) . "\n";
+ if ( count ( $ll ) ) $s .= implode ( ' ' , $ll ) . "\n";
+ $t = implode ( "\n" , $t );
# Load whitelist
$sat = array () ; # stand-alone-templates; must be lowercase
- $wl_title = Title::newFromText ( $wgMetadataWhitelist ) ;
- $wl_article = new Article ( $wl_title ) ;
- $wl = explode ( "\n" , $wl_article->getContent() ) ;
- foreach ( $wl AS $x )
- {
- $isentry = false ;
- $x = trim ( $x ) ;
- while ( substr ( $x , 0 , 1 ) == '*' )
- {
- $isentry = true ;
- $x = trim ( substr ( $x , 1 ) ) ;
+ $wl_title = Title::newFromText ( $wgMetadataWhitelist );
+ $wl_article = new Article ( $wl_title );
+ $wl = explode ( "\n" , $wl_article->getContent() );
+ foreach ( $wl AS $x ) {
+ $isentry = false;
+ $x = trim ( $x );
+ while ( substr ( $x , 0 , 1 ) == '*' ) {
+ $isentry = true;
+ $x = trim ( substr ( $x , 1 ) );
}
- if ( $isentry )
- {
- $sat[] = strtolower ( $x ) ;
+ if ( $isentry ) {
+ $sat[] = strtolower ( $x );
}
}
# Templates, but only some
- $t = explode ( '{{' , $t ) ;
+ $t = explode ( '{{' , $t );
$tl = array () ;
- foreach ( $t AS $key => $x )
- {
- $y = explode ( '}}' , $x , 2 ) ;
- if ( count ( $y ) == 2 )
- {
- $z = $y[0] ;
- $z = explode ( '|' , $z ) ;
- $tn = array_shift ( $z ) ;
- if ( in_array ( strtolower ( $tn ) , $sat ) )
- {
- $tl[] = '{{' . $y[0] . '}}' ;
- $t[$key] = $y[1] ;
- $y = explode ( '}}' , $y[1] , 2 ) ;
+ foreach ( $t AS $key => $x ) {
+ $y = explode ( '}}' , $x , 2 );
+ if ( count ( $y ) == 2 ) {
+ $z = $y[0];
+ $z = explode ( '|' , $z );
+ $tn = array_shift ( $z );
+ if ( in_array ( strtolower ( $tn ) , $sat ) ) {
+ $tl[] = '{{' . $y[0] . '}}';
+ $t[$key] = $y[1];
+ $y = explode ( '}}' , $y[1] , 2 );
}
- else $t[$key] = '{{' . $x ;
+ else $t[$key] = '{{' . $x;
}
- else if ( $key != 0 ) $t[$key] = '{{' . $x ;
- else $t[$key] = $x ;
+ else if ( $key != 0 ) $t[$key] = '{{' . $x;
+ else $t[$key] = $x;
}
- if ( count ( $tl ) ) $s .= implode ( ' ' , $tl ) ;
- $t = implode ( '' , $t ) ;
+ if ( count ( $tl ) ) $s .= implode ( ' ' , $tl );
+ $t = implode ( '' , $t );
- $t = str_replace ( "\n\n\n" , "\n" , $t ) ;
- $this->mArticle->mContent = $t ;
- $this->mMetaData = $s ;
+ $t = str_replace ( "\n\n\n" , "\n" , $t );
+ $this->mArticle->mContent = $t;
+ $this->mMetaData = $s;
}
+ /*
+ * Check if a page was deleted while the user was editing it, before submit.
+ * Note that we rely on the logging table, which hasn't been always there,
+ * but that doesn't matter, because this only applies to brand new
+ * deletes.
+ */
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 ) {
+ if ( $this->lastDelete ) {
+ $deleteTime = wfTimestamp( TS_MW, $this->lastDelete->log_timestamp );
+ if ( $deleteTime > $this->starttime ) {
$this->deletedSinceEdit = true;
}
}
@@ -362,61 +359,34 @@ class EditPage {
*/
function edit() {
global $wgOut, $wgUser, $wgRequest;
-
- if ( !wfRunHooks( 'AlternateEdit', array( &$this ) ) )
+ // Allow extensions to modify/prevent this form or submission
+ if ( !wfRunHooks( 'AlternateEdit', array( &$this ) ) ) {
return;
+ }
wfProfileIn( __METHOD__ );
wfDebug( __METHOD__.": enter\n" );
- // this is not an article
- $wgOut->setArticleFlag(false);
+ // This is not an article
+ $wgOut->setArticleFlag( false );
$this->importFormData( $wgRequest );
$this->firsttime = false;
- if( $this->live ) {
+ if ( $this->live ) {
$this->livePreview();
wfProfileOut( __METHOD__ );
return;
}
-
- $wgOut->addScriptFile( 'edit.js' );
-
- if( wfReadOnly() ) {
- $this->readOnlyPage( $this->getContent() );
- wfProfileOut( __METHOD__ );
- return;
- }
- $permErrors = $this->mTitle->getUserPermissionsErrors('edit', $wgUser);
-
- if( !$this->mTitle->exists() ) {
- $permErrors = array_merge( $permErrors,
- wfArrayDiff2( $this->mTitle->getUserPermissionsErrors('create', $wgUser), $permErrors ) );
+ if ( wfReadOnly() && $this->save ) {
+ // Force preview
+ $this->save = false;
+ $this->preview = true;
}
- # Ignore some permissions errors.
- $remove = array();
- foreach( $permErrors as $error ) {
- if ( ( $this->preview || $this->diff ) &&
- ($error[0] == 'blockedtext' || $error[0] == 'autoblockedtext'))
- {
- // Don't worry about blocks when previewing/diffing
- $remove[] = $error;
- }
-
- if ($error[0] == 'readonlytext')
- {
- if ($this->edit) {
- $this->formtype = 'preview';
- } elseif ($this->save || $this->preview || $this->diff) {
- $remove[] = $error;
- }
- }
- }
- $permErrors = wfArrayDiff2( $permErrors, $remove );
-
+ $wgOut->addScriptFile( 'edit.js' );
+ $permErrors = $this->getEditPermissionErrors();
if ( $permErrors ) {
wfDebug( __METHOD__.": User can't edit\n" );
$this->readOnlyPage( $this->getContent(), true, $permErrors, 'edit' );
@@ -431,7 +401,7 @@ class EditPage {
$this->formtype = 'diff';
} else { # First time through
$this->firsttime = true;
- if( $this->previewOnOpen() ) {
+ if ( $this->previewOnOpen() ) {
$this->formtype = 'preview';
} else {
$this->extractMetaDataFromArticle () ;
@@ -448,13 +418,32 @@ class EditPage {
$this->isValidCssJsSubpage = $this->mTitle->isValidCssJsSubpage();
# Show applicable editing introductions
- if( $this->formtype == 'initial' || $this->firsttime )
+ if ( $this->formtype == 'initial' || $this->firsttime )
$this->showIntro();
- if( $this->mTitle->isTalkPage() ) {
+ if ( $this->mTitle->isTalkPage() ) {
$wgOut->addWikiMsg( 'talkpagetext' );
}
+ # Optional notices on a per-namespace and per-page basis
+ $editnotice_ns = 'editnotice-'.$this->mTitle->getNamespace();
+ $editnotice_page = $editnotice_ns.'-'.$this->mTitle->getDBkey();
+ if ( !wfEmptyMsg( $editnotice_ns, wfMsgForContent( $editnotice_ns ) ) ) {
+ $wgOut->addWikiText( wfMsgForContent( $editnotice_ns ) );
+ }
+ if ( MWNamespace::hasSubpages( $this->mTitle->getNamespace() ) ) {
+ $parts = explode( '/', $this->mTitle->getDBkey() );
+ $editnotice_base = $editnotice_ns;
+ while ( count( $parts ) > 0 ) {
+ $editnotice_base .= '-'.array_shift( $parts );
+ if ( !wfEmptyMsg( $editnotice_base, wfMsgForContent( $editnotice_base ) ) ) {
+ $wgOut->addWikiText( wfMsgForContent( $editnotice_base ) );
+ }
+ }
+ } else if ( !wfEmptyMsg( $editnotice_page, wfMsgForContent( $editnotice_page ) ) ) {
+ $wgOut->addWikiText( wfMsgForContent( $editnotice_page ) );
+ }
+
# Attempt submission here. This will check for edit conflicts,
# and redundantly check for locked database, blocked IPs, etc.
# that edit() already checked just in case someone tries to sneak
@@ -471,13 +460,13 @@ class EditPage {
# First time through: get contents, set time for conflict
# checking, etc.
if ( 'initial' == $this->formtype || $this->firsttime ) {
- if ($this->initialiseForm() === false) {
+ if ( $this->initialiseForm() === false) {
$this->noSuchSectionPage();
wfProfileOut( __METHOD__."-business-end" );
wfProfileOut( __METHOD__ );
return;
}
- if( !$this->mTitle->getArticleId() )
+ if ( !$this->mTitle->getArticleId() )
wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
}
@@ -485,6 +474,27 @@ class EditPage {
wfProfileOut( __METHOD__."-business-end" );
wfProfileOut( __METHOD__ );
}
+
+ protected function getEditPermissionErrors() {
+ global $wgUser;
+ $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
+ # Can this title be created?
+ if ( !$this->mTitle->exists() ) {
+ $permErrors = array_merge( $permErrors,
+ wfArrayDiff2( $this->mTitle->getUserPermissionsErrors( 'create', $wgUser ), $permErrors ) );
+ }
+ # Ignore some permissions errors when a user is just previewing/viewing diffs
+ $remove = array();
+ foreach( $permErrors as $error ) {
+ if ( ($this->preview || $this->diff) &&
+ ($error[0] == 'blockedtext' || $error[0] == 'autoblockedtext') )
+ {
+ $remove[] = $error;
+ }
+ }
+ $permErrors = wfArrayDiff2( $permErrors, $remove );
+ return $permErrors;
+ }
/**
* Show a read-only error
@@ -510,19 +520,19 @@ class EditPage {
*/
protected function previewOnOpen() {
global $wgRequest, $wgUser;
- if( $wgRequest->getVal( 'preview' ) == 'yes' ) {
+ if ( $wgRequest->getVal( 'preview' ) == 'yes' ) {
// Explicit override from request
return true;
- } elseif( $wgRequest->getVal( 'preview' ) == 'no' ) {
+ } elseif ( $wgRequest->getVal( 'preview' ) == 'no' ) {
// Explicit override from request
return false;
- } elseif( $this->section == 'new' ) {
+ } elseif ( $this->section == 'new' ) {
// Nothing *to* preview for new sections
return false;
- } elseif( ( $wgRequest->getVal( 'preload' ) !== '' || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
+ } elseif ( ( $wgRequest->getVal( 'preload' ) !== '' || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
// Standard preference behaviour
return true;
- } elseif( !$this->mTitle->exists() && $this->mTitle->getNamespace() == NS_CATEGORY ) {
+ } elseif ( !$this->mTitle->exists() && $this->mTitle->getNamespace() == NS_CATEGORY ) {
// Categories are special
return true;
} else {
@@ -542,7 +552,7 @@ class EditPage {
# Section edit can come from either the form or a link
$this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
- if( $request->wasPosted() ) {
+ 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.
@@ -550,7 +560,7 @@ class EditPage {
$this->textbox2 = $this->safeUnicodeInput( $request, 'wpTextbox2' );
$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);
@@ -560,7 +570,7 @@ class EditPage {
$this->scrolltop = $request->getIntOrNull( 'wpScrolltop' );
- if( is_null( $this->edittime ) ) {
+ if ( is_null( $this->edittime ) ) {
# If the form is incomplete, force to preview.
wfDebug( "$fname: Form data appears to be incomplete\n" );
wfDebug( "POST DATA: " . var_export( $_POST, true ) . "\n" );
@@ -591,11 +601,11 @@ class EditPage {
}
}
$this->save = !$this->preview && !$this->diff;
- if( !preg_match( '/^\d{14}$/', $this->edittime )) {
+ if ( !preg_match( '/^\d{14}$/', $this->edittime )) {
$this->edittime = null;
}
- if( !preg_match( '/^\d{14}$/', $this->starttime )) {
+ if ( !preg_match( '/^\d{14}$/', $this->starttime )) {
$this->starttime = null;
}
@@ -605,10 +615,12 @@ class EditPage {
$this->watchthis = $request->getCheck( 'wpWatchthis' );
# Don't force edit summaries when a user is editing their own user or talk page
- if( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) && $this->mTitle->getText() == $wgUser->getName() ) {
+ if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) &&
+ $this->mTitle->getText() == $wgUser->getName() )
+ {
$this->allowBlankSummary = true;
} else {
- $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' );
+ $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' ) || !$wgUser->getOption( 'forceeditsummary');
}
$this->autoSumm = $request->getText( 'wpAutoSummary' );
@@ -662,28 +674,30 @@ class EditPage {
*/
protected function showIntro() {
global $wgOut, $wgUser;
- if( $this->suppressIntro )
+ if ( $this->suppressIntro ) {
return;
-
+ }
# Show a warning message when someone creates/edits a user (talk) page but the user does not exists
- if( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK ) {
+ if ( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK ) {
$parts = explode( '/', $this->mTitle->getText(), 2 );
$username = $parts[0];
$id = User::idFromName( $username );
$ip = User::isIP( $username );
-
if ( $id == 0 && !$ip ) {
$wgOut->wrapWikiMsg( '<div class="mw-userpage-userdoesnotexist error">$1</div>',
array( 'userpage-userdoesnotexist', $username ) );
}
}
-
- if( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
- if( $wgUser->isLoggedIn() ) {
+ # Try to add a custom edit intro, or use the standard one if this is not possible.
+ if ( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
+ if ( $wgUser->isLoggedIn() ) {
$wgOut->wrapWikiMsg( '<div class="mw-newarticletext">$1</div>', 'newarticletext' );
} else {
$wgOut->wrapWikiMsg( '<div class="mw-newarticletextanon">$1</div>', 'newarticletextanon' );
}
+ }
+ # Give a notice if the user is editing a deleted page...
+ if ( !$this->mTitle->exists() ) {
$this->showDeletionLog( $wgOut );
}
}
@@ -694,9 +708,9 @@ class EditPage {
* @return bool
*/
protected function showCustomIntro() {
- if( $this->editintro ) {
+ if ( $this->editintro ) {
$title = Title::newFromText( $this->editintro );
- if( $title instanceof Title && $title->exists() && $title->userCanRead() ) {
+ if ( $title instanceof Title && $title->exists() && $title->userCanRead() ) {
global $wgOut;
$revision = Revision::newFromTitle( $title );
$wgOut->addWikiTextTitleTidy( $revision->getText(), $this->mTitle );
@@ -714,24 +728,24 @@ class EditPage {
* @return one of the constants describing the result
*/
function internalAttemptSave( &$result, $bot = false ) {
- global $wgSpamRegex, $wgFilterCallback, $wgUser, $wgOut, $wgParser;
+ global $wgFilterCallback, $wgUser, $wgOut, $wgParser;
global $wgMaxArticleSize;
$fname = 'EditPage::attemptSave';
wfProfileIn( $fname );
wfProfileIn( "$fname-checks" );
- if( !wfRunHooks( 'EditPage::attemptSave', array( &$this ) ) )
+ if ( !wfRunHooks( 'EditPage::attemptSave', array( &$this ) ) )
{
wfDebug( "Hook 'EditPage::attemptSave' aborted article saving" );
return self::AS_HOOK_ERROR;
}
# Check image redirect
- if ( $this->mTitle->getNamespace() == NS_IMAGE &&
+ if ( $this->mTitle->getNamespace() == NS_FILE &&
Title::newFromRedirect( $this->textbox1 ) instanceof Title &&
!$wgUser->isAllowed( 'upload' ) ) {
- if( $wgUser->isAnon() ) {
+ if ( $wgUser->isAnon() ) {
return self::AS_IMAGE_REDIRECT_ANON;
} else {
return self::AS_IMAGE_REDIRECT_LOGGED;
@@ -743,12 +757,15 @@ class EditPage {
$this->mMetaData = '' ;
# Check for spam
- $matches = array();
- if ( $wgSpamRegex && preg_match( $wgSpamRegex, $this->textbox1, $matches ) ) {
- $result['spam'] = $matches[0];
+ $match = self::matchSpamRegex( $this->summary );
+ if ( $match === false ) {
+ $match = self::matchSpamRegex( $this->textbox1 );
+ }
+ if ( $match !== false ) {
+ $result['spam'] = $match;
$ip = wfGetIP();
$pdbk = $this->mTitle->getPrefixedDBkey();
- $match = str_replace( "\n", '', $matches[0] );
+ $match = str_replace( "\n", '', $match );
wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" );
wfProfileOut( "$fname-checks" );
wfProfileOut( $fname );
@@ -765,7 +782,7 @@ class EditPage {
wfProfileOut( "$fname-checks" );
wfProfileOut( $fname );
return self::AS_HOOK_ERROR;
- } elseif( $this->hookError != '' ) {
+ } elseif ( $this->hookError != '' ) {
# ...or the hook could be expecting us to produce an error
wfProfileOut( "$fname-checks" );
wfProfileOut( $fname );
@@ -823,7 +840,6 @@ class EditPage {
# If article is new, insert it.
$aid = $this->mTitle->getArticleID( GAID_FOR_UPDATE );
if ( 0 == $aid ) {
-
// Late check for create permission, just in case *PARANOIA*
if ( !$this->mTitle->userCan( 'create' ) ) {
wfDebug( "$fname: no create permission\n" );
@@ -833,8 +849,8 @@ class EditPage {
# Don't save a new article if it's blank.
if ( '' == $this->textbox1 ) {
- wfProfileOut( $fname );
- return self::AS_BLANK_ARTICLE;
+ wfProfileOut( $fname );
+ return self::AS_BLANK_ARTICLE;
}
// Run post-section-merge edit filter
@@ -860,10 +876,10 @@ class EditPage {
wfDebug("timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n");
- if( $this->mArticle->getTimestamp() != $this->edittime ) {
+ if ( $this->mArticle->getTimestamp() != $this->edittime ) {
$this->isConflict = true;
- if( $this->section == 'new' ) {
- if( $this->mArticle->getUserText() == $wgUser->getName() &&
+ if ( $this->section == 'new' ) {
+ if ( $this->mArticle->getUserText() == $wgUser->getName() &&
$this->mArticle->getComment() == $this->summary ) {
// Probably a duplicate submission of a new comment.
// This can happen when squid resends a request after
@@ -878,7 +894,7 @@ class EditPage {
}
$userid = $wgUser->getId();
- if ( $this->isConflict) {
+ if ( $this->isConflict ) {
wfDebug( "EditPage::editForm conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
$this->mArticle->getTimestamp() . "')\n" );
$text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime);
@@ -887,21 +903,21 @@ class EditPage {
wfDebug( "EditPage::editForm getting section '$this->section'\n" );
$text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary);
}
- if( is_null( $text ) ) {
+ if ( is_null( $text ) ) {
wfDebug( "EditPage::editForm activating conflict; section replace failed.\n" );
$this->isConflict = true;
$text = $this->textbox1;
}
# Suppress edit conflict with self, except for section edits where merging is required.
- if ( ( $this->section == '' ) && ( 0 != $userid ) && ( $this->mArticle->getUser() == $userid ) ) {
+ if ( $this->section == '' && $userid && $this->userWasLastToEdit($userid,$this->edittime) ) {
wfDebug( "EditPage::editForm Suppressing edit conflict, same user.\n" );
$this->isConflict = false;
} else {
# switch from section editing to normal editing in edit conflict
- if($this->isConflict) {
+ if ( $this->isConflict ) {
# Attempt merge
- if( $this->mergeChangesInto( $text ) ){
+ if ( $this->mergeChangesInto( $text ) ) {
// Successful merge! Maybe we should tell the user the good news?
$this->isConflict = false;
wfDebug( "EditPage::editForm Suppressing edit conflict, successful merge.\n" );
@@ -928,12 +944,10 @@ class EditPage {
}
# 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) &&
+ if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp($oldtext, $text) &&
!is_object( Title::newFromRedirect( $text ) ) # check if it's not a redirect
) {
-
- if( md5( $this->summary ) == $this->autoSumm ) {
+ if ( md5( $this->summary ) == $this->autoSumm ) {
$this->missingSummary = true;
wfProfileOut( $fname );
return self::AS_SUMMARY_NEEDED;
@@ -941,7 +955,7 @@ class EditPage {
}
# And a similar thing for new sections
- if( $this->section == 'new' && !$this->allowBlankSummary && $wgUser->getOption( 'forceeditsummary' ) ) {
+ if ( $this->section == 'new' && !$this->allowBlankSummary ) {
if (trim($this->summary) == '') {
$this->missingSummary = true;
wfProfileOut( $fname );
@@ -952,26 +966,26 @@ class EditPage {
# All's well
wfProfileIn( "$fname-sectionanchor" );
$sectionanchor = '';
- if( $this->section == 'new' ) {
+ if ( $this->section == 'new' ) {
if ( $this->textbox1 == '' ) {
$this->missingComment = true;
return self::AS_TEXTBOX_EMPTY;
}
- if( $this->summary != '' ) {
+ if ( $this->summary != '' ) {
$sectionanchor = $wgParser->guessSectionNameFromWikiText( $this->summary );
# This is a new section, so create a link to the new section
# in the revision summary.
$cleanSummary = $wgParser->stripSectionName( $this->summary );
$this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary );
}
- } elseif( $this->section != '' ) {
+ } elseif ( $this->section != '' ) {
# Try to get a section anchor from the section source, redirect to edited section if header found
# XXX: might be better to integrate this into Article::replaceSection
# for duplicate heading checking and maybe parsing
$hasmatch = preg_match( "/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches );
# we can't deal with anchors, includes, html etc in the header for now,
# headline would need to be parsed to improve this
- if($hasmatch and strlen($matches[2]) > 0) {
+ if ( $hasmatch and strlen($matches[2]) > 0 ) {
$sectionanchor = $wgParser->guessSectionNameFromWikiText( $matches[2] );
}
}
@@ -993,7 +1007,7 @@ class EditPage {
}
# update the article here
- if( $this->mArticle->updateArticle( $text, $this->summary, $this->minoredit,
+ if ( $this->mArticle->updateArticle( $text, $this->summary, $this->minoredit,
$this->watchthis, $bot, $sectionanchor ) ) {
wfProfileOut( $fname );
return self::AS_SUCCESS_UPDATE;
@@ -1003,6 +1017,48 @@ class EditPage {
wfProfileOut( $fname );
return self::AS_END;
}
+
+ /**
+ * Check if no edits were made by other users since
+ * the time a user started editing the page. Limit to
+ * 50 revisions for the sake of performance.
+ */
+ protected function userWasLastToEdit( $id, $edittime ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->select( 'revision',
+ 'rev_user',
+ array(
+ 'rev_page' => $this->mArticle->getId(),
+ 'rev_timestamp > '.$dbw->addQuotes( $dbw->timestamp($edittime) )
+ ),
+ __METHOD__,
+ array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ) );
+ while( $row = $res->fetchObject() ) {
+ if( $row->rev_user != $id ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check given input text against $wgSpamRegex, and return the text of the first match.
+ * @return mixed -- matching string or false
+ */
+ public static function matchSpamRegex( $text ) {
+ global $wgSpamRegex;
+ if ( $wgSpamRegex ) {
+ // For back compatibility, $wgSpamRegex may be a single string or an array of regexes.
+ $regexes = (array)$wgSpamRegex;
+ foreach( $regexes as $regex ) {
+ $matches = array();
+ if ( preg_match( $regex, $text, $matches ) ) {
+ return $matches[0];
+ }
+ }
+ }
+ return false;
+ }
/**
* Initialise form fields in the object
@@ -1010,15 +1066,35 @@ class EditPage {
*/
function initialiseForm() {
$this->edittime = $this->mArticle->getTimestamp();
- $this->textbox1 = $this->getContent(false);
- if ($this->textbox1 === false) return false;
-
- if ( !$this->mArticle->exists() && $this->mTitle->getNamespace() == NS_MEDIAWIKI )
- $this->textbox1 = wfMsgWeirdKey( $this->mTitle->getText() );
+ $this->textbox1 = $this->getContent( false );
+ if ( $this->textbox1 === false ) return false;
wfProxyCheck();
return true;
}
+ function setHeaders() {
+ global $wgOut, $wgTitle;
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ if ( $this->formtype == 'preview' ) {
+ $wgOut->setPageTitleActionText( wfMsg( 'preview' ) );
+ }
+ if ( $this->isConflict ) {
+ $wgOut->setPageTitle( wfMsg( 'editconflict', $wgTitle->getPrefixedText() ) );
+ } elseif ( $this->section != '' ) {
+ $msg = $this->section == 'new' ? 'editingcomment' : 'editingsection';
+ $wgOut->setPageTitle( wfMsg( $msg, $wgTitle->getPrefixedText() ) );
+ } else {
+ # Use the title defined by DISPLAYTITLE magic word when present
+ if ( isset($this->mParserOutput)
+ && ( $dt = $this->mParserOutput->getDisplayTitle() ) !== false ) {
+ $title = $dt;
+ } else {
+ $title = $wgTitle->getPrefixedText();
+ }
+ $wgOut->setPageTitle( wfMsg( 'editing', $title ) );
+ }
+ }
+
/**
* Send the edit form and related headers to $wgOut
* @param $formCallback Optional callable that takes an OutputPage
@@ -1026,13 +1102,13 @@ class EditPage {
* near the top, for captchas and the like.
*/
function showEditForm( $formCallback=null ) {
- global $wgOut, $wgUser, $wgLang, $wgContLang, $wgMaxArticleSize, $wgTitle;
+ global $wgOut, $wgUser, $wgLang, $wgContLang, $wgMaxArticleSize, $wgTitle, $wgRequest;
# If $wgTitle is null, that means we're in API mode.
# Some hook probably called this function without checking
# for is_null($wgTitle) first. Bail out right here so we don't
# do lots of work just to discard it right after.
- if(is_null($wgTitle))
+ if (is_null($wgTitle))
return;
$fname = 'EditPage::showEditForm';
@@ -1042,60 +1118,55 @@ class EditPage {
wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) ) ;
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ #need to parse the preview early so that we know which templates are used,
+ #otherwise users with "show preview after edit box" will get a blank list
+ #we parse this near the beginning so that setHeaders can do the title
+ #setting work instead of leaving it in getPreviewText
+ $previewOutput = '';
+ if ( $this->formtype == 'preview' ) {
+ $previewOutput = $this->getPreviewText();
+ }
+
+ $this->setHeaders();
# Enabled article-related sidebar, toplinks, etc.
$wgOut->setArticleRelated( true );
- if ( $this->formtype == 'preview' ) {
- $wgOut->setPageTitleActionText( wfMsg( 'preview' ) );
- }
-
if ( $this->isConflict ) {
- $s = wfMsg( 'editconflict', $wgTitle->getPrefixedText() );
- $wgOut->setPageTitle( $s );
$wgOut->addWikiMsg( 'explainconflict' );
$this->textbox2 = $this->textbox1;
$this->textbox1 = $this->getContent();
$this->edittime = $this->mArticle->getTimestamp();
} else {
- if( $this->section != '' ) {
- if( $this->section == 'new' ) {
- $s = wfMsg('editingcomment', $wgTitle->getPrefixedText() );
- } else {
- $s = wfMsg('editingsection', $wgTitle->getPrefixedText() );
- $matches = array();
- if( !$this->summary && !$this->preview && !$this->diff ) {
- preg_match( "/^(=+)(.+)\\1/mi",
- $this->textbox1,
- $matches );
- if( !empty( $matches[2] ) ) {
- global $wgParser;
- $this->summary = "/* " .
- $wgParser->stripSectionName(trim($matches[2])) .
- " */ ";
- }
+ if ( $this->section != '' && $this->section != 'new' ) {
+ $matches = array();
+ if ( !$this->summary && !$this->preview && !$this->diff ) {
+ preg_match( "/^(=+)(.+)\\1/mi",
+ $this->textbox1,
+ $matches );
+ if ( !empty( $matches[2] ) ) {
+ global $wgParser;
+ $this->summary = "/* " .
+ $wgParser->stripSectionName(trim($matches[2])) .
+ " */ ";
}
}
- } else {
- $s = wfMsg( 'editing', $wgTitle->getPrefixedText() );
}
- $wgOut->setPageTitle( $s );
if ( $this->missingComment ) {
$wgOut->wrapWikiMsg( '<div id="mw-missingcommenttext">$1</div>', 'missingcommenttext' );
}
- if( $this->missingSummary && $this->section != 'new' ) {
+ if ( $this->missingSummary && $this->section != 'new' ) {
$wgOut->wrapWikiMsg( '<div id="mw-missingsummary">$1</div>', 'missingsummary' );
}
- if( $this->missingSummary && $this->section == 'new' ) {
+ if ( $this->missingSummary && $this->section == 'new' ) {
$wgOut->wrapWikiMsg( '<div id="mw-missingcommentheader">$1</div>', 'missingcommentheader' );
}
- if( $this->hookError !== '' ) {
+ if ( $this->hookError !== '' ) {
$wgOut->addWikiText( $this->hookError );
}
@@ -1105,46 +1176,54 @@ 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 ) ) {
+ if ( !$this->mArticle->mRevision->userCan( Revision::DELETED_TEXT ) ) {
$wgOut->addWikiMsg( 'rev-deleted-text-permission' );
- } else if( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
+ } else if ( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
$wgOut->addWikiMsg( 'rev-deleted-text-view' );
}
- if( !$this->mArticle->mRevision->isCurrent() ) {
+ if ( !$this->mArticle->mRevision->isCurrent() ) {
$this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() );
$wgOut->addWikiMsg( 'editingold' );
}
}
}
- if( wfReadOnly() ) {
- $wgOut->addHTML( '<div id="mw-read-only-warning">'.wfMsgWikiHTML( 'readonlywarning' ).'</div>' );
- } elseif( $wgUser->isAnon() && $this->formtype != 'preview' ) {
- $wgOut->addHTML( '<div id="mw-anon-edit-warning">'.wfMsgWikiHTML( 'anoneditwarning' ).'</div>' );
+ if ( wfReadOnly() ) {
+ $wgOut->wrapWikiMsg( "<div id=\"mw-read-only-warning\">\n$1\n</div>", array( 'readonlywarning', wfReadOnlyReason() ) );
+ } elseif ( $wgUser->isAnon() && $this->formtype != 'preview' ) {
+ $wgOut->wrapWikiMsg( '<div id="mw-anon-edit-warning">$1</div>', 'anoneditwarning' );
} else {
- if( $this->isCssJsSubpage && $this->formtype != 'preview' ) {
+ if ( $this->isCssJsSubpage ) {
# Check the skin exists
- if( $this->isValidCssJsSubpage ) {
- $wgOut->addWikiMsg( 'usercssjsyoucanpreview' );
+ if ( $this->isValidCssJsSubpage ) {
+ if ( $this->formtype !== 'preview' ) {
+ $wgOut->addWikiMsg( 'usercssjsyoucanpreview' );
+ }
} else {
$wgOut->addWikiMsg( 'userinvalidcssjstitle', $wgTitle->getSkinFromCssJsSubpage() );
}
}
}
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
+ $classes = array(); // Textarea CSS
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
# Show a warning if editing an interface message
$wgOut->addWikiMsg( 'editinginterface' );
- } elseif( $this->mTitle->isProtected( 'edit' ) ) {
+ } elseif ( $this->mTitle->isProtected( 'edit' ) ) {
# Is the title semi-protected?
- if( $this->mTitle->isSemiProtected() ) {
+ if ( $this->mTitle->isSemiProtected() ) {
$noticeMsg = 'semiprotectedpagewarning';
+ $classes[] = 'mw-textarea-sprotected';
} else {
# Then it must be protected based on static groups (regular)
$noticeMsg = 'protectedpagewarning';
+ $classes[] = 'mw-textarea-protected';
}
+ $wgOut->addHTML( "<div class='mw-warning-with-logexcerpt'>\n" );
$wgOut->addWikiMsg( $noticeMsg );
+ LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '', 1 );
+ $wgOut->addHTML( "</div>\n" );
}
if ( $this->mTitle->isCascadeProtected() ) {
# Is this page under cascading protection from some source pages?
@@ -1158,7 +1237,7 @@ class EditPage {
}
$wgOut->wrapWikiMsg( $notice, array( 'cascadeprotectedwarning', count($cascadeSources) ) );
}
- if( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) != array() ){
+ if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) ) {
$wgOut->addWikiMsg( 'titleprotectedwarning' );
}
@@ -1166,30 +1245,21 @@ class EditPage {
$this->kblength = (int)(strlen( $this->textbox1 ) / 1024);
}
if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) {
- $wgOut->addWikiMsg( 'longpageerror', $wgLang->formatNum( $this->kblength ), $wgMaxArticleSize );
- } elseif( $this->kblength > 29 ) {
+ $wgOut->addHTML( "<div class='error' id='mw-edit-longpageerror'>\n" );
+ $wgOut->addWikiMsg( 'longpageerror', $wgLang->formatNum( $this->kblength ), $wgLang->formatNum( $wgMaxArticleSize ) );
+ $wgOut->addHTML( "</div>\n" );
+ } elseif ( $this->kblength > 29 ) {
+ $wgOut->addHTML( "<div id='mw-edit-longpagewarning'>\n" );
$wgOut->addWikiMsg( 'longpagewarning', $wgLang->formatNum( $this->kblength ) );
+ $wgOut->addHTML( "</div>\n" );
}
- #need to parse the preview early so that we know which templates are used,
- #otherwise users with "show preview after edit box" will get a blank list
- if ( $this->formtype == 'preview' ) {
- $previewOutput = $this->getPreviewText();
- }
-
- $rows = $wgUser->getIntOption( 'rows' );
- $cols = $wgUser->getIntOption( 'cols' );
-
- $ew = $wgUser->getOption( 'editwidth' );
- if ( $ew ) $ew = " style=\"width:100%\"";
- else $ew = '';
-
- $q = 'action=submit';
+ $q = 'action='.$this->action;
#if ( "no" == $redirect ) { $q .= "&redirect=no"; }
$action = $wgTitle->escapeLocalURL( $q );
- $summary = wfMsg('summary');
- $subject = wfMsg('subject');
+ $summary = wfMsg( 'summary' );
+ $subject = wfMsg( 'subject' );
$cancel = $sk->makeKnownLink( $wgTitle->getPrefixedText(),
wfMsgExt('cancel', array('parseinline')) );
@@ -1208,7 +1278,7 @@ class EditPage {
'[[' . wfMsgForContent( 'copyrightpage' ) . ']]' );
}
- if( $wgUser->getOption('showtoolbar') and !$this->isCssJsSubpage ) {
+ if ( $wgUser->getOption('showtoolbar') and !$this->isCssJsSubpage ) {
# prepare toolbar for edit buttons
$toolbar = EditPage::getEditToolbar();
} else {
@@ -1216,35 +1286,31 @@ class EditPage {
}
// activate checkboxes if user wants them to be always active
- if( !$this->preview && !$this->diff ) {
+ if ( !$this->preview && !$this->diff ) {
# Sort out the "watch" checkbox
- if( $wgUser->getOption( 'watchdefault' ) ) {
+ if ( $wgUser->getOption( 'watchdefault' ) ) {
# Watch all edits
$this->watchthis = true;
- } elseif( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
+ } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
# Watch creations
$this->watchthis = true;
- } elseif( $this->mTitle->userIsWatching() ) {
+ } elseif ( $this->mTitle->userIsWatching() ) {
# Already watched
$this->watchthis = true;
}
+
+ # May be overriden by request parameters
+ if( $wgRequest->getBool( 'watchthis' ) ) {
+ $this->watchthis = true;
+ }
- if( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
+ if ( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
}
$wgOut->addHTML( $this->editFormPageTop );
if ( $wgUser->getOption( 'previewontop' ) ) {
-
- if ( 'preview' == $this->formtype ) {
- $this->showPreview( $previewOutput );
- } else {
- $wgOut->addHTML( '<div id="wikiPreview"></div>' );
- }
-
- if ( 'diff' == $this->formtype ) {
- $this->showDiff();
- }
+ $this->displayPreviewArea( $previewOutput, true );
}
@@ -1262,28 +1328,28 @@ class EditPage {
# 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 );
+ 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<input tabindex='1' type='text' value=\"$summarytext\" name='wpSummary' id='wpSummary' maxlength='200' size='60' />{$summaryhiddens}<br />";
+ if ( $this->section == 'new' ) {
+ $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" : '';
+ $subjectpreview = $summarytext && $this->preview ? "<div class=\"mw-summary-preview\">". wfMsg('subject-preview') . $sk->commentBlock( $formattedSummary, $this->mTitle, true )."</div>\n" : '';
$summarypreview = '';
} else {
$commentsubject = '';
- $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" : '';
+ $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 = '';
}
# Set focus to the edit box on load, except on preview or diff, where it would interfere with the display
- if( !$this->preview && !$this->diff ) {
+ if ( !$this->preview && !$this->diff ) {
$wgOut->setOnloadHandler( 'document.editform.wpTextbox1.focus()' );
}
- $templates = ($this->preview || $this->section != '') ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates();
+ $templates = $this->getTemplates();
$formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
$hiddencats = $this->mArticle->getHiddenCategories();
@@ -1294,20 +1360,24 @@ class EditPage {
$metadata = $this->mMetaData ;
$metadata = htmlspecialchars( $wgContLang->recodeForEdit( $metadata ) ) ;
$top = wfMsgWikiHtml( 'metadata_help' );
+ /* ToDo: Replace with clean code */
+ $ew = $wgUser->getOption( 'editwidth' );
+ if ( $ew ) $ew = " style=\"width:100%\"";
+ else $ew = '';
+ $cols = $wgUser->getIntOption( 'cols' );
+ /* /ToDo */
$metadata = $top . "<textarea name='metadata' rows='3' cols='{$cols}'{$ew}>{$metadata}</textarea>" ;
}
else $metadata = "" ;
- $hidden = '';
$recreate = '';
- if ($this->wasDeletedSinceLastEdit()) {
+ if ( $this->wasDeletedSinceLastEdit() ) {
if ( 'save' != $this->formtype ) {
$wgOut->addWikiMsg('deletedwhileediting');
} else {
// Hide the toolbar and edit area, use can click preview to get it back
// Add an confirmation checkbox and explanation.
$toolbar = '';
- $hidden = 'type="hidden" style="display:none;"';
$recreate = $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ));
$recreate .=
"<br /><input tabindex='1' type='checkbox' value='1' name='wpRecreate' id='wpRecreate' />".
@@ -1317,7 +1387,7 @@ class EditPage {
$tabindex = 2;
- $checkboxes = self::getCheckboxes( $tabindex, $sk,
+ $checkboxes = $this->getCheckboxes( $tabindex, $sk,
array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) );
$checkboxhtml = implode( $checkboxes, "\n" );
@@ -1334,47 +1404,34 @@ class EditPage {
END
);
- if( is_callable( $formCallback ) ) {
+ if ( is_callable( $formCallback ) ) {
call_user_func_array( $formCallback, array( &$wgOut ) );
}
wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
// Put these up at the top to ensure they aren't lost on early form submission
- $wgOut->addHTML( "
-<input type='hidden' value=\"" . htmlspecialchars( $this->section ) . "\" name=\"wpSection\" />
-<input type='hidden' value=\"{$this->starttime}\" name=\"wpStarttime\" />\n
-<input type='hidden' value=\"{$this->edittime}\" name=\"wpEdittime\" />\n
-<input type='hidden' value=\"{$this->scrolltop}\" name=\"wpScrolltop\" id=\"wpScrolltop\" />\n" );
-
- $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";
- }
+ $this->showFormBeforeText();
$wgOut->addHTML( <<<END
-$recreate
+{$recreate}
{$commentsubject}
{$subjectpreview}
{$this->editFormTextBeforeContent}
-<textarea tabindex='1' accesskey="," name="wpTextbox1" id="wpTextbox1" rows='{$rows}'
-cols='{$cols}'{$ew} $hidden>{$encodedtext}</textarea>
END
);
+ $this->showTextbox1( $classes );
$wgOut->wrapWikiMsg( "<div id=\"editpage-copywarn\">\n$1\n</div>", $copywarnMsg );
- $wgOut->addHTML( $this->editFormTextAfterWarn );
- $wgOut->addHTML( "
+ $wgOut->addHTML( <<<END
+{$this->editFormTextAfterWarn}
{$metadata}
{$editsummary}
{$summarypreview}
{$checkboxhtml}
{$safemodehtml}
-");
+END
+);
$wgOut->addHTML(
"<div class='editButtons'>
@@ -1398,20 +1455,18 @@ 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>' );
-
- $wgOut->addHTML( $this->editFormTextAfterTools );
+ $this->showEditTools();
- $wgOut->addHTML( "
+ $wgOut->addHTML( <<<END
+{$this->editFormTextAfterTools}
<div class='templatesUsed'>
{$formattedtemplates}
</div>
<div class='hiddencats'>
{$formattedhiddencats}
</div>
-");
+END
+);
if ( $this->isConflict && wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
$wgOut->wrapWikiMsg( '==$1==', "yourdiff" );
@@ -1421,26 +1476,88 @@ END
$de->showDiff( wfMsg( "yourtext" ), wfMsg( "storedversion" ) );
$wgOut->wrapWikiMsg( '==$1==', "yourtext" );
- $wgOut->addHTML( "<textarea tabindex='6' id='wpTextbox2' name=\"wpTextbox2\" rows='{$rows}' cols='{$cols}'>"
- . htmlspecialchars( $this->safeUnicodeOutput( $this->textbox2 ) ) . "\n</textarea>" );
+ $this->showTextbox2();
}
$wgOut->addHTML( $this->editFormTextBottom );
$wgOut->addHTML( "</form>\n" );
if ( !$wgUser->getOption( 'previewontop' ) ) {
+ $this->displayPreviewArea( $previewOutput, false );
+ }
- if ( $this->formtype == 'preview') {
- $this->showPreview( $previewOutput );
- } else {
- $wgOut->addHTML( '<div id="wikiPreview"></div>' );
- }
+ wfProfileOut( $fname );
+ }
- if ( $this->formtype == 'diff') {
- $this->showDiff();
- }
+ protected function showFormBeforeText() {
+ global $wgOut;
+ $wgOut->addHTML( "
+<input type='hidden' value=\"" . htmlspecialchars( $this->section ) . "\" name=\"wpSection\" />
+<input type='hidden' value=\"{$this->starttime}\" name=\"wpStarttime\" />\n
+<input type='hidden' value=\"{$this->edittime}\" name=\"wpEdittime\" />\n
+<input type='hidden' value=\"{$this->scrolltop}\" name=\"wpScrolltop\" id=\"wpScrolltop\" />\n" );
+ }
+
+ protected function showTextbox1( $classes ) {
+ $attribs = array( 'tabindex' => 1 );
+
+ if ( $this->wasDeletedSinceLastEdit() )
+ $attribs['type'] = 'hidden';
+ if ( !empty($classes) )
+ $attribs['class'] = implode(' ',$classes);
+
+ $this->showTextbox( $this->textbox1, 'wpTextbox1', $attribs );
+ }
+
+ protected function showTextbox2() {
+ $this->showTextbox( $this->textbox2, 'wpTextbox2', array( 'tabindex' => 6 ) );
+ }
+
+ protected function showTextbox( $content, $name, $attribs = array() ) {
+ global $wgOut, $wgUser;
+
+ $wikitext = $this->safeUnicodeOutput( $content );
+ if ( $wikitext !== '' ) {
+ // 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.
+ $wikitext .= "\n";
+ }
+
+ $attribs['accesskey'] = ',';
+ $attribs['id'] = $name;
+
+ if ( $wgUser->getOption( 'editwidth' ) )
+ $attribs['style'] = 'width: 100%';
+
+ $wgOut->addHTML( Xml::textarea(
+ $name,
+ $wikitext,
+ $wgUser->getIntOption( 'cols' ), $wgUser->getIntOption( 'rows' ),
+ $attribs ) );
+ }
+
+ protected function displayPreviewArea( $previewOutput, $isOnTop = false ) {
+ global $wgOut;
+ $classes = array();
+ if ( $isOnTop )
+ $classes[] = 'ontop';
+
+ $attribs = array( 'id' => 'wikiPreview', 'class' => implode( ' ', $classes ) );
+
+ if ( $this->formtype != 'preview' )
+ $attribs['style'] = 'display: none;';
+
+ $wgOut->addHTML( Xml::openElement( 'div', $attribs ) );
+ if ( $this->formtype == 'preview' ) {
+ $this->showPreview( $previewOutput );
}
- wfProfileOut( $fname );
+ $wgOut->addHTML( '</div>' );
+
+ if ( $this->formtype == 'diff') {
+ $this->showDiff();
+ }
}
/**
@@ -1451,17 +1568,16 @@ END
*/
protected function showPreview( $text ) {
global $wgOut;
-
- $wgOut->addHTML( '<div id="wikiPreview">' );
- if($this->mTitle->getNamespace() == NS_CATEGORY) {
+ if ( $this->mTitle->getNamespace() == NS_CATEGORY) {
$this->mArticle->openShowCategory();
}
+ # This hook seems slightly odd here, but makes things more
+ # consistent for extensions.
wfRunHooks( 'OutputPageBeforeHTML',array( &$wgOut, &$text ) );
$wgOut->addHTML( $text );
- if($this->mTitle->getNamespace() == NS_CATEGORY) {
+ if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
$this->mArticle->closeShowCategory();
}
- $wgOut->addHTML( '</div>' );
}
/**
@@ -1477,16 +1593,22 @@ END
function doLivePreviewScript() {
global $wgOut, $wgTitle;
$wgOut->addScriptFile( 'preview.js' );
- $liveAction = $wgTitle->getLocalUrl( 'action=submit&wpPreview=true&live=true' );
+ $liveAction = $wgTitle->getLocalUrl( "action={$this->action}&wpPreview=true&live=true" );
return "return !lpDoPreview(" .
"editform.wpTextbox1.value," .
'"' . $liveAction . '"' . ")";
}
+ protected function showEditTools() {
+ global $wgOut;
+ $wgOut->addHTML( '<div class="mw-editTools">' );
+ $wgOut->addWikiMsgArray( 'edittools', array(), array( 'content' ) );
+ $wgOut->addHTML( '</div>' );
+ }
+
function getLastDelete() {
$dbr = wfGetDB( DB_SLAVE );
- $fname = 'EditPage::getLastDelete';
- $res = $dbr->select(
+ $data = $dbr->selectRow(
array( 'logging', 'user' ),
array( 'log_type',
'log_action',
@@ -1502,27 +1624,20 @@ END
'log_type' => 'delete',
'log_action' => 'delete',
'user_id=log_user' ),
- $fname,
+ __METHOD__,
array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' ) );
- if($dbr->numRows($res) == 1) {
- while ( $x = $dbr->fetchObject ( $res ) )
- $data = $x;
- $dbr->freeResult ( $res ) ;
- } else {
- $data = null;
- }
return $data;
}
/**
- * @todo document
+ * Get the rendered text for previewing.
+ * @return string
*/
function getPreviewText() {
- global $wgOut, $wgUser, $wgTitle, $wgParser, $wgLang, $wgContLang;
+ global $wgOut, $wgUser, $wgTitle, $wgParser, $wgLang, $wgContLang, $wgMessageCache;
- $fname = 'EditPage::getPreviewText';
- wfProfileIn( $fname );
+ wfProfileIn( __METHOD__ );
if ( $this->mTriedSave && !$this->mTokenOk ) {
if ( $this->mTokenOkExceptSuffix ) {
@@ -1538,7 +1653,7 @@ END
$parserOptions->setEditSection( false );
global $wgRawHtml;
- if( $wgRawHtml && !$this->mTokenOk ) {
+ if ( $wgRawHtml && !$this->mTokenOk ) {
// Could be an offsite preview attempt. This is very unsafe if
// HTML is enabled, as it could be an attack.
return $wgOut->parse( "<div class='previewnote'>" .
@@ -1549,21 +1664,22 @@ END
# XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here
if ( $this->isCssJsSubpage ) {
- if(preg_match("/\\.css$/", $this->mTitle->getText() ) ) {
+ if (preg_match("/\\.css$/", $this->mTitle->getText() ) ) {
$previewtext = wfMsg('usercsspreview');
- } else if(preg_match("/\\.js$/", $this->mTitle->getText() ) ) {
+ } else if (preg_match("/\\.js$/", $this->mTitle->getText() ) ) {
$previewtext = wfMsg('userjspreview');
}
$parserOptions->setTidy(true);
- $parserOutput = $wgParser->parse( $previewtext , $this->mTitle, $parserOptions );
- $wgOut->addHTML( $parserOutput->mText );
- $previewHTML = '';
+ $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions );
+ $previewHTML = $parserOutput->mText;
+ } elseif ( $rt = Title::newFromRedirect( $this->textbox1 ) ) {
+ $previewHTML = $this->mArticle->viewRedirect( $rt, false );
} else {
$toparse = $this->textbox1;
# If we're adding a comment, we need to show the
# summary as the headline
- if($this->section=="new" && $this->summary!="") {
+ if ( $this->section=="new" && $this->summary!="" ) {
$toparse="== {$this->summary} ==\n\n".$toparse;
}
@@ -1571,19 +1687,9 @@ END
// 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 );
- }
+ list( /* $unused */, $lang ) = $wgMessageCache->figureMessage( $this->mTitle->getText() );
+ $obj = wfGetLangObj( $lang );
+ $parserOptions->setTargetLanguage( $obj );
}
@@ -1593,20 +1699,9 @@ END
$this->mTitle, $parserOptions );
$previewHTML = $parserOutput->getText();
+ $this->mParserOutput = $parserOutput;
$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() ) );
- }
-
- foreach ( $parserOutput->getTemplates() as $ns => $template)
- foreach ( array_keys( $template ) as $dbk)
- $this->mPreviewTemplates[] = Title::makeTitle($ns, $dbk);
-
if ( count( $parserOutput->getWarnings() ) ) {
$note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
}
@@ -1615,18 +1710,26 @@ END
$previewhead = '<h2>' . htmlspecialchars( wfMsg( 'preview' ) ) . "</h2>\n" .
"<div class='previewnote'>" . $wgOut->parse( $note ) . "</div>\n";
if ( $this->isConflict ) {
- $previewhead.='<h2>' . htmlspecialchars( wfMsg( 'previewconflict' ) ) . "</h2>\n";
+ $previewhead .='<h2>' . htmlspecialchars( wfMsg( 'previewconflict' ) ) . "</h2>\n";
}
- if( $wgUser->getOption( 'previewontop' ) ) {
- // Spacer for the edit toolbar
- $previewfoot = '<p><br /></p>';
+ wfProfileOut( __METHOD__ );
+ return $previewhead . $previewHTML;
+ }
+
+ function getTemplates() {
+ if ( $this->preview || $this->section != '' ) {
+ $templates = array();
+ if ( !isset($this->mParserOutput) ) return $templates;
+ foreach( $this->mParserOutput->getTemplates() as $ns => $template) {
+ foreach( array_keys( $template ) as $dbk ) {
+ $templates[] = Title::makeTitle($ns, $dbk);
+ }
+ }
+ return $templates;
} else {
- $previewfoot = '';
+ return $this->mArticle->getUsedTemplates();
}
-
- wfProfileOut( $fname );
- return $previewhead . $previewHTML . $previewfoot;
}
/**
@@ -1639,22 +1742,22 @@ END
# If the user made changes, preserve them when showing the markup
# (This happens when a user is blocked during edit, for instance)
$first = $this->firsttime || ( !$this->save && $this->textbox1 == '' );
- if( $first ) {
+ if ( $first ) {
$source = $this->mTitle->exists() ? $this->getContent() : false;
} else {
$source = $this->textbox1;
}
# Spit out the source or the user's modified version
- if( $source !== false ) {
- $rows = $wgUser->getOption( 'rows' );
- $cols = $wgUser->getOption( 'cols' );
+ if ( $source !== false ) {
+ $rows = $wgUser->getIntOption( 'rows' );
+ $cols = $wgUser->getIntOption( 'cols' );
$attribs = array( 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $cols, 'rows' => $rows, 'readonly' => 'readonly' );
- $wgOut->addHtml( '<hr />' );
+ $wgOut->addHTML( '<hr />' );
$wgOut->addWikiMsg( $first ? 'blockedoriginalsource' : 'blockededitsource', $this->mTitle->getPrefixedText() );
# Why we don't use Xml::element here?
# Is it because if $source is '', it returns <textarea />?
- $wgOut->addHtml( Xml::openElement( 'textarea', $attribs ) . htmlspecialchars( $source ) . Xml::closeElement( 'textarea' ) );
+ $wgOut->addHTML( Xml::openElement( 'textarea', $attribs ) . htmlspecialchars( $source ) . Xml::closeElement( 'textarea' ) );
}
}
@@ -1672,13 +1775,13 @@ END
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
- $wgOut->addHtml( wfMsgWikiHtml( 'whitelistedittext', $loginLink ) );
+ $wgOut->addHTML( wfMsgWikiHtml( 'whitelistedittext', $loginLink ) );
$wgOut->returnToMain( false, $wgTitle );
}
/**
* Creates a basic error page which informs the user that
- * they have attempted to edit a nonexistant section.
+ * they have attempted to edit a nonexistent section.
*/
function noSuchSectionPage() {
global $wgOut, $wgTitle;
@@ -1703,11 +1806,11 @@ END
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
- $wgOut->addHtml( '<div id="spamprotected">' );
+ $wgOut->addHTML( '<div id="spamprotected">' );
$wgOut->addWikiMsg( 'spamprotectiontext' );
if ( $match )
$wgOut->addWikiMsg( 'spamprotectionmatch', wfEscapeWikiText( $match ) );
- $wgOut->addHtml( '</div>' );
+ $wgOut->addHTML( '</div>' );
$wgOut->returnToMain( false, $wgTitle );
}
@@ -1724,7 +1827,7 @@ END
// This is the revision the editor started from
$baseRevision = $this->getBaseRevision();
- if( is_null( $baseRevision ) ) {
+ if ( is_null( $baseRevision ) ) {
wfProfileOut( $fname );
return false;
}
@@ -1733,14 +1836,14 @@ END
// The current state, we want to merge updates into it
$currentRevision = Revision::loadFromTitle(
$db, $this->mTitle );
- if( is_null( $currentRevision ) ) {
+ if ( is_null( $currentRevision ) ) {
wfProfileOut( $fname );
return false;
}
$currentText = $currentRevision->getText();
$result = '';
- if( wfMerge( $baseText, $editText, $currentText, $result ) ){
+ if ( wfMerge( $baseText, $editText, $currentText, $result ) ) {
$editText = $result;
wfProfileOut( $fname );
return true;
@@ -1759,7 +1862,7 @@ END
*/
function checkUnicodeCompliantBrowser() {
global $wgBrowserBlackList;
- if( empty( $_SERVER["HTTP_USER_AGENT"] ) ) {
+ if ( empty( $_SERVER["HTTP_USER_AGENT"] ) ) {
// No User-Agent header sent? Trust it by default...
return true;
}
@@ -1861,7 +1964,7 @@ END
array(
'image' => $wgLang->getImageFile('button-image'),
'id' => 'mw-editbutton-image',
- 'open' => '[['.$wgContLang->getNsText(NS_IMAGE).':',
+ 'open' => '[['.$wgContLang->getNsText(NS_FILE).':',
'close' => ']]',
'sample' => wfMsg('image_sample'),
'tip' => wfMsg('image_tip'),
@@ -1951,7 +2054,7 @@ END
*
* @return array
*/
- public static function getCheckboxes( &$tabindex, $skin, $checked ) {
+ public function getCheckboxes( &$tabindex, $skin, $checked ) {
global $wgUser;
$checkboxes = array();
@@ -1981,6 +2084,7 @@ END
Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
"&nbsp;<label for='wpWatchthis'".$skin->tooltip('watch', 'withaccess').">{$watchLabel}</label>";
}
+ wfRunHooks( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
return $checkboxes;
}
@@ -2058,7 +2162,7 @@ END
);
$buttons['diff'] = Xml::element('input', $temp, '');
- wfRunHooks( 'EditPageBeforeEditButtons', array( &$this, &$buttons ) );
+ wfRunHooks( 'EditPageBeforeEditButtons', array( &$this, &$buttons, &$tabindex ) );
return $buttons;
}
@@ -2117,7 +2221,7 @@ END
}
global $wgOut;
- $wgOut->addHtml( '<div id="wikiDiff">' . $difftext . '</div>' );
+ $wgOut->addHTML( '<div id="wikiDiff">' . $difftext . '</div>' );
}
/**
@@ -2174,20 +2278,20 @@ END
$working = 0;
for( $i = 0; $i < strlen( $invalue ); $i++ ) {
$bytevalue = ord( $invalue{$i} );
- if( $bytevalue <= 0x7F ) { //0xxx xxxx
+ if ( $bytevalue <= 0x7F ) { //0xxx xxxx
$result .= chr( $bytevalue );
$bytesleft = 0;
- } elseif( $bytevalue <= 0xBF ) { //10xx xxxx
+ } elseif ( $bytevalue <= 0xBF ) { //10xx xxxx
$working = $working << 6;
$working += ($bytevalue & 0x3F);
$bytesleft--;
- if( $bytesleft <= 0 ) {
+ if ( $bytesleft <= 0 ) {
$result .= "&#x" . strtoupper( dechex( $working ) ) . ";";
}
- } elseif( $bytevalue <= 0xDF ) { //110x xxxx
+ } elseif ( $bytevalue <= 0xDF ) { //110x xxxx
$working = $bytevalue & 0x1F;
$bytesleft = 1;
- } elseif( $bytevalue <= 0xEF ) { //1110 xxxx
+ } elseif ( $bytevalue <= 0xEF ) { //1110 xxxx
$working = $bytevalue & 0x0F;
$bytesleft = 2;
} else { //1111 0xxx
@@ -2210,7 +2314,7 @@ END
function unmakesafe( $invalue ) {
$result = "";
for( $i = 0; $i < strlen( $invalue ); $i++ ) {
- if( ( substr( $invalue, $i, 3 ) == "&#x" ) && ( $invalue{$i+3} != '0' ) ) {
+ if ( ( substr( $invalue, $i, 3 ) == "&#x" ) && ( $invalue{$i+3} != '0' ) ) {
$i += 3;
$hexstring = "";
do {
@@ -2221,7 +2325,7 @@ END
// Do some sanity checks. These aren't needed for reversability,
// but should help keep the breakage down if the editor
// breaks one of the entities whilst editing.
- if ((substr($invalue,$i,1)==";") and (strlen($hexstring) <= 6)) {
+ if ( (substr($invalue,$i,1)==";") and (strlen($hexstring) <= 6) ) {
$codepoint = hexdec($hexstring);
$result .= codepointToUtf8( $codepoint );
} else {
@@ -2251,16 +2355,30 @@ END
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">' );
+ $count = $pager->getNumRows();
+ if ( $count > 0 ) {
+ $pager->mLimit = 10;
+ $out->addHTML( '<div class="mw-warning-with-logexcerpt">' );
$out->addWikiMsg( 'recreate-deleted-warn' );
$out->addHTML(
$loglist->beginLogEventsList() .
$pager->getBody() .
$loglist->endLogEventsList()
);
- $out->addHtml( '</div>' );
+ if($count > 10){
+ $out->addHTML( $wgUser->getSkin()->link(
+ SpecialPage::getTitleFor( 'Log' ),
+ wfMsgHtml( 'deletelog-fulllog' ),
+ array(),
+ array(
+ 'type' => 'delete',
+ 'page' => $this->mTitle->getPrefixedText() ) ) );
+ }
+ $out->addHTML( '</div>' );
+ return true;
}
+
+ return false;
}
/**
@@ -2273,7 +2391,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 ) {
+ if ( $value == self::AS_SUCCESS_UPDATE || $value == self::AS_SUCCESS_NEW_ARTICLE ) {
$this->didSave = true;
}
@@ -2334,7 +2452,7 @@ END
}
function getBaseRevision() {
- if ($this->mBaseRevision == false) {
+ if ( $this->mBaseRevision == false ) {
$db = wfGetDB( DB_MASTER );
$baseRevision = Revision::loadFromTimestamp(
$db, $this->mTitle, $this->edittime );
diff --git a/includes/Exception.php b/includes/Exception.php
index ab25f0b8..eb715986 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -83,7 +83,7 @@ class MWException extends Exception {
function getHTML() {
global $wgShowExceptionDetails;
if( $wgShowExceptionDetails ) {
- return '<p>' . htmlspecialchars( $this->getMessage() ) .
+ return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) .
'</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
"</p>\n";
} else {
@@ -129,7 +129,16 @@ class MWException extends Exception {
$file = $this->getFile();
$line = $this->getLine();
$message = $this->getMessage();
- return $wgRequest->getRequestURL() . " Exception from line $line of $file: $message";
+ if ( isset( $wgRequest ) ) {
+ $url = $wgRequest->getRequestURL();
+ if ( !$url ) {
+ $url = '[no URL]';
+ }
+ } else {
+ $url = '[no req]';
+ }
+
+ return "$url Exception from line $line of $file: $message";
}
/** Output the exception report using HTML */
@@ -137,7 +146,7 @@ class MWException extends Exception {
global $wgOut;
if ( $this->useOutputPage() ) {
$wgOut->setPageTitle( $this->getPageTitle() );
- $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setRobotPolicy( "noindex,nofollow" );
$wgOut->setArticleRelated( false );
$wgOut->enableClientCache( false );
$wgOut->redirect( '' );
@@ -169,7 +178,7 @@ class MWException extends Exception {
wfDebugLog( 'exception', $log );
}
if ( $wgCommandLineMode ) {
- fwrite( STDERR, $this->getText() );
+ wfPrintError( $this->getText() );
} else {
$this->reportHTML();
}
@@ -268,7 +277,7 @@ function wfReportException( Exception $e ) {
$e2->__toString() . "\n";
if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) {
- fwrite( STDERR, $message );
+ wfPrintError( $message );
} else {
echo nl2br( htmlspecialchars( $message ) ). "\n";
}
@@ -288,6 +297,21 @@ function wfReportException( Exception $e ) {
}
/**
+ * Print a message, if possible to STDERR.
+ * Use this in command line mode only (see wgCommandLineMode)
+ */
+function wfPrintError( $message ) {
+ #NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602).
+ # Try to produce meaningful output anyway. Using echo may corrupt output to STDOUT though.
+ if ( defined( 'STDERR' ) ) {
+ fwrite( STDERR, $message );
+ }
+ else {
+ echo( $message );
+ }
+}
+
+/**
* Exception handler which simulates the appropriate catch() handling:
*
* try {
diff --git a/includes/Exif.php b/includes/Exif.php
index bd93eb76..d5cf09cf 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -48,7 +48,7 @@ class Exif {
/**
* Exif tags grouped by category, the tagname itself is the key and the type
* is the value, in the case of more than one possible value type they are
- * seperated by commas.
+ * separated by commas.
*/
var $mExifTags;
@@ -780,7 +780,28 @@ class FormatExif {
}
break;
- // TODO: Flash
+ case 'Flash':
+ $flashDecode = array(
+ 'fired' => $val & bindec( '00000001' ),
+ 'return' => ($val & bindec( '00000110' )) >> 1,
+ 'mode' => ($val & bindec( '00011000' )) >> 3,
+ 'function' => ($val & bindec( '00100000' )) >> 5,
+ 'redeye' => ($val & bindec( '01000000' )) >> 6,
+// 'reserved' => ($val & bindec( '10000000' )) >> 7,
+ );
+
+ # We do not need to handle unknown values since all are used.
+ foreach( $flashDecode as $subTag => $subValue ) {
+ # We do not need any message for zeroed values.
+ if( $subTag != 'fired' && $subValue == 0) {
+ continue;
+ }
+ $fullTag = $tag . '-' . $subTag ;
+ $flashMsgs[] = $this->msg( $fullTag, $subValue );
+ }
+ $tags[$tag] = $wgLang->commaList( $flashMsgs );
+ break;
+
case 'FocalPlaneResolutionUnit':
switch( $val ) {
case 2:
diff --git a/includes/Export.php b/includes/Export.php
index 7d0a824e..5f040b13 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -32,6 +32,7 @@ class WikiExporter {
const FULL = 0;
const CURRENT = 1;
+ const LOGS = 2;
const BUFFER = 0;
const STREAM = 1;
@@ -71,16 +72,16 @@ class WikiExporter {
*
* @param $sink mixed
*/
- function setOutputSink( &$sink ) {
+ public function setOutputSink( &$sink ) {
$this->sink =& $sink;
}
- function openStream() {
+ public function openStream() {
$output = $this->writer->openStream();
$this->sink->writeOpenStream( $output );
}
- function closeStream() {
+ public function closeStream() {
$output = $this->writer->closeStream();
$this->sink->writeCloseStream( $output );
}
@@ -90,7 +91,7 @@ class WikiExporter {
* in the database, either including complete history or only
* the most recent version.
*/
- function allPages() {
+ public function allPages() {
return $this->dumpFrom( '' );
}
@@ -101,7 +102,7 @@ class WikiExporter {
* @param $end Int: Exclusive upper limit (this id is not included)
* If 0, no upper limit.
*/
- function pagesByRange( $start, $end ) {
+ public function pagesByRange( $start, $end ) {
$condition = 'page_id >= ' . intval( $start );
if( $end ) {
$condition .= ' AND page_id < ' . intval( $end );
@@ -112,13 +113,13 @@ class WikiExporter {
/**
* @param $title Title
*/
- function pageByTitle( $title ) {
+ public function pageByTitle( $title ) {
return $this->dumpFrom(
'page_namespace=' . $title->getNamespace() .
' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) );
}
- function pageByName( $name ) {
+ public function pageByName( $name ) {
$title = Title::newFromText( $name );
if( is_null( $title ) ) {
return new WikiError( "Can't export invalid title" );
@@ -127,26 +128,36 @@ class WikiExporter {
}
}
- function pagesByName( $names ) {
+ public function pagesByName( $names ) {
foreach( $names as $name ) {
$this->pageByName( $name );
}
}
+ public function allLogs() {
+ return $this->dumpFrom( '' );
+ }
- // -------------------- private implementation below --------------------
+ public function logsByRange( $start, $end ) {
+ $condition = 'log_id >= ' . intval( $start );
+ if( $end ) {
+ $condition .= ' AND log_id < ' . intval( $end );
+ }
+ return $this->dumpFrom( $condition );
+ }
# Generates the distinct list of authors of an article
# Not called by default (depends on $this->list_authors)
# Can be set by Special:Export when not exporting whole history
- function do_list_authors ( $page , $revision , $cond ) {
+ protected function do_list_authors( $page , $revision , $cond ) {
$fname = "do_list_authors" ;
wfProfileIn( $fname );
$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 ;
+ $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() ) {
@@ -163,87 +174,101 @@ class WikiExporter {
$this->author_list .= "</contributors>";
}
- function dumpFrom( $cond = '' ) {
+ protected function dumpFrom( $cond = '' ) {
$fname = 'WikiExporter::dumpFrom';
wfProfileIn( $fname );
+
+ # For logs dumps...
+ if( $this->history & self::LOGS ) {
+ $where = array( 'user_id = log_user' );
+ # Hide private logs
+ $where[] = LogEventsList::getExcludeClause( $this->db );
+ if( $cond ) $where[] = $cond;
+ $result = $this->db->select( array('logging','user'),
+ '*',
+ $where,
+ $fname,
+ array( 'ORDER BY' => 'log_id', 'USE INDEX' => array('logging' => 'PRIMARY') )
+ );
+ $wrapper = $this->db->resultObject( $result );
+ $this->outputLogStream( $wrapper );
+ # For page dumps...
+ } else {
+ list($page,$revision,$text) = $this->db->tableNamesN('page','revision','text');
- $page = $this->db->tableName( 'page' );
- $revision = $this->db->tableName( 'revision' );
- $text = $this->db->tableName( 'text' );
-
- $order = 'ORDER BY page_id';
- $limit = '';
+ $order = 'ORDER BY page_id';
+ $limit = '';
- if( $this->history == WikiExporter::FULL ) {
- $join = 'page_id=rev_page';
- } elseif( $this->history == WikiExporter::CURRENT ) {
- if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
- $this->do_list_authors ( $page , $revision , $cond );
- }
- $join = 'page_id=rev_page AND page_latest=rev_id';
- } elseif ( is_array( $this->history ) ) {
- $join = 'page_id=rev_page';
- if ( $this->history['dir'] == 'asc' ) {
- $op = '>';
- $order .= ', rev_timestamp';
+ if( $this->history == WikiExporter::FULL ) {
+ $join = 'page_id=rev_page';
+ } elseif( $this->history == WikiExporter::CURRENT ) {
+ if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
+ $this->do_list_authors ( $page , $revision , $cond );
+ }
+ $join = 'page_id=rev_page AND page_latest=rev_id';
+ } elseif ( is_array( $this->history ) ) {
+ $join = 'page_id=rev_page';
+ if ( $this->history['dir'] == 'asc' ) {
+ $op = '>';
+ $order .= ', rev_timestamp';
+ } else {
+ $op = '<';
+ $order .= ', rev_timestamp DESC';
+ }
+ if ( !empty( $this->history['offset'] ) ) {
+ $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
+ $this->db->timestamp( $this->history['offset'] ) );
+ }
+ if ( !empty( $this->history['limit'] ) ) {
+ $limitNum = intval( $this->history['limit'] );
+ if ( $limitNum > 0 ) {
+ $limit = "LIMIT $limitNum";
+ }
+ }
} else {
- $op = '<';
- $order .= ', rev_timestamp DESC';
+ wfProfileOut( $fname );
+ return new WikiError( "$fname given invalid history dump type." );
}
- if ( !empty( $this->history['offset'] ) ) {
- $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
- $this->db->timestamp( $this->history['offset'] ) );
+ $where = ( $cond == '' ) ? '' : "$cond AND";
+
+ if( $this->buffer == WikiExporter::STREAM ) {
+ $prev = $this->db->bufferResults( false );
}
- if ( !empty( $this->history['limit'] ) ) {
- $limitNum = intval( $this->history['limit'] );
- if ( $limitNum > 0 ) {
- $limit = "LIMIT $limitNum";
- }
+ if( $cond == '' ) {
+ // Optimization hack for full-database dump
+ $revindex = $pageindex = $this->db->useIndexClause("PRIMARY");
+ $straight = ' /*! STRAIGHT_JOIN */ ';
+ } else {
+ $pageindex = '';
+ $revindex = '';
+ $straight = '';
}
- } else {
- wfProfileOut( $fname );
- return new WikiError( "$fname given invalid history dump type." );
- }
- $where = ( $cond == '' ) ? '' : "$cond AND";
-
- if( $this->buffer == WikiExporter::STREAM ) {
- $prev = $this->db->bufferResults( false );
- }
- if( $cond == '' ) {
- // Optimization hack for full-database dump
- $revindex = $pageindex = $this->db->useIndexClause("PRIMARY");
- $straight = ' /*! STRAIGHT_JOIN */ ';
- } else {
- $pageindex = '';
- $revindex = '';
- $straight = '';
- }
- if( $this->text == WikiExporter::STUB ) {
- $sql = "SELECT $straight * FROM
+ if( $this->text == WikiExporter::STUB ) {
+ $sql = "SELECT $straight * FROM
$page $pageindex,
$revision $revindex
WHERE $where $join
$order $limit";
- } else {
- $sql = "SELECT $straight * FROM
+ } else {
+ $sql = "SELECT $straight * FROM
$page $pageindex,
$revision $revindex,
$text
WHERE $where $join AND rev_text_id=old_id
$order $limit";
- }
- $result = $this->db->query( $sql, $fname );
- $wrapper = $this->db->resultObject( $result );
- $this->outputStream( $wrapper );
+ }
+ $result = $this->db->query( $sql, $fname );
+ $wrapper = $this->db->resultObject( $result );
+ $this->outputPageStream( $wrapper );
- if ( $this->list_authors ) {
- $this->outputStream( $wrapper );
- }
+ if ( $this->list_authors ) {
+ $this->outputPageStream( $wrapper );
+ }
- if( $this->buffer == WikiExporter::STREAM ) {
- $this->db->bufferResults( $prev );
+ if( $this->buffer == WikiExporter::STREAM ) {
+ $this->db->bufferResults( $prev );
+ }
}
-
wfProfileOut( $fname );
}
@@ -258,9 +283,8 @@ class WikiExporter {
* blob storage types will make queries to pull source data.
*
* @param $resultset ResultWrapper
- * @access private
*/
- function outputStream( $resultset ) {
+ protected function outputPageStream( $resultset ) {
$last = null;
while( $row = $resultset->fetchObject() ) {
if( is_null( $last ) ||
@@ -292,6 +316,14 @@ class WikiExporter {
}
$resultset->free();
}
+
+ protected function outputLogStream( $resultset ) {
+ while( $row = $resultset->fetchObject() ) {
+ $output = $this->writer->writeLogItem( $row );
+ $this->sink->writeLogItem( $row, $output );
+ }
+ $resultset->free();
+ }
}
/**
@@ -320,7 +352,7 @@ class XmlDumpWriter {
function openStream() {
global $wgContLanguageCode;
$ver = $this->schemaVersion();
- return wfElement( 'mediawiki', array(
+ return Xml::element( 'mediawiki', array(
'xmlns' => "http://www.mediawiki.org/xml/export-$ver/",
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
@@ -346,30 +378,30 @@ class XmlDumpWriter {
function sitename() {
global $wgSitename;
- return wfElement( 'sitename', array(), $wgSitename );
+ return Xml::element( 'sitename', array(), $wgSitename );
}
function generator() {
global $wgVersion;
- return wfElement( 'generator', array(), "MediaWiki $wgVersion" );
+ return Xml::element( 'generator', array(), "MediaWiki $wgVersion" );
}
function homelink() {
- return wfElement( 'base', array(), Title::newMainPage()->getFullUrl() );
+ return Xml::element( 'base', array(), Title::newMainPage()->getFullUrl() );
}
function caseSetting() {
global $wgCapitalLinks;
// "case-insensitive" option is reserved for future
$sensitivity = $wgCapitalLinks ? 'first-letter' : 'case-sensitive';
- return wfElement( 'case', array(), $sensitivity );
+ return Xml::element( 'case', array(), $sensitivity );
}
function namespaces() {
global $wgContLang;
$spaces = " <namespaces>\n";
foreach( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
- $spaces .= ' ' . wfElement( 'namespace', array( 'key' => $ns ), $title ) . "\n";
+ $spaces .= ' ' . Xml::element( 'namespace', array( 'key' => $ns ), $title ) . "\n";
}
$spaces .= " </namespaces>";
return $spaces;
@@ -395,10 +427,10 @@ class XmlDumpWriter {
function openPage( $row ) {
$out = " <page>\n";
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $out .= ' ' . wfElementClean( 'title', array(), $title->getPrefixedText() ) . "\n";
- $out .= ' ' . wfElement( 'id', array(), strval( $row->page_id ) ) . "\n";
+ $out .= ' ' . Xml::elementClean( 'title', array(), $title->getPrefixedText() ) . "\n";
+ $out .= ' ' . Xml::element( 'id', array(), strval( $row->page_id ) ) . "\n";
if( '' != $row->page_restrictions ) {
- $out .= ' ' . wfElement( 'restrictions', array(),
+ $out .= ' ' . Xml::element( 'restrictions', array(),
strval( $row->page_restrictions ) ) . "\n";
}
return $out;
@@ -426,12 +458,12 @@ class XmlDumpWriter {
wfProfileIn( $fname );
$out = " <revision>\n";
- $out .= " " . wfElement( 'id', null, strval( $row->rev_id ) ) . "\n";
+ $out .= " " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n";
$out .= $this->writeTimestamp( $row->rev_timestamp );
if( $row->rev_deleted & Revision::DELETED_USER ) {
- $out .= " " . wfElement( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
+ $out .= " " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
} else {
$out .= $this->writeContributor( $row->rev_user, $row->rev_user_text );
}
@@ -440,22 +472,22 @@ class XmlDumpWriter {
$out .= " <minor/>\n";
}
if( $row->rev_deleted & Revision::DELETED_COMMENT ) {
- $out .= " " . wfElement( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+ $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
} elseif( $row->rev_comment != '' ) {
- $out .= " " . wfElementClean( 'comment', null, strval( $row->rev_comment ) ) . "\n";
+ $out .= " " . Xml::elementClean( 'comment', null, strval( $row->rev_comment ) ) . "\n";
}
if( $row->rev_deleted & Revision::DELETED_TEXT ) {
- $out .= " " . wfElement( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+ $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
} elseif( isset( $row->old_text ) ) {
// Raw text from the database may have invalid chars
$text = strval( Revision::getRevisionText( $row ) );
- $out .= " " . wfElementClean( 'text',
+ $out .= " " . Xml::elementClean( 'text',
array( 'xml:space' => 'preserve' ),
strval( $text ) ) . "\n";
} else {
// Stub output
- $out .= " " . wfElement( 'text',
+ $out .= " " . Xml::element( 'text',
array( 'id' => $row->rev_text_id ),
"" ) . "\n";
}
@@ -465,19 +497,67 @@ class XmlDumpWriter {
wfProfileOut( $fname );
return $out;
}
+
+ /**
+ * Dumps a <logitem> section on the output stream, with
+ * data filled in from the given database row.
+ *
+ * @param $row object
+ * @return string
+ * @access private
+ */
+ function writeLogItem( $row ) {
+ $fname = 'WikiExporter::writeLogItem';
+ wfProfileIn( $fname );
+
+ $out = " <logitem>\n";
+ $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
+
+ $out .= $this->writeTimestamp( $row->log_timestamp );
+
+ if( $row->log_deleted & LogPage::DELETED_USER ) {
+ $out .= " " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
+ } else {
+ $out .= $this->writeContributor( $row->log_user, $row->user_name );
+ }
+
+ if( $row->log_deleted & LogPage::DELETED_COMMENT ) {
+ $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+ } elseif( $row->log_comment != '' ) {
+ $out .= " " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n";
+ }
+
+ $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
+ $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
+
+ if( $row->log_deleted & LogPage::DELETED_ACTION ) {
+ $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+ } else {
+ $title = Title::makeTitle( $row->log_namespace, $row->log_title );
+ $out .= " " . Xml::elementClean( 'logtitle', null, $title->getPrefixedText() ) . "\n";
+ $out .= " " . Xml::elementClean( 'params',
+ array( 'xml:space' => 'preserve' ),
+ strval( $row->log_params ) ) . "\n";
+ }
+
+ $out .= " </logitem>\n";
+
+ wfProfileOut( $fname );
+ return $out;
+ }
function writeTimestamp( $timestamp ) {
$ts = wfTimestamp( TS_ISO_8601, $timestamp );
- return " " . wfElement( 'timestamp', null, $ts ) . "\n";
+ return " " . Xml::element( '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";
+ $out .= " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
+ $out .= " " . Xml::element( 'id', null, strval( $id ) ) . "\n";
} else {
- $out .= " " . wfElementClean( 'ip', null, strval( $text ) ) . "\n";
+ $out .= " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
}
$out .= " </contributor>\n";
return $out;
@@ -505,10 +585,10 @@ class XmlDumpWriter {
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" .
+ " " . Xml::elementClean( 'comment', null, $file->getDescription() ) . "\n" .
+ " " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
+ " " . Xml::element( 'src', null, $file->getFullUrl() ) . "\n" .
+ " " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
" </upload>\n";
}
@@ -539,6 +619,10 @@ class DumpOutput {
function writeRevision( $rev, $string ) {
$this->write( $string );
}
+
+ function writeLogItem( $rev, $string ) {
+ $this->write( $string );
+ }
/**
* Override to write to a different stream type.
@@ -654,6 +738,10 @@ class DumpFilter {
$this->sink->writeRevision( $rev, $string );
}
}
+
+ function writeLogItem( $rev, $string ) {
+ $this->sink->writeRevision( $rev, $string );
+ }
/**
* Override for page-based filter types.
@@ -692,7 +780,9 @@ class DumpNamespaceFilter extends DumpFilter {
"NS_USER_TALK" => NS_USER_TALK,
"NS_PROJECT" => NS_PROJECT,
"NS_PROJECT_TALK" => NS_PROJECT_TALK,
- "NS_IMAGE" => NS_IMAGE,
+ "NS_FILE" => NS_FILE,
+ "NS_FILE_TALK" => NS_FILE_TALK,
+ "NS_IMAGE" => NS_IMAGE, // NS_IMAGE is an alias for NS_FILE
"NS_IMAGE_TALK" => NS_IMAGE_TALK,
"NS_MEDIAWIKI" => NS_MEDIAWIKI,
"NS_MEDIAWIKI_TALK" => NS_MEDIAWIKI_TALK,
diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php
index e2b78566..d095aba0 100644
--- a/includes/ExternalStore.php
+++ b/includes/ExternalStore.php
@@ -14,7 +14,7 @@
*/
class ExternalStore {
/* Fetch data from given URL */
- static function fetchFromURL($url) {
+ static function fetchFromURL( $url ) {
global $wgExternalStores;
if( !$wgExternalStores )
@@ -44,7 +44,7 @@ class ExternalStore {
$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;
}
@@ -66,4 +66,47 @@ class ExternalStore {
return $store->store( $params, $data );
}
}
+
+ /**
+ * Like insert() above, but does more of the work for us.
+ * This function does not need a url param, it builds it by
+ * itself. It also fails-over to the next possible clusters.
+ *
+ * @param string $data
+ * Returns the URL of the stored data item, or false on error
+ */
+ public static function insertToDefault( $data ) {
+ global $wgDefaultExternalStore;
+ $tryStores = (array)$wgDefaultExternalStore;
+ $error = false;
+ while ( count( $tryStores ) > 0 ) {
+ $index = mt_rand(0, count( $tryStores ) - 1);
+ $storeUrl = $tryStores[$index];
+ wfDebug( __METHOD__.": trying $storeUrl\n" );
+ list( $proto, $params ) = explode( '://', $storeUrl, 2 );
+ $store = self::getStoreObject( $proto );
+ if ( $store === false ) {
+ throw new MWException( "Invalid external storage protocol - $storeUrl" );
+ }
+ try {
+ $url = $store->store( $params, $data ); // Try to save the object
+ } catch ( DBConnectionError $error ) {
+ $url = false;
+ }
+ if ( $url ) {
+ return $url; // Done!
+ } else {
+ unset( $tryStores[$index] ); // Don't try this one again!
+ $tryStores = array_values( $tryStores ); // Must have consecutive keys
+ wfDebugLog( 'ExternalStorage', "Unable to store text to external storage $storeUrl" );
+ }
+ }
+ // All stores failed
+ if ( $error ) {
+ // Rethrow the last connection error
+ throw $error;
+ } else {
+ throw new MWException( "Unable to store text to external storage" );
+ }
+ }
}
diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php
index 549412d1..9fa7d1b1 100644
--- a/includes/ExternalStoreDB.php
+++ b/includes/ExternalStoreDB.php
@@ -56,7 +56,7 @@ class ExternalStoreDB {
* Fetch data from given URL
* @param string $url An url of the form DB://cluster/id or DB://cluster/id/itemid for concatened storage.
*/
- function fetchFromURL($url) {
+ function fetchFromURL( $url ) {
$path = explode( '/', $url );
$cluster = $path[2];
$id = $path[3];
@@ -122,12 +122,11 @@ class ExternalStoreDB {
* @return string URL
*/
function store( $cluster, $data ) {
- $fname = 'ExternalStoreDB::store';
-
- $dbw =& $this->getMaster( $cluster );
-
+ $dbw = $this->getMaster( $cluster );
$id = $dbw->nextSequenceValue( 'blob_blob_id_seq' );
- $dbw->insert( $this->getTable( $dbw ), array( 'blob_id' => $id, 'blob_text' => $data ), $fname );
+ $dbw->insert( $this->getTable( $dbw ),
+ array( 'blob_id' => $id, 'blob_text' => $data ),
+ __METHOD__ );
$id = $dbw->insertId();
if ( $dbw->getFlag( DBO_TRX ) ) {
$dbw->immediateCommit();
diff --git a/includes/FakeTitle.php b/includes/FakeTitle.php
index 4c2eddc8..10bfa538 100644
--- a/includes/FakeTitle.php
+++ b/includes/FakeTitle.php
@@ -3,15 +3,13 @@
/**
* Fake title class that triggers an error if any members are called
*/
-class FakeTitle {
+class FakeTitle extends Title {
function error() { throw new MWException( "Attempt to call member function of FakeTitle\n" ); }
// PHP 5.1 method overload
function __call( $name, $args ) { $this->error(); }
// PHP <5.1 compatibility
- function getInterwikiLink() { $this->error(); }
- function getInterwikiCached() { $this->error(); }
function isLocal() { $this->error(); }
function isTrans() { $this->error(); }
function getText() { $this->error(); }
@@ -28,20 +26,20 @@ class FakeTitle {
function getPrefixedText() { $this->error(); }
function getFullText() { $this->error(); }
function getPrefixedURL() { $this->error(); }
- function getFullURL() {$this->error(); }
- function getLocalURL() { $this->error(); }
- function escapeLocalURL() { $this->error(); }
- function escapeFullURL() { $this->error(); }
- function getInternalURL() { $this->error(); }
+ function getFullURL( $query = '', $variant = false ) {$this->error(); }
+ function getLocalURL( $query = '', $variant = false ) { $this->error(); }
+ function escapeLocalURL( $query = '' ) { $this->error(); }
+ function escapeFullURL( $query = '' ) { $this->error(); }
+ function getInternalURL( $query = '', $variant = false ) { $this->error(); }
function getEditURL() { $this->error(); }
function getEscapedText() { $this->error(); }
function isExternal() { $this->error(); }
- function isSemiProtected() { $this->error(); }
- function isProtected() { $this->error(); }
+ function isSemiProtected( $action = 'edit' ) { $this->error(); }
+ function isProtected( $action = '' ) { $this->error(); }
function userIsWatching() { $this->error(); }
- function userCan() { $this->error(); }
+ function userCan( $action, $doExpensiveQueries = true ) { $this->error(); }
function userCanCreate() { $this->error(); }
- function userCanEdit() { $this->error(); }
+ function userCanEdit( $doExpensiveQueries = true ) { $this->error(); }
function userCanMove() { $this->error(); }
function isMovable() { $this->error(); }
function userCanRead() { $this->error(); }
@@ -79,6 +77,7 @@ class FakeTitle {
function equals() { $this->error(); }
function exists() { $this->error(); }
function isAlwaysKnown() { $this->error(); }
+ function isKnown() { $this->error(); }
function touchLinks() { $this->error(); }
function trackbackURL() { $this->error(); }
function trackbackRDF() { $this->error(); }
diff --git a/includes/Feed.php b/includes/Feed.php
index 512057d9..fe6d8feb 100644
--- a/includes/Feed.php
+++ b/includes/Feed.php
@@ -52,25 +52,45 @@ class FeedItem {
$this->Comments = $Comments;
}
- /**
- * @static
- */
- function xmlEncode( $string ) {
+ public function xmlEncode( $string ) {
$string = str_replace( "\r\n", "\n", $string );
$string = preg_replace( '/[\x00-\x08\x0b\x0c\x0e-\x1f]/', '', $string );
return htmlspecialchars( $string );
}
- function getTitle() { return $this->xmlEncode( $this->Title ); }
- function getUrl() { return $this->xmlEncode( $this->Url ); }
- function getDescription() { return $this->xmlEncode( $this->Description ); }
- function getLanguage() {
+ public function getTitle() {
+ return $this->xmlEncode( $this->Title );
+ }
+
+ public function getUrl() {
+ return $this->xmlEncode( $this->Url );
+ }
+
+ public function getDescription() {
+ return $this->xmlEncode( $this->Description );
+ }
+
+ public function getLanguage() {
global $wgContLanguageCode;
return $wgContLanguageCode;
}
- function getDate() { return $this->Date; }
- function getAuthor() { return $this->xmlEncode( $this->Author ); }
- function getComments() { return $this->xmlEncode( $this->Comments ); }
+
+ public function getDate() {
+ return $this->Date;
+ }
+ public function getAuthor() {
+ return $this->xmlEncode( $this->Author );
+ }
+ public function getComments() {
+ return $this->xmlEncode( $this->Comments );
+ }
+
+ /**
+ * Quickie hack... strip out wikilinks to more legible form from the comment.
+ */
+ public static function stripComment( $text ) {
+ return preg_replace( '/\[\[([^]]*\|)?([^]]+)\]\]/', '\2', $text );
+ }
/**#@-*/
}
@@ -149,7 +169,7 @@ class ChannelFeed extends FeedItem {
global $wgStylePath, $wgStyleVersion;
$this->httpHeaders();
- echo '<?xml version="1.0" encoding="utf-8"?>' . "\n";
+ echo '<?xml version="1.0"?>' . "\n";
echo '<?xml-stylesheet type="text/css" href="' .
htmlspecialchars( wfExpandUrl( "$wgStylePath/common/feed.css?$wgStyleVersion" ) ) .
'"?' . ">\n";
diff --git a/includes/FeedUtils.php b/includes/FeedUtils.php
index aa784c02..38bff363 100644
--- a/includes/FeedUtils.php
+++ b/includes/FeedUtils.php
@@ -75,17 +75,20 @@ class FeedUtils {
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 ) ) );
-
+
+ // Don't bother generating the diff if we won't be able to show it
+ if ( $wgFeedDiffCutoff > 0 ) {
+ $de = new DifferenceEngine( $title, $oldid, $newid );
+ $diffText = $de->getDiff(
+ wfMsg( 'previousrevision' ), // hack
+ wfMsg( 'revisionasof',
+ $wgContLang->timeanddate( $timestamp ) ) );
+ }
- if ( strlen( $diffText ) > $wgFeedDiffCutoff ) {
+ if ( ( strlen( $diffText ) > $wgFeedDiffCutoff ) || ( $wgFeedDiffCutoff <= 0 ) ) {
// Omit large diffs
$diffLink = $title->escapeFullUrl(
'diff=' . $newid .
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
index bc80c2b2..66086b0f 100644
--- a/includes/FileDeleteForm.php
+++ b/includes/FileDeleteForm.php
@@ -55,7 +55,7 @@ class FileDeleteForm {
$this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
if( !self::haveDeletableFile($this->file, $this->oldfile, $this->oldimage) ) {
- $wgOut->addHtml( $this->prepareMessage( 'filedelete-nofile' ) );
+ $wgOut->addHTML( $this->prepareMessage( 'filedelete-nofile' ) );
$wgOut->addReturnTo( $this->title );
return;
}
@@ -78,7 +78,7 @@ class FileDeleteForm {
$wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) );
if( $status->ok ) {
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->addHtml( $this->prepareMessage( 'filedelete-success' ) );
+ $wgOut->addHTML( $this->prepareMessage( 'filedelete-success' ) );
// Return to the main page if we just deleted all versions of the
// file, otherwise go back to the description page
$wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() );
@@ -105,16 +105,24 @@ class FileDeleteForm {
} else {
$status = $file->delete( $reason, $suppress );
if( $status->ok ) {
+ $id = $title->getArticleID( GAID_FOR_UPDATE );
// 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( $article->doDeleteArticle( $reason, $suppress, $id ) ) {
+ global $wgRequest;
+ if( $wgRequest->getCheck( 'wpWatch' ) ) {
+ $article->doWatch();
+ } elseif( $title->userIsWatching() ) {
+ $article->doUnwatch();
+ }
+ wfRunHooks('ArticleDeleteComplete', array(&$article, &$wgUser, $reason, $id));
+ }
}
}
}
- if( $status->isGood() ) wfRunHooks('FileDeleteComplete', array(
- &$file, &$oldimage, &$article, &$wgUser, &$reason));
+ if( $status->isGood() )
+ wfRunHooks('FileDeleteComplete', array( &$file, &$oldimage, &$article, &$wgUser, &$reason));
return $status;
}
@@ -123,46 +131,60 @@ class FileDeleteForm {
* Show the confirmation form
*/
private function showForm() {
- global $wgOut, $wgUser, $wgRequest, $wgContLang;
- $align = $wgContLang->isRtl() ? 'left' : 'right';
+ global $wgOut, $wgUser, $wgRequest;
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>";
+ $suppress = "<tr id=\"wpDeleteSuppressRow\">
+ <td></td>
+ <td class='mw-input'>" .
+ Xml::checkLabel( wfMsg( 'revdelete-suppress' ),
+ 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '3' ) ) .
+ "</td>
+ </tr>";
} else {
$suppress = '';
}
- $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) ) .
+ $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $this->title->userIsWatching();
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction(),
+ 'id' => 'mw-img-deleteconfirm' ) ) .
Xml::openElement( 'fieldset' ) .
Xml::element( 'legend', null, wfMsg( 'filedelete-legend' ) ) .
Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->oldimage ) ) .
$this->prepareMessage( 'filedelete-intro' ) .
- Xml::openElement( 'table' ) .
+ Xml::openElement( 'table', array( 'id' => 'mw-img-deleteconfirm-table' ) ) .
"<tr>
- <td align='$align'>" .
+ <td class='mw-label'>" .
Xml::label( wfMsg( 'filedelete-comment' ), 'wpDeleteReasonList' ) .
"</td>
- <td>" .
+ <td class='mw-input'>" .
Xml::listDropDown( 'wpDeleteReasonList',
wfMsgForContent( 'filedelete-reason-dropdown' ),
wfMsgForContent( 'filedelete-reason-otherlist' ), '', 'wpReasonDropDown', 1 ) .
"</td>
</tr>
<tr>
- <td align='$align'>" .
+ <td class='mw-label'>" .
Xml::label( wfMsg( 'filedelete-otherreason' ), 'wpReason' ) .
"</td>
- <td>" .
- Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ), array( 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ) ) .
+ <td class='mw-input'>" .
+ Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ),
+ array( 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ) ) .
"</td>
</tr>
{$suppress}
<tr>
<td></td>
- <td>" .
- Xml::submitButton( wfMsg( 'filedelete-submit' ), array( 'name' => 'mw-filedelete-submit', 'id' => 'mw-filedelete-submit', 'tabindex' => '3' ) ) .
+ <td class='mw-input'>" .
+ Xml::checkLabel( wfMsg( 'watchthis' ),
+ 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
+ "</td>
+ </tr>
+ <tr>
+ <td></td>
+ <td class='mw-submit'>" .
+ Xml::submitButton( wfMsg( 'filedelete-submit' ),
+ array( 'name' => 'mw-filedelete-submit', 'id' => 'mw-filedelete-submit', 'tabindex' => '4' ) ) .
"</td>
</tr>" .
Xml::closeElement( 'table' ) .
@@ -175,7 +197,7 @@ class FileDeleteForm {
$form .= '<p class="mw-filedelete-editreasons">' . $link . '</p>';
}
- $wgOut->addHtml( $form );
+ $wgOut->addHTML( $form );
}
/**
@@ -183,7 +205,7 @@ class FileDeleteForm {
*/
private function showLogEntries() {
global $wgOut;
- $wgOut->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ $wgOut->addHTML( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
LogEventsList::showLogExtract( $wgOut, 'delete', $this->title->getPrefixedText() );
}
diff --git a/includes/FileRevertForm.php b/includes/FileRevertForm.php
index 385d83bc..c7c73246 100644
--- a/includes/FileRevertForm.php
+++ b/includes/FileRevertForm.php
@@ -57,7 +57,7 @@ class FileRevertForm {
}
if( !$this->haveOldVersion() ) {
- $wgOut->addHtml( wfMsgExt( 'filerevert-badversion', 'parse' ) );
+ $wgOut->addHTML( wfMsgExt( 'filerevert-badversion', 'parse' ) );
$wgOut->returnToMain( false, $this->title );
return;
}
@@ -69,7 +69,7 @@ class FileRevertForm {
// TODO: Preserve file properties from database instead of reloading from file
$status = $this->file->upload( $source, $comment, $comment );
if( $status->isGood() ) {
- $wgOut->addHtml( wfMsgExt( 'filerevert-success', 'parse', $this->title->getText(),
+ $wgOut->addHTML( wfMsgExt( 'filerevert-success', 'parse', $this->title->getText(),
$wgLang->date( $this->getTimestamp(), true ),
$wgLang->time( $this->getTimestamp(), true ),
wfExpandUrl( $this->file->getArchiveUrl( $this->archiveName ) ) ) );
@@ -104,7 +104,7 @@ class FileRevertForm {
$form .= '</fieldset>';
$form .= '</form>';
- $wgOut->addHtml( $form );
+ $wgOut->addHTML( $form );
}
/**
diff --git a/includes/FileStore.php b/includes/FileStore.php
index c01350c0..278777b4 100644
--- a/includes/FileStore.php
+++ b/includes/FileStore.php
@@ -35,39 +35,22 @@ class FileStore {
* This is attached to your master database connection, so if you
* suffer an uncaught error the lock will be released when the
* connection is closed.
- *
- * @todo Probably only works on MySQL. Abstract to the Database class?
+ * @see Database::lock()
*/
static function lock() {
- global $wgDBtype;
- if ($wgDBtype != 'mysql')
- return true;
$dbw = wfGetDB( DB_MASTER );
$lockname = $dbw->addQuotes( FileStore::lockName() );
- $result = $dbw->query( "SELECT GET_LOCK($lockname, 5) AS lockstatus", __METHOD__ );
- $row = $dbw->fetchObject( $result );
- $dbw->freeResult( $result );
-
- if( $row->lockstatus == 1 ) {
- return true;
- } else {
- wfDebug( __METHOD__." failed to acquire lock\n" );
- return false;
- }
+ return $dbw->lock( $lockname, __METHOD__ );
}
/**
* Release the global file store lock.
+ * @see Database::unlock()
*/
static function unlock() {
- global $wgDBtype;
- if ($wgDBtype != 'mysql')
- return true;
$dbw = wfGetDB( DB_MASTER );
$lockname = $dbw->addQuotes( FileStore::lockName() );
- $result = $dbw->query( "SELECT RELEASE_LOCK($lockname)", __METHOD__ );
- $dbw->fetchObject( $result );
- $dbw->freeResult( $result );
+ return $dbw->unlock( $lockname, __METHOD__ );
}
private static function lockName() {
@@ -123,7 +106,7 @@ class FileStore {
} else {
if( !file_exists( dirname( $destPath ) ) ) {
wfSuppressWarnings();
- $ok = mkdir( dirname( $destPath ), 0777, true );
+ $ok = wfMkdirParents( dirname( $destPath ) );
wfRestoreWarnings();
if( !$ok ) {
diff --git a/includes/FormOptions.php b/includes/FormOptions.php
index 5888a0c4..262c8c7f 100644
--- a/includes/FormOptions.php
+++ b/includes/FormOptions.php
@@ -176,8 +176,8 @@ class FormOptions implements ArrayAccess {
throw new MWException( 'Unsupported datatype' );
}
- if ( $value !== $default && $value !== null ) {
- $this->options[$name]['value'] = $value;
+ if ( $value !== null ) {
+ $this->options[$name]['value'] = $value === $default ? null : $value;
}
}
}
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index d1336d47..33f5831d 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -8,10 +8,12 @@ if ( !defined( 'MEDIAWIKI' ) ) {
* Global functions used everywhere
*/
-require_once dirname(__FILE__) . '/LogPage.php';
require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php';
require_once dirname(__FILE__) . '/XmlFunctions.php';
+// Hide compatibility functions from Doxygen
+/// @cond
+
/**
* Compatibility functions
*
@@ -87,6 +89,9 @@ if ( !function_exists( 'array_diff_key' ) ) {
}
}
+/// @endcond
+
+
/**
* Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
*/
@@ -145,16 +150,31 @@ function wfRandom() {
}
/**
- * We want / and : to be included as literal characters in our title URLs.
+ * We want some things to be included as literal characters in our title URLs
+ * for prettiness, which urlencode encodes by default. According to RFC 1738,
+ * all of the following should be safe:
+ *
+ * ;:@&=$-_.+!*'(),
+ *
+ * But + is not safe because it's used to indicate a space; &= are only safe in
+ * paths and not in queries (and we don't distinguish here); ' seems kind of
+ * scary; and urlencode() doesn't touch -_. to begin with. Plus, although /
+ * is reserved, we don't care. So the list we unescape is:
+ *
+ * ;:@$!*(),/
+ *
* %2F in the page titles seems to fatally break for some reason.
*
* @param $s String:
* @return string
*/
-function wfUrlencode ( $s ) {
+function wfUrlencode( $s ) {
$s = urlencode( $s );
- $s = preg_replace( '/%3[Aa]/', ':', $s );
- $s = preg_replace( '/%2[Ff]/', '/', $s );
+ $s = str_ireplace(
+ array( '%3B','%3A','%40','%24','%21','%2A','%28','%29','%2C','%2F' ),
+ array( ';', ':', '@', '$', '!', '*', '(', ')', ',', '/' ),
+ $s
+ );
return $s;
}
@@ -174,6 +194,7 @@ function wfUrlencode ( $s ) {
*/
function wfDebug( $text, $logonly = false ) {
global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage;
+ global $wgDebugLogPrefix;
static $recursion = 0;
static $cache = array(); // Cache of unoutputted messages
@@ -206,11 +227,26 @@ function wfDebug( $text, $logonly = false ) {
# Strip unprintables; they can switch terminal modes when binary data
# gets dumped, which is pretty annoying.
$text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
+ $text = $wgDebugLogPrefix . $text;
wfErrorLog( $text, $wgDebugLogFile );
}
}
/**
+ * Send a line giving PHP memory usage.
+ * @param $exact Bool : print exact values instead of kilobytes (default: false)
+ */
+function wfDebugMem( $exact = false ) {
+ $mem = memory_get_usage();
+ if( !$exact ) {
+ $mem = floor( $mem / 1024 ) . ' kilobytes';
+ } else {
+ $mem .= ' bytes';
+ }
+ wfDebug( "Memory usage: $mem\n" );
+}
+
+/**
* Send a line to a supplementary debug log file, if configured, or main debug log if not.
* $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log.
*
@@ -220,12 +256,17 @@ function wfDebug( $text, $logonly = false ) {
* log file is specified, (default true)
*/
function wfDebugLog( $logGroup, $text, $public = true ) {
- global $wgDebugLogGroups;
- if( $text{strlen( $text ) - 1} != "\n" ) $text .= "\n";
+ global $wgDebugLogGroups, $wgShowHostnames;
+ $text = trim($text)."\n";
if( isset( $wgDebugLogGroups[$logGroup] ) ) {
$time = wfTimestamp( TS_DB );
$wiki = wfWikiID();
- wfErrorLog( "$time $wiki: $text", $wgDebugLogGroups[$logGroup] );
+ if ( $wgShowHostnames ) {
+ $host = wfHostname();
+ } else {
+ $host = '';
+ }
+ wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
} else if ( $public === true ) {
wfDebug( $text, true );
}
@@ -245,16 +286,50 @@ function wfLogDBError( $text ) {
}
/**
- * Log to a file without getting "file size exceeded" signals
+ * Log to a file without getting "file size exceeded" signals.
+ *
+ * Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will
+ * send lines to the specified port, prefixed by the specified prefix and a space.
*/
function wfErrorLog( $text, $file ) {
- wfSuppressWarnings();
- $exists = file_exists( $file );
- $size = $exists ? filesize( $file ) : false;
- if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
- error_log( $text, 3, $file );
+ if ( substr( $file, 0, 4 ) == 'udp:' ) {
+ if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) {
+ // IPv6 bracketed host
+ $protocol = $m[1];
+ $host = $m[2];
+ $port = $m[3];
+ $prefix = isset( $m[4] ) ? $m[4] : false;
+ } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) {
+ $protocol = $m[1];
+ $host = $m[2];
+ $port = $m[3];
+ $prefix = isset( $m[4] ) ? $m[4] : false;
+ } else {
+ throw new MWException( __METHOD__.": Invalid UDP specification" );
+ }
+ // Clean it up for the multiplexer
+ if ( strval( $prefix ) !== '' ) {
+ $text = preg_replace( '/^/m', $prefix . ' ', $text );
+ if ( substr( $text, -1 ) != "\n" ) {
+ $text .= "\n";
+ }
+ }
+
+ $sock = fsockopen( "$protocol://$host", $port );
+ if ( !$sock ) {
+ return;
+ }
+ fwrite( $sock, $text );
+ fclose( $sock );
+ } else {
+ wfSuppressWarnings();
+ $exists = file_exists( $file );
+ $size = $exists ? filesize( $file ) : false;
+ if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
+ error_log( $text, 3, $file );
+ }
+ wfRestoreWarnings();
}
- wfRestoreWarnings();
}
/**
@@ -320,6 +395,47 @@ function wfReadOnlyReason() {
}
/**
+ * Return a Language object from $langcode
+ * @param $langcode Mixed: either:
+ * - a Language object
+ * - code of the language to get the message for, if it is
+ * a valid code create a language for that language, if
+ * it is a string but not a valid code then make a basic
+ * language object
+ * - a boolean: if it's false then use the current users
+ * language (as a fallback for the old parameter
+ * functionality), or if it is true then use the wikis
+ * @return Language object
+ */
+function wfGetLangObj( $langcode = false ){
+ # Identify which language to get or create a language object for.
+ if( $langcode instanceof Language )
+ # Great, we already have the object!
+ return $langcode;
+
+ global $wgContLang;
+ if( $langcode === $wgContLang->getCode() || $langcode === true )
+ # $langcode is the language code of the wikis content language object.
+ # or it is a boolean and value is true
+ return $wgContLang;
+
+ global $wgLang;
+ if( $langcode === $wgLang->getCode() || $langcode === false )
+ # $langcode is the language code of user language object.
+ # or it was a boolean and value is false
+ return $wgLang;
+
+ $validCodes = array_keys( Language::getLanguageNames() );
+ if( in_array( $langcode, $validCodes ) )
+ # $langcode corresponds to a valid language.
+ return Language::factory( $langcode );
+
+ # $langcode is a string, but not a valid language code; use content language.
+ wfDebug( 'Invalid language code passed to wfGetLangObj, falling back to content language.' );
+ return $wgContLang;
+}
+
+/**
* Get a message from anywhere, for the current user language.
*
* Use wfMsgForContent() instead if the message should NOT
@@ -458,7 +574,7 @@ function wfMsgWeirdKey ( $key ) {
* @private
*/
function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) {
- global $wgParser, $wgContLang, $wgMessageCache, $wgLang;
+ global $wgContLang, $wgMessageCache;
wfRunHooks('NormalizeMessageKey', array(&$key, &$useDB, &$langCode, &$transform));
@@ -469,21 +585,7 @@ function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) {
$message = $wgMessageCache->transform( $message );
}
} else {
- if( $langCode === true ) {
- $lang = &$wgContLang;
- } 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.' );
- }
- }
+ $lang = wfGetLangObj( $langCode );
# MessageCache::get() does this already, Language::getMessage() doesn't
# ISSUE: Should we try to handle "message/lang" here too?
@@ -565,40 +667,47 @@ function wfMsgWikiHtml( $key ) {
/**
* Returns message in the requested format
* @param string $key Key of the message
- * @param array $options Processing rules:
- * <i>parse</i>: parses wikitext to html
- * <i>parseinline</i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
- * <i>escape</i>: filters message through htmlspecialchars
- * <i>escapenoentities</i>: same, but allows entity references like &nbsp; through
- * <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.
+ * @param array $options Processing rules. Can take the following options:
+ * <i>parse</i>: parses wikitext to html
+ * <i>parseinline</i>: parses wikitext to html and removes the surrounding
+ * p's added by parser or tidy
+ * <i>escape</i>: filters message through htmlspecialchars
+ * <i>escapenoentities</i>: same, but allows entity references like &nbsp; through
+ * <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
+ * Also can accept a single associative argument, of the form 'language' => 'xx':
+ * <i>language</i>: Language object or 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 ) {
- global $wgOut, $wgParser;
+ global $wgOut;
$args = func_get_args();
array_shift( $args );
array_shift( $args );
-
- if( !is_array($options) ) {
- $options = array($options);
+ $options = (array)$options;
+
+ foreach( $options as $arrayKey => $option ) {
+ if( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
+ # An unknown index, neither numeric nor "language"
+ trigger_error( "wfMsgExt called with incorrect parameter key $arrayKey", E_USER_WARNING );
+ } elseif( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
+ array( 'parse', 'parseinline', 'escape', 'escapenoentities',
+ 'replaceafter', 'parsemag', 'content' ) ) ) {
+ # A numeric index with unknown value
+ trigger_error( "wfMsgExt called with incorrect parameter $option", E_USER_WARNING );
+ }
}
- if( in_array('content', $options) ) {
+ if( in_array('content', $options, true ) ) {
$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';
- }
+ $langCode = wfGetLangObj( $options['language'] );
} else {
$forContent = false;
$langCode = false;
@@ -606,34 +715,34 @@ function wfMsgExt( $key, $options ) {
$string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false );
- if( !in_array('replaceafter', $options) ) {
+ if( !in_array('replaceafter', $options, true ) ) {
$string = wfMsgReplaceArgs( $string, $args );
}
- if( in_array('parse', $options) ) {
+ if( in_array('parse', $options, true ) ) {
$string = $wgOut->parse( $string, true, !$forContent );
- } elseif ( in_array('parseinline', $options) ) {
+ } elseif ( in_array('parseinline', $options, true ) ) {
$string = $wgOut->parse( $string, true, !$forContent );
$m = array();
if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
$string = $m[1];
}
- } elseif ( in_array('parsemag', $options) ) {
+ } elseif ( in_array('parsemag', $options, true ) ) {
global $wgMessageCache;
if ( isset( $wgMessageCache ) ) {
- $string = $wgMessageCache->transform( $string, !$forContent );
+ $string = $wgMessageCache->transform( $string,
+ !$forContent,
+ is_object( $langCode ) ? $langCode : null );
}
}
- if ( in_array('escape', $options) ) {
+ if ( in_array('escape', $options, true ) ) {
$string = htmlspecialchars ( $string );
- } elseif ( in_array( 'escapenoentities', $options ) ) {
- $string = htmlspecialchars( $string );
- $string = str_replace( '&amp;', '&', $string );
- $string = Sanitizer::normalizeCharReferences( $string );
+ } elseif ( in_array( 'escapenoentities', $options, true ) ) {
+ $string = Sanitizer::escapeHtmlAllowEntities( $string );
}
- if( in_array('replaceafter', $options) ) {
+ if( in_array('replaceafter', $options, true ) ) {
$string = wfMsgReplaceArgs( $string, $args );
}
@@ -707,18 +816,25 @@ function wfDebugDieBacktrace( $msg = '' ) {
* @return string
*/
function wfHostname() {
- if ( function_exists( 'posix_uname' ) ) {
- // This function not present on Windows
- $uname = @posix_uname();
- } else {
- $uname = false;
- }
- if( is_array( $uname ) && isset( $uname['nodename'] ) ) {
- return $uname['nodename'];
- } else {
- # This may be a virtual server.
- return $_SERVER['SERVER_NAME'];
+ static $host;
+ if ( is_null( $host ) ) {
+ if ( function_exists( 'posix_uname' ) ) {
+ // This function not present on Windows
+ $uname = @posix_uname();
+ } else {
+ $uname = false;
+ }
+ if( is_array( $uname ) && isset( $uname['nodename'] ) ) {
+ $host = $uname['nodename'];
+ } elseif ( getenv( 'COMPUTERNAME' ) ) {
+ # Windows computer name
+ $host = getenv( 'COMPUTERNAME' );
+ } else {
+ # This may be a virtual server.
+ $host = $_SERVER['SERVER_NAME'];
+ }
}
+ return $host;
}
/**
@@ -929,7 +1045,7 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
*/
function wfEscapeWikiText( $text ) {
$text = str_replace(
- array( '[', '|', ']', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ),
+ array( '[', '|', ']', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), # }}
array( '&#91;', '&#124;', '&#93;', '&#39;', 'ISBN&#32;', 'RFC&#32;', '&#58;//', "\n&#61;", '&#123;&#123;' ),
htmlspecialchars($text) );
return $text;
@@ -1029,6 +1145,34 @@ function wfArrayToCGI( $array1, $array2 = NULL )
}
/**
+ * This is the logical opposite of wfArrayToCGI(): it accepts a query string as
+ * its argument and returns the same string in array form. This allows compa-
+ * tibility with legacy functions that accept raw query strings instead of nice
+ * arrays. Of course, keys and values are urldecode()d. Don't try passing in-
+ * valid query strings, or it will explode.
+ *
+ * @param $query string Query string
+ * @return array Array version of input
+ */
+function wfCgiToArray( $query ) {
+ if( isset( $query[0] ) and $query[0] == '?' ) {
+ $query = substr( $query, 1 );
+ }
+ $bits = explode( '&', $query );
+ $ret = array();
+ foreach( $bits as $bit ) {
+ if( $bit === '' ) {
+ continue;
+ }
+ list( $key, $value ) = explode( '=', $bit );
+ $key = urldecode( $key );
+ $value = urldecode( $value );
+ $ret[$key] = $value;
+ }
+ return $ret;
+}
+
+/**
* Append a query string to an existing URL, which may or may not already
* have query string parameters already. If so, they will be combined.
*
@@ -1132,7 +1276,7 @@ function wfMerge( $old, $mine, $yours, &$result ){
# This check may also protect against code injection in
# case of broken installations.
- if(! file_exists( $wgDiff3 ) ){
+ if( !$wgDiff3 || !file_exists( $wgDiff3 ) ) {
wfDebug( "diff3 not found\n" );
return false;
}
@@ -1246,7 +1390,10 @@ function wfDiff( $before, $after, $params = '-u' ) {
}
/**
- * @todo document
+ * A wrapper around the PHP function var_export().
+ * Either print it or add it to the regular output ($wgOut).
+ *
+ * @param $var A PHP variable to dump.
*/
function wfVarDump( $var ) {
global $wgOut;
@@ -1322,6 +1469,7 @@ function wfResetOutputBuffers( $resetGzipEncoding=true ) {
// Reset the 'Content-Encoding' field set by this handler
// so we can start fresh.
header( 'Content-Encoding:' );
+ break;
}
}
}
@@ -1568,11 +1716,11 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
# 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)) {
+ } elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da)) {
# TS_ISO_8601
- } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)[\+\- ](\d\d)$/',$ts,$da)) {
+ } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/',$ts,$da)) {
# TS_POSTGRES
- } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/',$ts,$da)) {
+ } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/',$ts,$da)) {
# TS_POSTGRES
} else {
# Bogus value; fall back to the epoch...
@@ -1648,7 +1796,7 @@ function swap( &$x, &$y ) {
}
function wfGetCachedNotice( $name ) {
- global $wgOut, $parserMemc;
+ global $wgOut, $wgRenderHashAppend, $parserMemc;
$fname = 'wfGetCachedNotice';
wfProfileIn( $fname );
@@ -1670,7 +1818,9 @@ function wfGetCachedNotice( $name ) {
}
}
- $cachedNotice = $parserMemc->get( wfMemcKey( $name ) );
+ // Use the extra hash appender to let eg SSL variants separately cache.
+ $key = wfMemcKey( $name . $wgRenderHashAppend );
+ $cachedNotice = $parserMemc->get( $key );
if( is_array( $cachedNotice ) ) {
if( md5( $notice ) == $cachedNotice['hash'] ) {
$notice = $cachedNotice['html'];
@@ -1684,7 +1834,7 @@ function wfGetCachedNotice( $name ) {
if( $needParse ) {
if( is_object( $wgOut ) ) {
$parsed = $wgOut->parse( $notice );
- $parserMemc->set( wfMemcKey( $name ), array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
+ $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
$notice = $parsed;
} else {
wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' );
@@ -1777,69 +1927,20 @@ function wfTempDir() {
/**
* Make directory, and make all parent directories if they don't exist
*
- * @param string $fullDir Full path to directory to create
+ * @param string $dir Full path to directory to create
* @param int $mode Chmod value to use, default is $wgDirectoryMode
* @return bool
*/
-function wfMkdirParents( $fullDir, $mode = null ) {
+function wfMkdirParents( $dir, $mode = null ) {
global $wgDirectoryMode;
- if( strval( $fullDir ) === '' )
- return true;
- if( file_exists( $fullDir ) )
- return 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
+ if( strval( $dir ) === '' || file_exists( $dir ) )
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;
- }
+ if ( is_null( $mode ) )
+ $mode = $wgDirectoryMode;
- foreach ( $createList as $dir ) {
- # use chmod to override the umask, as suggested by the PHP manual
- if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
- wfDebugLog( 'mkdir', "Unable to create directory $dir\n" );
- return false;
- }
- }
- return true;
+ return mkdir( $dir, $mode, true ); // PHP5 <3
}
/**
@@ -1998,7 +2099,7 @@ function wfIniGetBool( $setting ) {
* @return collected stdout as a string (trailing newlines stripped)
*/
function wfShellExec( $cmd, &$retval=null ) {
- global $IP, $wgMaxShellMemory, $wgMaxShellFileSize;
+ global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime;
if( wfIniGetBool( 'safe_mode' ) ) {
wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
@@ -2008,7 +2109,7 @@ function wfShellExec( $cmd, &$retval=null ) {
wfInitShellLocale();
if ( php_uname( 's' ) == 'Linux' ) {
- $time = intval( ini_get( 'max_execution_time' ) );
+ $time = intval( $wgMaxShellTime );
$mem = intval( $wgMaxShellMemory );
$filesize = intval( $wgMaxShellFileSize );
@@ -2030,6 +2131,10 @@ function wfShellExec( $cmd, &$retval=null ) {
passthru( $cmd, $retval );
$output = ob_get_contents();
ob_end_clean();
+
+ if ( $retval == 127 ) {
+ wfDebugLog( 'exec', "Possibly missing executable file: $cmd\n" );
+ }
return $output;
}
@@ -2167,28 +2272,51 @@ function wfRelativePath( $path, $from ) {
}
/**
- * array_merge() does awful things with "numeric" indexes, including
- * string indexes when happen to look like integers. When we want
- * to merge arrays with arbitrary string indexes, we don't want our
- * arrays to be randomly corrupted just because some of them consist
- * of numbers.
- *
- * Fuck you, PHP. Fuck you in the ear!
+ * Backwards array plus for people who haven't bothered to read the PHP manual
+ * XXX: will not darn your socks for you.
*
* @param array $array1, [$array2, [...]]
* @return array
*/
function wfArrayMerge( $array1/* ... */ ) {
- $out = $array1;
- for( $i = 1; $i < func_num_args(); $i++ ) {
- foreach( func_get_arg( $i ) as $key => $value ) {
- $out[$key] = $value;
- }
+ $args = func_get_args();
+ $args = array_reverse( $args, true );
+ $out = array();
+ foreach ( $args as $arg ) {
+ $out += $arg;
}
return $out;
}
/**
+ * Merge arrays in the style of getUserPermissionsErrors, with duplicate removal
+ * e.g.
+ * wfMergeErrorArrays(
+ * array( array( 'x' ) ),
+ * array( array( 'x', '2' ) ),
+ * array( array( 'x' ) ),
+ * array( array( 'y') )
+ * );
+ * returns:
+ * array(
+ * array( 'x', '2' ),
+ * array( 'x' ),
+ * array( 'y' )
+ * )
+ */
+function wfMergeErrorArrays(/*...*/) {
+ $args = func_get_args();
+ $out = array();
+ foreach ( $args as $errors ) {
+ foreach ( $errors as $params ) {
+ $spec = implode( "\t", $params );
+ $out[$spec] = $params;
+ }
+ }
+ return array_values( $out );
+}
+
+/**
* Make a URL index, appropriate for the el_index field of externallinks.
*/
function wfMakeUrlIndex( $url ) {
@@ -2560,7 +2688,7 @@ function wfSplitWikiID( $wiki ) {
* will always return the same object, unless the underlying connection or load
* balancer is manually destroyed.
*/
-function &wfGetDB( $db = DB_LAST, $groups = array(), $wiki = false ) {
+function &wfGetDB( $db, $groups = array(), $wiki = false ) {
return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
}
@@ -2590,10 +2718,15 @@ function &wfGetLBFactory() {
* current version. An image object will be returned which
* was created at the specified time.
* @param mixed $flags FileRepo::FIND_ flags
+ * @param boolean $bypass Bypass the file cache even if it could be used
* @return File, or false if the file does not exist
*/
-function wfFindFile( $title, $time = false, $flags = 0 ) {
- return RepoGroup::singleton()->findFile( $title, $time, $flags );
+function wfFindFile( $title, $time = false, $flags = 0, $bypass = false ) {
+ if( !$time && !$flags && !$bypass ) {
+ return FileCache::singleton()->findFile( $title );
+ } else {
+ return RepoGroup::singleton()->findFile( $title, $time, $flags );
+ }
}
/**
@@ -2646,6 +2779,8 @@ function wfBoolToStr( $value ) {
* @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).
+ * @since r24808 (v1.11) Using this method of loading extension messages will not work
+ * on MediaWiki prior to that
*/
function wfLoadExtensionMessages( $extensionName, $langcode = false ) {
global $wgExtensionMessagesFiles, $wgMessageCache, $wgLang, $wgContLang;
diff --git a/includes/HTMLCacheUpdate.php b/includes/HTMLCacheUpdate.php
index 1f250214..402102ea 100644
--- a/includes/HTMLCacheUpdate.php
+++ b/includes/HTMLCacheUpdate.php
@@ -37,7 +37,7 @@ class HTMLCacheUpdate
$this->mRowsPerQuery = $wgUpdateRowsPerQuery;
}
- function doUpdate() {
+ public function doUpdate() {
# Fetch the IDs
$cond = $this->getToCondition();
$dbr = wfGetDB( DB_SLAVE );
@@ -50,16 +50,17 @@ class HTMLCacheUpdate
$this->invalidateIDs( $res );
}
}
+ wfRunHooks( 'HTMLCacheUpdate::doUpdate', array($this->mTitle) );
}
- function insertJobs( ResultWrapper $res ) {
+ protected function insertJobs( ResultWrapper $res ) {
$numRows = $res->numRows();
$numBatches = ceil( $numRows / $this->mRowsPerJob );
$realBatchSize = $numRows / $numBatches;
$start = false;
$jobs = array();
do {
- for ( $i = 0; $i < $realBatchSize - 1; $i++ ) {
+ for ( $i = 0; $i <= $realBatchSize - 1; $i++ ) {
$row = $res->fetchRow();
if ( $row ) {
$id = $row[0];
@@ -82,17 +83,13 @@ class HTMLCacheUpdate
Job::batchInsert( $jobs );
}
- function getPrefix() {
+ protected function getPrefix() {
static $prefixes = array(
'pagelinks' => 'pl',
'imagelinks' => 'il',
'categorylinks' => 'cl',
'templatelinks' => 'tl',
'redirect' => 'rd',
-
- # Not needed
- # 'externallinks' => 'el',
- # 'langlinks' => 'll'
);
if ( is_null( $this->mPrefix ) ) {
@@ -104,11 +101,11 @@ class HTMLCacheUpdate
return $this->mPrefix;
}
- function getFromField() {
+ public function getFromField() {
return $this->getPrefix() . '_from';
}
- function getToCondition() {
+ public function getToCondition() {
$prefix = $this->getPrefix();
switch ( $this->mTable ) {
case 'pagelinks':
@@ -129,7 +126,7 @@ class HTMLCacheUpdate
/**
* Invalidate a set of IDs, right now
*/
- function invalidateIDs( ResultWrapper $res ) {
+ public function invalidateIDs( ResultWrapper $res ) {
global $wgUseFileCache, $wgUseSquid;
if ( $res->numRows() == 0 ) {
@@ -175,8 +172,7 @@ class HTMLCacheUpdate
# Update file cache
if ( $wgUseFileCache ) {
foreach ( $titles as $title ) {
- $cm = new HTMLFileCache($title);
- @unlink($cm->fileCacheName());
+ HTMLFileCache::clearFileCache( $title );
}
}
}
@@ -185,7 +181,9 @@ class HTMLCacheUpdate
}
/**
- * @todo document (e.g. one-sentence top-level class description).
+ * Job wrapper for HTMLCacheUpdate. Gets run whenever a related
+ * job gets called from the queue.
+ *
* @ingroup JobQueue
*/
class HTMLCacheUpdateJob extends Job {
@@ -204,7 +202,7 @@ class HTMLCacheUpdateJob extends Job {
$this->end = $params['end'];
}
- function run() {
+ public function run() {
$update = new HTMLCacheUpdate( $this->title, $this->table );
$fromField = $update->getFromField();
diff --git a/includes/HTMLFileCache.php b/includes/HTMLFileCache.php
index ba2196eb..e267962c 100644
--- a/includes/HTMLFileCache.php
+++ b/includes/HTMLFileCache.php
@@ -20,25 +20,29 @@
* @ingroup Cache
*/
class HTMLFileCache {
- var $mTitle, $mFileCache;
+ var $mTitle, $mFileCache, $mType;
- function HTMLFileCache( &$title ) {
- $this->mTitle =& $title;
- $this->mFileCache = '';
+ public function __construct( &$title, $type = 'view' ) {
+ $this->mTitle = $title;
+ $this->mType = ($type == 'raw' || $type == 'view' ) ? $type : false;
+ $this->fileCacheName(); // init name
}
- function fileCacheName() {
- global $wgFileCacheDirectory;
+ public function fileCacheName() {
if( !$this->mFileCache ) {
+ global $wgFileCacheDirectory, $wgRequest;
+ # Store raw pages (like CSS hits) elsewhere
+ $subdir = ($this->mType === 'raw') ? 'raw/' : '';
$key = $this->mTitle->getPrefixedDbkey();
$hash = md5( $key );
+ # Avoid extension confusion
$key = str_replace( '.', '%2E', urlencode( $key ) );
-
+
$hash1 = substr( $hash, 0, 1 );
$hash2 = substr( $hash, 0, 2 );
- $this->mFileCache = "{$wgFileCacheDirectory}/{$hash1}/{$hash2}/{$key}.html";
+ $this->mFileCache = "{$wgFileCacheDirectory}/{$subdir}{$hash1}/{$hash2}/{$key}.html";
- if($this->useGzip())
+ if( $this->useGzip() )
$this->mFileCache .= '.gz';
wfDebug( " fileCacheName() - {$this->mFileCache}\n" );
@@ -46,38 +50,72 @@ class HTMLFileCache {
return $this->mFileCache;
}
- function isFileCached() {
+ public function isFileCached() {
+ if( $this->mType === false ) return false;
return file_exists( $this->fileCacheName() );
}
- function fileCacheTime() {
+ public function fileCacheTime() {
return wfTimestamp( TS_MW, filemtime( $this->fileCacheName() ) );
}
+
+ /**
+ * Check if pages can be cached for this request/user
+ * @return bool
+ */
+ public static function useFileCache() {
+ global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest, $wgLang, $wgContLang;
+ if( !$wgUseFileCache ) return false;
+ // Get all query values
+ $queryVals = $wgRequest->getValues();
+ foreach( $queryVals as $query => $val ) {
+ if( $query == 'title' || $query == 'curid' ) continue;
+ // Normal page view in query form can have action=view.
+ // Raw hits for pages also stored, like .css pages for example.
+ else if( $query == 'action' && ($val == 'view' || $val == 'raw') ) continue;
+ else if( $query == 'usemsgcache' && $val == 'yes' ) continue;
+ // Below are header setting params
+ else if( $query == 'maxage' || $query == 'smaxage' || $query == 'ctype' || $query == 'gen' )
+ continue;
+ else
+ return false;
+ }
+ // Check for non-standard user language; this covers uselang,
+ // and extensions for auto-detecting user language.
+ $ulang = $wgLang->getCode();
+ $clang = $wgContLang->getCode();
+ // Check that there are no other sources of variation
+ return !$wgShowIPinHeader && !$wgUser->getId() && !$wgUser->getNewtalk() && $ulang == $clang;
+ }
- function isFileCacheGood( $timestamp ) {
+ /*
+ * Check if up to date cache file exists
+ * @param $timestamp string
+ */
+ public function isFileCacheGood( $timestamp = '' ) {
global $wgCacheEpoch;
if( !$this->isFileCached() ) return false;
+ if( !$timestamp ) return true; // should be invalidated on change
$cachetime = $this->fileCacheTime();
- $good = (( $timestamp <= $cachetime ) &&
- ( $wgCacheEpoch <= $cachetime ));
+ $good = $timestamp <= $cachetime && $wgCacheEpoch <= $cachetime;
- wfDebug(" isFileCacheGood() - cachetime $cachetime, touched {$timestamp} epoch {$wgCacheEpoch}, good $good\n");
+ wfDebug(" isFileCacheGood() - cachetime $cachetime, touched '{$timestamp}' epoch {$wgCacheEpoch}, good $good\n");
return $good;
}
- function useGzip() {
+ public function useGzip() {
global $wgUseGzip;
return $wgUseGzip;
}
/* In handy string packages */
- function fetchRawText() {
+ public function fetchRawText() {
return file_get_contents( $this->fileCacheName() );
}
- function fetchPageText() {
+ public function fetchPageText() {
if( $this->useGzip() ) {
/* Why is there no gzfile_get_contents() or gzdecode()? */
return implode( '', gzfile( $this->fileCacheName() ) );
@@ -87,15 +125,18 @@ class HTMLFileCache {
}
/* Working directory to/from output */
- function loadFromFileCache() {
+ public function loadFromFileCache() {
global $wgOut, $wgMimeType, $wgOutputEncoding, $wgContLanguageCode;
wfDebug(" loadFromFileCache()\n");
- $filename=$this->fileCacheName();
- $wgOut->sendCacheControl();
-
- header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" );
- header( "Content-language: $wgContLanguageCode" );
+ $filename = $this->fileCacheName();
+ // Raw pages should handle cache control on their own,
+ // even when using file cache. This reduces hits from clients.
+ if( $this->mType !== 'raw' ) {
+ $wgOut->sendCacheControl();
+ header( "Content-Type: $wgMimeType; charset={$wgOutputEncoding}" );
+ header( "Content-Language: $wgContLanguageCode" );
+ }
if( $this->useGzip() ) {
if( wfClientAcceptsGzip() ) {
@@ -109,18 +150,22 @@ class HTMLFileCache {
readfile( $filename );
}
- function checkCacheDirs() {
+ protected function checkCacheDirs() {
$filename = $this->fileCacheName();
- $mydir2=substr($filename,0,strrpos($filename,'/')); # subdirectory level 2
- $mydir1=substr($mydir2,0,strrpos($mydir2,'/')); # subdirectory level 1
+ $mydir2 = substr($filename,0,strrpos($filename,'/')); # subdirectory level 2
+ $mydir1 = substr($mydir2,0,strrpos($mydir2,'/')); # subdirectory level 1
- if(!file_exists($mydir1)) { mkdir($mydir1,0775); } # create if necessary
- if(!file_exists($mydir2)) { mkdir($mydir2,0775); }
+ wfMkdirParents( $mydir1 );
+ wfMkdirParents( $mydir2 );
}
- function saveToFileCache( $origtext ) {
+ public function saveToFileCache( $origtext ) {
+ global $wgUseFileCache;
+ if( !$wgUseFileCache ) {
+ return $origtext; // return to output
+ }
$text = $origtext;
- if(strcmp($text,'') == 0) return '';
+ if( strcmp($text,'') == 0 ) return '';
wfDebug(" saveToFileCache()\n", false);
@@ -155,4 +200,13 @@ class HTMLFileCache {
return $text;
}
+ public static function clearFileCache( $title ) {
+ global $wgUseFileCache;
+ if( !$wgUseFileCache ) return false;
+ $fc = new self( $title, 'view' );
+ @unlink( $fc->fileCacheName() );
+ $fc = new self( $title, 'raw' );
+ @unlink( $fc->fileCacheName() );
+ return true;
+ }
}
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index 3772926d..664ceb4f 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -1,41 +1,33 @@
<?php
/**
- * Pure virtual parent
- * @todo document (needs a one-sentence top-level class description, that answers the question: "what is a HistoryBlob?")
+ * Base class for general text storage via the "object" flag in old_flags, or
+ * two-part external storage URLs. Used for represent efficient concatenated
+ * storage, and migration-related pointer objects.
*/
interface HistoryBlob
{
/**
- * setMeta and getMeta currently aren't used for anything, I just thought
- * they might be useful in the future.
- * @param $meta String: a single string.
- */
- public function setMeta( $meta );
-
- /**
- * setMeta and getMeta currently aren't used for anything, I just thought
- * they might be useful in the future.
- * Gets the meta-value
- */
- public function getMeta();
-
- /**
* Adds an item of text, returns a stub object which points to the item.
* You must call setLocation() on the stub object before storing it to the
* database
+ * Returns the key for getItem()
*/
public function addItem( $text );
/**
- * Get item by hash
+ * Get item by key, or false if the key is not present
*/
- public function getItem( $hash );
+ public function getItem( $key );
- # Set the "default text"
- # This concept is an odd property of the current DB schema, whereby each text item has a revision
- # associated with it. The default text is the text of the associated revision. There may, however,
- # be other revisions in the same object
+ /**
+ * Set the "default text"
+ * This concept is an odd property of the current DB schema, whereby each text item has a revision
+ * associated with it. The default text is the text of the associated revision. There may, however,
+ * be other revisions in the same object.
+ *
+ * Default text is not required for two-part external storage URLs.
+ */
public function setText( $text );
/**
@@ -45,13 +37,15 @@ interface HistoryBlob
}
/**
- * The real object
- * @todo document (needs one-sentence top-level class description + function descriptions).
+ * Concatenated gzip (CGZ) storage
+ * Improves compression ratio by concatenating like objects before gzipping
*/
class ConcatenatedGzipHistoryBlob implements HistoryBlob
{
public $mVersion = 0, $mCompressed = false, $mItems = array(), $mDefaultHash = '';
- public $mFast = 0, $mSize = 0;
+ public $mSize = 0;
+ public $mMaxSize = 10000000;
+ public $mMaxCount = 100;
/** Constructor */
public function ConcatenatedGzipHistoryBlob() {
@@ -60,34 +54,16 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob
}
}
- #
- # HistoryBlob implementation:
- #
-
- /** @todo document */
- public function setMeta( $metaData ) {
- $this->uncompress();
- $this->mItems['meta'] = $metaData;
- }
-
- /** @todo document */
- public function getMeta() {
- $this->uncompress();
- return $this->mItems['meta'];
- }
-
- /** @todo document */
public function addItem( $text ) {
$this->uncompress();
$hash = md5( $text );
- $this->mItems[$hash] = $text;
- $this->mSize += strlen( $text );
-
- $stub = new HistoryBlobStub( $hash );
- return $stub;
+ if ( !isset( $this->mItems[$hash] ) ) {
+ $this->mItems[$hash] = $text;
+ $this->mSize += strlen( $text );
+ }
+ return $hash;
}
- /** @todo document */
public function getItem( $hash ) {
$this->uncompress();
if ( array_key_exists( $hash, $this->mItems ) ) {
@@ -97,29 +73,27 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob
}
}
- /** @todo document */
public function setText( $text ) {
$this->uncompress();
- $stub = $this->addItem( $text );
- $this->mDefaultHash = $stub->mHash;
+ $this->mDefaultHash = $this->addItem( $text );
}
- /** @todo document */
public function getText() {
$this->uncompress();
return $this->getItem( $this->mDefaultHash );
}
- # HistoryBlob implemented.
-
-
- /** @todo document */
+ /**
+ * Remove an item
+ */
public function removeItem( $hash ) {
$this->mSize -= strlen( $this->mItems[$hash] );
unset( $this->mItems[$hash] );
}
- /** @todo document */
+ /**
+ * Compress the bulk data in the object
+ */
public function compress() {
if ( !$this->mCompressed ) {
$this->mItems = gzdeflate( serialize( $this->mItems ) );
@@ -127,7 +101,9 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob
}
}
- /** @todo document */
+ /**
+ * Uncompress bulk data
+ */
public function uncompress() {
if ( $this->mCompressed ) {
$this->mItems = unserialize( gzinflate( $this->mItems ) );
@@ -136,39 +112,22 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob
}
- /** @todo document */
function __sleep() {
$this->compress();
return array( 'mVersion', 'mCompressed', 'mItems', 'mDefaultHash' );
}
- /** @todo document */
function __wakeup() {
$this->uncompress();
}
/**
- * Determines if this object is happy
+ * Helper function for compression jobs
+ * Returns true until the object is "full" and ready to be committed
*/
- public function isHappy( $maxFactor, $factorThreshold ) {
- if ( count( $this->mItems ) == 0 ) {
- return true;
- }
- if ( !$this->mFast ) {
- $this->uncompress();
- $record = serialize( $this->mItems );
- $size = strlen( $record );
- $avgUncompressed = $size / count( $this->mItems );
- $compressed = strlen( gzdeflate( $record ) );
-
- if ( $compressed < $factorThreshold * 1024 ) {
- return true;
- } else {
- return $avgUncompressed * $maxFactor < $compressed;
- }
- } else {
- return count( $this->mItems ) <= 10;
- }
+ public function isHappy() {
+ return $this->mSize < $this->mMaxSize
+ && count( $this->mItems ) < $this->mMaxCount;
}
}
@@ -184,12 +143,15 @@ $wgBlobCache = array();
/**
- * @todo document (needs one-sentence top-level class description + some function descriptions).
+ * Pointer object for an item within a CGZ blob stored in the text table.
*/
class HistoryBlobStub {
var $mOldId, $mHash, $mRef;
- /** @todo document */
+ /**
+ * @param string $hash The content hash of the text
+ * @param integer $oldid The old_id for the CGZ object
+ */
function HistoryBlobStub( $hash = '', $oldid = 0 ) {
$this->mHash = $hash;
}
@@ -216,7 +178,6 @@ class HistoryBlobStub {
return $this->mRef;
}
- /** @todo document */
function getText() {
$fname = 'HistoryBlobStub::getText';
global $wgBlobCache;
@@ -264,7 +225,9 @@ class HistoryBlobStub {
return $obj->getItem( $this->mHash );
}
- /** @todo document */
+ /**
+ * Get the content hash
+ */
function getHash() {
return $this->mHash;
}
@@ -282,7 +245,9 @@ class HistoryBlobStub {
class HistoryBlobCurStub {
var $mCurId;
- /** @todo document */
+ /**
+ * @param integer $curid The cur_id pointed to
+ */
function HistoryBlobCurStub( $curid = 0 ) {
$this->mCurId = $curid;
}
@@ -295,7 +260,6 @@ class HistoryBlobCurStub {
$this->mCurId = $id;
}
- /** @todo document */
function getText() {
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mCurId ) );
@@ -305,3 +269,311 @@ class HistoryBlobCurStub {
return $row->cur_text;
}
}
+
+/**
+ * Diff-based history compression
+ * Requires xdiff 1.5+ and zlib
+ */
+class DiffHistoryBlob implements HistoryBlob {
+ /** Uncompressed item cache */
+ var $mItems = array();
+
+ /** Total uncompressed size */
+ var $mSize = 0;
+
+ /**
+ * Array of diffs. If a diff D from A to B is notated D = B - A, and Z is
+ * an empty string:
+ *
+ * { item[map[i]] - item[map[i-1]] where i > 0
+ * diff[i] = {
+ * { item[map[i]] - Z where i = 0
+ */
+ var $mDiffs;
+
+ /** The diff map, see above */
+ var $mDiffMap;
+
+ /**
+ * The key for getText()
+ */
+ var $mDefaultKey;
+
+ /**
+ * Compressed storage
+ */
+ var $mCompressed;
+
+ /**
+ * True if the object is locked against further writes
+ */
+ var $mFrozen = false;
+
+ /**
+ * The maximum uncompressed size before the object becomes sad
+ * Should be less than max_allowed_packet
+ */
+ var $mMaxSize = 10000000;
+
+ /**
+ * The maximum number of text items before the object becomes sad
+ */
+ var $mMaxCount = 100;
+
+ /** Constants from xdiff.h */
+ const XDL_BDOP_INS = 1;
+ const XDL_BDOP_CPY = 2;
+ const XDL_BDOP_INSB = 3;
+
+ function __construct() {
+ if ( !function_exists( 'gzdeflate' ) ) {
+ throw new MWException( "Need zlib support to read or write DiffHistoryBlob\n" );
+ }
+ }
+
+ function addItem( $text ) {
+ if ( $this->mFrozen ) {
+ throw new MWException( __METHOD__.": Cannot add more items after sleep/wakeup" );
+ }
+
+ $this->mItems[] = $text;
+ $this->mSize += strlen( $text );
+ $this->mDiffs = null; // later
+ return count( $this->mItems ) - 1;
+ }
+
+ function getItem( $key ) {
+ return $this->mItems[$key];
+ }
+
+ function setText( $text ) {
+ $this->mDefaultKey = $this->addItem( $text );
+ }
+
+ function getText() {
+ return $this->getItem( $this->mDefaultKey );
+ }
+
+ function compress() {
+ if ( !function_exists( 'xdiff_string_rabdiff' ) ){
+ throw new MWException( "Need xdiff 1.5+ support to write DiffHistoryBlob\n" );
+ }
+ if ( isset( $this->mDiffs ) ) {
+ // Already compressed
+ return;
+ }
+ if ( !count( $this->mItems ) ) {
+ // Empty
+ return;
+ }
+
+ // Create two diff sequences: one for main text and one for small text
+ $sequences = array(
+ 'small' => array(
+ 'tail' => '',
+ 'diffs' => array(),
+ 'map' => array(),
+ ),
+ 'main' => array(
+ 'tail' => '',
+ 'diffs' => array(),
+ 'map' => array(),
+ ),
+ );
+ $smallFactor = 0.5;
+
+ for ( $i = 0; $i < count( $this->mItems ); $i++ ) {
+ $text = $this->mItems[$i];
+ if ( $i == 0 ) {
+ $seqName = 'main';
+ } else {
+ $mainTail = $sequences['main']['tail'];
+ if ( strlen( $text ) < strlen( $mainTail ) * $smallFactor ) {
+ $seqName = 'small';
+ } else {
+ $seqName = 'main';
+ }
+ }
+ $seq =& $sequences[$seqName];
+ $tail = $seq['tail'];
+ $diff = $this->diff( $tail, $text );
+ $seq['diffs'][] = $diff;
+ $seq['map'][] = $i;
+ $seq['tail'] = $text;
+ }
+ unset( $seq ); // unlink dangerous alias
+
+ // Knit the sequences together
+ $tail = '';
+ $this->mDiffs = array();
+ $this->mDiffMap = array();
+ foreach ( $sequences as $seq ) {
+ if ( !count( $seq['diffs'] ) ) {
+ continue;
+ }
+ if ( $tail === '' ) {
+ $this->mDiffs[] = $seq['diffs'][0];
+ } else {
+ $head = $this->patch( '', $seq['diffs'][0] );
+ $this->mDiffs[] = $this->diff( $tail, $head );
+ }
+ $this->mDiffMap[] = $seq['map'][0];
+ for ( $i = 1; $i < count( $seq['diffs'] ); $i++ ) {
+ $this->mDiffs[] = $seq['diffs'][$i];
+ $this->mDiffMap[] = $seq['map'][$i];
+ }
+ $tail = $seq['tail'];
+ }
+ }
+
+ function diff( $t1, $t2 ) {
+ # Need to do a null concatenation with warnings off, due to bugs in the current version of xdiff
+ # "String is not zero-terminated"
+ wfSuppressWarnings();
+ $diff = xdiff_string_rabdiff( $t1, $t2 ) . '';
+ wfRestoreWarnings();
+ return $diff;
+ }
+
+ function patch( $base, $diff ) {
+ if ( function_exists( 'xdiff_string_bpatch' ) ) {
+ wfSuppressWarnings();
+ $text = xdiff_string_bpatch( $base, $diff ) . '';
+ wfRestoreWarnings();
+ return $text;
+ }
+
+ # Pure PHP implementation
+
+ $header = unpack( 'Vofp/Vcsize', substr( $diff, 0, 8 ) );
+
+ # Check the checksum if mhash is available
+ if ( extension_loaded( 'mhash' ) ) {
+ $ofp = mhash( MHASH_ADLER32, $base );
+ if ( $ofp !== substr( $diff, 0, 4 ) ) {
+ wfDebug( __METHOD__. ": incorrect base checksum\n" );
+ return false;
+ }
+ }
+ if ( $header['csize'] != strlen( $base ) ) {
+ wfDebug( __METHOD__. ": incorrect base length\n" );
+ return false;
+ }
+
+ $p = 8;
+ $out = '';
+ while ( $p < strlen( $diff ) ) {
+ $x = unpack( 'Cop', substr( $diff, $p, 1 ) );
+ $op = $x['op'];
+ ++$p;
+ switch ( $op ) {
+ case self::XDL_BDOP_INS:
+ $x = unpack( 'Csize', substr( $diff, $p, 1 ) );
+ $p++;
+ $out .= substr( $diff, $p, $x['size'] );
+ $p += $x['size'];
+ break;
+ case self::XDL_BDOP_INSB:
+ $x = unpack( 'Vcsize', substr( $diff, $p, 4 ) );
+ $p += 4;
+ $out .= substr( $diff, $p, $x['csize'] );
+ $p += $x['csize'];
+ break;
+ case self::XDL_BDOP_CPY:
+ $x = unpack( 'Voff/Vcsize', substr( $diff, $p, 8 ) );
+ $p += 8;
+ $out .= substr( $base, $x['off'], $x['csize'] );
+ break;
+ default:
+ wfDebug( __METHOD__.": invalid op\n" );
+ return false;
+ }
+ }
+ return $out;
+ }
+
+ function uncompress() {
+ if ( !$this->mDiffs ) {
+ return;
+ }
+ $tail = '';
+ for ( $diffKey = 0; $diffKey < count( $this->mDiffs ); $diffKey++ ) {
+ $textKey = $this->mDiffMap[$diffKey];
+ $text = $this->patch( $tail, $this->mDiffs[$diffKey] );
+ $this->mItems[$textKey] = $text;
+ $tail = $text;
+ }
+ }
+
+ function __sleep() {
+ $this->compress();
+ if ( !count( $this->mItems ) ) {
+ // Empty object
+ $info = false;
+ } else {
+ // Take forward differences to improve the compression ratio for sequences
+ $map = '';
+ $prev = 0;
+ foreach ( $this->mDiffMap as $i ) {
+ if ( $map !== '' ) {
+ $map .= ',';
+ }
+ $map .= $i - $prev;
+ $prev = $i;
+ }
+ $info = array(
+ 'diffs' => $this->mDiffs,
+ 'map' => $map
+ );
+ }
+ if ( isset( $this->mDefaultKey ) ) {
+ $info['default'] = $this->mDefaultKey;
+ }
+ $this->mCompressed = gzdeflate( serialize( $info ) );
+ return array( 'mCompressed' );
+ }
+
+ function __wakeup() {
+ // addItem() doesn't work if mItems is partially filled from mDiffs
+ $this->mFrozen = true;
+ $info = unserialize( gzinflate( $this->mCompressed ) );
+ unset( $this->mCompressed );
+
+ if ( !$info ) {
+ // Empty object
+ return;
+ }
+
+ if ( isset( $info['default'] ) ) {
+ $this->mDefaultKey = $info['default'];
+ }
+ $this->mDiffs = $info['diffs'];
+ if ( isset( $info['base'] ) ) {
+ // Old format
+ $this->mDiffMap = range( 0, count( $this->mDiffs ) - 1 );
+ array_unshift( $this->mDiffs,
+ pack( 'VVCV', 0, 0, self::XDL_BDOP_INSB, strlen( $info['base'] ) ) .
+ $info['base'] );
+ } else {
+ // New format
+ $map = explode( ',', $info['map'] );
+ $cur = 0;
+ $this->mDiffMap = array();
+ foreach ( $map as $i ) {
+ $cur += $i;
+ $this->mDiffMap[] = $cur;
+ }
+ }
+ $this->uncompress();
+ }
+
+ /**
+ * Helper function for compression jobs
+ * Returns true until the object is "full" and ready to be committed
+ */
+ function isHappy() {
+ return $this->mSize < $this->mMaxSize
+ && count( $this->mItems ) < $this->mMaxCount;
+ }
+
+}
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index 555a79b7..269d45ff 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -1,24 +1,48 @@
<?php
/**
+ * @defgroup HTTP HTTP
+ * @file
+ * @ingroup HTTP
+ */
+
+/**
* Various HTTP related functions
+ * @ingroup HTTP
*/
class Http {
- static function get( $url, $timeout = 'default' ) {
- return Http::request( "GET", $url, $timeout );
+
+ /**
+ * Simple wrapper for Http::request( 'GET' )
+ * @see Http::request()
+ */
+ public static function get( $url, $timeout = 'default', $opts = array() ) {
+ return Http::request( "GET", $url, $timeout, $opts );
}
- static function post( $url, $timeout = 'default' ) {
- return Http::request( "POST", $url, $timeout );
+ /**
+ * Simple wrapper for Http::request( 'POST' )
+ * @see Http::request()
+ */
+ public static function post( $url, $timeout = 'default', $opts = array() ) {
+ return Http::request( "POST", $url, $timeout, $opts );
}
/**
* Get the contents of a file by HTTP
- *
- * if $timeout is 'default', $wgHTTPTimeout is used
+ * @param $method string HTTP method. Usually GET/POST
+ * @param $url string Full URL to act on
+ * @param $timeout int Seconds to timeout. 'default' falls to $wgHTTPTimeout
+ * @param $curlOptions array Optional array of extra params to pass
+ * to curl_setopt()
*/
- static function request( $method, $url, $timeout = 'default' ) {
- global $wgHTTPTimeout, $wgHTTPProxy, $wgVersion, $wgTitle;
+ public static function request( $method, $url, $timeout = 'default', $curlOptions = array() ) {
+ global $wgHTTPTimeout, $wgHTTPProxy, $wgTitle;
+
+ // Go ahead and set the timeout if not otherwise specified
+ if ( $timeout == 'default' ) {
+ $timeout = $wgHTTPTimeout;
+ }
wfDebug( __METHOD__ . ": $method $url\n" );
# Use curl if available
@@ -30,13 +54,12 @@ class Http {
curl_setopt($c, CURLOPT_PROXY, $wgHTTPProxy);
}
- if ( $timeout == 'default' ) {
- $timeout = $wgHTTPTimeout;
- }
curl_setopt( $c, CURLOPT_TIMEOUT, $timeout );
- curl_setopt( $c, CURLOPT_USERAGENT, "MediaWiki/$wgVersion" );
- if ( $method == 'POST' )
+ curl_setopt( $c, CURLOPT_USERAGENT, self :: userAgent() );
+ if ( $method == 'POST' ) {
curl_setopt( $c, CURLOPT_POST, true );
+ curl_setopt( $c, CURLOPT_POSTFIELDS, '' );
+ }
else
curl_setopt( $c, CURLOPT_CUSTOMREQUEST, $method );
@@ -48,6 +71,12 @@ class Http {
if ( is_object( $wgTitle ) ) {
curl_setopt( $c, CURLOPT_REFERER, $wgTitle->getFullURL() );
}
+
+ if ( is_array( $curlOptions ) ) {
+ foreach( $curlOptions as $option => $value ) {
+ curl_setopt( $c, $option, $value );
+ }
+ }
ob_start();
curl_exec( $c );
@@ -55,20 +84,24 @@ class Http {
ob_end_clean();
# Don't return the text of error messages, return false on error
- if ( curl_getinfo( $c, CURLINFO_HTTP_CODE ) != 200 ) {
+ $retcode = curl_getinfo( $c, CURLINFO_HTTP_CODE );
+ if ( $retcode != 200 ) {
+ wfDebug( __METHOD__ . ": HTTP return code $retcode\n" );
$text = false;
}
# Don't return truncated output
- if ( curl_errno( $c ) != CURLE_OK ) {
+ $errno = curl_errno( $c );
+ if ( $errno != CURLE_OK ) {
+ $errstr = curl_error( $c );
+ wfDebug( __METHOD__ . ": CURL error code $errno: $errstr\n" );
$text = false;
}
curl_close( $c );
} else {
# Otherwise use file_get_contents...
- # This may take 3 minutes to time out, and doesn't have local fetch capabilities
+ # This doesn't have local fetch capabilities...
- global $wgVersion;
- $headers = array( "User-Agent: MediaWiki/$wgVersion" );
+ $headers = array( "User-Agent: " . self :: userAgent() );
if( strcasecmp( $method, 'post' ) == 0 ) {
// Required for HTTP 1.0 POSTs
$headers[] = "Content-Length: 0";
@@ -76,20 +109,21 @@ class Http {
$opts = array(
'http' => array(
'method' => $method,
- 'header' => implode( "\r\n", $headers ) ) );
+ 'header' => implode( "\r\n", $headers ),
+ 'timeout' => $timeout ) );
$ctx = stream_context_create($opts);
- $url_fopen = ini_set( 'allow_url_fopen', 1 );
$text = file_get_contents( $url, false, $ctx );
- ini_set( 'allow_url_fopen', $url_fopen );
}
return $text;
}
/**
* Check if the URL can be served by localhost
+ * @param $url string Full url to check
+ * @return bool
*/
- static function isLocalURL( $url ) {
+ public static function isLocalURL( $url ) {
global $wgCommandLineMode, $wgConf;
if ( $wgCommandLineMode ) {
return false;
@@ -117,4 +151,12 @@ class Http {
}
return false;
}
+
+ /**
+ * Return a standard user-agent we can use for external requests.
+ */
+ public static function userAgent() {
+ global $wgVersion;
+ return "MediaWiki/$wgVersion";
+ }
}
diff --git a/includes/IEContentAnalyzer.php b/includes/IEContentAnalyzer.php
index 59abc6a6..df4d36f0 100644
--- a/includes/IEContentAnalyzer.php
+++ b/includes/IEContentAnalyzer.php
@@ -569,8 +569,9 @@ class IEContentAnalyzer {
$chunk3 = substr( $chunk, 0, 3 );
$chunk4 = substr( $chunk, 0, 4 );
$chunk5 = substr( $chunk, 0, 5 );
+ $chunk5uc = strtoupper( $chunk5 );
$chunk8 = substr( $chunk, 0, 8 );
- if ( $chunk5 == 'GIF87' || $chunk5 == 'GIF89' ) {
+ if ( $chunk5uc == 'GIF87' || $chunk5uc == 'GIF89' ) {
return 'image/gif';
}
if ( $chunk2 == "\xff\xd8" ) {
@@ -579,7 +580,7 @@ class IEContentAnalyzer {
if ( $chunk2 == 'BM'
&& substr( $chunk, 6, 2 ) == "\000\000"
- && substr( $chunk, 8, 2 ) != "\000\000" )
+ && substr( $chunk, 8, 2 ) == "\000\000" )
{
return 'image/bmp'; // another non-standard MIME
}
@@ -800,7 +801,7 @@ class IEContentAnalyzer {
}
// BinHex
- if ( !strncasecmp( $remainder, $binhexMagic, strlen( $binhexMagic ) ) ) {
+ if ( !strncmp( $remainder, $binhexMagic, strlen( $binhexMagic ) ) ) {
$found['binhex'] = true;
}
}
diff --git a/includes/IP.php b/includes/IP.php
index e76f66c1..e5973c2b 100644
--- a/includes/IP.php
+++ b/includes/IP.php
@@ -141,7 +141,7 @@ class IP {
public static function toOctet( $ip_int ) {
// Convert to padded uppercase hex
$ip_hex = wfBaseConvert($ip_int, 10, 16, 32, false);
- // Seperate into 8 octets
+ // Separate into 8 octets
$ip_oct = substr( $ip_hex, 0, 4 );
for ($n=1; $n < 8; $n++) {
$ip_oct .= ':' . substr($ip_hex, 4*$n, 4);
@@ -150,6 +150,41 @@ class IP {
$ip_oct = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip_oct );
return $ip_oct;
}
+
+ /**
+ * Given a hexadecimal number, returns to an IPv6 address in octet notation
+ * @param $ip string hex IP
+ * @return string
+ */
+ public static function HextoOctet( $ip_hex ) {
+ // Convert to padded uppercase hex
+ $ip_hex = str_pad( strtoupper($ip_hex), 32, '0');
+ // Separate into 8 octets
+ $ip_oct = substr( $ip_hex, 0, 4 );
+ for ($n=1; $n < 8; $n++) {
+ $ip_oct .= ':' . substr($ip_hex, 4*$n, 4);
+ }
+ // NO leading zeroes
+ $ip_oct = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip_oct );
+ return $ip_oct;
+ }
+
+ /**
+ * Converts a hexadecimal number to an IPv4 address in octet notation
+ * @param $ip string Hex IP
+ * @return string
+ */
+ public static function hexToQuad( $ip ) {
+ // Converts a hexadecimal IP to nnn.nnn.nnn.nnn format
+ $dec = wfBaseConvert( $ip, 16, 10 );
+ $parts[3] = $dec % 256;
+ $dec /= 256;
+ $parts[2] = $dec % 256;
+ $dec /= 256;
+ $parts[1] = $dec % 256;
+ $parts[0] = $dec / 256;
+ return implode( '.', array_reverse( $parts ) );
+ }
/**
* Convert a network specification in IPv6 CIDR notation to an integer network and a number of bits
@@ -320,7 +355,7 @@ class IP {
public static function toHex( $ip ) {
$n = self::toUnsigned( $ip );
if ( $n !== false ) {
- $n = ( self::isIPv6($ip) ) ? "v6-" . wfBaseConvert( $n, 10, 16, 32, false ) : wfBaseConvert( $n, 10, 16, 8, false );
+ $n = self::isIPv6($ip) ? "v6-" . wfBaseConvert( $n, 10, 16, 32, false ) : wfBaseConvert( $n, 10, 16, 8, false );
}
return $n;
}
@@ -426,12 +461,16 @@ class IP {
} elseif ( strpos( $range, '-' ) !== false ) {
# Explicit range
list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
- $start = self::toUnsigned( $start ); $end = self::toUnsigned( $end );
- if ( $start > $end ) {
- $start = $end = false;
+ if( self::isIPAddress( $start ) && self::isIPAddress( $end ) ) {
+ $start = self::toUnsigned( $start ); $end = self::toUnsigned( $end );
+ if ( $start > $end ) {
+ $start = $end = false;
+ } else {
+ $start = sprintf( '%08X', $start );
+ $end = sprintf( '%08X', $end );
+ }
} else {
- $start = sprintf( '%08X', $start );
- $end = sprintf( '%08X', $end );
+ $start = $end = false;
}
} else {
# Single IP
diff --git a/includes/ImageFunctions.php b/includes/ImageFunctions.php
index af05c1c9..73d935a7 100644
--- a/includes/ImageFunctions.php
+++ b/includes/ImageFunctions.php
@@ -4,9 +4,10 @@
* http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers
*
* @param $length String: CSS/SVG length.
- * @return Integer: length in pixels
+ * @param $viewportSize: Float optional scale for percentage units...
+ * @return float: length in pixels
*/
-function wfScaleSVGUnit( $length ) {
+function wfScaleSVGUnit( $length, $viewportSize=512 ) {
static $unitLength = array(
'px' => 1.0,
'pt' => 1.25,
@@ -14,17 +15,74 @@ function wfScaleSVGUnit( $length ) {
'mm' => 3.543307,
'cm' => 35.43307,
'in' => 90.0,
+ 'em' => 16.0, // fake it?
+ 'ex' => 12.0, // fake it?
'' => 1.0, // "User units" pixels by default
- '%' => 2.0, // Fake it!
);
$matches = array();
- if( preg_match( '/^(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)$/', $length, $matches ) ) {
+ if( preg_match( '/^\s*(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/', $length, $matches ) ) {
$length = floatval( $matches[1] );
$unit = $matches[2];
- return round( $length * $unitLength[$unit] );
+ if( $unit == '%' ) {
+ return $length * 0.01 * $viewportSize;
+ } else {
+ return $length * $unitLength[$unit];
+ }
} else {
// Assume pixels
- return round( floatval( $length ) );
+ return floatval( $length );
+ }
+}
+
+class XmlSizeFilter {
+ const DEFAULT_WIDTH = 512;
+ const DEFAULT_HEIGHT = 512;
+ var $first = true;
+ var $width = self::DEFAULT_WIDTH;
+ var $height = self::DEFAULT_HEIGHT;
+ function filter( $name, $attribs ) {
+ if( $this->first ) {
+ $defaultWidth = self::DEFAULT_WIDTH;
+ $defaultHeight = self::DEFAULT_HEIGHT;
+ $aspect = 1.0;
+ $width = null;
+ $height = null;
+
+ if( isset( $attribs['viewBox'] ) ) {
+ // min-x min-y width height
+ $viewBox = preg_split( '/\s+/', trim( $attribs['viewBox'] ) );
+ if( count( $viewBox ) == 4 ) {
+ $viewWidth = wfScaleSVGUnit( $viewBox[2] );
+ $viewHeight = wfScaleSVGUnit( $viewBox[3] );
+ if( $viewWidth > 0 && $viewHeight > 0 ) {
+ $aspect = $viewWidth / $viewHeight;
+ $defaultHeight = $defaultWidth / $aspect;
+ }
+ }
+ }
+ if( isset( $attribs['width'] ) ) {
+ $width = wfScaleSVGUnit( $attribs['width'], $defaultWidth );
+ }
+ if( isset( $attribs['height'] ) ) {
+ $height = wfScaleSVGUnit( $attribs['height'], $defaultHeight );
+ }
+
+ if( !isset( $width ) && !isset( $height ) ) {
+ $width = $defaultWidth;
+ $height = $width / $aspect;
+ } elseif( isset( $width ) && !isset( $height ) ) {
+ $height = $width / $aspect;
+ } elseif( isset( $height ) && !isset( $width ) ) {
+ $width = $height * $aspect;
+ }
+
+ if( $width > 0 && $height > 0 ) {
+ $this->width = intval( round( $width ) );
+ $this->height = intval( round( $height ) );
+ }
+
+ $this->first = false;
+ }
}
}
@@ -38,30 +96,14 @@ function wfScaleSVGUnit( $length ) {
* @return array
*/
function wfGetSVGsize( $filename ) {
- $width = 256;
- $height = 256;
-
- // Read a chunk of the file
- $f = fopen( $filename, "rt" );
- if( !$f ) return false;
- $chunk = fread( $f, 4096 );
- fclose( $f );
-
- // Uber-crappy hack! Run through a real XML parser.
- $matches = array();
- if( !preg_match( '/<svg\s*([^>]*)\s*>/s', $chunk, $matches ) ) {
- return false;
- }
- $tag = $matches[1];
- if( preg_match( '/(?:^|\s)width\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
- $width = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
+ $filter = new XmlSizeFilter();
+ $xml = new XmlTypeCheck( $filename, array( $filter, 'filter' ) );
+ if( $xml->wellFormed ) {
+ return array( $filter->width, $filter->height, 'SVG',
+ "width=\"$filter->width\" height=\"$filter->height\"" );
}
- if( preg_match( '/(?:^|\s)height\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) {
- $height = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) );
- }
-
- return array( $width, $height, 'SVG',
- "width=\"$width\" height=\"$height\"" );
+
+ return false;
}
/**
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
index 492a3e06..f3f525c1 100644
--- a/includes/ImageGallery.php
+++ b/includes/ImageGallery.php
@@ -244,7 +244,7 @@ class ImageGallery
$img = wfFindFile( $nt, $time );
- if( $nt->getNamespace() != NS_IMAGE || !$img ) {
+ if( $nt->getNamespace() != NS_FILE || !$img ) {
# We're dealing with a non-image, spit out the name and be done with it.
$thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
. htmlspecialchars( $nt->getText() ) . '</div>';
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index 30fcf13e..314d478e 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -22,22 +22,28 @@ class ImagePage extends Article {
$this->dupes = null;
$this->repo = null;
}
+
+ public function setFile( $file ) {
+ $this->displayImg = $file;
+ $this->img = $file;
+ $this->fileLoaded = true;
+ }
protected function loadFile() {
- if ( $this->fileLoaded ) {
+ if( $this->fileLoaded ) {
return true;
}
$this->fileLoaded = true;
$this->displayImg = $this->img = false;
wfRunHooks( 'ImagePageFindFile', array( $this, &$this->img, &$this->displayImg ) );
- if ( !$this->img ) {
+ if( !$this->img ) {
$this->img = wfFindFile( $this->mTitle );
- if ( !$this->img ) {
+ if( !$this->img ) {
$this->img = wfLocalFile( $this->mTitle );
}
}
- if ( !$this->displayImg ) {
+ if( !$this->displayImg ) {
$this->displayImg = $this->img;
}
$this->repo = $this->img->getRepo();
@@ -47,18 +53,18 @@ class ImagePage extends Article {
* Handler for action=render
* Include body text only; none of the image extras
*/
- function render() {
+ public function render() {
global $wgOut;
$wgOut->setArticleBodyOnly( true );
parent::view();
}
- function view() {
+ public 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() ) {
+ if( $this->mTitle->getNamespace() == NS_FILE && $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();
@@ -66,8 +72,8 @@ class ImagePage extends Article {
// 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 );
+ $wgOut->addHTML( $this->viewRedirect( Title::makeTitle( NS_FILE, $this->img->getName() ),
+ /* $appendSubtitle */ true, /* $forceKnown */ true ) );
$this->viewUpdates();
return;
}
@@ -76,10 +82,10 @@ class ImagePage extends Article {
$diff = $wgRequest->getVal( 'diff' );
$diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
- if ( $this->mTitle->getNamespace() != NS_IMAGE || ( isset( $diff ) && $diffOnly ) )
+ if( $this->mTitle->getNamespace() != NS_FILE || ( isset( $diff ) && $diffOnly ) )
return Article::view();
- if ( $wgShowEXIF && $this->displayImg->exists() ) {
+ if( $wgShowEXIF && $this->displayImg->exists() ) {
// FIXME: bad interface, see note on MediaHandler::formatMetadata().
$formattedMetadata = $this->displayImg->formatMetadata();
$showmeta = $formattedMetadata !== false;
@@ -87,24 +93,25 @@ class ImagePage extends Article {
$showmeta = false;
}
- if ( $this->displayImg->exists() )
+ if( !$diff && $this->displayImg->exists() )
$wgOut->addHTML( $this->showTOC($showmeta) );
- $this->openShowImage();
+ if( !$diff )
+ $this->openShowImage();
# No need to display noarticletext, we use our own message, output in openShowImage()
- if ( $this->getID() ) {
+ if( $this->getID() ) {
Article::view();
} else {
# Just need to set the right headers
$wgOut->setArticleFlag( true );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
$this->viewUpdates();
}
# Show shared description, if needed
- if ( $this->mExtraDescription ) {
+ if( $this->mExtraDescription ) {
$fol = wfMsgNoTrans( 'shareddescriptionfollows' );
if( $fol != '-' && !wfEmptyMsg( 'shareddescriptionfollows', $fol ) ) {
$wgOut->addWikiText( $fol );
@@ -118,21 +125,21 @@ class ImagePage extends Article {
$this->imageHistory();
// TODO: Cleanup the following
- $wgOut->addHTML( Xml::element( 'h2',
- array( 'id' => 'filelinks' ),
+ $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() ) {
+ if( $this->img->isLocal() ) {
$this->imageRedirects();
}
$this->imageLinks();
- if ( $showmeta ) {
+ if( $showmeta ) {
global $wgStylePath, $wgStyleVersion;
- $expand = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-expand' ) ) );
- $collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) );
+ $expand = htmlspecialchars( Xml::escapeJsString( wfMsg( 'metadata-expand' ) ) );
+ $collapse = htmlspecialchars( Xml::escapeJsString( wfMsg( 'metadata-collapse' ) ) );
$wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ). "\n" );
$wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
$wgOut->addScriptFile( 'metadata.js' );
@@ -143,32 +150,32 @@ class ImagePage extends Article {
public function getRedirectTarget() {
$this->loadFile();
- if ( $this->img->isLocal() ) {
+ if( $this->img->isLocal() ) {
return parent::getRedirectTarget();
}
// Foreign image page
$from = $this->img->getRedirected();
$to = $this->img->getName();
- if ( $from == $to ) {
+ if( $from == $to ) {
return null;
}
- return $this->mRedirectTarget = Title::makeTitle( NS_IMAGE, $to );
+ return $this->mRedirectTarget = Title::makeTitle( NS_FILE, $to );
}
public function followRedirect() {
$this->loadFile();
- if ( $this->img->isLocal() ) {
+ if( $this->img->isLocal() ) {
return parent::followRedirect();
}
$from = $this->img->getRedirected();
$to = $this->img->getName();
- if ( $from == $to ) {
+ if( $from == $to ) {
return false;
}
- return Title::makeTitle( NS_IMAGE, $to );
+ return Title::makeTitle( NS_FILE, $to );
}
public function isRedirect( $text = false ) {
$this->loadFile();
- if ( $this->img->isLocal() )
+ if( $this->img->isLocal() )
return parent::isRedirect( $text );
return (bool)$this->img->getRedirected();
@@ -191,10 +198,10 @@ class ImagePage extends Article {
public function getDuplicates() {
$this->loadFile();
- if ( !is_null($this->dupes) ) {
+ if( !is_null($this->dupes) ) {
return $this->dupes;
}
- if ( !( $hash = $this->img->getSha1() ) ) {
+ if( !( $hash = $this->img->getSha1() ) ) {
return $this->dupes = array();
}
$dupes = RepoGroup::singleton()->findBySha1( $hash );
@@ -203,9 +210,9 @@ class ImagePage extends Article {
$size = $this->img->getSize();
foreach ( $dupes as $index => $file ) {
$key = $file->getRepoName().':'.$file->getName();
- if ( $key == $self )
+ if( $key == $self )
unset( $dupes[$index] );
- if ( $file->getSize() != $size )
+ if( $file->getSize() != $size )
unset( $dupes[$index] );
}
return $this->dupes = $dupes;
@@ -216,15 +223,13 @@ class ImagePage extends Article {
/**
* Create the TOC
*
- * @access private
- *
* @param bool $metadata Whether or not to show the metadata link
* @return string
*/
- function showTOC( $metadata ) {
+ protected function showTOC( $metadata ) {
global $wgLang;
$r = '<ul id="filetoc">
- <li><a href="#file">' . $wgLang->getNsText( NS_IMAGE ) . '</a></li>
+ <li><a href="#file">' . $wgLang->getNsText( NS_FILE ) . '</a></li>
<li><a href="#filehistory">' . wfMsgHtml( 'filehist' ) . '</a></li>
<li><a href="#filelinks">' . wfMsgHtml( 'imagelinks' ) . '</a></li>' .
($metadata ? ' <li><a href="#metadata">' . wfMsgHtml( 'metadata' ) . '</a></li>' : '') . '
@@ -237,16 +242,15 @@ class ImagePage extends Article {
*
* FIXME: bad interface, see note on MediaHandler::formatMetadata().
*
- * @access private
- *
* @param array $exif The array containing the EXIF data
* @return string
*/
- function makeMetadataTable( $metadata ) {
+ protected function makeMetadataTable( $metadata ) {
$r = wfMsg( 'metadata-help' ) . "\n\n";
$r .= "{| id=mw_metadata class=mw_metadata\n";
foreach ( $metadata as $type => $stuff ) {
foreach ( $stuff as $v ) {
+ # FIXME, why is this using escapeId for a class?!
$class = Sanitizer::escapeId( $v['id'] );
if( $type == 'collapsed' ) {
$class .= ' collapsable';
@@ -266,7 +270,7 @@ class ImagePage extends Article {
* Omit noarticletext if sharedupload; text will be fetched from the
* shared upload server if possible.
*/
- function getContent() {
+ public function getContent() {
$this->loadFile();
if( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
return '';
@@ -274,7 +278,7 @@ class ImagePage extends Article {
return Article::getContent();
}
- function openShowImage() {
+ protected function openShowImage() {
global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang, $wgContLang;
$this->loadFile();
@@ -298,10 +302,10 @@ class ImagePage extends Article {
$sk = $wgUser->getSkin();
$dirmark = $wgContLang->getDirMark();
- if ( $this->displayImg->exists() ) {
+ if( $this->displayImg->exists() ) {
# image
$page = $wgRequest->getIntOrNull( 'page' );
- if ( is_null( $page ) ) {
+ if( is_null( $page ) ) {
$params = array();
$page = 1;
} else {
@@ -318,16 +322,16 @@ class ImagePage extends Article {
wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) ) ;
- if ( $this->displayImg->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->displayImg->getSize() ), $mime );
# We'll show a thumbnail of this image
- if ( $width > $maxWidth || $height > $maxHeight ) {
+ if( $width > $maxWidth || $height > $maxHeight ) {
# Calculate the thumbnail size.
# First case, the limiting factor is the width, not the height.
- if ( $width / $height >= $maxWidth / $maxHeight ) {
+ if( $width / $height >= $maxWidth / $maxHeight ) {
$height = round( $height * $maxWidth / $width);
$width = $maxWidth;
# Note that $height <= $maxHeight now.
@@ -339,8 +343,10 @@ class ImagePage extends Article {
# because of rounding.
}
$msgbig = wfMsgHtml( 'show-big-image' );
- $msgsmall = wfMsgExt( 'show-big-image-thumb',
- array( 'parseinline' ), $wgLang->formatNum( $width ), $wgLang->formatNum( $height ) );
+ $msgsmall = wfMsgExt( 'show-big-image-thumb', 'parseinline',
+ $wgLang->formatNum( $width ),
+ $wgLang->formatNum( $height )
+ );
} else {
# Image is small enough to show full size on image page
$msgbig = htmlspecialchars( $this->displayImg->getName() );
@@ -359,11 +365,11 @@ class ImagePage extends Article {
'<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . "$dirmark " . $longDesc;
}
- if ( $this->displayImg->isMultipage() ) {
+ if( $this->displayImg->isMultipage() ) {
$wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
}
- if ( $thumbnail ) {
+ if( $thumbnail ) {
$options = array(
'alt' => $this->displayImg->getTitle()->getPrefixedText(),
'file-link' => true,
@@ -373,10 +379,10 @@ class ImagePage extends Article {
$anchorclose . '</div>' );
}
- if ( $this->displayImg->isMultipage() ) {
+ if( $this->displayImg->isMultipage() ) {
$count = $this->displayImg->pageCount();
- if ( $page > 1 ) {
+ if( $page > 1 ) {
$label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
$link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
$thumb1 = $sk->makeThumbLinkObj( $this->mTitle, $this->displayImg, $link, $label, 'none',
@@ -385,7 +391,7 @@ class ImagePage extends Article {
$thumb1 = '';
}
- if ( $page < $count ) {
+ if( $page < $count ) {
$label = wfMsg( 'imgmultipagenext' );
$link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
$thumb2 = $sk->makeThumbLinkObj( $this->mTitle, $this->displayImg, $link, $label, 'none',
@@ -422,7 +428,7 @@ class ImagePage extends Article {
}
} else {
#if direct link is allowed but it's not a renderable image, show an icon.
- if ( $this->displayImg->isSafeFile() ) {
+ if( $this->displayImg->isSafeFile() ) {
$icon= $this->displayImg->iconThumb();
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
@@ -434,10 +440,10 @@ class ImagePage extends Article {
}
- if ($showLink) {
+ if($showLink) {
$filename = wfEscapeWikiText( $this->displayImg->getName() );
- if ( !$this->displayImg->isSafeFile() ) {
+ if( !$this->displayImg->isSafeFile() ) {
$warning = wfMsgNoTrans( 'mediawarning' );
$wgOut->addWikiText( <<<EOT
<div class="fullMedia">
@@ -474,7 +480,7 @@ EOT
/**
* Show a notice that the file is from a shared repository
*/
- function printSharedImageText() {
+ protected function printSharedImageText() {
global $wgOut, $wgUser;
$this->loadFile();
@@ -482,12 +488,12 @@ EOT
$descUrl = $this->img->getDescriptionUrl();
$descText = $this->img->getDescriptionText();
$s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml( 'sharedupload' );
- if ( $descUrl ) {
+ if( $descUrl ) {
$sk = $wgUser->getSkin();
$link = $sk->makeExternalLink( $descUrl, wfMsg( 'shareduploadwiki-linktext' ) );
$msg = ( $descText ) ? 'shareduploadwiki-desc' : 'shareduploadwiki';
$msg = wfMsgExt( $msg, array( 'parseinline', 'replaceafter' ), $link );
- if ( $msg != '-' ) {
+ if( $msg != '-' ) {
# Show message only if not voided by local sysops
$s .= $msg;
}
@@ -495,7 +501,7 @@ EOT
$s .= "</div>";
$wgOut->addHTML( $s );
- if ( $descText ) {
+ if( $descText ) {
$this->mExtraDescription = $descText;
}
}
@@ -503,7 +509,7 @@ EOT
/*
* Check for files with the same name on the foreign repos.
*/
- function checkSharedConflict() {
+ protected function checkSharedConflict() {
global $wgOut, $wgUser;
$repoGroup = RepoGroup::singleton();
@@ -538,7 +544,7 @@ EOT
}
}
- function checkSharedConflictCallback( $repo ) {
+ public function checkSharedConflictCallback( $repo ) {
$this->loadFile();
$dupfile = $repo->newFile( $this->img->getTitle() );
if( $dupfile && $dupfile->exists() ) {
@@ -548,7 +554,7 @@ EOT
return false;
}
- function getUploadUrl() {
+ public function getUploadUrl() {
$this->loadFile();
$uploadTitle = SpecialPage::getTitleFor( 'Upload' );
return $uploadTitle->getFullUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) );
@@ -558,7 +564,7 @@ EOT
* Print out the various links at the bottom of the image page, e.g. reupload,
* external editing (and instructions link) etc.
*/
- function uploadLinksBox() {
+ protected function uploadLinksBox() {
global $wgUser, $wgOut;
$this->loadFile();
@@ -567,69 +573,49 @@ EOT
$sk = $wgUser->getSkin();
- $wgOut->addHtml( '<br /><ul>' );
+ $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>" );
+ $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>" );
+ $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( '<li>' . $elink . ' <small>' . wfMsgExt( 'edit-externally-help', array( 'parseinline' ) ) . '</small></li>' );
- $wgOut->addHtml( '</ul>' );
+ $wgOut->addHTML( '</ul>' );
}
- function closeShowImage()
- {
- # For overloading
-
- }
+ protected function closeShowImage() {} # For overloading
/**
* If the page we've just displayed is in the "Image" namespace,
* we follow it with an upload history of the image and its usage.
*/
- function imageHistory()
- {
+ protected function imageHistory() {
global $wgOut, $wgUseExternalEditor;
$this->loadFile();
- if ( $this->img->exists() ) {
- $list = new ImageHistoryList( $this );
- $file = $this->img;
- $dims = $file->getDimensionsString();
- $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, $file );
- }
- $s .= $list->endImageHistoryList();
- } else { $s=''; }
- $wgOut->addHTML( $s );
+ $pager = new ImageHistoryPseudoPager( $this );
+ $wgOut->addHTML( $pager->getBody() );
- $this->img->resetHistory(); // free db resources
+ $this->img->resetHistory(); // free db resources
# Exist check because we don't want to show this on pages where an image
# doesn't exist along with the noimage message, that would suck. -ævar
if( $wgUseExternalEditor && $this->img->exists() ) {
$this->uploadLinksBox();
}
-
}
- function imageLinks()
- {
- global $wgUser, $wgOut;
+ protected function imageLinks() {
+ global $wgUser, $wgOut, $wgLang;
$limit = 100;
@@ -643,21 +629,30 @@ EOT
array( 'LIMIT' => $limit + 1)
);
$count = $dbr->numRows( $res );
- if ( $count == 0 ) {
+ if( $count == 0 ) {
$wgOut->addHTML( "<div id='mw-imagepage-nolinkstoimage'>\n" );
$wgOut->addWikiMsg( 'nolinkstoimage' );
$wgOut->addHTML( "</div>\n" );
return;
}
+
$wgOut->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
- $wgOut->addWikiMsg( 'linkstoimage', $count );
- $wgOut->addHTML( "<ul class='mw-imagepage-linktoimage'>\n" );
+ if( $count <= $limit - 1 ) {
+ $wgOut->addWikiMsg( 'linkstoimage', $count );
+ } else {
+ // More links than the limit. Add a link to [[Special:Whatlinkshere]]
+ $wgOut->addWikiMsg( 'linkstoimage-more',
+ $wgLang->formatNum( $limit ),
+ $this->mTitle->getPrefixedDBkey()
+ );
+ }
+ $wgOut->addHTML( "<ul class='mw-imagepage-linkstoimage'>\n" );
$sk = $wgUser->getSkin();
$count = 0;
while ( $s = $res->fetchObject() ) {
$count++;
- if ( $count <= $limit ) {
+ 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, "" );
@@ -668,19 +663,20 @@ EOT
$res->free();
// Add a links to [[Special:Whatlinkshere]]
- if ( $count > $limit )
+ if( $count > $limit )
$wgOut->addWikiMsg( 'morelinkstoimage', $this->mTitle->getPrefixedDBkey() );
}
- function imageRedirects()
- {
- global $wgUser, $wgOut;
+ protected function imageRedirects() {
+ global $wgUser, $wgOut, $wgLang;
- $redirects = $this->getTitle()->getRedirectsHere( NS_IMAGE );
- if ( count( $redirects ) == 0 ) return;
+ $redirects = $this->getTitle()->getRedirectsHere( NS_FILE );
+ if( count( $redirects ) == 0 ) return;
$wgOut->addHTML( "<div id='mw-imagepage-section-redirectstofile'>\n" );
- $wgOut->addWikiMsg( 'redirectstofile', count( $redirects ) );
+ $wgOut->addWikiMsg( 'redirectstofile',
+ $wgLang->formatNum( count( $redirects ) )
+ );
$wgOut->addHTML( "<ul class='mw-imagepage-redirectstofile'>\n" );
$sk = $wgUser->getSkin();
@@ -692,25 +688,28 @@ EOT
}
- function imageDupes() {
- global $wgOut, $wgUser;
+ protected function imageDupes() {
+ global $wgOut, $wgUser, $wgLang;
$this->loadFile();
$dupes = $this->getDuplicates();
- if ( count( $dupes ) == 0 ) return;
+ if( count( $dupes ) == 0 ) return;
$wgOut->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
- $wgOut->addWikiMsg( 'duplicatesoffile', count( $dupes ) );
+ $wgOut->addWikiMsg( 'duplicatesoffile',
+ $wgLang->formatNum( count( $dupes ) )
+ );
$wgOut->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
$sk = $wgUser->getSkin();
foreach ( $dupes as $file ) {
- if ( $file->isLocal() )
+ if( $file->isLocal() )
$link = $sk->makeKnownLinkObj( $file->getTitle(), "" );
- else
- $link = $sk->makeExternalLink( $file->getDescriptionUrl(),
+ else {
+ $link = $sk->makeExternalLink( $file->getDescriptionUrl(),
$file->getTitle()->getPrefixedText() );
+ }
$wgOut->addHTML( "<li>{$link}</li>\n" );
}
$wgOut->addHTML( "</ul></div>\n" );
@@ -742,7 +741,7 @@ EOT
/**
* Override handling of action=purge
*/
- function doPurge() {
+ public function doPurge() {
$this->loadFile();
if( $this->img->exists() ) {
wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
@@ -762,7 +761,7 @@ EOT
function showError( $description ) {
global $wgOut;
$wgOut->setPageTitle( wfMsg( "internalerror" ) );
- $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setRobotPolicy( "noindex,nofollow" );
$wgOut->setArticleRelated( false );
$wgOut->enableClientCache( false );
$wgOut->addWikiText( $description );
@@ -788,34 +787,36 @@ class ImageHistoryList {
$this->imagePage = $imagePage;
}
- function getImagePage() {
+ public function getImagePage() {
return $this->imagePage;
}
- function getSkin() {
+ public function getSkin() {
return $this->skin;
}
- function getFile() {
+ public function getFile() {
return $this->img;
}
- public function beginImageHistoryList() {
+ public function beginImageHistoryList( $navLinks = '' ) {
global $wgOut, $wgUser;
return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) )
. $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) )
+ . $navLinks
. Xml::openElement( 'table', array( 'class' => 'filehistory' ) ) . "\n"
. '<tr><td></td>'
. ( $this->current->isLocal() && ($wgUser->isAllowed('delete') || $wgUser->isAllowed('deleterevision') ) ? '<td></td>' : '' )
. '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-thumb' ) . '</th>'
. '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
- . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
- . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
. "</tr>\n";
}
- public function endImageHistoryList() {
- return "</table>\n";
+ public function endImageHistoryList( $navLinks = '' ) {
+ return "</table>\n$navLinks\n";
}
public function imageHistoryLine( $iscur, $file ) {
@@ -910,6 +911,21 @@ class ImageHistoryList {
$row .= Xml::element( 'a', array( 'href' => $url ), $wgLang->timeAndDate( $timestamp, true ) );
}
+ // Thumbnail
+ if( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE ) && !$file->isDeleted( File::DELETED_FILE ) ) {
+ $params = array(
+ 'width' => '120',
+ 'height' => '120',
+ );
+ $thumbnail = $file->transform( $params );
+ $options = array(
+ 'alt' => wfMsg( 'filehist-thumbtext', $wgLang->timeAndDate( $timestamp, true ) ),
+ 'file-link' => true,
+ );
+ $row .= '</td><td>' . $thumbnail->toHtml( $options );
+ } else {
+ $row .= '</td><td>' . wfMsgHtml( 'filehist-nothumb' );
+ }
$row .= "</td><td>";
// Image dimensions
@@ -934,7 +950,7 @@ class ImageHistoryList {
$row .= '</td><td>';
// Don't show deleted descriptions
- if ( $file->isDeleted(File::DELETED_COMMENT) ) {
+ if( $file->isDeleted(File::DELETED_COMMENT) ) {
$row .= '<span class="history-deleted">' . wfMsgHtml('rev-deleted-comment') . '</span>';
} else {
$row .= $this->skin->commentBlock( $description, $this->title );
@@ -947,3 +963,128 @@ class ImageHistoryList {
return "<tr{$classAttr}>{$row}</tr>\n";
}
}
+
+class ImageHistoryPseudoPager extends ReverseChronologicalPager {
+ function __construct( $imagePage ) {
+ parent::__construct();
+ $this->mImagePage = $imagePage;
+ $this->mTitle = clone( $imagePage->getTitle() );
+ $this->mTitle->setFragment( '#filehistory' );
+ $this->mImg = NULL;
+ $this->mHist = array();
+ $this->mRange = array( 0, 0 ); // display range
+ }
+
+ function getTitle() {
+ return $this->mTitle;
+ }
+
+ function getQueryInfo() {
+ return false;
+ }
+
+ function getIndexField() {
+ return '';
+ }
+
+ function formatRow( $row ) {
+ return '';
+ }
+
+ function getBody() {
+ $s = '';
+ $this->doQuery();
+ if( count($this->mHist) ) {
+ $list = new ImageHistoryList( $this->mImagePage );
+ # Generate prev/next links
+ $navLink = $this->getNavigationBar();
+ $s = $list->beginImageHistoryList($navLink);
+ // Skip rows there just for paging links
+ for( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
+ $file = $this->mHist[$i];
+ $s .= $list->imageHistoryLine( !$file->isOld(), $file );
+ }
+ $s .= $list->endImageHistoryList($navLink);
+ }
+ return $s;
+ }
+
+ function doQuery() {
+ if( $this->mQueryDone ) return;
+ $this->mImg = $this->mImagePage->getFile(); // ensure loading
+ if( !$this->mImg->exists() ) {
+ return;
+ }
+ $queryLimit = $this->mLimit + 1; // limit plus extra row
+ if( $this->mIsBackwards ) {
+ // Fetch the file history
+ $this->mHist = $this->mImg->getHistory($queryLimit,null,$this->mOffset,false);
+ // The current rev may not meet the offset/limit
+ $numRows = count($this->mHist);
+ if( $numRows <= $this->mLimit && $this->mImg->getTimestamp() > $this->mOffset ) {
+ $this->mHist = array_merge( array($this->mImg), $this->mHist );
+ }
+ } else {
+ // The current rev may not meet the offset
+ if( !$this->mOffset || $this->mImg->getTimestamp() < $this->mOffset ) {
+ $this->mHist[] = $this->mImg;
+ }
+ // Old image versions (fetch extra row for nav links)
+ $oiLimit = count($this->mHist) ? $this->mLimit : $this->mLimit+1;
+ // Fetch the file history
+ $this->mHist = array_merge( $this->mHist,
+ $this->mImg->getHistory($oiLimit,$this->mOffset,null,false) );
+ }
+ $numRows = count($this->mHist); // Total number of query results
+ if( $numRows ) {
+ # Index value of top item in the list
+ $firstIndex = $this->mIsBackwards ?
+ $this->mHist[$numRows-1]->getTimestamp() : $this->mHist[0]->getTimestamp();
+ # Discard the extra result row if there is one
+ if( $numRows > $this->mLimit && $numRows > 1 ) {
+ if( $this->mIsBackwards ) {
+ # Index value of item past the index
+ $this->mPastTheEndIndex = $this->mHist[0]->getTimestamp();
+ # Index value of bottom item in the list
+ $lastIndex = $this->mHist[1]->getTimestamp();
+ # Display range
+ $this->mRange = array( 1, $numRows-1 );
+ } else {
+ # Index value of item past the index
+ $this->mPastTheEndIndex = $this->mHist[$numRows-1]->getTimestamp();
+ # Index value of bottom item in the list
+ $lastIndex = $this->mHist[$numRows-2]->getTimestamp();
+ # Display range
+ $this->mRange = array( 0, $numRows-2 );
+ }
+ } else {
+ # Setting indexes to an empty string means that they will be
+ # omitted if they would otherwise appear in URLs. It just so
+ # happens that this is the right thing to do in the standard
+ # UI, in all the relevant cases.
+ $this->mPastTheEndIndex = '';
+ # Index value of bottom item in the list
+ $lastIndex = $this->mIsBackwards ?
+ $this->mHist[0]->getTimestamp() : $this->mHist[$numRows-1]->getTimestamp();
+ # Display range
+ $this->mRange = array( 0, $numRows-1 );
+ }
+ } else {
+ $firstIndex = '';
+ $lastIndex = '';
+ $this->mPastTheEndIndex = '';
+ }
+ if( $this->mIsBackwards ) {
+ $this->mIsFirst = ( $numRows < $queryLimit );
+ $this->mIsLast = ( $this->mOffset == '' );
+ $this->mLastShown = $firstIndex;
+ $this->mFirstShown = $lastIndex;
+ } else {
+ $this->mIsFirst = ( $this->mOffset == '' );
+ $this->mIsLast = ( $numRows < $queryLimit );
+ $this->mLastShown = $lastIndex;
+ $this->mFirstShown = $firstIndex;
+ }
+ $this->mQueryDone = true;
+ }
+}
diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php
index da9b6fd6..3ab0b858 100644
--- a/includes/ImageQueryPage.php
+++ b/includes/ImageQueryPage.php
@@ -34,7 +34,7 @@ class ImageQueryPage extends QueryPage {
}
}
- $out->addHtml( $gallery->toHtml() );
+ $out->addHTML( $gallery->toHtml() );
}
}
@@ -45,9 +45,9 @@ class ImageQueryPage extends QueryPage {
* @return Image
*/
private function prepareImage( $row ) {
- $namespace = isset( $row->namespace ) ? $row->namespace : NS_IMAGE;
+ $namespace = isset( $row->namespace ) ? $row->namespace : NS_FILE;
$title = Title::makeTitleSafe( $namespace, $row->title );
- return ( $title instanceof Title && $title->getNamespace() == NS_IMAGE )
+ return ( $title instanceof Title && $title->getNamespace() == NS_FILE )
? wfFindFile( $title )
: null;
}
diff --git a/includes/Import.php b/includes/Import.php
new file mode 100644
index 00000000..56e7a7fb
--- /dev/null
+++ b/includes/Import.php
@@ -0,0 +1,1133 @@
+<?php
+/**
+ * MediaWiki page data importer
+ * Copyright (C) 2003,2005 Brion Vibber <brion@pobox.com>
+ * http://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ *
+ * @ingroup SpecialPage
+ */
+class WikiRevision {
+ var $title = null;
+ var $id = 0;
+ var $timestamp = "20010115000000";
+ var $user = 0;
+ var $user_text = "";
+ var $text = "";
+ var $comment = "";
+ var $minor = false;
+ var $type = "";
+ var $action = "";
+ var $params = "";
+
+ function setTitle( $title ) {
+ if( is_object( $title ) ) {
+ $this->title = $title;
+ } elseif( is_null( $title ) ) {
+ throw new MWException( "WikiRevision given a null title in import. You may need to adjust \$wgLegalTitleChars." );
+ } else {
+ throw new MWException( "WikiRevision given non-object title in import." );
+ }
+ }
+
+ function setID( $id ) {
+ $this->id = $id;
+ }
+
+ function setTimestamp( $ts ) {
+ # 2003-08-05T18:30:02Z
+ $this->timestamp = wfTimestamp( TS_MW, $ts );
+ }
+
+ function setUsername( $user ) {
+ $this->user_text = $user;
+ }
+
+ function setUserIP( $ip ) {
+ $this->user_text = $ip;
+ }
+
+ function setText( $text ) {
+ $this->text = $text;
+ }
+
+ function setComment( $text ) {
+ $this->comment = $text;
+ }
+
+ function setMinor( $minor ) {
+ $this->minor = (bool)$minor;
+ }
+
+ function setSrc( $src ) {
+ $this->src = $src;
+ }
+
+ function setFilename( $filename ) {
+ $this->filename = $filename;
+ }
+
+ function setSize( $size ) {
+ $this->size = intval( $size );
+ }
+
+ function setType( $type ) {
+ $this->type = $type;
+ }
+
+ function setAction( $action ) {
+ $this->action = $action;
+ }
+
+ function setParams( $params ) {
+ $this->params = $params;
+ }
+
+ function getTitle() {
+ return $this->title;
+ }
+
+ function getID() {
+ return $this->id;
+ }
+
+ function getTimestamp() {
+ return $this->timestamp;
+ }
+
+ function getUser() {
+ return $this->user_text;
+ }
+
+ function getText() {
+ return $this->text;
+ }
+
+ function getComment() {
+ return $this->comment;
+ }
+
+ function getMinor() {
+ return $this->minor;
+ }
+
+ function getSrc() {
+ return $this->src;
+ }
+
+ function getFilename() {
+ return $this->filename;
+ }
+
+ function getSize() {
+ return $this->size;
+ }
+
+ function getType() {
+ return $this->type;
+ }
+
+ function getAction() {
+ return $this->action;
+ }
+
+ function getParams() {
+ return $this->params;
+ }
+
+ function importOldRevision() {
+ $dbw = wfGetDB( DB_MASTER );
+
+ # Sneak a single revision into place
+ $user = User::newFromName( $this->getUser() );
+ if( $user ) {
+ $userId = intval( $user->getId() );
+ $userText = $user->getName();
+ } else {
+ $userId = 0;
+ $userText = $this->getUser();
+ }
+
+ // avoid memory leak...?
+ $linkCache = LinkCache::singleton();
+ $linkCache->clear();
+
+ $article = new Article( $this->title );
+ $pageId = $article->getId();
+ if( $pageId == 0 ) {
+ # must create the page...
+ $pageId = $article->insertOn( $dbw );
+ $created = true;
+ } else {
+ $created = false;
+
+ $prior = $dbw->selectField( 'revision', '1',
+ array( 'rev_page' => $pageId,
+ 'rev_timestamp' => $dbw->timestamp( $this->timestamp ),
+ 'rev_user_text' => $userText,
+ 'rev_comment' => $this->getComment() ),
+ __METHOD__
+ );
+ if( $prior ) {
+ // FIXME: this could fail slightly for multiple matches :P
+ wfDebug( __METHOD__ . ": skipping existing revision for [[" .
+ $this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
+ return false;
+ }
+ }
+
+ # FIXME: Use original rev_id optionally (better for backups)
+ # Insert the row
+ $revision = new Revision( array(
+ 'page' => $pageId,
+ 'text' => $this->getText(),
+ 'comment' => $this->getComment(),
+ 'user' => $userId,
+ 'user_text' => $userText,
+ 'timestamp' => $this->timestamp,
+ 'minor_edit' => $this->minor,
+ ) );
+ $revId = $revision->insertOn( $dbw );
+ $changed = $article->updateIfNewerOn( $dbw, $revision );
+
+ # To be on the safe side...
+ $tempTitle = $GLOBALS['wgTitle'];
+ $GLOBALS['wgTitle'] = $this->title;
+
+ if( $created ) {
+ wfDebug( __METHOD__ . ": running onArticleCreate\n" );
+ Article::onArticleCreate( $this->title );
+
+ wfDebug( __METHOD__ . ": running create updates\n" );
+ $article->createUpdates( $revision );
+
+ } elseif( $changed ) {
+ wfDebug( __METHOD__ . ": running onArticleEdit\n" );
+ Article::onArticleEdit( $this->title, 'skiptransclusions' ); // leave templatelinks for editUpdates()
+
+ wfDebug( __METHOD__ . ": running edit updates\n" );
+ $article->editUpdates(
+ $this->getText(),
+ $this->getComment(),
+ $this->minor,
+ $this->timestamp,
+ $revId );
+ }
+ $GLOBALS['wgTitle'] = $tempTitle;
+
+ return true;
+ }
+
+ function importLogItem() {
+ $dbw = wfGetDB( DB_MASTER );
+ # FIXME: this will not record autoblocks
+ if( !$this->getTitle() ) {
+ wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
+ $this->timestamp . "\n" );
+ return;
+ }
+ # Check if it exists already
+ // FIXME: use original log ID (better for backups)
+ $prior = $dbw->selectField( 'logging', '1',
+ array( 'log_type' => $this->getType(),
+ 'log_action' => $this->getAction(),
+ 'log_timestamp' => $dbw->timestamp( $this->timestamp ),
+ 'log_namespace' => $this->getTitle()->getNamespace(),
+ 'log_title' => $this->getTitle()->getDBkey(),
+ 'log_comment' => $this->getComment(),
+ #'log_user_text' => $this->user_text,
+ 'log_params' => $this->params ),
+ __METHOD__
+ );
+ // FIXME: this could fail slightly for multiple matches :P
+ if( $prior ) {
+ wfDebug( __METHOD__ . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp " .
+ $this->timestamp . "\n" );
+ return false;
+ }
+ $log_id = $dbw->nextSequenceValue( 'log_log_id_seq' );
+ $data = array(
+ 'log_id' => $log_id,
+ 'log_type' => $this->type,
+ 'log_action' => $this->action,
+ 'log_timestamp' => $dbw->timestamp( $this->timestamp ),
+ 'log_user' => User::idFromName( $this->user_text ),
+ #'log_user_text' => $this->user_text,
+ 'log_namespace' => $this->getTitle()->getNamespace(),
+ 'log_title' => $this->getTitle()->getDBkey(),
+ 'log_comment' => $this->getComment(),
+ 'log_params' => $this->params
+ );
+ $dbw->insert( 'logging', $data, __METHOD__ );
+ }
+
+ function importUpload() {
+ wfDebug( __METHOD__ . ": STUB\n" );
+
+ /**
+ // from file revert...
+ $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
+ $comment = $wgRequest->getText( 'wpComment' );
+ // TODO: Preserve file properties from database instead of reloading from file
+ $status = $this->file->upload( $source, $comment, $comment );
+ if( $status->isGood() ) {
+ */
+
+ /**
+ // from file upload...
+ $this->mLocalFile = wfLocalFile( $nt );
+ $this->mDestName = $this->mLocalFile->getName();
+ //....
+ $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
+ File::DELETE_SOURCE, $this->mFileProps );
+ if ( !$status->isGood() ) {
+ $resultDetails = array( 'internal' => $status->getWikiText() );
+ */
+
+ // @fixme upload() uses $wgUser, which is wrong here
+ // it may also create a page without our desire, also wrong potentially.
+ // and, it will record a *current* upload, but we might want an archive version here
+
+ $file = wfLocalFile( $this->getTitle() );
+ if( !$file ) {
+ var_dump( $file );
+ wfDebug( "IMPORT: Bad file. :(\n" );
+ return false;
+ }
+
+ $source = $this->downloadSource();
+ if( !$source ) {
+ wfDebug( "IMPORT: Could not fetch remote file. :(\n" );
+ return false;
+ }
+
+ $status = $file->upload( $source,
+ $this->getComment(),
+ $this->getComment(), // Initial page, if none present...
+ File::DELETE_SOURCE,
+ false, // props...
+ $this->getTimestamp() );
+
+ if( $status->isGood() ) {
+ // yay?
+ wfDebug( "IMPORT: is ok?\n" );
+ return true;
+ }
+
+ wfDebug( "IMPORT: is bad? " . $status->getXml() . "\n" );
+ return false;
+
+ }
+
+ function downloadSource() {
+ global $wgEnableUploads;
+ if( !$wgEnableUploads ) {
+ return false;
+ }
+
+ $tempo = tempnam( wfTempDir(), 'download' );
+ $f = fopen( $tempo, 'wb' );
+ if( !$f ) {
+ wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
+ return false;
+ }
+
+ // @fixme!
+ $src = $this->getSrc();
+ $data = Http::get( $src );
+ if( !$data ) {
+ wfDebug( "IMPORT: couldn't fetch source $src\n" );
+ fclose( $f );
+ unlink( $tempo );
+ return false;
+ }
+
+ fwrite( $f, $data );
+ fclose( $f );
+
+ return $tempo;
+ }
+
+}
+
+/**
+ * implements Special:Import
+ * @ingroup SpecialPage
+ */
+class WikiImporter {
+ var $mDebug = false;
+ var $mSource = null;
+ var $mPageCallback = null;
+ var $mPageOutCallback = null;
+ var $mRevisionCallback = null;
+ var $mLogItemCallback = null;
+ var $mUploadCallback = null;
+ var $mTargetNamespace = null;
+ var $mXmlNamespace = false;
+ var $lastfield;
+ var $tagStack = array();
+
+ function __construct( $source ) {
+ $this->setRevisionCallback( array( $this, "importRevision" ) );
+ $this->setUploadCallback( array( $this, "importUpload" ) );
+ $this->setLogItemCallback( array( $this, "importLogItem" ) );
+ $this->mSource = $source;
+ }
+
+ function throwXmlError( $err ) {
+ $this->debug( "FAILURE: $err" );
+ wfDebug( "WikiImporter XML error: $err\n" );
+ }
+
+ function handleXmlNamespace ( $parser, $data, $prefix=false, $uri=false ) {
+ if( preg_match( '/www.mediawiki.org/',$prefix ) ) {
+ $prefix = str_replace( '/','\/',$prefix );
+ $this->mXmlNamespace='/^'.$prefix.':/';
+ }
+ }
+
+ function stripXmlNamespace($name) {
+ if( $this->mXmlNamespace ) {
+ return(preg_replace($this->mXmlNamespace,'',$name,1));
+ }
+ else {
+ return($name);
+ }
+ }
+
+ # --------------
+
+ function doImport() {
+ if( empty( $this->mSource ) ) {
+ return new WikiErrorMsg( "importnotext" );
+ }
+
+ $parser = xml_parser_create_ns( "UTF-8" );
+
+ # case folding violates XML standard, turn it off
+ xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+ xml_set_object( $parser, $this );
+ xml_set_element_handler( $parser, "in_start", "" );
+ xml_set_start_namespace_decl_handler( $parser, "handleXmlNamespace" );
+
+ $offset = 0; // for context extraction on error reporting
+ do {
+ $chunk = $this->mSource->readChunk();
+ if( !xml_parse( $parser, $chunk, $this->mSource->atEnd() ) ) {
+ wfDebug( "WikiImporter::doImport encountered XML parsing error\n" );
+ return new WikiXmlError( $parser, wfMsgHtml( 'import-parse-failure' ), $chunk, $offset );
+ }
+ $offset += strlen( $chunk );
+ } while( $chunk !== false && !$this->mSource->atEnd() );
+ xml_parser_free( $parser );
+
+ return true;
+ }
+
+ function debug( $data ) {
+ if( $this->mDebug ) {
+ wfDebug( "IMPORT: $data\n" );
+ }
+ }
+
+ function notice( $data ) {
+ global $wgCommandLineMode;
+ if( $wgCommandLineMode ) {
+ print "$data\n";
+ } else {
+ global $wgOut;
+ $wgOut->addHTML( "<li>" . htmlspecialchars( $data ) . "</li>\n" );
+ }
+ }
+
+ /**
+ * Set debug mode...
+ */
+ function setDebug( $debug ) {
+ $this->mDebug = $debug;
+ }
+
+ /**
+ * Sets the action to perform as each new page in the stream is reached.
+ * @param $callback callback
+ * @return callback
+ */
+ function setPageCallback( $callback ) {
+ $previous = $this->mPageCallback;
+ $this->mPageCallback = $callback;
+ return $previous;
+ }
+
+ /**
+ * Sets the action to perform as each page in the stream is completed.
+ * Callback accepts the page title (as a Title object), a second object
+ * with the original title form (in case it's been overridden into a
+ * local namespace), and a count of revisions.
+ *
+ * @param $callback callback
+ * @return callback
+ */
+ function setPageOutCallback( $callback ) {
+ $previous = $this->mPageOutCallback;
+ $this->mPageOutCallback = $callback;
+ return $previous;
+ }
+
+ /**
+ * Sets the action to perform as each page revision is reached.
+ * @param $callback callback
+ * @return callback
+ */
+ function setRevisionCallback( $callback ) {
+ $previous = $this->mRevisionCallback;
+ $this->mRevisionCallback = $callback;
+ return $previous;
+ }
+
+ /**
+ * Sets the action to perform as each file upload version is reached.
+ * @param $callback callback
+ * @return callback
+ */
+ function setUploadCallback( $callback ) {
+ $previous = $this->mUploadCallback;
+ $this->mUploadCallback = $callback;
+ return $previous;
+ }
+
+ /**
+ * Sets the action to perform as each log item reached.
+ * @param $callback callback
+ * @return callback
+ */
+ function setLogItemCallback( $callback ) {
+ $previous = $this->mLogItemCallback;
+ $this->mLogItemCallback = $callback;
+ return $previous;
+ }
+
+ /**
+ * Set a target namespace to override the defaults
+ */
+ function setTargetNamespace( $namespace ) {
+ if( is_null( $namespace ) ) {
+ // Don't override namespaces
+ $this->mTargetNamespace = null;
+ } elseif( $namespace >= 0 ) {
+ // FIXME: Check for validity
+ $this->mTargetNamespace = intval( $namespace );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Default per-revision callback, performs the import.
+ * @param $revision WikiRevision
+ * @private
+ */
+ function importRevision( $revision ) {
+ $dbw = wfGetDB( DB_MASTER );
+ return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
+ }
+
+ /**
+ * Default per-revision callback, performs the import.
+ * @param $revision WikiRevision
+ * @private
+ */
+ function importLogItem( $rev ) {
+ $dbw = wfGetDB( DB_MASTER );
+ return $dbw->deadlockLoop( array( $rev, 'importLogItem' ) );
+ }
+
+ /**
+ * Dummy for now...
+ */
+ function importUpload( $revision ) {
+ //$dbw = wfGetDB( DB_MASTER );
+ //return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
+ return false;
+ }
+
+ /**
+ * Alternate per-revision callback, for debugging.
+ * @param $revision WikiRevision
+ * @private
+ */
+ function debugRevisionHandler( &$revision ) {
+ $this->debug( "Got revision:" );
+ if( is_object( $revision->title ) ) {
+ $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
+ } else {
+ $this->debug( "-- Title: <invalid>" );
+ }
+ $this->debug( "-- User: " . $revision->user_text );
+ $this->debug( "-- Timestamp: " . $revision->timestamp );
+ $this->debug( "-- Comment: " . $revision->comment );
+ $this->debug( "-- Text: " . $revision->text );
+ }
+
+ /**
+ * Notify the callback function when a new <page> is reached.
+ * @param $title Title
+ * @private
+ */
+ function pageCallback( $title ) {
+ if( is_callable( $this->mPageCallback ) ) {
+ call_user_func( $this->mPageCallback, $title );
+ }
+ }
+
+ /**
+ * Notify the callback function when a </page> is closed.
+ * @param $title Title
+ * @param $origTitle Title
+ * @param $revisionCount int
+ * @param $successCount Int: number of revisions for which callback returned true
+ * @private
+ */
+ function pageOutCallback( $title, $origTitle, $revisionCount, $successCount ) {
+ if( is_callable( $this->mPageOutCallback ) ) {
+ call_user_func( $this->mPageOutCallback, $title, $origTitle,
+ $revisionCount, $successCount );
+ }
+ }
+
+ # XML parser callbacks from here out -- beware!
+ function donothing( $parser, $x, $y="" ) {
+ #$this->debug( "donothing" );
+ }
+
+ function in_start( $parser, $name, $attribs ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "in_start $name" );
+ if( $name != "mediawiki" ) {
+ return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" );
+ }
+ xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
+ }
+
+ function in_mediawiki( $parser, $name, $attribs ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "in_mediawiki $name" );
+ if( $name == 'siteinfo' ) {
+ xml_set_element_handler( $parser, "in_siteinfo", "out_siteinfo" );
+ } elseif( $name == 'page' ) {
+ $this->push( $name );
+ $this->workRevisionCount = 0;
+ $this->workSuccessCount = 0;
+ $this->uploadCount = 0;
+ $this->uploadSuccessCount = 0;
+ xml_set_element_handler( $parser, "in_page", "out_page" );
+ } elseif( $name == 'logitem' ) {
+ $this->push( $name );
+ $this->workRevision = new WikiRevision;
+ xml_set_element_handler( $parser, "in_logitem", "out_logitem" );
+ } else {
+ return $this->throwXMLerror( "Expected <page>, got <$name>" );
+ }
+ }
+ function out_mediawiki( $parser, $name ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "out_mediawiki $name" );
+ if( $name != "mediawiki" ) {
+ return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" );
+ }
+ xml_set_element_handler( $parser, "donothing", "donothing" );
+ }
+
+
+ function in_siteinfo( $parser, $name, $attribs ) {
+ // no-ops for now
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "in_siteinfo $name" );
+ switch( $name ) {
+ case "sitename":
+ case "base":
+ case "generator":
+ case "case":
+ case "namespaces":
+ case "namespace":
+ break;
+ default:
+ return $this->throwXMLerror( "Element <$name> not allowed in <siteinfo>." );
+ }
+ }
+
+ function out_siteinfo( $parser, $name ) {
+ $name = $this->stripXmlNamespace($name);
+ if( $name == "siteinfo" ) {
+ xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
+ }
+ }
+
+
+ function in_page( $parser, $name, $attribs ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "in_page $name" );
+ switch( $name ) {
+ case "id":
+ case "title":
+ case "restrictions":
+ $this->appendfield = $name;
+ $this->appenddata = "";
+ xml_set_element_handler( $parser, "in_nothing", "out_append" );
+ xml_set_character_data_handler( $parser, "char_append" );
+ break;
+ case "revision":
+ $this->push( "revision" );
+ if( is_object( $this->pageTitle ) ) {
+ $this->workRevision = new WikiRevision;
+ $this->workRevision->setTitle( $this->pageTitle );
+ $this->workRevisionCount++;
+ } else {
+ // Skipping items due to invalid page title
+ $this->workRevision = null;
+ }
+ xml_set_element_handler( $parser, "in_revision", "out_revision" );
+ break;
+ case "upload":
+ $this->push( "upload" );
+ if( is_object( $this->pageTitle ) ) {
+ $this->workRevision = new WikiRevision;
+ $this->workRevision->setTitle( $this->pageTitle );
+ $this->uploadCount++;
+ } else {
+ // Skipping items due to invalid page title
+ $this->workRevision = null;
+ }
+ xml_set_element_handler( $parser, "in_upload", "out_upload" );
+ break;
+ default:
+ return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
+ }
+ }
+
+ function out_page( $parser, $name ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "out_page $name" );
+ $this->pop();
+ if( $name != "page" ) {
+ return $this->throwXMLerror( "Expected </page>, got </$name>" );
+ }
+ xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
+
+ $this->pageOutCallback( $this->pageTitle, $this->origTitle,
+ $this->workRevisionCount, $this->workSuccessCount );
+
+ $this->workTitle = null;
+ $this->workRevision = null;
+ $this->workRevisionCount = 0;
+ $this->workSuccessCount = 0;
+ $this->pageTitle = null;
+ $this->origTitle = null;
+ }
+
+ function in_nothing( $parser, $name, $attribs ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "in_nothing $name" );
+ return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
+ }
+
+ function char_append( $parser, $data ) {
+ $this->debug( "char_append '$data'" );
+ $this->appenddata .= $data;
+ }
+
+ function out_append( $parser, $name ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "out_append $name" );
+ if( $name != $this->appendfield ) {
+ return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
+ }
+
+ switch( $this->appendfield ) {
+ case "title":
+ $this->workTitle = $this->appenddata;
+ $this->origTitle = Title::newFromText( $this->workTitle );
+ if( !is_null( $this->mTargetNamespace ) && !is_null( $this->origTitle ) ) {
+ $this->pageTitle = Title::makeTitle( $this->mTargetNamespace,
+ $this->origTitle->getDBkey() );
+ } else {
+ $this->pageTitle = Title::newFromText( $this->workTitle );
+ }
+ if( is_null( $this->pageTitle ) ) {
+ // Invalid page title? Ignore the page
+ $this->notice( "Skipping invalid page title '$this->workTitle'" );
+ } elseif( $this->pageTitle->getInterwiki() != '' ) {
+ $this->notice( "Skipping interwiki page title '$this->workTitle'" );
+ $this->pageTitle = null;
+ } else {
+ $this->pageCallback( $this->workTitle );
+ }
+ break;
+ case "id":
+ if ( $this->parentTag() == 'revision' || $this->parentTag() == 'logitem' ) {
+ if( $this->workRevision )
+ $this->workRevision->setID( $this->appenddata );
+ }
+ break;
+ case "text":
+ if( $this->workRevision )
+ $this->workRevision->setText( $this->appenddata );
+ break;
+ case "username":
+ if( $this->workRevision )
+ $this->workRevision->setUsername( $this->appenddata );
+ break;
+ case "ip":
+ if( $this->workRevision )
+ $this->workRevision->setUserIP( $this->appenddata );
+ break;
+ case "timestamp":
+ if( $this->workRevision )
+ $this->workRevision->setTimestamp( $this->appenddata );
+ break;
+ case "comment":
+ if( $this->workRevision )
+ $this->workRevision->setComment( $this->appenddata );
+ break;
+ case "type":
+ if( $this->workRevision )
+ $this->workRevision->setType( $this->appenddata );
+ break;
+ case "action":
+ if( $this->workRevision )
+ $this->workRevision->setAction( $this->appenddata );
+ break;
+ case "logtitle":
+ if( $this->workRevision )
+ $this->workRevision->setTitle( Title::newFromText( $this->appenddata ) );
+ break;
+ case "params":
+ if( $this->workRevision )
+ $this->workRevision->setParams( $this->appenddata );
+ break;
+ case "minor":
+ if( $this->workRevision )
+ $this->workRevision->setMinor( true );
+ break;
+ case "filename":
+ if( $this->workRevision )
+ $this->workRevision->setFilename( $this->appenddata );
+ break;
+ case "src":
+ if( $this->workRevision )
+ $this->workRevision->setSrc( $this->appenddata );
+ break;
+ case "size":
+ if( $this->workRevision )
+ $this->workRevision->setSize( intval( $this->appenddata ) );
+ break;
+ default:
+ $this->debug( "Bad append: {$this->appendfield}" );
+ }
+ $this->appendfield = "";
+ $this->appenddata = "";
+
+ $parent = $this->parentTag();
+ xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
+ xml_set_character_data_handler( $parser, "donothing" );
+ }
+
+ function in_revision( $parser, $name, $attribs ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "in_revision $name" );
+ switch( $name ) {
+ case "id":
+ case "timestamp":
+ case "comment":
+ case "minor":
+ case "text":
+ $this->appendfield = $name;
+ xml_set_element_handler( $parser, "in_nothing", "out_append" );
+ xml_set_character_data_handler( $parser, "char_append" );
+ break;
+ case "contributor":
+ $this->push( "contributor" );
+ xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
+ break;
+ default:
+ return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
+ }
+ }
+
+ function out_revision( $parser, $name ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "out_revision $name" );
+ $this->pop();
+ if( $name != "revision" ) {
+ return $this->throwXMLerror( "Expected </revision>, got </$name>" );
+ }
+ xml_set_element_handler( $parser, "in_page", "out_page" );
+
+ if( $this->workRevision ) {
+ $ok = call_user_func_array( $this->mRevisionCallback,
+ array( $this->workRevision, $this ) );
+ if( $ok ) {
+ $this->workSuccessCount++;
+ }
+ }
+ }
+
+ function in_logitem( $parser, $name, $attribs ) {
+ $name = $this->stripXmlNamespace($name);
+ $this->debug( "in_logitem $name" );
+ switch( $name ) {
+ case "id":
+ case "timestamp":
+ case "comment":
+ case "type":
+ case "action":
+ case "logtitle":
+ case "params":
+ $this->appendfield = $name;
+ xml_set_element_handler( $parser, "in_nothing", "out_append" );
+ xml_set_character_data_handler( $parser, "char_append" );
+ break;
+ case "contributor":
+ $this->push( "contributor" );
+ xml_set_element_handler( $parser, "in_contributor", "out_contributor" );</