summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2007-09-14 13:18:58 +0200
committerPierre Schmitz <pierre@archlinux.de>2007-09-14 13:18:58 +0200
commit8f416baead93a48e5799e44b8bd2e2c4859f4e04 (patch)
treecd47ac55eb80a39e3225e8b4f3161b88ea16c2cf /includes
parentd7d08bd1a17618c7d77a6b9b2989e9f7293d6ed6 (diff)
auf Version 1.11 aktualisiert; Login-Bug behoben
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxDispatcher.php51
-rw-r--r--includes/AjaxFunctions.php34
-rw-r--r--includes/AjaxResponse.php32
-rw-r--r--includes/Article.php345
-rw-r--r--includes/AuthPlugin.php5
-rw-r--r--includes/AutoLoader.php50
-rw-r--r--includes/BagOStuff.php134
-rw-r--r--includes/Block.php23
-rw-r--r--includes/CacheDependency.php2
-rw-r--r--includes/CategoryPage.php37
-rw-r--r--includes/Categoryfinder.php2
-rw-r--r--includes/ChangesList.php11
-rw-r--r--includes/CoreParserFunctions.php27
-rw-r--r--includes/Credits.php2
-rw-r--r--includes/Database.php173
-rw-r--r--includes/DatabaseFunctions.php2
-rw-r--r--includes/DatabaseOracle.php18
-rw-r--r--includes/DatabasePostgres.php98
-rw-r--r--includes/DateFormatter.php2
-rw-r--r--includes/DefaultSettings.php319
-rw-r--r--includes/Defines.php58
-rw-r--r--includes/DifferenceEngine.php153
-rw-r--r--includes/DjVuImage.php2
-rw-r--r--includes/EditPage.php258
-rw-r--r--includes/EmaillingJob.php25
-rw-r--r--includes/EnotifNotifyJob.php26
-rw-r--r--includes/Exception.php2
-rw-r--r--includes/Exif.php4
-rw-r--r--includes/Export.php4
-rw-r--r--includes/ExternalEdit.php4
-rw-r--r--includes/ExternalStore.php7
-rw-r--r--includes/ExternalStoreDB.php2
-rw-r--r--includes/ExternalStoreHttp.php2
-rw-r--r--includes/FakeTitle.php2
-rw-r--r--includes/FileDeleteForm.php220
-rw-r--r--includes/FileRevertForm.php165
-rw-r--r--includes/FileStore.php8
-rw-r--r--includes/GlobalFunctions.php186
-rw-r--r--includes/HTMLCacheUpdate.php32
-rw-r--r--includes/HTMLFileCache.php2
-rw-r--r--includes/HTMLForm.php53
-rw-r--r--includes/HistoryBlob.php2
-rw-r--r--includes/Hooks.php16
-rw-r--r--includes/HttpFunctions.php38
-rw-r--r--includes/IP.php47
-rw-r--r--includes/ImageFunctions.php111
-rw-r--r--includes/ImageGallery.php103
-rw-r--r--includes/ImagePage.php544
-rw-r--r--includes/ImageQueryPage.php8
-rw-r--r--includes/JobQueue.php112
-rw-r--r--includes/Licenses.php2
-rw-r--r--includes/LinkBatch.php33
-rw-r--r--includes/LinkCache.php2
-rw-r--r--includes/LinkFilter.php2
-rw-r--r--includes/Linker.php574
-rw-r--r--includes/LinksUpdate.php16
-rw-r--r--includes/LoadBalancer.php2
-rw-r--r--includes/LogPage.php71
-rw-r--r--includes/MacBinary.php4
-rw-r--r--includes/MagicWord.php178
-rw-r--r--includes/Math.php49
-rw-r--r--includes/MediaTransformOutput.php98
-rw-r--r--includes/MemcachedSessions.php2
-rw-r--r--includes/MessageCache.php33
-rw-r--r--includes/Metadata.php2
-rw-r--r--includes/MimeMagic.php538
-rw-r--r--includes/Namespace.php88
-rw-r--r--includes/ObjectCache.php4
-rw-r--r--includes/OutputHandler.php75
-rw-r--r--includes/OutputPage.php281
-rw-r--r--includes/PageHistory.php26
-rw-r--r--includes/PageQueryPage.php2
-rw-r--r--includes/Pager.php122
-rw-r--r--includes/Parser.php420
-rw-r--r--includes/ParserCache.php2
-rw-r--r--includes/ParserOptions.php2
-rw-r--r--includes/ParserOutput.php59
-rw-r--r--includes/PatrolLog.php18
-rw-r--r--includes/Profiler.php5
-rw-r--r--includes/ProfilerSimple.php2
-rw-r--r--includes/ProfilerSimpleUDP.php2
-rw-r--r--includes/ProfilerStub.php2
-rw-r--r--includes/ProtectionForm.php90
-rw-r--r--includes/ProxyTools.php2
-rw-r--r--includes/QueryPage.php18
-rw-r--r--includes/RawPage.php2
-rw-r--r--includes/RecentChange.php72
-rw-r--r--includes/RefreshLinksJob.php48
-rw-r--r--includes/Revision.php2
-rw-r--r--includes/Sanitizer.php92
-rw-r--r--includes/SearchEngine.php21
-rw-r--r--includes/SearchMySQL.php6
-rw-r--r--includes/SearchMySQL4.php2
-rw-r--r--includes/SearchOracle.php2
-rw-r--r--includes/SearchPostgres.php3
-rw-r--r--includes/SearchTsearch2.php2
-rw-r--r--includes/SearchUpdate.php2
-rw-r--r--includes/Setup.php74
-rw-r--r--includes/SiteConfiguration.php2
-rw-r--r--includes/SiteStats.php62
-rw-r--r--includes/Skin.php104
-rw-r--r--includes/SkinTemplate.php60
-rw-r--r--includes/SpecialAllmessages.php5
-rw-r--r--includes/SpecialAllpages.php112
-rw-r--r--includes/SpecialAncientpages.php2
-rw-r--r--includes/SpecialBlockip.php69
-rw-r--r--includes/SpecialBlockme.php2
-rw-r--r--includes/SpecialBooksources.php22
-rw-r--r--includes/SpecialBrokenRedirects.php8
-rw-r--r--includes/SpecialCategories.php13
-rw-r--r--includes/SpecialConfirmemail.php23
-rw-r--r--includes/SpecialContributions.php129
-rw-r--r--includes/SpecialDeadendpages.php4
-rw-r--r--includes/SpecialDisambiguations.php4
-rw-r--r--includes/SpecialDoubleRedirects.php9
-rw-r--r--includes/SpecialEmailuser.php11
-rw-r--r--includes/SpecialExport.php46
-rw-r--r--includes/SpecialFewestrevisions.php2
-rw-r--r--includes/SpecialImagelist.php20
-rw-r--r--includes/SpecialImport.php31
-rw-r--r--includes/SpecialIpblocklist.php168
-rw-r--r--includes/SpecialListredirects.php2
-rw-r--r--includes/SpecialListusers.php28
-rw-r--r--includes/SpecialLockdb.php2
-rw-r--r--includes/SpecialLog.php106
-rw-r--r--includes/SpecialLonelypages.php4
-rw-r--r--includes/SpecialLongpages.php2
-rw-r--r--includes/SpecialMIMEsearch.php31
-rw-r--r--includes/SpecialMostcategories.php3
-rw-r--r--includes/SpecialMostimages.php2
-rw-r--r--includes/SpecialMostlinked.php2
-rw-r--r--includes/SpecialMostlinkedcategories.php2
-rw-r--r--includes/SpecialMostlinkedtemplates.php131
-rw-r--r--includes/SpecialMostrevisions.php2
-rw-r--r--includes/SpecialMovepage.php65
-rw-r--r--includes/SpecialNewimages.php5
-rw-r--r--includes/SpecialNewpages.php22
-rw-r--r--includes/SpecialPage.php30
-rw-r--r--includes/SpecialPopularpages.php2
-rw-r--r--includes/SpecialPreferences.php295
-rw-r--r--includes/SpecialPrefixindex.php26
-rw-r--r--includes/SpecialProtectedpages.php132
-rw-r--r--includes/SpecialRandompage.php2
-rw-r--r--includes/SpecialRandomredirect.php2
-rw-r--r--includes/SpecialRecentchanges.php16
-rw-r--r--includes/SpecialRecentchangeslinked.php3
-rw-r--r--includes/SpecialResetpass.php4
-rw-r--r--includes/SpecialRevisiondelete.php2
-rw-r--r--includes/SpecialSearch.php15
-rw-r--r--includes/SpecialShortpages.php2
-rw-r--r--includes/SpecialSpecialpages.php6
-rw-r--r--includes/SpecialStatistics.php89
-rw-r--r--includes/SpecialUncategorizedcategories.php2
-rw-r--r--includes/SpecialUncategorizedimages.php2
-rw-r--r--includes/SpecialUncategorizedpages.php2
-rw-r--r--includes/SpecialUncategorizedtemplates.php31
-rw-r--r--includes/SpecialUndelete.php146
-rw-r--r--includes/SpecialUnlockdb.php2
-rw-r--r--includes/SpecialUnusedcategories.php4
-rw-r--r--includes/SpecialUnusedimages.php2
-rw-r--r--includes/SpecialUnusedtemplates.php5
-rw-r--r--includes/SpecialUnwatchedpages.php2
-rw-r--r--includes/SpecialUpload.php900
-rw-r--r--includes/SpecialUploadMogile.php2
-rw-r--r--includes/SpecialUserlogin.php34
-rw-r--r--includes/SpecialUserlogout.php2
-rw-r--r--includes/SpecialUserrights.php225
-rw-r--r--includes/SpecialVersion.php17
-rw-r--r--includes/SpecialWantedcategories.php2
-rw-r--r--includes/SpecialWantedpages.php66
-rw-r--r--includes/SpecialWatchlist.php194
-rw-r--r--includes/SpecialWhatlinkshere.php35
-rw-r--r--includes/SpecialWithoutinterwiki.php10
-rw-r--r--includes/SquidUpdate.php21
-rw-r--r--includes/StreamFile.php5
-rw-r--r--includes/StringUtils.php2
-rw-r--r--includes/StubObject.php2
-rw-r--r--includes/Title.php338
-rw-r--r--includes/User.php284
-rw-r--r--includes/UserMailer.php195
-rw-r--r--includes/Utf8Case.php2
-rw-r--r--includes/WatchedItem.php17
-rw-r--r--includes/WatchlistEditor.php493
-rw-r--r--includes/WebRequest.php92
-rw-r--r--includes/WebResponse.php2
-rw-r--r--includes/WebStart.php2
-rw-r--r--includes/Wiki.php39
-rw-r--r--includes/WikiError.php2
-rw-r--r--includes/Xml.php180
-rw-r--r--includes/XmlFunctions.php5
-rw-r--r--includes/ZhClient.php1
-rw-r--r--includes/ZhConversion.php1
-rw-r--r--includes/api/ApiBase.php165
-rw-r--r--includes/api/ApiFeedWatchlist.php121
-rw-r--r--includes/api/ApiFormatBase.php41
-rw-r--r--includes/api/ApiFormatJson.php31
-rw-r--r--includes/api/ApiFormatJson_json.php2
-rw-r--r--includes/api/ApiFormatPhp.php6
-rw-r--r--includes/api/ApiFormatWddx.php6
-rw-r--r--includes/api/ApiFormatXml.php6
-rw-r--r--includes/api/ApiFormatYaml.php6
-rw-r--r--includes/api/ApiFormatYaml_spyc.php2
-rw-r--r--includes/api/ApiHelp.php8
-rw-r--r--includes/api/ApiLogin.php139
-rw-r--r--includes/api/ApiMain.php268
-rw-r--r--includes/api/ApiOpenSearch.php14
-rw-r--r--includes/api/ApiPageSet.php115
-rw-r--r--includes/api/ApiQuery.php253
-rw-r--r--includes/api/ApiQueryAllLinks.php179
-rw-r--r--includes/api/ApiQueryAllUsers.php204
-rw-r--r--includes/api/ApiQueryAllpages.php104
-rw-r--r--includes/api/ApiQueryBacklinks.php125
-rw-r--r--includes/api/ApiQueryBase.php247
-rw-r--r--includes/api/ApiQueryCategories.php157
-rw-r--r--includes/api/ApiQueryCategoryMembers.php238
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php200
-rw-r--r--includes/api/ApiQueryExternalLinks.php93
-rw-r--r--includes/api/ApiQueryImageInfo.php156
-rw-r--r--includes/api/ApiQueryImages.php118
-rw-r--r--includes/api/ApiQueryInfo.php126
-rw-r--r--includes/api/ApiQueryLangLinks.php94
-rw-r--r--includes/api/ApiQueryLinks.php162
-rw-r--r--includes/api/ApiQueryLogEvents.php155
-rw-r--r--includes/api/ApiQueryRecentChanges.php117
-rw-r--r--includes/api/ApiQueryRevisions.php187
-rw-r--r--includes/api/ApiQuerySearch.php151
-rw-r--r--includes/api/ApiQuerySiteinfo.php202
-rw-r--r--includes/api/ApiQueryUserContributions.php231
-rw-r--r--includes/api/ApiQueryUserInfo.php133
-rw-r--r--includes/api/ApiQueryWatchlist.php133
-rw-r--r--includes/api/ApiResult.php42
-rw-r--r--includes/cbt/CBTCompiler.php2
-rw-r--r--includes/cbt/CBTProcessor.php2
-rw-r--r--includes/filerepo/ArchivedFile.php108
-rw-r--r--includes/filerepo/FSRepo.php530
-rw-r--r--includes/filerepo/File.php1133
-rw-r--r--includes/filerepo/FileRepo.php404
-rw-r--r--includes/filerepo/FileRepoStatus.php171
-rw-r--r--includes/filerepo/ForeignDBFile.php42
-rw-r--r--includes/filerepo/ForeignDBRepo.php57
-rw-r--r--includes/filerepo/ICRepo.php313
-rw-r--r--includes/filerepo/LocalFile.php1573
-rw-r--r--includes/filerepo/LocalRepo.php65
-rw-r--r--includes/filerepo/OldLocalFile.php232
-rw-r--r--includes/filerepo/README41
-rw-r--r--includes/filerepo/RepoGroup.php150
-rw-r--r--includes/filerepo/UnregisteredLocalFile.php109
-rw-r--r--includes/media/BMP.php10
-rw-r--r--includes/media/Bitmap.php91
-rw-r--r--includes/media/DjVu.php16
-rw-r--r--includes/media/Generic.php179
-rw-r--r--includes/media/SVG.php21
-rw-r--r--includes/memcached-client.php40
-rw-r--r--includes/normal/CleanUpTest.php2
-rw-r--r--includes/normal/RandomTest.php2
-rw-r--r--includes/normal/Utf8Test.php2
-rw-r--r--includes/normal/UtfNormal.php55
-rw-r--r--includes/normal/UtfNormalBench.php2
-rw-r--r--includes/normal/UtfNormalGenerate.php2
-rw-r--r--includes/normal/UtfNormalTest.php2
-rw-r--r--includes/normal/UtfNormalUtil.php2
-rw-r--r--includes/proxy_check.php2
-rw-r--r--includes/templates/NoLocalSettings.php5
-rw-r--r--includes/templates/Userlogin.php49
264 files changed, 17208 insertions, 4971 deletions
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index ca129029..7b85ed20 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -1,10 +1,9 @@
<?php
+/**
+ * Handle ajax requests and send them to the proper handler.
+ */
-if( !defined( 'MEDIAWIKI' ) ) {
- die( 1 );
-}
-
-if ( ! $wgUseAjax ) {
+if( !(defined( 'MEDIAWIKI' ) && $wgUseAjax ) ) {
die( 1 );
}
@@ -15,10 +14,16 @@ require_once( 'AjaxFunctions.php' );
* @addtogroup Ajax
*/
class AjaxDispatcher {
- var $mode;
- var $func_name;
- var $args;
+ /** The way the request was made, either a 'get' or a 'post' */
+ private $mode;
+ /** Name of the requested handler */
+ private $func_name;
+
+ /** Arguments passed */
+ private $args;
+
+ /** Load up our object with user supplied data */
function __construct() {
wfProfileIn( __METHOD__ );
@@ -32,24 +37,41 @@ class AjaxDispatcher {
$this->mode = "post";
}
- if ($this->mode == "get") {
+ switch( $this->mode ) {
+
+ case 'get':
$this->func_name = isset( $_GET["rs"] ) ? $_GET["rs"] : '';
if (! empty($_GET["rsargs"])) {
$this->args = $_GET["rsargs"];
} else {
$this->args = array();
}
- } else {
+ break;
+
+ case 'post':
$this->func_name = isset( $_POST["rs"] ) ? $_POST["rs"] : '';
if (! empty($_POST["rsargs"])) {
$this->args = $_POST["rsargs"];
} else {
$this->args = array();
}
+ break;
+
+ default:
+ return;
+ # Or we could throw an exception:
+ #throw new MWException( __METHOD__ . ' called without any data (mode empty).' );
+
}
+
wfProfileOut( __METHOD__ );
}
+ /** Pass the request to our internal function.
+ * BEWARE! Data are passed as they have been supplied by the user,
+ * they should be carefully handled in the function processing the
+ * request.
+ */
function performAction() {
global $wgAjaxExportList, $wgOut;
@@ -62,8 +84,13 @@ class AjaxDispatcher {
wfHttpError( 400, 'Bad Request',
"unknown function " . (string) $this->func_name );
} else {
+ if ( strpos( $this->func_name, '::' ) !== false ) {
+ $func = explode( '::', $this->func_name, 2 );
+ } else {
+ $func = $this->func_name;
+ }
try {
- $result = call_user_func_array($this->func_name, $this->args);
+ $result = call_user_func_array($func, $this->args);
if ( $result === false || $result === NULL ) {
wfHttpError( 500, 'Internal Error',
@@ -93,4 +120,4 @@ class AjaxDispatcher {
}
}
-?>
+
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
index 86f853db..4fb76dcc 100644
--- a/includes/AjaxFunctions.php
+++ b/includes/AjaxFunctions.php
@@ -136,22 +136,29 @@ function wfSajaxSearch( $term ) {
/**
* Called for AJAX watch/unwatch requests.
- * @param $pageID Integer ID of the page to be watched/unwatched
+ * @param $pagename Prefixed title string for page to watch/unwatch
* @param $watch String 'w' to watch, 'u' to unwatch
- * @return String '<w#>' or '<u#>' on successful watch or unwatch, respectively, or '<err#>' on error (invalid XML in case we want to add HTML sometime)
+ * @return String '<w#>' or '<u#>' on successful watch or unwatch,
+ * respectively, followed by an HTML message to display in the alert box; or
+ * '<err#>' on error
*/
-function wfAjaxWatch($pageID = "", $watch = "") {
- if(wfReadOnly())
- return '<err#>'; // redirect to action=(un)watch, which will display the database lock message
+function wfAjaxWatch($pagename = "", $watch = "") {
+ if(wfReadOnly()) {
+ // redirect to action=(un)watch, which will display the database lock
+ // message
+ return '<err#>';
+ }
- if(('w' !== $watch && 'u' !== $watch) || !is_numeric($pageID))
+ if('w' !== $watch && 'u' !== $watch) {
return '<err#>';
+ }
$watch = 'w' === $watch;
- $pageID = intval($pageID);
- $title = Title::newFromID($pageID);
- if(!$title)
+ $title = Title::newFromText($pagename);
+ if(!$title) {
+ // Invalid title
return '<err#>';
+ }
$article = new Article($title);
$watching = $title->userIsWatching();
@@ -170,7 +177,10 @@ function wfAjaxWatch($pageID = "", $watch = "") {
$dbw->commit();
}
}
-
- return $watch ? '<w#>' : '<u#>';
+ if( $watch ) {
+ return '<w#>'.wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+ } else {
+ return '<u#>'.wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
+ }
}
-?>
+
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index cb4af1b5..8fa08539 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -8,14 +8,27 @@ if( !defined( 'MEDIAWIKI' ) ) {
* @addtogroup Ajax
*/
class AjaxResponse {
- var $mCacheDuration;
- var $mVary;
- var $mDisabled;
- var $mText;
- var $mResponseCode;
- var $mLastModified;
- var $mContentType;
+ /** Number of seconds to get the response cached by a proxy */
+ private $mCacheDuration;
+
+ /** HTTP header Content-Type */
+ private $mContentType;
+
+ /** @todo document */
+ private $mDisabled;
+
+ /** Date for the HTTP header Last-modified */
+ private $mLastModified;
+
+ /** HTTP response code */
+ private $mResponseCode;
+
+ /** HTTP Vary header */
+ private $mVary;
+
+ /** Content of our HTTP response */
+ private $mText;
function __construct( $text = NULL ) {
$this->mCacheDuration = NULL;
@@ -52,18 +65,21 @@ class AjaxResponse {
$this->mDisabled = true;
}
+ /** Add content to the response */
function addText( $text ) {
if ( ! $this->mDisabled && $text ) {
$this->mText .= $text;
}
}
+ /** Output text */
function printText() {
if ( ! $this->mDisabled ) {
print $this->mText;
}
}
+ /** Construct the header and output it */
function sendHeaders() {
global $wgUseSquid, $wgUseESI;
@@ -204,4 +220,4 @@ class AjaxResponse {
return true;
}
}
-?>
+
diff --git a/includes/Article.php b/includes/Article.php
index 0130ceba..7ba55c54 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -37,6 +37,18 @@ class Article {
/**@}}*/
/**
+ * Constants used by internal components to get rollback results
+ */
+ const SUCCESS = 0; // Operation successful
+ const PERM_DENIED = 1; // Permission denied
+ const BLOCKED = 2; // User has been blocked
+ const READONLY = 3; // Wiki is in read-only mode
+ const BAD_TOKEN = 4; // Invalid token specified
+ const BAD_TITLE = 5; // $this is not a valid Article
+ const ALREADY_ROLLED = 6; // Someone else already rolled this back. $from and $summary will be set
+ const ONLY_AUTHOR = 7; // User is the only author of the page
+
+ /**
* Constructor and clear the article
* @param $title Reference to a Title object.
* @param $oldId Integer revision ID, null to fetch from request, zero for current
@@ -257,13 +269,16 @@ class Article {
'page_random',
'page_touched',
'page_latest',
- 'page_len' ) ;
- wfRunHooks( 'ArticlePageDataBefore', array( &$this , &$fields ) ) ;
- $row = $dbr->selectRow( 'page',
+ 'page_len',
+ );
+ wfRunHooks( 'ArticlePageDataBefore', array( &$this, &$fields ) );
+ $row = $dbr->selectRow(
+ 'page',
$fields,
$conditions,
- 'Article::pageData' );
- wfRunHooks( 'ArticlePageDataAfter', array( &$this , &$row ) ) ;
+ __METHOD__
+ );
+ wfRunHooks( 'ArticlePageDataAfter', array( &$this, &$row ) );
return $row ;
}
@@ -500,6 +515,10 @@ class Article {
* @return bool
*/
function isCurrent() {
+ # If no oldid, this is the current version.
+ if ($this->getOldID() == 0)
+ return true;
+
return $this->exists() &&
isset( $this->mRevision ) &&
$this->mRevision->isCurrent();
@@ -604,7 +623,7 @@ class Article {
function view() {
global $wgUser, $wgOut, $wgRequest, $wgContLang;
global $wgEnableParserCache, $wgStylePath, $wgUseRCPatrol, $wgParser;
- global $wgUseTrackbacks, $wgNamespaceRobotPolicies;
+ global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies;
$sk = $wgUser->getSkin();
wfProfileIn( __METHOD__ );
@@ -632,6 +651,8 @@ class Article {
# Discourage indexing of printable versions, but encourage following
if( $wgOut->isPrintable() ) {
$policy = 'noindex,follow';
+ } elseif ( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
+ $policy = $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()];
} elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
# Honour customised robot policies for this namespace
$policy = $wgNamespaceRobotPolicies[$ns];
@@ -678,10 +699,12 @@ class Article {
}
# Should the parser cache be used?
- $pcache = $wgEnableParserCache &&
- intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 &&
- $this->exists() &&
- empty( $oldid );
+ $pcache = $wgEnableParserCache
+ && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0
+ && $this->exists()
+ && empty( $oldid )
+ && !$this->mTitle->isCssOrJsPage()
+ && !$this->mTitle->isCssJsSubpage();
wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" );
if ( $wgUser->getOption( 'stubthreshold' ) ) {
wfIncrStats( 'pcache_miss_stub' );
@@ -718,9 +741,12 @@ class Article {
}
$outputDone = false;
- wfRunHooks( 'ArticleViewHeader', array( &$this ) );
+ 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;
}
}
@@ -768,15 +794,24 @@ class Article {
}
if( !$outputDone ) {
$wgOut->setRevisionId( $this->getRevIdFetched() );
- # wrap user css and user js in pre and don't parse
- # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found
- if (
- $ns == NS_USER &&
- preg_match('/\\/[\\w]+\\.(?:css|js)$/', $this->mTitle->getDBkey())
- ) {
- $wgOut->addWikiText( wfMsg('clearyourcache'));
- $wgOut->addHTML( '<pre>'.htmlspecialchars($this->mContent)."\n</pre>" );
- } else if ( $rt = Title::newFromRedirect( $text ) ) {
+
+ // Pages containing custom CSS or JavaScript get special treatment
+ if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
+ $wgOut->addHtml( wfMsgExt( 'clearyourcache', 'parse' ) );
+
+ // Give hooks a chance to customise the output
+ if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
+ // Wrap the whole lot in a <pre> and don't parse
+ $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" );
+ }
+
+ }
+
+ elseif ( $rt = Title::newFromRedirect( $text ) ) {
# Display redirect
$imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
$imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png';
@@ -869,8 +904,8 @@ class Article {
$rmvtxt = "";
if ($wgUser->isAllowed( 'trackback' )) {
$delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid="
- . $o->tb_id . "&token=" . $wgUser->editToken());
- $rmvtxt = wfMsg('trackbackremove', $delurl);
+ . $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
+ $rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
$tbtext .= wfMsg(strlen($o->tb_ex) ? 'trackbackexcerpt' : 'trackback',
$o->tb_title,
@@ -1146,7 +1181,7 @@ class Article {
if( $section == 'new' ) {
# Inserting a new section
- $subject = $summary ? "== {$summary} ==\n\n" : '';
+ $subject = $summary ? wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n" : '';
$text = strlen( trim( $oldtext ) ) > 0
? "{$oldtext}\n\n{$subject}{$text}"
: "{$subject}{$text}";
@@ -1172,7 +1207,7 @@ class Article {
# If this is a comment, add the summary as headline
if ( $comment && $summary != "" ) {
- $text = "== {$summary} ==\n\n".$text;
+ $text = wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n".$text;
}
$this->doEdit( $text, $summary, $flags );
@@ -1219,7 +1254,10 @@ class Article {
}
}
- $this->doRedirect( $this->isRedirect( $text ), $sectionanchor );
+ $extraq = ''; // Give extensions a chance to modify URL query on update
+ wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraq ) );
+
+ $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraq );
}
return $good;
}
@@ -1359,6 +1397,7 @@ class Article {
$dbw->commit();
}
} else {
+ $revision = null;
// Keep the same revision ID, but do some updates on it
$revisionId = $this->getRevIdFetched();
// Update page_touched, this is usually implicit in the page update
@@ -1426,19 +1465,18 @@ class Article {
# Clear caches
Article::onArticleCreate( $this->mTitle );
- wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text,
- $summary, $flags & EDIT_MINOR,
- null, null, &$flags ) );
+ wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
}
if ( $good && !( $flags & EDIT_DEFER_UPDATES ) ) {
wfDoUpdates();
}
- wfRunHooks( 'ArticleSaveComplete',
- array( &$this, &$wgUser, $text,
- $summary, $flags & EDIT_MINOR,
- null, null, &$flags ) );
+ if ( $good ) {
+ wfRunHooks( 'ArticleSaveComplete', array( &$this, &$wgUser, $text, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
+ }
wfProfileOut( __METHOD__ );
return $good;
@@ -1458,12 +1496,14 @@ class Article {
* @param boolean $noRedir Add redirect=no
* @param string $sectionAnchor section to redirect to, including "#"
*/
- function doRedirect( $noRedir = false, $sectionAnchor = '' ) {
+ function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) {
global $wgOut;
if ( $noRedir ) {
$query = 'redirect=no';
+ if( $extraq )
+ $query .= "&$query";
} else {
- $query = '';
+ $query = $extraq;
}
$wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
}
@@ -1690,7 +1730,13 @@ class Article {
}
# Prepare a null revision to be added to the history
- $comment = $wgContLang->ucfirst( wfMsgForContent( $protect ? 'protectedarticle' : 'unprotectedarticle', $this->mTitle->getPrefixedText() ) );
+ $modified = $current != '' && $protect;
+ if ( $protect ) {
+ $comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle';
+ } else {
+ $comment_type = 'unprotectedarticle';
+ }
+ $comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $this->mTitle->getPrefixedText() ) );
foreach( $limit as $action => $restrictions ) {
# Check if the group level required to edit also can protect pages
@@ -1744,7 +1790,7 @@ class Article {
$log = new LogPage( 'protect' );
if( $protect ) {
- $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
+ $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) );
} else {
$log->addEntry( 'unprotect', $this->mTitle, $reason );
}
@@ -1994,11 +2040,10 @@ class Article {
/**
- * Fetch deletion log
+ * Show relevant lines from the deletion log
*/
- function showLogExtract( &$out ) {
- # Show relevant lines from the deletion log:
- $out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ function showLogExtract( $out ) {
+ $out->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . '</h2>' );
$logViewer = new LogViewer(
new LogReader(
new FauxRequest(
@@ -2080,7 +2125,8 @@ class Article {
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len'
+ 'ar_len' => 'rev_len',
+ 'ar_page_id' => 'page_id',
), array(
'page_id' => $id,
'page_id = rev_page'
@@ -2132,60 +2178,52 @@ class Article {
}
/**
- * Revert a modification
- */
- function rollback() {
- global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
-
+ * Roll back the most recent consecutive set of edits to a page
+ * from the same user; fails if there are no eligible edits to
+ * roll back to, e.g. user is the sole contributor
+ *
+ * @param string $fromP - Name of the user whose edits to rollback.
+ * @param string $summary - Custom summary. Set to default summary if empty.
+ * @param string $token - Rollback token.
+ * @param bool $bot - If true, mark all reverted edits as bot.
+ *
+ * @param array $resultDetails contains result-specific dict of additional values
+ * ALREADY_ROLLED : 'current' (rev)
+ * SUCCESS : 'summary' (str), 'current' (rev), 'target' (rev)
+ *
+ * @return self::SUCCESS on succes, self::* on failure
+ */
+ public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) {
+ global $wgUser, $wgUseRCPatrol;
+ $resultDetails = null;
+
if( $wgUser->isAllowed( 'rollback' ) ) {
if( $wgUser->isBlocked() ) {
- $wgOut->blockedPage();
- return;
+ return self::BLOCKED;
}
} else {
- $wgOut->permissionRequired( 'rollback' );
- return;
+ return self::PERM_DENIED;
}
-
+
if ( wfReadOnly() ) {
- $wgOut->readOnlyPage( $this->getContent() );
- return;
- }
- if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ),
- array( $this->mTitle->getPrefixedText(),
- $wgRequest->getVal( 'from' ) ) ) ) {
- $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
- $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
- return;
+ return self::READONLY;
}
- $dbw = wfGetDB( DB_MASTER );
-
- # Enhanced rollback, marks edits rc_bot=1
- $bot = $wgRequest->getBool( 'bot' );
+ if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) )
+ return self::BAD_TOKEN;
- # Replace all this user's current edits with the next one down
+ $dbw = wfGetDB( DB_MASTER );
# Get the last editor
$current = Revision::newFromTitle( $this->mTitle );
if( is_null( $current ) ) {
# Something wrong... no page?
- $wgOut->addHTML( wfMsg( 'notanarticle' ) );
- return;
+ return self::BAD_TITLE;
}
- $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) );
+ $from = str_replace( '_', ' ', $fromP );
if( $from != $current->getUserText() ) {
- $wgOut->setPageTitle( wfMsg('rollbackfailed') );
- $wgOut->addWikiText( wfMsg( 'alreadyrolled',
- htmlspecialchars( $this->mTitle->getPrefixedText()),
- htmlspecialchars( $from ),
- htmlspecialchars( $current->getUserText() ) ) );
- if( $current->getComment() != '') {
- $wgOut->addHTML(
- wfMsg( 'editcomment',
- $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
- }
- return;
+ $resultDetails = array( 'current' => $current );
+ return self::ALREADY_ROLLED;
}
# Get the last edit not by this guy
@@ -2203,11 +2241,9 @@ class Article {
);
if( $s === false ) {
# Something wrong
- $wgOut->setPageTitle(wfMsg('rollbackfailed'));
- $wgOut->addHTML( wfMsg( 'cantrollback' ) );
- return;
+ return self::ONLY_AUTHOR;
}
-
+
$set = array();
if ( $bot ) {
# Mark all reverted edits as bot
@@ -2220,27 +2256,100 @@ class Article {
if ( $set ) {
$dbw->update( 'recentchanges', $set,
- array( /* WHERE */
- 'rc_cur_id' => $current->getPage(),
- 'rc_user_text' => $current->getUserText(),
- "rc_timestamp > '{$s->rev_timestamp}'",
- ), __METHOD__
- );
+ array( /* WHERE */
+ 'rc_cur_id' => $current->getPage(),
+ 'rc_user_text' => $current->getUserText(),
+ "rc_timestamp > '{$s->rev_timestamp}'",
+ ), __METHOD__
+ );
}
# Get the edit summary
$target = Revision::newFromId( $s->rev_id );
- $newComment = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
- $newComment = $wgRequest->getText( 'summary', $newComment );
+ if( empty( $summary ) )
+ $summary = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
+
+ # Save
+ $flags = EDIT_UPDATE | EDIT_MINOR;
+ if( $bot )
+ $flags |= EDIT_FORCE_BOT;
+ $this->doEdit( $target->getText(), $summary, $flags );
+
+ $resultDetails = array(
+ 'summary' => $summary,
+ 'current' => $current,
+ 'target' => $target,
+ );
+ return self::SUCCESS;
+ }
- # Save it!
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
- $wgOut->addHTML( '<h2>' . htmlspecialchars( $newComment ) . "</h2>\n<hr />\n" );
+ /**
+ * User interface for rollback operations
+ */
+ function rollback() {
+ global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
- $this->updateArticle( $target->getText(), $newComment, 1, $this->mTitle->userIsWatching(), $bot );
+ $details = null;
+ $result = $this->doRollback(
+ $wgRequest->getVal( 'from' ),
+ $wgRequest->getText( 'summary' ),
+ $wgRequest->getVal( 'token' ),
+ $wgRequest->getBool( 'bot' ),
+ $details
+ );
+
+ switch( $result ) {
+ case self::BLOCKED:
+ $wgOut->blockedPage();
+ break;
+ case self::PERM_DENIED:
+ $wgOut->permissionRequired( 'rollback' );
+ break;
+ case self::READONLY:
+ $wgOut->readOnlyPage( $this->getContent() );
+ break;
+ case self::BAD_TOKEN:
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
+ break;
+ case self::BAD_TITLE:
+ $wgOut->addHtml( wfMsg( 'notanarticle' ) );
+ break;
+ case self::ALREADY_ROLLED:
+ $current = $details['current'];
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addWikiText(
+ wfMsg( 'alreadyrolled',
+ htmlspecialchars( $this->mTitle->getPrefixedText() ),
+ htmlspecialchars( $wgRequest->getVal( 'from' ) ),
+ htmlspecialchars( $current->getUserText() )
+ )
+ );
+ if( $current->getComment() != '' ) {
+ $wgOut->addHtml( wfMsg( 'editcomment',
+ $wgUser->getSkin()->formatComment( $current->getComment() ) ) );
+ }
+ break;
+ case self::ONLY_AUTHOR:
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addHtml( wfMsg( 'cantrollback' ) );
+ break;
+ case self::SUCCESS:
+ $current = $details['current'];
+ $target = $details['target'];
+ $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->returnToMain( false, $this->mTitle );
+ break;
+ default:
+ throw new MWException( __METHOD__ . ": Unknown return value `{$result}`" );
+ }
- $wgOut->returnToMain( false );
}
@@ -2268,7 +2377,7 @@ class Article {
/**
* Do standard deferred updates after page edit.
* Update links tables, site stats, search index and message cache.
- * Every 1000th edit, prune the recent changes table.
+ * Every 100th edit, prune the recent changes table.
*
* @private
* @param $text New text of the article
@@ -2296,12 +2405,11 @@ class Article {
$u = new LinksUpdate( $this->mTitle, $poutput );
$u->doUpdate();
- if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
- wfSeedRandom();
- if ( 0 == mt_rand( 0, 999 ) ) {
- # Periodically flush old entries from the recentchanges table.
+ if( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
+ if ( 0 == mt_rand( 0, 99 ) ) {
+ // Flush old entries from the `recentchanges` table; we do this on
+ // random requests so as to avoid an increase in writes for no good reason
global $wgRCMaxAge;
-
$dbw = wfGetDB( DB_MASTER );
$cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
$recentchanges = $dbw->tableName( 'recentchanges' );
@@ -2410,7 +2518,12 @@ class Article {
$userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
. $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
- $r = "\n\t\t\t\t<div id=\"mw-revision-info\">" . wfMsg( 'revision-info', $td, $userlinks ) . "</div>\n" .
+ $m = wfMsg( 'revision-info-current' );
+ $infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
+ ? 'revision-info-current'
+ : 'revision-info';
+
+ $r = "\n\t\t\t\t<div id=\"mw-{$infomsg}\">" . wfMsg( $infomsg, $td, $userlinks ) . "</div>\n" .
"\n\t\t\t\t<div id=\"mw-revision-nav\">" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
@@ -2701,16 +2814,22 @@ class Article {
$page = $this->mTitle->getSubjectPage();
$wgOut->setPagetitle( $page->getPrefixedText() );
- $wgOut->setSubtitle( wfMsg( 'infosubtitle' ));
-
- # first, see if the page exists at all.
- $exists = $page->getArticleId() != 0;
- if( !$exists ) {
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- $wgOut->addHTML(wfMsgWeirdKey ( $this->mTitle->getText() ) );
+ $wgOut->setPageTitleActionText( wfMsg( 'info_short' ) );
+ $wgOut->setSubtitle( wfMsg( 'infosubtitle' ) );
+
+ if( !$this->mTitle->exists() ) {
+ $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() ) ) );
} else {
- $wgOut->addHTML(wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' ) );
+ $msg = $wgUser->isLoggedIn()
+ ? 'noarticletext'
+ : 'noarticletextanon';
+ $wgOut->addHtml( wfMsgExt( $msg, 'parse' ) );
}
+ $wgOut->addHtml( '</div>' );
} else {
$dbr = wfGetDB( DB_SLAVE );
$wl_clause = array(
@@ -2959,5 +3078,3 @@ class Article {
}
}
-
-?>
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index 9395032f..87a79438 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -219,9 +219,10 @@ class AuthPlugin {
* forget the & on your function declaration.
*
* @param $user User object.
+ * @param $autocreate bool True if user is being autocreated on login
* @public
*/
- function initUser( &$user ) {
+ function initUser( $user, $autocreate=false ) {
# Override this to do something.
}
@@ -234,4 +235,4 @@ class AuthPlugin {
}
}
-?>
+
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 3d7bcdf3..25c728cd 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -22,6 +22,7 @@ function __autoload($className) {
'TurckBagOStuff' => 'includes/BagOStuff.php',
'APCBagOStuff' => 'includes/BagOStuff.php',
'eAccelBagOStuff' => 'includes/BagOStuff.php',
+ 'XCacheBagOStuff' => 'includes/BagOStuff.php',
'DBABagOStuff' => 'includes/BagOStuff.php',
'Block' => 'includes/Block.php',
'HTMLFileCache' => 'includes/HTMLFileCache.php',
@@ -93,17 +94,20 @@ function __autoload($className) {
'HistoryBlobStub' => 'includes/HistoryBlob.php',
'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php',
- 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
'Http' => 'includes/HttpFunctions.php',
- 'Image' => 'includes/Image.php',
- 'ArchivedFile' => 'includes/Image.php',
'IP' => 'includes/IP.php',
'ThumbnailImage' => 'includes/Image.php',
'ImageGallery' => 'includes/ImageGallery.php',
'ImagePage' => 'includes/ImagePage.php',
'ImageHistoryList' => 'includes/ImagePage.php',
'ImageRemote' => 'includes/ImageRemote.php',
+ 'FileDeleteForm' => 'includes/FileDeleteForm.php',
+ 'FileRevertForm' => 'includes/FileRevertForm.php',
'Job' => 'includes/JobQueue.php',
+ 'EmaillingJob' => 'includes/EmaillingJob.php',
+ 'EnotifNotifyJob' => 'includes/EnotifNotifyJob.php',
+ 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
+ 'RefreshLinksJob' => 'includes/RefreshLinksJob.php',
'Licenses' => 'includes/Licenses.php',
'License' => 'includes/Licenses.php',
'LinkBatch' => 'includes/LinkBatch.php',
@@ -115,6 +119,7 @@ function __autoload($className) {
'LogPage' => 'includes/LogPage.php',
'MacBinary' => 'includes/MacBinary.php',
'MagicWord' => 'includes/MagicWord.php',
+ 'MagicWordArray' => 'includes/MagicWord.php',
'MathRenderer' => 'includes/Math.php',
'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
'ThumbnailImage' => 'includes/MediaTransformOutput.php',
@@ -191,6 +196,7 @@ function __autoload($className) {
'MostimagesPage' => 'includes/SpecialMostimages.php',
'MostlinkedPage' => 'includes/SpecialMostlinked.php',
'MostlinkedCategoriesPage' => 'includes/SpecialMostlinkedcategories.php',
+ 'SpecialMostlinkedtemplates' => 'includes/SpecialMostlinkedtemplates.php',
'MostrevisionsPage' => 'includes/SpecialMostrevisions.php',
'FewestrevisionsPage' => 'includes/SpecialFewestrevisions.php',
'MovePageForm' => 'includes/SpecialMovepage.php',
@@ -209,6 +215,7 @@ function __autoload($className) {
'ShortPagesPage' => 'includes/SpecialShortpages.php',
'UncategorizedCategoriesPage' => 'includes/SpecialUncategorizedcategories.php',
'UncategorizedPagesPage' => 'includes/SpecialUncategorizedpages.php',
+ 'UncategorizedTemplatesPage' => 'includes/SpecialUncategorizedtemplates.php',
'PageArchive' => 'includes/SpecialUndelete.php',
'UndeleteForm' => 'includes/SpecialUndelete.php',
'DBUnlockForm' => 'includes/SpecialUnlockdb.php',
@@ -244,10 +251,28 @@ function __autoload($className) {
'WikiError' => 'includes/WikiError.php',
'WikiErrorMsg' => 'includes/WikiError.php',
'WikiXmlError' => 'includes/WikiError.php',
- 'XCacheBagOStuff' => 'includes/BagOStuff.php',
'Xml' => 'includes/Xml.php',
'ZhClient' => 'includes/ZhClient.php',
'memcached' => 'includes/memcached-client.php',
+ 'EmaillingJob' => 'includes/JobQueue.php',
+ 'WatchlistEditor' => 'includes/WatchlistEditor.php',
+
+ # filerepo
+ 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
+ 'File' => 'includes/filerepo/File.php',
+ 'FileRepo' => 'includes/filerepo/FileRepo.php',
+ 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
+ 'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php',
+ 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
+ 'FSRepo' => 'includes/filerepo/FSRepo.php',
+ 'Image' => 'includes/filerepo/LocalFile.php',
+ 'LocalFile' => 'includes/filerepo/LocalFile.php',
+ 'LocalFileDeleteBatch' => 'includes/filerepo/LocalFile.php',
+ 'LocalFileRestoreBatch' => 'includes/filerepo/LocalFile.php',
+ 'LocalRepo' => 'includes/filerepo/LocalRepo.php',
+ 'OldLocalFile' => 'includes/filerepo/OldLocalFile.php',
+ 'RepoGroup' => 'includes/filerepo/RepoGroup.php',
+ 'UnregisteredLocalFile' => 'includes/filerepo/UnregisteredLocalFile.php',
# Media
'BitmapHandler' => 'includes/media/Bitmap.php',
@@ -287,14 +312,27 @@ function __autoload($className) {
'ApiPageSet' => 'includes/api/ApiPageSet.php',
'ApiQuery' => 'includes/api/ApiQuery.php',
'ApiQueryAllpages' => 'includes/api/ApiQueryAllpages.php',
+ 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
+ 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.php',
'ApiQueryBase' => 'includes/api/ApiQueryBase.php',
+ 'ApiQueryGeneratorBase' => 'includes/api/ApiQueryBase.php',
'ApiQueryBacklinks' => 'includes/api/ApiQueryBacklinks.php',
+ 'ApiQueryCategories' => 'includes/api/ApiQueryCategories.php',
+ 'ApiQueryCategoryMembers' => 'includes/api/ApiQueryCategoryMembers.php',
'ApiQueryContributions' => 'includes/api/ApiQueryUserContributions.php',
+ 'ApiQueryExternalLinks' => 'includes/api/ApiQueryExternalLinks.php',
+ 'ApiQueryExtLinksUsage' => 'includes/api/ApiQueryExtLinksUsage.php',
+ 'ApiQueryImages' => 'includes/api/ApiQueryImages.php',
+ 'ApiQueryImageInfo' => 'includes/api/ApiQueryImageInfo.php',
'ApiQueryInfo' => 'includes/api/ApiQueryInfo.php',
+ 'ApiQueryLangLinks' => 'includes/api/ApiQueryLangLinks.php',
+ 'ApiQueryLinks' => 'includes/api/ApiQueryLinks.php',
'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.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',
'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
'ApiResult' => 'includes/api/ApiResult.php',
);
@@ -346,6 +384,4 @@ function wfLoadAllExtensions() {
require( $file );
}
}
-}
-
-?>
+} \ No newline at end of file
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
index e8abb74e..a40d020e 100644
--- a/includes/BagOStuff.php
+++ b/includes/BagOStuff.php
@@ -172,7 +172,7 @@ class HashBagOStuff extends BagOStuff {
*/
var $bag;
- function HashBagOStuff() {
+ function __construct() {
$this->bag = array();
}
@@ -222,7 +222,7 @@ abstract class SqlBagOStuff extends BagOStuff {
var $table;
var $lastexpireall = 0;
- function SqlBagOStuff($tablename = 'objectcache') {
+ function __construct($tablename = 'objectcache') {
$this->table = $tablename;
}
@@ -238,8 +238,8 @@ abstract class SqlBagOStuff extends BagOStuff {
}
if($row=$this->_fetchobject($res)) {
$this->_debug("get: retrieved data; exp time is " . $row->exptime);
- if ( $row->exptime != $this->_maxdatetime() &&
- wfTimestamp( TS_UNIX, $row->exptime ) < time() )
+ if ( $row->exptime != $this->_maxdatetime() &&
+ wfTimestamp( TS_UNIX, $row->exptime ) < time() )
{
$this->_debug("get: key has expired, deleting");
$this->delete($key);
@@ -253,6 +253,9 @@ abstract class SqlBagOStuff extends BagOStuff {
}
function set($key,$value,$exptime=0) {
+ if ( wfReadOnly() ) {
+ return false;
+ }
$exptime = intval($exptime);
if($exptime < 0) $exptime = 0;
if($exptime == 0) {
@@ -272,6 +275,9 @@ abstract class SqlBagOStuff extends BagOStuff {
}
function delete($key,$time=0) {
+ if ( wfReadOnly() ) {
+ return false;
+ }
$this->_query(
"DELETE FROM $0 WHERE keyname='$1'", $key );
return true; /* ? */
@@ -339,12 +345,18 @@ abstract class SqlBagOStuff extends BagOStuff {
function expireall() {
/* Remove any items that have expired */
+ if ( wfReadOnly() ) {
+ return false;
+ }
$now = $this->_fromunixtime( time() );
$this->_query( "DELETE FROM $0 WHERE exptime < '$now'" );
}
function deleteall() {
/* Clear *all* items from cache table */
+ if ( wfReadOnly() ) {
+ return false;
+ }
$this->_query( "DELETE FROM $0" );
}
@@ -495,55 +507,22 @@ class TurckBagOStuff extends BagOStuff {
*
*/
class APCBagOStuff extends BagOStuff {
- public function get( $key ) {
- return apc_fetch($key);
- }
-
- public function set( $key, $value, $exptime = 0 ) {
- return apc_store($key, $value, $exptime);
- }
-
- public function delete( $key, $time = 0 ) {
- return apc_delete( $key );
- }
-
- public function add($key, $value, $exptime=0) {
- return apc_add( $key, $value, $exptime );
- }
-}
-
-
-/**
- * Wrapper for XCache object caching functions
- *
- */
-class XCacheBagOStuff extends BagOStuff {
- public function get( $key ) {
- return (xcache_isset($key) ? unserialize(xcache_get($key)) : false);
- }
-
- public function set( $key, $value, $exptime = 0 ) {
- return xcache_set($key, serialize($value), $exptime);
- }
-
- public function delete( $key, $time = 0 ) {
- return xcache_unset( $key );
- }
-
- public function incr($key, $value=1) {
- return (xcache_isset($key) && xcache_inc($key. $value));
- }
-
- public function decr($key, $value=1) {
- return (xcache_isset($key) && xcache_dec($key. $value));
+ function get($key) {
+ $val = apc_fetch($key);
+ if ( is_string( $val ) ) {
+ $val = unserialize( $val );
+ }
+ return $val;
}
-
- public function add($key, $value, $exptime=0) {
- return (!xcache_isset($key) && $this->set( $key, $value, $exptime ));
+
+ function set($key, $value, $exptime=0) {
+ apc_store($key, serialize($value), $exptime);
+ return true;
}
-
- public function replace($key, $value, $exptime=0) {
- return (xcache_isset($key) && $this->set( $key, $value, $exptime ));
+
+ function delete($key, $time=0) {
+ apc_delete($key);
+ return true;
}
}
@@ -586,11 +565,57 @@ class eAccelBagOStuff extends BagOStuff {
}
/**
+ * Wrapper for XCache object caching functions; identical interface
+ * to the APC wrapper
+ */
+class XCacheBagOStuff extends BagOStuff {
+
+ /**
+ * Get a value from the XCache object cache
+ *
+ * @param string $key Cache key
+ * @return mixed
+ */
+ public function get( $key ) {
+ $val = xcache_get( $key );
+ if( is_string( $val ) )
+ $val = unserialize( $val );
+ return $val;
+ }
+
+ /**
+ * Store a value in the XCache object cache
+ *
+ * @param string $key Cache key
+ * @param mixed $value Object to store
+ * @param int $expire Expiration time
+ * @return bool
+ */
+ public function set( $key, $value, $expire = 0 ) {
+ xcache_set( $key, serialize( $value ), $expire );
+ return true;
+ }
+
+ /**
+ * Remove a value from the XCache object cache
+ *
+ * @param string $key Cache key
+ * @param int $time Not used in this implementation
+ * @return bool
+ */
+ public function delete( $key, $time = 0 ) {
+ xcache_unset( $key );
+ return true;
+ }
+
+}
+
+/**
* @todo document
*/
class DBABagOStuff extends BagOStuff {
var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
-
+
function __construct( $handler = 'db3', $dir = false ) {
if ( $dir === false ) {
global $wgTmpDirectory;
@@ -617,7 +642,7 @@ class DBABagOStuff extends BagOStuff {
if ( !is_string( $blob ) ) {
return array( null, 0 );
} else {
- return array(
+ return array(
unserialize( substr( $blob, 11 ) ),
intval( substr( $blob, 0, 10 ) )
);
@@ -684,6 +709,7 @@ class DBABagOStuff extends BagOStuff {
function delete( $key, $time = 0 ) {
wfProfileIn( __METHOD__ );
+ wfDebug( __METHOD__."($key)\n" );
$handle = $this->getWriter();
if ( !$handle ) {
return false;
@@ -718,5 +744,5 @@ class DBABagOStuff extends BagOStuff {
return $ret;
}
}
+
-?>
diff --git a/includes/Block.php b/includes/Block.php
index 94bfa5b4..3688d7cf 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -15,7 +15,8 @@
class Block
{
/* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
- $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName;
+ $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName,
+ $mBlockEmail;
/* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster, $mByName;
const EB_KEEP_EXPIRED = 1;
@@ -24,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 )
+ $hideName = 0, $blockEmail = 0 )
{
$this->mId = 0;
# Expand valid IPv6 addresses
@@ -40,7 +41,7 @@ class Block
$this->mExpiry = self::decodeExpiry( $expiry );
$this->mEnableAutoblock = $enableAutoblock;
$this->mHideName = $hideName;
-
+ $this->mBlockEmail = $blockEmail;
$this->mForUpdate = false;
$this->mFromMaster = false;
$this->mByName = false;
@@ -76,7 +77,7 @@ class Block
$this->mAddress = $this->mReason = $this->mTimestamp = '';
$this->mId = $this->mAnonOnly = $this->mCreateAccount =
$this->mEnableAutoblock = $this->mAuto = $this->mUser =
- $this->mBy = $this->mHideName = 0;
+ $this->mBy = $this->mHideName = $this->mBlockEmail = 0;
$this->mByName = false;
}
@@ -262,6 +263,7 @@ class Block
$this->mAnonOnly = $row->ipb_anon_only;
$this->mCreateAccount = $row->ipb_create_account;
$this->mEnableAutoblock = $row->ipb_enable_autoblock;
+ $this->mBlockEmail = $row->ipb_block_email;
$this->mHideName = $row->ipb_deleted;
$this->mId = $row->ipb_id;
$this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
@@ -371,6 +373,7 @@ class Block
# Unset ipb_enable_autoblock for IP blocks, makes no sense
if ( !$this->mUser ) {
$this->mEnableAutoblock = 0;
+ $this->mBlockEmail = 0; //Same goes for email...
}
# Don't collide with expired blocks
@@ -392,7 +395,8 @@ class Block
'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
'ipb_range_start' => $this->mRangeStart,
'ipb_range_end' => $this->mRangeEnd,
- 'ipb_deleted' => $this->mHideName
+ 'ipb_deleted' => $this->mHideName,
+ 'ipb_block_email' => $this->mBlockEmail
), 'Block::insert', array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
@@ -438,9 +442,6 @@ class Block
* @return bool Whether or not an autoblock was inserted.
*/
function doAutoblock( $autoblockip, $justInserted = false ) {
- # Check if this IP address is already blocked
- $dbw = wfGetDB( DB_MASTER );
-
# If autoblocks are disabled, go away.
if ( !$this->mEnableAutoblock ) {
return;
@@ -627,11 +628,11 @@ class Block
/**
* Decode expiry which has come from the DB
*/
- static function decodeExpiry( $expiry ) {
+ static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
if ( $expiry == '' || $expiry == Block::infinity() ) {
return Block::infinity();
} else {
- return wfTimestamp( TS_MW, $expiry );
+ return wfTimestamp( $timestampType, $expiry );
}
}
@@ -698,4 +699,4 @@ class Block
}
}
-?>
+
diff --git a/includes/CacheDependency.php b/includes/CacheDependency.php
index bb5c5437..1d48c383 100644
--- a/includes/CacheDependency.php
+++ b/includes/CacheDependency.php
@@ -344,4 +344,4 @@ class ConstantDependency extends CacheDependency {
}
}
-?>
+
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 356f9ea2..76a388a6 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -90,6 +90,11 @@ class CategoryViewer {
$this->getImageSection() .
$this->getCategoryBottom();
+ // Give a proper message if category is empty
+ if ( $r == '' ) {
+ $r = wfMsgExt( 'category-empty', array( 'parse' ) );
+ }
+
wfProfileOut( __METHOD__ );
return $r;
}
@@ -101,7 +106,7 @@ class CategoryViewer {
$this->children_start_char = array();
if( $this->showGallery ) {
$this->gallery = new ImageGallery();
- $this->gallery->setParsing();
+ $this->gallery->setHideBadImages();
}
}
@@ -147,7 +152,7 @@ class CategoryViewer {
/**
* Add a page in the image namespace
*/
- function addImage( $title, $sortkey, $pageLength ) {
+ function addImage( Title $title, $sortkey, $pageLength, $isRedirect = false ) {
if ( $this->showGallery ) {
$image = new Image( $title );
if( $this->flip ) {
@@ -156,18 +161,18 @@ class CategoryViewer {
$this->gallery->add( $image );
}
} else {
- $this->addPage( $title, $sortkey, $pageLength );
+ $this->addPage( $title, $sortkey, $pageLength, $isRedirect );
}
}
/**
* Add a miscellaneous page
*/
- function addPage( $title, $sortkey, $pageLength ) {
+ function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) {
global $wgContLang;
- $this->articles[] = $this->getSkin()->makeSizeLinkObj(
- $pageLength, $title, $wgContLang->convert( $title->getPrefixedText() )
- );
+ $this->articles[] = $isRedirect
+ ? '<span class="redirect-in-category">' . $this->getSkin()->makeKnownLinkObj( $title ) . '</span>'
+ : $this->getSkin()->makeSizeLinkObj( $pageLength, $title );
$this->articles_start_char[] = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
}
@@ -194,7 +199,7 @@ class CategoryViewer {
}
$res = $dbr->select(
array( 'page', 'categorylinks' ),
- array( 'page_title', 'page_namespace', 'page_len', 'cl_sortkey' ),
+ array( 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey' ),
array( $pageCondition,
'cl_from = page_id',
'cl_to' => $this->title->getDBKey()),
@@ -219,23 +224,25 @@ class CategoryViewer {
if( $title->getNamespace() == NS_CATEGORY ) {
$this->addSubcategory( $title, $x->cl_sortkey, $x->page_len );
- } elseif( $title->getNamespace() == NS_IMAGE ) {
- $this->addImage( $title, $x->cl_sortkey, $x->page_len );
+ } elseif( $this->showGallery && $title->getNamespace() == NS_IMAGE ) {
+ $this->addImage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
} else {
- $this->addPage( $title, $x->cl_sortkey, $x->page_len );
+ $this->addPage( $title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect );
}
}
$dbr->freeResult( $res );
}
function getCategoryTop() {
- $r = "<br style=\"clear:both;\"/>\n";
+ $r = '';
if( $this->until != '' ) {
$r .= $this->pagingLinks( $this->title, $this->nextPage, $this->until, $this->limit );
} elseif( $this->nextPage != '' || $this->from != '' ) {
$r .= $this->pagingLinks( $this->title, $this->from, $this->nextPage, $this->limit );
}
- return $r;
+ return $r == ''
+ ? $r
+ : "<br style=\"clear:both;\"/>\n" . $r;
}
function getSubcategorySection() {
@@ -352,7 +359,7 @@ class CategoryViewer {
}
$cont_msg = "";
if ( $articles_start_char[$index] == $prev_start_char )
- $cont_msg = wfMsgHtml('listingcontinuesabbrev');
+ $cont_msg = ' ' . wfMsgHtml( 'listingcontinuesabbrev' );
$r .= "<h3>" . htmlspecialchars( $articles_start_char[$index] ) . "$cont_msg</h3>\n<ul>";
$prev_start_char = $articles_start_char[$index];
}
@@ -423,4 +430,4 @@ class CategoryViewer {
}
-?>
+
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index 7faae935..b94dcf5e 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -189,4 +189,4 @@ class Categoryfinder {
} # END OF CLASS "Categoryfinder"
-?>
+
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index 751e1226..8d0f9508 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -9,7 +9,7 @@ class RCCacheEntry extends RecentChange
var $curlink , $difflink, $lastlink , $usertalklink , $versionlink ;
var $userlink, $timestamp, $watched;
- function newFromParent( $rc ) {
+ static function newFromParent( $rc ) {
$rc2 = new RCCacheEntry;
$rc2->mAttribs = $rc->mAttribs;
$rc2->mExtra = $rc->mExtra;
@@ -171,7 +171,8 @@ class ChangesList {
? 'rcid='.$rc->mAttribs['rc_id']
: '';
$articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
- if($watched) $articlelink = '<strong>'.$articlelink.'</strong>';
+ if( $watched )
+ $articlelink = "<strong class=\"mw-watched\">{$articlelink}</strong>";
global $wgContLang;
$articlelink .= $wgContLang->getDirMark();
@@ -204,7 +205,7 @@ class ChangesList {
*/
function usePatrol() {
global $wgUseRCPatrol, $wgUser;
- return( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) );
+ return( $wgUseRCPatrol && ($wgUser->isAllowed('patrol') || $wgUser->isAllowed('patrolmarks')) );
}
/**
@@ -568,7 +569,7 @@ class EnhancedChangesList extends ChangesList {
function maybeWatchedLink( $link, $watched=false ) {
if( $watched ) {
// FIXME: css style might be more appropriate
- return '<strong>' . $link . '</strong>';
+ return '<strong class="mw-watched">' . $link . '</strong>';
} else {
return $link;
}
@@ -703,4 +704,4 @@ class EnhancedChangesList extends ChangesList {
}
}
-?>
+
diff --git a/includes/CoreParserFunctions.php b/includes/CoreParserFunctions.php
index 72ceb45f..a5f45016 100644
--- a/includes/CoreParserFunctions.php
+++ b/includes/CoreParserFunctions.php
@@ -97,15 +97,20 @@ class CoreParserFunctions {
return $parser->getFunctionLang()->convertPlural( $text, $arg0, $arg1, $arg2, $arg3, $arg4 );
}
- static function displaytitle( $parser, $param = '' ) {
- $parserOptions = new ParserOptions;
- $local_parser = clone $parser;
- $t2 = $local_parser->parse ( $param, $parser->mTitle, $parserOptions, false );
- $parser->mOutput->mHTMLtitle = $t2->GetText();
-
- # Add subtitle
- $t = $parser->mTitle->getPrefixedText();
- $parser->mOutput->mSubtitle .= wfMsg('displaytitle', $t);
+ /**
+ * Override the title of the page when viewed,
+ * provided we've been given a title which
+ * will normalise to the canonical title
+ *
+ * @param Parser $parser Parent parser
+ * @param string $text Desired title text
+ * @return string
+ */
+ static function displaytitle( $parser, $text = '' ) {
+ $text = trim( Sanitizer::decodeCharReferences( $text ) );
+ $title = Title::newFromText( $text );
+ if( $title instanceof Title && $title->getFragment() == '' && $title->equals( $parser->mTitle ) )
+ $parser->mOutput->setDisplayTitle( $text );
return '';
}
@@ -156,7 +161,7 @@ class CoreParserFunctions {
static function pad( $string = '', $length = 0, $char = 0, $direction = STR_PAD_RIGHT ) {
$length = min( max( $length, 0 ), 500 );
$char = substr( $char, 0, 1 );
- return ( $string && (int)$length > 0 && strlen( trim( (string)$char ) ) > 0 )
+ return ( $string !== '' && (int)$length > 0 && strlen( trim( (string)$char ) ) > 0 )
? str_pad( $string, $length, (string)$char, $direction )
: $string;
}
@@ -193,4 +198,4 @@ class CoreParserFunctions {
return '';
}
}
-?>
+
diff --git a/includes/Credits.php b/includes/Credits.php
index 87382a86..580a8d92 100644
--- a/includes/Credits.php
+++ b/includes/Credits.php
@@ -185,4 +185,4 @@ function creditOthersLink($article) {
return $skin->makeKnownLink($article->mTitle->getPrefixedText(), wfMsg('others'), 'action=credits');
}
-?>
+
diff --git a/includes/Database.php b/includes/Database.php
index 3fd6ad16..4f8c7d5e 100644
--- a/includes/Database.php
+++ b/includes/Database.php
@@ -214,7 +214,7 @@ border=\"0\" ALT=\"Google\"></A>
$cache = new HTMLFileCache( $t );
if( $cache->isFileCached() ) {
- // FIXME: $msg is not defined on the next line.
+ // @todo, FIXME: $msg is not defined on the next line.
$msg = '<p style="color: red"><b>'.$msg."<br />\n" .
$cachederror . "</b></p>\n";
@@ -441,6 +441,14 @@ class Database {
}
/**
+ * Returns true if this database does an implicit order by when the column has an index
+ * For example: SELECT page_title FROM page LIMIT 1
+ */
+ function implicitOrderby() {
+ return true;
+ }
+
+ /**
* Returns true if this database can do a native search on IP columns
* e.g. this works as expected: .. WHERE rc_ip = '127.42.12.102/32';
*/
@@ -448,6 +456,13 @@ class Database {
return false;
}
+ /**
+ * Returns true if this database can use functional indexes
+ */
+ function functionalIndexes() {
+ return false;
+ }
+
/**#@+
* Get function
*/
@@ -582,7 +597,7 @@ class Database {
@/**/$this->mConn = mysql_connect( $server, $user, $password, true );
}
if ($this->mConn === false) {
- $iplus = $i + 1;
+ #$iplus = $i + 1;
#wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n");
}
}
@@ -678,9 +693,12 @@ class Database {
* Usually aborts on failure. If errors are explicitly ignored, returns success.
*
* @param $sql String: SQL query
- * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST comment (you can use __METHOD__ or add some extra info)
- * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors... maybe best to catch the exception instead?
- * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure if $tempIgnore set
+ * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST
+ * comment (you can use __METHOD__ or add some extra info)
+ * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors...
+ * maybe best to catch the exception instead?
+ * @return true for a successful write query, ResultWrapper object for a successful read query,
+ * or false on failure if $tempIgnore set
* @throws DBQueryError Thrown when the database returns an error of any kind
*/
public function query( $sql, $fname = '', $tempIgnore = false ) {
@@ -765,7 +783,7 @@ class Database {
wfProfileOut( $queryProf );
wfProfileOut( $totalProf );
}
- return $ret;
+ return $this->resultObject( $ret );
}
/**
@@ -909,6 +927,9 @@ class Database {
* Free a result object
*/
function freeResult( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
if ( !@/**/mysql_free_result( $res ) ) {
throw new DBUnexpectedError( $this, "Unable to free MySQL result" );
}
@@ -924,6 +945,9 @@ class Database {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@/**/$row = mysql_fetch_object( $res );
if( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
@@ -940,6 +964,9 @@ class Database {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@/**/$row = mysql_fetch_array( $res );
if ( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
@@ -951,6 +978,9 @@ class Database {
* Get the number of rows in a result object
*/
function numRows( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@/**/$n = mysql_num_rows( $res );
if( $this->lastErrno() ) {
throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) );
@@ -962,14 +992,24 @@ class Database {
* Get the number of fields in a result object
* See documentation for mysql_num_fields()
*/
- function numFields( $res ) { return mysql_num_fields( $res ); }
+ function numFields( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return mysql_num_fields( $res );
+ }
/**
* Get a field name in a result object
* See documentation for mysql_field_name():
* http://www.php.net/mysql_field_name
*/
- function fieldName( $res, $n ) { return mysql_field_name( $res, $n ); }
+ function fieldName( $res, $n ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return mysql_field_name( $res, $n );
+ }
/**
* Get the inserted value of an auto-increment row
@@ -987,7 +1027,12 @@ class Database {
* Change the position of the cursor in a result object
* See mysql_data_seek()
*/
- function dataSeek( $res, $row ) { return mysql_data_seek( $res, $row ); }
+ function dataSeek( $res, $row ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return mysql_data_seek( $res, $row );
+ }
/**
* Get the last error number
@@ -1091,6 +1136,7 @@ class Database {
}
if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
+ if ( isset( $options['HAVING'] ) ) $preLimitTail .= " HAVING {$options['HAVING']}";
if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
//if (isset($options['LIMIT'])) {
@@ -1101,7 +1147,7 @@ class Database {
if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $postLimitTail .= ' FOR UPDATE';
if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $postLimitTail .= ' LOCK IN SHARE MODE';
- if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
+ if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
# Various MySQL extensions
if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) $startOpts .= ' /*! STRAIGHT_JOIN */';
@@ -1175,7 +1221,6 @@ class Database {
if (isset($options['EXPLAIN'])) {
$sql = 'EXPLAIN ' . $sql;
}
-
return $this->query( $sql, $fname );
}
@@ -1352,9 +1397,9 @@ class Database {
function fieldInfo( $table, $field ) {
$table = $this->tableName( $table );
$res = $this->query( "SELECT * FROM $table LIMIT 1" );
- $n = mysql_num_fields( $res );
+ $n = mysql_num_fields( $res->result );
for( $i = 0; $i < $n; $i++ ) {
- $meta = mysql_fetch_field( $res, $i );
+ $meta = mysql_fetch_field( $res->result, $i );
if( $field == $meta->name ) {
return new MySQLField($meta);
}
@@ -1366,6 +1411,9 @@ class Database {
* mysql_field_type() wrapper
*/
function fieldType( $res, $index ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
return mysql_field_type( $res, $index );
}
@@ -1455,6 +1503,7 @@ class Database {
* (for the log)
* @param array $options An array of UPDATE options, can be one or
* more of IGNORE, LOW_PRIORITY
+ * @return bool
*/
function update( $table, $values, $conds, $fname = 'Database::update', $options = array() ) {
$table = $this->tableName( $table );
@@ -1463,7 +1512,7 @@ class Database {
if ( $conds != '*' ) {
$sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
}
- $this->query( $sql, $fname );
+ return $this->query( $sql, $fname );
}
/**
@@ -1498,8 +1547,15 @@ class Database {
$list .= "($value)";
} elseif ( ($mode == LIST_SET) && is_numeric( $field ) ) {
$list .= "$value";
- } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array ($value) ) {
+ } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array($value) ) {
$list .= $field." IN (".$this->makeList($value).") ";
+ } elseif( is_null($value) ) {
+ if ( $mode == LIST_AND || $mode == LIST_OR ) {
+ $list .= "$field IS ";
+ } elseif ( $mode == LIST_SET ) {
+ $list .= "$field = ";
+ }
+ $list .= 'NULL';
} else {
if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
$list .= "$field = ";
@@ -1534,7 +1590,7 @@ class Database {
global $wgSharedDB;
# Skip quoted literals
if ( $name{0} != '`' ) {
- if ( $this->mTablePrefix !== '' && strpos( '.', $name ) === false ) {
+ if ( $this->mTablePrefix !== '' && strpos( $name, '.' ) === false ) {
$name = "{$this->mTablePrefix}$name";
}
if ( isset( $wgSharedDB ) && "{$this->mTablePrefix}user" == $name ) {
@@ -1570,7 +1626,7 @@ class Database {
* This is handy when you need to construct SQL for joins
*
* Example:
- * list( $user, $watchlist ) = $dbr->tableNames('user','watchlist');
+ * list( $user, $watchlist ) = $dbr->tableNamesN('user','watchlist');
* $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
* WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
*/
@@ -2001,7 +2057,12 @@ class Database {
*/
function resultObject( $result ) {
if( empty( $result ) ) {
- return NULL;
+ return false;
+ } elseif ( $result instanceof ResultWrapper ) {
+ return $result;
+ } elseif ( $result === true ) {
+ // Successful write query
+ return $result;
} else {
return new ResultWrapper( $this, $result );
}
@@ -2046,8 +2107,7 @@ class Database {
*/
function getLag() {
$res = $this->query( 'SHOW PROCESSLIST' );
- # Find slave SQL thread. Assumed to be the second one running, which is a bit
- # dubious, but unfortunately there's no easy rigorous way
+ # Find slave SQL thread
while ( $row = $this->fetchObject( $res ) ) {
/* This should work for most situations - when default db
* for thread is not specified, it had no events executed,
@@ -2176,7 +2236,7 @@ class Database {
$cmd = $this->replaceVars( $cmd );
$res = $this->query( $cmd, __METHOD__, true );
if ( $resultCallback ) {
- call_user_func( $resultCallback, $this->resultObject( $res ) );
+ call_user_func( $resultCallback, $res );
}
if ( false === $res ) {
@@ -2244,40 +2304,55 @@ class DatabaseMysql extends Database {
* Result wrapper for grabbing data queried by someone else
* @addtogroup Database
*/
-class ResultWrapper {
- var $db, $result;
+class ResultWrapper implements Iterator {
+ var $db, $result, $pos = 0, $currentRow = null;
/**
- * @todo document
+ * Create a new result object from a result resource and a Database object
*/
- function ResultWrapper( &$database, $result ) {
- $this->db =& $database;
- $this->result =& $result;
+ function ResultWrapper( $database, $result ) {
+ $this->db = $database;
+ if ( $result instanceof ResultWrapper ) {
+ $this->result = $result->result;
+ } else {
+ $this->result = $result;
+ }
}
/**
- * @todo document
+ * Get the number of rows in a result object
*/
function numRows() {
return $this->db->numRows( $this->result );
}
/**
- * @todo document
+ * Fetch the next row from the given result object, in object form.
+ * Fields can be retrieved with $row->fieldname, with fields acting like
+ * member variables.
+ *
+ * @param $res SQL result object as returned from Database::query(), etc.
+ * @return MySQL row object
+ * @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject() {
return $this->db->fetchObject( $this->result );
}
/**
- * @todo document
+ * Fetch the next row from the given result object, in associative array
+ * form. Fields are retrieved with $row['fieldname'].
+ *
+ * @param $res SQL result object as returned from Database::query(), etc.
+ * @return MySQL row object
+ * @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow() {
return $this->db->fetchRow( $this->result );
}
/**
- * @todo document
+ * Free a result object
*/
function free() {
$this->db->freeResult( $this->result );
@@ -2285,16 +2360,48 @@ class ResultWrapper {
unset( $this->db );
}
+ /**
+ * Change the position of the cursor in a result object
+ * See mysql_data_seek()
+ */
function seek( $row ) {
$this->db->dataSeek( $this->result, $row );
}
-
+
+ /*********************
+ * Iterator functions
+ * Note that using these in combination with the non-iterator functions
+ * above may cause rows to be skipped or repeated.
+ */
+
function rewind() {
if ($this->numRows()) {
$this->db->dataSeek($this->result, 0);
}
+ $this->pos = 0;
+ $this->currentRow = null;
+ }
+
+ function current() {
+ if ( is_null( $this->currentRow ) ) {
+ $this->next();
+ }
+ return $this->currentRow;
+ }
+
+ function key() {
+ return $this->pos;
}
+ function next() {
+ $this->pos++;
+ $this->currentRow = $this->fetchObject();
+ return $this->currentRow;
+ }
+
+ function valid() {
+ return $this->current() !== false;
+ }
}
-?>
+
diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php
index 4b31b4f0..a4a0444f 100644
--- a/includes/DatabaseFunctions.php
+++ b/includes/DatabaseFunctions.php
@@ -399,4 +399,4 @@ function wfUseIndexClause( $index, $dbi = DB_SLAVE ) {
return false;
}
}
-?>
+
diff --git a/includes/DatabaseOracle.php b/includes/DatabaseOracle.php
index 2b720df7..38485481 100644
--- a/includes/DatabaseOracle.php
+++ b/includes/DatabaseOracle.php
@@ -128,6 +128,9 @@ class DatabaseOracle extends Database {
function implicitGroupby() {
return false;
}
+ function implicitOrderby() {
+ return false;
+ }
function searchableIPs() {
return true;
}
@@ -659,7 +662,7 @@ echo "error!\n";
#if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $tailOpts .= ' FOR UPDATE';
#if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $tailOpts .= ' LOCK IN SHARE MODE';
- if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
+ if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
$useIndex = $this->useIndexClause( $options['USE INDEX'] );
@@ -675,11 +678,20 @@ echo "error!\n";
}
function ping() {
- wfDebug( "Function ping() not written for DatabasePostgres.php yet");
+ wfDebug( "Function ping() not written for DatabaseOracle.php yet");
return true;
}
+ /**
+ * How lagged is this slave?
+ *
+ * @return int
+ */
+ public function getLag() {
+ # Not implemented for Oracle
+ return 0;
+ }
} // end DatabaseOracle class
-?>
+
diff --git a/includes/DatabasePostgres.php b/includes/DatabasePostgres.php
index 07b3339d..32c061a0 100644
--- a/includes/DatabasePostgres.php
+++ b/includes/DatabasePostgres.php
@@ -102,9 +102,15 @@ class DatabasePostgres extends Database {
function implicitGroupby() {
return false;
}
+ function implicitOrderby() {
+ return false;
+ }
function searchableIPs() {
return true;
}
+ function functionalIndexes() {
+ return true;
+ }
static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0)
{
@@ -155,6 +161,7 @@ class DatabasePostgres extends Database {
$this->mOpened = true;
## If this is the initial connection, setup the schema stuff and possibly create the user
+ ## TODO: Move this out of open()
if (defined('MEDIAWIKI_INSTALL')) {
global $wgDBname, $wgDBuser, $wgDBpassword, $wgDBsuperuser, $wgDBmwschema,
$wgDBts2schema;
@@ -504,12 +511,18 @@ class DatabasePostgres extends Database {
}
function freeResult( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
if ( !@pg_free_result( $res ) ) {
throw new DBUnexpectedError($this, "Unable to free Postgres result\n" );
}
}
function fetchObject( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@$row = pg_fetch_object( $res );
# FIXME: HACK HACK HACK HACK debug
@@ -523,6 +536,9 @@ class DatabasePostgres extends Database {
}
function fetchRow( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@$row = pg_fetch_array( $res );
if( pg_last_error($this->mConn) ) {
throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
@@ -531,14 +547,27 @@ class DatabasePostgres extends Database {
}
function numRows( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
@$n = pg_num_rows( $res );
if( pg_last_error($this->mConn) ) {
throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
}
return $n;
}
- function numFields( $res ) { return pg_num_fields( $res ); }
- function fieldName( $res, $n ) { return pg_field_name( $res, $n ); }
+ function numFields( $res ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return pg_num_fields( $res );
+ }
+ function fieldName( $res, $n ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return pg_field_name( $res, $n );
+ }
/**
* This must be called after nextSequenceVal
@@ -547,7 +576,13 @@ class DatabasePostgres extends Database {
return $this->mInsertId;
}
- function dataSeek( $res, $row ) { return pg_result_seek( $res, $row ); }
+ function dataSeek( $res, $row ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return pg_result_seek( $res, $row );
+ }
+
function lastError() {
if ( $this->mConn ) {
return pg_last_error();
@@ -561,7 +596,7 @@ class DatabasePostgres extends Database {
}
function affectedRows() {
- if( !isset( $this->mLastResult ) )
+ if( !isset( $this->mLastResult ) or ! $this->mLastResult )
return 0;
return pg_affected_rows( $this->mLastResult );
@@ -601,13 +636,9 @@ class DatabasePostgres extends Database {
if ( !$res ) {
return NULL;
}
-
while ( $row = $this->fetchObject( $res ) ) {
if ( $row->indexname == $index ) {
return $row;
-
- // BUG: !!!! This code needs to be synced up with database.php
-
}
}
return false;
@@ -666,7 +697,7 @@ class DatabasePostgres extends Database {
$sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
if ( $multi ) {
- if ( $wgDBversion >= 8.1 ) {
+ if ( $wgDBversion >= 8.2 ) {
$first = true;
foreach ( $args as $row ) {
if ( $first ) {
@@ -920,10 +951,10 @@ class DatabasePostgres extends Database {
. "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
. "AND c.relkind IN ('" . implode("','", $types) . "')";
$res = $this->query( $SQL );
- $count = $res ? pg_num_rows($res) : 0;
+ $count = $res ? $res->numRows() : 0;
if ($res)
$this->freeResult( $res );
- return $count;
+ return $count ? true : false;
}
/*
@@ -953,7 +984,7 @@ END;
$this->addQuotes($trigger)));
if (!$res)
return NULL;
- $rows = pg_num_rows($res);
+ $rows = $res->numRows();
$this->freeResult($res);
return $rows;
}
@@ -977,7 +1008,7 @@ END;
$res = $this->query($SQL);
if (!$res)
return NULL;
- $rows = pg_num_rows($res);
+ $rows = $res->numRows();
$this->freeResult($res);
return $rows;
}
@@ -990,7 +1021,12 @@ END;
$SQL = "SELECT rolname FROM pg_catalog.pg_namespace n, pg_catalog.pg_roles r "
."WHERE n.nspowner=r.oid AND n.nspname = '$eschema'";
$res = $this->query( $SQL );
- $owner = $res ? pg_num_rows($res) ? pg_fetch_result($res, 0, 0) : false : false;
+ if ( $res && $res->numRows() ) {
+ $row = $res->fetchObject();
+ $owner = $row->rolname;
+ } else {
+ $owner = false;
+ }
if ($res)
$this->freeResult($res);
return $owner;
@@ -1008,7 +1044,7 @@ END;
. "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
. "AND a.attrelid = c.oid AND a.attname = '$ecol'";
$res = $this->query( $SQL, $fname );
- $count = $res ? pg_num_rows($res) : 0;
+ $count = $res ? $res->numRows() : 0;
if ($res)
$this->freeResult( $res );
return $count;
@@ -1102,10 +1138,10 @@ END;
$this->doQuery("COMMIT");
}
- function encodeBlob($b) {
- return array('bytea',pg_escape_bytea($b));
+ function encodeBlob( $b ) {
+ return pg_escape_bytea( $b );
}
- function decodeBlob($b) {
+ function decodeBlob( $b ) {
return pg_unescape_bytea( $b );
}
@@ -1133,8 +1169,7 @@ END;
}
/**
- * Returns an optional USE INDEX clause to go after the table, and a
- * string to go at the end of the query
+ * Various select options
*
* @private
*
@@ -1144,7 +1179,7 @@ END;
*/
function makeSelectOptions( $options ) {
$preLimitTail = $postLimitTail = '';
- $startOpts = '';
+ $startOpts = $useIndex = '';
$noKeyOptions = array();
foreach ( $options as $key => $option ) {
@@ -1154,6 +1189,7 @@ END;
}
if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY " . $options['GROUP BY'];
+ if ( isset( $options['HAVING'] ) ) $preLimitTail .= " HAVING {$options['HAVING']}";
if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY " . $options['ORDER BY'];
//if (isset($options['LIMIT'])) {
@@ -1164,13 +1200,7 @@ END;
if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $postLimitTail .= ' FOR UPDATE';
if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $postLimitTail .= ' LOCK IN SHARE MODE';
- if ( isset( $noKeyOptions['DISTINCT'] ) && isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
-
- if ( isset( $options['USE INDEX'] ) && ! is_array( $options['USE INDEX'] ) ) {
- $useIndex = $this->useIndexClause( $options['USE INDEX'] );
- } else {
- $useIndex = '';
- }
+ if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
}
@@ -1183,8 +1213,16 @@ END;
wfDebug( "Function ping() not written for DatabasePostgres.php yet");
return true;
}
-
+
+ /**
+ * How lagged is this slave?
+ *
+ */
+ public function getLag() {
+ # Not implemented for PostgreSQL
+ return false;
+ }
} // end DatabasePostgres class
-?>
+
diff --git a/includes/DateFormatter.php b/includes/DateFormatter.php
index 88a64453..bbad6d15 100644
--- a/includes/DateFormatter.php
+++ b/includes/DateFormatter.php
@@ -282,4 +282,4 @@ class DateFormatter
}
}
-?>
+
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index d8f9a621..1ed8779a 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -27,11 +27,11 @@ if( !defined( 'MEDIAWIKI' ) ) {
* Create a site configuration object
* Not used for much in a default install
*/
-require_once( 'includes/SiteConfiguration.php' );
+require_once( "$IP/includes/SiteConfiguration.php" );
$wgConf = new SiteConfiguration;
/** MediaWiki version number */
-$wgVersion = '1.10.2';
+$wgVersion = '1.11.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
@@ -125,8 +125,9 @@ $wgUsePathInfo =
* in LocalSettings.php. Generally you should not need to change this
* unless you don't like seeing "index.php".
*/
-$wgScript = false; /// defaults to "{$wgScriptPath}/index.php"
-$wgRedirectScript = false; /// defaults to "{$wgScriptPath}/redirect.php"
+$wgScriptExtension = '.php'; /// extension to append to script names by default
+$wgScript = false; /// defaults to "{$wgScriptPath}/index{$wgScriptExtension}"
+$wgRedirectScript = false; /// defaults to "{$wgScriptPath}/redirect{$wgScriptExtension}"
/**#@-*/
@@ -163,16 +164,6 @@ $wgUploadBaseUrl = "";
/**#@-*/
/**
- * By default deleted files are simply discarded; to save them and
- * make it possible to undelete images, create a directory which
- * is writable to the web server but is not exposed to the internet.
- *
- * Set $wgSaveDeletedFiles to true and set up the save path in
- * $wgFileStore['deleted']['directory'].
- */
-$wgSaveDeletedFiles = false;
-
-/**
* New file storage paths; currently used only for deleted files.
* Set it like this:
*
@@ -180,10 +171,61 @@ $wgSaveDeletedFiles = false;
*
*/
$wgFileStore = array();
-$wgFileStore['deleted']['directory'] = null; // Don't forget to set this.
+$wgFileStore['deleted']['directory'] = false;// Defaults to $wgUploadDirectory/deleted
$wgFileStore['deleted']['url'] = null; // Private
$wgFileStore['deleted']['hash'] = 3; // 3-level subdirectory split
+/**#@+
+ * File repository structures
+ *
+ * $wgLocalFileRepo is a single repository structure, and $wgForeignFileRepo is
+ * a an array of such structures. Each repository structure is an associative
+ * array of properties configuring the repository.
+ *
+ * Properties required for all repos:
+ * class The class name for the repository. May come from the core or an extension.
+ * The core repository classes are LocalRepo, ForeignDBRepo, FSRepo.
+ *
+ * name A unique name for the repository.
+ *
+ * For all core repos:
+ * url Base public URL
+ * hashLevels The number of directory levels for hash-based division of files
+ * thumbScriptUrl The URL for thumb.php (optional, not recommended)
+ * transformVia404 Whether to skip media file transformation on parse and rely on a 404
+ * handler instead.
+ * initialCapital Equivalent to $wgCapitalLinks, determines whether filenames implicitly
+ * start with a capital letter. The current implementation may give incorrect
+ * description page links when the local $wgCapitalLinks and initialCapital
+ * are mismatched.
+ * pathDisclosureProtection
+ * May be 'paranoid' to remove all parameters from error messages, 'none' to
+ * leave the paths in unchanged, or 'simple' to replace paths with
+ * placeholders. Default for LocalRepo is 'simple'.
+ *
+ * These settings describe a foreign MediaWiki installation. They are optional, and will be ignored
+ * for local repositories:
+ * descBaseUrl URL of image description pages, e.g. http://en.wikipedia.org/wiki/Image:
+ * scriptDirUrl URL of the MediaWiki installation, equivalent to $wgScriptPath, e.g.
+ * http://en.wikipedia.org/w
+ *
+ * articleUrl Equivalent to $wgArticlePath, e.g. http://en.wikipedia.org/wiki/$1
+ * fetchDescription Fetch the text of the remote file description page. Equivalent to
+ * $wgFetchCommonsDescriptions.
+ *
+ * ForeignDBRepo:
+ * dbType, dbServer, dbUser, dbPassword, dbName, dbFlags
+ * equivalent to the corresponding member of $wgDBservers
+ * tablePrefix Table prefix, the foreign wiki's $wgDBprefix
+ * hasSharedCache True if the wiki's shared cache is accessible via the local $wgMemc
+ *
+ * The default is to initialise these arrays from the MW<1.11 backwards compatible settings:
+ * $wgUploadPath, $wgThumbnailScriptPath, $wgSharedUploadDirectory, etc.
+ */
+$wgLocalFileRepo = false;
+$wgForeignFileRepos = array();
+/**#@-*/
+
/**
* Allowed title characters -- regex character class
* Don't change this unless you know what you're doing
@@ -261,34 +303,34 @@ $wgAntivirus= NULL;
*
* @global array $wgAntivirusSetup
*/
-$wgAntivirusSetup= array(
+$wgAntivirusSetup = array(
#setup for clamav
'clamav' => array (
'command' => "clamscan --no-summary ",
- 'codemap'=> array (
- "0"=> AV_NO_VIRUS, #no virus
- "1"=> AV_VIRUS_FOUND, #virus found
- "52"=> AV_SCAN_ABORTED, #unsupported file format (probably imune)
- "*"=> AV_SCAN_FAILED, #else scan failed
+ 'codemap' => array (
+ "0" => AV_NO_VIRUS, # no virus
+ "1" => AV_VIRUS_FOUND, # virus found
+ "52" => AV_SCAN_ABORTED, # unsupported file format (probably imune)
+ "*" => AV_SCAN_FAILED, # else scan failed
),
- 'messagepattern'=> '/.*?:(.*)/sim',
+ 'messagepattern' => '/.*?:(.*)/sim',
),
#setup for f-prot
'f-prot' => array (
'command' => "f-prot ",
- 'codemap'=> array (
- "0"=> AV_NO_VIRUS, #no virus
- "3"=> AV_VIRUS_FOUND, #virus found
- "6"=> AV_VIRUS_FOUND, #virus found
- "*"=> AV_SCAN_FAILED, #else scan failed
+ 'codemap' => array (
+ "0" => AV_NO_VIRUS, # no virus
+ "3" => AV_VIRUS_FOUND, # virus found
+ "6" => AV_VIRUS_FOUND, # virus found
+ "*" => AV_SCAN_FAILED, # else scan failed
),
- 'messagepattern'=> '/.*?Infection:(.*)$/m',
+ 'messagepattern' => '/.*?Infection:(.*)$/m',
),
);
@@ -355,6 +397,10 @@ $wgActionPaths = array();
* no file of the given name is found in the local repository (for [[Image:..]],
* [[Media:..]] links). Thumbnails will also be looked for and generated in this
* directory.
+ *
+ * Note that these configuration settings can now be defined on a per-
+ * repository basis for an arbitrary number of file repositories, using the
+ * $wgForeignFileRepos variable.
*/
$wgUseSharedUploads = false;
/** Full path on the web server where shared uploads can be found */
@@ -392,7 +438,7 @@ $wgUploadNavigationUrl = false;
* apache servers don't have read/write access to the thumbnail path.
*
* Example:
- * $wgThumbnailScriptPath = "{$wgScriptPath}/thumb.php";
+ * $wgThumbnailScriptPath = "{$wgScriptPath}/thumb{$wgScriptExtension}";
*/
$wgThumbnailScriptPath = false;
$wgSharedThumbnailScriptPath = false;
@@ -600,6 +646,19 @@ $wgDBmysql5 = false;
$wgLocalDatabases = array();
/**
+ * For multi-wiki clusters with multiple master servers; if an alternate
+ * is listed for the requested database, a connection to it will be opened
+ * instead of to the current wiki's regular master server when cross-wiki
+ * data operations are done from here.
+ *
+ * Requires that the other server be accessible by network, with the same
+ * username/password as the primary.
+ *
+ * eg $wgAlternateMaster['enwiki'] = 'ariel';
+ */
+$wgAlternateMaster = array();
+
+/**
* Object cache settings
* See Defines.php for types
*/
@@ -619,7 +678,6 @@ $wgLinkCacheMemcached = false; # Not fully tested
$wgUseMemCached = false;
$wgMemCachedDebug = false; # Will be set to false in Setup.php, if the server isn't working
$wgMemCachedServers = array( '127.0.0.1:11000' );
-$wgMemCachedDebug = false;
$wgMemCachedPersistent = false;
/**
@@ -806,6 +864,7 @@ $wgRedirectSources = false;
$wgShowIPinHeader = true; # For non-logged in users
$wgMaxNameChars = 255; # Maximum number of bytes in username
+$wgMaxSigChars = 255; # Maximum number of Unicode characters in signature
$wgMaxArticleSize = 2048; # Maximum article size in kilobytes
$wgExtraSubtitle = '';
@@ -869,8 +928,12 @@ $wgColorErrors = true;
$wgShowExceptionDetails = false;
/**
- * disable experimental dmoz-like category browsing. Output things like:
- * Encyclopedia > Music > Style of Music > Jazz
+ * Expose backend server host names through the API and various HTML comments
+ */
+$wgShowHostnames = false;
+
+/**
+ * Use experimental, DMOZ-like category browser
*/
$wgUseCategoryBrowser = false;
@@ -920,9 +983,10 @@ $wgHitcounterUpdateFreq = 1;
# Basic user rights and block settings
$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
+$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
+$wgSysopEmailBans = true; # Allow sysops to ban users from accessing Emailuser
# Pages anonymous user may see as an array, e.g.:
# array ( "Main Page", "Special:Userlogin", "Wikipedia:Help");
@@ -947,6 +1011,11 @@ $wgEmailConfirmToEdit=false;
* combined with the permissions of all groups that a given user is listed
* in in the user_groups table.
*
+ * Note: Don't set $wgGroupPermissions = array(); unless you know what you're
+ * doing! This will wipe all permissions, and may mean that your users are
+ * unable to perform certain essential tasks or access new functionality
+ * when new permissions are introduced and default grants established.
+ *
* Functionality to make pages inaccessible has not been extensively tested
* for security. Use at your own risk!
*
@@ -1009,6 +1078,7 @@ $wgGroupPermissions['sysop']['unwatchedpages'] = true;
$wgGroupPermissions['sysop']['autoconfirmed'] = true;
$wgGroupPermissions['sysop']['upload_by_url'] = true;
$wgGroupPermissions['sysop']['ipblock-exempt'] = true;
+$wgGroupPermissions['sysop']['blockemail'] = true;
// Permission to change users' group assignments
$wgGroupPermissions['bureaucrat']['userrights'] = true;
@@ -1033,8 +1103,14 @@ $wgGroupPermissions['bureaucrat']['userrights'] = true;
$wgRestrictionTypes = array( 'edit', 'move' );
/**
- * Set of permission keys that can be selected via action=protect.
- * 'autoconfirm' allows all registerd users if $wgAutoConfirmAge is 0.
+ * Rights which can be required for each protection level (via action=protect)
+ *
+ * You can add a new protection level that requires a specific
+ * permission by manipulating this array. The ordering of elements
+ * dictates the order on the protection form's lists.
+ *
+ * '' will be ignored (i.e. unprotected)
+ * 'sysop' is quietly rewritten to 'protect' for backwards compatibility
*/
$wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
@@ -1074,7 +1150,20 @@ $wgAutoConfirmAge = 0;
$wgAutoConfirmCount = 0;
//$wgAutoConfirmCount = 50;
-
+/**
+ * These settings can be used to give finer control over who can assign which
+ * groups at Special:Userrights. Example configuration:
+ *
+ * // Bureaucrat can add any group
+ * $wgAddGroups['bureaucrat'] = true;
+ * // Bureaucrats can only remove bots and sysops
+ * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' );
+ * // Sysops can make bots
+ * $wgAddGroups['sysop'] = array( 'bot' );
+ * // Sysops can disable other sysops in an emergency, and disable bots
+ * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' );
+ */
+$wgAddGroups = $wgRemoveGroups = array();
# Proxy scanner settings
#
@@ -1125,7 +1214,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '63';
+$wgStyleVersion = '97';
# Server-side caching:
@@ -1174,6 +1263,21 @@ $wgEnotifRevealEditorAddress = false; # UPO; reply-to address may be filled with
$wgEnotifMinorEdits = true; # UPO; false: "minor edits" on pages do not trigger notification mails.
# # Attention: _every_ change on a user_talk page trigger a notification mail (if the user is not yet notified)
+# Send a generic mail instead of a personalised mail for each user. This
+# always uses UTC as the time zone, and doesn't include the username.
+#
+# For pages with many users watching, this can significantly reduce mail load.
+# Has no effect when using sendmail rather than SMTP;
+
+$wgEnotifImpersonal = false;
+
+# Maximum number of users to mail at once when using impersonal mail. Should
+# match the limit on your mail server.
+$wgEnotifMaxRecips = 500;
+
+# Send mails via the job queue.
+$wgEnotifUseJobQ = false;
+
/**
* Array of usernames who will be sent a notification email for every change which occurs on a wiki
*/
@@ -1230,7 +1334,11 @@ $wgSquidMaxage = 18000;
/**
* A list of proxy servers (ips if possible) to purge on changes don't specify
- * ports here (80 is default)
+ * ports here (80 is default). When mediawiki is running behind a proxy, its
+ * address should be listed in $wgSquidServers otherwise mediawiki won't rely
+ * on the X-FORWARDED-FOR header to determine the user IP address and
+ * all users will appear to come from the proxy IP address. Don't use domain
+ * names here, only IP adresses.
*/
# $wgSquidServers = array('127.0.0.1');
$wgSquidServers = array();
@@ -1283,6 +1391,18 @@ $wgWantedPagesThreshold = 1;
$wgAllowSlowParserFunctions = false;
/**
+ * Maps jobs to their handling classes; extensions
+ * can add to this to provide custom jobs
+ */
+$wgJobClasses = array(
+ 'refreshLinks' => 'RefreshLinksJob',
+ 'htmlCacheUpdate' => 'HTMLCacheUpdateJob',
+ 'html_cache_update' => 'HTMLCacheUpdateJob', // backwards-compatible
+ 'sendMail' => 'EmaillingJob',
+ 'enotifNotify' => 'EnotifNotifyJob',
+);
+
+/**
* 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.
@@ -1322,7 +1442,11 @@ $wgDebugFunctionEntry = 0;
/** Lots of debugging output from SquidUpdate.php */
$wgDebugSquid = false;
+/** Whereas to count the number of time an article is viewed.
+ * Does not work if pages are cached (for example with squid).
+ */
$wgDisableCounters = false;
+
$wgDisableTextSearch = false;
$wgDisableSearchContext = false;
/**
@@ -1335,6 +1459,13 @@ $wgEnableUploads = false;
/**
* Show EXIF data, on by default if available.
* Requires PHP's EXIF extension: http://www.php.net/manual/en/ref.exif.php
+ *
+ * NOTE FOR WINDOWS USERS:
+ * To enable EXIF functions, add the folloing lines to the
+ * "Windows extensions" section of php.ini:
+ *
+ * extension=extensions/php_mbstring.dll
+ * extension=extensions/php_exif.dll
*/
$wgShowEXIF = function_exists( 'exif_read_data' );
@@ -1373,7 +1504,7 @@ $wgAntiLockFlags = 0;
$wgDiff3 = '/usr/bin/diff3';
/**
- * We can also compress text in the old revisions table. If this is set on, old
+ * We can also compress text stored in the 'text' table. If this is set on, new
* revisions will be compressed on page save if zlib support is available. Any
* compressed revisions will be decompressed on load regardless of this setting
* *but will not be readable at all* if zlib support is not available.
@@ -1389,7 +1520,7 @@ $wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' );
/** Files with these extensions will never be allowed as uploads. */
$wgFileBlacklist = array(
# HTML may contain cookie-stealing JavaScript and web bugs
- 'html', 'htm', 'js', 'jsb',
+ 'html', 'htm', 'js', 'jsb', 'mhtml', 'mht',
# PHP scripts may execute arbitrary code on the server
'php', 'phtml', 'php3', 'php4', 'php5', 'phps',
# Other types that may be interpreted by some servers
@@ -1445,10 +1576,15 @@ $wgNamespacesToBeSearchedDefault = array(
NS_MAIN => true,
);
-/** If set, a bold ugly notice will show up at the top of every page. */
+/**
+ * Site notice shown at the top of each page
+ *
+ * This message can contain wiki text, and can also be set through the
+ * MediaWiki:Sitenotice page. You can also provide a separate message for
+ * logged-out users using the MediaWiki:Anonnotice page.
+ */
$wgSiteNotice = '';
-
#
# Images settings
#
@@ -1463,6 +1599,7 @@ $wgMediaHandlers = array(
'image/gif' => 'BitmapHandler',
'image/x-ms-bmp' => 'BmpHandler',
'image/svg+xml' => 'SvgHandler',
+ 'image/svg' => 'SvgHandler',
'image/vnd.djvu' => 'DjVuHandler',
);
@@ -1780,6 +1917,28 @@ $wgExtensionFunctions = array();
$wgSkinExtensionFunctions = array();
/**
+ * Extension messages files
+ * Associative array mapping extension name to the filename where messages can be found.
+ * The file must create a variable called $messages.
+ * When the messages are needed, the extension should call wfLoadMessagesFile()
+ */
+$wgExtensionMessagesFiles = array();
+
+/**
+ * Parser output hooks.
+ * This is an associative array where the key is an extension-defined tag
+ * (typically the extension name), and the value is a PHP callback.
+ * These will be called as an OutputPageParserOutput hook, if the relevant
+ * tag has been registered with the parser output object.
+ *
+ * Registration is done with $pout->addOutputHook( $tag, $data ).
+ *
+ * The callback has the form:
+ * function outputHook( $outputPage, $parserOutput, $data ) { ... }
+ */
+$wgParserOutputHooks = array();
+
+/**
* List of valid skin names.
* The key should be the name in all lower case, the value should be a display name.
* The default skins will be added later, by Skin::getSkinNames(). Use
@@ -1937,6 +2096,13 @@ $wgThumbLimits = array(
);
/**
+ * Adjust width of upright images when parameter 'upright' is used
+ * This allows a nicer look for upright images without the need to fix the width
+ * by hardcoded px in wiki sourcecode.
+ */
+$wgThumbUpright = 0.75;
+
+/**
* On category pages, show thumbnail gallery for images belonging to that
* category instead of listing them as articles.
*/
@@ -1978,7 +2144,13 @@ $wgBrowserBlackList = array(
* @link http://en.wikipedia.org/w/index.php?title=User%3A%C6var_Arnfj%F6r%F0_Bjarmason%2Ftestme&diff=12356041&oldid=12355864
* @link http://en.wikipedia.org/wiki/Template%3AOS9
*/
- '/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/'
+ '/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
+
+ /**
+ * Google wireless transcoder, seems to eat a lot of chars alive
+ * http://it.wikipedia.org/w/index.php?title=Luciano_Ligabue&diff=prev&oldid=8857361
+ */
+ '/^Mozilla\/4\.0 \(compatible; MSIE 6.0; Windows NT 5.0; Google Wireless Transcoder;\)/'
);
/**
@@ -2075,7 +2247,7 @@ $wgLogTypes = array( '',
* Extensions with custom log types may add to this array.
*/
$wgLogNames = array(
- '' => 'log',
+ '' => 'all-logs-page',
'block' => 'blocklogpage',
'protect' => 'protectlogpage',
'rights' => 'rightslog',
@@ -2114,12 +2286,14 @@ $wgLogActions = array(
'block/block' => 'blocklogentry',
'block/unblock' => 'unblocklogentry',
'protect/protect' => 'protectedarticle',
+ 'protect/modify' => 'modifiedarticleprotection',
'protect/unprotect' => 'unprotectedarticle',
'rights/rights' => 'rightslogentry',
'delete/delete' => 'deletedarticle',
'delete/restore' => 'undeletedarticle',
'delete/revision' => 'revdelete-logentry',
'upload/upload' => 'uploadedimage',
+ 'upload/overwrite' => 'overwroteimage',
'upload/revert' => 'uploadedimage',
'move/move' => '1movedto2',
'move/move_redir' => '1movedto2_redir',
@@ -2178,6 +2352,16 @@ $wgNoFollowNsExceptions = array();
$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.
+ * Example:
+ * $wgArticleRobotPolicies = array( 'Main Page' => 'noindex' );
+ */
+$wgArticleRobotPolicies = array();
+
+/**
* Specifies the minimal length of a user password. If set to
* 0, empty passwords are allowed.
*/
@@ -2385,7 +2569,7 @@ $wgUpdateRowsPerQuery = 10;
/**
* Enable AJAX framework
*/
-$wgUseAjax = false;
+$wgUseAjax = true;
/**
* Enable auto suggestion for the search bar
@@ -2405,12 +2589,22 @@ $wgAjaxExportList = array( );
* Requires $wgUseAjax to be true too.
* Causes wfAjaxWatch to be added to $wgAjaxExportList
*/
-$wgAjaxWatch = false;
+$wgAjaxWatch = true;
+
+/**
+ * Enable AJAX check for file overwrite, pre-upload
+ */
+$wgAjaxUploadDestCheck = true;
+
+/**
+ * Enable previewing licences via AJAX
+ */
+$wgAjaxLicensePreview = true;
/**
* Allow DISPLAYTITLE to change title display
*/
-$wgAllowDisplayTitle = false ;
+$wgAllowDisplayTitle = true;
/**
* Array of usernames which may not be registered or logged in from
@@ -2491,13 +2685,28 @@ $wgDjvuPostProcessor = 'pnmtojpeg';
$wgDjvuOutputExtension = 'jpg';
/**
-* Enable direct access to the data API
-* through api.php
-*/
+ * Enable the MediaWiki API for convenient access to
+ * machine-readable data via api.php
+ *
+ * See http://www.mediawiki.org/wiki/API
+ */
$wgEnableAPI = true;
+
+/**
+ * Allow the API to be used to perform write operations
+ * (page edits, rollback, etc.) when an authorised user
+ * accesses it
+ */
$wgEnableWriteAPI = false;
/**
+ * API module extensions
+ * Associative array mapping module name to class name.
+ * Extension modules may override the core modules.
+ */
+$wgAPIModules = array();
+
+/**
* Parser test suite files to be run by parserTests.php when no specific
* filename is passed to it.
*
@@ -2532,4 +2741,10 @@ $wgEnableCascadingProtection = true;
*/
$wgDisableOutputCompression = false;
-?>
+/**
+ * If lag is higher than $wgSlaveLagWarning, show a warning in some special
+ * pages (like watchlist). If the lag is higher than $wgSlaveLagCritical,
+ * show a more obvious warning.
+ */
+$wgSlaveLagWarning = 10;
+$wgSlaveLagCritical = 30;
diff --git a/includes/Defines.php b/includes/Defines.php
index 98e76277..c923c256 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -205,5 +205,61 @@ define( 'LIST_SET', 2 );
define( 'LIST_NAMES', 3);
define( 'LIST_OR', 4);
+/**
+ * Unicode and normalisation related
+ */
+define( 'UNICODE_HANGUL_FIRST', 0xac00 );
+define( 'UNICODE_HANGUL_LAST', 0xd7a3 );
+
+define( 'UNICODE_HANGUL_LBASE', 0x1100 );
+define( 'UNICODE_HANGUL_VBASE', 0x1161 );
+define( 'UNICODE_HANGUL_TBASE', 0x11a7 );
+
+define( 'UNICODE_HANGUL_LCOUNT', 19 );
+define( 'UNICODE_HANGUL_VCOUNT', 21 );
+define( 'UNICODE_HANGUL_TCOUNT', 28 );
+define( 'UNICODE_HANGUL_NCOUNT', UNICODE_HANGUL_VCOUNT * UNICODE_HANGUL_TCOUNT );
+
+define( 'UNICODE_HANGUL_LEND', UNICODE_HANGUL_LBASE + UNICODE_HANGUL_LCOUNT - 1 );
+define( 'UNICODE_HANGUL_VEND', UNICODE_HANGUL_VBASE + UNICODE_HANGUL_VCOUNT - 1 );
+define( 'UNICODE_HANGUL_TEND', UNICODE_HANGUL_TBASE + UNICODE_HANGUL_TCOUNT - 1 );
+
+define( 'UNICODE_SURROGATE_FIRST', 0xd800 );
+define( 'UNICODE_SURROGATE_LAST', 0xdfff );
+define( 'UNICODE_MAX', 0x10ffff );
+define( 'UNICODE_REPLACEMENT', 0xfffd );
+
+
+define( 'UTF8_HANGUL_FIRST', "\xea\xb0\x80" /*codepointToUtf8( UNICODE_HANGUL_FIRST )*/ );
+define( 'UTF8_HANGUL_LAST', "\xed\x9e\xa3" /*codepointToUtf8( UNICODE_HANGUL_LAST )*/ );
+
+define( 'UTF8_HANGUL_LBASE', "\xe1\x84\x80" /*codepointToUtf8( UNICODE_HANGUL_LBASE )*/ );
+define( 'UTF8_HANGUL_VBASE', "\xe1\x85\xa1" /*codepointToUtf8( UNICODE_HANGUL_VBASE )*/ );
+define( 'UTF8_HANGUL_TBASE', "\xe1\x86\xa7" /*codepointToUtf8( UNICODE_HANGUL_TBASE )*/ );
+
+define( 'UTF8_HANGUL_LEND', "\xe1\x84\x92" /*codepointToUtf8( UNICODE_HANGUL_LEND )*/ );
+define( 'UTF8_HANGUL_VEND', "\xe1\x85\xb5" /*codepointToUtf8( UNICODE_HANGUL_VEND )*/ );
+define( 'UTF8_HANGUL_TEND', "\xe1\x87\x82" /*codepointToUtf8( UNICODE_HANGUL_TEND )*/ );
+
+define( 'UTF8_SURROGATE_FIRST', "\xed\xa0\x80" /*codepointToUtf8( UNICODE_SURROGATE_FIRST )*/ );
+define( 'UTF8_SURROGATE_LAST', "\xed\xbf\xbf" /*codepointToUtf8( UNICODE_SURROGATE_LAST )*/ );
+define( 'UTF8_MAX', "\xf4\x8f\xbf\xbf" /*codepointToUtf8( UNICODE_MAX )*/ );
+define( 'UTF8_REPLACEMENT', "\xef\xbf\xbd" /*codepointToUtf8( UNICODE_REPLACEMENT )*/ );
+#define( 'UTF8_REPLACEMENT', '!' );
+
+define( 'UTF8_OVERLONG_A', "\xc1\xbf" );
+define( 'UTF8_OVERLONG_B', "\xe0\x9f\xbf" );
+define( 'UTF8_OVERLONG_C', "\xf0\x8f\xbf\xbf" );
+
+# These two ranges are illegal
+define( 'UTF8_FDD0', "\xef\xb7\x90" /*codepointToUtf8( 0xfdd0 )*/ );
+define( 'UTF8_FDEF', "\xef\xb7\xaf" /*codepointToUtf8( 0xfdef )*/ );
+define( 'UTF8_FFFE', "\xef\xbf\xbe" /*codepointToUtf8( 0xfffe )*/ );
+define( 'UTF8_FFFF', "\xef\xbf\xbf" /*codepointToUtf8( 0xffff )*/ );
+
+define( 'UTF8_HEAD', false );
+define( 'UTF8_TAIL', true );
+
+
+
-?>
diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php
index af65ce3a..99bb4798 100644
--- a/includes/DifferenceEngine.php
+++ b/includes/DifferenceEngine.php
@@ -6,6 +6,14 @@
*/
/**
+ * Constant to indicate diff cache compatibility.
+ * Bump this when changing the diff formatting in a way that
+ * fixes important bugs or such to force cached diff views to
+ * clear.
+ */
+define( 'MW_DIFF_VERSION', '1.11a' );
+
+/**
* @todo document
* @public
* @addtogroup DifferenceEngine
@@ -148,8 +156,42 @@ CONTROL;
} else {
$rollback = '';
}
- if( $wgUseRCPatrol && $this->mRcidMarkPatrolled != 0 && $wgUser->isAllowed( 'patrol' ) ) {
- $patrol = ' [' . $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'markaspatrolleddiff' ), "action=markpatrolled&rcid={$this->mRcidMarkPatrolled}" ) . ']';
+
+ // Prepare a change patrol link, if applicable
+ if( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) ) {
+ // If we've been given an explicit change identifier, use it; saves time
+ if( $this->mRcidMarkPatrolled ) {
+ $rcid = $this->mRcidMarkPatrolled;
+ } else {
+ // Look for an unpatrolled change corresponding to this diff
+ $change = RecentChange::newFromConds(
+ array(
+ // Add redundant timestamp condition so we can use the
+ // existing index
+ 'rc_timestamp' => $this->mNewRev->getTimestamp(),
+ 'rc_this_oldid' => $this->mNewid,
+ 'rc_last_oldid' => $this->mOldid,
+ 'rc_patrolled' => 0,
+ ),
+ __METHOD__
+ );
+ if( $change instanceof RecentChange ) {
+ $rcid = $change->mAttribs['rc_id'];
+ } else {
+ // None found
+ $rcid = 0;
+ }
+ }
+ // Build the link
+ if( $rcid ) {
+ $patrol = ' [' . $sk->makeKnownLinkObj(
+ $this->mTitle,
+ wfMsgHtml( 'markaspatrolleddiff' ),
+ "action=markpatrolled&rcid={$rcid}"
+ ) . ']';
+ } else {
+ $patrol = '';
+ }
} else {
$patrol = '';
}
@@ -176,14 +218,14 @@ CONTROL;
wfMsg( 'minoreditletter') ) . ' ';
}
- $oldHeader = "<strong>{$this->mOldtitle}</strong><br />" .
- $sk->revUserTools( $this->mOldRev ) . "<br />" .
- $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly ) . "<br />" .
- $prevlink;
- $newHeader = "<strong>{$this->mNewtitle}</strong><br />" .
- $sk->revUserTools( $this->mNewRev ) . " $rollback<br />" .
- $newminor . $sk->revComment( $this->mNewRev, !$diffOnly ) . "<br />" .
- $nextlink . $patrol;
+ $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $this->mOldtitle . '</strong></div>' .
+ '<div id="mw-diff-otitle2">' . $sk->revUserTools( $this->mOldRev ) . "</div>" .
+ '<div id="mw-diff-otitle3">' . $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly ) . "</div>" .
+ '<div id="mw-diff-otitle4">' . $prevlink . '</div>';
+ $newHeader = '<div id="mw-diff-ntitle1"><strong>' .$this->mNewtitle . '</strong></div>' .
+ '<div id="mw-diff-ntitle2">' . $sk->revUserTools( $this->mNewRev ) . " $rollback</div>" .
+ '<div id="mw-diff-ntitle3">' . $newminor . $sk->revComment( $this->mNewRev, !$diffOnly ) . "</div>" .
+ '<div id="mw-diff-ntitle4">' . $nextlink . $patrol . '</div>';
$this->showDiff( $oldHeader, $newHeader );
@@ -282,24 +324,39 @@ CONTROL;
* Returns false if the diff could not be generated, otherwise returns true
*/
function showDiff( $otitle, $ntitle ) {
- global $wgOut;
- $diff = $this->getDiff( $otitle, $ntitle );
+ global $wgOut, $wgRequest;
+ $diff = $this->getDiff( $otitle, $ntitle, $wgRequest->getVal( 'action' ) == 'purge' );
if ( $diff === false ) {
$wgOut->addWikitext( wfMsg( 'missingarticle', "<nowiki>(fixme, bug)</nowiki>" ) );
return false;
} else {
+ $this->showDiffStyle();
$wgOut->addHTML( $diff );
return true;
}
}
+
+ /**
+ * Add style sheets and supporting JS for diff display.
+ */
+ function showDiffStyle() {
+ global $wgStylePath, $wgStyleVersion, $wgOut;
+ $wgOut->addStyle( 'common/diff.css' );
+
+ // JS is needed to detect old versions of Mozilla to work around an annoyance bug.
+ $wgOut->addScript( "<script type=\"text/javascript\" src=\"$wgStylePath/common/diff.js?$wgStyleVersion\"></script>" );
+ }
/**
- * Get diff table, including header
- * Note that the interface has changed, it's no longer static.
- * Returns false on error
+ * Get complete diff table, including header
+ *
+ * @param Title $otitle Old title
+ * @param Title $ntitle New title
+ * @param bool $skipCache Skip the diff cache for this request?
+ * @return mixed
*/
- function getDiff( $otitle, $ntitle ) {
- $body = $this->getDiffBody();
+ function getDiff( $otitle, $ntitle, $skipCache = false ) {
+ $body = $this->getDiffBody( $skipCache );
if ( $body === false ) {
return false;
} else {
@@ -310,19 +367,20 @@ CONTROL;
/**
* Get the diff table body, without header
- * Results are cached
- * Returns false on error
+ *
+ * @param bool $skipCache Skip cache for this request?
+ * @return mixed
*/
- function getDiffBody() {
+ function getDiffBody( $skipCache = false ) {
global $wgMemc;
$fname = 'DifferenceEngine::getDiffBody';
wfProfileIn( $fname );
// Cacheable?
$key = false;
- if ( $this->mOldid && $this->mNewid ) {
+ if ( $this->mOldid && $this->mNewid && !$skipCache ) {
// Try cache
- $key = wfMemcKey( 'diff', 'oldid', $this->mOldid, 'newid', $this->mNewid );
+ $key = wfMemcKey( 'diff', 'version', MW_DIFF_VERSION, 'oldid', $this->mOldid, 'newid', $this->mNewid );
$difftext = $wgMemc->get( $key );
if ( $difftext ) {
wfIncrStats( 'diff_cache_hit' );
@@ -488,10 +546,14 @@ CONTROL;
$ntitle = '<span class="history-deleted">'.$ntitle.'</span>';
}
$header = "
- <table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>
+ <table class='diff'>
+ <col class='diff-marker' />
+ <col class='diff-content' />
+ <col class='diff-marker' />
+ <col class='diff-content' />
<tr>
- <td colspan='2' width='50%' align='center' class='diff-otitle'>{$otitle}</td>
- <td colspan='2' width='50%' align='center' class='diff-ntitle'>{$ntitle}</td>
+ <td colspan='2' class='diff-otitle'>{$otitle}</td>
+ <td colspan='2' class='diff-ntitle'>{$ntitle}</td>
</tr>
";
@@ -530,16 +592,15 @@ CONTROL;
}
// Load the new revision object
- if( $this->mNewid ) {
- $this->mNewRev = Revision::newFromId( $this->mNewid );
- } else {
- $this->mNewRev = Revision::newFromTitle( $this->mTitle );
- }
-
- if( is_null( $this->mNewRev ) ) {
+ $this->mNewRev = $this->mNewid
+ ? Revision::newFromId( $this->mNewid )
+ : Revision::newFromTitle( $this->mTitle );
+ if( !$this->mNewRev instanceof Revision )
return false;
- }
-
+
+ // Update the new revision ID in case it was 0 (makes life easier doing UI stuff)
+ $this->mNewid = $this->mNewRev->getId();
+
// Set assorted variables
$timestamp = $wgLang->timeanddate( $this->mNewRev->getTimestamp(), true );
$this->mNewPage = $this->mNewRev->getTitle();
@@ -588,7 +649,8 @@ CONTROL;
$oldEdit = $this->mOldPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mOldid );
$this->mOldtitle = "<a href='$oldLink'>" . htmlspecialchars( wfMsg( 'revisionasof', $t ) )
. "</a> (<a href='$oldEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
- //now that we considered old rev, we can make undo link (bug 8133, multi-edit undo)
+
+ // Add an "undo" link
$newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undoafter=' . $this->mOldid . '&undo=' . $this->mNewid);
$this->mNewtitle .= " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
}
@@ -1721,8 +1783,8 @@ class TableDiffFormatter extends DiffFormatter
}
function _block_header( $xbeg, $xlen, $ybeg, $ylen ) {
- $r = '<tr><td colspan="2" align="left"><strong><!--LINE '.$xbeg."--></strong></td>\n" .
- '<td colspan="2" align="left"><strong><!--LINE '.$ybeg."--></strong></td></tr>\n";
+ $r = '<tr><td colspan="2" class="diff-lineno"><!--LINE '.$xbeg."--></td>\n" .
+ '<td colspan="2" class="diff-lineno"><!--LINE '.$ybeg."--></td></tr>\n";
return $r;
}
@@ -1738,17 +1800,25 @@ class TableDiffFormatter extends DiffFormatter
# HTML-escape parameter before calling this
function addedLine( $line ) {
- return "<td>+</td><td class='diff-addedline'>{$line}</td>";
+ return $this->wrapLine( '+', 'diff-addedline', $line );
}
# HTML-escape parameter before calling this
function deletedLine( $line ) {
- return "<td>-</td><td class='diff-deletedline'>{$line}</td>";
+ return $this->wrapLine( '-', 'diff-deletedline', $line );
}
# HTML-escape parameter before calling this
function contextLine( $line ) {
- return "<td> </td><td class='diff-context'>{$line}</td>";
+ return $this->wrapLine( ' ', 'diff-context', $line );
+ }
+
+ private function wrapLine( $marker, $class, $line ) {
+ if( $line !== '' ) {
+ // The <div> wrapper is needed for 'overflow: auto' style to scroll properly
+ $line = "<div>$line</div>";
+ }
+ return "<td class='diff-marker'>$marker</td><td class='$class'>$line</td>";
}
function emptyLine() {
@@ -1801,4 +1871,5 @@ class TableDiffFormatter extends DiffFormatter
}
}
-?>
+
+
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index 1e423565..b48aaffd 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -104,7 +104,9 @@ class DjVuImage {
}
function getInfo() {
+ wfSuppressWarnings();
$file = fopen( $this->mFilename, 'rb' );
+ wfRestoreWarnings();
if( $file === false ) {
wfDebug( __METHOD__ . ": missing or failed file read\n" );
return false;
diff --git a/includes/EditPage.php b/includes/EditPage.php
index bec6e300..cceb053d 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -20,6 +20,7 @@ class EditPage {
var $firsttime;
var $lastDelete;
var $mTokenOk = false;
+ var $mTokenOkExceptSuffix = false;
var $mTriedSave = false;
var $tooBig = false;
var $kblength = false;
@@ -97,6 +98,11 @@ class EditPage {
$text = $this->mArticle->getContent();
+ 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.
@@ -292,7 +298,6 @@ class EditPage {
*/
function edit() {
global $wgOut, $wgUser, $wgRequest, $wgTitle;
- global $wgEmailConfirmToEdit;
if ( ! wfRunHooks( 'AlternateEdit', array( &$this ) ) )
return;
@@ -313,57 +318,38 @@ class EditPage {
return;
}
- if ( ! $this->mTitle->userCan( 'edit' ) ) {
- wfDebug( "$fname: user can't edit\n" );
- $wgOut->readOnlyPage( $this->getContent(), true );
- wfProfileOut( $fname );
- return;
- }
- wfDebug( "$fname: Checking blocks\n" );
- if ( !$this->preview && !$this->diff && $wgUser->isBlockedFrom( $this->mTitle, !$this->save ) ) {
- # When previewing, don't check blocked state - will get caught at save time.
- # Also, check when starting edition is done against slave to improve performance.
- wfDebug( "$fname: user is blocked\n" );
- $this->blockedPage();
- wfProfileOut( $fname );
- return;
- }
- if ( !$wgUser->isAllowed('edit') ) {
- if ( $wgUser->isAnon() ) {
- wfDebug( "$fname: user must log in\n" );
- $this->userNotLoggedInPage();
- wfProfileOut( $fname );
- return;
- } else {
- wfDebug( "$fname: read-only page\n" );
- $wgOut->readOnlyPage( $this->getContent(), true );
- wfProfileOut( $fname );
- return;
+ $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser);
+ if( !$this->mTitle->exists() )
+ $permErrors += $this->mTitle->getUserPermissionsErrors( 'create', $wgUser);
+
+ # 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;
+ }
}
}
- if ($wgEmailConfirmToEdit && !$wgUser->isEmailConfirmed()) {
- wfDebug("$fname: user must confirm e-mail address\n");
- $this->userNotConfirmedPage();
- wfProfileOut($fname);
- return;
- }
- if ( !$this->mTitle->userCan( 'create' ) && !$this->mTitle->exists() ) {
- wfDebug( "$fname: no create permission\n" );
- $this->noCreatePermission();
+ # array_diff returns elements in $permErrors that are not in $remove.
+ $permErrors = array_diff( $permErrors, $remove );
+
+ if ( !empty($permErrors) )
+ {
+ wfDebug( "$fname: User can't edit\n" );
+ $wgOut->readOnlyPage( $this->getContent(), true, $permErrors );
wfProfileOut( $fname );
return;
- }
- if ( wfReadOnly() ) {
- wfDebug( "$fname: read-only mode is engaged\n" );
- if( $this->save || $this->preview ) {
- $this->formtype = 'preview';
- } else if ( $this->diff ) {
- $this->formtype = 'diff';
- } else {
- $wgOut->readOnlyPage( $this->getContent() );
- wfProfileOut( $fname );
- return;
- }
} else {
if ( $this->save ) {
$this->formtype = 'save';
@@ -410,9 +396,10 @@ class EditPage {
}
}
- if(!$this->mTitle->getArticleID() && ('initial' == $this->formtype || $this->firsttime )) { # new article
+ # Show applicable editing introductions
+ if( $this->formtype == 'initial' || $this->firsttime )
$this->showIntro();
- }
+
if( $this->mTitle->isTalkPage() ) {
$wgOut->addWikiText( wfMsg( 'talkpagetext' ) );
}
@@ -449,17 +436,30 @@ class EditPage {
}
/**
- * Return true if this page should be previewed when the edit form
- * is initially opened.
+ * Should we show a preview when the edit form is first shown?
+ *
* @return bool
- * @private
*/
- function previewOnOpen() {
- global $wgUser;
- return $this->section != 'new' &&
- ( ( $wgUser->getOption( 'previewonfirst' ) && $this->mTitle->exists() ) ||
- ( $this->mTitle->getNamespace() == NS_CATEGORY &&
- !$this->mTitle->exists() ) );
+ private function previewOnOpen() {
+ global $wgRequest, $wgUser;
+ if( $wgRequest->getVal( 'preview' ) == 'yes' ) {
+ // Explicit override from request
+ return true;
+ } elseif( $wgRequest->getVal( 'preview' ) == 'no' ) {
+ // Explicit override from request
+ return false;
+ } elseif( $this->section == 'new' ) {
+ // Nothing *to* preview for new sections
+ return false;
+ } elseif( ( $wgRequest->getVal( 'preload' ) !== '' || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
+ // Standard preference behaviour
+ return true;
+ } elseif( !$this->mTitle->exists() && $this->mTitle->getNamespace() == NS_CATEGORY ) {
+ // Categories are special
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -547,9 +547,10 @@ class EditPage {
$this->summary = '';
$this->edittime = '';
$this->starttime = wfTimestampNow();
+ $this->edit = false;
$this->preview = false;
$this->save = false;
- $this->diff = false;
+ $this->diff = false;
$this->minoredit = false;
$this->watchthis = false;
$this->recreate = false;
@@ -575,35 +576,45 @@ class EditPage {
*/
function tokenOk( &$request ) {
global $wgUser;
- if( $wgUser->isAnon() ) {
- # Anonymous users may not have a session
- # open. Check for suffix anyway.
- $this->mTokenOk = ( EDIT_TOKEN_SUFFIX == $request->getVal( 'wpEditToken' ) );
- } else {
- $this->mTokenOk = $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
- }
+ $token = $request->getVal( 'wpEditToken' );
+ $this->mTokenOk = $wgUser->matchEditToken( $token );
+ $this->mTokenOkExceptSuffix = $wgUser->matchEditTokenNoSuffix( $token );
return $this->mTokenOk;
}
- /** */
- function showIntro() {
+ /**
+ * Show all applicable editing introductions
+ */
+ private function showIntro() {
global $wgOut, $wgUser;
- $addstandardintro=true;
- if($this->editintro) {
- $introtitle=Title::newFromText($this->editintro);
- if(isset($introtitle) && $introtitle->userCanRead()) {
- $rev=Revision::newFromTitle($introtitle);
- if($rev) {
- $wgOut->addSecondaryWikiText($rev->getText());
- $addstandardintro=false;
- }
- }
- }
- if($addstandardintro) {
- if ( $wgUser->isLoggedIn() )
+ if( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
+ if( $wgUser->isLoggedIn() ) {
$wgOut->addWikiText( wfMsg( 'newarticletext' ) );
- else
+ } else {
$wgOut->addWikiText( wfMsg( 'newarticletextanon' ) );
+ }
+ $this->showDeletionLog( $wgOut );
+ }
+ }
+
+ /**
+ * Attempt to show a custom editing introduction, if supplied
+ *
+ * @return bool
+ */
+ private function showCustomIntro() {
+ if( $this->editintro ) {
+ $title = Title::newFromText( $this->editintro );
+ if( $title instanceof Title && $title->exists() && $title->userCanRead() ) {
+ global $wgOut;
+ $revision = Revision::newFromTitle( $title );
+ $wgOut->addSecondaryWikiText( $revision->getText() );
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
}
}
@@ -762,7 +773,7 @@ class EditPage {
if ( $this->isConflict) {
wfDebug( "EditPage::editForm conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
- $this->mArticle->getTimestamp() . "'\n" );
+ $this->mArticle->getTimestamp() . "')\n" );
$text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime);
}
else {
@@ -777,7 +788,7 @@ class EditPage {
# Suppress edit conflict with self, except for section edits where merging is required.
if ( ( $this->section == '' ) && ( 0 != $userid ) && ( $this->mArticle->getUser() == $userid ) ) {
- wfDebug( "Suppressing edit conflict, same user.\n" );
+ wfDebug( "EditPage::editForm Suppressing edit conflict, same user.\n" );
$this->isConflict = false;
} else {
# switch from section editing to normal editing in edit conflict
@@ -786,11 +797,11 @@ class EditPage {
if( $this->mergeChangesInto( $text ) ){
// Successful merge! Maybe we should tell the user the good news?
$this->isConflict = false;
- wfDebug( "Suppressing edit conflict, successful merge.\n" );
+ wfDebug( "EditPage::editForm Suppressing edit conflict, successful merge.\n" );
} else {
$this->section = '';
$this->textbox1 = $text;
- wfDebug( "Keeping edit conflict, failed merge.\n" );
+ wfDebug( "EditPage::editForm Keeping edit conflict, failed merge.\n" );
}
}
}
@@ -831,6 +842,10 @@ class EditPage {
}
if( $this->summary != '' ) {
$sectionanchor = $this->sectionAnchor( $this->summary );
+ # This is a new section, so create a link to the new section
+ # in the revision summary.
+ $this->summary = wfMsgForContent('newsectionsummary') .
+ " [[{$this->mTitle->getPrefixedText()}#{$this->summary}|{$this->summary}]]";
}
} elseif( $this->section != '' ) {
# Try to get a section anchor from the section source, redirect to edited section if header found
@@ -909,6 +924,10 @@ class EditPage {
# Enabled article-related sidebar, toplinks, etc.
$wgOut->setArticleRelated( true );
+ if ( $this->formtype == 'preview' ) {
+ $wgOut->setPageTitleActionText( wfMsg( 'preview' ) );
+ }
+
if ( $this->isConflict ) {
$s = wfMsg( 'editconflict', $this->mTitle->getPrefixedText() );
$wgOut->setPageTitle( $s );
@@ -1002,13 +1021,14 @@ class EditPage {
}
if ( $this->mTitle->isCascadeProtected() ) {
# Is this page under cascading protection from some source pages?
- list($cascadeSources, $restrictions) = $this->mTitle->getCascadeProtectionSources();
+ list($cascadeSources, /* $restrictions */) = $this->mTitle->getCascadeProtectionSources();
if ( count($cascadeSources) > 0 ) {
# Explain, and list the titles responsible
$notice = wfMsgExt( 'cascadeprotectedwarning', array('parsemag'), count($cascadeSources) ) . "\n";
- foreach( $cascadeSources as $id => $page )
+ foreach( $cascadeSources as $page ) {
$notice .= '* [[:' . $page->getPrefixedText() . "]]\n";
}
+ }
$wgOut->addWikiText( $notice );
}
@@ -1089,7 +1109,7 @@ class EditPage {
}
if ( 'diff' == $this->formtype ) {
- $wgOut->addHTML( $this->getDiff() );
+ $this->showDiff();
}
}
@@ -1115,7 +1135,7 @@ class EditPage {
if( !$this->preview && !$this->diff ) {
$wgOut->setOnloadHandler( 'document.editform.wpTextbox1.focus()' );
}
- $templates = ($this->preview || $this->section) ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates();
+ $templates = ($this->preview || $this->section != '') ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates();
$formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
global $wgUseMetadataEdit ;
@@ -1229,10 +1249,7 @@ END
* include the constant suffix to prevent editing from
* broken text-mangling proxies.
*/
- if ( $wgUser->isLoggedIn() )
- $token = htmlspecialchars( $wgUser->editToken() );
- else
- $token = EDIT_TOKEN_SUFFIX;
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "\n<input type='hidden' value=\"$token\" name=\"wpEditToken\" />\n" );
@@ -1271,7 +1288,7 @@ END
}
if ( $this->formtype == 'diff') {
- $wgOut->addHTML( $this->getDiff() );
+ $this->showDiff();
}
}
@@ -1292,6 +1309,7 @@ END
if($this->mTitle->getNamespace() == NS_CATEGORY) {
$this->mArticle->openShowCategory();
}
+ wfRunHooks( 'OutputPageBeforeHTML',array( &$wgOut, &$text ) );
$wgOut->addHTML( $text );
if($this->mTitle->getNamespace() == NS_CATEGORY) {
$this->mArticle->closeShowCategory();
@@ -1363,7 +1381,11 @@ END
wfProfileIn( $fname );
if ( $this->mTriedSave && !$this->mTokenOk ) {
- $msg = 'session_fail_preview';
+ if ( $this->mTokenOkExceptSuffix ) {
+ $msg = 'token_suffix_mismatch';
+ } else {
+ $msg = 'session_fail_preview';
+ }
} else {
$msg = 'previewnote';
}
@@ -1414,6 +1436,9 @@ END
$previewHTML = $parserOutput->getText();
$wgOut->addParserOutputNoText( $parserOutput );
+
+ # ParserOutput might have altered the page title, so reset it
+ $wgOut->setPageTitle( wfMsg( 'editing', $this->mTitle->getPrefixedText() ) );
foreach ( $parserOutput->getTemplates() as $ns => $template)
foreach ( array_keys( $template ) as $dbk)
@@ -1497,7 +1522,7 @@ END
$wgOut->setArticleRelated( false );
$wgOut->addWikiText( wfMsg( 'nosuchsectiontext', $this->section ) );
- $wgOut->returnToMain( false );
+ $wgOut->returnToMain( false, $this->mTitle->getPrefixedUrl() );
}
/**
@@ -1893,10 +1918,8 @@ END
*
* If this is a section edit, we'll replace the section as for final
* save and then make a comparison.
- *
- * @return string HTML
*/
- function getDiff() {
+ function showDiff() {
$oldtext = $this->mArticle->fetchContent();
$newtext = $this->mArticle->replaceSection(
$this->section, $this->textbox1, $this->summary, $this->edittime );
@@ -1907,11 +1930,13 @@ END
$de = new DifferenceEngine( $this->mTitle );
$de->setText( $oldtext, $newtext );
$difftext = $de->getDiff( $oldtitle, $newtitle );
+ $de->showDiffStyle();
} else {
$difftext = '';
}
- return '<div id="wikiDiff">' . $difftext . '</div>';
+ global $wgOut;
+ $wgOut->addHtml( '<div id="wikiDiff">' . $difftext . '</div>' );
}
/**
@@ -2034,7 +2059,32 @@ END
$wgOut->setPageTitle( wfMsg( 'nocreatetitle' ) );
$wgOut->addWikiText( wfMsg( 'nocreatetext' ) );
}
-
+
+ /**
+ * If there are rows in the deletion log for this page, show them,
+ * along with a nice little note for the user
+ *
+ * @param OutputPage $out
+ */
+ private function showDeletionLog( $out ) {
+ $title = $this->mArticle->getTitle();
+ $reader = new LogReader(
+ new FauxRequest(
+ array(
+ 'page' => $title->getPrefixedText(),
+ 'type' => 'delete',
+ )
+ )
+ );
+ if( $reader->hasRows() ) {
+ $out->addHtml( '<div id="mw-recreate-deleted-warn">' );
+ $out->addWikiText( wfMsg( 'recreate-deleted-warn' ) );
+ $viewer = new LogViewer( $reader );
+ $viewer->showList( $out );
+ $out->addHtml( '</div>' );
+ }
+ }
+
}
-?>
+
diff --git a/includes/EmaillingJob.php b/includes/EmaillingJob.php
new file mode 100644
index 00000000..73d71c56
--- /dev/null
+++ b/includes/EmaillingJob.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Old job used for sending single notification emails;
+ * kept for backwards-compatibility
+ */
+class EmaillingJob extends Job {
+
+ function __construct( $title, $params, $id = 0 ) {
+ parent::__construct( 'sendMail', Title::newMainPage(), $params, $id );
+ }
+
+ function run() {
+ userMailer(
+ $this->params['to'],
+ $this->params['from'],
+ $this->params['subj'],
+ $this->params['body'],
+ $this->params['replyto']
+ );
+ return true;
+ }
+
+}
+
diff --git a/includes/EnotifNotifyJob.php b/includes/EnotifNotifyJob.php
new file mode 100644
index 00000000..70d1de69
--- /dev/null
+++ b/includes/EnotifNotifyJob.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Job for email notification mails
+ */
+class EnotifNotifyJob extends Job {
+
+ function __construct( $title, $params, $id = 0 ) {
+ parent::__construct( 'enotifNotify', $title, $params, $id );
+ }
+
+ function run() {
+ $enotif = new EmailNotification();
+ $enotif->actuallyNotifyOnPageChange(
+ User::newFromName( $this->params['editor'], false ),
+ $this->title,
+ $this->params['timestamp'],
+ $this->params['summary'],
+ $this->params['minorEdit'],
+ $this->params['oldid']
+ );
+ return true;
+ }
+
+}
+
diff --git a/includes/Exception.php b/includes/Exception.php
index 4cf0b7ba..02819cc9 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -235,4 +235,4 @@ function wfExceptionHandler( $e ) {
exit( 1 );
}
-?>
+
diff --git a/includes/Exif.php b/includes/Exif.php
index 3a06ca1b..d98a8e0d 100644
--- a/includes/Exif.php
+++ b/includes/Exif.php
@@ -405,7 +405,7 @@ class Exif {
*
* @return int
*/
- function version() {
+ public static function version() {
return 1; // We don't need no bloddy constants!
}
@@ -1131,4 +1131,4 @@ define( 'MW_EXIF_UNDEFINED', Exif::UNDEFINED );
define( 'MW_EXIF_SLONG', Exif::SLONG );
define( 'MW_EXIF_SRATIONAL', Exif::SRATIONAL );
-?>
+
diff --git a/includes/Export.php b/includes/Export.php
index 9307795d..c3ef9451 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -558,7 +558,7 @@ class Dump7ZipOutput extends DumpPipeOutput {
$command = "7za a -bd -si " . wfEscapeShellArg( $file );
// Suppress annoying useless crap from p7zip
// Unfortunately this could suppress real error messages too
- $command .= " >/dev/null 2>&1";
+ $command .= ' >' . wfGetNull() . ' 2>&1';
parent::DumpPipeOutput( $command );
}
}
@@ -767,4 +767,4 @@ function xmlsafe( $string ) {
return $string;
}
-?>
+
diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php
index c8ed8bde..f3fc22e3 100644
--- a/includes/ExternalEdit.php
+++ b/includes/ExternalEdit.php
@@ -46,7 +46,7 @@ class ExternalEdit {
$extension="wiki";
} elseif($this->mMode=="file") {
$type="Edit file";
- $image = new Image( $this->mTitle );
+ $image = wfLocalFile( $this->mTitle );
$img_url = $image->getURL();
if(strpos($img_url,"://")) {
$url = $img_url;
@@ -72,4 +72,4 @@ CONTROL;
echo $control;
}
}
-?>
+
diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php
index fb66b652..5efc6e25 100644
--- a/includes/ExternalStore.php
+++ b/includes/ExternalStore.php
@@ -41,10 +41,9 @@ class ExternalStore {
return false;
$class='ExternalStore'.ucfirst($proto);
- /* Preloaded modules might exist, especially ones serving multiple protocols */
+ /* Any custom modules should be added to $wgAutoLoadClasses for on-demand loading */
if (!class_exists($class)) {
- if (!include_once($class.'.php'))
- return false;
+ return false;
}
$store=new $class();
return $store;
@@ -66,4 +65,4 @@ class ExternalStore {
}
}
}
-?>
+
diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php
index 7b4ffc2f..f9046f74 100644
--- a/includes/ExternalStoreDB.php
+++ b/includes/ExternalStoreDB.php
@@ -144,4 +144,4 @@ class ExternalStoreDB {
return "DB://$cluster/$id";
}
}
-?>
+
diff --git a/includes/ExternalStoreHttp.php b/includes/ExternalStoreHttp.php
index e6656986..cff6c4d4 100644
--- a/includes/ExternalStoreHttp.php
+++ b/includes/ExternalStoreHttp.php
@@ -19,4 +19,4 @@ class ExternalStoreHttp {
* whatever, for initial ext storage
*/
}
-?>
+
diff --git a/includes/FakeTitle.php b/includes/FakeTitle.php
index 293bdaf0..b63ae505 100644
--- a/includes/FakeTitle.php
+++ b/includes/FakeTitle.php
@@ -84,4 +84,4 @@ class FakeTitle {
function trackbackRDF() { $this->error(); }
}
-?>
+
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
new file mode 100644
index 00000000..ee165cd1
--- /dev/null
+++ b/includes/FileDeleteForm.php
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * File deletion user interface
+ *
+ * @addtogroup Media
+ * @author Rob Church <robchur@gmail.com>
+ */
+class FileDeleteForm {
+
+ private $title = null;
+ private $file = null;
+
+ private $oldfile = null;
+ private $oldimage = '';
+
+ /**
+ * Constructor
+ *
+ * @param File $file File we're deleting
+ */
+ public function __construct( $file ) {
+ $this->title = $file->getTitle();
+ $this->file = $file;
+ }
+
+ /**
+ * Fulfil the request; shows the form or deletes the file,
+ * pending authentication, confirmation, etc.
+ */
+ public function execute() {
+ global $wgOut, $wgRequest, $wgUser;
+ $this->setHeaders();
+
+ if( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ } elseif( !$wgUser->isLoggedIn() ) {
+ $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
+ return;
+ } elseif( !$wgUser->isAllowed( 'delete' ) ) {
+ $wgOut->permissionError( 'delete' );
+ return;
+ } elseif( $wgUser->isBlocked() ) {
+ $wgOut->blockedPage();
+ return;
+ }
+
+ $this->oldimage = $wgRequest->getText( 'oldimage', false );
+ $token = $wgRequest->getText( 'wpEditToken' );
+ if( $this->oldimage && !$this->isValidOldSpec() ) {
+ $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->oldimage ) );
+ return;
+ }
+ if( $this->oldimage )
+ $this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
+
+ if( !$this->haveDeletableFile() ) {
+ $wgOut->addHtml( $this->prepareMessage( 'filedelete-nofile' ) );
+ $wgOut->addReturnTo( $this->title );
+ return;
+ }
+
+ // Perform the deletion if appropriate
+ if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
+ $comment = $wgRequest->getText( 'wpReason' );
+ if( $this->oldimage ) {
+ $status = $this->file->deleteOld( $this->oldimage, $comment );
+ if( $status->ok ) {
+ // Need to do a log item
+ $log = new LogPage( 'delete' );
+ $log->addEntry( 'delete', $this->title, wfMsg( 'deletedrevision' , $this->oldimage ) );
+ }
+ } else {
+ $status = $this->file->delete( $comment );
+ if( $status->ok ) {
+ // Need to delete the associated article
+ $article = new Article( $this->title );
+ $article->doDeleteArticle( $comment );
+ }
+ }
+ if( !$status->isGood() )
+ $wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) );
+ if( $status->ok ) {
+ $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() );
+ }
+ return;
+ }
+
+ $this->showForm();
+ $this->showLogEntries();
+ }
+
+ /**
+ * Show the confirmation form
+ */
+ private function showForm() {
+ global $wgOut, $wgUser, $wgRequest;
+
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) );
+ $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->oldimage ) );
+ $form .= '<fieldset><legend>' . wfMsgHtml( 'filedelete-legend' ) . '</legend>';
+ $form .= $this->prepareMessage( 'filedelete-intro' );
+
+ $form .= '<p>' . Xml::inputLabel( wfMsg( 'filedelete-comment' ), 'wpReason', 'wpReason',
+ 60, $wgRequest->getText( 'wpReason' ) ) . '</p>';
+ $form .= '<p>' . Xml::submitButton( wfMsg( 'filedelete-submit' ) ) . '</p>';
+ $form .= '</fieldset>';
+ $form .= '</form>';
+
+ $wgOut->addHtml( $form );
+ }
+
+ /**
+ * Show deletion log fragments pertaining to the current file
+ */
+ private function showLogEntries() {
+ global $wgOut;
+ $wgOut->addHtml( '<h2>' . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+ $reader = new LogViewer(
+ new LogReader(
+ new FauxRequest(
+ array(
+ 'type' => 'delete',
+ 'page' => $this->title->getPrefixedText(),
+ )
+ )
+ )
+ );
+ $reader->showList( $wgOut );
+ }
+
+ /**
+ * Prepare a message referring to the file being deleted,
+ * showing an appropriate message depending upon whether
+ * it's a current file or an old version
+ *
+ * @param string $message Message base
+ * @return string
+ */
+ private function prepareMessage( $message ) {
+ global $wgLang, $wgServer;
+ if( $this->oldimage ) {
+ return wfMsgExt(
+ "{$message}-old",
+ 'parse',
+ $this->title->getText(),
+ $wgLang->date( $this->getTimestamp(), true ),
+ $wgLang->time( $this->getTimestamp(), true ),
+ $wgServer . $this->file->getArchiveUrl( $this->oldimage )
+ );
+ } else {
+ return wfMsgExt(
+ $message,
+ 'parse',
+ $this->title->getText()
+ );
+ }
+ }
+
+ /**
+ * Set headers, titles and other bits
+ */
+ private function setHeaders() {
+ global $wgOut, $wgUser;
+ $wgOut->setPageTitle( wfMsg( 'filedelete', $this->title->getText() ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->setSubtitle( wfMsg( 'filedelete-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->title ) ) );
+ }
+
+ /**
+ * Is the provided `oldimage` value valid?
+ *
+ * @return bool
+ */
+ private function isValidOldSpec() {
+ return strlen( $this->oldimage ) >= 16
+ && strpos( $this->oldimage, '/' ) === false
+ && strpos( $this->oldimage, '\\' ) === false;
+ }
+
+ /**
+ * Could we delete the file specified? If an `oldimage`
+ * value was provided, does it correspond to an
+ * existing, local, old version of this file?
+ *
+ * @return bool
+ */
+ private function haveDeletableFile() {
+ return $this->oldimage
+ ? $this->oldfile && $this->oldfile->exists() && $this->oldfile->isLocal()
+ : $this->file && $this->file->exists() && $this->file->isLocal();
+ }
+
+ /**
+ * Prepare the form action
+ *
+ * @return string
+ */
+ private function getAction() {
+ $q = array();
+ $q[] = 'action=delete';
+ if( $this->oldimage )
+ $q[] = 'oldimage=' . urlencode( $this->oldimage );
+ return $this->title->getLocalUrl( implode( '&', $q ) );
+ }
+
+ /**
+ * Extract the timestamp of the old version
+ *
+ * @return string
+ */
+ private function getTimestamp() {
+ return $this->oldfile->getTimestamp();
+ }
+
+} \ No newline at end of file
diff --git a/includes/FileRevertForm.php b/includes/FileRevertForm.php
new file mode 100644
index 00000000..55f21fff
--- /dev/null
+++ b/includes/FileRevertForm.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * File reversion user interface
+ *
+ * @addtogroup Media
+ * @author Rob Church <robchur@gmail.com>
+ */
+class FileRevertForm {
+
+ private $title = null;
+ private $file = null;
+ private $oldimage = '';
+ private $timestamp = false;
+
+ /**
+ * Constructor
+ *
+ * @param File $file File we're reverting
+ */
+ public function __construct( $file ) {
+ $this->title = $file->getTitle();
+ $this->file = $file;
+ }
+
+ /**
+ * Fulfil the request; shows the form or reverts the file,
+ * pending authentication, confirmation, etc.
+ */
+ public function execute() {
+ global $wgOut, $wgRequest, $wgUser, $wgLang, $wgServer;
+ $this->setHeaders();
+
+ if( wfReadOnly() ) {
+ $wgOut->readOnlyPage();
+ return;
+ } elseif( !$wgUser->isLoggedIn() ) {
+ $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
+ return;
+ } elseif( !$this->title->userCan( 'edit' ) ) {
+ // The standard read-only thing doesn't make a whole lot of sense
+ // here; surely it should show the image or something? -- RC
+ $article = new Article( $this->title );
+ $wgOut->readOnlyPage( $article->getContent(), true );
+ return;
+ } elseif( $wgUser->isBlocked() ) {
+ $wgOut->blockedPage();
+ return;
+ }
+
+ $this->oldimage = $wgRequest->getText( 'oldimage' );
+ $token = $wgRequest->getText( 'wpEditToken' );
+ if( !$this->isValidOldSpec() ) {
+ $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->oldimage ) );
+ return;
+ }
+
+ if( !$this->haveOldVersion() ) {
+ $wgOut->addHtml( wfMsgExt( 'filerevert-badversion', 'parse' ) );
+ $wgOut->returnToMain( false, $this->title );
+ return;
+ }
+
+ // Perform the reversion if appropriate
+ if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
+ $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
+ $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() ) {
+ $wgOut->addHtml( wfMsgExt( 'filerevert-success', 'parse', $this->title->getText(),
+ $wgLang->date( $this->getTimestamp(), true ),
+ $wgLang->time( $this->getTimestamp(), true ),
+ $wgServer . $this->file->getArchiveUrl( $this->oldimage ) ) );
+ $wgOut->returnToMain( false, $this->title );
+ } else {
+ $wgOut->addWikiText( $status->getWikiText() );
+ }
+ return;
+ }
+
+ // Show the form
+ $this->showForm();
+ }
+
+ /**
+ * Show the confirmation form
+ */
+ private function showForm() {
+ global $wgOut, $wgUser, $wgRequest, $wgLang, $wgContLang, $wgServer;
+ $timestamp = $this->getTimestamp();
+
+ $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) );
+ $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->oldimage ) );
+ $form .= '<fieldset><legend>' . wfMsgHtml( 'filerevert-legend' ) . '</legend>';
+ $form .= wfMsgExt( 'filerevert-intro', 'parse', $this->title->getText(),
+ $wgLang->date( $timestamp, true ), $wgLang->time( $timestamp, true ), $wgServer . $this->file->getArchiveUrl( $this->oldimage ) );
+ $form .= '<p>' . Xml::inputLabel( wfMsg( 'filerevert-comment' ), 'wpComment', 'wpComment',
+ 60, wfMsgForContent( 'filerevert-defaultcomment',
+ $wgContLang->date( $timestamp, false, false ), $wgContLang->time( $timestamp, false, false ) ) ) . '</p>';
+ $form .= '<p>' . Xml::submitButton( wfMsg( 'filerevert-submit' ) ) . '</p>';
+ $form .= '</fieldset>';
+ $form .= '</form>';
+
+ $wgOut->addHtml( $form );
+ }
+
+ /**
+ * Set headers, titles and other bits
+ */
+ private function setHeaders() {
+ global $wgOut, $wgUser;
+ $wgOut->setPageTitle( wfMsg( 'filerevert', $this->title->getText() ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->setSubtitle( wfMsg( 'filerevert-backlink', $wgUser->getSkin()->makeKnownLinkObj( $this->title ) ) );
+ }
+
+ /**
+ * Is the provided `oldimage` value valid?
+ *
+ * @return bool
+ */
+ private function isValidOldSpec() {
+ return strlen( $this->oldimage ) >= 16
+ && strpos( $this->oldimage, '/' ) === false
+ && strpos( $this->oldimage, '\\' ) === false;
+ }
+
+ /**
+ * Does the provided `oldimage` value correspond
+ * to an existing, local, old version of this file?
+ *
+ * @return bool
+ */
+ private function haveOldVersion() {
+ $file = wfFindFile( $this->title, $this->oldimage );
+ return $file && $file->exists() && $file->isLocal();
+ }
+
+ /**
+ * Prepare the form action
+ *
+ * @return string
+ */
+ private function getAction() {
+ $q = array();
+ $q[] = 'action=revert';
+ $q[] = 'oldimage=' . urlencode( $this->oldimage );
+ return $this->title->getLocalUrl( implode( '&', $q ) );
+ }
+
+ /**
+ * Extract the timestamp of the old version
+ *
+ * @return string
+ */
+ private function getTimestamp() {
+ if( $this->timestamp === false ) {
+ $file = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
+ $this->timestamp = $file->getTimestamp();
+ }
+ return $this->timestamp;
+ }
+
+} \ No newline at end of file
diff --git a/includes/FileStore.php b/includes/FileStore.php
index dcec71c5..1554d66e 100644
--- a/includes/FileStore.php
+++ b/includes/FileStore.php
@@ -219,7 +219,7 @@ class FileStore {
* Confirm that the given file key is valid.
* Note that a valid key may refer to a file that does not exist.
*
- * Key should consist of a 32-digit base-36 SHA-1 hash and
+ * Key should consist of a 31-digit base-36 SHA-1 hash and
* an optional alphanumeric extension, all lowercase.
* The whole must not exceed 64 characters.
*
@@ -227,7 +227,7 @@ class FileStore {
* @return boolean
*/
static function validKey( $key ) {
- return preg_match( '/^[0-9a-z]{32}(\.[0-9a-z]{1,31})?$/', $key );
+ return preg_match( '/^[0-9a-z]{31,32}(\.[0-9a-z]{1,31})?$/', $key );
}
@@ -249,7 +249,7 @@ class FileStore {
return false;
}
- $base36 = wfBaseConvert( $hash, 16, 36, 32 );
+ $base36 = wfBaseConvert( $hash, 16, 36, 31 );
if( $extension == '' ) {
$key = $base36;
} else {
@@ -376,4 +376,4 @@ class FSTransaction {
*/
class FSException extends MWException { }
-?>
+
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 1ffde741..67cc1f39 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -429,18 +429,11 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform =
* @param $key String:
*/
function wfMsgWeirdKey ( $key ) {
- $subsource = str_replace ( ' ' , '_' , $key ) ;
- $source = wfMsgForContentNoTrans( $subsource ) ;
- if ( wfEmptyMsg( $subsource, $source) ) {
- # Try again with first char lower case
- $subsource = strtolower ( substr ( $subsource , 0 , 1 ) ) . substr ( $subsource , 1 ) ;
- $source = wfMsgForContentNoTrans( $subsource ) ;
- }
- if ( wfEmptyMsg( $subsource, $source ) ) {
- # Didn't work either, return blank text
- $source = "" ;
- }
- return $source ;
+ $source = wfMsgGetKey( $key, false, true, false );
+ if ( wfEmptyMsg( $key, $source ) )
+ return "";
+ else
+ return $source;
}
/**
@@ -454,6 +447,17 @@ function wfMsgWeirdKey ( $key ) {
function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
global $wgParser, $wgContLang, $wgMessageCache, $wgLang;
+ /* <Vyznev> btw, is all that code in wfMsgGetKey() that check
+ * if the message cache exists of not really necessary, or is
+ * it just paranoia?
+ * <TimStarling> Vyznev: it's probably not necessary
+ * <TimStarling> I think I wrote it in an attempt to report DB
+ * connection errors properly
+ * <TimStarling> but eventually we gave up on using the
+ * message cache for that and just hard-coded the strings
+ * <TimStarling> it may have other uses, it's not mere paranoia
+ */
+
if ( is_object( $wgMessageCache ) )
$transstat = $wgMessageCache->getTransform();
@@ -468,16 +472,18 @@ function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
$lang = &$wgLang;
}
- wfSuppressWarnings();
+ # MessageCache::get() does this already, Language::getMessage() doesn't
+ # ISSUE: Should we try to handle "message/lang" here too?
+ $key = str_replace( ' ' , '_' , $wgContLang->lcfirst( $key ) );
+ wfSuppressWarnings();
if( is_object( $lang ) ) {
$message = $lang->getMessage( $key );
} else {
$message = false;
}
wfRestoreWarnings();
- if($message === false)
- $message = Language::getMessage($key);
+
if ( $transform && strstr( $message, '{{' ) !== false ) {
$message = $wgParser->transformMsg($message, $wgMessageCache->getParserOptions() );
}
@@ -586,7 +592,7 @@ function wfMsgExt( $key, $options ) {
} elseif ( in_array('parseinline', $options) ) {
$string = $wgOut->parse( $string, true, true );
$m = array();
- if( preg_match( "~^<p>(.*)\n?</p>$~", $string, $m ) ) {
+ if( preg_match( '/^<p>(.*)\n?<\/p>$/sU', $string, $m ) ) {
$string = $m[1];
}
} elseif ( in_array('parsemag', $options) ) {
@@ -695,14 +701,14 @@ function wfHostname() {
* @return string
*/
function wfReportTime() {
- global $wgRequestTime;
+ global $wgRequestTime, $wgShowHostnames;
$now = wfTime();
$elapsed = $now - $wgRequestTime;
- $com = sprintf( "<!-- Served by %s in %01.3f secs. -->",
- wfHostname(), $elapsed );
- return $com;
+ return $wgShowHostnames
+ ? sprintf( "<!-- Served by %s in %01.3f secs. -->", wfHostname(), $elapsed )
+ : sprintf( "<!-- Served in %01.3f secs. -->", $elapsed );
}
/**
@@ -813,7 +819,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
if ( $po < 0 ) { $po = 0; }
$q = "limit={$limit}&offset={$po}";
if ( '' != $query ) { $q .= '&'.$query; }
- $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$prev}</a>";
+ $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-prevlink\">{$prev}</a>";
} else { $plink = $prev; }
$no = $offset + $limit;
@@ -823,7 +829,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
if ( $atend ) {
$nlink = $next;
} else {
- $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$next}</a>";
+ $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-nextlink\">{$next}</a>";
}
$nums = wfNumLink( $offset, 20, $title, $query ) . ' | ' .
wfNumLink( $offset, 50, $title, $query ) . ' | ' .
@@ -844,7 +850,7 @@ function wfNumLink( $offset, $limit, &$title, $query = '' ) {
$q .= 'limit='.$limit.'&offset='.$offset;
$fmtLimit = $wgLang->formatNum( $limit );
- $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$fmtLimit}</a>";
+ $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\" class=\"mw-numlink\">{$fmtLimit}</a>";
return $s;
}
@@ -1657,47 +1663,11 @@ function wfTempDir() {
* Make directory, and make all parent directories if they don't exist
*/
function wfMkdirParents( $fullDir, $mode = 0777 ) {
- if ( strval( $fullDir ) === '' ) {
+ if( strval( $fullDir ) === '' )
return true;
- }
-
- # 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( file_exists( $fullDir ) )
return true;
- } elseif ( $currentDir === false ) {
- # Went all the way back to root and it apparently doesn't exist
- return false;
- }
-
- # Now go forward creating directories
- $createList = array_reverse( $createList );
- 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( str_replace( '/', DIRECTORY_SEPARATOR, $fullDir ), $mode, true );
}
/**
@@ -1761,7 +1731,7 @@ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
* @return bool
*/
function wfEmptyMsg( $msg, $wfMsgOut ) {
- return $wfMsgOut === "&lt;$msg&gt;";
+ return $wfMsgOut === htmlspecialchars( "<$msg>" );
}
/**
@@ -1902,11 +1872,15 @@ function wfRegexReplacement( $string ) {
* We'll consider it so always, as we don't want \s in our Unix paths either.
*
* @param string $path
+ * @param string $suffix to remove if present
* @return string
*/
-function wfBaseName( $path ) {
+function wfBaseName( $path, $suffix='' ) {
+ $encSuffix = ($suffix == '')
+ ? ''
+ : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' );
$matches = array();
- if( preg_match( '#([^/\\\\]*)[/\\\\]*$#', $path, $matches ) ) {
+ if( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
return $matches[1];
} else {
return '';
@@ -2266,4 +2240,84 @@ function &wfGetDB( $db = DB_LAST, $groups = array() ) {
$ret = $wgLoadBalancer->getConnection( $db, true, $groups );
return $ret;
}
-?>
+
+/**
+ * Find a file.
+ * Shortcut for RepoGroup::singleton()->findFile()
+ * @param mixed $title Title object or string. May be interwiki.
+ * @param mixed $time Requested time for an archived image, or false for the
+ * current version. An image object will be returned which
+ * existed at or before the specified time.
+ * @return File, or false if the file does not exist
+ */
+function wfFindFile( $title, $time = false ) {
+ return RepoGroup::singleton()->findFile( $title, $time );
+}
+
+/**
+ * Get an object referring to a locally registered file.
+ * Returns a valid placeholder object if the file does not exist.
+ */
+function wfLocalFile( $title ) {
+ return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
+}
+
+/**
+ * Should low-performance queries be disabled?
+ *
+ * @return bool
+ */
+function wfQueriesMustScale() {
+ global $wgMiserMode;
+ return $wgMiserMode
+ || ( SiteStats::pages() > 100000
+ && SiteStats::edits() > 1000000
+ && SiteStats::users() > 10000 );
+}
+
+/**
+ * Get the path to a specified script file, respecting file
+ * extensions; this is a wrapper around $wgScriptExtension etc.
+ *
+ * @param string $script Script filename, sans extension
+ * @return string
+ */
+function wfScript( $script = 'index' ) {
+ global $wgScriptPath, $wgScriptExtension;
+ return "{$wgScriptPath}/{$script}{$wgScriptExtension}";
+}
+
+/**
+ * Convenience function converts boolean values into "true"
+ * or "false" (string) values
+ *
+ * @param bool $value
+ * @return string
+ */
+function wfBoolToStr( $value ) {
+ return $value ? 'true' : 'false';
+}
+
+/**
+ * Load an extension messages file
+ */
+function wfLoadExtensionMessages( $extensionName ) {
+ global $wgExtensionMessagesFiles, $wgMessageCache;
+ if ( !empty( $wgExtensionMessagesFiles[$extensionName] ) ) {
+ $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName] );
+ // Prevent double-loading
+ $wgExtensionMessagesFiles[$extensionName] = false;
+ }
+}
+
+/**
+ * Get a platform-independent path to the null file, e.g.
+ * /dev/null
+ *
+ * @return string
+ */
+function wfGetNull() {
+ return wfIsWindows()
+ ? 'NUL'
+ : '/dev/null';
+} \ No newline at end of file
diff --git a/includes/HTMLCacheUpdate.php b/includes/HTMLCacheUpdate.php
index 9a0b6a08..260439b2 100644
--- a/includes/HTMLCacheUpdate.php
+++ b/includes/HTMLCacheUpdate.php
@@ -67,13 +67,13 @@ class HTMLCacheUpdate
break;
}
}
- if ( $id !== false ) {
- // One less on the end to avoid duplicating the boundary
- $job = new HTMLCacheUpdateJob( $this->mTitle, $this->mTable, $start, $id - 1 );
- } else {
- $job = new HTMLCacheUpdateJob( $this->mTitle, $this->mTable, $start, false );
- }
- $jobs[] = $job;
+
+ $params = array(
+ 'table' => $this->mTable,
+ 'start' => $start,
+ 'end' => ( $id !== false ? $id - 1 : false ),
+ );
+ $jobs[] = new HTMLCacheUpdateJob( $this->mTitle, $params );
$start = $id;
} while ( $start );
@@ -193,20 +193,14 @@ class HTMLCacheUpdateJob extends Job {
/**
* Construct a job
* @param Title $title The title linked to
- * @param string $table The name of the link table.
- * @param integer $start Beginning page_id or false for open interval
- * @param integer $end End page_id or false for open interval
+ * @param array $params Job parameters (table, start and end page_ids)
* @param integer $id job_id
*/
- function __construct( $title, $table, $start, $end, $id = 0 ) {
- $params = array(
- 'table' => $table,
- 'start' => $start,
- 'end' => $end );
+ function __construct( $title, $params, $id = 0 ) {
parent::__construct( 'htmlCacheUpdate', $title, $params, $id );
- $this->table = $table;
- $this->start = intval( $start );
- $this->end = intval( $end );
+ $this->table = $params['table'];
+ $this->start = $params['start'];
+ $this->end = $params['end'];
}
function run() {
@@ -229,4 +223,4 @@ class HTMLCacheUpdateJob extends Job {
return true;
}
}
-?>
+
diff --git a/includes/HTMLFileCache.php b/includes/HTMLFileCache.php
index 1d3778b2..a7466814 100644
--- a/includes/HTMLFileCache.php
+++ b/includes/HTMLFileCache.php
@@ -154,4 +154,4 @@ class HTMLFileCache {
}
-?>
+
diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php
index 715c8c88..69ec1007 100644
--- a/includes/HTMLForm.php
+++ b/includes/HTMLForm.php
@@ -1,7 +1,6 @@
<?php
/**
- * This file contain a class to easily build HTML forms as well as custom
- * functions used by SpecialUserrights.php
+ * This file contain a class to easily build HTML forms
*/
/**
@@ -106,53 +105,3 @@ class HTMLForm {
"<textarea name=\"{$varname}\" rows=\"5\" cols=\"{$size}\">{$s}</textarea>\n";
}
} // end class
-
-/** Build a select with all defined groups
- *
- * used by SpecialUserrights.php
- * @todo move it to there, and don't forget to copy it for SpecialMakesysop.php
- *
- * @param $selectname String: name of this element. Name of form is automaticly prefixed.
- * @param $selectmsg String: FIXME
- * @param $selected Array: array of element selected when posted. Only multiples will show them.
- * @param $multiple Boolean: A multiple elements select.
- * @param $size Integer: number of elements to be shown ignored for non-multiple (default 6).
- * @param $reverse Boolean: if true, multiple select will hide selected elements (default false).
- * @todo Document $selectmsg
-*/
-function HTMLSelectGroups($selectname, $selectmsg, $selected=array(), $multiple=false, $size=6, $reverse=false) {
- $groups = User::getAllGroups();
- $out = htmlspecialchars( wfMsg( $selectmsg ) );
- $out .= "<br />";
-
- if( $multiple ) {
- $attribs = array(
- 'name' => $selectname . '[]',
- 'multiple'=> 'multiple',
- 'size' => $size );
- } else {
- $attribs = array( 'name' => $selectname );
- }
- $attribs['style'] = 'width: 100%';
- $out .= wfElement( 'select', $attribs, null );
-
- foreach( $groups as $group ) {
- $attribs = array( 'value' => $group );
- if( $multiple ) {
- // for multiple will only show the things we want
- if( !in_array( $group, $selected ) xor $reverse ) {
- continue;
- }
- } else {
- if( in_array( $group, $selected ) ) {
- $attribs['selected'] = 'selected';
- }
- }
- $out .= wfElement( 'option', $attribs, User::getGroupName( $group ) ) . "\n";
- }
-
- $out .= "</select>\n";
- return $out;
-}
-
-?>
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index 9dfd6d61..984ee2d4 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -310,4 +310,4 @@ class HistoryBlobCurStub {
}
-?>
+
diff --git a/includes/Hooks.php b/includes/Hooks.php
index b428b08d..20103db4 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -119,6 +119,20 @@ function wfRunHooks($event, $args = null) {
global $wgOut;
$wgOut->showFatalError($retval);
return false;
+ } elseif( $retval === null ) {
+ if( is_array( $callback ) ) {
+ if( is_object( $callback[0] ) ) {
+ $prettyClass = get_class( $callback[0] );
+ } else {
+ $prettyClass = strval( $callback[0] );
+ }
+ $prettyFunc = $prettyClass . '::' . strval( $callback[1] );
+ } else {
+ $prettyFunc = strval( $callback );
+ }
+ throw new MWException( "Detected bug in an extension! " .
+ "Hook $prettyFunc failed to return a value; " .
+ "should return true to continue hook processing or false to abort." );
} else if (!$retval) {
return false;
}
@@ -126,4 +140,4 @@ function wfRunHooks($event, $args = null) {
return true;
}
-?>
+
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index a9fb13ca..6ea3abd0 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -4,14 +4,23 @@
* Various HTTP related functions
*/
class Http {
+ static function get( $url, $timeout = 'default' ) {
+ return Http::request( "GET", $url, $timeout );
+ }
+
+ static function post( $url, $timeout = 'default' ) {
+ return Http::request( "POST", $url, $timeout );
+ }
+
/**
* Get the contents of a file by HTTP
*
* if $timeout is 'default', $wgHTTPTimeout is used
*/
- static function get( $url, $timeout = 'default' ) {
+ static function request( $method, $url, $timeout = 'default' ) {
global $wgHTTPTimeout, $wgHTTPProxy, $wgVersion, $wgTitle;
+ wfDebug( __METHOD__ . ": $method $url\n" );
# Use curl if available
if ( function_exists( 'curl_init' ) ) {
$c = curl_init( $url );
@@ -26,6 +35,10 @@ class Http {
}
curl_setopt( $c, CURLOPT_TIMEOUT, $timeout );
curl_setopt( $c, CURLOPT_USERAGENT, "MediaWiki/$wgVersion" );
+ if ( $method == 'POST' )
+ curl_setopt( $c, CURLOPT_POST, true );
+ else
+ curl_setopt( $c, CURLOPT_CUSTOMREQUEST, $method );
# Set the referer to $wgTitle, even in command-line mode
# This is useful for interwiki transclusion, where the foreign
@@ -45,12 +58,29 @@ class Http {
if ( curl_getinfo( $c, CURLINFO_HTTP_CODE ) != 200 ) {
$text = false;
}
+ # Don't return truncated output
+ if ( curl_errno( $c ) != CURLE_OK ) {
+ $text = false;
+ }
curl_close( $c );
} else {
- # Otherwise use file_get_contents, or its compatibility function from GlobalFunctions.php
+ # Otherwise use file_get_contents...
# This may take 3 minutes to time out, and doesn't have local fetch capabilities
+
+ global $wgVersion;
+ $headers = array( "User-Agent: MediaWiki/$wgVersion" );
+ if( strcasecmp( $method, 'post' ) == 0 ) {
+ // Required for HTTP 1.0 POSTs
+ $headers[] = "Content-Length: 0";
+ }
+ $opts = array(
+ 'http' => array(
+ 'method' => $method,
+ 'header' => implode( "\r\n", $headers ) ) );
+ $ctx = stream_context_create($opts);
+
$url_fopen = ini_set( 'allow_url_fopen', 1 );
- $text = file_get_contents( $url );
+ $text = file_get_contents( $url, false, $ctx );
ini_set( 'allow_url_fopen', $url_fopen );
}
return $text;
@@ -88,4 +118,4 @@ class Http {
return false;
}
}
-?>
+
diff --git a/includes/IP.php b/includes/IP.php
index 8a2756c9..db712c3b 100644
--- a/includes/IP.php
+++ b/includes/IP.php
@@ -22,7 +22,12 @@ define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)');
define( 'RE_IPV6_ADD', '(:(:' . RE_IPV6_WORD . '){1,7}|' . RE_IPV6_WORD . '(:{1,2}' . RE_IPV6_WORD . '|::$){1,7})' );
define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
// This might be useful for regexps used elsewhere, matches any IPv6 or IPv6 address or network
-define( 'IP_ADDRESS_STRING', RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)|' . RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)');
+define( 'IP_ADDRESS_STRING',
+ '(?:' .
+ RE_IP_ADD . '(\/' . RE_IP_PREFIX . '|)' .
+ '|' .
+ RE_IPV6_ADD . '(\/' . RE_IPV6_PREFIX . '|)' .
+ ')' );
/**
* A collection of public static functions to play with IP address
@@ -109,13 +114,14 @@ class IP {
* @return string
*/
public static function sanitizeIP( $ip ) {
- if ( !$ip ) return null;
+ $ip = trim( $ip );
+ if ( $ip === '' ) return null;
// Trim and return IPv4 addresses
- if ( self::isIPv4($ip) ) return trim($ip);
+ if ( self::isIPv4($ip) ) return $ip;
// Only IPv6 addresses can be expanded
if ( !self::isIPv6($ip) ) return $ip;
// Remove any whitespaces, convert to upper case
- $ip = strtoupper( trim($ip) );
+ $ip = strtoupper( $ip );
// Expand zero abbreviations
if ( strpos( $ip, '::' ) !== false ) {
$ip = str_replace('::', str_repeat(':0', 8 - substr_count($ip, ':')) . ':', $ip);
@@ -462,22 +468,29 @@ class IP {
* @return valid dotted quad IPv4 address or null
*/
public static function canonicalize( $addr ) {
- if ( self::isValid( $addr ) )
- return $addr;
+ if ( self::isValid( $addr ) )
+ return $addr;
+
+ // Annoying IPv6 representations like ::ffff:1.2.3.4
+ if ( strpos($addr,':') !==false && strpos($addr,'.') !==false ) {
+ $addr = str_replace( '.', ':', $addr );
+ if( IP::isIPv6( $addr ) )
+ return $addr;
+ }
- // IPv6 loopback address
- $m = array();
- if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) )
- return '127.0.0.1';
+ // IPv6 loopback address
+ $m = array();
+ if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) )
+ return '127.0.0.1';
- // IPv4-mapped and IPv4-compatible IPv6 addresses
- if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . '(' . RE_IP_ADD . ')$/i', $addr, $m ) )
- return $m[1];
- if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . RE_IPV6_WORD . ':' . RE_IPV6_WORD . '$/i', $addr, $m ) )
- return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
+ // IPv4-mapped and IPv4-compatible IPv6 addresses
+ if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . '(' . RE_IP_ADD . ')$/i', $addr, $m ) )
+ return $m[1];
+ if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . RE_IPV6_WORD . ':' . RE_IPV6_WORD . '$/i', $addr, $m ) )
+ return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
- return null; // give up
+ return null; // give up
}
}
-?>
+
diff --git a/includes/ImageFunctions.php b/includes/ImageFunctions.php
index d04110d4..3e87c994 100644
--- a/includes/ImageFunctions.php
+++ b/includes/ImageFunctions.php
@@ -1,113 +1,4 @@
<?php
-
-/**
- * Returns the image directory of an image
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the image file.
- * @public
- */
-function wfImageDir( $fname ) {
- global $wgUploadDirectory, $wgHashedUploadDirectory;
-
- if (!$wgHashedUploadDirectory) { return $wgUploadDirectory; }
-
- $hash = md5( $fname );
- $dest = $wgUploadDirectory . '/' . $hash{0} . '/' . substr( $hash, 0, 2 );
-
- return $dest;
-}
-
-/**
- * Returns the image directory of an image's thumbnail
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the original image file
- * @param $shared Boolean: (optional) use the shared upload directory (default: 'false').
- * @public
- */
-function wfImageThumbDir( $fname, $shared = false ) {
- $base = wfImageArchiveDir( $fname, 'thumb', $shared );
- if ( Image::isHashed( $shared ) ) {
- $dir = "$base/$fname";
- } else {
- $dir = $base;
- }
-
- return $dir;
-}
-
-/**
- * Old thumbnail directory, kept for conversion
- */
-function wfDeprecatedThumbDir( $thumbName , $subdir='thumb', $shared=false) {
- return wfImageArchiveDir( $thumbName, $subdir, $shared );
-}
-
-/**
- * Returns the image directory of an image's old version
- * The result is an absolute path.
- *
- * This function is called from thumb.php before Setup.php is included
- *
- * @param $fname String: file name of the thumbnail file, including file size prefix.
- * @param $subdir String: subdirectory of the image upload directory that should be used for storing the old version. Default is 'archive'.
- * @param $shared Boolean use the shared upload directory (only relevant for other functions which call this one). Default is 'false'.
- * @public
- */
-function wfImageArchiveDir( $fname , $subdir='archive', $shared=false ) {
- global $wgUploadDirectory, $wgHashedUploadDirectory;
- global $wgSharedUploadDirectory, $wgHashedSharedUploadDirectory;
- $dir = $shared ? $wgSharedUploadDirectory : $wgUploadDirectory;
- $hashdir = $shared ? $wgHashedSharedUploadDirectory : $wgHashedUploadDirectory;
- if (!$hashdir) { return $dir.'/'.$subdir; }
- $hash = md5( $fname );
-
- return $dir.'/'.$subdir.'/'.$hash[0].'/'.substr( $hash, 0, 2 );
-}
-
-
-/*
- * Return the hash path component of an image path (URL or filesystem),
- * e.g. "/3/3c/", or just "/" if hashing is not used.
- *
- * @param $dbkey The filesystem / database name of the file
- * @param $fromSharedDirectory Use the shared file repository? It may
- * use different hash settings from the local one.
- */
-function wfGetHashPath ( $dbkey, $fromSharedDirectory = false ) {
- if( Image::isHashed( $fromSharedDirectory ) ) {
- $hash = md5($dbkey);
- return '/' . $hash{0} . '/' . substr( $hash, 0, 2 ) . '/';
- } else {
- return '/';
- }
-}
-
-/**
- * Returns the image URL of an image's old version
- *
- * @param $name String: file name of the image file
- * @param $subdir String: (optional) subdirectory of the image upload directory that is used by the old version. Default is 'archive'
- * @public
- */
-function wfImageArchiveUrl( $name, $subdir='archive' ) {
- global $wgUploadPath, $wgHashedUploadDirectory;
-
- if ($wgHashedUploadDirectory) {
- $hash = md5( substr( $name, 15) );
- $url = $wgUploadPath.'/'.$subdir.'/' . $hash{0} . '/' .
- substr( $hash, 0, 2 ) . '/'.$name;
- } else {
- $url = $wgUploadPath.'/'.$subdir.'/'.$name;
- }
- return wfUrlencode($url);
-}
-
/**
* Return a rounded pixel equivalent for a labeled CSS/SVG length.
* http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers
@@ -256,4 +147,4 @@ function wfFitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
}
-?>
+
diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php
index fba7714c..64f266f6 100644
--- a/includes/ImageGallery.php
+++ b/includes/ImageGallery.php
@@ -17,11 +17,17 @@ class ImageGallery
var $mImages, $mShowBytes, $mShowFilename;
var $mCaption = false;
var $mSkin = false;
+ var $mRevisionId = 0;
/**
- * Is the gallery on a wiki page (i.e. not a special page)
+ * Hide blacklisted images?
*/
- var $mParsing;
+ var $mHideBadImages;
+
+ /**
+ * Registered parser object for output callbacks
+ */
+ var $mParser;
/**
* Contextual title, used when images are being screened
@@ -31,6 +37,8 @@ class ImageGallery
private $mPerRow = 4; // How many images wide should the gallery be?
private $mWidths = 120, $mHeights = 120; // How wide/tall each thumbnail should be
+
+ private $mAttribs = array();
/**
* Create a new image gallery object.
@@ -39,14 +47,22 @@ class ImageGallery
$this->mImages = array();
$this->mShowBytes = true;
$this->mShowFilename = true;
- $this->mParsing = false;
+ $this->mParser = false;
+ $this->mHideBadImages = false;
}
/**
- * Set the "parse" bit so we know to hide "bad" images
+ * Register a parser object
*/
- function setParsing( $val = true ) {
- $this->mParsing = $val;
+ function setParser( $parser ) {
+ $this->mParser = $parser;
+ }
+
+ /**
+ * Set bad image flag
+ */
+ function setHideBadImages( $flag = true ) {
+ $this->mHideBadImages = $flag;
}
/**
@@ -127,22 +143,30 @@ class ImageGallery
/**
* Add an image to the gallery.
*
- * @param $image Image object that is added to the gallery
+ * @param $title Title object of the image that is added to the gallery
* @param $html String: additional HTML text to be shown. The name and size of the image are always shown.
*/
- function add( $image, $html='' ) {
- $this->mImages[] = array( &$image, $html );
- wfDebug( "ImageGallery::add " . $image->getName() . "\n" );
+ function add( $title, $html='' ) {
+ if ( $title instanceof File ) {
+ // Old calling convention
+ $title = $title->getTitle();
+ }
+ $this->mImages[] = array( $title, $html );
+ wfDebug( "ImageGallery::add " . $title->getText() . "\n" );
}
/**
* Add an image at the beginning of the gallery.
*
- * @param $image Image object that is added to the gallery
+ * @param $title Title object of the image that is added to the gallery
* @param $html String: Additional HTML text to be shown. The name and size of the image are always shown.
*/
- function insert( $image, $html='' ) {
- array_unshift( $this->mImages, array( &$image, $html ) );
+ function insert( $title, $html='' ) {
+ if ( $title instanceof File ) {
+ // Old calling convention
+ $title = $title->getTitle();
+ }
+ array_unshift( $this->mImages, array( &$title, $html ) );
}
@@ -172,6 +196,19 @@ class ImageGallery
function setShowFilename( $f ) {
$this->mShowFilename = ( $f == true);
}
+
+ /**
+ * Set arbitrary attributes to go on the HTML gallery output element.
+ * Should be suitable for a &lt;table&gt; element.
+ *
+ * Note -- if taking from user input, you should probably run through
+ * Sanitizer::validateAttributes() first.
+ *
+ * @param array of HTML attribute pairs
+ */
+ function setAttributes( $attribs ) {
+ $this->mAttribs = $attribs;
+ }
/**
* Return a HTML representation of the image gallery
@@ -188,23 +225,33 @@ class ImageGallery
$sk = $this->getSkin();
- $s = '<table class="gallery" cellspacing="0" cellpadding="0">';
+ $attribs = Sanitizer::mergeAttributes(
+ array(
+ 'class' => 'gallery',
+ 'cellspacing' => '0',
+ 'cellpadding' => '0' ),
+ $this->mAttribs );
+ $s = Xml::openElement( 'table', $attribs );
if( $this->mCaption )
$s .= "\n\t<caption>{$this->mCaption}</caption>";
$params = array( 'width' => $this->mWidths, 'height' => $this->mHeights );
$i = 0;
foreach ( $this->mImages as $pair ) {
- $img =& $pair[0];
+ $nt = $pair[0];
$text = $pair[1];
+
+ # Give extensions a chance to select the file revision for us
+ $time = false;
+ wfRunHooks( 'BeforeGalleryFindFile', array( &$this, &$nt, &$time ) );
- $nt = $img->getTitle();
+ $img = wfFindFile( $nt, $time );
- if( $nt->getNamespace() != NS_IMAGE ) {
+ if( $nt->getNamespace() != NS_IMAGE || !$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>';
- } elseif( $this->mParsing && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
+ } elseif( $this->mHideBadImages && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
# The image is blacklisted, just show it as a text link.
$thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
. $sk->makeKnownLinkObj( $nt, htmlspecialchars( $nt->getText() ) ) . '</div>';
@@ -214,15 +261,26 @@ class ImageGallery
. htmlspecialchars( $img->getLastError() ) . '</div>';
} else {
$vpad = floor( ( 1.25*$this->mHeights - $thumb->height ) /2 ) - 2;
- $thumbhtml = "\n\t\t\t".'<div class="thumb" style="padding: ' . $vpad . 'px 0; width: '.($this->mWidths+30).'px;">'
- . $sk->makeKnownLinkObj( $nt, $thumb->toHtml() ) . '</div>';
+
+ $thumbhtml = "\n\t\t\t".
+ '<div class="thumb" style="padding: ' . $vpad . 'px 0; width: ' .($this->mWidths+30).'px;">'
+ # Auto-margin centering for block-level elements. Needed now that we have video
+ # handlers since they may emit block-level elements as opposed to simple <img> tags.
+ # ref http://css-discuss.incutio.com/?page=CenteringBlockElement
+ . '<div style="margin-left: auto; margin-right: auto; width: ' .$this->mWidths.'px;">'
+ . $thumb->toHtml( array( 'desc-link' => true ) ) . '</div></div>';
+
+ // Call parser transform hook
+ if ( $this->mParser && $img->getHandler() ) {
+ $img->getHandler()->parserTransformHook( $this->mParser, $img );
+ }
}
//TODO
//$ul = $sk->makeLink( $wgContLang->getNsText( Namespace::getUser() ) . ":{$ut}", $ut );
if( $this->mShowBytes ) {
- if( $img->exists() ) {
+ if( $img ) {
$nb = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
$wgLang->formatNum( $img->getSize() ) );
} else {
@@ -292,4 +350,5 @@ class ImageGallery
}
} //class
-?>
+
+
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index 13f8e46a..3cf6d0ac 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -16,8 +16,18 @@ if( !defined( 'MEDIAWIKI' ) )
class ImagePage extends Article {
/* private */ var $img; // Image object this page is shown for
+ /* private */ var $repo;
var $mExtraDescription = false;
+ function __construct( $title ) {
+ parent::__construct( $title );
+ $this->img = wfFindFile( $this->mTitle );
+ if ( !$this->img ) {
+ $this->img = wfLocalFile( $this->mTitle );
+ }
+ $this->repo = $this->img->repo;
+ }
+
/**
* Handler for action=render
* Include body text only; none of the image extras
@@ -31,8 +41,6 @@ class ImagePage extends Article {
function view() {
global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
- $this->img = new Image( $this->mTitle );
-
$diff = $wgRequest->getVal( 'diff' );
$diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
@@ -40,10 +48,10 @@ class ImagePage extends Article {
return Article::view();
if ($wgShowEXIF && $this->img->exists()) {
- $exif = $this->img->getExifData();
- $showmeta = count($exif) ? true : false;
+ // FIXME: bad interface, see note on MediaHandler::formatMetadata().
+ $formattedMetadata = $this->img->formatMetadata();
+ $showmeta = $formattedMetadata !== false;
} else {
- $exif = false;
$showmeta = false;
}
@@ -76,12 +84,12 @@ class ImagePage extends Article {
$this->imageHistory();
$this->imageLinks();
- if ( $exif ) {
+ if ( $showmeta ) {
global $wgStylePath, $wgStyleVersion;
$expand = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-expand' ) ) );
$collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) );
$wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ). "\n" );
- $wgOut->addWikiText( $this->makeMetadataTable( $exif ) );
+ $wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
$wgOut->addHTML(
"<script type=\"text/javascript\" src=\"$wgStylePath/common/metadata.js?$wgStyleVersion\"></script>\n" .
"<script type=\"text/javascript\">attachMetadataToggle('mw_metadata', '$expand', '$collapse');</script>\n" );
@@ -100,9 +108,9 @@ class ImagePage extends Article {
global $wgLang;
$r = '<ul id="filetoc">
<li><a href="#file">' . $wgLang->getNsText( NS_IMAGE ) . '</a></li>
- <li><a href="#filehistory">' . wfMsgHtml( 'imghistory' ) . '</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>' : '') . '
+ ($metadata ? ' <li><a href="#metadata">' . wfMsgHtml( 'metadata' ) . '</a></li>' : '') . '
</ul>';
return $r;
}
@@ -110,57 +118,39 @@ class ImagePage extends Article {
/**
* Make a table with metadata to be shown in the output page.
*
+ * FIXME: bad interface, see note on MediaHandler::formatMetadata().
+ *
* @access private
*
* @param array $exif The array containing the EXIF data
* @return string
*/
- function makeMetadataTable( $exif ) {
+ function makeMetadataTable( $metadata ) {
$r = wfMsg( 'metadata-help' ) . "\n\n";
$r .= "{| id=mw_metadata class=mw_metadata\n";
- $visibleFields = $this->visibleMetadataFields();
- foreach( $exif as $k => $v ) {
- $tag = strtolower( $k );
- $msg = wfMsg( "exif-$tag" );
- $class = "exif-$tag";
- if( !in_array( $tag, $visibleFields ) ) {
- $class .= ' collapsable';
+ foreach ( $metadata as $type => $stuff ) {
+ foreach ( $stuff as $v ) {
+ $class = Sanitizer::escapeId( $v['id'] );
+ if( $type == 'collapsed' ) {
+ $class .= ' collapsable';
+ }
+ $r .= "|- class=\"$class\"\n";
+ $r .= "!| {$v['name']}\n";
+ $r .= "|| {$v['value']}\n";
}
- $r .= "|- class=\"$class\"\n";
- $r .= "!| $msg\n";
- $r .= "|| $v\n";
}
$r .= '|}';
return $r;
}
/**
- * Get a list of EXIF metadata items which should be displayed when
- * the metadata table is collapsed.
- *
- * @return array of strings
- * @access private
- */
- function visibleMetadataFields() {
- $fields = array();
- $lines = explode( "\n", wfMsgForContent( 'metadata-fields' ) );
- foreach( $lines as $line ) {
- $matches = array();
- if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
- $fields[] = $matches[1];
- }
- }
- return $fields;
- }
-
- /**
* Overloading Article's getContent method.
*
* Omit noarticletext if sharedupload; text will be fetched from the
* shared upload server if possible.
*/
function getContent() {
- if( $this->img && $this->img->fromSharedDirectory && 0 == $this->getID() ) {
+ if( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
return '';
}
return Article::getContent();
@@ -203,12 +193,15 @@ class ImagePage extends Article {
$mime = $this->img->getMimeType();
$showLink = false;
$linkAttribs = array( 'href' => $full_url );
+ $longDesc = $this->img->getLongDesc();
+
+ wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) ) ;
- if ( $this->img->allowInlineDisplay() and $width and $height) {
+ if ( $this->img->allowInlineDisplay() ) {
# image
# "Download high res version" link below the image
- $msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
+ #$msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
# We'll show a thumbnail of this image
if ( $width > $maxWidth || $height > $maxHeight ) {
# Calculate the thumbnail size.
@@ -242,21 +235,20 @@ class ImagePage extends Article {
} else {
$anchorclose .=
$msgsmall .
- '<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $msgsize;
+ '<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $longDesc;
}
if ( $this->img->isMultipage() ) {
$wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
}
- $imgAttribs = array(
- 'border' => 0,
- 'alt' => $this->img->getTitle()->getPrefixedText()
- );
-
if ( $thumbnail ) {
+ $options = array(
+ 'alt' => $this->img->getTitle()->getPrefixedText(),
+ 'file-link' => true,
+ );
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
- $thumbnail->toHtml( $imgAttribs, $linkAttribs ) .
+ $thumbnail->toHtml( $options ) .
$anchorclose . '</div>' );
}
@@ -265,8 +257,8 @@ class ImagePage extends Article {
if ( $page > 1 ) {
$label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
- $link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
- $thumb1 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
+ $link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
+ $thumb1 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none',
array( 'page' => $page - 1 ) );
} else {
$thumb1 = '';
@@ -274,8 +266,8 @@ class ImagePage extends Article {
if ( $page < $count ) {
$label = wfMsg( 'imgmultipagenext' );
- $link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
- $thumb2 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
+ $link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
+ $thumb2 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none',
array( 'page' => $page + 1 ) );
} else {
$thumb2 = '';
@@ -304,9 +296,9 @@ class ImagePage extends Article {
if ($this->img->isSafeFile()) {
$icon= $this->img->iconThumb();
- $wgOut->addHTML( '<div class="fullImageLink" id="file"><a href="' . $full_url . '">' .
- $icon->toHtml() .
- '</a></div>' );
+ $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
+ $icon->toHtml( array( 'desc-link' => true ) ) .
+ '</div>' );
}
$showLink = true;
@@ -314,42 +306,32 @@ class ImagePage extends Article {
if ($showLink) {
- // Workaround for incorrect MIME type on SVGs uploaded in previous versions
- if ($mime == 'image/svg') $mime = 'image/svg+xml';
-
$filename = wfEscapeWikiText( $this->img->getName() );
- $info = wfMsg( 'file-info', $sk->formatSize( $this->img->getSize() ), $mime );
- $infores = '';
-
- // Check for MIME type. Other types may have more information in the future.
- if (substr($mime,0,9) == 'image/svg' ) {
- $infores = wfMsg('file-svg', $width_orig, $height_orig ) . '<br />';
- }
global $wgContLang;
$dirmark = $wgContLang->getDirMark();
if (!$this->img->isSafeFile()) {
$warning = wfMsg( 'mediawarning' );
- $wgOut->addWikiText( <<<END
-<div class="fullMedia">$infores
+ $wgOut->addWikiText( <<<EOT
+<div class="fullMedia">
<span class="dangerousLink">[[Media:$filename|$filename]]</span>$dirmark
-<span class="fileInfo"> $info</span>
+<span class="fileInfo"> $longDesc</span>
</div>
<div class="mediaWarning">$warning</div>
-END
+EOT
);
} else {
- $wgOut->addWikiText( <<<END
-<div class="fullMedia">$infores
-[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $info</span>
+ $wgOut->addWikiText( <<<EOT
+<div class="fullMedia">
+[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $longDesc</span>
</div>
-END
+EOT
);
}
}
- if($this->img->fromSharedDirectory) {
+ if(!$this->img->isLocal()) {
$this->printSharedImageText();
}
} else {
@@ -363,27 +345,21 @@ END
}
function printSharedImageText() {
- global $wgRepositoryBaseUrl, $wgFetchCommonsDescriptions, $wgOut, $wgUser;
-
- $url = $wgRepositoryBaseUrl . urlencode($this->mTitle->getDBkey());
- $sharedtext = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
- if ($wgRepositoryBaseUrl && !$wgFetchCommonsDescriptions) {
+ global $wgOut, $wgUser;
+ $descUrl = $this->img->getDescriptionUrl();
+ $descText = $this->img->getDescriptionText();
+ $s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
+ if ( $descUrl && !$descText) {
$sk = $wgUser->getSkin();
- $title = SpecialPage::getTitleFor( 'Upload' );
- $link = $sk->makeKnownLinkObj($title, wfMsgHtml('shareduploadwiki-linktext'),
- array( 'wpDestFile' => urlencode( $this->img->getName() )));
- $sharedtext .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
+ $link = $sk->makeExternalLink( $descUrl, wfMsg('shareduploadwiki-linktext') );
+ $s .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
}
- $sharedtext .= "</div>";
- $wgOut->addHTML($sharedtext);
-
- if ($wgRepositoryBaseUrl && $wgFetchCommonsDescriptions) {
- $renderUrl = wfAppendQuery( $url, 'action=render' );
- wfDebug( "Fetching shared description from $renderUrl\n" );
- $text = Http::get( $renderUrl );
- if ($text)
- $this->mExtraDescription = $text;
+ $s .= "</div>";
+ $wgOut->addHTML($s);
+
+ if ( $descText ) {
+ $this->mExtraDescription = $descText;
}
}
@@ -400,7 +376,7 @@ END
function uploadLinksBox() {
global $wgUser, $wgOut;
- if( $this->img->fromSharedDirectory )
+ if( !$this->img->isLocal() )
return;
$sk = $wgUser->getSkin();
@@ -408,7 +384,7 @@ END
$wgOut->addHtml( '<br /><ul>' );
# "Upload a new version of this file" link
- if( $wgUser->isAllowed( 'reupload' ) ) {
+ if( UploadForm::userCanReUpload($wgUser,$this->img->name) ) {
$ulink = $sk->makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) );
$wgOut->addHtml( "<li><div class='plainlinks'>{$ulink}</div></li>" );
}
@@ -439,25 +415,31 @@ END
$line = $this->img->nextHistoryLine();
if ( $line ) {
- $list = new ImageHistoryList( $sk );
+ $list = new ImageHistoryList( $sk, $this->img );
+ $file = $this->repo->newFileFromRow( $line );
+ $dims = $file->getDimensionsString();
$s = $list->beginImageHistoryList() .
$list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp),
$this->mTitle->getDBkey(), $line->img_user,
$line->img_user_text, $line->img_size, $line->img_description,
- $line->img_width, $line->img_height
+ $dims
);
while ( $line = $this->img->nextHistoryLine() ) {
- $s .= $list->imageHistoryLine( false, $line->img_timestamp,
- $line->oi_archive_name, $line->img_user,
- $line->img_user_text, $line->img_size, $line->img_description,
- $line->img_width, $line->img_height
+ $file = $this->repo->newFileFromRow( $line );
+ $dims = $file->getDimensionsString();
+ $s .= $list->imageHistoryLine( false, $line->oi_timestamp,
+ $line->oi_archive_name, $line->oi_user,
+ $line->oi_user_text, $line->oi_size, $line->oi_description,
+ $dims
);
}
$s .= $list->endImageHistoryList();
} else { $s=''; }
$wgOut->addHTML( $s );
+ $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() ) {
@@ -496,207 +478,31 @@ END
$wgOut->addHTML( "</ul>\n" );
}
- function delete()
- {
- global $wgUser, $wgOut, $wgRequest;
-
- $confirm = $wgRequest->wasPosted();
- $reason = $wgRequest->getVal( 'wpReason' );
- $image = $wgRequest->getVal( 'image' );
- $oldimage = $wgRequest->getVal( 'oldimage' );
-
- # Only sysops can delete images. Previously ordinary users could delete
- # old revisions, but this is no longer the case.
- if ( !$wgUser->isAllowed('delete') ) {
- $wgOut->permissionRequired( 'delete' );
- return;
- }
- if ( $wgUser->isBlocked() ) {
- return $this->blockedIPpage();
- }
- if ( wfReadOnly() ) {
- $wgOut->readOnlyPage();
- return;
- }
-
- # Better double-check that it hasn't been deleted yet!
- $wgOut->setPagetitle( wfMsg( 'confirmdelete' ) );
- if ( ( !is_null( $image ) )
- && ( '' == trim( $image ) ) ) {
- $wgOut->showFatalError( wfMsg( 'cannotdelete' ) );
- return;
- }
-
- $this->img = new Image( $this->mTitle );
-
- # Deleting old images doesn't require confirmation
- if ( !is_null( $oldimage ) || $confirm ) {
- if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
- $this->doDelete( $reason );
- } else {
- $wgOut->showFatalError( wfMsg( 'sessionfailure' ) );
- }
- return;
- }
-
- if ( !is_null( $image ) ) {
- $q = '&image=' . urlencode( $image );
- } else if ( !is_null( $oldimage ) ) {
- $q = '&oldimage=' . urlencode( $oldimage );
- } else {
- $q = '';
- }
- return $this->confirmDelete( $q, $wgRequest->getText( 'wpReason' ) );
- }
-
- /*
- * Delete an image.
- * @param $reason User provided reason for deletion.
- */
- function doDelete( $reason ) {
- global $wgOut, $wgRequest;
-
- $oldimage = $wgRequest->getVal( 'oldimage' );
-
- if ( !is_null( $oldimage ) ) {
- if ( strlen( $oldimage ) < 16 ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
- if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
- if ( !$this->doDeleteOldImage( $oldimage ) ) {
- return;
- }
- $deleted = $oldimage;
- } else {
- $ok = $this->img->delete( $reason );
- if( !$ok ) {
- # If the deletion operation actually failed, bug out:
- $wgOut->showFileDeleteError( $this->img->getName() );
- return;
- }
-
- # Image itself is now gone, and database is cleaned.
- # Now we remove the image description page.
-
- $article = new Article( $this->mTitle );
- $article->doDeleteArticle( $reason ); # ignore errors
-
- $deleted = $this->img->getName();
- }
-
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
-
- $loglink = '[[Special:Log/delete|' . wfMsg( 'deletionlog' ) . ']]';
- $text = wfMsg( 'deletedtext', $deleted, $loglink );
-
- $wgOut->addWikiText( $text );
-
- $wgOut->returnToMain( false, $this->mTitle->getPrefixedText() );
- }
-
/**
- * @return success
+ * Delete the file, or an earlier version of it
*/
- function doDeleteOldImage( $oldimage )
- {
- global $wgOut;
-
- $ok = $this->img->deleteOld( $oldimage, '' );
- if( !$ok ) {
- # If we actually have a file and can't delete it, throw an error.
- # Something went awry...
- $wgOut->showFileDeleteError( "$oldimage" );
- } else {
- # Log the deletion
- $log = new LogPage( 'delete' );
- $log->addEntry( 'delete', $this->mTitle, wfMsg('deletedrevision',$oldimage) );
- }
- return $ok;
- }
-
- function revert() {
- global $wgOut, $wgRequest, $wgUser;
-
- $oldimage = $wgRequest->getText( 'oldimage' );
- if ( strlen( $oldimage ) < 16 ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
- if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) {
- $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
- return;
- }
-
- if ( wfReadOnly() ) {
- $wgOut->readOnlyPage();
- return;
- }
- if( $wgUser->isAnon() ) {
- $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
+ public function delete() {
+ if( !$this->img->exists() || !$this->img->isLocal() ) {
+ // Standard article deletion
+ Article::delete();
return;
}
- if ( ! $this->mTitle->userCan( 'edit' ) ) {
- $wgOut->readOnlyPage( $this->getContent(), true );
- return;
- }
- if ( $wgUser->isBlocked() ) {
- return $this->blockedIPpage();
- }
- if( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
- $wgOut->showErrorPage( 'internalerror', 'sessionfailure' );
- return;
- }
- $name = substr( $oldimage, 15 );
-
- $dest = wfImageDir( $name );
- $archive = wfImageArchiveDir( $name );
- $curfile = "{$dest}/{$name}";
-
- if ( !is_dir( $dest ) ) wfMkdirParents( $dest );
- if ( !is_dir( $archive ) ) wfMkdirParents( $archive );
-
- if ( ! is_file( $curfile ) ) {
- $wgOut->showFileNotFoundError( htmlspecialchars( $curfile ) );
- return;
- }
- $oldver = wfTimestampNow() . "!{$name}";
-
- if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) {
- $wgOut->showFileRenameError( $curfile, "${archive}/{$oldver}" );
- return;
- }
- if ( ! copy( "{$archive}/{$oldimage}", $curfile ) ) {
- $wgOut->showFileCopyError( "${archive}/{$oldimage}", $curfile );
- return;
- }
-
- # Record upload and update metadata cache
- $img = Image::newFromName( $name );
- $img->recordUpload( $oldver, wfMsg( "reverted" ) );
-
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotpolicy( 'noindex,nofollow' );
- $wgOut->addHTML( wfMsg( 'imagereverted' ) );
-
- $descTitle = $img->getTitle();
- $wgOut->returnToMain( false, $descTitle->getPrefixedText() );
+ $deleter = new FileDeleteForm( $this->img );
+ $deleter->execute();
}
- function blockedIPpage() {
- $edit = new EditPage( $this );
- return $edit->blockedIPpage();
+ /**
+ * Revert the file to an earlier version
+ */
+ public function revert() {
+ $reverter = new FileRevertForm( $this->img );
+ $reverter->execute();
}
/**
* Override handling of action=purge
*/
function doPurge() {
- $this->img = new Image( $this->mTitle );
if( $this->img->exists() ) {
wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
$update = new HTMLCacheUpdate( $this->mTitle, 'imagelinks' );
@@ -709,82 +515,120 @@ END
parent::doPurge();
}
+ /**
+ * Display an error with a wikitext description
+ */
+ function showError( $description ) {
+ global $wgOut;
+ $wgOut->setPageTitle( wfMsg( "internalerror" ) );
+ $wgOut->setRobotpolicy( "noindex,nofollow" );
+ $wgOut->setArticleRelated( false );
+ $wgOut->enableClientCache( false );
+ $wgOut->addWikiText( $description );
+ }
+
}
/**
- * @todo document
+ * Builds the image revision log shown on image pages
+ *
* @addtogroup Media
*/
class ImageHistoryList {
- function ImageHistoryList( &$skin ) {
- $this->skin =& $skin;
- }
- function beginImageHistoryList() {
- $s = "\n" .
- Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'imghistory' ) ) .
- "\n<p>" . wfMsg( 'imghistlegend' ) . "</p>\n".'<ul class="special">';
- return $s;
- }
+ protected $img, $skin, $title, $repo;
- function endImageHistoryList() {
- $s = "</ul>\n";
- return $s;
+ public function __construct( $skin, $img ) {
+ $this->skin = $skin;
+ $this->img = $img;
+ $this->title = $img->getTitle();
}
- function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $width, $height ) {
- global $wgUser, $wgLang, $wgTitle, $wgContLang;
-
- $datetime = $wgLang->timeanddate( $timestamp, true );
- $del = wfMsgHtml( 'deleteimg' );
- $delall = wfMsgHtml( 'deleteimgcompletely' );
- $cur = wfMsgHtml( 'cur' );
+ public function beginImageHistoryList() {
+ global $wgOut, $wgUser;
+ return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) )
+ . $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) )
+ . Xml::openElement( 'table', array( 'class' => 'filehistory' ) ) . "\n"
+ . '<tr><td></td>'
+ . ( $this->img->isLocal() && $wgUser->isAllowed( 'delete' ) ? '<td></td>' : '' )
+ . '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
+ . '<th class="mw-imagepage-filesize">' . wfMsgHtml( 'filehist-filesize' ) . '</th>'
+ . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
+ . "</tr>\n";
+ }
- if ( $iscur ) {
- $url = Image::imageUrl( $img );
- $rlink = $cur;
- if ( $wgUser->isAllowed('delete') ) {
- $link = $wgTitle->escapeLocalURL( 'image=' . $wgTitle->getPartialURL() .
- '&action=delete' );
- $style = $this->skin->getInternalLinkAttributes( $link, $delall );
+ public function endImageHistoryList() {
+ return "</table>\n";
+ }
- $dlink = '<a href="'.$link.'"'.$style.'>'.$delall.'</a>';
- } else {
- $dlink = $del;
- }
+ public function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $dims ) {
+ global $wgUser, $wgLang, $wgContLang;
+ $local = $this->img->isLocal();
+ $row = '';
+
+ // Deletion link
+ if( $local && $wgUser->isAllowed( 'delete' ) ) {
+ $row .= '<td>';
+ $q = array();
+ $q[] = 'action=delete';
+ if( !$iscur )
+ $q[] = 'oldimage=' . urlencode( $img );
+ $row .= '(' . $this->skin->makeKnownLinkObj(
+ $this->title,
+ wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
+ implode( '&', $q )
+ ) . ')';
+ $row .= '</td>';
+ }
+
+ // Reversion link/current indicator
+ $row .= '<td>';
+ if( $iscur ) {
+ $row .= '(' . wfMsgHtml( 'filehist-current' ) . ')';
+ } elseif( $local && $wgUser->isLoggedIn() && $this->title->userCan( 'edit' ) ) {
+ $q = array();
+ $q[] = 'action=revert';
+ $q[] = 'oldimage=' . urlencode( $img );
+ $q[] = 'wpEditToken=' . urlencode( $wgUser->editToken( $img ) );
+ $row .= '(' . $this->skin->makeKnownLinkObj(
+ $this->title,
+ wfMsgHtml( 'filehist-revert' ),
+ implode( '&', $q )
+ ) . ')';
+ }
+ $row .= '</td>';
+
+ // Date/time and image link
+ $row .= '<td>';
+ $url = $iscur ? $this->img->getUrl() : $this->img->getArchiveUrl( $img );
+ $row .= Xml::element(
+ 'a',
+ array( 'href' => $url ),
+ $wgLang->timeAndDate( $timestamp, true )
+ );
+ $row .= '</td>';
+
+ // Uploading user
+ $row .= '<td>';
+ if( $local ) {
+ $row .= $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
} else {
- $url = htmlspecialchars( wfImageArchiveUrl( $img ) );
- if( $wgUser->getID() != 0 && $wgTitle->userCan( 'edit' ) ) {
- $token = urlencode( $wgUser->editToken( $img ) );
- $rlink = $this->skin->makeKnownLinkObj( $wgTitle,
- wfMsgHtml( 'revertimg' ), 'action=revert&oldimage=' .
- urlencode( $img ) . "&wpEditToken=$token" );
- $dlink = $this->skin->makeKnownLinkObj( $wgTitle,
- $del, 'action=delete&oldimage=' . urlencode( $img ) .
- "&wpEditToken=$token" );
- } else {
- # Having live active links for non-logged in users
- # means that bots and spiders crawling our site can
- # inadvertently change content. Baaaad idea.
- $rlink = wfMsgHtml( 'revertimg' );
- $dlink = $del;
- }
+ $row .= htmlspecialchars( $usertext );
}
-
- $userlink = $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
- $nbytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
- $wgLang->formatNum( $size ) );
- $widthheight = wfMsgHtml( 'widthheight', $width, $height );
- $style = $this->skin->getInternalLinkAttributes( $url, $datetime );
+ $row .= '</td>';
- $s = "<li> ({$dlink}) ({$rlink}) <a href=\"{$url}\"{$style}>{$datetime}</a> . . {$userlink} . . {$widthheight} ({$nbytes})";
+ // Image dimensions
+ $row .= '<td>' . htmlspecialchars( $dims ) . '</td>';
- $s .= $this->skin->commentBlock( $description, $wgTitle );
- $s .= "</li>\n";
- return $s;
- }
+ // File size
+ $row .= '<td class="mw-imagepage-filesize">' . $this->skin->formatSize( $size ) . '</td>';
-}
+ // Comment
+ $row .= '<td>' . $this->skin->formatComment( $description, $this->title ) . '</td>';
+ return "<tr>{$row}</tr>\n";
+ }
-?>
+}
diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php
index 93f090a1..8948ddc6 100644
--- a/includes/ImageQueryPage.php
+++ b/includes/ImageQueryPage.php
@@ -30,8 +30,8 @@ class ImageQueryPage extends QueryPage {
# $num [should update this to use a Pager]
for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
$image = $this->prepareImage( $row );
- if( $image instanceof Image ) {
- $gallery->add( $image, $this->getCellHtml( $row ) );
+ if( $image ) {
+ $gallery->add( $image->getTitle(), $this->getCellHtml( $row ) );
}
}
@@ -49,7 +49,7 @@ class ImageQueryPage extends QueryPage {
$namespace = isset( $row->namespace ) ? $row->namespace : NS_IMAGE;
$title = Title::makeTitleSafe( $namespace, $row->title );
return ( $title instanceof Title && $title->getNamespace() == NS_IMAGE )
- ? new Image( $title )
+ ? wfFindFile( $title )
: null;
}
@@ -65,4 +65,4 @@ class ImageQueryPage extends QueryPage {
}
-?>
+
diff --git a/includes/JobQueue.php b/includes/JobQueue.php
index 140130fa..a2780bdb 100644
--- a/includes/JobQueue.php
+++ b/includes/JobQueue.php
@@ -4,6 +4,8 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point\n" );
}
+require_once('UserMailer.php');
+
/**
* Class to both describe a background job and handle jobs.
*/
@@ -37,6 +39,46 @@ abstract class Job {
*/
/**
+ * Pop a job of a certain type. This tries less hard than pop() to
+ * actually find a job; it may be adversely affected by concurrent job
+ * runners.
+ */
+ static function pop_type($type) {
+ wfProfilein( __METHOD__ );
+
+ $dbw = wfGetDB( DB_MASTER );
+
+
+ $row = $dbw->selectRow( 'job', '*', array( 'job_cmd' => $type ), __METHOD__,
+ array( 'LIMIT' => 1 ));
+
+ if ($row === false) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ /* Ensure we "own" this row */
+ $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
+ $affected = $dbw->affectedRows();
+
+ if ($affected == 0) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ $namespace = $row->job_namespace;
+ $dbkey = $row->job_title;
+ $title = Title::makeTitleSafe( $namespace, $dbkey );
+ $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
+
+ $dbw->delete( 'job', $job->insertFields(), __METHOD__ );
+ $dbw->immediateCommit();
+
+ wfProfileOut( __METHOD__ );
+ return $job;
+ }
+
+ /**
* Pop a job off the front of the queue
* @static
* @param $offset Number of jobs to skip
@@ -125,20 +167,23 @@ abstract class Job {
}
/**
- * Create an object of a subclass
+ * Create the appropriate object to handle a specific job
+ *
+ * @param string $command Job command
+ * @param Title $title Associated title
+ * @param array $params Job parameters
+ * @param int $id Job identifier
+ * @return Job
*/
static function factory( $command, $title, $params = false, $id = 0 ) {
- switch ( $command ) {
- case 'refreshLinks':
- return new RefreshLinksJob( $title, $params, $id );
- case 'htmlCacheUpdate':
- case 'html_cache_update': # BC
- return new HTMLCacheUpdateJob( $title, $params['table'], $params['start'], $params['end'], $id );
- default:
- throw new MWException( "Invalid job command \"$command\"" );
+ global $wgJobClasses;
+ if( isset( $wgJobClasses[$command] ) ) {
+ $class = $wgJobClasses[$command];
+ return new $class( $title, $params, $id );
}
+ throw new MWException( "Invalid job command `{$command}`" );
}
-
+
static function makeBlob( $params ) {
if ( $params !== false ) {
return serialize( $params );
@@ -245,50 +290,3 @@ abstract class Job {
}
}
-
-/**
- * Background job to update links for a given title.
- */
-class RefreshLinksJob extends Job {
- function __construct( $title, $params = '', $id = 0 ) {
- parent::__construct( 'refreshLinks', $title, $params, $id );
- }
-
- /**
- * Run a refreshLinks job
- * @return boolean success
- */
- function run() {
- global $wgParser;
- wfProfileIn( __METHOD__ );
-
- $linkCache =& LinkCache::singleton();
- $linkCache->clear();
-
- if ( is_null( $this->title ) ) {
- $this->error = "refreshLinks: Invalid title";
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $revision = Revision::newFromTitle( $this->title );
- if ( !$revision ) {
- $this->error = 'refreshLinks: Article not found "' . $this->title->getPrefixedDBkey() . '"';
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- wfProfileIn( __METHOD__.'-parse' );
- $options = new ParserOptions;
- $parserOutput = $wgParser->parse( $revision->getText(), $this->title, $options, true, true, $revision->getId() );
- wfProfileOut( __METHOD__.'-parse' );
- wfProfileIn( __METHOD__.'-update' );
- $update = new LinksUpdate( $this->title, $parserOutput, false );
- $update->doUpdate();
- wfProfileOut( __METHOD__.'-update' );
- wfProfileOut( __METHOD__ );
- return true;
- }
-}
-
-?>
diff --git a/includes/Licenses.php b/includes/Licenses.php
index f4586ae5..6a034468 100644
--- a/includes/Licenses.php
+++ b/includes/Licenses.php
@@ -172,4 +172,4 @@ class License {
$this->text = strrev( $text );
}
}
-?>
+
diff --git a/includes/LinkBatch.php b/includes/LinkBatch.php
index 065c540a..8ab3393e 100644
--- a/includes/LinkBatch.php
+++ b/includes/LinkBatch.php
@@ -3,7 +3,7 @@
/**
* Class representing a list of titles
* The execute() method checks them all for existence and adds them to a LinkCache object
- +
+ *
* @addtogroup Cache
*/
class LinkBatch {
@@ -156,19 +156,26 @@ class LinkBatch {
} else {
$sql .= ' OR ';
}
- $sql .= "({$prefix}_namespace=$ns AND {$prefix}_title IN (";
-
- $firstTitle = true;
- foreach( $dbkeys as $dbkey => $unused ) {
- if ( $firstTitle ) {
- $firstTitle = false;
- } else {
- $sql .= ',';
+
+ if (count($dbkeys)==1) { // avoid multiple-reference syntax if simple equality can be used
+
+ $sql .= "({$prefix}_namespace=$ns AND {$prefix}_title=".
+ $db->addQuotes(current(array_keys($dbkeys))).
+ ")";
+ } else {
+ $sql .= "({$prefix}_namespace=$ns AND {$prefix}_title IN (";
+
+ $firstTitle = true;
+ foreach( $dbkeys as $dbkey => $unused ) {
+ if ( $firstTitle ) {
+ $firstTitle = false;
+ } else {
+ $sql .= ',';
+ }
+ $sql .= $db->addQuotes( $dbkey );
}
- $sql .= $db->addQuotes( $dbkey );
+ $sql .= '))';
}
-
- $sql .= '))';
}
if ( $first && $firstTitle ) {
# No titles added
@@ -179,4 +186,4 @@ class LinkBatch {
}
}
-?>
+
diff --git a/includes/LinkCache.php b/includes/LinkCache.php
index 53fb640a..7c49d88e 100644
--- a/includes/LinkCache.php
+++ b/includes/LinkCache.php
@@ -169,4 +169,4 @@ class LinkCache {
$this->mBadLinks = array();
}
}
-?>
+
diff --git a/includes/LinkFilter.php b/includes/LinkFilter.php
index 39341d5d..ee668f08 100644
--- a/includes/LinkFilter.php
+++ b/includes/LinkFilter.php
@@ -105,4 +105,4 @@ class LinkFilter {
return $like;
}
}
-?>
+
diff --git a/includes/Linker.php b/includes/Linker.php
index b12e2ad0..9397b800 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -12,6 +12,12 @@
* @addtogroup Skins
*/
class Linker {
+
+ /**
+ * Flags for userToolLinks()
+ */
+ const TOOL_LINKS_NOBLOCK = 1;
+
function __construct() {}
/**
@@ -96,7 +102,7 @@ class Linker {
wfProfileIn( 'Linker::makeLink' );
$nt = Title::newFromText( $title );
if ($nt) {
- $result = $this->makeLinkObj( Title::newFromText( $title ), $text, $query, $trail );
+ $result = $this->makeLinkObj( $nt, $text, $query, $trail );
} else {
wfDebug( 'Invalid title passed to Linker::makeLink(): "'.$title."\"\n" );
$result = $text == "" ? $title : $text;
@@ -218,32 +224,39 @@ class Linker {
$retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
} else {
wfProfileIn( $fname.'-immediate' );
+
+ # Handles links to special pages wich do not exist in the database:
+ if( $nt->getNamespace() == NS_SPECIAL ) {
+ if( SpecialPage::exists( $nt->getDbKey() ) ) {
+ $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
+ } else {
+ $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
+ }
+ wfProfileOut( $fname.'-immediate' );
+ wfProfileOut( $fname );
+ return $retVal;
+ }
+
# Work out link colour immediately
$aid = $nt->getArticleID() ;
if ( 0 == $aid ) {
$retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
} else {
- $threshold = $wgUser->getOption('stubthreshold') ;
- if ( $threshold > 0 ) {
- $dbr = wfGetDB( DB_SLAVE );
- $s = $dbr->selectRow(
- array( 'page' ),
- array( 'page_len',
- 'page_namespace',
- 'page_is_redirect' ),
- array( 'page_id' => $aid ), $fname ) ;
- if ( $s !== false ) {
- $size = $s->page_len;
- if ( $s->page_is_redirect OR $s->page_namespace != NS_MAIN ) {
- $size = $threshold*2 ; # Really big
- }
- } else {
- $size = $threshold*2 ; # Really big
+ $stub = false;
+ if ( $nt->isContentPage() ) {
+ $threshold = $wgUser->getOption('stubthreshold');
+ if ( $threshold > 0 ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $s = $dbr->selectRow(
+ array( 'page' ),
+ array( 'page_len',
+ 'page_is_redirect' ),
+ array( 'page_id' => $aid ), $fname ) ;
+ $stub = ( $s !== false && !$s->page_is_redirect &&
+ $s->page_len < $threshold );
}
- } else {
- $size = 1 ;
}
- if ( $size < $threshold ) {
+ if ( $stub ) {
$retVal = $this->makeStubLinkObj( $nt, $text, $query, $trail, $prefix );
} else {
$retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
@@ -324,7 +337,9 @@ class Linker {
$fname = 'Linker::makeBrokenLinkObj';
wfProfileIn( $fname );
- if ( '' == $query ) {
+ if( $nt->getNamespace() == NS_SPECIAL ) {
+ $q = $query;
+ } else if ( '' == $query ) {
$q = 'action=edit';
} else {
$q = 'action=edit&'.$query;
@@ -418,42 +433,118 @@ class Linker {
return $s;
}
- /** @todo document */
- function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
- $thumb = false, $manual_thumb = '', $valign = '' )
+ /**
+ * Creates the HTML source for images
+ * @deprecated use makeImageLink2
+ *
+ * @param object $title
+ * @param string $label label text
+ * @param string $alt alt text
+ * @param string $align horizontal alignment: none, left, center, right)
+ * @param array $handlerParams Parameters to be passed to the media handler
+ * @param boolean $framed shows image in original size in a frame
+ * @param boolean $thumb shows image as thumbnail in a frame
+ * @param string $manualthumb image name for the manual thumbnail
+ * @param string $valign vertical alignment: baseline, sub, super, top, text-top, middle, bottom, text-bottom
+ * @return string
+ */
+ function makeImageLinkObj( $title, $label, $alt, $align = '', $handlerParams = array(), $framed = false,
+ $thumb = false, $manualthumb = '', $valign = '', $time = false )
{
- global $wgContLang, $wgUser, $wgThumbLimits;
-
- $img = new Image( $nt );
+ $frameParams = array( 'alt' => $alt, 'caption' => $label );
+ if ( $align ) {
+ $frameParams['align'] = $align;
+ }
+ if ( $framed ) {
+ $frameParams['framed'] = true;
+ }
+ if ( $thumb ) {
+ $frameParams['thumbnail'] = true;
+ }
+ if ( $manualthumb ) {
+ $frameParams['manualthumb'] = $manualthumb;
+ }
+ if ( $valign ) {
+ $frameParams['valign'] = $valign;
+ }
+ $file = wfFindFile( $title, $time );
+ return $this->makeImageLink2( $title, $file, $frameParams, $handlerParams );
+ }
- if ( !$img->allowInlineDisplay() && $img->exists() ) {
- return $this->makeKnownLinkObj( $nt );
+ /**
+ * Make an image link
+ * @param Title $title Title object
+ * @param File $file File object, or false if it doesn't exist
+ *
+ * @param array $frameParams Associative array of parameters external to the media handler.
+ * Boolean parameters are indicated by presence or absence, the value is arbitrary and
+ * will often be false.
+ * thumbnail If present, downscale and frame
+ * manualthumb Image name to use as a thumbnail, instead of automatic scaling
+ * framed Shows image in original size in a frame
+ * frameless Downscale but don't frame
+ * upright If present, tweak default sizes for portrait orientation
+ * upright_factor Fudge factor for "upright" tweak (default 0.75)
+ * border If present, show a border around the image
+ * align Horizontal alignment (left, right, center, none)
+ * valign Vertical alignment (baseline, sub, super, top, text-top, middle,
+ * bottom, text-bottom)
+ * alt Alternate text for image (i.e. alt attribute). Plain text.
+ * caption HTML for image caption.
+ *
+ * @param array $handlerParams Associative array of media handler parameters, to be passed
+ * to transform(). Typical keys are "width" and "page".
+ */
+ function makeImageLink2( Title $title, $file, $frameParams = array(), $handlerParams = array() ) {
+ global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright;
+ if ( $file && !$file->allowInlineDisplay() ) {
+ wfDebug( __METHOD__.': '.$title->getPrefixedDBkey()." does not allow inline display\n" );
+ return $this->makeKnownLinkObj( $title );
}
- $error = $prefix = $postfix = '';
- $page = isset( $params['page'] ) ? $params['page'] : false;
+ // Shortcuts
+ $fp =& $frameParams;
+ $hp =& $handlerParams;
- if ( 'center' == $align )
+ // Clean up parameters
+ $page = isset( $hp['page'] ) ? $hp['page'] : false;
+ if ( !isset( $fp['align'] ) ) $fp['align'] = '';
+ if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
+
+ $prefix = $postfix = '';
+
+ if ( 'center' == $fp['align'] )
{
$prefix = '<div class="center">';
$postfix = '</div>';
- $align = 'none';
+ $fp['align'] = 'none';
}
+ if ( $file && !isset( $hp['width'] ) ) {
+ $hp['width'] = $file->getWidth( $page );
- if ( !isset( $params['width'] ) ) {
- $params['width'] = $img->getWidth( $page );
- if( $thumb || $framed ) {
+ if( isset( $fp['thumbnail'] ) || isset( $fp['framed'] ) || isset( $fp['frameless'] ) || !$hp['width'] ) {
$wopt = $wgUser->getOption( 'thumbsize' );
if( !isset( $wgThumbLimits[$wopt] ) ) {
$wopt = User::getDefaultOption( 'thumbsize' );
}
- $params['width'] = min( $params['width'], $wgThumbLimits[$wopt] );
+ // Reduce width for upright images when parameter 'upright' is used
+ if ( isset( $fp['upright'] ) && $fp['upright'] == 0 ) {
+ $fp['upright'] = $wgThumbUpright;
+ }
+ // Use width which is smaller: real image width or user preference width
+ // For caching health: If width scaled down due to upright parameter, round to full __0 pixel to avoid the creation of a lot of odd thumbs
+ $prefWidth = isset( $fp['upright'] ) ?
+ round( $wgThumbLimits[$wopt] * $fp['upright'], -1 ) :
+ $wgThumbLimits[$wopt];
+ if ( $hp['width'] <= 0 || $prefWidth < $hp['width'] ) {
+ $hp['width'] = $prefWidth;
+ }
}
}
- if ( $thumb || $framed ) {
+ if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) {
# Create a thumbnail. Alignment depends on language
# writing direction, # right aligned for left-to-right-
@@ -462,159 +553,174 @@ class Linker {
#
# If thumbnail width has not been provided, it is set
# to the default user option as specified in Language*.php
- if ( $align == '' ) {
- $align = $wgContLang->isRTL() ? 'left' : 'right';
+ if ( $fp['align'] == '' ) {
+ $fp['align'] = $wgContLang->isRTL() ? 'left' : 'right';
}
- return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
+ return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp ).$postfix;
}
- if ( $params['width'] && $img->exists() ) {
+ if ( $file && $hp['width'] ) {
# Create a resized image, without the additional thumbnail features
- $thumb = $img->transform( $params );
+ $thumb = $file->transform( $hp );
} else {
$thumb = false;
}
- if ( $page ) {
- $query = 'page=' . urlencode( $page );
- } else {
- $query = '';
- }
- $u = $nt->getLocalURL( $query );
- $imgAttribs = array(
- 'alt' => $alt,
- 'longdesc' => $u
- );
- if ( $valign ) {
- $imgAttribs['style'] = "vertical-align: $valign";
- }
- $linkAttribs = array(
- 'href' => $u,
- 'class' => 'image',
- 'title' => $alt
- );
-
if ( !$thumb ) {
- $s = $this->makeBrokenImageLinkObj( $img->getTitle() );
+ $s = $this->makeBrokenImageLinkObj( $title );
} else {
- $s = $thumb->toHtml( $imgAttribs, $linkAttribs );
+ $s = $thumb->toHtml( array(
+ 'desc-link' => true,
+ 'alt' => $fp['alt'],
+ 'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false ,
+ 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ) );
}
- if ( '' != $align ) {
- $s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
+ if ( '' != $fp['align'] ) {
+ $s = "<div class=\"float{$fp['align']}\"><span>{$s}</span></div>";
}
return str_replace("\n", ' ',$prefix.$s.$postfix);
}
/**
* Make HTML for a thumbnail including image, border and caption
- * $img is an Image object
+ * @param Title $title
+ * @param File $file File object or false if it doesn't exist
*/
- function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
+ function makeThumbLinkObj( Title $title, $file, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manualthumb = "" ) {
+ $frameParams = array(
+ 'alt' => $alt,
+ 'caption' => $label,
+ 'align' => $align
+ );
+ if ( $framed ) $frameParams['framed'] = true;
+ if ( $manualthumb ) $frameParams['manualthumb'] = $manualthumb;
+ return $this->makeThumbLink2( $title, $file, $frameParams, $params );
+ }
+
+ function makeThumbLink2( Title $title, $file, $frameParams = array(), $handlerParams = array() ) {
global $wgStylePath, $wgContLang;
- $thumbUrl = '';
- $error = '';
+ $exists = $file && $file->exists();
+
+ # Shortcuts
+ $fp =& $frameParams;
+ $hp =& $handlerParams;
- $page = isset( $params['page'] ) ? $params['page'] : false;
+ $page = isset( $hp['page'] ) ? $hp['page'] : false;
+ if ( !isset( $fp['align'] ) ) $fp['align'] = 'right';
+ if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
+ if ( !isset( $fp['caption'] ) ) $fp['caption'] = '';
- if ( empty( $params['width'] ) ) {
- $params['width'] = 180;
+ if ( empty( $hp['width'] ) ) {
+ // Reduce width for upright images when parameter 'upright' is used
+ $hp['width'] = isset( $fp['upright'] ) ? 130 : 180;
}
$thumb = false;
- if ( $manual_thumb != '' ) {
- # Use manually specified thumbnail
- $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
- if( $manual_title ) {
- $manual_img = new Image( $manual_title );
- $thumb = $manual_img->getUnscaledThumb();
- }
- } elseif ( $framed ) {
- // Use image dimensions, don't scale
- $thumb = $img->getUnscaledThumb( $page );
- } else {
- $thumb = $img->transform( $params );
- }
- if ( $thumb ) {
- $outerWidth = $thumb->getWidth() + 2;
+ if ( !$exists ) {
+ $outerWidth = $hp['width'] + 2;
} else {
- $outerWidth = $params['width'] + 2;
+ if ( isset( $fp['manualthumb'] ) ) {
+ # Use manually specified thumbnail
+ $manual_title = Title::makeTitleSafe( NS_IMAGE, $fp['manualthumb'] );
+ if( $manual_title ) {
+ $manual_img = wfFindFile( $manual_title );
+ if ( $manual_img ) {
+ $thumb = $manual_img->getUnscaledThumb();
+ } else {
+ $exists = false;
+ }
+ }
+ } elseif ( isset( $fp['framed'] ) ) {
+ // Use image dimensions, don't scale
+ $thumb = $file->getUnscaledThumb( $page );
+ } else {
+ # Do not present an image bigger than the source, for bitmap-style images
+ # This is a hack to maintain compatibility with arbitrary pre-1.10 behaviour
+ $srcWidth = $file->getWidth( $page );
+ if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
+ $hp['width'] = $srcWidth;
+ }
+ $thumb = $file->transform( $hp );
+ }
+
+ if ( $thumb ) {
+ $outerWidth = $thumb->getWidth() + 2;
+ } else {
+ $outerWidth = $hp['width'] + 2;
+ }
}
$query = $page ? 'page=' . urlencode( $page ) : '';
- $u = $img->getTitle()->getLocalURL( $query );
+ $url = $title->getLocalURL( $query );
$more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
$magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
$textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
- $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
- if ( !$thumb ) {
- $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
+ $s = "<div class=\"thumb t{$fp['align']}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
+ if( !$exists ) {
+ $s .= $this->makeBrokenImageLinkObj( $title );
$zoomicon = '';
- } elseif( !$img->exists() ) {
- $s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
+ } elseif ( !$thumb ) {
+ $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
$zoomicon = '';
} else {
- $imgAttribs = array(
- 'alt' => $alt,
- 'longdesc' => $u,
- 'class' => 'thumbimage'
- );
- $linkAttribs = array(
- 'href' => $u,
- 'class' => 'internal',
- 'title' => $alt
- );
-
- $s .= $thumb->toHtml( $imgAttribs, $linkAttribs );
- if ( $framed ) {
+ $s .= $thumb->toHtml( array(
+ 'alt' => $fp['alt'],
+ 'img-class' => 'thumbimage',
+ 'desc-link' => true ) );
+ if ( isset( $fp['framed'] ) ) {
$zoomicon="";
} else {
$zoomicon = '<div class="magnify" style="float:'.$magnifyalign.'">'.
- '<a href="'.$u.'" class="internal" title="'.$more.'">'.
+ '<a href="'.$url.'" class="internal" title="'.$more.'">'.
'<img src="'.$wgStylePath.'/common/images/magnify-clip.png" ' .
'width="15" height="11" alt="" /></a></div>';
}
}
- $s .= ' <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$label."</div></div></div>";
+ $s .= ' <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$fp['caption']."</div></div></div>";
return str_replace("\n", ' ', $s);
}
/**
- * Pass a title object, not a title string
+ * Make a "broken" link to an image
+ *
+ * @param Title $title Image title
+ * @param string $text Link label
+ * @param string $query Query string
+ * @param string $trail Link trail
+ * @param string $prefix Link prefix
+ * @return string
*/
- function makeBrokenImageLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
- # Fail gracefully
- if ( ! isset($nt) ) {
- # throw new MWException();
+ public function makeBrokenImageLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' ) {
+ global $wgEnableUploads;
+ if( $title instanceof Title ) {
+ wfProfileIn( __METHOD__ );
+ if( $wgEnableUploads ) {
+ $upload = SpecialPage::getTitleFor( 'Upload' );
+ if( $text == '' )
+ $text = htmlspecialchars( $title->getPrefixedText() );
+ $q = 'wpDestFile=' . $title->getPartialUrl();
+ if( $query != '' )
+ $q .= '&' . $query;
+ list( $inside, $trail ) = self::splitTrail( $trail );
+ $style = $this->getInternalLinkAttributesObj( $title, $text, 'yes' );
+ wfProfileOut( __METHOD__ );
+ return '<a href="' . $upload->escapeLocalUrl( $q ) . '"'
+ . $style . '>' . $prefix . $text . $inside . '</a>' . $trail;
+ } else {
+ wfProfileOut( __METHOD__ );
+ return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix );
+ }
+ } else {
return "<!-- ERROR -->{$prefix}{$text}{$trail}";
}
-
- $fname = 'Linker::makeBrokenImageLinkObj';
- wfProfileIn( $fname );
-
- $q = 'wpDestFile=' . urlencode( $nt->getDBkey() );
- if ( '' != $query ) {
- $q .= "&$query";
- }
- $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
- $url = $uploadTitle->escapeLocalURL( $q );
-
- if ( '' == $text ) {
- $text = htmlspecialchars( $nt->getPrefixedText() );
- }
- $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" );
- list( $inside, $trail ) = Linker::splitTrail( $trail );
- $s = "<a href=\"{$url}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
-
- wfProfileOut( $fname );
- return $s;
}
- /** @todo document */
- function makeMediaLink( $name, /* wtf?! */ $url, $alt = '' ) {
+ /** @deprecated use Linker::makeMediaLinkObj() */
+ function makeMediaLink( $name, $unused = '', $text = '' ) {
$nt = Title::makeTitleSafe( NS_IMAGE, $name );
- return $this->makeMediaLinkObj( $nt, $alt );
+ return $this->makeMediaLinkObj( $nt, $text );
}
/**
@@ -632,13 +738,13 @@ class Linker {
### HOTFIX. Instead of breaking, return empty string.
return $text;
} else {
- $img = new Image( $title );
- if( $img->exists() ) {
+ $img = wfFindFile( $title );
+ if( $img ) {
$url = $img->getURL();
$class = 'internal';
} else {
$upload = SpecialPage::getTitleFor( 'Upload' );
- $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $img->getName() ) );
+ $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $title->getDbKey() ) );
$class = 'new';
}
$alt = htmlspecialchars( $title->getText() );
@@ -694,15 +800,18 @@ class Linker {
}
/**
- * @param $userId Integer: user id in database.
- * @param $userText String: user name in database.
- * @param $redContribsWhenNoEdits Bool: return a red contribs link when the user had no edits and this is true.
- * @return string HTML fragment with talk and/or block links
+ * Generate standard user tool links (talk, contributions, block link, etc.)
+ *
+ * @param int $userId User identifier
+ * @param string $userText User name or IP address
+ * @param bool $redContribsWhenNoEdits Should the contributions link be red if the user has no edits?
+ * @param int $flags Customisation flags (e.g. self::TOOL_LINKS_NOBLOCK)
+ * @return string
*/
- public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false ) {
+ public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0 ) {
global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
$talkable = !( $wgDisableAnonTalk && 0 == $userId );
- $blockable = ( $wgSysopUserBans || 0 == $userId );
+ $blockable = ( $wgSysopUserBans || 0 == $userId ) && !$flags & self::TOOL_LINKS_NOBLOCK;
$items = array();
if( $talkable ) {
@@ -711,7 +820,7 @@ class Linker {
if( $userId ) {
// check if the user has an edit
if( $redContribsWhenNoEdits && User::edits( $userId ) == 0 ) {
- $style = "class='new'";
+ $style = " class='new'";
} else {
$style = '';
}
@@ -818,16 +927,33 @@ class Linker {
function formatComment($comment, $title = NULL, $local = false) {
wfProfileIn( __METHOD__ );
- global $wgContLang;
+ # Sanitize text a bit:
$comment = str_replace( "\n", " ", $comment );
$comment = htmlspecialchars( $comment );
- # The pattern for autogen comments is / * foo * /, which makes for
- # some nasty regex.
- # We look for all comments, match any text before and after the comment,
- # add a separator where needed and format the comment itself with CSS
+ # Render autocomments and make links:
+ $comment = $this->formatAutoComments( $comment, $title, $local );
+ $comment = $this->formatLinksInComment( $comment );
+
+ wfProfileOut( __METHOD__ );
+ return $comment;
+ }
+
+ /**
+ * The pattern for autogen comments is / * foo * /, which makes for
+ * some nasty regex.
+ * We look for all comments, match any text before and after the comment,
+ * add a separator where needed and format the comment itself with CSS
+ * Called by Linker::formatComment.
+ *
+ * @param $comment Comment text
+ * @param $title An optional title object used to links to sections
+ *
+ * @todo Document the $local parameter.
+ */
+ private function formatAutocomments( $comment, $title = NULL, $local = false ) {
$match = array();
- while (preg_match('/(.*)\/\*\s*(.*?)\s*\*\/(.*)/', $comment,$match)) {
+ while (preg_match('!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment,$match)) {
$pre=$match[1];
$auto=$match[2];
$post=$match[3];
@@ -859,10 +985,23 @@ class Linker {
$comment=$pre.$auto.$post;
}
- # format regular and media links - all other wiki formatting
- # is ignored
+ return $comment;
+ }
+
+ /**
+ * Formats wiki links and media links in text; all other wiki formatting
+ * is ignored
+ *
+ * @param string $comment Text to format links in
+ * @return string
+ */
+ public function formatLinksInComment( $comment ) {
+ global $wgContLang;
+
$medians = '(?:' . preg_quote( Namespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
$medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA ), '/' ) . '):';
+
+ $match = array();
while(preg_match('/\[\[:?(.*?)(\|(.*?))*\]\](.*)$/',$comment,$match)) {
# Handle link renaming [[foo|text]] will show link as "text"
if( "" != $match[3] ) {
@@ -889,7 +1028,7 @@ class Linker {
}
$comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 );
}
- wfProfileOut( __METHOD__ );
+
return $comment;
}
@@ -914,7 +1053,7 @@ class Linker {
return " <span class=\"comment\">($formatted)</span>";
}
}
-
+
/**
* Wrap and format the given revision's comment block, if the current
* user is allowed to view it.
@@ -981,31 +1120,66 @@ class Linker {
. "</script>\n";
}
- /** @todo document */
+ /**
+ * Used to generate section edit links that point to "other" pages
+ * (sections that are really part of included pages).
+ *
+ * @param $title Title string.
+ * @param $section Integer: section number.
+ */
public function editSectionLinkForOther( $title, $section ) {
- global $wgContLang;
-
$title = Title::newFromText( $title );
- $editurl = '&section='.$section;
- $url = $this->makeKnownLinkObj( $title, wfMsg('editsection'), 'action=edit'.$editurl );
-
- return "<span class=\"editsection\">[".$url."]</span>";
-
+ return $this->doEditSectionLink( $title, $section, '', 'EditSectionLinkForOther' );
}
/**
- * @param $title Title object.
+ * @param $nt Title object.
* @param $section Integer: section number.
* @param $hint Link String: title, or default if omitted or empty
*/
- public function editSectionLink( $nt, $section, $hint='' ) {
- global $wgContLang;
+ public function editSectionLink( Title $nt, $section, $hint='' ) {
+ if( $hint != '' ) {
+ $hint = wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) );
+ $hint = " title=\"$hint\"";
+ }
+ return $this->doEditSectionLink( $nt, $section, $hint, 'EditSectionLink' );
+ }
+ /**
+ * Implement editSectionLink and editSectionLinkForOther.
+ *
+ * @param $nt Title object
+ * @param $section Integer, section number
+ * @param $hint String, for HTML title attribute
+ * @param $hook String, name of hook to run
+ * @return String, HTML to use for edit link
+ */
+ protected function doEditSectionLink( Title $nt, $section, $hint, $hook ) {
+ global $wgContLang;
$editurl = '&section='.$section;
- $hint = ( $hint=='' ) ? '' : ' title="' . wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) ) . '"';
- $url = $this->makeKnownLinkObj( $nt, wfMsg('editsection'), 'action=edit'.$editurl, '', '', '', $hint );
+ $url = $this->makeKnownLinkObj(
+ $nt,
+ wfMsg('editsection'),
+ 'action=edit'.$editurl,
+ '', '', '', $hint
+ );
+ $result = null;
- return "<span class=\"editsection\">[".$url."]</span>";
+ // The two hooks have slightly different interfaces . . .
+ if( $hook == 'EditSectionLink' ) {
+ wfRunHooks( $hook, array( &$this, $nt, $section, $hint, $url, &$result ) );
+ } elseif( $hook == 'EditSectionLinkForOther' ) {
+ wfRunHooks( $hook, array( &$this, $nt, $section, $url, &$result ) );
+ }
+
+ // For reverse compatibility, add the brackets *after* the hook is run,
+ // and even add them to hook-provided text.
+ if( is_null( $result ) ) {
+ $result = wfMsg( 'editsection-brackets', $url );
+ } else {
+ $result = wfMsg( 'editsection-brackets', $result );
+ }
+ return "<span class=\"editsection\">$result</span>";
}
/**
@@ -1061,15 +1235,28 @@ class Linker {
* @param Revision $rev
*/
function generateRollback( $rev ) {
- global $wgUser, $wgRequest;
+ return '<span class="mw-rollback-link">['
+ . $this->buildRollbackLink( $rev )
+ . ']</span>';
+ }
+
+ /**
+ * Build a raw rollback link, useful for collections of "tool" links
+ *
+ * @param Revision $rev
+ * @return string
+ */
+ public function buildRollbackLink( $rev ) {
+ global $wgRequest, $wgUser;
$title = $rev->getTitle();
-
- $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
- $extraRollback .= '&token=' . urlencode(
- $wgUser->editToken( array( $title->getPrefixedText(), $rev->getUserText() ) ) );
- return '<span class="mw-rollback-link">['. $this->makeKnownLinkObj( $title,
- wfMsg('rollbacklink'),
- 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extraRollback ) .']</span>';
+ $extra = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
+ $extra .= '&token=' . urlencode( $wgUser->editToken( array( $title->getPrefixedText(),
+ $rev->getUserText() ) ) );
+ return $this->makeKnownLinkObj(
+ $title,
+ wfMsgHtml( 'rollbacklink' ),
+ 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extra
+ );
}
/**
@@ -1133,28 +1320,7 @@ class Linker {
*/
public function formatSize( $size ) {
global $wgLang;
- // For small sizes no decimal places necessary
- $round = 0;
- if( $size > 1024 ) {
- $size = $size / 1024;
- if( $size > 1024 ) {
- $size = $size / 1024;
- // For MB and bigger two decimal places are smarter
- $round = 2;
- if( $size > 1024 ) {
- $size = $size / 1024;
- $msg = 'size-gigabytes';
- } else {
- $msg = 'size-megabytes';
- }
- } else {
- $msg = 'size-kilobytes';
- }
- } else {
- $msg = 'size-bytes';
- }
- $size = round( $size, $round );
- return wfMsgHtml( $msg, $wgLang->formatNum( $size ) );
+ return htmlspecialchars( $wgLang->formatSize( $size ) );
}
/**
@@ -1208,4 +1374,6 @@ class Linker {
}
}
-?>
+
+
+
diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php
index 856c665d..9bcd9d67 100644
--- a/includes/LinksUpdate.php
+++ b/includes/LinksUpdate.php
@@ -9,7 +9,7 @@ class LinksUpdate {
/**@{{
* @private
*/
- var $mId, //!< Page ID of the article linked from
+ var $mId, //!< Page ID of the article linked from
$mTitle, //!< Title object of the article linked from
$mLinks, //!< Map of title strings to IDs for the links in the document
$mImages, //!< DB keys of the images used, in the array key only
@@ -24,10 +24,10 @@ class LinksUpdate {
/**
* Constructor
- * Initialize private variables
- * @param $title Integer: FIXME
- * @param $parserOutput FIXME
- * @param $recursive Boolean: FIXME, default 'true'.
+ *
+ * @param Title $title Title of the page we're updating
+ * @param ParserOutput $parserOutput Output from a full parse of this page
+ * @param bool $recursive Queue jobs for recursive updates?
*/
function LinksUpdate( $title, $parserOutput, $recursive = true ) {
global $wgAntiLockFlags;
@@ -64,6 +64,8 @@ class LinksUpdate {
}
$this->mRecursive = $recursive;
+
+ wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
}
/**
@@ -188,7 +190,7 @@ class LinksUpdate {
break;
}
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $jobs[] = Job::factory( 'refreshLinks', $title );
+ $jobs[] = new RefreshLinksJob( $title, '' );
}
Job::batchInsert( $jobs );
}
@@ -594,4 +596,4 @@ class LinksUpdate {
return $arr;
}
}
-?>
+
diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php
index 4ebe26c7..65a6d5a6 100644
--- a/includes/LoadBalancer.php
+++ b/includes/LoadBalancer.php
@@ -646,4 +646,4 @@ class LoadBalancer {
}
}
-?>
+
diff --git a/includes/LogPage.php b/includes/LogPage.php
index af03bbba..8982b59f 100644
--- a/includes/LogPage.php
+++ b/includes/LogPage.php
@@ -55,42 +55,52 @@ class LogPage {
$dbw = wfGetDB( DB_MASTER );
$uid = $wgUser->getID();
+ $log_id = $dbw->nextSequenceValue( 'log_log_id_seq' );
$this->timestamp = $now = wfTimestampNow();
- $dbw->insert( 'logging',
- array(
- 'log_type' => $this->type,
- 'log_action' => $this->action,
- 'log_timestamp' => $dbw->timestamp( $now ),
- 'log_user' => $uid,
- 'log_namespace' => $this->target->getNamespace(),
- 'log_title' => $this->target->getDBkey(),
- 'log_comment' => $this->comment,
- 'log_params' => $this->params
- ), $fname
+ $data = array(
+ 'log_type' => $this->type,
+ 'log_action' => $this->action,
+ 'log_timestamp' => $dbw->timestamp( $now ),
+ 'log_user' => $uid,
+ 'log_namespace' => $this->target->getNamespace(),
+ 'log_title' => $this->target->getDBkey(),
+ 'log_comment' => $this->comment,
+ 'log_params' => $this->params
);
+ # log_id doesn't exist on Wikimedia servers yet, and it's a tricky
+ # schema update to do. Hack it for now to ignore the field on MySQL.
+ if ( !is_null( $log_id ) ) {
+ $data['log_id'] = $log_id;
+ }
+ $dbw->insert( 'logging', $data, $fname );
+
# And update recentchanges
if ( $this->updateRecentChanges ) {
$titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
- $rcComment = $this->actionText;
- if( '' != $this->comment ) {
- if ($rcComment == '')
- $rcComment = $this->comment;
- else
- $rcComment .= ': ' . $this->comment;
- }
-
+ $rcComment = $this->getRcComment();
RecentChange::notifyLog( $now, $titleObj, $wgUser, $rcComment, '',
$this->type, $this->action, $this->target, $this->comment, $this->params );
}
return true;
}
+ public function getRcComment() {
+ $rcComment = $this->actionText;
+ if( '' != $this->comment ) {
+ if ($rcComment == '')
+ $rcComment = $this->comment;
+ else
+ $rcComment .= ': ' . $this->comment;
+ }
+ return $rcComment;
+ }
+
/**
* @static
*/
- function validTypes() {
+ public static function validTypes() {
global $wgLogTypes;
return $wgLogTypes;
}
@@ -98,7 +108,7 @@ class LogPage {
/**
* @static
*/
- function isLogType( $type ) {
+ public static function isLogType( $type ) {
return in_array( $type, LogPage::validTypes() );
}
@@ -120,7 +130,7 @@ class LogPage {
* @todo handle missing log types
* @static
*/
- function logHeader( $type ) {
+ static function logHeader( $type ) {
global $wgLogHeaders;
return wfMsg( $wgLogHeaders[$type] );
}
@@ -128,7 +138,7 @@ class LogPage {
/**
* @static
*/
- function actionText( $type, $action, $title = NULL, $skin = NULL, $params = array(), $filterWikilinks=false, $translate=false ) {
+ static function actionText( $type, $action, $title = NULL, $skin = NULL, $params = array(), $filterWikilinks=false, $translate=false ) {
global $wgLang, $wgContLang, $wgLogActions;
$key = "$type/$action";
@@ -145,14 +155,17 @@ class LogPage {
switch( $type ) {
case 'move':
$titleLink = $skin->makeLinkObj( $title, $title->getPrefixedText(), 'redirect=no' );
- $params[0] = $skin->makeLinkObj( Title::newFromText( $params[0] ), $params[0] );
+ $params[0] = $skin->makeLinkObj( Title::newFromText( $params[0] ), htmlspecialchars( $params[0] ) );
break;
case 'block':
if( substr( $title->getText(), 0, 1 ) == '#' ) {
$titleLink = $title->getText();
} else {
- $titleLink = $skin->makeLinkObj( $title, $title->getText() );
- $titleLink .= ' (' . $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Contributions', $title->getDBkey() ), wfMsg( 'contribslink' ) ) . ')';
+ // TODO: Store the user identifier in the parameters
+ // to make this faster for future log entries
+ $id = User::idFromName( $title->getText() );
+ $titleLink = $skin->userLink( $id, $title->getText() )
+ . $skin->userToolLinks( $id, $title->getText(), false, Linker::TOOL_LINKS_NOBLOCK );
}
break;
case 'rights':
@@ -233,7 +246,7 @@ class LogPage {
* Create a blob from a parameter array
* @static
*/
- function makeParamBlob( $params ) {
+ static function makeParamBlob( $params ) {
return implode( "\n", $params );
}
@@ -241,7 +254,7 @@ class LogPage {
* Extract a parameter array from a blob
* @static
*/
- function extractParams( $blob ) {
+ static function extractParams( $blob ) {
if ( $blob === '' ) {
return array();
} else {
@@ -285,4 +298,4 @@ class LogPage {
}
-?>
+
diff --git a/includes/MacBinary.php b/includes/MacBinary.php
index 2f6ad4f4..da357e52 100644
--- a/includes/MacBinary.php
+++ b/includes/MacBinary.php
@@ -100,7 +100,7 @@ class MacBinary {
fseek( $this->handle, 0 );
$head = fread( $this->handle, 128 );
- $this->hexdump( $head );
+ #$this->hexdump( $head );
if( strlen( $head ) < 128 ) {
wfDebug( "$fname: couldn't read full MacBinary header\n" );
@@ -268,4 +268,4 @@ class MacBinary {
}
}
-?>
+
diff --git a/includes/MagicWord.php b/includes/MagicWord.php
index bf72a0c8..f7a9400d 100644
--- a/includes/MagicWord.php
+++ b/includes/MagicWord.php
@@ -86,7 +86,6 @@ class MagicWord {
'subjectpagename',
'subjectpagenamee',
'numberofusers',
- 'rawsuffix',
'newsectionlink',
'numberofpages',
'currentversion',
@@ -387,6 +386,181 @@ class MagicWord {
function isCaseSensitive() {
return $this->mCaseSensitive;
}
+
+ function getId() {
+ return $this->mId;
+ }
}
-?>
+/**
+ * Class for handling an array of magic words
+ */
+class MagicWordArray {
+ var $names = array();
+ var $hash;
+ var $baseRegex, $regex;
+
+ function __construct( $names = array() ) {
+ $this->names = $names;
+ }
+
+ /**
+ * Add a magic word by name
+ */
+ public function add( $name ) {
+ global $wgContLang;
+ $this->names[] = $name;
+ $this->hash = $this->baseRegex = $this->regex = null;
+ }
+
+ /**
+ * Add a number of magic words by name
+ */
+ public function addArray( $names ) {
+ $this->names = array_merge( $this->names, array_values( $names ) );
+ $this->hash = $this->baseRegex = $this->regex = null;
+ }
+
+ /**
+ * Get a 2-d hashtable for this array
+ */
+ function getHash() {
+ if ( is_null( $this->hash ) ) {
+ global $wgContLang;
+ $this->hash = array( 0 => array(), 1 => array() );
+ foreach ( $this->names as $name ) {
+ $magic = MagicWord::get( $name );
+ $case = intval( $magic->isCaseSensitive() );
+ foreach ( $magic->getSynonyms() as $syn ) {
+ if ( !$case ) {
+ $syn = $wgContLang->lc( $syn );
+ }
+ $this->hash[$case][$syn] = $name;
+ }
+ }
+ }
+ return $this->hash;
+ }
+
+ /**
+ * Get the base regex
+ */
+ function getBaseRegex() {
+ if ( is_null( $this->baseRegex ) ) {
+ $this->baseRegex = array( 0 => '', 1 => '' );
+ foreach ( $this->names as $name ) {
+ $magic = MagicWord::get( $name );
+ $case = intval( $magic->isCaseSensitive() );
+ foreach ( $magic->getSynonyms() as $i => $syn ) {
+ $group = "(?P<{$i}_{$name}>" . preg_quote( $syn, '/' ) . ')';
+ if ( $this->baseRegex[$case] === '' ) {
+ $this->baseRegex[$case] = $group;
+ } else {
+ $this->baseRegex[$case] .= '|' . $group;
+ }
+ }
+ }
+ }
+ return $this->baseRegex;
+ }
+
+ /**
+ * Get an unanchored regex
+ */
+ function getRegex() {
+ if ( is_null( $this->regex ) ) {
+ $base = $this->getBaseRegex();
+ $this->regex = array( '', '' );
+ if ( $this->baseRegex[0] !== '' ) {
+ $this->regex[0] = "/{$base[0]}/iuS";
+ }
+ if ( $this->baseRegex[1] !== '' ) {
+ $this->regex[1] = "/{$base[1]}/S";
+ }
+ }
+ return $this->regex;
+ }
+
+ /**
+ * Get a regex for matching variables
+ */
+ function getVariableRegex() {
+ return str_replace( "\\$1", "(.*?)", $this->getRegex() );
+ }
+
+ /**
+ * Get an anchored regex for matching variables
+ */
+ function getVariableStartToEndRegex() {
+ $base = $this->getBaseRegex();
+ $newRegex = array( '', '' );
+ if ( $base[0] !== '' ) {
+ $newRegex[0] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[0]})$/iuS" );
+ }
+ if ( $base[1] !== '' ) {
+ $newRegex[1] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[1]})$/S" );
+ }
+ return $newRegex;
+ }
+
+ /**
+ * Parse a match array from preg_match
+ */
+ function parseMatch( $m ) {
+ reset( $m );
+ while ( list( $key, $value ) = each( $m ) ) {
+ if ( $key === 0 || $value === '' ) {
+ continue;
+ }
+ $parts = explode( '_', $key, 2 );
+ if ( count( $parts ) != 2 ) {
+ // This shouldn't happen
+ // continue;
+ throw new MWException( __METHOD__ . ': bad parameter name' );
+ }
+ list( /* $synIndex */, $magicName ) = $parts;
+ $paramValue = next( $m );
+ return array( $magicName, $paramValue );
+ }
+ // This shouldn't happen either
+ throw new MWException( __METHOD__.': parameter not found' );
+ return array( false, false );
+ }
+
+ /**
+ * Match some text, with parameter capture
+ * Returns an array with the magic word name in the first element and the
+ * parameter in the second element.
+ * Both elements are false if there was no match.
+ */
+ public function matchVariableStartToEnd( $text ) {
+ global $wgContLang;
+ $regexes = $this->getVariableStartToEndRegex();
+ foreach ( $regexes as $regex ) {
+ if ( $regex !== '' ) {
+ $m = false;
+ if ( preg_match( $regex, $text, $m ) ) {
+ return $this->parseMatch( $m );
+ }
+ }
+ }
+ return array( false, false );
+ }
+
+ /**
+ * Match some text, without parameter capture
+ * Returns the magic word name, or false if there was no capture
+ */
+ public function matchStartToEnd( $text ) {
+ $hash = $this->getHash();
+ if ( isset( $hash[1][$text] ) ) {
+ return $hash[1][$text];
+ }
+ global $wgContLang;
+ $lc = $wgContLang->lc( $text );
+ if ( isset( $hash[0][$lc] ) ) {
+ return $hash[0][$lc];
+ }
+ return false;
+ }
+}
diff --git a/includes/Math.php b/includes/Math.php
index 88934e5f..2771d04c 100644
--- a/includes/Math.php
+++ b/includes/Math.php
@@ -20,8 +20,9 @@ class MathRenderer {
var $mathml = '';
var $conservativeness = 0;
- function __construct( $tex ) {
+ function __construct( $tex, $params=array() ) {
$this->tex = $tex;
+ $this->params = $params;
}
function setOutputMode( $mode ) {
@@ -157,8 +158,8 @@ class MathRenderer {
$dbw = wfGetDB( DB_MASTER );
$dbw->replace( 'math', array( 'math_inputhash' ),
array(
- 'math_inputhash' => $md5_sql,
- 'math_outputhash' => $outmd5_sql,
+ 'math_inputhash' => $dbw->encodeBlob($md5_sql),
+ 'math_outputhash' => $dbw->encodeBlob($outmd5_sql),
'math_html_conservativeness' => $this->conservativeness,
'math_html' => $this->html,
'math_mathml' => $this->mathml,
@@ -186,13 +187,13 @@ class MathRenderer {
$dbr = wfGetDB( DB_SLAVE );
$rpage = $dbr->selectRow( 'math',
array( 'math_outputhash','math_html_conservativeness','math_html','math_mathml' ),
- array( 'math_inputhash' => pack("H32", $this->md5)), # Binary packed, not hex
+ array( 'math_inputhash' => $dbr->encodeBlob(pack("H32", $this->md5))), # Binary packed, not hex
$fname
);
if( $rpage !== false ) {
# Tailing 0x20s can get dropped by the database, add it back on if necessary:
- $xhash = unpack( 'H32md5', $rpage->math_outputhash . " " );
+ $xhash = unpack( 'H32md5', $dbr->decodeBlob($rpage->math_outputhash) . " " );
$this->hash = $xhash ['md5'];
$this->conservativeness = $rpage->math_html_conservativeness;
@@ -233,24 +234,44 @@ class MathRenderer {
*/
function _doRender() {
if( $this->mode == MW_MATH_MATHML && $this->mathml != '' ) {
- return "<math xmlns='http://www.w3.org/1998/Math/MathML'>{$this->mathml}</math>";
+ return Xml::tags( 'math',
+ $this->_attribs( 'math',
+ array( 'xmlns' => 'http://www.w3.org/1998/Math/MathML' ) ),
+ $this->mathml );
}
if (($this->mode == MW_MATH_PNG) || ($this->html == '') ||
(($this->mode == MW_MATH_SIMPLE) && ($this->conservativeness != 2)) ||
(($this->mode == MW_MATH_MODERN || $this->mode == MW_MATH_MATHML) && ($this->conservativeness == 0))) {
return $this->_linkToMathImage();
} else {
- return '<span class="texhtml">'.$this->html.'</span>';
+ return Xml::tags( 'span',
+ $this->_attribs( 'span',
+ array( 'class' => 'texhtml' ) ),
+ $this->html );
}
}
+
+ function _attribs( $tag, $defaults=array(), $overrides=array() ) {
+ $attribs = Sanitizer::validateTagAttributes( $this->params, $tag );
+ $attribs = Sanitizer::mergeAttributes( $defaults, $attribs );
+ $attribs = Sanitizer::mergeAttributes( $attribs, $overrides );
+ return $attribs;
+ }
function _linkToMathImage() {
global $wgMathPath;
- $url = htmlspecialchars( "$wgMathPath/" . substr($this->hash, 0, 1)
+ $url = "$wgMathPath/" . substr($this->hash, 0, 1)
.'/'. substr($this->hash, 1, 1) .'/'. substr($this->hash, 2, 1)
- . "/{$this->hash}.png" );
- $alt = trim(str_replace("\n", ' ', htmlspecialchars( $this->tex )));
- return "<img class='tex' src=\"$url\" alt=\"$alt\" />";
+ . "/{$this->hash}.png";
+
+ return Xml::element( 'img',
+ $this->_attribs(
+ 'img',
+ array(
+ 'class' => 'tex',
+ 'alt' => $this->tex ),
+ array(
+ 'src' => $url ) ) );
}
function _getHashPath() {
@@ -262,11 +283,11 @@ class MathRenderer {
return $path;
}
- public static function renderMath( $tex ) {
+ public static function renderMath( $tex, $params=array() ) {
global $wgUser;
- $math = new MathRenderer( $tex );
+ $math = new MathRenderer( $tex, $params );
$math->setOutputMode( $wgUser->getOption('math'));
return $math->render();
}
}
-?>
+
diff --git a/includes/MediaTransformOutput.php b/includes/MediaTransformOutput.php
index 60057e3a..c6cf9ac2 100644
--- a/includes/MediaTransformOutput.php
+++ b/includes/MediaTransformOutput.php
@@ -1,11 +1,13 @@
<?php
/**
- * Base class for the output of MediaHandler::doTransform() and Image::transform().
+ * Base class for the output of MediaHandler::doTransform() and File::transform().
*
* @addtogroup Media
*/
abstract class MediaTransformOutput {
+ var $file, $width, $height, $url, $page, $path;
+
/**
* Get the width of the output box
*/
@@ -36,12 +38,23 @@ abstract class MediaTransformOutput {
/**
* Fetch HTML for this transform output
- * @param array $attribs Advisory associative array of HTML attributes supplied
- * by the linker. These can be incorporated into the output in any way.
- * @param array $linkAttribs Attributes of a suggested enclosing <a> tag.
- * May be ignored.
+ *
+ * @param array $options Associative array of options. Boolean options
+ * should be indicated with a value of true for true, and false or
+ * absent for false.
+ *
+ * alt Alternate text or caption
+ * desc-link Boolean, show a description link
+ * file-link Boolean, show a file download link
+ * valign vertical-align property, if the output is an inline element
+ * img-class Class applied to the <img> tag, if there is such a tag
+ *
+ * For images, desc-link and file-link are implemented as a click-through. For
+ * sounds and videos, they may be displayed in other ways.
+ *
+ * @return string
*/
- abstract function toHtml( $attribs = array() , $linkAttribs = false );
+ abstract function toHtml( $options = array() );
/**
* This will be overridden to return true in error classes
@@ -60,6 +73,19 @@ abstract class MediaTransformOutput {
return $contents;
}
}
+
+ function getDescLinkAttribs( $alt = false ) {
+ $query = $this->page ? ( 'page=' . urlencode( $this->page ) ) : '';
+ $title = $this->file->getTitle();
+ if ( strval( $alt ) === '' ) {
+ $alt = $title->getText();
+ }
+ return array(
+ 'href' => $this->file->getTitle()->getLocalURL( $query ),
+ 'class' => 'image',
+ 'title' => $alt
+ );
+ }
}
@@ -74,7 +100,8 @@ class ThumbnailImage extends MediaTransformOutput {
* @param string $url URL path to the thumb
* @private
*/
- function ThumbnailImage( $url, $width, $height, $path = false ) {
+ function ThumbnailImage( $file, $url, $width, $height, $path = false, $page = false ) {
+ $this->file = $file;
$this->url = $url;
# These should be integers when they get here.
# If not, there's a bug somewhere. But let's at
@@ -82,28 +109,56 @@ class ThumbnailImage extends MediaTransformOutput {
$this->width = round( $width );
$this->height = round( $height );
$this->path = $path;
+ $this->page = $page;
}
/**
* Return HTML <img ... /> tag for the thumbnail, will include
* width and height attributes and a blank alt text (as required).
+ *
+ * @param array $options Associative array of options. Boolean options
+ * should be indicated with a value of true for true, and false or
+ * absent for false.
*
- * You can set or override additional attributes by passing an
- * associative array of name => data pairs. The data will be escaped
- * for HTML output, so should be in plaintext.
+ * alt Alternate text or caption
+ * desc-link Boolean, show a description link
+ * file-link Boolean, show a file download link
+ * valign vertical-align property, if the output is an inline element
+ * img-class Class applied to the <img> tag, if there is such a tag
*
- * If $linkAttribs is given, the image will be enclosed in an <a> tag.
+ * For images, desc-link and file-link are implemented as a click-through. For
+ * sounds and videos, they may be displayed in other ways.
*
- * @param array $attribs
- * @param array $linkAttribs
* @return string
* @public
*/
- function toHtml( $attribs = array(), $linkAttribs = false ) {
- $attribs['src'] = $this->url;
- $attribs['width'] = $this->width;
- $attribs['height'] = $this->height;
- if( !isset( $attribs['alt'] ) ) $attribs['alt'] = '';
+ function toHtml( $options = array() ) {
+ if ( count( func_get_args() ) == 2 ) {
+ throw new MWException( __METHOD__ .' called in the old style' );
+ }
+
+ $alt = empty( $options['alt'] ) ? '' : $options['alt'];
+ if ( !empty( $options['desc-link'] ) ) {
+ $linkAttribs = $this->getDescLinkAttribs( $alt );
+ } elseif ( !empty( $options['file-link'] ) ) {
+ $linkAttribs = array( 'href' => $this->file->getURL() );
+ } else {
+ $linkAttribs = false;
+ }
+
+ $attribs = array(
+ 'alt' => $alt,
+ 'src' => $this->url,
+ 'width' => $this->width,
+ 'height' => $this->height,
+ 'border' => 0,
+ );
+ if ( !empty( $options['valign'] ) ) {
+ $attribs['style'] = "vertical-align: {$options['valign']}";
+ }
+ if ( !empty( $options['img-class'] ) ) {
+ $attribs['class'] = $options['img-class'];
+ }
return $this->linkWrap( $linkAttribs, Xml::element( 'img', $attribs ) );
}
@@ -130,7 +185,7 @@ class MediaTransformError extends MediaTransformOutput {
$this->path = false;
}
- function toHtml( $attribs = array(), $linkAttribs = false ) {
+ function toHtml( $options = array() ) {
return "<table class=\"MediaTransformError\" style=\"" .
"width: {$this->width}px; height: {$this->height}px;\"><tr><td>" .
$this->htmlMsg .
@@ -158,9 +213,10 @@ class MediaTransformError extends MediaTransformOutput {
class TransformParameterError extends MediaTransformError {
function __construct( $params ) {
parent::__construct( 'thumbnail_error',
- max( @$params['width'], 180 ), max( @$params['height'], 180 ),
+ max( isset( $params['width'] ) ? $params['width'] : 0, 180 ),
+ max( isset( $params['height'] ) ? $params['height'] : 0, 180 ),
wfMsg( 'thumbnail_invalid_params' ) );
}
}
-?>
+
diff --git a/includes/MemcachedSessions.php b/includes/MemcachedSessions.php
index 3bcf5535..3b248cf0 100644
--- a/includes/MemcachedSessions.php
+++ b/includes/MemcachedSessions.php
@@ -69,4 +69,4 @@ function memsess_gc( $maxlifetime ) {
session_set_save_handler( 'memsess_open', 'memsess_close', 'memsess_read', 'memsess_write', 'memsess_destroy', 'memsess_gc' );
-?>
+
diff --git a/includes/MessageCache.php b/includes/MessageCache.php
index e2cbf5f6..10c95a7e 100644
--- a/includes/MessageCache.php
+++ b/includes/MessageCache.php
@@ -23,6 +23,7 @@ class MessageCache {
var $mExtensionMessages = array();
var $mInitialised = false;
var $mDeferred = true;
+ var $mAllMessagesLoaded;
function __construct( &$memCached, $useDB, $expiry, $memcPrefix) {
wfProfileIn( __METHOD__ );
@@ -627,7 +628,6 @@ class MessageCache {
/**
* Add a 2-D array of messages by lang. Useful for extensions.
- * Introduced in 1.9. Please do not use it for now, for backwards compatibility.
*
* @param array $messages The array to be added
*/
@@ -670,12 +670,39 @@ class MessageCache {
}
}
- static function loadAllMessages() {
+ function loadAllMessages() {
+ global $wgExtensionMessagesFiles;
+ if ( $this->mAllMessagesLoaded ) {
+ return;
+ }
+ $this->mAllMessagesLoaded = true;
+
# Some extensions will load their messages when you load their class file
wfLoadAllExtensions();
# Others will respond to this hook
wfRunHooks( 'LoadAllMessages' );
+ # Some register their messages in $wgExtensionMessagesFiles
+ foreach ( $wgExtensionMessagesFiles as $name => $file ) {
+ if ( $file ) {
+ $this->loadMessagesFile( $file );
+ $wgExtensionMessagesFiles[$name] = false;
+ }
+ }
# Still others will respond to neither, they are EVIL. We sometimes need to know!
}
+
+ /**
+ * Load messages from a given file
+ */
+ function loadMessagesFile( $filename ) {
+ $magicWords = false;
+ require( $filename );
+ $this->addMessagesByLang( $messages );
+
+ if ( $magicWords !== false ) {
+ global $wgContLang;
+ $wgContLang->addMagicWordsByLang( $magicWords );
+ }
+ }
}
-?>
+
diff --git a/includes/Metadata.php b/includes/Metadata.php
index b995b223..f5b0b247 100644
--- a/includes/Metadata.php
+++ b/includes/Metadata.php
@@ -365,4 +365,4 @@ function getKnownLicenses() {
return $knownLicenses;
}
-?>
+
diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php
index db35535d..264a3595 100644
--- a/includes/MimeMagic.php
+++ b/includes/MimeMagic.php
@@ -111,56 +111,67 @@ class MimeMagic {
* --- load mime.types ---
*/
- global $wgMimeTypeFile;
+ global $wgMimeTypeFile, $IP;
- $types= MM_WELL_KNOWN_MIME_TYPES;
+ $types = MM_WELL_KNOWN_MIME_TYPES;
- if ($wgMimeTypeFile) {
- if (is_file($wgMimeTypeFile) and is_readable($wgMimeTypeFile)) {
- wfDebug("MimeMagic::MimeMagic: loading mime types from $wgMimeTypeFile\n");
-
- $types.= "\n";
- $types.= file_get_contents($wgMimeTypeFile);
+ if ( $wgMimeTypeFile == 'includes/mime.types' ) {
+ $wgMimeTypeFile = "$IP/$wgMimeTypeFile";
+ }
+
+ if ( $wgMimeTypeFile ) {
+ if ( is_file( $wgMimeTypeFile ) and is_readable( $wgMimeTypeFile ) ) {
+ wfDebug( __METHOD__.": loading mime types from $wgMimeTypeFile\n" );
+ $types .= "\n";
+ $types .= file_get_contents( $wgMimeTypeFile );
+ } else {
+ wfDebug( __METHOD__.": can't load mime types from $wgMimeTypeFile\n" );
}
- else wfDebug("MimeMagic::MimeMagic: can't load mime types from $wgMimeTypeFile\n");
+ } else {
+ wfDebug( __METHOD__.": no mime types file defined, using build-ins only.\n" );
}
- else wfDebug("MimeMagic::MimeMagic: no mime types file defined, using build-ins only.\n");
- $types= str_replace(array("\r\n","\n\r","\n\n","\r\r","\r"),"\n",$types);
- $types= str_replace("\t"," ",$types);
+ $types = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $types );
+ $types = str_replace( "\t", " ", $types );
- $this->mMimeToExt= array();
- $this->mToMime= array();
+ $this->mMimeToExt = array();
+ $this->mToMime = array();
- $lines= explode("\n",$types);
- foreach ($lines as $s) {
- $s= trim($s);
- if (empty($s)) continue;
- if (strpos($s,'#')===0) continue;
+ $lines = explode( "\n",$types );
+ foreach ( $lines as $s ) {
+ $s = trim( $s );
+ if ( empty( $s ) ) continue;
+ if ( strpos( $s, '#' ) === 0 ) continue;
- $s= strtolower($s);
- $i= strpos($s,' ');
+ $s = strtolower( $s );
+ $i = strpos( $s, ' ' );
- if ($i===false) continue;
+ if ( $i === false ) continue;
#print "processing MIME line $s<br>";
- $mime= substr($s,0,$i);
- $ext= trim(substr($s,$i+1));
+ $mime = substr( $s, 0, $i );
+ $ext = trim( substr($s, $i+1 ) );
- if (empty($ext)) continue;
+ if ( empty( $ext ) ) continue;
- if ( !empty($this->mMimeToExt[$mime])) $this->mMimeToExt[$mime] .= ' '.$ext;
- else $this->mMimeToExt[$mime]= $ext;
+ if ( !empty( $this->mMimeToExt[$mime] ) ) {
+ $this->mMimeToExt[$mime] .= ' ' . $ext;
+ } else {
+ $this->mMimeToExt[$mime] = $ext;
+ }
- $extensions= explode(' ',$ext);
+ $extensions = explode( ' ', $ext );
- foreach ($extensions as $e) {
- $e= trim($e);
- if (empty($e)) continue;
+ foreach ( $extensions as $e ) {
+ $e = trim( $e );
+ if ( empty( $e ) ) continue;
- if ( !empty($this->mExtToMime[$e])) $this->mExtToMime[$e] .= ' '.$mime;
- else $this->mExtToMime[$e]= $mime;
+ if ( !empty( $this->mExtToMime[$e] ) ) {
+ $this->mExtToMime[$e] .= ' ' . $mime;
+ } else {
+ $this->mExtToMime[$e] = $mime;
+ }
}
}
@@ -169,62 +180,69 @@ class MimeMagic {
*/
global $wgMimeInfoFile;
+ if ( $wgMimeInfoFile == 'includes/mime.info' ) {
+ $wgMimeInfoFile = "$IP/$wgMimeInfoFile";
+ }
- $info= MM_WELL_KNOWN_MIME_INFO;
-
- if ($wgMimeInfoFile) {
- if (is_file($wgMimeInfoFile) and is_readable($wgMimeInfoFile)) {
- wfDebug("MimeMagic::MimeMagic: loading mime info from $wgMimeInfoFile\n");
+ $info = MM_WELL_KNOWN_MIME_INFO;
- $info.= "\n";
- $info.= file_get_contents($wgMimeInfoFile);
+ if ( $wgMimeInfoFile ) {
+ if ( is_file( $wgMimeInfoFile ) and is_readable( $wgMimeInfoFile ) ) {
+ wfDebug( __METHOD__.": loading mime info from $wgMimeInfoFile\n" );
+ $info .= "\n";
+ $info .= file_get_contents( $wgMimeInfoFile );
+ } else {
+ wfDebug(__METHOD__.": can't load mime info from $wgMimeInfoFile\n");
}
- else wfDebug("MimeMagic::MimeMagic: can't load mime info from $wgMimeInfoFile\n");
+ } else {
+ wfDebug(__METHOD__.": no mime info file defined, using build-ins only.\n");
}
- else wfDebug("MimeMagic::MimeMagic: no mime info file defined, using build-ins only.\n");
- $info= str_replace(array("\r\n","\n\r","\n\n","\r\r","\r"),"\n",$info);
- $info= str_replace("\t"," ",$info);
+ $info = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $info);
+ $info = str_replace( "\t", " ", $info );
- $this->mMimeTypeAliases= array();
- $this->mMediaTypes= array();
+ $this->mMimeTypeAliases = array();
+ $this->mMediaTypes = array();
- $lines= explode("\n",$info);
- foreach ($lines as $s) {
- $s= trim($s);
- if (empty($s)) continue;
- if (strpos($s,'#')===0) continue;
+ $lines = explode( "\n", $info );
+ foreach ( $lines as $s ) {
+ $s = trim( $s );
+ if ( empty( $s ) ) continue;
+ if ( strpos( $s, '#' ) === 0 ) continue;
- $s= strtolower($s);
- $i= strpos($s,' ');
+ $s = strtolower( $s );
+ $i = strpos( $s, ' ' );
- if ($i===false) continue;
+ if ( $i === false ) continue;
#print "processing MIME INFO line $s<br>";
- $match= array();
- if (preg_match('!\[\s*(\w+)\s*\]!',$s,$match)) {
- $s= preg_replace('!\[\s*(\w+)\s*\]!','',$s);
- $mtype= trim(strtoupper($match[1]));
+ $match = array();
+ if ( preg_match( '!\[\s*(\w+)\s*\]!', $s, $match ) ) {
+ $s = preg_replace( '!\[\s*(\w+)\s*\]!', '', $s );
+ $mtype = trim( strtoupper( $match[1] ) );
+ } else {
+ $mtype = MEDIATYPE_UNKNOWN;
}
- else $mtype= MEDIATYPE_UNKNOWN;
- $m= explode(' ',$s);
+ $m = explode( ' ', $s );
- if (!isset($this->mMediaTypes[$mtype])) $this->mMediaTypes[$mtype]= array();
+ if ( !isset( $this->mMediaTypes[$mtype] ) ) {
+ $this->mMediaTypes[$mtype] = array();
+ }
- foreach ($m as $mime) {
- $mime= trim($mime);
- if (empty($mime)) continue;
+ foreach ( $m as $mime ) {
+ $mime = trim( $mime );
+ if ( empty( $mime ) ) continue;
- $this->mMediaTypes[$mtype][]= $mime;
+ $this->mMediaTypes[$mtype][] = $mime;
}
- if (sizeof($m)>1) {
- $main= $m[0];
- for ($i=1; $i<sizeof($m); $i+= 1) {
- $mime= $m[$i];
- $this->mMimeTypeAliases[$mime]= $main;
+ if ( sizeof( $m ) > 1 ) {
+ $main = $m[0];
+ for ( $i=1; $i<sizeof($m); $i += 1 ) {
+ $mime = $m[$i];
+ $this->mMimeTypeAliases[$mime] = $main;
}
}
}
@@ -244,14 +262,14 @@ class MimeMagic {
/** returns a list of file extensions for a given mime type
* as a space separated string.
*/
- function getExtensionsForType($mime) {
- $mime= strtolower($mime);
+ function getExtensionsForType( $mime ) {
+ $mime = strtolower( $mime );
- $r= @$this->mMimeToExt[$mime];
+ $r = @$this->mMimeToExt[$mime];
- if (@!$r and isset($this->mMimeTypeAliases[$mime])) {
- $mime= $this->mMimeTypeAliases[$mime];
- $r= @$this->mMimeToExt[$mime];
+ if ( @!$r and isset( $this->mMimeTypeAliases[$mime] ) ) {
+ $mime = $this->mMimeTypeAliases[$mime];
+ $r = @$this->mMimeToExt[$mime];
}
return $r;
@@ -260,22 +278,22 @@ class MimeMagic {
/** returns a list of mime types for a given file extension
* as a space separated string.
*/
- function getTypesForExtension($ext) {
- $ext= strtolower($ext);
+ function getTypesForExtension( $ext ) {
+ $ext = strtolower( $ext );
- $r= isset( $this->mExtToMime[$ext] ) ? $this->mExtToMime[$ext] : null;
+ $r = isset( $this->mExtToMime[$ext] ) ? $this->mExtToMime[$ext] : null;
return $r;
}
/** returns a single mime type for a given file extension.
* This is always the first type from the list returned by getTypesForExtension($ext).
*/
- function guessTypesForExtension($ext) {
- $m= $this->getTypesForExtension( $ext );
- if( is_null($m) ) return NULL;
+ function guessTypesForExtension( $ext ) {
+ $m = $this->getTypesForExtension( $ext );
+ if ( is_null( $m ) ) return NULL;
- $m= trim( $m );
- $m= preg_replace('/\s.*$/','',$m);
+ $m = trim( $m );
+ $m = preg_replace( '/\s.*$/', '', $m );
return $m;
}
@@ -285,17 +303,17 @@ class MimeMagic {
* returns true if a match was found, NULL if the mime type is unknown,
* and false if the mime type is known but no matches where found.
*/
- function isMatchingExtension($extension,$mime) {
- $ext= $this->getExtensionsForType($mime);
+ function isMatchingExtension( $extension, $mime ) {
+ $ext = $this->getExtensionsForType( $mime );
- if (!$ext) {
+ if ( !$ext ) {
return NULL; //unknown
}
- $ext= explode(' ',$ext);
+ $ext = explode( ' ', $ext );
- $extension= strtolower($extension);
- if (in_array($extension,$ext)) {
+ $extension = strtolower( $extension );
+ if ( in_array( $extension, $ext ) ) {
return true;
}
@@ -347,16 +365,18 @@ class MimeMagic {
* or misinterpreter by the default mime detection (namely xml based formats like XHTML or SVG).
*
* @param string $file The file to check
- * @param bool $useExt switch for allowing to use the file extension to guess the mime type. true by default.
+ * @param mixed $ext The file extension, or true to extract it from the filename.
+ * Set it to false to ignore the extension.
*
* @return string the mime type of $file
*/
- function guessMimeType( $file, $useExt=true ) {
- $fname = 'MimeMagic::guessMimeType';
- $mime= $this->detectMimeType($file,$useExt);
+ function guessMimeType( $file, $ext = true ) {
+ $mime = $this->detectMimeType( $file, $ext );
// Read a chunk of the file
+ wfSuppressWarnings();
$f = fopen( $file, "rt" );
+ wfRestoreWarnings();
if( !$f ) return "unknown/unknown";
$head = fread( $f, 1024 );
fclose( $f );
@@ -369,67 +389,88 @@ class MimeMagic {
$mime = "application/x-msmetafile";
}
- if (strpos($mime,"text/")===0 || $mime==="application/xml") {
+ if ( strpos( $mime, "text/" ) === 0 || $mime === "application/xml" ) {
- $xml_type= NULL;
- $script_type= NULL;
+ $xml_type = NULL;
+ $script_type = NULL;
/*
* look for XML formats (XHTML and SVG)
*/
- if ($mime==="text/sgml" ||
- $mime==="text/plain" ||
- $mime==="text/html" ||
- $mime==="text/xml" ||
- $mime==="application/xml") {
-
- if (substr($head,0,5)=="<?xml") $xml_type= "ASCII";
- elseif (substr($head,0,8)=="\xef\xbb\xbf<?xml") $xml_type= "UTF-8";
- elseif (substr($head,0,10)=="\xfe\xff\x00<\x00?\x00x\x00m\x00l") $xml_type= "UTF-16BE";
- elseif (substr($head,0,10)=="\xff\xfe<\x00?\x00x\x00m\x00l\x00") $xml_type= "UTF-16LE";
+ if ($mime === "text/sgml" ||
+ $mime === "text/plain" ||
+ $mime === "text/html" ||
+ $mime === "text/xml" ||
+ $mime === "application/xml") {
+
+ if ( substr( $head, 0, 5 ) == "<?xml" ) {
+ $xml_type = "ASCII";
+ } elseif ( substr( $head, 0, 8 ) == "\xef\xbb\xbf<?xml") {
+ $xml_type = "UTF-8";
+ } elseif ( substr( $head, 0, 10 ) == "\xfe\xff\x00<\x00?\x00x\x00m\x00l" ) {
+ $xml_type = "UTF-16BE";
+ } elseif ( substr( $head, 0, 10 ) == "\xff\xfe<\x00?\x00x\x00m\x00l\x00") {
+ $xml_type = "UTF-16LE";
+ }
- if ($xml_type) {
- if ($xml_type!=="UTF-8" && $xml_type!=="ASCII") $head= iconv($xml_type,"ASCII//IGNORE",$head);
+ if ( $xml_type ) {
+ if ( $xml_type !== "UTF-8" && $xml_type !== "ASCII" ) {
+ $head = iconv( $xml_type, "ASCII//IGNORE", $head );
+ }
- $match= array();
- $doctype= "";
- $tag= "";
+ $match = array();
+ $doctype = "";
+ $tag = "";
- if (preg_match('%<!DOCTYPE\s+[\w-]+\s+PUBLIC\s+["'."'".'"](.*?)["'."'".'"].*>%sim',$head,$match)) $doctype= $match[1];
- if (preg_match('%<(\w+).*>%sim',$head,$match)) $tag= $match[1];
+ if ( preg_match( '%<!DOCTYPE\s+[\w-]+\s+PUBLIC\s+["'."'".'"](.*?)["'."'".'"].*>%sim',
+ $head, $match ) ) {
+ $doctype = $match[1];
+ }
+ if ( preg_match( '%<(\w+).*>%sim', $head, $match ) ) {
+ $tag = $match[1];
+ }
#print "<br>ANALYSING $file ($mime): doctype= $doctype; tag= $tag<br>";
- if (strpos($doctype,"-//W3C//DTD SVG")===0) $mime= "image/svg+xml";
- elseif ($tag==="svg") $mime= "image/svg+xml";
- elseif (strpos($doctype,"-//W3C//DTD XHTML")===0) $mime= "text/html";
- elseif ($tag==="html") $mime= "text/html";
+ if ( strpos( $doctype, "-//W3C//DTD SVG" ) === 0 ) {
+ $mime = "image/svg+xml";
+ } elseif ( $tag === "svg" ) {
+ $mime = "image/svg+xml";
+ } elseif ( strpos( $doctype, "-//W3C//DTD XHTML" ) === 0 ) {
+ $mime = "text/html";
+ } elseif ( $tag === "html" ) {
+ $mime = "text/html";
+ }
}
}
/*
* look for shell scripts
*/
- if (!$xml_type) {
- $script_type= NULL;
-
- #detect by shebang
- if (substr($head,0,2)=="#!") $script_type= "ASCII";
- elseif (substr($head,0,5)=="\xef\xbb\xbf#!") $script_type= "UTF-8";
- elseif (substr($head,0,7)=="\xfe\xff\x00#\x00!") $script_type= "UTF-16BE";
- elseif (substr($head,0,7)=="\xff\xfe#\x00!") $script_type= "UTF-16LE";
+ if ( !$xml_type ) {
+ $script_type = NULL;
+
+ # detect by shebang
+ if ( substr( $head, 0, 2) == "#!" ) {
+ $script_type = "ASCII";
+ } elseif ( substr( $head, 0, 5) == "\xef\xbb\xbf#!" ) {
+ $script_type = "UTF-8";
+ } elseif ( substr( $head, 0, 7) == "\xfe\xff\x00#\x00!" ) {
+ $script_type = "UTF-16BE";
+ } elseif ( substr( $head, 0, 7 ) == "\xff\xfe#\x00!" ) {
+ $script_type= "UTF-16LE";
+ }
- if ($script_type) {
- if ($script_type!=="UTF-8" && $script_type!=="ASCII") $head= iconv($script_type,"ASCII//IGNORE",$head);
+ if ( $script_type ) {
+ if ( $script_type !== "UTF-8" && $script_type !== "ASCII") {
+ $head = iconv( $script_type, "ASCII//IGNORE", $head);
+ }
- $match= array();
- $prog= "";
+ $match = array();
- if (preg_match('%/?([^\s]+/)(w+)%sim',$head,$match)) {
- $script= $match[2]; // FIXME: $script variable not used; should this be "$prog = $match[2];" instead?
+ if ( preg_match( '%/?([^\s]+/)(\w+)%', $head, $match ) ) {
+ $mime = "application/x-{$match[2]}";
}
-
- $mime= "application/x-$prog";
}
}
@@ -450,42 +491,43 @@ class MimeMagic {
( strpos( $head, "<\x00?\x00\t" ) !== false ) ||
( strpos( $head, "<\x00?\x00=" ) !== false ) ) {
- $mime= "application/x-php";
+ $mime = "application/x-php";
}
}
}
- if (isset($this->mMimeTypeAliases[$mime])) $mime= $this->mMimeTypeAliases[$mime];
+ if ( isset( $this->mMimeTypeAliases[$mime] ) ) {
+ $mime = $this->mMimeTypeAliases[$mime];
+ }
- wfDebug("$fname: final mime type of $file: $mime\n");
+ wfDebug(__METHOD__.": final mime type of $file: $mime\n");
return $mime;
}
/** Internal mime type detection, please use guessMimeType() for application code instead.
* Detection is done using an external program, if $wgMimeDetectorCommand is set.
* Otherwise, the fileinfo extension and mime_content_type are tried (in this order), if they are available.
- * If the dections fails and $useExt is true, the mime type is guessed from the file extension, using guessTypesForExtension.
+ * If the dections fails and $ext is not false, the mime type is guessed from the file extension, using
+ * guessTypesForExtension.
* If the mime type is still unknown, getimagesize is used to detect the mime type if the file is an image.
* If no mime type can be determined, this function returns "unknown/unknown".
*
* @param string $file The file to check
- * @param bool $useExt switch for allowing to use the file extension to guess the mime type. true by default.
+ * @param mixed $ext The file extension, or true to extract it from the filename.
+ * Set it to false to ignore the extension.
*
* @return string the mime type of $file
* @access private
*/
- function detectMimeType( $file, $useExt=true ) {
- $fname = 'MimeMagic::detectMimeType';
-
+ function detectMimeType( $file, $ext = true ) {
global $wgMimeDetectorCommand;
- $m= NULL;
- if ($wgMimeDetectorCommand) {
- $fn= wfEscapeShellArg($file);
- $m= `$wgMimeDetectorCommand $fn`;
- }
- else if (function_exists("finfo_open") && function_exists("finfo_file")) {
+ $m = NULL;
+ if ( $wgMimeDetectorCommand ) {
+ $fn = wfEscapeShellArg( $file );
+ $m = `$wgMimeDetectorCommand $fn`;
+ } elseif ( function_exists( "finfo_open" ) && function_exists( "finfo_file" ) ) {
# This required the fileinfo extension by PECL,
# see http://pecl.php.net/package/fileinfo
@@ -500,13 +542,12 @@ class MimeMagic {
$mime_magic_resource = finfo_open(FILEINFO_MIME); /* return mime type ala mimetype extension */
if ($mime_magic_resource) {
- $m= finfo_file($mime_magic_resource, $file);
-
- finfo_close($mime_magic_resource);
+ $m = finfo_file( $mime_magic_resource, $file );
+ finfo_close( $mime_magic_resource );
+ } else {
+ wfDebug( __METHOD__.": finfo_open failed on ".FILEINFO_MIME."!\n" );
}
- else wfDebug("$fname: finfo_open failed on ".FILEINFO_MIME."!\n");
- }
- else if (function_exists("mime_content_type")) {
+ } elseif ( function_exists( "mime_content_type" ) ) {
# NOTE: this function is available since PHP 4.3.0, but only if
# PHP was compiled with --with-mime-magic or, before 4.3.2, with --enable-mime-magic.
@@ -517,93 +558,99 @@ class MimeMagic {
# Also note that this has been DEPRECATED in favor of the fileinfo extension by PECL, see above.
# see http://www.php.net/manual/en/ref.mime-magic.php for details.
- $m= mime_content_type($file);
+ $m = mime_content_type($file);
if ( $m == 'text/plain' ) {
// mime_content_type sometimes considers DJVU files to be text/plain.
$deja = new DjVuImage( $file );
if( $deja->isValid() ) {
- wfDebug("$fname: (re)detected $file as image/vnd.djvu\n");
+ wfDebug( __METHOD__.": (re)detected $file as image/vnd.djvu\n" );
$m = 'image/vnd.djvu';
}
}
+ } else {
+ wfDebug( __METHOD__.": no magic mime detector found!\n" );
}
- else wfDebug("$fname: no magic mime detector found!\n");
- if ($m) {
- #normalize
- $m= preg_replace('![;, ].*$!','',$m); #strip charset, etc
- $m= trim($m);
- $m= strtolower($m);
+ if ( $m ) {
+ # normalize
+ $m = preg_replace( '![;, ].*$!', '', $m ); #strip charset, etc
+ $m = trim( $m );
+ $m = strtolower( $m );
- if (strpos($m,'unknown')!==false) $m= NULL;
- else {
- wfDebug("$fname: magic mime type of $file: $m\n");
+ if ( strpos( $m, 'unknown' ) !== false ) {
+ $m = NULL;
+ } else {
+ wfDebug( __METHOD__.": magic mime type of $file: $m\n" );
return $m;
}
}
- #if still not known, use getimagesize to find out the type of image
- #TODO: skip things that do not have a well-known image extension? Would that be safe?
+ # if still not known, use getimagesize to find out the type of image
+ # TODO: skip things that do not have a well-known image extension? Would that be safe?
wfSuppressWarnings();
$gis = getimagesize( $file );
wfRestoreWarnings();
- $notAnImage= false;
-
- if ($gis && is_array($gis) && $gis[2]) {
- switch ($gis[2]) {
- case IMAGETYPE_GIF: $m= "image/gif"; break;
- case IMAGETYPE_JPEG: $m= "image/jpeg"; break;
- case IMAGETYPE_PNG: $m= "image/png"; break;
- case IMAGETYPE_SWF: $m= "application/x-shockwave-flash"; break;
- case IMAGETYPE_PSD: $m= "application/photoshop"; break;
- case IMAGETYPE_BMP: $m= "image/bmp"; break;
- case IMAGETYPE_TIFF_II: $m= "image/tiff"; break;
- case IMAGETYPE_TIFF_MM: $m= "image/tiff"; break;
- case IMAGETYPE_JPC: $m= "image"; break;
- case IMAGETYPE_JP2: $m= "image/jpeg2000"; break;
- case IMAGETYPE_JPX: $m= "image/jpeg2000"; break;
- case IMAGETYPE_JB2: $m= "image"; break;
- case IMAGETYPE_SWC: $m= "application/x-shockwave-flash"; break;
- case IMAGETYPE_IFF: $m= "image/vnd.xiff"; break;
- case IMAGETYPE_WBMP: $m= "image/vnd.wap.wbmp"; break;
- case IMAGETYPE_XBM: $m= "image/x-xbitmap"; break;
+ $notAnImage = false;
+
+ if ( $gis && is_array($gis) && $gis[2] ) {
+
+ switch ( $gis[2] ) {
+ case IMAGETYPE_GIF: $m = "image/gif"; break;
+ case IMAGETYPE_JPEG: $m = "image/jpeg"; break;
+ case IMAGETYPE_PNG: $m = "image/png"; break;
+ case IMAGETYPE_SWF: $m = "application/x-shockwave-flash"; break;
+ case IMAGETYPE_PSD: $m = "application/photoshop"; break;
+ case IMAGETYPE_BMP: $m = "image/bmp"; break;
+ case IMAGETYPE_TIFF_II: $m = "image/tiff"; break;
+ case IMAGETYPE_TIFF_MM: $m = "image/tiff"; break;
+ case IMAGETYPE_JPC: $m = "image"; break;
+ case IMAGETYPE_JP2: $m = "image/jpeg2000"; break;
+ case IMAGETYPE_JPX: $m = "image/jpeg2000"; break;
+ case IMAGETYPE_JB2: $m = "image"; break;
+ case IMAGETYPE_SWC: $m = "application/x-shockwave-flash"; break;
+ case IMAGETYPE_IFF: $m = "image/vnd.xiff"; break;
+ case IMAGETYPE_WBMP: $m = "image/vnd.wap.wbmp"; break;
+ case IMAGETYPE_XBM: $m = "image/x-xbitmap"; break;
}
- if ($m) {
- wfDebug("$fname: image mime type of $file: $m\n");
+ if ( $m ) {
+ wfDebug( __METHOD__.": image mime type of $file: $m\n" );
return $m;
}
- else $notAnImage= true;
+ else {
+ $notAnImage = true;
+ }
} else {
// Also test DjVu
$deja = new DjVuImage( $file );
if( $deja->isValid() ) {
- wfDebug("$fname: detected $file as image/vnd.djvu\n");
+ wfDebug( __METHOD__.": detected $file as image/vnd.djvu\n" );
return 'image/vnd.djvu';
}
}
- #if desired, look at extension as a fallback.
- if ($useExt) {
+ # if desired, look at extension as a fallback.
+ if ( $ext === true ) {
$i = strrpos( $file, '.' );
- $e= strtolower( $i ? substr( $file, $i + 1 ) : '' );
-
- $m= $this->guessTypesForExtension($e);
+ $ext = strtolower( $i ? substr( $file, $i + 1 ) : '' );
+ }
+ if ( $ext ) {
+ $m = $this->guessTypesForExtension( $ext );
- #TODO: if $notAnImage is set, do not trust the file extension if
+ # TODO: if $notAnImage is set, do not trust the file extension if
# the results is one of the image types that should have been recognized
# by getimagesize
- if ($m) {
- wfDebug("$fname: extension mime type of $file: $m\n");
+ if ( $m ) {
+ wfDebug( __METHOD__.": extension mime type of $file: $m\n" );
return $m;
}
}
#unknown type
- wfDebug("$fname: failed to guess mime type for $file!\n");
+ wfDebug( __METHOD__.": failed to guess mime type for $file!\n" );
return "unknown/unknown";
}
@@ -623,61 +670,61 @@ class MimeMagic {
*
* @return (int?string?) a value to be used with the MEDIATYPE_xxx constants.
*/
- function getMediaType($path=NULL,$mime=NULL) {
+ function getMediaType( $path = NULL, $mime = NULL ) {
if( !$mime && !$path ) return MEDIATYPE_UNKNOWN;
- #if mime type is unknown, guess it
- if( !$mime ) $mime= $this->guessMimeType($path,false);
+ # If mime type is unknown, guess it
+ if( !$mime ) $mime = $this->guessMimeType( $path, false );
- #special code for ogg - detect if it's video (theora),
- #else label it as sound.
- if( $mime=="application/ogg" && file_exists($path) ) {
+ # Special code for ogg - detect if it's video (theora),
+ # else label it as sound.
+ if( $mime == "application/ogg" && file_exists( $path ) ) {
// Read a chunk of the file
$f = fopen( $path, "rt" );
- if( !$f ) return MEDIATYPE_UNKNOWN;
+ if ( !$f ) return MEDIATYPE_UNKNOWN;
$head = fread( $f, 256 );
fclose( $f );
- $head= strtolower( $head );
+ $head = strtolower( $head );
- #This is an UGLY HACK, file should be parsed correctly
- if( strpos($head,'theora')!==false ) return MEDIATYPE_VIDEO;
- elseif( strpos($head,'vorbis')!==false ) return MEDIATYPE_AUDIO;
- elseif( strpos($head,'flac')!==false ) return MEDIATYPE_AUDIO;
- elseif( strpos($head,'speex')!==false ) return MEDIATYPE_AUDIO;
+ # This is an UGLY HACK, file should be parsed correctly
+ if ( strpos( $head, 'theora' ) !== false ) return MEDIATYPE_VIDEO;
+ elseif ( strpos( $head, 'vorbis' ) !== false ) return MEDIATYPE_AUDIO;
+ elseif ( strpos( $head, 'flac' ) !== false ) return MEDIATYPE_AUDIO;
+ elseif ( strpos( $head, 'speex' ) !== false ) return MEDIATYPE_AUDIO;
else return MEDIATYPE_MULTIMEDIA;
}
- #check for entry for full mime type
+ # check for entry for full mime type
if( $mime ) {
- $type= $this->findMediaType($mime);
- if( $type!==MEDIATYPE_UNKNOWN ) return $type;
+ $type = $this->findMediaType( $mime );
+ if( $type !== MEDIATYPE_UNKNOWN ) return $type;
}
- #check for entry for file extension
- $e= NULL;
- if( $path ) {
+ # Check for entry for file extension
+ $e = NULL;
+ if ( $path ) {
$i = strrpos( $path, '.' );
- $e= strtolower( $i ? substr( $path, $i + 1 ) : '' );
+ $e = strtolower( $i ? substr( $path, $i + 1 ) : '' );
- #TODO: look at multi-extension if this fails, parse from full path
+ # TODO: look at multi-extension if this fails, parse from full path
- $type= $this->findMediaType('.'.$e);
- if( $type!==MEDIATYPE_UNKNOWN ) return $type;
+ $type = $this->findMediaType( '.' . $e );
+ if ( $type !== MEDIATYPE_UNKNOWN ) return $type;
}
- #check major mime type
+ # Check major mime type
if( $mime ) {
- $i= strpos($mime,'/');
+ $i = strpos( $mime, '/' );
if( $i !== false ) {
- $major= substr($mime,0,$i);
- $type= $this->findMediaType($major);
- if( $type!==MEDIATYPE_UNKNOWN ) return $type;
+ $major = substr( $mime, 0, $i );
+ $type = $this->findMediaType( $major );
+ if( $type !== MEDIATYPE_UNKNOWN ) return $type;
}
}
- if( !$type ) $type= MEDIATYPE_UNKNOWN;
+ if( !$type ) $type = MEDIATYPE_UNKNOWN;
return $type;
}
@@ -689,25 +736,26 @@ class MimeMagic {
* This funktion relies on the mapping defined by $this->mMediaTypes
* @access private
*/
- function findMediaType($extMime) {
-
- if (strpos($extMime,'.')===0) { #if it's an extension, look up the mime types
- $m= $this->getTypesForExtension(substr($extMime,1));
- if (!$m) return MEDIATYPE_UNKNOWN;
-
- $m= explode(' ',$m);
- }
- else { #normalize mime type
- if (isset($this->mMimeTypeAliases[$extMime])) {
- $extMime= $this->mMimeTypeAliases[$extMime];
+ function findMediaType( $extMime ) {
+ if ( strpos( $extMime, '.' ) === 0 ) { #if it's an extension, look up the mime types
+ $m = $this->getTypesForExtension( substr( $extMime, 1 ) );
+ if ( !$m ) return MEDIATYPE_UNKNOWN;
+
+ $m = explode( ' ', $m );
+ } else {
+ # Normalize mime type
+ if ( isset( $this->mMimeTypeAliases[$extMime] ) ) {
+ $extMime = $this->mMimeTypeAliases[$extMime];
}
- $m= array($extMime);
+ $m = array($extMime);
}
- foreach ($m as $mime) {
- foreach ($this->mMediaTypes as $type => $codes) {
- if (in_array($mime,$codes,true)) return $type;
+ foreach ( $m as $mime ) {
+ foreach ( $this->mMediaTypes as $type => $codes ) {
+ if ( in_array($mime, $codes, true ) ) {
+ return $type;
+ }
}
}
@@ -715,4 +763,4 @@ class MimeMagic {
}
}
-?>
+
diff --git a/includes/Namespace.php b/includes/Namespace.php
index dd67b55a..f4df3bac 100644
--- a/includes/Namespace.php
+++ b/includes/Namespace.php
@@ -44,54 +44,67 @@ if( is_array( $wgExtraNamespaces ) ) {
class Namespace {
/**
- * Check if the given namespace might be moved
+ * Can pages in the given namespace be moved?
+ *
+ * @param int $index Namespace index
* @return bool
*/
- static function isMovable( $index ) {
+ public static function isMovable( $index ) {
return !( $index < NS_MAIN || $index == NS_IMAGE || $index == NS_CATEGORY );
}
/**
- * Check if the given namespace is not a talk page
+ * Is the given namespace is a subject (non-talk) namespace?
+ *
+ * @param int $index Namespace index
* @return bool
*/
- static function isMain( $index ) {
- return ! Namespace::isTalk( $index );
+ public static function isMain( $index ) {
+ return !self::isTalk( $index );
}
/**
- * Check if the give namespace is a talk page
+ * Is the given namespace a talk namespace?
+ *
+ * @param int $index Namespace index
* @return bool
*/
- static function isTalk( $index ) {
- return ($index > NS_MAIN) // Special namespaces are negative
- && ($index % 2); // Talk namespaces are odd-numbered
+ public static function isTalk( $index ) {
+ return $index > NS_MAIN
+ && $index % 2;
}
/**
- * Get the talk namespace corresponding to the given index
+ * Get the talk namespace index for a given namespace
+ *
+ * @param int $index Namespace index
+ * @return int
*/
- static function getTalk( $index ) {
- if ( Namespace::isTalk( $index ) ) {
- return $index;
- } else {
- # FIXME
- return $index + 1;
- }
+ public static function getTalk( $index ) {
+ return self::isTalk( $index )
+ ? $index
+ : $index + 1;
}
- static function getSubject( $index ) {
- if ( Namespace::isTalk( $index ) ) {
- return $index - 1;
- } else {
- return $index;
- }
+ /**
+ * Get the subject namespace index for a given namespace
+ *
+ * @param int $index Namespace index
+ * @return int
+ */
+ public static function getSubject( $index ) {
+ return self::isTalk( $index )
+ ? $index - 1
+ : $index;
}
/**
* Returns the canonical (English Wikipedia) name for a given index
+ *
+ * @param int $index Namespace index
+ * @return string
*/
- static function getCanonicalName( $index ) {
+ public static function getCanonicalName( $index ) {
global $wgCanonicalNamespaceNames;
return $wgCanonicalNamespaceNames[$index];
}
@@ -99,8 +112,11 @@ class Namespace {
/**
* Returns the index for a given canonical name, or NULL
* The input *must* be converted to lower case first
+ *
+ * @param string $name Namespace name
+ * @return int
*/
- static function getCanonicalIndex( $name ) {
+ public static function getCanonicalIndex( $name ) {
global $wgCanonicalNamespaceNames;
static $xNamespaces = false;
if ( $xNamespaces === false ) {
@@ -118,10 +134,12 @@ class Namespace {
/**
* Can this namespace ever have a talk namespace?
+ *
* @param $index Namespace index
+ * @return bool
*/
- static function canTalk( $index ) {
- return( $index >= NS_MAIN );
+ public static function canTalk( $index ) {
+ return $index >= NS_MAIN;
}
/**
@@ -134,8 +152,16 @@ class Namespace {
public static function isContent( $index ) {
global $wgContentNamespaces;
return $index == NS_MAIN || in_array( $index, $wgContentNamespaces );
- }
+ }
+
+ /**
+ * Can pages in a namespace be watched?
+ *
+ * @param int $index
+ * @return bool
+ */
+ public static function isWatchable( $index ) {
+ return $index >= NS_MAIN;
+ }
-}
-
-?>
+} \ No newline at end of file
diff --git a/includes/ObjectCache.php b/includes/ObjectCache.php
index 3b43dd53..7d9caf8a 100644
--- a/includes/ObjectCache.php
+++ b/includes/ObjectCache.php
@@ -71,7 +71,7 @@ function &wfGetCache( $inputType ) {
} elseif ( function_exists( 'apc_fetch') ) {
$wgCaches[CACHE_ACCEL] = new APCBagOStuff;
} elseif( function_exists( 'xcache_get' ) ) {
- $wgCaches[CACHE_ACCEL] = new XCacheBagOStuff;
+ $wgCaches[CACHE_ACCEL] = new XCacheBagOStuff();
} elseif ( function_exists( 'mmcache_get' ) ) {
$wgCaches[CACHE_ACCEL] = new TurckBagOStuff;
} else {
@@ -123,4 +123,4 @@ function &wfGetParserCacheStorage() {
return $ret;
}
-?>
+
diff --git a/includes/OutputHandler.php b/includes/OutputHandler.php
index d7e7c90f..d8ac12b5 100644
--- a/includes/OutputHandler.php
+++ b/includes/OutputHandler.php
@@ -18,30 +18,67 @@ function wfOutputHandler( $s ) {
}
/**
+ * Get the "file extension" that some client apps will estimate from
+ * the currently-requested URL.
+ * This isn't on WebRequest because we need it when things aren't initialized
+ * @private
+ */
+function wfRequestExtension() {
+ /// @fixme -- this sort of dupes some code in WebRequest::getRequestUrl()
+ if( isset( $_SERVER['REQUEST_URI'] ) ) {
+ // Strip the query string...
+ list( $path ) = explode( '?', $_SERVER['REQUEST_URI'], 2 );
+ } elseif( isset( $_SERVER['SCRIPT_NAME'] ) ) {
+ // Probably IIS. QUERY_STRING appears separately.
+ $path = $_SERVER['SCRIPT_NAME'];
+ } else {
+ // Can't get the path from the server? :(
+ return '';
+ }
+
+ $period = strrpos( $path, '.' );
+ if( $period !== false ) {
+ return strtolower( substr( $path, $period ) );
+ }
+ return '';
+}
+
+/**
* Handler that compresses data with gzip if allowed by the Accept header.
* Unlike ob_gzhandler, it works for HEAD requests too.
*/
function wfGzipHandler( $s ) {
- if ( function_exists( 'gzencode' ) && !headers_sent() ) {
- $tokens = preg_split( '/[,; ]/', $_SERVER['HTTP_ACCEPT_ENCODING'] );
- if ( in_array( 'gzip', $tokens ) ) {
- header( 'Content-Encoding: gzip' );
- $s = gzencode( $s, 3 );
-
- # Set vary header if it hasn't been set already
- $headers = headers_list();
- $foundVary = false;
- foreach ( $headers as $header ) {
- if ( substr( $header, 0, 5 ) == 'Vary:' ) {
- $foundVary = true;
- break;
- }
- }
- if ( !$foundVary ) {
- header( 'Vary: Accept-Encoding' );
- }
+ if( !function_exists( 'gzencode' ) || headers_sent() ) {
+ return $s;
+ }
+
+ $ext = wfRequestExtension();
+ if( $ext == '.gz' || $ext == '.tgz' ) {
+ // Don't do gzip compression if the URL path ends in .gz or .tgz
+ // This confuses Safari and triggers a download of the page,
+ // even though it's pretty clearly labeled as viewable HTML.
+ // Bad Safari! Bad!
+ return $s;
+ }
+
+ $tokens = preg_split( '/[,; ]/', $_SERVER['HTTP_ACCEPT_ENCODING'] );
+ if ( in_array( 'gzip', $tokens ) ) {
+ header( 'Content-Encoding: gzip' );
+ $s = gzencode( $s, 3 );
+ }
+
+ // Set vary header if it hasn't been set already
+ $headers = headers_list();
+ $foundVary = false;
+ foreach ( $headers as $header ) {
+ if ( substr( $header, 0, 5 ) == 'Vary:' ) {
+ $foundVary = true;
+ break;
}
}
+ if ( !$foundVary ) {
+ header( 'Vary: Accept-Encoding' );
+ }
return $s;
}
@@ -61,4 +98,4 @@ function wfDoContentLength( $length ) {
}
}
-?>
+
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index 03e832a4..06467157 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -15,6 +15,7 @@ class OutputPage {
var $mLastModified, $mETag, $mCategoryLinks;
var $mScripts, $mLinkColours, $mPageLinkTitle;
+ var $mAllowUserJs;
var $mSuppressQuickbar;
var $mOnloadHandler;
var $mDoNothing;
@@ -27,12 +28,15 @@ class OutputPage {
var $mNewSectionLink = false;
var $mNoGallery = false;
+ var $mPageTitleActionText = '';
/**
* Constructor
* Initialise private variables
*/
function __construct() {
+ global $wgAllowUserJs;
+ $this->mAllowUserJs = $wgAllowUserJs;
$this->mMetatags = $this->mKeywords = $this->mLinktags = array();
$this->mHTMLtitle = $this->mPagetitle = $this->mBodytext =
$this->mRedirect = $this->mLastModified =
@@ -51,6 +55,7 @@ class OutputPage {
$this->mETag = false;
$this->mRevisionId = null;
$this->mNewSectionLink = false;
+ $this->mTemplateIds = array();
}
public function redirect( $url, $responsecode = '302' ) {
@@ -71,6 +76,13 @@ class OutputPage {
function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); }
function addKeyword( $text ) { array_push( $this->mKeywords, $text ); }
function addScript( $script ) { $this->mScripts .= "\t\t".$script; }
+ function addStyle( $style ) {
+ global $wgStylePath, $wgStyleVersion;
+ $this->addLink(
+ array(
+ 'rel' => 'stylesheet',
+ 'href' => $wgStylePath . '/' . $style . '?' . $wgStyleVersion ) );
+ }
/**
* Add a self-contained script tag with the given contents
@@ -97,6 +109,10 @@ class OutputPage {
$this->mHeadItems[$name] = $value;
}
+ function hasHeadItem( $name ) {
+ return isset( $this->mHeadItems[$name] );
+ }
+
function setETag($tag) { $this->mETag = $tag; }
function setArticleBodyOnly($only) { $this->mArticleBodyOnly = $only; }
function getArticleBodyOnly($only) { return $this->mArticleBodyOnly; }
@@ -146,7 +162,11 @@ class OutputPage {
# Wed, 20 Aug 2003 06:51:19 GMT; length=5202
# this breaks strtotime().
$modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
+
+ wfSuppressWarnings(); // E_STRICT system time bitching
$modsinceTime = strtotime( $modsince );
+ wfRestoreWarnings();
+
$ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 );
wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", false );
wfDebug( "$fname: -- we might send Last-Modified : $lastmod\n", false );
@@ -174,26 +194,13 @@ class OutputPage {
}
}
+ function setPageTitleActionText( $text ) {
+ $this->mPageTitleActionText = $text;
+ }
+
function getPageTitleActionText () {
- global $action;
- switch($action) {
- case 'edit':
- case 'delete':
- case 'protect':
- case 'unprotect':
- case 'watch':
- case 'unwatch':
- // Display title is already customized
- return '';
- case 'history':
- return wfMsg('history_short');
- case 'submit':
- // FIXME: bug 2735; not correct for special pages etc
- return wfMsg('preview');
- case 'info':
- return wfMsg('info_short');
- default:
- return '';
+ if ( isset( $this->mPageTitleActionText ) ) {
+ return $this->mPageTitleActionText;
}
}
@@ -283,6 +290,9 @@ class OutputPage {
public function suppressQuickbar() { $this->mSuppressQuickbar = true; }
public function isQuickbarSuppressed() { return $this->mSuppressQuickbar; }
+ public function disallowUserJs() { $this->mAllowUserJs = false; }
+ public function isUserJsAllowed() { return $this->mAllowUserJs; }
+
public function addHTML( $text ) { $this->mBodytext .= $text; }
public function clearHTML() { $this->mBodytext = ''; }
public function getHTML() { return $this->mBodytext; }
@@ -363,14 +373,24 @@ class OutputPage {
if ( $parserOutput->getCacheTime() == -1 ) {
$this->enableClientCache( false );
}
- if ( $parserOutput->mHTMLtitle != "" ) {
- $this->mPagetitle = $parserOutput->mHTMLtitle ;
- }
- if ( $parserOutput->mSubtitle != '' ) {
- $this->mSubtitle .= $parserOutput->mSubtitle ;
- }
$this->mNoGallery = $parserOutput->getNoGallery();
$this->mHeadItems = array_merge( $this->mHeadItems, (array)$parserOutput->mHeadItems );
+ // Versioning...
+ $this->mTemplateIds += (array)$parserOutput->mTemplateIds;
+
+ # Display title
+ if( ( $dt = $parserOutput->getDisplayTitle() ) !== false )
+ $this->setPageTitle( $dt );
+
+ # Hooks registered in the object
+ global $wgParserOutputHooks;
+ foreach ( $parserOutput->getOutputHooks() as $hookInfo ) {
+ list( $hookName, $data ) = $hookInfo;
+ if ( isset( $wgParserOutputHooks[$hookName] ) ) {
+ call_user_func( $wgParserOutputHooks[$hookName], $this, $parserOutput, $data );
+ }
+ }
+
wfRunHooks( 'OutputPageParserOutput', array( &$this, $parserOutput ) );
}
@@ -730,27 +750,51 @@ class OutputPage {
* @return nothing
*/
function blockedPage( $return = true ) {
- global $wgUser, $wgContLang, $wgTitle;
+ global $wgUser, $wgContLang, $wgTitle, $wgLang;
$this->setPageTitle( wfMsg( 'blockedtitle' ) );
$this->setRobotpolicy( 'noindex,nofollow' );
$this->setArticleRelated( false );
- $id = $wgUser->blockedBy();
+ $name = User::whoIs( $wgUser->blockedBy() );
$reason = $wgUser->blockedFor();
+ $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $wgUser->mBlock->mTimestamp ), true );
$ip = wfGetIP();
- if ( is_numeric( $id ) ) {
- $name = User::whoIs( $id );
- } else {
- $name = $id;
- }
$link = '[[' . $wgContLang->getNsText( NS_USER ) . ":{$name}|{$name}]]";
$blockid = $wgUser->mBlock->mId;
- $this->addWikiText( wfMsg( 'blockedtext', $link, $reason, $ip, $name, $blockid ) );
-
+ $blockExpiry = $wgUser->mBlock->mExpiry;
+ if ( $blockExpiry == 'infinity' ) {
+ // Entry in database (table ipblocks) is 'infinity' but 'ipboptions' uses 'infinite' or 'indefinite'
+ // Search for localization in 'ipboptions'
+ $scBlockExpiryOptions = wfMsg( 'ipboptions' );
+ foreach ( explode( ',', $scBlockExpiryOptions ) as $option ) {
+ if ( strpos( $option, ":" ) === false )
+ continue;
+ list( $show, $value ) = explode( ":", $option );
+ if ( $value == 'infinite' || $value == 'indefinite' ) {
+ $blockExpiry = $show;
+ break;
+ }
+ }
+ } else {
+ $blockExpiry = $wgLang->timeanddate( wfTimestamp( TS_MW, $blockExpiry ), true );
+ }
+
+ if ( $wgUser->mBlock->mAuto ) {
+ $msg = 'autoblockedtext';
+ } else {
+ $msg = 'blockedtext';
+ }
+
+ /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked.
+ * This could be a username, an ip range, or a single ip. */
+ $intended = $wgUser->mBlock->mAddress;
+
+ $this->addWikiText( wfMsg( $msg, $link, $reason, $ip, $name, $blockid, $blockExpiry, $intended, $blockTimestamp ) );
+
# Don't auto-return to special pages
if( $return ) {
$return = $wgTitle->getNamespace() > -1 ? $wgTitle->getPrefixedText() : NULL;
@@ -759,13 +803,13 @@ class OutputPage {
}
/**
- * Outputs a pretty page to explain why the request exploded.
+ * Output a standard error page
*
- * @param string $title Message key for page title.
- * @param string $msg Message key for page text.
- * @return nothing
+ * @param string $title Message key for page title
+ * @param string $msg Message key for page text
+ * @param array $params Message parameters
*/
- public function showErrorPage( $title, $msg ) {
+ public function showErrorPage( $title, $msg, $params = array() ) {
global $wgTitle;
$this->mDebugtext .= 'Original title: ' .
@@ -776,12 +820,36 @@ class OutputPage {
$this->setArticleRelated( false );
$this->enableClientCache( false );
$this->mRedirect = '';
-
$this->mBodytext = '';
- $this->addWikiText( wfMsg( $msg ) );
+
+ array_unshift( $params, 'parse' );
+ array_unshift( $params, $msg );
+ $this->addHtml( call_user_func_array( 'wfMsgExt', $params ) );
+
$this->returnToMain( false );
}
+ /**
+ * Output a standard permission error page
+ *
+ * @param array $errors Error message keys
+ */
+ public function showPermissionsErrorPage( $errors )
+ {
+ global $wgTitle;
+
+ $this->mDebugtext .= 'Original title: ' .
+ $wgTitle->getPrefixedText() . "\n";
+ $this->setPageTitle( wfMsg( 'permissionserrors' ) );
+ $this->setHTMLTitle( wfMsg( 'permissionserrors' ) );
+ $this->setRobotpolicy( 'noindex,nofollow' );
+ $this->setArticleRelated( false );
+ $this->enableClientCache( false );
+ $this->mRedirect = '';
+ $this->mBodytext = '';
+ $this->addWikiText( $this->formatPermissionsErrorMessage( $errors ) );
+ }
+
/** @deprecated */
public function errorpage( $title, $msg ) {
throw new ErrorPageError( $title, $msg );
@@ -898,38 +966,75 @@ class OutputPage {
}
/**
+ * @param array $errors An array of arrays returned by Title::getUserPermissionsErrors
+ * @return string The error-messages, formatted into a list.
+ */
+ public function formatPermissionsErrorMessage( $errors ) {
+ $text = '';
+
+ if (sizeof( $errors ) > 1) {
+
+ $text .= wfMsgExt( 'permissionserrorstext', array( 'parse' ), count( $errors ) ) . "\n";
+ $text .= '<ul class="permissions-errors">' . "\n";
+
+ foreach( $errors as $error )
+ {
+ $text .= '<li>';
+ $text .= call_user_func_array( 'wfMsg', $error );
+ $text .= "</li>\n";
+ }
+ $text .= '</ul>';
+ } else {
+ $text .= call_user_func_array( 'wfMsg', $errors[0]);
+ }
+
+ return $text;
+ }
+
+ /**
* @todo document
* @param bool $protected Is the reason the page can't be reached because it's protected?
* @param mixed $source
+ * @param bool $protected, page is protected?
+ * @param array $reason, array of arrays( msg, args )
*/
- public function readOnlyPage( $source = null, $protected = false ) {
+ public function readOnlyPage( $source = null, $protected = false, $reasons = array() ) {
global $wgUser, $wgReadOnlyFile, $wgReadOnly, $wgTitle;
$skin = $wgUser->getSkin();
$this->setRobotpolicy( 'noindex,nofollow' );
$this->setArticleRelated( false );
-
- if( $protected ) {
+
+ if ( !empty($reasons) ) {
$this->setPageTitle( wfMsg( 'viewsource' ) );
$this->setSubtitle( wfMsg( 'viewsourcefor', $skin->makeKnownLinkObj( $wgTitle ) ) );
- list( $cascadeSources, $restrictions ) = $wgTitle->getCascadeProtectionSources();
+ $this->addWikiText( $this->formatPermissionsErrorMessage( $reasons ) );
+ } else if( $protected ) {
+ $this->setPageTitle( wfMsg( 'viewsource' ) );
+ $this->setSubtitle( wfMsg( 'viewsourcefor', $skin->makeKnownLinkObj( $wgTitle ) ) );
+ list( $cascadeSources, /* $restrictions */ ) = $wgTitle->getCascadeProtectionSources();
- # Determine if protection is due to the page being a system message
- # and show an appropriate explanation
+ // Show an appropriate explanation depending upon the reason
+ // for the protection...all of these should be moved to the
+ // callers
if( $wgTitle->getNamespace() == NS_MEDIAWIKI ) {
+ // User isn't allowed to edit the interface
$this->addWikiText( wfMsg( 'protectedinterface' ) );
- } if ( $cascadeSources && count($cascadeSources) > 0 ) {
- $titles = '';
-
- foreach ( $cascadeSources as $title ) {
- $titles .= '* [[:' . $title->getPrefixedText() . "]]\n";
- }
-
- $notice = wfMsgExt( 'cascadeprotected', array('parsemag'), count($cascadeSources) ) . "\n$titles";
-
- $this->addWikiText( $notice );
+ } elseif( $cascadeSources && ( $count = count( $cascadeSources ) ) > 0 ) {
+ // Cascading protection
+ $titles = '';
+ foreach( $cascadeSources as $title )
+ $titles .= "* [[:" . $title->getPrefixedText() . "]]\n";
+ $this->addWikiText( wfMsgExt( 'cascadeprotected', 'parsemag', $count ) . "\n{$titles}" );
+ } elseif( !$wgTitle->isProtected( 'edit' ) && $wgTitle->isNamespaceProtected() ) {
+ // Namespace protection
+ $ns = $wgTitle->getNamespace() == NS_MAIN
+ ? wfMsg( 'nstab-main' )
+ : $wgTitle->getNsText();
+ $this->addWikiText( wfMsg( 'namespaceprotected', $ns ) );
} else {
+ // Standard protection
$this->addWikiText( wfMsg( 'protectedpagetext' ) );
}
} else {
@@ -950,8 +1055,8 @@ class OutputPage {
htmlspecialchars( $source ) . "\n</textarea>";
$this->addHTML( $text );
}
- $article = new Article($wgTitle);
- $this->addHTML( $skin->formatTemplates($article->getUsedTemplates()) );
+ $article = new Article( $wgTitle );
+ $this->addHTML( $skin->formatTemplates( $article->getUsedTemplates() ) );
$this->returnToMain( false );
}
@@ -1016,12 +1121,25 @@ class OutputPage {
}
/**
- * return from error messages or notes
- * @param $auto automatically redirect the user after 10 seconds
- * @param $returnto page title to return to. Default is Main Page.
+ * Add a "return to" link pointing to a specified title
+ *
+ * @param Title $title Title to link
+ */
+ public function addReturnTo( $title ) {
+ global $wgUser;
+ $link = wfMsg( 'returnto', $wgUser->getSkin()->makeLinkObj( $title ) );
+ $this->addHtml( "<p>{$link}</p>\n" );
+ }
+
+ /**
+ * Add a "return to" link pointing to a specified title,
+ * or the title indicated in the request, or else the main page
+ *
+ * @param null $unused No longer used
+ * @param Title $returnto Title to return to
*/
- public function returnToMain( $auto = true, $returnto = NULL ) {
- global $wgUser, $wgOut, $wgRequest;
+ public function returnToMain( $unused = null, $returnto = NULL ) {
+ global $wgRequest;
if ( $returnto == NULL ) {
$returnto = $wgRequest->getText( 'returnto' );
@@ -1040,14 +1158,7 @@ class OutputPage {
$titleObj = Title::newMainPage();
}
- $sk = $wgUser->getSkin();
- $link = $sk->makeLinkObj( $titleObj, '' );
-
- $r = wfMsg( 'returnto', $link );
- if ( $auto ) {
- $wgOut->addMeta( 'http:Refresh', '10;url=' . $titleObj->escapeFullURL() );
- }
- $wgOut->addHTML( "\n<p>$r</p>\n" );
+ $this->addReturnTo( $titleObj );
}
/**
@@ -1114,7 +1225,7 @@ class OutputPage {
$ret .= "<link rel='stylesheet' type='text/css' $media href='$printsheet' />\n";
$sk = $wgUser->getSkin();
- $ret .= $sk->getHeadScripts();
+ $ret .= $sk->getHeadScripts( $this->mAllowUserJs );
$ret .= $this->mScripts;
$ret .= $sk->getUserStyles();
$ret .= $this->getHeadItems();
@@ -1191,10 +1302,30 @@ class OutputPage {
/**
* Show an "add new section" link?
*
- * @return bool True if the parser output instructs us to add one
+ * @return bool
*/
public function showNewSectionLink() {
return $this->mNewSectionLink;
}
+
+ /**
+ * Show a warning about slave lag
+ *
+ * If the lag is higher than $wgSlaveLagCritical seconds,
+ * then the warning is a bit more obvious. If the lag is
+ * lower than $wgSlaveLagWarning, then no warning is shown.
+ *
+ * @param int $lag Slave lag
+ */
+ public function showLagWarning( $lag ) {
+ global $wgSlaveLagWarning, $wgSlaveLagCritical;
+ if( $lag >= $wgSlaveLagWarning ) {
+ $message = $lag < $wgSlaveLagCritical
+ ? 'lag-warn-normal'
+ : 'lag-warn-high';
+ $warning = wfMsgExt( $message, 'parse', $lag );
+ $this->addHtml( "<div class=\"mw-{$message}\">\n{$warning}\n</div>\n" );
+ }
+ }
+
}
-?>
diff --git a/includes/PageHistory.php b/includes/PageHistory.php
index b1cf41f0..d84c3515 100644
--- a/includes/PageHistory.php
+++ b/includes/PageHistory.php
@@ -62,6 +62,7 @@ class PageHistory {
* Setup page variables.
*/
$wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
+ $wgOut->setPageTitleActionText( wfMsg( 'history_short' ) );
$wgOut->setArticleFlag( false );
$wgOut->setArticleRelated( true );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
@@ -244,8 +245,26 @@ class PageHistory {
if( $row->rev_deleted & Revision::DELETED_TEXT ) {
$s .= ' ' . wfMsgHtml( 'deletedrev' );
}
- if( $wgUser->isAllowed( 'rollback' ) && $latest ) {
- $s .= ' '.$this->mSkin->generateRollback( $rev );
+
+ $tools = array();
+
+ if ( !is_null( $next ) && is_object( $next ) ) {
+ if( $wgUser->isAllowed( 'rollback' ) && $latest ) {
+ $tools[] = '<span class="mw-rollback-link">'
+ . $this->mSkin->buildRollbackLink( $rev )
+ . '</span>';
+ }
+
+ $undolink = $this->mSkin->makeKnownLinkObj(
+ $this->mTitle,
+ wfMsgHtml( 'editundo' ),
+ 'action=edit&undoafter=' . $next->rev_id . '&undo=' . $rev->getId()
+ );
+ $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>";
+ }
+
+ if( $tools ) {
+ $s .= ' (' . implode( ' | ', $tools ) . ')';
}
wfRunHooks( 'PageHistoryLineEnding', array( &$row , &$s ) );
@@ -589,4 +608,5 @@ class PageHistoryPager extends ReverseChronologicalPager {
}
}
-?>
+
+
diff --git a/includes/PageQueryPage.php b/includes/PageQueryPage.php
index 5b82ebf6..53b7765e 100644
--- a/includes/PageQueryPage.php
+++ b/includes/PageQueryPage.php
@@ -23,4 +23,4 @@ class PageQueryPage extends QueryPage {
}
}
-?>
+
diff --git a/includes/Pager.php b/includes/Pager.php
index a475dc16..70d0873c 100644
--- a/includes/Pager.php
+++ b/includes/Pager.php
@@ -12,41 +12,42 @@ interface Pager {
/**
* IndexPager is an efficient pager which uses a (roughly unique) index in the
* data set to implement paging, rather than a "LIMIT offset,limit" clause.
- * In MySQL, such a limit/offset clause requires counting through the specified number
- * of offset rows to find the desired data, which can be expensive for large offsets.
+ * In MySQL, such a limit/offset clause requires counting through the
+ * specified number of offset rows to find the desired data, which can be
+ * expensive for large offsets.
*
- * ReverseChronologicalPager is a child class of the abstract IndexPager, and contains
- * some formatting and display code which is specific to the use of timestamps as
- * indexes. Here is a synopsis of its operation:
+ * ReverseChronologicalPager is a child class of the abstract IndexPager, and
+ * contains some formatting and display code which is specific to the use of
+ * timestamps as indexes. Here is a synopsis of its operation:
*
- * * The query is specified by the offset, limit and direction (dir) parameters, in
- * addition to any subclass-specific parameters.
+ * * The query is specified by the offset, limit and direction (dir)
+ * parameters, in addition to any subclass-specific parameters.
+ * * The offset is the non-inclusive start of the DB query. A row with an
+ * index value equal to the offset will never be shown.
+ * * The query may either be done backwards, where the rows are returned by
+ * the database in the opposite order to which they are displayed to the
+ * user, or forwards. This is specified by the "dir" parameter, dir=prev
+ * means backwards, anything else means forwards. The offset value
+ * specifies the start of the database result set, which may be either
+ * the start or end of the displayed data set. This allows "previous"
+ * links to be implemented without knowledge of the index value at the
+ * start of the previous page.
+ * * An additional row beyond the user-specified limit is always requested.
+ * This allows us to tell whether we should display a "next" link in the
+ * case of forwards mode, or a "previous" link in the case of backwards
+ * mode. Determining whether to display the other link (the one for the
+ * page before the start of the database result set) can be done
+ * heuristically by examining the offset.
*
- * * The offset is the non-inclusive start of the DB query. A row with an index value
- * equal to the offset will never be shown.
+ * * An empty offset indicates that the offset condition should be omitted
+ * from the query. This naturally produces either the first page or the
+ * last page depending on the dir parameter.
*
- * * The query may either be done backwards, where the rows are returned by the database
- * in the opposite order to which they are displayed to the user, or forwards. This is
- * specified by the "dir" parameter, dir=prev means backwards, anything else means
- * forwards. The offset value specifies the start of the database result set, which
- * may be either the start or end of the displayed data set. This allows "previous"
- * links to be implemented without knowledge of the index value at the start of the
- * previous page.
- *
- * * An additional row beyond the user-specified limit is always requested. This allows
- * us to tell whether we should display a "next" link in the case of forwards mode,
- * or a "previous" link in the case of backwards mode. Determining whether to
- * display the other link (the one for the page before the start of the database
- * result set) can be done heuristically by examining the offset.
- *
- * * An empty offset indicates that the offset condition should be omitted from the query.
- * This naturally produces either the first page or the last page depending on the
- * dir parameter.
- *
- * Subclassing the pager to implement concrete functionality should be fairly simple,
- * please see the examples in PageHistory.php and SpecialIpblocklist.php. You just need
- * to override formatRow(), getQueryInfo() and getIndexField(). Don't forget to call the
- * parent constructor if you override it.
+ * Subclassing the pager to implement concrete functionality should be fairly
+ * simple, please see the examples in PageHistory.php and
+ * SpecialIpblocklist.php. You just need to override formatRow(),
+ * getQueryInfo() and getIndexField(). Don't forget to call the parent
+ * constructor if you override it.
*
* @addtogroup Pager
*/
@@ -75,9 +76,9 @@ abstract class IndexPager implements Pager {
global $wgRequest, $wgUser;
$this->mRequest = $wgRequest;
- # NB: the offset is quoted, not validated. It is treated as an arbitrary string
- # to support the widest variety of index types. Be careful outputting it into
- # HTML!
+ # NB: the offset is quoted, not validated. It is treated as an
+ # arbitrary string to support the widest variety of index types. Be
+ # careful outputting it into HTML!
$this->mOffset = $this->mRequest->getText( 'offset' );
# Use consistent behavior for the limit options
@@ -106,6 +107,9 @@ abstract class IndexPager implements Pager {
$this->mResult = $this->reallyDoQuery( $this->mOffset, $queryLimit, $descending );
$this->extractResultInfo( $this->mOffset, $queryLimit, $this->mResult );
$this->mQueryDone = true;
+
+ $this->preprocessResults( $this->mResult );
+ $this->mResult->rewind(); // Paranoia
wfProfileOut( $fname );
}
@@ -131,9 +135,10 @@ abstract class IndexPager implements Pager {
$lastIndex = $row[$this->mIndexField];
} else {
$this->mPastTheEndRow = null;
- # Setting indexes to an empty string means that they will be omitted
- # if they would otherwise appear in URLs. It just so happens that this
- # is the right thing to do in the standard UI, in all the relevant cases.
+ # Setting indexes to an empty string means that they will be
+ # omitted if they would otherwise appear in URLs. It just so
+ # happens that this is the right thing to do in the standard
+ # UI, in all the relevant cases.
$this->mPastTheEndIndex = '';
$res->seek( $numRows - 1 );
$row = $res->fetchRow();
@@ -160,21 +165,22 @@ abstract class IndexPager implements Pager {
}
/**
- * Do a query with specified parameters, rather than using the object context
+ * Do a query with specified parameters, rather than using the object
+ * context
*
* @param string $offset Index offset, inclusive
* @param integer $limit Exact query limit
* @param boolean $descending Query direction, false for ascending, true for descending
* @return ResultWrapper
*/
- function reallyDoQuery( $offset, $limit, $ascending ) {
+ function reallyDoQuery( $offset, $limit, $descending ) {
$fname = __METHOD__ . ' (' . get_class( $this ) . ')';
$info = $this->getQueryInfo();
$tables = $info['tables'];
$fields = $info['fields'];
$conds = isset( $info['conds'] ) ? $info['conds'] : array();
$options = isset( $info['options'] ) ? $info['options'] : array();
- if ( $ascending ) {
+ if ( $descending ) {
$options['ORDER BY'] = $this->mIndexField;
$operator = '>';
} else {
@@ -190,6 +196,13 @@ abstract class IndexPager implements Pager {
}
/**
+ * Pre-process results; useful for performing batch existence checks, etc.
+ *
+ * @param ResultWrapper $result Result wrapper
+ */
+ protected function preprocessResults( $result ) {}
+
+ /**
* Get the formatted result list. Calls getStartBody(), formatRow() and
* getEndBody(), concatenates the results and returns them.
*/
@@ -331,9 +344,10 @@ abstract class IndexPager implements Pager {
}
/**
- * Get paging links. If a link is disabled, the item from $disabledTexts will
- * be used. If there is no such item, the unlinked text from $linkTexts will
- * be used. Both $linkTexts and $disabledTexts are arrays of HTML.
+ * Get paging links. If a link is disabled, the item from $disabledTexts
+ * will be used. If there is no such item, the unlinked text from
+ * $linkTexts will be used. Both $linkTexts and $disabledTexts are arrays
+ * of HTML.
*/
function getPagingLinks( $linkTexts, $disabledTexts = array() ) {
$queries = $this->getPagingQueries();
@@ -667,20 +681,22 @@ abstract class TablePager extends IndexPager {
}
/**
- * Return true if the named field should be sortable by the UI, false otherwise
+ * Return true if the named field should be sortable by the UI, false
+ * otherwise
+ *
* @param string $field
*/
abstract function isFieldSortable( $field );
/**
- * Format a table cell. The return value should be HTML, but use an empty string
- * not &nbsp; for empty cells. Do not include the <td> and </td>.
+ * Format a table cell. The return value should be HTML, but use an empty
+ * string not &nbsp; for empty cells. Do not include the <td> and </td>.
+ *
+ * The current result row is available as $this->mCurrentRow, in case you
+ * need more context.
*
* @param string $name The database field name
* @param string $value The value retrieved from the database
- *
- * The current result row is available as $this->mCurrentRow, in case you need
- * more context.
*/
abstract function formatValue( $name, $value );
@@ -690,10 +706,10 @@ abstract class TablePager extends IndexPager {
abstract function getDefaultSort();
/**
- * An array mapping database field names to a textual description of the field
- * name, for use in the table header. The description should be plain text, it
- * will be HTML-escaped later.
+ * An array mapping database field names to a textual description of the
+ * field name, for use in the table header. The description should be plain
+ * text, it will be HTML-escaped later.
*/
abstract function getFieldNames();
}
-?>
+
diff --git a/includes/Parser.php b/includes/Parser.php
index 8e36e170..32e7f2a8 100644
--- a/includes/Parser.php
+++ b/includes/Parser.php
@@ -1,5 +1,7 @@
<?php
+
/**
+ *
* File for Parser and related classes
*
* @addtogroup Parser
@@ -10,7 +12,7 @@
* changes in an incompatible way, so the parser cache
* can automatically discard old data.
*/
-define( 'MW_PARSER_VERSION', '1.6.1' );
+define( 'MW_PARSER_VERSION', '1.6.2' );
define( 'RLH_FOR_UPDATE', 1 );
@@ -95,8 +97,9 @@ class Parser
* @private
*/
# Persistent:
- var $mTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables;
-
+ var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables,
+ $mImageParams, $mImageParamsMagicArray;
+
# Cleared with clearState():
var $mOutput, $mAutonumber, $mDTopen, $mStripState;
var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
@@ -126,11 +129,12 @@ class Parser
*/
function Parser() {
$this->mTagHooks = array();
+ $this->mTransparentTagHooks = array();
$this->mFunctionHooks = array();
$this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
$this->mFirstCall = true;
}
-
+
/**
* Do various kinds of initialisation on the first call of the parser
*/
@@ -138,12 +142,12 @@ class Parser
if ( !$this->mFirstCall ) {
return;
}
-
+
wfProfileIn( __METHOD__ );
global $wgAllowDisplayTitle, $wgAllowSlowParserFunctions;
-
+
$this->setHook( 'pre', array( $this, 'renderPreTag' ) );
-
+
$this->setFunctionHook( 'int', array( 'CoreParserFunctions', 'intFunction' ), SFH_NO_HASH );
$this->setFunctionHook( 'ns', array( 'CoreParserFunctions', 'ns' ), SFH_NO_HASH );
$this->setFunctionHook( 'urlencode', array( 'CoreParserFunctions', 'urlencode' ), SFH_NO_HASH );
@@ -306,7 +310,7 @@ class Parser
$fixtags = array(
# french spaces, last one Guillemet-left
# only if there is something before the space
- '/(.) (?=\\?|:|;|!|\\302\\273)/' => '\\1&nbsp;\\2',
+ '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&nbsp;\\2',
# french spaces, Guillemet-right
'/(\\302\\253) /' => '\\1&nbsp;',
);
@@ -327,6 +331,26 @@ class Parser
wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
+//!JF Move to its own function
+
+ $uniq_prefix = $this->mUniqPrefix;
+ $matches = array();
+ $elements = array_keys( $this->mTransparentTagHooks );
+ $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
+
+ foreach( $matches as $marker => $data ) {
+ list( $element, $content, $params, $tag ) = $data;
+ $tagName = strtolower( $element );
+ if( isset( $this->mTransparentTagHooks[$tagName] ) ) {
+ $output = call_user_func_array( $this->mTransparentTagHooks[$tagName],
+ array( $content, $params, $this ) );
+ } else {
+ $output = $tag;
+ }
+ $this->mStripState->general->setPair( $marker, $output );
+ }
+ $text = $this->mStripState->unstripGeneral( $text );
+
$text = Sanitizer::normalizeCharReferences( $text );
if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) {
@@ -398,12 +422,15 @@ class Parser
* Expand templates and variables in the text, producing valid, static wikitext.
* Also removes comments.
*/
- function preprocess( $text, $title, $options ) {
+ function preprocess( $text, $title, $options, $revid = null ) {
wfProfileIn( __METHOD__ );
$this->clearState();
$this->setOutputType( OT_PREPROCESS );
$this->mOptions = $options;
$this->mTitle = $title;
+ if( $revid !== null ) {
+ $this->mRevisionId = $revid;
+ }
wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
$text = $this->strip( $text, $this->mStripState );
wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
@@ -449,7 +476,7 @@ class Parser
* @param $text Source text string.
* @param $uniq_prefix
*
- * @private
+ * @public
* @static
*/
function extractTagsAndParams($elements, $text, &$matches, $uniq_prefix = ''){
@@ -480,7 +507,7 @@ class Parser
$inside = $p[4];
}
- $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . '-QINU';
+ $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . "-QINU\x07";
$stripped .= $marker;
if ( $close === '/>' ) {
@@ -587,7 +614,8 @@ class Parser
$output = Xml::escapeTagsOnly( $content );
break;
case 'math':
- $output = $wgContLang->armourMath( MathRenderer::renderMath( $content ) );
+ $output = $wgContLang->armourMath(
+ MathRenderer::renderMath( $content, $params ) );
break;
case 'gallery':
$output = $this->renderImageGallery( $content, $params );
@@ -725,7 +753,7 @@ class Parser
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
- 2 => array('file', '/dev/null', 'a') // FIXME: this line in UNIX-specific, it generates a warning on Windows, because /dev/null is not a valid Windows file.
+ 2 => array('file', wfGetNull(), 'a')
);
$pipes = array();
$process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
@@ -1000,7 +1028,7 @@ class Parser
$text = strtr( $text, array( '<noinclude>' => '', '</noinclude>' => '') );
$text = StringUtils::delimiterReplace( '<includeonly>', '</includeonly>', '', $text );
- $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
+ $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ), array(), array_keys( $this->mTransparentTagHooks ) );
$text = $this->replaceVariables( $text, $args );
wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
@@ -1797,11 +1825,15 @@ class Parser
$this->mOutput->addImage( $nt->getDBkey() );
continue;
} elseif( $ns == NS_SPECIAL ) {
- $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
+ if( SpecialPage::exists( $nt->getDBkey() ) ) {
+ $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
+ } else {
+ $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
+ }
continue;
} elseif( $ns == NS_IMAGE ) {
- $img = new Image( $nt );
- if( $img->exists() ) {
+ $img = wfFindFile( $nt );
+ if( $img ) {
// Force a blue link if the file exists; may be a remote
// upload on the shared repository, and we want to see its
// auto-generated page.
@@ -1918,15 +1950,22 @@ class Parser
wfProfileIn( $fname );
$ret = $target; # default return value is no change
- # bug 7425
- $target = trim( $target );
-
# Some namespaces don't allow subpages,
# so only perform processing if subpages are allowed
if( $this->areSubpagesAllowed() ) {
+ $hash = strpos( $target, '#' );
+ if( $hash !== false ) {
+ $suffix = substr( $target, $hash );
+ $target = substr( $target, 0, $hash );
+ } else {
+ $suffix = '';
+ }
+ # bug 7425
+ $target = trim( $target );
# Look at the first character
if( $target != '' && $target{0} == '/' ) {
# / at end means we don't want the slash to be shown
+ $m = array();
$trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
if( $trailingSlashes ) {
$noslash = $target = substr( $target, 1, -strlen($m[0][0]) );
@@ -1934,9 +1973,9 @@ class Parser
$noslash = substr( $target, 1 );
}
- $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash);
+ $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash) . $suffix;
if( '' === $text ) {
- $text = $target;
+ $text = $target . $suffix;
} # this might be changed for ugliness reasons
} else {
# check for .. subpage backlinks
@@ -1954,13 +1993,14 @@ class Parser
if( substr( $nodotdot, -1, 1 ) == '/' ) {
$nodotdot = substr( $nodotdot, 0, -1 );
if( '' === $text ) {
- $text = $nodotdot;
+ $text = $nodotdot . $suffix;
}
}
$nodotdot = trim( $nodotdot );
if( $nodotdot != '' ) {
$ret .= '/' . $nodotdot;
}
+ $ret .= $suffix;
}
}
}
@@ -2406,6 +2446,8 @@ class Parser
$oldtz = getenv( 'TZ' );
putenv( 'TZ='.$wgLocaltimezone );
}
+
+ wfSuppressWarnings(); // E_STRICT system time bitching
$localTimestamp = date( 'YmdHis', $ts );
$localMonth = date( 'm', $ts );
$localMonthName = date( 'n', $ts );
@@ -2418,20 +2460,21 @@ class Parser
if ( isset( $wgLocaltimezone ) ) {
putenv( 'TZ='.$oldtz );
}
+ wfRestoreWarnings();
switch ( $index ) {
case 'currentmonth':
- return $varCache[$index] = $wgContLang->formatNum( date( 'm', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'm', $ts ) );
case 'currentmonthname':
- return $varCache[$index] = $wgContLang->getMonthName( date( 'n', $ts ) );
+ return $varCache[$index] = $wgContLang->getMonthName( gmdate( 'n', $ts ) );
case 'currentmonthnamegen':
- return $varCache[$index] = $wgContLang->getMonthNameGen( date( 'n', $ts ) );
+ return $varCache[$index] = $wgContLang->getMonthNameGen( gmdate( 'n', $ts ) );
case 'currentmonthabbrev':
- return $varCache[$index] = $wgContLang->getMonthAbbreviation( date( 'n', $ts ) );
+ return $varCache[$index] = $wgContLang->getMonthAbbreviation( gmdate( 'n', $ts ) );
case 'currentday':
- return $varCache[$index] = $wgContLang->formatNum( date( 'j', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'j', $ts ) );
case 'currentday2':
- return $varCache[$index] = $wgContLang->formatNum( date( 'd', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'd', $ts ) );
case 'localmonth':
return $varCache[$index] = $wgContLang->formatNum( $localMonth );
case 'localmonthname':
@@ -2445,25 +2488,25 @@ class Parser
case 'localday2':
return $varCache[$index] = $wgContLang->formatNum( $localDay2 );
case 'pagename':
- return $this->mTitle->getText();
+ return wfEscapeWikiText( $this->mTitle->getText() );
case 'pagenamee':
return $this->mTitle->getPartialURL();
case 'fullpagename':
- return $this->mTitle->getPrefixedText();
+ return wfEscapeWikiText( $this->mTitle->getPrefixedText() );
case 'fullpagenamee':
return $this->mTitle->getPrefixedURL();
case 'subpagename':
- return $this->mTitle->getSubpageText();
+ return wfEscapeWikiText( $this->mTitle->getSubpageText() );
case 'subpagenamee':
return $this->mTitle->getSubpageUrlForm();
case 'basepagename':
- return $this->mTitle->getBaseText();
+ return wfEscapeWikiText( $this->mTitle->getBaseText() );
case 'basepagenamee':
return wfUrlEncode( str_replace( ' ', '_', $this->mTitle->getBaseText() ) );
case 'talkpagename':
if( $this->mTitle->canTalk() ) {
$talkPage = $this->mTitle->getTalkPage();
- return $talkPage->getPrefixedText();
+ return wfEscapeWikiText( $talkPage->getPrefixedText() );
} else {
return '';
}
@@ -2476,7 +2519,7 @@ class Parser
}
case 'subjectpagename':
$subjPage = $this->mTitle->getSubjectPage();
- return $subjPage->getPrefixedText();
+ return wfEscapeWikiText( $subjPage->getPrefixedText() );
case 'subjectpagenamee':
$subjPage = $this->mTitle->getSubjectPage();
return $subjPage->getPrefixedUrl();
@@ -2505,19 +2548,19 @@ class Parser
case 'subjectspacee':
return( wfUrlencode( $this->mTitle->getSubjectNsText() ) );
case 'currentdayname':
- return $varCache[$index] = $wgContLang->getWeekdayName( date( 'w', $ts ) + 1 );
+ return $varCache[$index] = $wgContLang->getWeekdayName( gmdate( 'w', $ts ) + 1 );
case 'currentyear':
- return $varCache[$index] = $wgContLang->formatNum( date( 'Y', $ts ), true );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'Y', $ts ), true );
case 'currenttime':
return $varCache[$index] = $wgContLang->time( wfTimestamp( TS_MW, $ts ), false, false );
case 'currenthour':
- return $varCache[$index] = $wgContLang->formatNum( date( 'H', $ts ), true );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'H', $ts ), true );
case 'currentweek':
// @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
// int to remove the padding
- return $varCache[$index] = $wgContLang->formatNum( (int)date( 'W', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( (int)gmdate( 'W', $ts ) );
case 'currentdow':
- return $varCache[$index] = $wgContLang->formatNum( date( 'w', $ts ) );
+ return $varCache[$index] = $wgContLang->formatNum( gmdate( 'w', $ts ) );
case 'localdayname':
return $varCache[$index] = $wgContLang->getWeekdayName( $localDayOfWeek + 1 );
case 'localyear':
@@ -3089,7 +3132,7 @@ class Parser
$found = false; //access denied
wfDebug( "$fname: template inclusion denied for " . $title->getPrefixedDBkey() );
} else {
- $articleContent = $this->fetchTemplate( $title );
+ list($articleContent,$title) = $this->fetchTemplateAndtitle( $title );
if ( $articleContent !== false ) {
$found = true;
$text = $articleContent;
@@ -3220,6 +3263,7 @@ class Parser
PREG_SPLIT_DELIM_CAPTURE);
$text = '';
$nsec = $headingOffset;
+
for( $i = 0; $i < count($m); $i += 2 ) {
$text .= $m[$i];
if (!isset($m[$i + 1]) || $m[$i + 1] == "") continue;
@@ -3255,13 +3299,26 @@ class Parser
/**
* Fetch the unparsed text of a template and register a reference to it.
*/
- function fetchTemplate( $title ) {
- $text = false;
+ function fetchTemplateAndtitle( $title ) {
+ $text = $skip = false;
+ $finalTitle = $title;
// Loop to fetch the article, with up to 1 redirect
for ( $i = 0; $i < 2 && is_object( $title ); $i++ ) {
- $rev = Revision::newFromTitle( $title );
- $this->mOutput->addTemplate( $title, $title->getArticleID() );
- if ( $rev ) {
+ # Give extensions a chance to select the revision instead
+ $id = false; // Assume current
+ wfRunHooks( 'BeforeParserFetchTemplateAndtitle', array( &$this, &$title, &$skip, &$id ) );
+
+ if( $skip ) {
+ $text = false;
+ $this->mOutput->addTemplate( $title, $title->getArticleID(), null );
+ break;
+ }
+ $rev = $id ? Revision::newFromId( $id ) : Revision::newFromTitle( $title );
+ $rev_id = $rev ? $rev->getId() : 0;
+
+ $this->mOutput->addTemplate( $title, $title->getArticleID(), $rev_id );
+
+ if( $rev ) {
$text = $rev->getText();
} elseif( $title->getNamespace() == NS_MEDIAWIKI ) {
global $wgLang;
@@ -3278,9 +3335,15 @@ class Parser
break;
}
// Redirect?
+ $finalTitle = $title;
$title = Title::newFromRedirect( $text );
}
- return $text;
+ return array($text,$finalTitle);
+ }
+
+ function fetchTemplate( $title ) {
+ $rv = $this->fetchTemplateAndtitle($title);
+ return $rv[0];
}
/**
@@ -3375,7 +3438,13 @@ class Parser
}
/**
- * Detect __TOC__ magic word and set a placeholder
+ * Find the first __TOC__ magic word and set a <!--MWTOC-->
+ * placeholder that will then be replaced by the real TOC in
+ * ->formatHeadings, this works because at this points real
+ * comments will have already been discarded by the sanitizer.
+ *
+ * Any additional __TOC__ magic words left over will be discarded
+ * as there can only be one TOC on the page.
*/
function stripToc( $text ) {
# if the string __NOTOC__ (not case-sensitive) occurs in the HTML,
@@ -3453,17 +3522,13 @@ class Parser
$enoughToc = true;
}
- # Never ever show TOC if no headers
- if( $numMatches < 1 ) {
- $enoughToc = false;
- }
-
# We need this to perform operations on the HTML
$sk = $this->mOptions->getSkin();
# headline counter
$headlineCount = 0;
$sectionCount = 0; # headlineCount excluding template sections
+ $numVisible = 0;
# Ugh .. the TOC should have neat indentation levels which can be
# passed to the skin functions. These are determined here
@@ -3504,7 +3569,9 @@ class Parser
$toclevel++;
$sublevelCount[$toclevel] = 0;
if( $toclevel<$wgMaxTocLevel ) {
+ $prevtoclevel = $toclevel;
$toc .= $sk->tocIndent();
+ $numVisible++;
}
}
elseif ( $level < $prevlevel && $toclevel > 1 ) {
@@ -3528,7 +3595,12 @@ class Parser
}
}
if( $toclevel<$wgMaxTocLevel ) {
- $toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
+ if($prevtoclevel < $wgMaxTocLevel) {
+ # Unindent only if the previous toc level was shown :p
+ $toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
+ } else {
+ $toc .= $sk->tocLineEnd();
+ }
}
}
else {
@@ -3569,12 +3641,21 @@ class Parser
"\$this->mInterwikiLinkHolders['texts'][\$1]",
$canonized_headline );
- # strip out HTML
- $canonized_headline = preg_replace( '/<.*?' . '>/','',$canonized_headline );
- $tocline = trim( $canonized_headline );
+ # Strip out HTML (other than plain <sup> and <sub>: bug 8393)
+ $tocline = preg_replace(
+ array( '#<(?!/?(sup|sub)).*?'.'>#', '#<(/?(sup|sub)).*?'.'>#' ),
+ array( '', '<$1>'),
+ $canonized_headline
+ );
+ $tocline = trim( $tocline );
+
+ # For the anchor, strip out HTML-y stuff period
+ $canonized_headline = preg_replace( '/<.*?'.'>/', '', $canonized_headline );
+ $canonized_headline = trim( $canonized_headline );
+
# Save headline for section edit hint before it's escaped
- $headline_hint = trim( $canonized_headline );
- $canonized_headline = Sanitizer::escapeId( $tocline );
+ $headline_hint = $canonized_headline;
+ $canonized_headline = Sanitizer::escapeId( $canonized_headline );
$refers[$headlineCount] = $canonized_headline;
# count how many in assoc. array so we can track dupes in anchors
@@ -3611,9 +3692,14 @@ class Parser
$sectionCount++;
}
+ # Never ever show TOC if no headers
+ if( $numVisible < 1 ) {
+ $enoughToc = false;
+ }
+
if( $enoughToc ) {
- if( $toclevel<$wgMaxTocLevel ) {
- $toc .= $sk->tocUnindent( $toclevel - 1 );
+ if( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
+ $toc .= $sk->tocUnindent( $prevtoclevel - 1 );
}
$toc = $sk->tocList( $toc );
}
@@ -3759,11 +3845,16 @@ class Parser
* @private
*/
function getUserSig( &$user ) {
+ global $wgMaxSigChars;
+
$username = $user->getName();
$nickname = $user->getOption( 'nickname' );
$nickname = $nickname === '' ? $username : $nickname;
-
- if( $user->getBoolOption( 'fancysig' ) !== false ) {
+
+ if( mb_strlen( $nickname ) > $wgMaxSigChars ) {
+ $nickname = $username;
+ wfDebug( __METHOD__ . ": $username has overlong signature.\n" );
+ } elseif( $user->getBoolOption( 'fancysig' ) !== false ) {
# Sig. might contain markup; validate this
if( $this->validateSig( $nickname ) !== false ) {
# Validated; clean up (if needed) and return it
@@ -3903,6 +3994,14 @@ class Parser
return $oldVal;
}
+ function setTransparentTagHook( $tag, $callback ) {
+ $tag = strtolower( $tag );
+ $oldVal = isset( $this->mTransparentTagHooks[$tag] ) ? $this->mTransparentTagHooks[$tag] : null;
+ $this->mTransparentTagHooks[$tag] = $callback;
+
+ return $oldVal;
+ }
+
/**
* Create a function, e.g. {{sum:1|2|3}}
* The callback function should have the form:
@@ -4018,6 +4117,8 @@ class Parser
$this->mOutput->addLink( $title, $id );
} elseif ( $linkCache->isBadLink( $pdbk ) ) {
$colours[$pdbk] = 0;
+ } elseif ( $title->getNamespace() == NS_SPECIAL && !SpecialPage::exists( $pdbk ) ) {
+ $colours[$pdbk] = 0;
} else {
# Not in the link cache, add it to the query
if ( !isset( $current ) ) {
@@ -4055,16 +4156,11 @@ class Parser
$linkCache->addGoodLinkObj( $s->page_id, $title );
$this->mOutput->addLink( $title, $s->page_id );
- if ( $threshold > 0 ) {
- $size = $s->page_len;
- if ( $s->page_is_redirect || $s->page_namespace != 0 || $size >= $threshold ) {
- $colours[$pdbk] = 1;
- } else {
- $colours[$pdbk] = 2;
- }
- } else {
- $colours[$pdbk] = 1;
- }
+ $colours[$pdbk] = ( $threshold == 0 || (
+ $s->page_len >= $threshold || # always true if $threshold <= 0
+ $s->page_is_redirect ||
+ !Namespace::isContent( $s->page_namespace ) )
+ ? 1 : 2 );
}
}
wfProfileOut( $fname.'-check' );
@@ -4104,7 +4200,7 @@ class Parser
}
// process categories, check if a category exists in some variant
- foreach( $categories as $category){
+ foreach( $categories as $category ){
$variants = $wgContLang->convertLinkToAllVariants($category);
foreach($variants as $variant){
if($variant != $category){
@@ -4324,8 +4420,11 @@ class Parser
$ig->setContextTitle( $this->mTitle );
$ig->setShowBytes( false );
$ig->setShowFilename( false );
- $ig->setParsing();
+ $ig->setParser( $this );
+ $ig->setHideBadImages();
+ $ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
$ig->useSkin( $this->mOptions->getSkin() );
+ $ig->mRevisionId = $this->mRevisionId;
if( isset( $params['caption'] ) ) {
$caption = $params['caption'];
@@ -4342,6 +4441,8 @@ class Parser
if( isset( $params['heights'] ) ) {
$ig->setHeights( $params['heights'] );
}
+
+ wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) );
$lines = explode( "\n", $text );
foreach ( $lines as $line ) {
@@ -4373,7 +4474,7 @@ class Parser
);
$html = $pout->getText();
- $ig->add( new Image( $nt ), $html );
+ $ig->add( $nt, $html );
# Only add real images (bug #5586)
if ( $nt->getNamespace() == NS_IMAGE ) {
@@ -4383,10 +4484,50 @@ class Parser
return $ig->toHTML();
}
+ function getImageParams( $handler ) {
+ if ( $handler ) {
+ $handlerClass = get_class( $handler );
+ } else {
+ $handlerClass = '';
+ }
+ if ( !isset( $this->mImageParams[$handlerClass] ) ) {
+ // Initialise static lists
+ static $internalParamNames = array(
+ 'horizAlign' => array( 'left', 'right', 'center', 'none' ),
+ 'vertAlign' => array( 'baseline', 'sub', 'super', 'top', 'text-top', 'middle',
+ 'bottom', 'text-bottom' ),
+ 'frame' => array( 'thumbnail', 'manualthumb', 'framed', 'frameless',
+ 'upright', 'border' ),
+ );
+ static $internalParamMap;
+ if ( !$internalParamMap ) {
+ $internalParamMap = array();
+ foreach ( $internalParamNames as $type => $names ) {
+ foreach ( $names as $name ) {
+ $magicName = str_replace( '-', '_', "img_$name" );
+ $internalParamMap[$magicName] = array( $type, $name );
+ }
+ }
+ }
+
+ // Add handler params
+ $paramMap = $internalParamMap;
+ if ( $handler ) {
+ $handlerParamMap = $handler->getParamMap();
+ foreach ( $handlerParamMap as $magic => $paramName ) {
+ $paramMap[$magic] = array( 'handler', $paramName );
+ }
+ }
+ $this->mImageParams[$handlerClass] = $paramMap;
+ $this->mImageParamsMagicArray[$handlerClass] = new MagicWordArray( array_keys( $paramMap ) );
+ }
+ return array( $this->mImageParams[$handlerClass], $this->mImageParamsMagicArray[$handlerClass] );
+ }
+
/**
* Parse image options text and use it to make an image
*/
- function makeImage( $nt, $options ) {
+ function makeImage( $title, $options ) {
# @TODO: let the MediaHandler specify its transform parameters
#
# Check if the options text is of the form "options|alt text"
@@ -4398,6 +4539,9 @@ class Parser
# * ___px scale to ___ pixels width, no aligning. e.g. use in taxobox
# * center center the image
# * framed Keep original image size, no magnify-button.
+ # * frameless like 'thumb' but without a frame. Keeps user preferences for width
+ # * upright reduce width for upright images, rounded to full __0 px
+ # * border draw a 1px border around the image
# vertical-align values (no % or length right now):
# * baseline
# * sub
@@ -4407,67 +4551,66 @@ class Parser
# * middle
# * bottom
# * text-bottom
+
+ $parts = array_map( 'trim', explode( '|', $options) );
+ $sk = $this->mOptions->getSkin();
+ # Give extensions a chance to select the file revision for us
+ $skip = $time = false;
+ wfRunHooks( 'BeforeParserMakeImageLinkObj', array( &$this, &$title, &$skip, &$time ) );
- $part = array_map( 'trim', explode( '|', $options) );
-
- $mwAlign = array();
- $alignments = array( 'left', 'right', 'center', 'none', 'baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom' );
- foreach ( $alignments as $alignment ) {
- $mwAlign[$alignment] =& MagicWord::get( 'img_'.$alignment );
+ if ( $skip ) {
+ return $sk->makeLinkObj( $title );
}
- $mwThumb =& MagicWord::get( 'img_thumbnail' );
- $mwManualThumb =& MagicWord::get( 'img_manualthumb' );
- $mwWidth =& MagicWord::get( 'img_width' );
- $mwFramed =& MagicWord::get( 'img_framed' );
- $mwPage =& MagicWord::get( 'img_page' );
- $caption = '';
- $params = array();
- $framed = $thumb = false;
- $manual_thumb = '' ;
- $align = $valign = '';
- $sk = $this->mOptions->getSkin();
+ # Get parameter map
+ $file = wfFindFile( $title, $time );
+ $handler = $file ? $file->getHandler() : false;
- foreach( $part as $val ) {
- if ( !is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
- $thumb=true;
- } elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
- # use manually specified thumbnail
- $thumb=true;
- $manual_thumb = $match;
- } else {
- foreach( $alignments as $alignment ) {
- if ( ! is_null( $mwAlign[$alignment]->matchVariableStartToEnd($val) ) ) {
- switch ( $alignment ) {
- case 'left': case 'right': case 'center': case 'none':
- $align = $alignment; break;
- default:
- $valign = $alignment;
- }
- continue 2;
- }
- }
- if ( ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
- # Select a page in a multipage document
- $params['page'] = $match;
- } elseif ( !isset( $params['width'] ) && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
- wfDebug( "img_width match: $match\n" );
- # $match is the image width in pixels
+ list( $paramMap, $mwArray ) = $this->getImageParams( $handler );
+
+ # Process the input parameters
+ $caption = '';
+ $params = array( 'frame' => array(), 'handler' => array(),
+ 'horizAlign' => array(), 'vertAlign' => array() );
+ foreach( $parts as $part ) {
+ list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part );
+ if ( isset( $paramMap[$magicName] ) ) {
+ list( $type, $paramName ) = $paramMap[$magicName];
+ $params[$type][$paramName] = $value;
+
+ // Special case; width and height come in one variable together
+ if( $type == 'handler' && $paramName == 'width' ) {
$m = array();
- if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
- $params['width'] = intval( $m[1] );
- $params['height'] = intval( $m[2] );
+ if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $value, $m ) ) {
+ $params[$type]['width'] = intval( $m[1] );
+ $params[$type]['height'] = intval( $m[2] );
} else {
- $params['width'] = intval($match);
+ $params[$type]['width'] = intval( $value );
}
- } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
- $framed=true;
- } else {
- $caption = $val;
+ }
+ } else {
+ $caption = $part;
+ }
+ }
+
+ # Process alignment parameters
+ if ( $params['horizAlign'] ) {
+ $params['frame']['align'] = key( $params['horizAlign'] );
+ }
+ if ( $params['vertAlign'] ) {
+ $params['frame']['valign'] = key( $params['vertAlign'] );
+ }
+
+ # Validate the handler parameters
+ if ( $handler ) {
+ foreach ( $params['handler'] as $name => $value ) {
+ if ( !$handler->validateParam( $name, $value ) ) {
+ unset( $params['handler'][$name] );
}
}
}
+
# Strip bad stuff out of the alt text
$alt = $this->replaceLinkHoldersText( $caption );
@@ -4477,8 +4620,18 @@ class Parser
$alt = $this->mStripState->unstripBoth( $alt );
$alt = Sanitizer::stripAllTags( $alt );
+ $params['frame']['alt'] = $alt;
+ $params['frame']['caption'] = $caption;
+
# Linker does the rest
- return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $params, $framed, $thumb, $manual_thumb, $valign );
+ $ret = $sk->makeImageLink2( $title, $file, $params['frame'], $params['handler'] );
+
+ # Give the handler a chance to modify the parser object
+ if ( $handler ) {
+ $handler->parserTransformHook( $this, $file );
+ }
+
+ return $ret;
}
/**
@@ -4517,7 +4670,7 @@ class Parser
/**#@+
* Accessor
*/
- function getTags() { return array_keys( $this->mTagHooks ); }
+ function getTags() { return array_merge( array_keys($this->mTransparentTagHooks), array_keys( $this->mTagHooks ) ); }
/**#@-*/
@@ -4537,6 +4690,10 @@ class Parser
* for "replace", the whole page with the section replaced.
*/
private function extractSections( $text, $section, $mode, $newtext='' ) {
+ # I.... _hope_ this is right.
+ # Otherwise, sometimes we don't have things initialized properly.
+ $this->clearState();
+
# strip NOWIKI etc. to avoid confusion (true-parameter causes HTML
# comments to be stripped as well)
$stripState = new StripState;
@@ -4554,7 +4711,7 @@ class Parser
# now that we can be sure that no pseudo-sections are in the source,
# split it up by section
$uniq = preg_quote( $this->uniqPrefix(), '/' );
- $comment = "(?:$uniq-!--.*?QINU)";
+ $comment = "(?:$uniq-!--.*?QINU\x07)";
$secs = preg_split(
"/
(
@@ -4717,7 +4874,6 @@ class Parser
: $this->mTitle->getPrefixedText();
}
}
-
}
/**
@@ -4770,5 +4926,3 @@ class StripState {
return $text;
}
}
-
-?>
diff --git a/includes/ParserCache.php b/includes/ParserCache.php
index 1489fcf9..129b7132 100644
--- a/includes/ParserCache.php
+++ b/includes/ParserCache.php
@@ -116,4 +116,4 @@ class ParserCache {
}
-?>
+
diff --git a/includes/ParserOptions.php b/includes/ParserOptions.php
index e335720f..2200bfea 100644
--- a/includes/ParserOptions.php
+++ b/includes/ParserOptions.php
@@ -116,4 +116,4 @@ class ParserOptions
}
}
-?>
+
diff --git a/includes/ParserOutput.php b/includes/ParserOutput.php
index 03f1819c..d4daf1d1 100644
--- a/includes/ParserOutput.php
+++ b/includes/ParserOutput.php
@@ -14,13 +14,18 @@ class ParserOutput
$mTitleText, # title text of the chosen language variant
$mLinks, # 2-D map of NS/DBK to ID for the links in the document. ID=zero for broken.
$mTemplates, # 2-D map of NS/DBK to ID for the template references. ID=zero for broken.
+ $mTemplateIds, # 2-D map of NS/DBK to rev ID for the template references. ID=zero for broken.
$mImages, # DB keys of the images used, in the array key only
$mExternalLinks, # External link URLs, in the key only
- $mHTMLtitle, # Display HTML title
- $mSubtitle, # Additional subtitle
$mNewSection, # Show a new section link?
$mNoGallery, # No gallery on category page? (__NOGALLERY__)
- $mHeadItems; # Items to put in the <head> section
+ $mHeadItems, # Items to put in the <head> section
+ $mOutputHooks; # Hook tags as per $wgParserOutputHooks
+
+ /**
+ * Overridden title for display
+ */
+ private $displayTitle = false;
function ParserOutput( $text = '', $languageLinks = array(), $categoryLinks = array(),
$containsOldMagic = false, $titletext = '' )
@@ -36,15 +41,15 @@ class ParserOutput
$this->mTemplates = array();
$this->mImages = array();
$this->mExternalLinks = array();
- $this->mHTMLtitle = "" ;
- $this->mSubtitle = "" ;
$this->mNewSection = false;
$this->mNoGallery = false;
$this->mHeadItems = array();
+ $this->mTemplateIds = array();
+ $this->mOutputHooks = array();
}
function getText() { return $this->mText; }
- function &getLanguageLinks() { return $this->mLanguageLinks; }
+ function &getLanguageLinks() { return $this->mLanguageLinks; }
function getCategoryLinks() { return array_keys( $this->mCategories ); }
function &getCategories() { return $this->mCategories; }
function getCacheTime() { return $this->mCacheTime; }
@@ -55,6 +60,7 @@ class ParserOutput
function &getExternalLinks() { return $this->mExternalLinks; }
function getNoGallery() { return $this->mNoGallery; }
function getSubtitle() { return $this->mSubtitle; }
+ function getOutputHooks() { return (array)$this->mOutputHooks; }
function containsOldMagic() { return $this->mContainsOldMagic; }
function setText( $text ) { return wfSetVar( $this->mText, $text ); }
@@ -63,13 +69,15 @@ class ParserOutput
function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic, $com ); }
function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
function setTitleText( $t ) { return wfSetVar($this->mTitleText, $t); }
- function setSubtitle( $st ) { return wfSetVar( $this->mSubtitle, $st ); }
function addCategory( $c, $sort ) { $this->mCategories[$c] = $sort; }
- function addImage( $name ) { $this->mImages[$name] = 1; }
function addLanguageLink( $t ) { $this->mLanguageLinks[] = $t; }
function addExternalLink( $url ) { $this->mExternalLinks[$url] = 1; }
+ function addOutputHook( $hook, $data = false ) {
+ $this->mOutputHooks[] = array( $hook, $data );
+ }
+
function setNewSection( $value ) {
$this->mNewSection = (bool)$value;
}
@@ -88,14 +96,22 @@ class ParserOutput
}
$this->mLinks[$ns][$dbk] = $id;
}
+
+ function addImage( $name ) {
+ $this->mImages[$name] = 1;
+ }
- function addTemplate( $title, $id ) {
+ function addTemplate( $title, $page_id, $rev_id ) {
$ns = $title->getNamespace();
$dbk = $title->getDBkey();
if ( !isset( $this->mTemplates[$ns] ) ) {
$this->mTemplates[$ns] = array();
}
- $this->mTemplates[$ns][$dbk] = $id;
+ $this->mTemplates[$ns][$dbk] = $page_id;
+ if ( !isset( $this->mTemplateIds[$ns] ) ) {
+ $this->mTemplateIds[$ns] = array();
+ }
+ $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
}
/**
@@ -128,6 +144,27 @@ class ParserOutput
$this->mHeadItems[] = $section;
}
}
+
+ /**
+ * Override the title to be used for display
+ * -- this is assumed to have been validated
+ * (check equal normalisation, etc.)
+ *
+ * @param string $text Desired title text
+ */
+ public function setDisplayTitle( $text ) {
+ $this->displayTitle = $text;
+ }
+
+ /**
+ * Get the title to be used for display
+ *
+ * @return string
+ */
+ public function getDisplayTitle() {
+ return $this->displayTitle;
+ }
+
}
-?>
+
diff --git a/includes/PatrolLog.php b/includes/PatrolLog.php
index a22839ff..35cb4a02 100644
--- a/includes/PatrolLog.php
+++ b/includes/PatrolLog.php
@@ -46,14 +46,19 @@ class PatrolLog {
# these conditions would have gone into recentchanges, which we aren't
# supposed to be updating
if( is_object( $skin ) ) {
- list( $cur, $prev, $auto ) = $params;
+ list( $cur, /* $prev */, $auto ) = $params;
# Standard link to the page in question
$link = $skin->makeLinkObj( $title );
- # Generate a diff link
- $bits[] = 'oldid=' . urlencode( $cur );
- $bits[] = 'diff=prev';
- $bits = implode( '&', $bits );
- $diff = $skin->makeLinkObj( $title, htmlspecialchars( wfMsg( 'patrol-log-diff', $cur ) ), $bits );
+ if( $title->exists() ) {
+ # Generate a diff link
+ $bits[] = 'oldid=' . urlencode( $cur );
+ $bits[] = 'diff=prev';
+ $bits = implode( '&', $bits );
+ $diff = $skin->makeKnownLinkObj( $title, htmlspecialchars( wfMsg( 'patrol-log-diff', $cur ) ), $bits );
+ } else {
+ # Don't bother with a diff link, it's useless
+ $diff = htmlspecialchars( wfMsg( 'patrol-log-diff', $cur ) );
+ }
# Indicate whether or not the patrolling was automatic
$auto = $auto ? wfMsgHtml( 'patrol-log-auto' ) : '';
# Put it all together
@@ -80,4 +85,3 @@ class PatrolLog {
}
-?> \ No newline at end of file
diff --git a/includes/Profiler.php b/includes/Profiler.php
index da3a82ed..8e1cd147 100644
--- a/includes/Profiler.php
+++ b/includes/Profiler.php
@@ -301,6 +301,9 @@ class Profiler {
* @static
*/
function logToDB($name, $timeSum, $eventCount) {
+ # Do not log anything if database is readonly (bug 5375)
+ if( wfReadOnly() ) { return; }
+
# Warning: $wguname is a live patch, it should be moved to Setup.php
global $wguname, $wgProfilePerHost;
@@ -361,4 +364,4 @@ class Profiler {
}
-?>
+
diff --git a/includes/ProfilerSimple.php b/includes/ProfilerSimple.php
index f43c7dfc..b07f2517 100644
--- a/includes/ProfilerSimple.php
+++ b/includes/ProfilerSimple.php
@@ -122,4 +122,4 @@ class ProfilerSimple extends Profiler {
}
}
}
-?>
+
diff --git a/includes/ProfilerSimpleUDP.php b/includes/ProfilerSimpleUDP.php
index 500f1cbd..7d2f7e21 100644
--- a/includes/ProfilerSimpleUDP.php
+++ b/includes/ProfilerSimpleUDP.php
@@ -37,4 +37,4 @@ class ProfilerSimpleUDP extends ProfilerSimple {
socket_sendto($sock,$packet,$plength,0x100,$wgUDPProfilerHost,$wgUDPProfilerPort);
}
}
-?>
+
diff --git a/includes/ProfilerStub.php b/includes/ProfilerStub.php
index 4cf0aa44..c41845a4 100644
--- a/includes/ProfilerStub.php
+++ b/includes/ProfilerStub.php
@@ -23,4 +23,4 @@ function wfGetProfilingOutput( $s, $e ) {}
function wfProfileClose() {}
$wgProfiling = false;
-?>
+
diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php
index 3cafbd55..c249ec12 100644
--- a/includes/ProtectionForm.php
+++ b/includes/ProtectionForm.php
@@ -76,11 +76,12 @@ class ProtectionForm {
}
function execute() {
- global $wgRequest;
+ global $wgRequest, $wgOut;
if( $wgRequest->wasPosted() ) {
if( $this->save() ) {
- global $wgOut;
- $wgOut->redirect( $this->mTitle->getFullUrl() );
+ $article = new Article( $this->mTitle );
+ $q = $article->isRedirect() ? 'redirect=no' : '';
+ $wgOut->redirect( $this->mTitle->getFullUrl( $q ) );
}
} else {
$this->show();
@@ -99,7 +100,7 @@ class ProtectionForm {
return;
}
- list( $cascadeSources, $restrictions ) = $this->mTitle->getCascadeProtectionSources();
+ list( $cascadeSources, /* $restrictions */ ) = $this->mTitle->getCascadeProtectionSources();
if ( "" != $err ) {
$wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
@@ -188,6 +189,13 @@ class ProtectionForm {
if( !$ok ) {
throw new FatalError( "Unknown error at restriction save time." );
}
+
+ if( $wgRequest->getCheck( 'mwProtectWatch' ) ) {
+ $this->mArticle->doWatch();
+ } elseif( $this->mTitle->userIsWatching() ) {
+ $this->mArticle->doUnwatch();
+ }
+
return $ok;
}
@@ -232,18 +240,18 @@ class ProtectionForm {
$out .= "</tbody>\n";
$out .= "</table>\n";
- global $wgEnableCascadingProtection;
-
- if ($wgEnableCascadingProtection)
- $out .= $this->buildCascadeInput();
-
$out .= "<table>\n";
$out .= "<tbody>\n";
+ global $wgEnableCascadingProtection;
+ if( $wgEnableCascadingProtection )
+ $out .= '<tr><td></td><td>' . $this->buildCascadeInput() . "</td></tr>\n";
+
$out .= $this->buildExpiryInput();
if( !$this->disabled ) {
$out .= "<tr><td>" . $this->buildReasonInput() . "</td></tr>\n";
+ $out .= "<tr><td></td><td>" . $this->buildWatchInput() . "</td></tr>\n";
$out .= "<tr><td></td><td>" . $this->buildSubmit() . "</td></tr>\n";
}
@@ -270,22 +278,28 @@ class ProtectionForm {
$out = wfOpenElement( 'select', $attribs );
foreach( $wgRestrictionLevels as $key ) {
- $out .= $this->buildOption( $key, $selected );
+ $out .= Xml::option( $this->getOptionLabel( $key ), $key, $key == $selected );
}
$out .= "</select>\n";
return $out;
}
- function buildOption( $key, $selected ) {
- $text = ( $key == '' )
- ? wfMsg( 'protect-default' )
- : wfMsg( "protect-level-$key" );
- $selectedAttrib = ($selected == $key)
- ? array( 'selected' => 'selected' )
- : array();
- return wfElement( 'option',
- array( 'value' => $key ) + $selectedAttrib,
- $text );
+ /**
+ * Prepare the label for a protection selector option
+ *
+ * @param string $permission Permission required
+ * @return string
+ */
+ private function getOptionLabel( $permission ) {
+ if( $permission == '' ) {
+ return wfMsg( 'protect-default' );
+ } else {
+ $key = "protect-level-{$permission}";
+ $msg = wfMsg( $key );
+ if( wfEmptyMsg( $key, $msg ) )
+ $msg = wfMsg( 'protect-fallback', $permission );
+ return $msg;
+ }
}
function buildReasonInput() {
@@ -309,22 +323,21 @@ class ProtectionForm {
}
function buildExpiryInput() {
- $id = 'mwProtect-expiry';
-
- $ci = "<tr> <td align=\"right\">";
- $ci .= wfElement( 'label', array (
- 'id' => "$id-label",
- 'for' => $id ),
- wfMsg( 'protectexpiry' ) );
- $ci .= "</td> <td align=\"left\">";
- $ci .= wfElement( 'input', array(
- 'size' => 60,
- 'name' => $id,
- 'id' => $id,
- 'value' => $this->mExpiry ) + $this->disabledAttrib );
- $ci .= "</td></tr>";
-
- return $ci;
+ $attribs = array( 'id' => 'expires' ) + $this->disabledAttrib;
+ return '<tr>'
+ . '<td><label for="expires">' . wfMsgExt( 'protectexpiry', array( 'parseinline' ) ) . '</label></td>'
+ . '<td>' . Xml::input( 'mwProtect-expiry', 60, $this->mExpiry, $attribs ) . '</td>'
+ . '</tr>';
+ }
+
+ function buildWatchInput() {
+ global $wgUser;
+ return Xml::checkLabel(
+ wfMsg( 'watchthis' ),
+ 'mwProtectWatch',
+ 'mwProtectWatch',
+ $this->mTitle->userIsWatching() || $wgUser->getOption( 'watchdefault' )
+ );
}
function buildSubmit() {
@@ -360,7 +373,7 @@ class ProtectionForm {
* @access private
*/
function showLogExtract( &$out ) {
- # Show relevant lines from the deletion log:
+ # Show relevant lines from the protection log:
$out->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'protect' ) ) . "</h2>\n" );
$logViewer = new LogViewer(
new LogReader(
@@ -369,6 +382,5 @@ class ProtectionForm {
'type' => 'protect' ) ) ) );
$logViewer->showList( $out );
}
-}
-?>
+} \ No newline at end of file
diff --git a/includes/ProxyTools.php b/includes/ProxyTools.php
index f72b640f..6585de42 100644
--- a/includes/ProxyTools.php
+++ b/includes/ProxyTools.php
@@ -260,4 +260,4 @@ function wfIsAOLProxy( $ip ) {
-?>
+
diff --git a/includes/QueryPage.php b/includes/QueryPage.php
index 143c8be6..06710b6d 100644
--- a/includes/QueryPage.php
+++ b/includes/QueryPage.php
@@ -25,6 +25,7 @@ $wgQueryPages = array(
array( 'MostcategoriesPage', 'Mostcategories' ),
array( 'MostimagesPage', 'Mostimages' ),
array( 'MostlinkedCategoriesPage', 'Mostlinkedcategories' ),
+ array( 'SpecialMostlinkedtemplates', 'Mostlinkedtemplates' ),
array( 'MostlinkedPage', 'Mostlinked' ),
array( 'MostrevisionsPage', 'Mostrevisions' ),
array( 'FewestrevisionsPage', 'Fewestrevisions' ),
@@ -33,6 +34,7 @@ $wgQueryPages = array(
array( 'UncategorizedCategoriesPage', 'Uncategorizedcategories' ),
array( 'UncategorizedPagesPage', 'Uncategorizedpages' ),
array( 'UncategorizedImagesPage', 'Uncategorizedimages' ),
+ array( 'UncategorizedTemplatesPage', 'Uncategorizedtemplates' ),
array( 'UnusedCategoriesPage', 'Unusedcategories' ),
array( 'UnusedimagesPage', 'Unusedimages' ),
array( 'WantedCategoriesPage', 'Wantedcategories' ),
@@ -332,7 +334,8 @@ class QueryPage {
$num = $dbr->numRows($res);
$this->preprocessResults( $dbr, $res );
- $sk = $wgUser->getSkin();
+
+ $wgOut->addHtml( XML::openElement( 'div', array('class' => 'mw-spcontent') ) );
# Top header and navigation
if( $shownavigation ) {
@@ -347,6 +350,7 @@ class QueryPage {
# No results to show, so don't bother with "showing X of Y" etc.
# -- just let the user know and give up now
$wgOut->addHtml( '<p>' . wfMsgHtml( 'specialpage-empty' ) . '</p>' );
+ $wgOut->addHtml( XML::closeElement( 'div' ) );
return;
}
}
@@ -365,6 +369,8 @@ class QueryPage {
if( $shownavigation ) {
$wgOut->addHtml( '<p>' . $paging . '</p>' );
}
+
+ $wgOut->addHtml( XML::closeElement( 'div' ) );
return $num;
}
@@ -397,7 +403,7 @@ class QueryPage {
? ' class="not-patrolled"'
: '';
$html[] = $this->listoutput
- ? $format
+ ? $line
: "<li{$attr}>{$line}</li>\n";
}
}
@@ -411,7 +417,7 @@ class QueryPage {
? ' class="not-patrolled"'
: '';
$html[] = $this->listoutput
- ? $format
+ ? $line
: "<li{$attr}>{$line}</li>\n";
}
}
@@ -428,11 +434,11 @@ class QueryPage {
}
function openList( $offset ) {
- return "<ol start='" . ( $offset + 1 ) . "' class='special'>";
+ return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
}
function closeList() {
- return '</ol>';
+ return "</ol>\n";
}
/**
@@ -525,4 +531,4 @@ class QueryPage {
}
}
-?>
+
diff --git a/includes/RawPage.php b/includes/RawPage.php
index 93484829..9df94e50 100644
--- a/includes/RawPage.php
+++ b/includes/RawPage.php
@@ -220,4 +220,4 @@ class RawPage {
return $text;
}
}
-?>
+
diff --git a/includes/RecentChange.php b/includes/RecentChange.php
index fced4343..79f32d0c 100644
--- a/includes/RecentChange.php
+++ b/includes/RecentChange.php
@@ -80,6 +80,31 @@ class RecentChange
return NULL;
}
}
+
+ /**
+ * Find the first recent change matching some specific conditions
+ *
+ * @param array $conds Array of conditions
+ * @param mixed $fname Override the method name in profiling/logs
+ * @return RecentChange
+ */
+ public static function newFromConds( $conds, $fname = false ) {
+ if( $fname === false )
+ $fname = __METHOD__;
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select(
+ 'recentchanges',
+ '*',
+ $conds,
+ $fname
+ );
+ if( $res instanceof ResultWrapper && $res->numRows() > 0 ) {
+ $row = $res->fetchObject();
+ $res->free();
+ return self::newFromRow( $row );
+ }
+ return null;
+ }
# Accessors
@@ -195,10 +220,11 @@ class RecentChange
global $wgUseEnotif;
if( $wgUseEnotif ) {
# this would be better as an extension hook
+ global $wgUser;
include_once( "UserMailer.php" );
$enotif = new EmailNotification();
$title = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
- $enotif->notifyOnPageChange( $title,
+ $enotif->notifyOnPageChange( $wgUser, $title,
$this->mAttribs['rc_timestamp'],
$this->mAttribs['rc_comment'],
$this->mAttribs['rc_minor'],
@@ -209,24 +235,30 @@ class RecentChange
wfRunHooks( 'RecentChange_save', array( &$this ) );
}
- # Marks a certain row as patrolled
- function markPatrolled( $rcid )
- {
- $fname = 'RecentChange::markPatrolled';
-
+ /**
+ * Mark a given change as patrolled
+ *
+ * @param mixed $change RecentChange or corresponding rc_id
+ */
+ public static function markPatrolled( $change ) {
+ $rcid = $change instanceof RecentChange
+ ? $change->mAttribs['rc_id']
+ : $change;
$dbw = wfGetDB( DB_MASTER );
-
- $dbw->update( 'recentchanges',
- array( /* SET */
+ $dbw->update(
+ 'recentchanges',
+ array(
'rc_patrolled' => 1
- ), array( /* WHERE */
+ ),
+ array(
'rc_id' => $rcid
- ), $fname
+ ),
+ __METHOD__
);
}
# Makes an entry in the database corresponding to an edit
- /*static*/ function notifyEdit( $timestamp, &$title, $minor, &$user, $comment,
+ public static function notifyEdit( $timestamp, &$title, $minor, &$user, $comment,
$oldId, $lastTimestamp, $bot = "default", $ip = '', $oldSize = 0, $newSize = 0,
$newId = 0)
{
@@ -280,10 +312,8 @@ class RecentChange
* Makes an entry in the database corresponding to page creation
* Note: the title object must be loaded with the new id using resetArticleID()
* @todo Document parameters and return
- * @public
- * @static
*/
- public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot = "default",
+ public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot = 'default',
$ip='', $size = 0, $newId = 0 )
{
if ( !$ip ) {
@@ -292,7 +322,7 @@ class RecentChange
$ip = '';
}
}
- if ( $bot == 'default' ) {
+ if ( $bot === 'default' ) {
$bot = $user->isAllowed( 'bot' );
}
@@ -331,7 +361,7 @@ class RecentChange
}
# Makes an entry in the database corresponding to a rename
- /*static*/ function notifyMove( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='', $overRedir = false )
+ public static function notifyMove( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='', $overRedir = false )
{
if ( !$ip ) {
$ip = wfGetIP();
@@ -372,17 +402,17 @@ class RecentChange
$rc->save();
}
- /* static */ function notifyMoveToNew( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='' ) {
+ public static function notifyMoveToNew( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='' ) {
RecentChange::notifyMove( $timestamp, $oldTitle, $newTitle, $user, $comment, $ip, false );
}
- /* static */ function notifyMoveOverRedirect( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='' ) {
+ public static function notifyMoveOverRedirect( $timestamp, &$oldTitle, &$newTitle, &$user, $comment, $ip='' ) {
RecentChange::notifyMove( $timestamp, $oldTitle, $newTitle, $user, $comment, $ip, true );
}
# A log entry is different to an edit in that previous revisions are
# not kept
- /*static*/ function notifyLog( $timestamp, &$title, &$user, $comment, $ip='',
+ public static function notifyLog( $timestamp, &$title, &$user, $comment, $ip='',
$type, $action, $target, $logComment, $params )
{
if ( !$ip ) {
@@ -595,4 +625,4 @@ class RecentChange
}
}
}
-?>
+
diff --git a/includes/RefreshLinksJob.php b/includes/RefreshLinksJob.php
new file mode 100644
index 00000000..367d994f
--- /dev/null
+++ b/includes/RefreshLinksJob.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Background job to update links for a given title.
+ */
+class RefreshLinksJob extends Job {
+
+ function __construct( $title, $params = '', $id = 0 ) {
+ parent::__construct( 'refreshLinks', $title, $params, $id );
+ }
+
+ /**
+ * Run a refreshLinks job
+ * @return boolean success
+ */
+ function run() {
+ global $wgParser;
+ wfProfileIn( __METHOD__ );
+
+ $linkCache =& LinkCache::singleton();
+ $linkCache->clear();
+
+ if ( is_null( $this->title ) ) {
+ $this->error = "refreshLinks: Invalid title";
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ $revision = Revision::newFromTitle( $this->title );
+ if ( !$revision ) {
+ $this->error = 'refreshLinks: Article not found "' . $this->title->getPrefixedDBkey() . '"';
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ wfProfileIn( __METHOD__.'-parse' );
+ $options = new ParserOptions;
+ $parserOutput = $wgParser->parse( $revision->getText(), $this->title, $options, true, true, $revision->getId() );
+ wfProfileOut( __METHOD__.'-parse' );
+ wfProfileIn( __METHOD__.'-update' );
+ $update = new LinksUpdate( $this->title, $parserOutput, false );
+ $update->doUpdate();
+ wfProfileOut( __METHOD__.'-update' );
+ wfProfileOut( __METHOD__ );
+ return true;
+ }
+}
+
diff --git a/includes/Revision.php b/includes/Revision.php
index 71f214e3..39470923 100644
--- a/includes/Revision.php
+++ b/includes/Revision.php
@@ -888,4 +888,4 @@ define( 'MW_REV_DELETED_USER', Revision::DELETED_USER );
define( 'MW_REV_DELETED_RESTRICTED', Revision::DELETED_RESTRICTED );
-?>
+
diff --git a/includes/Sanitizer.php b/includes/Sanitizer.php
index fa5416dc..f2dcbf94 100644
--- a/includes/Sanitizer.php
+++ b/includes/Sanitizer.php
@@ -330,6 +330,9 @@ $wgHtmlEntityAliases = array(
* @addtogroup Parser
*/
class Sanitizer {
+ const NONE = 0;
+ const INITIAL_NONLETTER = 1;
+
/**
* Cleans up HTML, removes dangerous tags and attributes, and
* removes HTML comments
@@ -339,7 +342,7 @@ class Sanitizer {
* @param array $args for the processing callback
* @return string
*/
- static function removeHTMLtags( $text, $processCallback = null, $args = array() ) {
+ static function removeHTMLtags( $text, $processCallback = null, $args = array(), $extratags = array() ) {
global $wgUseTidy;
static $htmlpairs, $htmlsingle, $htmlsingleonly, $htmlnest, $tabletags,
@@ -349,13 +352,13 @@ class Sanitizer {
if ( !$staticInitialised ) {
- $htmlpairs = array( # Tags that must be closed
+ $htmlpairs = array_merge( $extratags, array( # Tags that must be closed
'b', 'del', 'i', 'ins', 'u', 'font', 'big', 'small', 'sub', 'sup', 'h1',
'h2', 'h3', 'h4', 'h5', 'h6', 'cite', 'code', 'em', 's',
'strike', 'strong', 'tt', 'var', 'div', 'center',
'blockquote', 'ol', 'ul', 'dl', 'table', 'caption', 'pre',
'ruby', 'rt' , 'rb' , 'rp', 'p', 'span', 'u'
- );
+ ) );
$htmlsingle = array(
'br', 'hr', 'li', 'dt', 'dd'
);
@@ -566,6 +569,7 @@ class Sanitizer {
*
* - Discards attributes not on a whitelist for the given element
* - Unsafe style attributes are discarded
+ * - Invalid id attributes are reencoded
*
* @param array $attribs
* @param string $element
@@ -575,7 +579,27 @@ class Sanitizer {
* @todo Check for unique id attribute :P
*/
static function validateTagAttributes( $attribs, $element ) {
- $whitelist = array_flip( Sanitizer::attributeWhitelist( $element ) );
+ return Sanitizer::validateAttributes( $attribs,
+ Sanitizer::attributeWhitelist( $element ) );
+ }
+
+ /**
+ * Take an array of attribute names and values and normalize or discard
+ * illegal values for the given whitelist.
+ *
+ * - Discards attributes not the given whitelist
+ * - Unsafe style attributes are discarded
+ * - Invalid id attributes are reencoded
+ *
+ * @param array $attribs
+ * @param array $whitelist list of allowed attribute names
+ * @return array
+ *
+ * @todo Check for legal values where the DTD limits things.
+ * @todo Check for unique id attribute :P
+ */
+ static function validateAttributes( $attribs, $whitelist ) {
+ $whitelist = array_flip( $whitelist );
$out = array();
foreach( $attribs as $attribute => $value ) {
if( !isset( $whitelist[$attribute] ) ) {
@@ -602,6 +626,33 @@ class Sanitizer {
}
/**
+ * Merge two sets of HTML attributes.
+ * Conflicting items in the second set will override those
+ * in the first, except for 'class' attributes which will be
+ * combined.
+ *
+ * @todo implement merging for other attributes such as style
+ * @param array $a
+ * @param array $b
+ * @return array
+ */
+ static function mergeAttributes( $a, $b ) {
+ $out = array_merge( $a, $b );
+ if( isset( $a['class'] )
+ && isset( $b['class'] )
+ && $a['class'] !== $b['class'] ) {
+
+ $out['class'] = implode( ' ',
+ array_unique(
+ preg_split( '/\s+/',
+ $a['class'] . ' ' . $b['class'],
+ -1,
+ PREG_SPLIT_NO_EMPTY ) ) );
+ }
+ return $out;
+ }
+
+ /**
* Pick apart some CSS and check it for forbidden or unsafe structures.
* Returns a sanitized string, or false if it was just too evil.
*
@@ -730,20 +781,29 @@ class Sanitizer {
* name attributes
* @see http://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with the id attribute
*
- * @static
- *
- * @param string $id
+ * @param string $id Id to validate
+ * @param int $flags Currently only two values: Sanitizer::INITIAL_NONLETTER
+ * (default) permits initial non-letter characters,
+ * such as if you're adding a prefix to them.
+ * Sanitizer::NONE will prepend an 'x' if the id
+ * would otherwise start with a nonletter.
* @return string
*/
- static function escapeId( $id ) {
+ static function escapeId( $id, $flags = Sanitizer::INITIAL_NONLETTER ) {
static $replace = array(
'%3A' => ':',
'%' => '.'
);
$id = urlencode( Sanitizer::decodeCharReferences( strtr( $id, ' ', '_' ) ) );
-
- return str_replace( array_keys( $replace ), array_values( $replace ), $id );
+ $id = str_replace( array_keys( $replace ), array_values( $replace ), $id );
+
+ if( ~$flags & Sanitizer::INITIAL_NONLETTER
+ && !preg_match( '/[a-zA-Z]/', $id[0] ) ) {
+ // Initial character must be a letter!
+ $id = "x$id";
+ }
+ return $id;
}
/**
@@ -1159,6 +1219,11 @@ class Sanitizer {
# 11.2.6
'td' => array_merge( $common, $tablecell, $tablealign ),
'th' => array_merge( $common, $tablecell, $tablealign ),
+
+ # 13.2
+ # Not usually allowed, but may be used for extension-style hooks
+ # such as <math> when it is rasterized
+ 'img' => array_merge( $common, array( 'alt' ) ),
# 15.2.1
'tt' => $common,
@@ -1185,6 +1250,11 @@ class Sanitizer {
'rb' => $common,
'rt' => $common, #array_merge( $common, array( 'rbspan' ) ),
'rp' => $common,
+
+ # MathML root element, where used for extensions
+ # 'title' may not be 100% valid here; it's XHTML
+ # http://www.w3.org/TR/REC-MathML/
+ 'math' => array( 'class', 'style', 'id', 'title' ),
);
return $whitelist;
}
@@ -1274,4 +1344,4 @@ class Sanitizer {
}
-?>
+
diff --git a/includes/SearchEngine.php b/includes/SearchEngine.php
index 24795ba9..11fc3deb 100644
--- a/includes/SearchEngine.php
+++ b/includes/SearchEngine.php
@@ -40,12 +40,10 @@ class SearchEngine {
* If an exact title match can be find, or a very slightly close match,
* return the title. If no match, returns NULL.
*
- * @static
* @param string $term
* @return Title
- * @private
*/
- function getNearMatch( $searchterm ) {
+ public static function getNearMatch( $searchterm ) {
global $wgContLang;
$allSearchTerms = array($searchterm);
@@ -124,8 +122,8 @@ class SearchEngine {
# There may have been a funny upload, or it may be on a shared
# file repository such as Wikimedia Commons.
if( $title->getNamespace() == NS_IMAGE ) {
- $image = new Image( $title );
- if( $image->exists() ) {
+ $image = wfFindFile( $title );
+ if( $image ) {
return $title;
}
}
@@ -176,9 +174,8 @@ class SearchEngine {
/**
* Make a list of searchable namespaces and their canonical names.
* @return array
- * @access public
*/
- function searchableNamespaces() {
+ public static function searchableNamespaces() {
global $wgContLang;
$arr = array();
foreach( $wgContLang->getNamespaces() as $ns => $name ) {
@@ -325,6 +322,14 @@ class SearchResultSet {
function next() {
return false;
}
+
+ /**
+ * Frees the result set, if applicable.
+ * @ access public
+ */
+ function free() {
+ // ...
+ }
}
@@ -366,4 +371,4 @@ class SearchEngineDummy {
function searchtitle() {}
function searchtext() {}
}
-?>
+
diff --git a/includes/SearchMySQL.php b/includes/SearchMySQL.php
index 0e02a684..905075ef 100644
--- a/includes/SearchMySQL.php
+++ b/includes/SearchMySQL.php
@@ -200,6 +200,10 @@ class MySQLSearchResultSet extends SearchResultSet {
return new SearchResult( $row );
}
}
+
+ function free() {
+ $this->mResultSet->free();
+ }
}
-?>
+
diff --git a/includes/SearchMySQL4.php b/includes/SearchMySQL4.php
index 97ce3850..6d2bbfef 100644
--- a/includes/SearchMySQL4.php
+++ b/includes/SearchMySQL4.php
@@ -65,4 +65,4 @@ class SearchMySQL4 extends SearchMySQL {
return " MATCH($field) AGAINST('$searchon' IN BOOLEAN MODE) ";
}
}
-?>
+
diff --git a/includes/SearchOracle.php b/includes/SearchOracle.php
index c9a675e6..95c59288 100644
--- a/includes/SearchOracle.php
+++ b/includes/SearchOracle.php
@@ -232,4 +232,4 @@ class OracleSearchResultSet extends SearchResultSet {
}
}
-?>
+
diff --git a/includes/SearchPostgres.php b/includes/SearchPostgres.php
index 7c3580e7..cf9e6981 100644
--- a/includes/SearchPostgres.php
+++ b/includes/SearchPostgres.php
@@ -64,6 +64,7 @@ class SearchPostgres extends SearchEngine {
$term = preg_replace('/:/', ' ', $term);
$searchstring = '';
+ $m = array();
if( preg_match_all('/([-!]?)(\S+)\s*/', $term, $m, PREG_SET_ORDER ) ) {
foreach( $m as $terms ) {
if (strlen($terms[1])) {
@@ -232,4 +233,4 @@ class PostgresSearchResultSet extends SearchResultSet {
}
-?>
+
diff --git a/includes/SearchTsearch2.php b/includes/SearchTsearch2.php
index b504f034..06eaa72d 100644
--- a/includes/SearchTsearch2.php
+++ b/includes/SearchTsearch2.php
@@ -119,4 +119,4 @@ class SearchTsearch2 extends SearchEngine {
}
-?>
+
diff --git a/includes/SearchUpdate.php b/includes/SearchUpdate.php
index 724197c1..849d6dc7 100644
--- a/includes/SearchUpdate.php
+++ b/includes/SearchUpdate.php
@@ -112,4 +112,4 @@ class SearchUpdateMyISAM extends SearchUpdate {
# Inherits everything
}
-?>
+
diff --git a/includes/Setup.php b/includes/Setup.php
index 47ba494f..66bae0a8 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -28,8 +28,8 @@ if ( !isset( $wgVersion ) ) {
}
// Set various default paths sensibly...
-if( $wgScript === false ) $wgScript = "$wgScriptPath/index.php";
-if( $wgRedirectScript === false ) $wgRedirectScript = "$wgScriptPath/redirect.php";
+if( $wgScript === false ) $wgScript = "$wgScriptPath/index$wgScriptExtension";
+if( $wgRedirectScript === false ) $wgRedirectScript = "$wgScriptPath/redirect$wgScriptExtension";
if( $wgArticlePath === false ) {
if( $wgUsePathInfo ) {
@@ -54,6 +54,67 @@ if( $wgTmpDirectory === false ) $wgTmpDirectory = "{$wgUploadDirectory}/tmp";
if( $wgReadOnlyFile === false ) $wgReadOnlyFile = "{$wgUploadDirectory}/lock_yBgMBwiR";
if( $wgFileCacheDirectory === false ) $wgFileCacheDirectory = "{$wgUploadDirectory}/cache";
+if ( empty( $wgFileStore['deleted']['directory'] ) ) {
+ $wgFileStore['deleted']['directory'] = "{$wgUploadDirectory}/deleted";
+}
+
+
+/**
+ * Initialise $wgLocalFileRepo from backwards-compatible settings
+ */
+if ( !$wgLocalFileRepo ) {
+ $wgLocalFileRepo = array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'directory' => $wgUploadDirectory,
+ 'url' => $wgUploadBaseUrl ? $wgUploadBaseUrl . $wgUploadPath : $wgUploadPath,
+ 'hashLevels' => $wgHashedUploadDirectory ? 2 : 0,
+ 'thumbScriptUrl' => $wgThumbnailScriptPath,
+ 'transformVia404' => !$wgGenerateThumbnailOnParse,
+ 'initialCapital' => $wgCapitalLinks,
+ 'deletedDir' => $wgFileStore['deleted']['directory'],
+ 'deletedHashLevels' => $wgFileStore['deleted']['hash']
+ );
+}
+/**
+ * Initialise shared repo from backwards-compatible settings
+ */
+if ( $wgUseSharedUploads ) {
+ if ( $wgSharedUploadDBname ) {
+ $wgForeignFileRepos[] = array(
+ 'class' => 'ForeignDBRepo',
+ 'name' => 'shared',
+ 'directory' => $wgSharedUploadDirectory,
+ 'url' => $wgSharedUploadPath,
+ 'hashLevels' => $wgHashedSharedUploadDirectory ? 2 : 0,
+ 'thumbScriptUrl' => $wgSharedThumbnailScriptPath,
+ 'transformVia404' => !$wgGenerateThumbnailOnParse,
+ 'dbType' => $wgDBtype,
+ 'dbServer' => $wgDBserver,
+ 'dbUser' => $wgDBuser,
+ 'dbPassword' => $wgDBpassword,
+ 'dbName' => $wgSharedUploadDBname,
+ 'dbFlags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT,
+ 'tablePrefix' => $wgSharedUploadDBprefix,
+ 'hasSharedCache' => $wgCacheSharedUploads,
+ 'descBaseUrl' => $wgRepositoryBaseUrl,
+ 'fetchDescription' => $wgFetchCommonsDescriptions,
+ );
+ } else {
+ $wgForeignFileRepos[] = array(
+ 'class' => 'FSRepo',
+ 'name' => 'shared',
+ 'directory' => $wgSharedUploadDirectory,
+ 'url' => $wgSharedUploadPath,
+ 'hashLevels' => $wgHashedSharedUploadDirectory ? 2 : 0,
+ 'thumbScriptUrl' => $wgSharedThumbnailScriptPath,
+ 'transformVia404' => !$wgGenerateThumbnailOnParse,
+ 'descBaseUrl' => $wgRepositoryBaseUrl,
+ 'fetchDescription' => $wgFetchCommonsDescriptions,
+ );
+ }
+}
+
require_once( "$IP/includes/AutoLoader.php" );
wfProfileIn( $fname.'-exception' );
@@ -167,6 +228,10 @@ if ( !$wgDBservers ) {
$wgLoadBalancer = new StubObject( 'wgLoadBalancer', 'LoadBalancer',
array( $wgDBservers, false, $wgMasterWaitTimeout, true ) );
$wgContLang = new StubContLang;
+
+// Now that variant lists may be available...
+$wgRequest->interpolateTitle();
+
$wgUser = new StubUser;
$wgLang = new StubUserLang;
$wgOut = new StubObject( 'wgOut', 'OutputPage' );
@@ -199,6 +264,9 @@ $wgPostCommitUpdateList = array();
if ( $wgAjaxSearch ) $wgAjaxExportList[] = 'wfSajaxSearch';
if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch';
+if ( $wgAjaxUploadDestCheck ) $wgAjaxExportList[] = 'UploadForm::ajaxGetExistsWarning';
+if( $wgAjaxLicensePreview )
+ $wgAjaxExportList[] = 'UploadForm::ajaxGetLicensePreview';
wfSeedRandom();
@@ -232,4 +300,4 @@ $wgFullyInitialised = true;
wfProfileOut( $fname.'-extensions' );
wfProfileOut( $fname );
-?>
+
diff --git a/includes/SiteConfiguration.php b/includes/SiteConfiguration.php
index 0968460c..353f5b3a 100644
--- a/includes/SiteConfiguration.php
+++ b/includes/SiteConfiguration.php
@@ -116,4 +116,4 @@ class SiteConfiguration {
}
}
-?>
+
diff --git a/includes/SiteStats.php b/includes/SiteStats.php
index e320a196..d7b9161a 100644
--- a/includes/SiteStats.php
+++ b/includes/SiteStats.php
@@ -5,7 +5,7 @@
*/
class SiteStats {
static $row, $loaded = false;
- static $admins;
+ static $admins, $jobs;
static $pageCount = array();
static function recache() {
@@ -27,24 +27,26 @@ class SiteStats {
$dbr = wfGetDB( DB_SLAVE );
self::$row = $dbr->selectRow( 'site_stats', '*', false, __METHOD__ );
}
+
+ self::$loaded = true;
}
static function loadAndLazyInit() {
wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
$row = self::doLoad( wfGetDB( DB_SLAVE ) );
-
- if( $row === false ) {
- // Might have just been initialzed during this request?
- wfDebug( __METHOD__ . ": site_stats missing on slave\n" );
+
+ if( !self::isSane( $row ) ) {
+ // Might have just been initialized during this request? Underflow?
+ wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" );
$row = self::doLoad( wfGetDB( DB_MASTER ) );
}
- if( $row === false ) {
+ if( !self::isSane( $row ) ) {
// Normally the site_stats table is initialized at install time.
- // Some manual construction scenarios may leave the table empty,
- // however, for instance when importing from a dump into a clean
- // schema with mwdumper.
- wfDebug( __METHOD__ . ": initializing empty site_stats\n" );
+ // Some manual construction scenarios may leave the table empty or
+ // broken, however, for instance when importing from a dump into a
+ // clean schema with mwdumper.
+ wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
global $IP;
require_once "$IP/maintenance/initStats.inc";
@@ -56,8 +58,8 @@ class SiteStats {
$row = self::doLoad( wfGetDB( DB_MASTER ) );
}
- if( $row === false ) {
- wfDebug( __METHOD__ . ": init of site_stats failed o_O\n" );
+ if( !self::isSane( $row ) ) {
+ wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O\n" );
}
return $row;
}
@@ -104,6 +106,18 @@ class SiteStats {
return self::$admins;
}
+ static function jobs() {
+ if ( !isset( self::$jobs ) ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ self::$jobs = $dbr->estimateRowCount('job');
+ /* Zero rows still do single row read for row that doesn't exist, but people are annoyed by that */
+ if (self::$jobs == 1) {
+ self::$jobs = 0;
+ }
+ }
+ return self::$jobs;
+ }
+
static function pagesInNs( $ns ) {
wfProfileIn( __METHOD__ );
if( !isset( self::$pageCount[$ns] ) ) {
@@ -114,6 +128,28 @@ class SiteStats {
return $pageCount[$ns];
}
+ /** Is the provided row of site stats sane, or should it be regenerated? */
+ private static function isSane( $row ) {
+ if(
+ $row === false
+ or $row->ss_total_pages < $row->ss_good_articles
+ or $row->ss_total_edits < $row->ss_total_pages
+ or $row->ss_users < $row->ss_admins
+ ) {
+ return false;
+ }
+ // Now check for underflow/overflow
+ foreach( array( 'total_views', 'total_edits', 'good_articles',
+ 'total_pages', 'users', 'admins', 'images' ) as $member ) {
+ if(
+ $row->{"ss_$member"} > 2000000000
+ or $row->{"ss_$member"} < 0
+ ) {
+ return false;
+ }
+ }
+ return true;
+ }
}
@@ -200,4 +236,4 @@ class SiteStatsUpdate {
*/
}
}
-?>
+
diff --git a/includes/Skin.php b/includes/Skin.php
index 0ca95f7e..f9e17057 100644
--- a/includes/Skin.php
+++ b/includes/Skin.php
@@ -22,6 +22,7 @@ class Skin extends Linker {
var $rcMoveIndex;
var $mWatchLinkNum = 0; // Appended to end of watch link id's
/**#@-*/
+ protected $mRevisionId; // The revision ID we're looking at, null if not applicable.
protected $skinname = 'standard' ;
/** Constructor, call parent constructor */
@@ -292,19 +293,21 @@ class Skin extends Linker {
* The odd calling convention is for backwards compatibility
*/
static function makeGlobalVariablesScript( $data ) {
- global $wgStylePath, $wgUser;
+ global $wgScript, $wgStylePath, $wgUser;
global $wgArticlePath, $wgScriptPath, $wgServer, $wgContLang, $wgLang;
global $wgTitle, $wgCanonicalNamespaceNames, $wgOut, $wgArticle;
global $wgBreakFrames, $wgRequest;
+ global $wgUseAjax, $wgAjaxWatch;
$ns = $wgTitle->getNamespace();
$nsname = isset( $wgCanonicalNamespaceNames[ $ns ] ) ? $wgCanonicalNamespaceNames[ $ns ] : $wgTitle->getNsText();
-
+
$vars = array(
'skin' => $data['skinname'],
'stylepath' => $wgStylePath,
'wgArticlePath' => $wgArticlePath,
'wgScriptPath' => $wgScriptPath,
+ 'wgScript' => $wgScript,
'wgServer' => $wgServer,
'wgCanonicalNamespace' => $nsname,
'wgCanonicalSpecialPageName' => SpecialPage::resolveAlias( $wgTitle->getDBKey() ),
@@ -312,6 +315,8 @@ class Skin extends Linker {
'wgPageName' => $wgTitle->getPrefixedDBKey(),
'wgTitle' => $wgTitle->getText(),
'wgAction' => $wgRequest->getText( 'action', 'view' ),
+ 'wgRestrictionEdit' => $wgTitle->getRestrictions( 'edit' ),
+ 'wgRestrictionMove' => $wgTitle->getRestrictions( 'move' ),
'wgArticleId' => $wgTitle->getArticleId(),
'wgIsArticle' => $wgOut->isArticle(),
'wgUserName' => $wgUser->isAnon() ? NULL : $wgUser->getName(),
@@ -330,24 +335,33 @@ class Skin extends Linker {
$vars['wgLivepreviewMessageError'] = wfMsg( 'livepreview-error' );
}
+ if($wgUseAjax && $wgAjaxWatch && $wgUser->isLoggedIn() ) {
+ $msgs = (object)array();
+ foreach ( array( 'watch', 'unwatch', 'watching', 'unwatching' ) as $msgName ) {
+ $msgs->{$msgName . 'Msg'} = wfMsg( $msgName );
+ }
+ $vars['wgAjaxWatch'] = $msgs;
+ }
+
return self::makeVariablesScript( $vars );
}
- function getHeadScripts() {
- global $wgStylePath, $wgUser, $wgAllowUserJs, $wgJsMimeType, $wgStyleVersion;
+ function getHeadScripts( $allowUserJs ) {
+ global $wgStylePath, $wgUser, $wgJsMimeType, $wgStyleVersion;
$r = self::makeGlobalVariablesScript( array( 'skinname' => $this->getSkinName() ) );
$r .= "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/wikibits.js?$wgStyleVersion\"></script>\n";
global $wgUseSiteJs;
if ($wgUseSiteJs) {
- if ($wgUser->isLoggedIn()) {
- $r .= "<script type=\"$wgJsMimeType\" src=\"".htmlspecialchars(self::makeUrl('-','action=raw&smaxage=0&gen=js'))."\"><!-- site js --></script>\n";
- } else {
- $r .= "<script type=\"$wgJsMimeType\" src=\"".htmlspecialchars(self::makeUrl('-','action=raw&gen=js'))."\"><!-- site js --></script>\n";
- }
- }
- if( $wgAllowUserJs && $wgUser->isLoggedIn() ) {
+ $jsCache = $wgUser->isLoggedIn() ? '&smaxage=0' : '';
+ $r .= "<script type=\"$wgJsMimeType\" src=\"".
+ htmlspecialchars(self::makeUrl('-',
+ "action=raw$jsCache&gen=js&useskin=" .
+ urlencode( $this->getSkinName() ) ) ) .
+ "\"><!-- site js --></script>\n";
+ }
+ if( $allowUserJs && $wgUser->isLoggedIn() ) {
$userpage = $wgUser->getUserPage();
$userjs = htmlspecialchars( self::makeUrl(
$userpage->getPrefixedText().'/'.$this->getSkinName().'.js',
@@ -385,7 +399,8 @@ class Skin extends Linker {
function getUserStylesheet() {
global $wgStylePath, $wgRequest, $wgContLang, $wgSquidMaxage, $wgStyleVersion;
$sheet = $this->getStylesheet();
- $s = "@import \"$wgStylePath/common/common.css?$wgStyleVersion\";\n";
+ $s = "@import \"$wgStylePath/common/shared.css?$wgStyleVersion\";\n";
+ $s .= "@import \"$wgStylePath/common/oldshared.css?$wgStyleVersion\";\n";
$s .= "@import \"$wgStylePath/$sheet?$wgStyleVersion\";\n";
if($wgContLang->isRTL()) $s .= "@import \"$wgStylePath/common/common_rtl.css?$wgStyleVersion\";\n";
@@ -398,36 +413,28 @@ class Skin extends Linker {
}
/**
- * This returns MediaWiki:Common.js. For some bizarre reason, it does
- * *not* return any custom user JS from user subpages. Huh?
+ * This returns MediaWiki:Common.js, and derived classes may add other JS.
+ * Despite its name, it does *not* return any custom user JS from user
+ * subpages. The returned script is sitewide and publicly cacheable and
+ * therefore must not include anything that varies according to user,
+ * interface language, etc. (although it may vary by skin). See
+ * makeGlobalVariablesScript for things that can vary per page view and are
+ * not cacheable.
*
- * @return string
+ * @return string Raw JavaScript to be returned
*/
- function getUserJs() {
+ public function getUserJs() {
wfProfileIn( __METHOD__ );
global $wgStylePath;
$s = "/* generated javascript */\n";
- $s .= "var skin = '{$this->skinname}';\nvar stylepath = '{$wgStylePath}';";
+ $s .= "var skin = '" . Xml::escapeJsString( $this->getSkinName() ) . "';\n";
+ $s .= "var stylepath = '" . Xml::escapeJsString( $wgStylePath ) . "';";
$s .= "\n\n/* MediaWiki:Common.js */\n";
$commonJs = wfMsgForContent('common.js');
if ( !wfEmptyMsg ( 'common.js', $commonJs ) ) {
$s .= $commonJs;
}
-
- global $wgUseAjax, $wgAjaxWatch;
- if($wgUseAjax && $wgAjaxWatch) {
- $s .= "
-
-/* AJAX (un)watch (see /skins/common/ajaxwatch.js) */
-var wgAjaxWatch = {
- watchMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'watch', array() ) )."',
- unwatchMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'unwatch', array() ) )."',
- watchingMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'watching', array() ) )."',
- unwatchingMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'unwatching', array() ) )."'
-};";
- }
-
wfProfileOut( __METHOD__ );
return $s;
}
@@ -701,7 +708,9 @@ END;
*/
function bottomScripts() {
global $wgJsMimeType;
- return "\n\t\t<script type=\"$wgJsMimeType\">if (window.runOnloadHook) runOnloadHook();</script>\n";
+ $bottomScriptText = "\n\t\t<script type=\"$wgJsMimeType\">if (window.runOnloadHook) runOnloadHook();</script>\n";
+ wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
+ return $bottomScriptText;
}
/** @return string Retrievied from HTML text */
@@ -739,8 +748,8 @@ END;
if ( $wgOut->isArticleRelated() ) {
if ( $wgTitle->getNamespace() == NS_IMAGE ) {
$name = $wgTitle->getDBkey();
- $image = new Image( $wgTitle );
- if( $image->exists() ) {
+ $image = wfFindFile( $wgTitle );
+ if( $image ) {
$link = htmlspecialchars( $image->getURL() );
$style = $this->getInternalLinkAttributes( $link, $name );
$s .= " | <a href=\"{$link}\"{$style}>{$name}</a>";
@@ -1427,29 +1436,6 @@ END;
return $s;
}
- function dateLink() {
- $t1 = Title::newFromText( gmdate( 'F j' ) );
- $t2 = Title::newFromText( gmdate( 'Y' ) );
-
- $id = $t1->getArticleID();
-
- if ( 0 == $id ) {
- $s = $this->makeBrokenLink( $t1->getText() );
- } else {
- $s = $this->makeKnownLink( $t1->getText() );
- }
- $s .= ', ';
-
- $id = $t2->getArticleID();
-
- if ( 0 == $id ) {
- $s .= $this->makeBrokenLink( $t2->getText() );
- } else {
- $s .= $this->makeKnownLink( $t2->getText() );
- }
- return $s;
- }
-
function talkLink() {
global $wgTitle;
@@ -1668,5 +1654,5 @@ END;
wfProfileOut( $fname );
return $bar;
}
-}
-?>
+
+} \ No newline at end of file
diff --git a/includes/SkinTemplate.php b/includes/SkinTemplate.php
index cddd2195..6ce40606 100644
--- a/includes/SkinTemplate.php
+++ b/includes/SkinTemplate.php
@@ -179,7 +179,7 @@ class SkinTemplate extends Skin {
$this->usercss = $this->userjs = $this->userjsprev = false;
$this->setupUserCss();
- $this->setupUserJs();
+ $this->setupUserJs( $out->isUserJsAllowed() );
$this->titletxt = $this->mTitle->getPrefixedText();
wfProfileOut( "$fname-stuff" );
@@ -285,11 +285,11 @@ class SkinTemplate extends Skin {
$tpl->setRef( 'userjsprev', $this->userjsprev);
global $wgUseSiteJs;
if ($wgUseSiteJs) {
- if($this->loggedin) {
- $tpl->set( 'jsvarurl', self::makeUrl('-','action=raw&smaxage=0&gen=js') );
- } else {
- $tpl->set( 'jsvarurl', self::makeUrl('-','action=raw&gen=js') );
- }
+ $jsCache = $this->loggedin ? '&smaxage=0' : '';
+ $tpl->set( 'jsvarurl',
+ self::makeUrl('-',
+ "action=raw$jsCache&gen=js&useskin=" .
+ urlencode( $this->getSkinName() ) ) );
} else {
$tpl->set('jsvarurl', false);
}
@@ -440,7 +440,8 @@ class SkinTemplate extends Skin {
// XXX: attach this from javascript, same with section editing
if($this->iseditable && $wgUser->getOption("editondblclick") )
{
- $tpl->set('body_ondblclick', 'document.location = "' .$content_actions['edit']['href'] .'";');
+ $encEditUrl = wfEscapeJsString( $this->mTitle->getLocalUrl( $this->editUrlOptions() ) );
+ $tpl->set('body_ondblclick', 'document.location = "' . $encEditUrl . '";');
} else {
$tpl->set('body_ondblclick', false);
}
@@ -591,7 +592,7 @@ class SkinTemplate extends Skin {
if( $selected ) {
$classes[] = 'selected';
}
- if( $checkEdit && $title->getArticleId() == 0 ) {
+ if( $checkEdit && !$title->isAlwaysKnown() && $title->getArticleId() == 0 ) {
$classes[] = 'new';
$query = 'action=edit';
}
@@ -822,7 +823,6 @@ class SkinTemplate extends Skin {
global $wgEnableUploads, $wgUploadNavigationUrl;
$action = $wgRequest->getText( 'action' );
- $oldid = $wgRequest->getVal( 'oldid' );
$nav_urls = array();
$nav_urls['mainpage'] = array( 'href' => self::makeMainPageUrl() );
@@ -852,21 +852,16 @@ class SkinTemplate extends Skin {
);
// Also add a "permalink" while we're at it
- if ( (int)$oldid ) {
+ if ( $this->mRevisionId ) {
$nav_urls['permalink'] = array(
'text' => wfMsg( 'permalink' ),
- 'href' => ''
+ 'href' => $wgTitle->getLocalURL( "oldid=$this->mRevisionId" )
);
- } else {
- $revid = $wgArticle ? $wgArticle->getLatest() : 0;
- if ( !( $revid == 0 ) )
- $nav_urls['permalink'] = array(
- 'text' => wfMsg( 'permalink' ),
- 'href' => $wgTitle->getLocalURL( "oldid=$revid" )
- );
}
-
- wfRunHooks( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink', array( &$this, &$nav_urls, &$oldid, &$revid ) );
+
+ // Copy in case this undocumented, shady hook tries to mess with internals
+ $revid = $this->mRevisionId;
+ wfRunHooks( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink', array( &$this, &$nav_urls, &$revid, &$revid ) );
}
if( $this->mTitle->getNamespace() != NS_SPECIAL ) {
@@ -896,10 +891,19 @@ class SkinTemplate extends Skin {
$ip = false;
}
- if($id || $ip) { # both anons and non-anons have contri list
+ if($id || $ip) { # both anons and non-anons have contribs list
$nav_urls['contributions'] = array(
'href' => self::makeSpecialUrlSubpage( 'Contributions', $this->mTitle->getText() )
);
+
+ if( $id ) {
+ $logPage = SpecialPage::getTitleFor( 'Log' );
+ $nav_urls['log'] = array( 'href' => $logPage->getLocalUrl( 'user='
+ . $this->mTitle->getPartialUrl() ) );
+ } else {
+ $nav_urls['log'] = false;
+ }
+
if ( $wgUser->isAllowed( 'block' ) ) {
$nav_urls['blockip'] = array(
'href' => self::makeSpecialUrlSubpage( 'Blockip', $this->mTitle->getText() )
@@ -909,6 +913,7 @@ class SkinTemplate extends Skin {
}
} else {
$nav_urls['contributions'] = false;
+ $nav_urls['log'] = false;
$nav_urls['blockip'] = false;
}
$nav_urls['emailuser'] = false;
@@ -974,9 +979,12 @@ class SkinTemplate extends Skin {
# If we use the site's dynamic CSS, throw that in, too
if ( $wgUseSiteCss ) {
$query = "usemsgcache=yes&action=raw&ctype=text/css&smaxage=$wgSquidMaxage";
+ $skinquery = '';
+ if (($us = $wgRequest->getVal('useskin', '')) !== '')
+ $skinquery = "&useskin=$us";
$sitecss .= '@import "' . self::makeNSUrl( 'Common.css', $query, NS_MEDIAWIKI) . '";' . "\n";
$sitecss .= '@import "' . self::makeNSUrl( ucfirst( $this->skinname ) . '.css', $query, NS_MEDIAWIKI ) . '";' . "\n";
- $sitecss .= '@import "' . self::makeUrl( '-', 'action=raw&gen=css' . $siteargs ) . '";' . "\n";
+ $sitecss .= '@import "' . self::makeUrl( '-', "action=raw&gen=css$siteargs$skinquery" ) . '";' . "\n";
}
# If we use any dynamic CSS, make a little CDATA block out of it.
@@ -990,14 +998,14 @@ class SkinTemplate extends Skin {
/**
* @private
*/
- function setupUserJs() {
+ function setupUserJs( $allowUserJs ) {
$fname = 'SkinTemplate::setupUserJs';
wfProfileIn( $fname );
- global $wgRequest, $wgAllowUserJs, $wgJsMimeType;
+ global $wgRequest, $wgJsMimeType;
$action = $wgRequest->getText('action');
- if( $wgAllowUserJs && $this->loggedin ) {
+ if( $allowUserJs && $this->loggedin ) {
if( $this->mTitle->isJsSubpage() and $this->userCanPreview( $action ) ) {
# XXX: additional security check/prompt?
$this->userjsprev = '/*<![CDATA[*/ ' . $wgRequest->getText('wpTextbox1') . ' /*]]>*/';
@@ -1177,4 +1185,4 @@ class QuickTemplate {
return ($msg != '-') && ($msg != ''); # ????
}
}
-?>
+
diff --git a/includes/SpecialAllmessages.php b/includes/SpecialAllmessages.php
index 0862cd17..4ba01e29 100644
--- a/includes/SpecialAllmessages.php
+++ b/includes/SpecialAllmessages.php
@@ -25,7 +25,8 @@ function wfSpecialAllmessages() {
$navText = wfMsg( 'allmessagestext' );
# Make sure all extension messages are available
- MessageCache::loadAllMessages();
+
+ $wgMessageCache->loadAllMessages();
$sortedArray = array_merge( Language::getMessagesFor( 'en' ), $wgMessageCache->getExtensionMessagesFor( 'en' ) );
ksort( $sortedArray );
@@ -196,4 +197,4 @@ $mw
return $txt;
}
-?>
+
diff --git a/includes/SpecialAllpages.php b/includes/SpecialAllpages.php
index 03e164bd..07ff120b 100644
--- a/includes/SpecialAllpages.php
+++ b/includes/SpecialAllpages.php
@@ -19,10 +19,7 @@ function wfSpecialAllpages( $par=NULL, $specialPage ) {
$indexPage = new SpecialAllpages();
- if( !in_array($namespace, array_keys($namespaces)) )
- $namespace = 0;
-
- $wgOut->setPagetitle( $namespace > 0 ?
+ $wgOut->setPagetitle( ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces) ) ) ?
wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
wfMsg( 'allarticles' )
);
@@ -53,41 +50,44 @@ class SpecialAllpages {
* @param string $from Article name we are starting listing at.
*/
function namespaceForm ( $namespace = NS_MAIN, $from = '' ) {
- global $wgScript;
+ global $wgScript, $wgContLang;
$t = SpecialPage::getTitleFor( $this->name );
-
- $namespaceselect = HTMLnamespaceselector($namespace, null);
-
- $frombox = "<input type='text' size='20' name='from' id='nsfrom' value=\""
- . htmlspecialchars ( $from ) . '"/>';
- $submitbutton = '<input type="submit" value="' . wfMsgHtml( 'allpagessubmit' ) . '" />';
-
- $out = "<div class='namespaceoptions'><form method='get' action='{$wgScript}'>";
- $out .= '<input type="hidden" name="title" value="'.$t->getPrefixedText().'" />';
- $out .= "
-<table id='nsselect' class='allpages'>
- <tr>
- <td align='right'>" . wfMsgHtml($this->nsfromMsg) . "</td>
- <td align='left'><label for='nsfrom'>$frombox</label></td>
- </tr>
- <tr>
- <td align='right'><label for='namespace'>" . wfMsgHtml('namespace') . "</label></td>
- <td align='left'>
- $namespaceselect $submitbutton
- </td>
- </tr>
-</table>
-";
- $out .= '</form></div>';
- return $out;
+ $align = $wgContLang->isRtl() ? 'left' : 'right';
+
+ $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
+ $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
+ $out .= Xml::hidden( 'title', $t->getPrefixedText() );
+ $out .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
+ $out .= "<tr>
+ <td align='$align'>" .
+ Xml::label( wfMsg( $this->nsfromMsg ), 'nsfrom' ) .
+ "</td>
+ <td>" .
+ Xml::input( 'from', 20, htmlspecialchars ( $from ), array( 'id' => 'nsfrom' ) ) .
+ "</td>
+ </tr>
+ <tr>
+ <td align='$align'>" .
+ Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
+ "</td>
+ <td>" .
+ Xml::namespaceSelector( $namespace, null ) .
+ Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
+ "</td>
+ </tr>";
+ $out .= Xml::closeElement( 'table' );
+ $out .= Xml::closeElement( 'form' );
+ $out .= Xml::closeElement( 'div' );
+ return $out;
}
/**
* @param integer $namespace (default NS_MAIN)
*/
function showToplevel ( $namespace = NS_MAIN, $including = false ) {
- global $wgOut;
+ global $wgOut, $wgContLang;
$fname = "indexShowToplevel";
+ $align = $wgContLang->isRtl() ? 'left' : 'right';
# TODO: Either make this *much* faster or cache the title index points
# in the querycache table.
@@ -101,7 +101,11 @@ function showToplevel ( $namespace = NS_MAIN, $including = false ) {
$lines = $wgMemc->get( $key );
if( !is_array( $lines ) ) {
- $firstTitle = $dbr->selectField( 'page', 'page_title', $where, $fname, array( 'LIMIT' => 1 ) );
+ $options = array( 'LIMIT' => 1 );
+ if ( ! $dbr->implicitOrderby() ) {
+ $options['ORDER BY'] = 'page_title';
+ }
+ $firstTitle = $dbr->selectField( 'page', 'page_title', $where, $fname, $options );
$lastTitle = $firstTitle;
# This array is going to hold the page_titles in order.
@@ -170,8 +174,8 @@ function showToplevel ( $namespace = NS_MAIN, $including = false ) {
$morelinks = '';
if ( $morelinks != '' ) {
$out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
- $out2 .= '<tr valign="top"><td align="left">' . $nsForm;
- $out2 .= '</td><td align="right" style="font-size: smaller; margin-bottom: 1em;">';
+ $out2 .= '<tr valign="top"><td>' . $nsForm;
+ $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">';
$out2 .= $morelinks . '</td></tr></table><hr />';
} else {
$out2 = $nsForm . '<hr />';
@@ -187,6 +191,8 @@ function showToplevel ( $namespace = NS_MAIN, $including = false ) {
* @param integer $namespace (Default NS_MAIN)
*/
function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
+ global $wgContLang;
+ $align = $wgContLang->isRtl() ? 'left' : 'right';
$inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
$outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
$queryparams = ($namespace ? "namespace=$namespace" : '');
@@ -196,9 +202,9 @@ function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
$out = wfMsgHtml(
'alphaindexline',
"<a href=\"$link\">$inpointf</a></td><td><a href=\"$link\">",
- "</a></td><td align=\"left\"><a href=\"$link\">$outpointf</a>"
+ "</a></td><td><a href=\"$link\">$outpointf</a>"
);
- return '<tr><td align="right">'.$out.'</td></tr>';
+ return '<tr><td align="' . $align . '">'.$out.'</td></tr>';
}
/**
@@ -209,14 +215,20 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
global $wgOut, $wgUser, $wgContLang;
$fname = 'indexShowChunk';
-
$sk = $wgUser->getSkin();
$fromList = $this->getNamespaceKeyAndText($namespace, $from);
+ $namespaces = $wgContLang->getNamespaces();
+ $align = $wgContLang->isRtl() ? 'left' : 'right';
+
$n = 0;
-
+
if ( !$fromList ) {
$out = wfMsgWikiHtml( 'allpagesbadtitle' );
+ } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
+ // Show errormessage and reset to NS_MAIN
+ $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
+ $namespace = NS_MAIN;
} else {
list( $namespace, $fromKey, $from ) = $fromList;
@@ -285,8 +297,11 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
} else {
# The previous chunk is not complete, need to link to the very first title
# available in the database
- $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', array( 'page_namespace' => $namespace ), $fname, array( 'LIMIT' => 1) );
-
+ $options = array( 'LIMIT' => 1 );
+ if ( ! $dbr->implicitOrderby() ) {
+ $options['ORDER BY'] = 'page_title';
+ }
+ $reallyFirstPage_title = $dbr->selectField( 'page', 'page_title', array( 'page_namespace' => $namespace ), $fname, $options );
# Show the previous link if it s not the current requested chunk
if( $from != $reallyFirstPage_title ) {
$prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title );
@@ -298,8 +313,8 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
$nsForm = $this->namespaceForm ( $namespace, $from );
$out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
- $out2 .= '<tr valign="top"><td align="left">' . $nsForm;
- $out2 .= '</td><td align="right" style="font-size: smaller; margin-bottom: 1em;">' .
+ $out2 .= '<tr valign="top"><td>' . $nsForm;
+ $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' .
$sk->makeKnownLink( $wgContLang->specialPage( "Allpages" ),
wfMsgHtml ( 'allpages' ) );
@@ -324,11 +339,16 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
$wgOut->addHtml( $out2 . $out );
if( isset($prevLink) or isset($nextLink) ) {
- $wgOut->addHtml( '<hr/><p style="font-size: smaller; float: right;">' );
- if( isset( $prevLink ) )
- $wgOut->addHTML( $prevLink . ' | ');
- if( isset( $nextLink ) )
+ $wgOut->addHtml( '<hr /><p style="font-size: smaller; float: ' . $align . '">' );
+ if( isset( $prevLink ) ) {
+ $wgOut->addHTML( $prevLink );
+ }
+ if( isset( $prevLink ) && isset( $nextLink ) ) {
+ $wgOut->addHTML( ' | ' );
+ }
+ if( isset( $nextLink ) ) {
$wgOut->addHTML( $nextLink );
+ }
$wgOut->addHTML( '</p>' );
}
diff --git a/includes/SpecialAncientpages.php b/includes/SpecialAncientpages.php
index c0bbb7ba..dee8fbde 100644
--- a/includes/SpecialAncientpages.php
+++ b/includes/SpecialAncientpages.php
@@ -60,4 +60,4 @@ function wfSpecialAncientpages() {
$app->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialBlockip.php b/includes/SpecialBlockip.php
index 5f47fa13..942ebe8b 100644
--- a/includes/SpecialBlockip.php
+++ b/includes/SpecialBlockip.php
@@ -43,6 +43,7 @@ function wfSpecialBlockip( $par ) {
*/
class IPBlockForm {
var $BlockAddress, $BlockExpiry, $BlockReason;
+# var $BlockEmail;
function IPBlockForm( $par ) {
global $wgRequest, $wgUser;
@@ -60,12 +61,13 @@ class IPBlockForm {
$this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault );
$this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault );
$this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault );
+ $this->BlockEmail = $wgRequest->getBool( 'wpEmailBan', false );
# Re-check user's rights to hide names, very serious, defaults to 0
- $this->BlockHideName = $wgRequest->getBool( 'wpHideName', 0 ) && $wgUser->isAllowed( 'hideuser' );
+ $this->BlockHideName = ( $wgRequest->getBool( 'wpHideName', 0 ) && $wgUser->isAllowed( 'hideuser' ) ) ? 1 : 0;
}
function showForm( $err ) {
- global $wgOut, $wgUser, $wgSysopUserBans;
+ global $wgOut, $wgUser, $wgSysopUserBans, $wgContLang;
$wgOut->setPagetitle( wfMsg( 'blockip' ) );
$wgOut->addWikiText( wfMsg( 'blockiptext' ) );
@@ -84,6 +86,7 @@ class IPBlockForm {
$titleObj = SpecialPage::getTitleFor( 'Blockip' );
$action = $titleObj->escapeLocalURL( "action=submit" );
+ $alignRight = $wgContLang->isRtl() ? 'left' : 'right';
if ( "" != $err ) {
$wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
@@ -141,7 +144,7 @@ class IPBlockForm {
$blockReasonList .= $optgroup;
}
- $token = htmlspecialchars( $wgUser->editToken() );
+ $token = $wgUser->editToken();
global $wgStylePath, $wgStyleVersion;
$wgOut->addHTML( "
@@ -150,8 +153,8 @@ class IPBlockForm {
<form id=\"blockip\" method=\"post\" action=\"{$action}\">
<table border='0'>
<tr>
- <td align=\"right\">{$mIpaddress}:</td>
- <td align=\"left\">
+ <td align=\"$alignRight\">{$mIpaddress}</td>
+ <td>
" . Xml::input( 'wpBlockAddress', 45, $this->BlockAddress,
array(
'tabindex' => '1',
@@ -162,8 +165,8 @@ class IPBlockForm {
<tr>");
if ($showblockoptions) {
$wgOut->addHTML("
- <td align=\"right\">{$mIpbexpiry}:</td>
- <td align=\"left\">
+ <td align=\"$alignRight\">{$mIpbexpiry}</td>
+ <td>
<select tabindex='2' id='wpBlockExpiry' name=\"wpBlockExpiry\" onchange=\"considerChangingExpiryFocus()\">
$blockExpiryFormOptions
</select>
@@ -173,8 +176,8 @@ class IPBlockForm {
$wgOut->addHTML("
</tr>
<tr id='wpBlockOther'>
- <td align=\"right\">{$mIpbother}:</td>
- <td align=\"left\">
+ <td align=\"$alignRight\">{$mIpbother}</td>
+ <td>
" . Xml::input( 'wpBlockOther', 45, $this->BlockOther,
array( 'tabindex' => '3', 'id' => 'mw-bi-other' ) ) . "
</td>
@@ -182,8 +185,8 @@ class IPBlockForm {
if ( $blockReasonList != '' ) {
$wgOut->addHTML("
<tr>
- <td align=\"right\">{$mIpbreasonother}:</td>
- <td align=\"left\">
+ <td align=\"$alignRight\">{$mIpbreasonother}</td>
+ <td>
<select tabindex='4' id=\"wpBlockReasonList\" name=\"wpBlockReasonList\">
$blockReasonList
</select>
@@ -192,15 +195,16 @@ class IPBlockForm {
}
$wgOut->addHTML("
<tr id=\"wpBlockReason\">
- <td align=\"right\">{$mIpbreason}:</td>
- <td align=\"left\">
+ <td align=\"$alignRight\">{$mIpbreason}</td>
+ <td>
" . Xml::input( 'wpBlockReason', 45, $this->BlockReason,
- array( 'tabindex' => '5', 'id' => 'mw-bi-reason' ) ) . "
+ array( 'tabindex' => '5', 'id' => 'mw-bi-reason',
+ 'maxlength'=> '200' ) ) . "
</td>
</tr>
<tr id='wpAnonOnlyRow'>
<td>&nbsp;</td>
- <td align=\"left\">
+ <td>
" . wfCheckLabel( wfMsgHtml( 'ipbanononly' ),
'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
array( 'tabindex' => '6' ) ) . "
@@ -208,7 +212,7 @@ class IPBlockForm {
</tr>
<tr id='wpCreateAccountRow'>
<td>&nbsp;</td>
- <td align=\"left\">
+ <td>
" . wfCheckLabel( wfMsgHtml( 'ipbcreateaccount' ),
'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
array( 'tabindex' => '7' ) ) . "
@@ -216,7 +220,7 @@ class IPBlockForm {
</tr>
<tr id='wpEnableAutoblockRow'>
<td>&nbsp;</td>
- <td align=\"left\">
+ <td>
" . wfCheckLabel( wfMsgHtml( 'ipbenableautoblock' ),
'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
array( 'tabindex' => '8' ) ) . "
@@ -228,7 +232,7 @@ class IPBlockForm {
$wgOut->addHTML("
<tr>
<td>&nbsp;</td>
- <td align=\"left\">
+ <td>
" . wfCheckLabel( wfMsgHtml( 'ipbhidename' ),
'wpHideName', 'wpHideName', $this->BlockHideName,
array( 'tabindex' => '9' ) ) . "
@@ -236,12 +240,27 @@ class IPBlockForm {
</tr>
");
}
+
+ global $wgSysopEmailBans;
+
+ if ( $wgSysopEmailBans && $wgUser->isAllowed( 'blockemail' ) ) {
+ $wgOut->addHTML("
+ <tr id='wpEnableEmailBan'>
+ <td>&nbsp;</td>
+ <td>
+ " . wfCheckLabel( wfMsgHtml( 'ipbemailban' ),
+ 'wpEmailBan', 'wpEmailBan', $this->BlockEmail,
+ array( 'tabindex' => '10' )) . "
+ </td>
+ </tr>
+ ");
+ }
$wgOut->addHTML("
<tr>
<td style='padding-top: 1em'>&nbsp;</td>
- <td style='padding-top: 1em' align=\"left\">
- " . Xml::submitButton( wfMsgHtml( 'ipbsubmit' ),
- array( 'name' => 'wpBlock', 'tabindex' => '10' ) ) . "
+ <td style='padding-top: 1em'>
+ " . Xml::submitButton( wfMsg( 'ipbsubmit' ),
+ array( 'name' => 'wpBlock', 'tabindex' => '11' ) ) . "
</td>
</tr>
</table>" .
@@ -354,10 +373,10 @@ class IPBlockForm {
# Create block
# Note: for a user block, ipb_address is only for display purposes
-
$block = new Block( $this->BlockAddress, $userId, $wgUser->getID(),
$reasonstr, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
- $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName);
+ $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName,
+ $this->BlockEmail);
if (wfRunHooks('BlockIp', array(&$block, &$wgUser))) {
@@ -418,6 +437,8 @@ class IPBlockForm {
$flags[] = 'nocreate';
if( !$this->BlockEnableAutoblock )
$flags[] = 'noautoblock';
+ if ( $this->BlockEmail )
+ $flags[] = 'noemail';
return implode( ',', $flags );
}
@@ -471,4 +492,4 @@ class IPBlockForm {
}
}
}
-?>
+
diff --git a/includes/SpecialBlockme.php b/includes/SpecialBlockme.php
index c2cb1a58..da2757ac 100644
--- a/includes/SpecialBlockme.php
+++ b/includes/SpecialBlockme.php
@@ -36,4 +36,4 @@ function wfSpecialBlockme() {
$wgOut->addWikiText( $success );
}
-?>
+
diff --git a/includes/SpecialBooksources.php b/includes/SpecialBooksources.php
index d3136ea4..5f103495 100644
--- a/includes/SpecialBooksources.php
+++ b/includes/SpecialBooksources.php
@@ -14,20 +14,20 @@ class SpecialBookSources extends SpecialPage {
* ISBN passed to the page, if any
*/
private $isbn = '';
-
+
/**
* Constructor
*/
public function __construct() {
parent::__construct( 'Booksources' );
}
-
+
/**
* Show the special page
*
* @param $isbn ISBN passed as a subpage parameter
*/
- public function execute( $isbn = false ) {
+ public function execute( $isbn ) {
global $wgOut, $wgRequest;
$this->setHeaders();
$this->isbn = $this->cleanIsbn( $isbn ? $isbn : $wgRequest->getText( 'isbn' ) );
@@ -36,7 +36,7 @@ class SpecialBookSources extends SpecialPage {
if( strlen( $this->isbn ) > 0 )
$this->showList();
}
-
+
/**
* Trim ISBN and remove characters which aren't required
*
@@ -46,7 +46,7 @@ class SpecialBookSources extends SpecialPage {
private function cleanIsbn( $isbn ) {
return trim( preg_replace( '![^0-9X]!', '', $isbn ) );
}
-
+
/**
* Generate a form to allow users to enter an ISBN
*
@@ -64,7 +64,7 @@ class SpecialBookSources extends SpecialPage {
$form .= '</fieldset>';
return $form;
}
-
+
/**
* Determine where to get the list of book sources from,
* format and output them
@@ -73,19 +73,19 @@ class SpecialBookSources extends SpecialPage {
*/
private function showList() {
global $wgOut, $wgContLang;
-
+
# Hook to allow extensions to insert additional HTML,
# e.g. for API-interacting plugins and so on
wfRunHooks( 'BookInformation', array( $this->isbn, &$wgOut ) );
-
+
# Check for a local page such as Project:Book_sources and use that if available
- $title = Title::makeTitleSafe( NS_PROJECT, wfMsg( 'booksources' ) ); # Should this be wfMsgForContent()? -- RC
+ $title = Title::makeTitleSafe( NS_PROJECT, wfMsgForContent( 'booksources' ) ); # Show list in content language
if( is_object( $title ) && $title->exists() ) {
$rev = Revision::newFromTitle( $title );
$wgOut->addWikiText( str_replace( 'MAGICNUMBER', $this->isbn, $rev->getText() ) );
return true;
}
-
+
# Fall back to the defaults given in the language file
$wgOut->addWikiText( wfMsgNoTrans( 'booksources-text' ) );
$wgOut->addHtml( '<ul>' );
@@ -110,4 +110,4 @@ class SpecialBookSources extends SpecialPage {
}
-?>
+
diff --git a/includes/SpecialBrokenRedirects.php b/includes/SpecialBrokenRedirects.php
index 208a7e1f..1fb48350 100644
--- a/includes/SpecialBrokenRedirects.php
+++ b/includes/SpecialBrokenRedirects.php
@@ -20,8 +20,7 @@ class BrokenRedirectsPage extends PageQueryPage {
function isSyndicated() { return false; }
function getPageHeader( ) {
- global $wgOut;
- return $wgOut->parse( wfMsg( 'brokenredirectstext' ) );
+ return wfMsgExt( 'brokenredirectstext', array( 'parse' ) );
}
function getSQL() {
@@ -36,7 +35,8 @@ class BrokenRedirectsPage extends PageQueryPage {
FROM $redirect AS rd
JOIN $page p1 ON (rd.rd_from=p1.page_id)
LEFT JOIN $page AS p2 ON (rd_namespace=p2.page_namespace AND rd_title=p2.page_title )
- WHERE p2.page_namespace IS NULL";
+ WHERE rd_namespace >= 0
+ AND p2.page_namespace IS NULL";
return $sql;
}
@@ -92,4 +92,4 @@ function wfSpecialBrokenRedirects() {
return $sbr->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialCategories.php b/includes/SpecialCategories.php
index 45e1ae6c..596569ed 100644
--- a/includes/SpecialCategories.php
+++ b/includes/SpecialCategories.php
@@ -53,14 +53,11 @@ class CategoryPager extends AlphabeticPager {
function formatRow($result) {
global $wgLang;
$title = Title::makeTitle( NS_CATEGORY, $result->cl_to );
- return (
- '<li>' .
- $this->getSkin()->makeLinkObj( $title, $title->getText() )
- . ' ' .
- wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
- $wgLang->formatNum( $result->count ) )
- . "</li>\n" );
+ $titleText = $this->getSkin()->makeLinkObj( $title, htmlspecialchars( $title->getText() ) );
+ $count = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
+ $wgLang->formatNum( $result->count ) );
+ return Xml::tags('li', null, "$titleText ($count)" ) . "\n";
}
}
-?>
+
diff --git a/includes/SpecialConfirmemail.php b/includes/SpecialConfirmemail.php
index 58e55899..ba419f25 100644
--- a/includes/SpecialConfirmemail.php
+++ b/includes/SpecialConfirmemail.php
@@ -1,23 +1,21 @@
<?php
/**
- * Main execution point
- *
- * @param $par Parameters passed to the page
- */
-function wfSpecialConfirmemail( $par ) {
- $form = new EmailConfirmation();
- $form->execute( $par );
-}
-
-/**
* Special page allows users to request email confirmation message, and handles
* processing of the confirmation code when the link in the email is followed
*
* @addtogroup SpecialPage
+ * @author Brion Vibber
* @author Rob Church <robchur@gmail.com>
*/
-class EmailConfirmation extends SpecialPage {
+class EmailConfirmation extends UnlistedSpecialPage {
+
+ /**
+ * Constructor
+ */
+ public function __construct() {
+ parent::__construct( 'Confirmemail' );
+ }
/**
* Main execution point
@@ -26,6 +24,7 @@ class EmailConfirmation extends SpecialPage {
*/
function execute( $code ) {
global $wgUser, $wgOut;
+ $this->setHeaders();
if( empty( $code ) ) {
if( $wgUser->isLoggedIn() ) {
if( User::isValidEmailAddr( $wgUser->getEmail() ) ) {
@@ -102,4 +101,4 @@ class EmailConfirmation extends SpecialPage {
}
-?>
+
diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php
index 82c8d608..cc1b2e6f 100644
--- a/includes/SpecialContributions.php
+++ b/includes/SpecialContributions.php
@@ -9,15 +9,21 @@ class ContribsPager extends IndexPager {
var $messages, $target;
var $namespace = '', $mDb;
- function __construct( $target, $namespace = false ) {
- global $wgUser;
-
+ function __construct( $target, $namespace = false, $year = false, $month = false ) {
parent::__construct();
foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg ) {
$this->messages[$msg] = wfMsgExt( $msg, array( 'escape') );
}
$this->target = $target;
$this->namespace = $namespace;
+
+ $year = intval($year);
+ $month = intval($month);
+
+ $this->year = ($year > 0 && $year < 10000) ? $year : false;
+ $this->month = ($month > 0 && $month < 13) ? $month : false;
+ $this->getDateCond();
+
$this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
}
@@ -29,7 +35,7 @@ class ContribsPager extends IndexPager {
function getQueryInfo() {
list( $index, $userCond ) = $this->getUserCond();
- $conds = array_merge( array( 'page_id=rev_page' ), $userCond, $this->getNamespaceCond() );
+ $conds = array_merge( array('page_id=rev_page'), $userCond, $this->getNamespaceCond() );
return array(
'tables' => array( 'page', 'revision' ),
@@ -39,7 +45,7 @@ class ContribsPager extends IndexPager {
'rev_user_text', 'rev_deleted'
),
'conds' => $conds,
- 'options' => array( 'FORCE INDEX' => $index )
+ 'options' => array( 'USE INDEX' => $index )
);
}
@@ -64,6 +70,33 @@ class ContribsPager extends IndexPager {
return array();
}
}
+
+ function getDateCond() {
+ if ( $this->year || $this->month ) {
+ // Assume this year if only a month is given
+ if ( $this->year ) {
+ $year_start = $this->year;
+ } else {
+ $year_start = substr( wfTimestampNow(), 0, 4 );
+ $thisMonth = gmdate( 'n' );
+ if( $this->month > $thisMonth ) {
+ // Future contributions aren't supposed to happen. :)
+ $year_start--;
+ }
+ }
+
+ if ( $this->month ) {
+ $month_end = str_pad($this->month + 1, 2, '0', STR_PAD_LEFT);
+ $year_end = $year_start;
+ } else {
+ $month_end = 0;
+ $year_end = $year_start + 1;
+ }
+ $ts_end = str_pad($year_end . $month_end, 14, '0' );
+
+ $this->mOffset = $ts_end;
+ }
+ }
function getIndexField() {
return 'rev_timestamp';
@@ -110,7 +143,7 @@ class ContribsPager extends IndexPager {
function formatRow( $row ) {
wfProfileIn( __METHOD__ );
- global $wgLang, $wgUser;
+ global $wgLang, $wgUser, $wgContLang;
$sk = $this->getSkin();
$rev = new Revision( $row );
@@ -138,8 +171,15 @@ class ContribsPager extends IndexPager {
}
$histlink='('.$sk->makeKnownLinkObj( $page, $this->messages['hist'], 'action=history' ) . ')';
- $comment = $sk->revComment( $rev );
+ $comment = $wgContLang->getDirMark() . $sk->revComment( $rev );
$d = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->rev_timestamp ), true );
+
+ if( $this->target == 'newbies' ) {
+ $userlink = ' . . ' . $sk->userLink( $row->rev_user, $row->rev_user_text );
+ $userlink .= ' (' . $sk->userTalkLink( $row->rev_user, $row->rev_user_text ) . ') ';
+ } else {
+ $userlink = '';
+ }
if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
$d = '<span class="history-deleted">' . $d . '</span>';
@@ -151,7 +191,7 @@ class ContribsPager extends IndexPager {
$mflag = '';
}
- $ret = "{$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}";
+ $ret = "{$d} {$histlink} {$difftext} {$mflag} {$link}{$userlink}{$comment} {$topmarktext}";
if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
$ret .= ' ' . wfMsgHtml( 'deletedrev' );
}
@@ -159,6 +199,16 @@ class ContribsPager extends IndexPager {
wfProfileOut( __METHOD__ );
return $ret;
}
+
+ /**
+ * Get the Database object in use
+ *
+ * @return Database
+ */
+ public function getDatabase() {
+ return $this->mDb;
+ }
+
}
/**
@@ -218,16 +268,48 @@ function wfSpecialContributions( $par = null ) {
if ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) ) {
$options['bot'] = '1';
}
+
+ $skip = $wgRequest->getText( 'offset' ) || $wgRequest->getText( 'dir' ) == 'prev';
+ # Offset overrides year/month selection
+ if ( ( $month = $wgRequest->getIntOrNull( 'month' ) ) !== null && $month !== -1 ) {
+ $options['month'] = intval( $month );
+ } else {
+ $options['month'] = '';
+ }
+ if ( ( $year = $wgRequest->getIntOrNull( 'year' ) ) !== null ) {
+ $options['year'] = intval( $year );
+ } else if( $options['month'] ) {
+ $thisMonth = intval( gmdate( 'n' ) );
+ $thisYear = intval( gmdate( 'Y' ) );
+ if( intval( $options['month'] ) > $thisMonth ) {
+ $thisYear--;
+ }
+ $options['year'] = $thisYear;
+ } else {
+ $options['year'] = '';
+ }
wfRunHooks( 'SpecialContributionsBeforeMainOutput', $id );
$wgOut->addHTML( contributionsForm( $options ) );
+ # Show original selected options, don't apply them so as to allow paging
+ $_GET['year'] = ''; // hack for Pager
+ $_GET['month'] = ''; // hack for Pager
+ if( $skip ) {
+ $options['year'] = '';
+ $options['month'] = '';
+ }
- $pager = new ContribsPager( $target, $options['namespace'] );
+ $pager = new ContribsPager( $target, $options['namespace'], $options['year'], $options['month'] );
if ( !$pager->getNumRows() ) {
$wgOut->addWikiText( wfMsg( 'nocontribs' ) );
return;
}
+
+ # Show a message about slave lag, if applicable
+ if( ( $lag = $pager->getDatabase()->getLag() ) > 0 )
+ $wgOut->showLagWarning( $lag );
+
$wgOut->addHTML(
'<p>' . $pager->getNavigationBar() . '</p>' .
$pager->getBody() .
@@ -279,6 +361,9 @@ function contributionsSub( $nt, $id ) {
}
# Other logs link
$tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'log' ), 'user=' . $nt->getPartialUrl() );
+
+ wfRunHooks( 'ContributionsToolLinks', array( $id, $nt, &$tools ) );
+
$links = implode( ' | ', $tools );
}
@@ -314,6 +399,14 @@ function contributionsForm( $options ) {
if ( !isset( $options['contribs'] ) ) {
$options['contribs'] = 'user';
}
+
+ if ( !isset( $options['year'] ) ) {
+ $options['year'] = '';
+ }
+
+ if ( !isset( $options['month'] ) ) {
+ $options['month'] = '';
+ }
if ( $options['contribs'] == 'newbie' ) {
$options['target'] = '';
@@ -322,7 +415,7 @@ function contributionsForm( $options ) {
$f = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
foreach ( $options as $name => $value ) {
- if ( in_array( $name, array( 'namespace', 'target', 'contribs' ) ) ) {
+ if ( in_array( $name, array( 'namespace', 'target', 'contribs', 'year', 'month' ) ) ) {
continue;
}
$f .= "\t" . Xml::hidden( $name, $value ) . "\n";
@@ -335,11 +428,19 @@ function contributionsForm( $options ) {
Xml::input( 'target', 20, $options['target']) . ' '.
Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
Xml::namespaceSelector( $options['namespace'], '' ) .
+ Xml::openElement( 'p' ) .
+ Xml::label( wfMsg( 'year' ), 'year' ) . ' '.
+ Xml::input( 'year', 4, $options['year'], array('id' => 'year', 'maxlength' => 4) ) . ' '.
+ Xml::label( wfMsg( 'month' ), 'month' ) . ' '.
+ Xml::monthSelector( $options['month'], -1 ) . ' '.
Xml::submitButton( wfMsg( 'sp-contributions-submit' ) ) .
- '</fieldset>' .
+ Xml::closeElement( 'p' );
+
+ $explain = wfMsgExt( 'sp-contributions-explain', 'parseinline' );
+ if( !wfEmptyMsg( 'sp-contributions-explain', $explain ) )
+ $f .= "<p>{$explain}</p>";
+
+ $f .= '</fieldset>' .
Xml::closeElement( 'form' );
return $f;
}
-
-
-?>
diff --git a/includes/SpecialDeadendpages.php b/includes/SpecialDeadendpages.php
index 48d27add..0d94161b 100644
--- a/includes/SpecialDeadendpages.php
+++ b/includes/SpecialDeadendpages.php
@@ -15,7 +15,7 @@ class DeadendPagesPage extends PageQueryPage {
}
function getPageHeader() {
- return '<p>' . wfMsg('deadendpagestext') . '</p>';
+ return wfMsgExt( 'deadendpagestext', array( 'parse' ) );
}
/**
@@ -62,4 +62,4 @@ function wfSpecialDeadendpages() {
return $depp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialDisambiguations.php b/includes/SpecialDisambiguations.php
index da0562ab..fb1d75e9 100644
--- a/includes/SpecialDisambiguations.php
+++ b/includes/SpecialDisambiguations.php
@@ -15,8 +15,7 @@ class DisambiguationsPage extends PageQueryPage {
function getPageHeader( ) {
- global $wgOut;
- return $wgOut->parse( wfMsg( 'disambiguations-text' ) );
+ return wfMsgExt( 'disambiguations-text', array( 'parse' ) );
}
function getSQL() {
@@ -105,4 +104,3 @@ function wfSpecialDisambiguations() {
return $sd->doQuery( $offset, $limit );
}
-?> \ No newline at end of file
diff --git a/includes/SpecialDoubleRedirects.php b/includes/SpecialDoubleRedirects.php
index e7b355c5..6d46fc50 100644
--- a/includes/SpecialDoubleRedirects.php
+++ b/includes/SpecialDoubleRedirects.php
@@ -19,8 +19,7 @@ class DoubleRedirectsPage extends PageQueryPage {
function isSyndicated() { return false; }
function getPageHeader( ) {
- #FIXME : probably need to add a backlink to the maintenance page.
- return '<p>'.wfMsg("doubleredirectstext")."</p><br />\n";
+ return wfMsgExt( 'doubleredirectstext', array( 'parse' ) );
}
function getSQLText( &$dbr, $namespace = null, $title = null ) {
@@ -64,6 +63,7 @@ class DoubleRedirectsPage extends PageQueryPage {
$fname = 'DoubleRedirectsPage::formatResult';
$titleA = Title::makeTitle( $result->namespace, $result->title );
+ $linkA = $skin->makeKnownLinkObj( $titleA,'', 'redirect=no' );
if ( $result && !isset( $result->nsb ) ) {
$dbr = wfGetDB( DB_SLAVE );
@@ -75,13 +75,12 @@ class DoubleRedirectsPage extends PageQueryPage {
}
}
if ( !$result ) {
- return '';
+ return "<s>{$linkA}</s>\n";
}
$titleB = Title::makeTitle( $result->nsb, $result->tb );
$titleC = Title::makeTitle( $result->nsc, $result->tc );
- $linkA = $skin->makeKnownLinkObj( $titleA,'', 'redirect=no' );
$edit = $skin->makeBrokenLinkObj( $titleA, "(".wfMsg("qbedit").")" , 'redirect=no');
$linkB = $skin->makeKnownLinkObj( $titleB, '', 'redirect=no' );
$linkC = $skin->makeKnownLinkObj( $titleC );
@@ -102,4 +101,4 @@ function wfSpecialDoubleRedirects() {
return $sdr->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialEmailuser.php b/includes/SpecialEmailuser.php
index 900a2c32..1d5a9647 100644
--- a/includes/SpecialEmailuser.php
+++ b/includes/SpecialEmailuser.php
@@ -45,6 +45,13 @@ function wfSpecialEmailuser( $par ) {
return;
}
+ if ( $wgUser->isBlockedFromEmailUser() ) {
+ // User has been blocked from sending e-mail. Show the std blocked form.
+ wfDebug( "User is blocked from sending e-mail.\n" );
+ $wgOut->blockedPage();
+ return;
+ }
+
$f = new EmailUserForm( $nu );
if ( "success" == $action ) {
@@ -108,7 +115,7 @@ class EmailUserForm {
$titleObj = SpecialPage::getTitleFor( "Emailuser" );
$action = $titleObj->escapeLocalURL( "target=" .
urlencode( $this->target->getName() ) . "&action=submit" );
- $token = $wgUser->editToken();
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "
<form id=\"emailuser\" method=\"post\" action=\"{$action}\">
@@ -185,4 +192,4 @@ class EmailUserForm {
$wgOut->returnToMain( false, $user->getUserPage() );
}
}
-?>
+
diff --git a/includes/SpecialExport.php b/includes/SpecialExport.php
index a597fdd0..12bd4d5c 100644
--- a/includes/SpecialExport.php
+++ b/includes/SpecialExport.php
@@ -53,7 +53,7 @@ function wfExportGetPagesFromCategory( $title ) {
*
*/
function wfSpecialExport( $page = '' ) {
- global $wgOut, $wgRequest, $wgExportAllowListContributors;
+ global $wgOut, $wgRequest, $wgSitename, $wgExportAllowListContributors;
global $wgExportAllowHistory, $wgExportMaxHistory;
$curonly = true;
@@ -71,7 +71,7 @@ function wfSpecialExport( $page = '' ) {
}
}
}
- else if( $wgRequest->wasPosted() ) {
+ else if( $wgRequest->wasPosted() && $page == '' ) {
$page = $wgRequest->getText( 'pages' );
$curonly = $wgRequest->getCheck( 'curonly' );
$rawOffset = $wgRequest->getVal( 'offset' );
@@ -131,6 +131,11 @@ function wfSpecialExport( $page = '' ) {
// This should provide safer streaming for pages with history
wfResetOutputBuffers();
header( "Content-type: application/xml; charset=utf-8" );
+ if( $wgRequest->getCheck( 'wpDownload' ) ) {
+ // Provide a sane filename suggestion
+ $filename = urlencode( $wgSitename . '-' . wfTimestampNow() . '.xml' );
+ $wgRequest->response()->header( "Content-disposition: attachment;filename={$filename}" );
+ }
$pages = explode( "\n", $page );
$db = wfGetDB( DB_SLAVE );
@@ -164,25 +169,28 @@ function wfSpecialExport( $page = '' ) {
return;
}
- $wgOut->addWikiText( wfMsg( "exporttext" ) );
- $titleObj = SpecialPage::getTitleFor( "Export" );
+ $self = SpecialPage::getTitleFor( 'Export' );
+ $wgOut->addHtml( wfMsgExt( 'exporttext', 'parse' ) );
+
+ $form = Xml::openElement( 'form', array( 'method' => 'post',
+ 'action' => $self->getLocalUrl( 'action=submit' ) ) );
+
+ $form .= Xml::inputLabel( wfMsg( 'export-addcattext' ) , 'catname', 'catname', 40 ) . '&nbsp;';
+ $form .= Xml::submitButton( wfMsg( 'export-addcat' ), array( 'name' => 'addcat' ) ) . '<br />';
+
+ $form .= Xml::openElement( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ) );
+ $form .= htmlspecialchars( $page );
+ $form .= Xml::closeElement( 'textarea' );
+ $form .= '<br />';
- $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalUrl() ) );
-
- $form .= wfInputLabel( wfMsg( 'export-addcattext' ), 'catname', 'catname', 40 ) . ' ';
- $form .= wfSubmitButton( wfMsg( 'export-addcat' ), array( 'name' => 'addcat' ) ) . '<br />';
-
- $form .= wfOpenElement( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ) ) . htmlspecialchars($page). '</textarea><br />';
-
if( $wgExportAllowHistory ) {
- $form .= wfCheck( 'curonly', true, array( 'value' => 'true', 'id' => 'curonly' ) );
- $form .= wfLabel( wfMsg( 'exportcuronly' ), 'curonly' ) . '<br />';
+ $form .= Xml::checkLabel( wfMsg( 'exportcuronly' ), 'curonly', 'curonly', true ) . '<br />';
} else {
- $wgOut->addWikiText( wfMsg( 'exportnohistory' ) );
+ $wgOut->addHtml( wfMsgExt( 'exportnohistory', 'parse' ) );
}
- $form .= wfHidden( 'action', 'submit' );
- $form .= wfSubmitButton( wfMsg( 'export-submit' ) ) . '</form>';
+ $form .= Xml::checkLabel( wfMsg( 'export-download' ), 'wpDownload', 'wpDownload', true ) . '<br />';
+
+ $form .= Xml::submitButton( wfMsg( 'export-submit' ) );
+ $form .= Xml::closeElement( 'form' );
$wgOut->addHtml( $form );
-}
-
-?>
+} \ No newline at end of file
diff --git a/includes/SpecialFewestrevisions.php b/includes/SpecialFewestrevisions.php
index 4c0cd686..ba6db8b6 100644
--- a/includes/SpecialFewestrevisions.php
+++ b/includes/SpecialFewestrevisions.php
@@ -62,4 +62,4 @@ function wfSpecialFewestrevisions() {
$frp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialImagelist.php b/includes/SpecialImagelist.php
index 92b9ae11..1688fe7c 100644
--- a/includes/SpecialImagelist.php
+++ b/includes/SpecialImagelist.php
@@ -57,7 +57,6 @@ class ImageListPager extends TablePager {
function getFieldNames() {
if ( !$this->mFieldNames ) {
$this->mFieldNames = array(
- 'links' => '',
'img_timestamp' => wfMsg( 'imagelist_date' ),
'img_name' => wfMsg( 'imagelist_name' ),
'img_user_text' => wfMsg( 'imagelist_user' ),
@@ -75,7 +74,6 @@ class ImageListPager extends TablePager {
function getQueryInfo() {
$fields = $this->getFieldNames();
- unset( $fields['links'] );
$fields = array_keys( $fields );
$fields[] = 'img_user';
return array(
@@ -112,17 +110,15 @@ class ImageListPager extends TablePager {
function formatValue( $field, $value ) {
global $wgLang;
switch ( $field ) {
- case 'links':
- $name = $this->mCurrentRow->img_name;
- $ilink = "<a href=\"" . htmlspecialchars( Image::imageUrl( $name ) ) .
- "\">" . $this->mMessages['imgfile'] . "</a>";
- $desc = $this->getSkin()->makeKnownLinkObj( Title::makeTitle( NS_IMAGE, $name ),
- $this->mMessages['imgdesc'] );
- return "$desc | $ilink";
case 'img_timestamp':
return $wgLang->timeanddate( $value, true );
case 'img_name':
- return htmlspecialchars( $value );
+ $name = $this->mCurrentRow->img_name;
+ $link = $this->getSkin()->makeKnownLinkObj( Title::makeTitle( NS_IMAGE, $name ), $value );
+ $image = wfLocalFile( $value );
+ $url = $image->getURL();
+ $download = Xml::element('a', array( "href" => $url ), $this->mMessages['imgfile'] );
+ return "$link ($download)";
case 'img_user_text':
if ( $this->mCurrentRow->img_user ) {
$link = $this->getSkin()->makeLinkObj( Title::makeTitle( NS_USER, $value ),
@@ -132,7 +128,7 @@ class ImageListPager extends TablePager {
}
return $link;
case 'img_size':
- return $wgLang->formatNum( $value );
+ return $this->getSkin()->formatSize( $value );
case 'img_description':
return $this->getSkin()->commentBlock( $value );
}
@@ -167,4 +163,4 @@ class ImageListPager extends TablePager {
}
}
-?>
+
diff --git a/includes/SpecialImport.php b/includes/SpecialImport.php
index c7b861d0..ad5d8e64 100644
--- a/includes/SpecialImport.php
+++ b/includes/SpecialImport.php
@@ -85,7 +85,7 @@ function wfSpecialImport( $page = '' ) {
}
}
- $action = $wgTitle->escapeLocalUrl( 'action=submit' );
+ $action = $wgTitle->getLocalUrl( 'action=submit' );
if( $wgUser->isAllowed( 'importupload' ) ) {
$wgOut->addWikiText( wfMsg( "importtext" ) );
@@ -209,6 +209,9 @@ class ImportReporter {
$nullRevision = Revision::newNullRevision(
$dbw, $title->getArticleId(), $comment, true );
$nullRevision->insertOn( $dbw );
+ # Update page record
+ $article = new Article( $title );
+ $article->updateRevisionOn( $dbw, $nullRevision );
}
}
@@ -857,13 +860,22 @@ class ImportStreamSource {
}
}
- function newFromURL( $url ) {
+ function newFromURL( $url, $method = 'GET' ) {
wfDebug( __METHOD__ . ": opening $url\n" );
- # fopen-wrappers are normally turned off for security.
- ini_set( "allow_url_fopen", true );
- $ret = ImportStreamSource::newFromFile( $url );
- ini_set( "allow_url_fopen", false );
- return $ret;
+ # Use the standard HTTP fetch function; it times out
+ # quicker and sorts out user-agent problems which might
+ # otherwise prevent importing from large sites, such
+ # as the Wikimedia cluster, etc.
+ $data = Http::request( $method, $url );
+ if( $data !== false ) {
+ $file = tmpfile();
+ fwrite( $file, $data );
+ fflush( $file );
+ fseek( $file, 0 );
+ return new ImportStreamSource( $file );
+ } else {
+ return new WikiErrorMsg( 'importcantopen' );
+ }
}
public static function newFromInterwiki( $interwiki, $page, $history=false ) {
@@ -873,10 +885,11 @@ class ImportStreamSource {
} else {
$params = $history ? 'history=1' : '';
$url = $link->getFullUrl( $params );
- return ImportStreamSource::newFromURL( $url );
+ # For interwikis, use POST to avoid redirects.
+ return ImportStreamSource::newFromURL( $url, "POST" );
}
}
}
-?>
+
diff --git a/includes/SpecialIpblocklist.php b/includes/SpecialIpblocklist.php
index 8cb5729e..4f093dcb 100644
--- a/includes/SpecialIpblocklist.php
+++ b/includes/SpecialIpblocklist.php
@@ -18,30 +18,49 @@ function wfSpecialIpblocklist() {
$ipu = new IPUnblockForm( $ip, $id, $reason );
- if ( "success" == $action ) {
- $ipu->showList( $wgOut->parse( wfMsg( 'unblocked', $successip ) ) );
- } else if ( "submit" == $action && $wgRequest->wasPosted() &&
- $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
- if ( ! $wgUser->isAllowed('block') ) {
+ if( $action == 'unblock' ) {
+ # Check permissions
+ if( !$wgUser->isAllowed( 'block' ) ) {
$wgOut->permissionRequired( 'block' );
return;
}
- # Can't unblock when the database is locked
+ # Check for database lock
if( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
- $ipu->doSubmit();
- } else if ( "unblock" == $action ) {
- # Can't unblock when the database is locked
+ # Show unblock form
+ $ipu->showForm( '' );
+ } elseif( $action == 'submit' && $wgRequest->wasPosted()
+ && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+ # Check permissions
+ if( !$wgUser->isAllowed( 'block' ) ) {
+ $wgOut->permissionRequired( 'block' );
+ return;
+ }
+ # Check for database lock
if( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
- $ipu->showForm( "" );
+ # Remove blocks and redirect user to success page
+ $ipu->doSubmit();
+ } elseif( $action == 'success' ) {
+ # Inform the user of a successful unblock
+ # (No need to check permissions or locks here,
+ # if something was done, then it's too late!)
+ if ( substr( $successip, 0, 1) == '#' ) {
+ // A block ID was unblocked
+ $ipu->showList( $wgOut->parse( wfMsg( 'unblocked-id', $successip ) ) );
+ } else {
+ // A username/IP was unblocked
+ $ipu->showList( $wgOut->parse( wfMsg( 'unblocked', $successip ) ) );
+ }
} else {
- $ipu->showList( "" );
+ # Just show the block list
+ $ipu->showList( '' );
}
+
}
/**
@@ -58,7 +77,7 @@ class IPUnblockForm {
}
function showForm( $err ) {
- global $wgOut, $wgUser, $wgSysopUserBans;
+ global $wgOut, $wgUser, $wgSysopUserBans, $wgContLang;
$wgOut->setPagetitle( wfMsg( 'unblockip' ) );
$wgOut->addWikiText( wfMsg( 'unblockiptext' ) );
@@ -67,7 +86,8 @@ class IPUnblockForm {
$ipr = wfMsgHtml( 'ipbreason' );
$ipus = wfMsgHtml( 'ipusubmit' );
$titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
- $action = $titleObj->escapeLocalURL( "action=submit" );
+ $action = $titleObj->getLocalURL( "action=submit" );
+ $alignRight = $wgContLang->isRtl() ? 'left' : 'right';
if ( "" != $err ) {
$wgOut->setSubtitle( wfMsg( "formerror" ) );
@@ -80,39 +100,43 @@ class IPUnblockForm {
$block = Block::newFromID( $this->id );
if ( $block ) {
$encName = htmlspecialchars( $block->getRedactedName() );
- $encId = htmlspecialchars( $this->id );
- $addressPart = $encName . "<input type='hidden' name=\"id\" value=\"$encId\" />";
+ $encId = $this->id;
+ $addressPart = $encName . Xml::hidden( 'id', $encId );
}
}
if ( !$addressPart ) {
- $addressPart = "<input tabindex='1' type='text' size='20' " .
- "name=\"wpUnblockAddress\" value=\"" . htmlspecialchars( $this->ip ) . "\" />";
+ $addressPart = Xml::input( 'wpUnblockAddress', 20, $this->ip, array( 'type' => 'text', 'tabindex' => '1' ) );
}
- $wgOut->addHTML( "
-<form id=\"unblockip\" method=\"post\" action=\"{$action}\">
- <table border='0'>
- <tr>
- <td align='right'>{$ipa}:</td>
- <td align='left'>
- {$addressPart}
- </td>
- </tr>
- <tr>
- <td align='right'>{$ipr}:</td>
- <td align='left'>
- <input tabindex='1' type='text' size='40' name=\"wpUnblockReason\" value=\"" . htmlspecialchars( $this->reason ) . "\" />
- </td>
- </tr>
- <tr>
- <td>&nbsp;</td>
- <td align='left'>
- <input tabindex='2' type='submit' name=\"wpBlock\" value=\"{$ipus}\" />
- </td>
- </tr>
- </table>
- <input type='hidden' name='wpEditToken' value=\"{$token}\" />
-</form>\n" );
+ $wgOut->addHTML(
+ Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'unblockip' ) ) .
+ Xml::openElement( 'table', array( 'border' => '0' ) ).
+ "<tr>
+ <td align='$alignRight'>
+ {$ipa}
+ </td>
+ <td>
+ {$addressPart}
+ </td>
+ </tr>
+ <tr>
+ <td align='$alignRight'>
+ {$ipr}
+ </td>
+ <td>" .
+ Xml::input( 'wpUnblockReason', 40, $this->reason, array( 'type' => 'text', 'tabindex' => '2' ) ) .
+ "</td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>" .
+ Xml::submitButton( $ipus, array( 'name' => 'wpBlock', 'tabindex' => '3' ) ) .
+ "</td>
+ </tr>" .
+ Xml::closeElement( 'table' ) .
+ Xml::hidden( 'wpEditToken', $token ) .
+ Xml::closeElement( 'form' ) . "\n"
+ );
}
@@ -200,46 +224,34 @@ class IPUnblockForm {
}
}
- # TODO: difference message between
- # a) an real empty list and
- # b) requested ip/username not on list
$pager = new IPBlocklistPager( $this, $conds );
if ( $pager->getNumRows() ) {
- $s = $this->searchForm() .
- $pager->getNavigationBar();
- $s .= "<ul>" .
- $pager->getBody() .
- "</ul>";
- $s .= $pager->getNavigationBar();
+ $wgOut->addHTML(
+ $this->searchForm() .
+ $pager->getNavigationBar() .
+ Xml::tags( 'ul', null, $pager->getBody() ) .
+ $pager->getNavigationBar()
+ );
+ } elseif ( $this->ip != '') {
+ $wgOut->addHTML( $this->searchForm() );
+ $wgOut->addWikiText( wfMsg( 'ipblocklist-no-results' ) );
} else {
- $s = $this->searchForm() .
- '<p>' . wfMsgHTML( 'ipblocklistempty' ) . '</p>';
+ $wgOut->addWikiText( wfMsg( 'ipblocklist-empty' ) );
}
- $wgOut->addHTML( $s );
}
function searchForm() {
global $wgTitle, $wgScript, $wgRequest;
return
- wfElement( 'form', array(
- 'action' => $wgScript ),
- null ) .
- wfHidden( 'title', $wgTitle->getPrefixedDbKey() ) .
- wfElement( 'input', array(
- 'type' => 'hidden',
- 'name' => 'action',
- 'value' => 'search' ) ).
- wfElement( 'input', array(
- 'type' => 'hidden',
- 'name' => 'limit',
- 'value' => $wgRequest->getText( 'limit' ) ) ) .
- wfElement( 'input', array(
- 'name' => 'ip',
- 'value' => $this->ip ) ) .
- wfElement( 'input', array(
- 'type' => 'submit',
- 'value' => wfMsg( 'ipblocklist-submit' ) ) ) .
- '</form>';
+ Xml::tags( 'form', array( 'action' => $wgScript ),
+ Xml::hidden( 'title', $wgTitle->getPrefixedDbKey() ) .
+ Xml::openElement( 'fieldset' ) .
+ Xml::element( 'legend', null, wfMsg( 'ipblocklist-legend' ) ) .
+ Xml::inputLabel( wfMsg( 'ipblocklist-username' ), 'ip', 'ip', /* size */ false, $this->ip ) .
+ '&nbsp;' .
+ Xml::submitButton( wfMsg( 'ipblocklist-submit' ) ) .
+ Xml::closeElement( 'fieldset' )
+ );
}
/**
@@ -257,7 +269,7 @@ class IPUnblockForm {
if( is_null( $msg ) ) {
$msg = array();
$keys = array( 'infiniteblock', 'expiringblock', 'contribslink', 'unblocklink',
- 'anononlyblock', 'createaccountblock', 'noautoblockblock' );
+ 'anononlyblock', 'createaccountblock', 'noautoblockblock', 'emailblock' );
foreach( $keys as $key ) {
$msg[$key] = wfMsgHtml( $key );
}
@@ -275,8 +287,8 @@ class IPUnblockForm {
if( $block->mAuto ) {
$target = $block->getRedactedName(); # Hide the IP addresses of auto-blocks; privacy
} else {
- $target = $sk->makeLinkObj( Title::makeTitle( NS_USER, $block->mAddress ), $block->mAddress );
- $target .= ' (' . $sk->makeKnownLinkObj( SpecialPage::getSafeTitleFor( 'Contributions', $block->mAddress ), $msg['contribslink'] ) . ')';
+ $target = $sk->userLink( $block->mUser, $block->mAddress )
+ . $sk->userToolLinks( $block->mUser, $block->mAddress, false, Linker::TOOL_LINKS_NOBLOCK );
}
$formattedTime = $wgLang->timeanddate( $block->mTimestamp, true );
@@ -298,6 +310,10 @@ class IPUnblockForm {
$properties[] = $msg['noautoblockblock'];
}
+ if ( $block->mBlockEmail && $block->mUser ) {
+ $properties[] = $msg['emailblock'];
+ }
+
$properties = implode( ', ', $properties );
$line = wfMsgReplaceArgs( $msg['blocklistline'], array( $formattedTime, $blocker, $target, $properties ) );
@@ -383,4 +399,4 @@ class IPBlocklistPager extends ReverseChronologicalPager {
}
}
-?>
+
diff --git a/includes/SpecialListredirects.php b/includes/SpecialListredirects.php
index 09dc2b39..581ea55b 100644
--- a/includes/SpecialListredirects.php
+++ b/includes/SpecialListredirects.php
@@ -62,4 +62,4 @@ function wfSpecialListredirects() {
$lrp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialListusers.php b/includes/SpecialListusers.php
index 42498430..460d4259 100644
--- a/includes/SpecialListusers.php
+++ b/includes/SpecialListusers.php
@@ -103,7 +103,6 @@ class UsersPager extends AlphabeticPager {
$this->doQuery();
}
$batch = new LinkBatch;
- $db = $this->mDb;
$this->mResult->rewind();
@@ -116,35 +115,30 @@ class UsersPager extends AlphabeticPager {
}
function getPageHeader( ) {
- global $wgRequest;
+ global $wgScript, $wgRequest;
$self = $this->getTitle();
# Form tag
- $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $self->getLocalUrl() ) ) .
+ $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) .
'<fieldset>' .
Xml::element( 'legend', array(), wfMsg( 'listusers' ) );
+ $out .= Xml::hidden( 'title', $self->getPrefixedDbKey() );
# Username field
$out .= Xml::label( wfMsg( 'listusersfrom' ), 'offset' ) . ' ' .
Xml::input( 'username', 20, $this->requestedUser, array( 'id' => 'offset' ) ) . ' ';
- if( $this->mLimit )
- $out .= Xml::hidden( 'limit', $this->mLimit );
-
# Group drop-down list
$out .= Xml::label( wfMsg( 'group' ), 'group' ) . ' ' .
Xml::openElement('select', array( 'name' => 'group', 'id' => 'group' ) ) .
- Xml::option( wfMsg( 'group-all' ), '' ); # Item for "all groups"
-
- $groups = User::getAllGroups();
- foreach( $groups as $group ) {
- $attribs = array( 'value' => $group );
- $attribs['selected'] = ( $group == $this->requestedGroup ) ? 'selected' : '';
- $out .= Xml::option( User::getGroupName( $group ), $attribs['value'], $attribs['selected'] );
- }
+ Xml::option( wfMsg( 'group-all' ), '' );
+ foreach( User::getAllGroups() as $group )
+ $out .= Xml::option( User::getGroupName( $group ), $group, $group == $this->requestedGroup );
$out .= Xml::closeElement( 'select' ) . ' ';
# Submit button and form bottom
+ if( $this->mLimit )
+ $out .= Xml::hidden( 'limit', $this->mLimit );
$out .= Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
'</fieldset>' .
Xml::closeElement( 'form' );
@@ -204,10 +198,6 @@ class UsersPager extends AlphabeticPager {
function wfSpecialListusers( $par = null ) {
global $wgRequest, $wgOut;
- list( $limit, $offset ) = wfCheckLimits();
-
- $groupTarget = isset($par) ? $par : $wgRequest->getVal( 'group' );
-
$up = new UsersPager($par);
# getBody() first to check, if empty
@@ -224,4 +214,4 @@ function wfSpecialListusers( $par = null ) {
$wgOut->addHTML( $s );
}
-?>
+
diff --git a/includes/SpecialLockdb.php b/includes/SpecialLockdb.php
index db4006f5..e57717e2 100644
--- a/includes/SpecialLockdb.php
+++ b/includes/SpecialLockdb.php
@@ -131,4 +131,4 @@ END
}
-?>
+
diff --git a/includes/SpecialLog.php b/includes/SpecialLog.php
index 3c9d0960..f0794eb5 100644
--- a/includes/SpecialLog.php
+++ b/includes/SpecialLog.php
@@ -74,7 +74,8 @@ class LogReader {
// XXX This all needs to use Pager, ugly hack for now.
global $wgMiserMode;
- if ($wgMiserMode && ($this->offset >10000)) $this->offset=10000;
+ if( $wgMiserMode )
+ $this->offset = min( $this->offset, 10000 );
}
/**
@@ -123,9 +124,10 @@ class LogReader {
function limitTitle( $page , $pattern ) {
global $wgMiserMode;
$title = Title::newFromText( $page );
- if( empty( $page ) || is_null( $title ) ) {
+
+ if( strlen( $page ) == 0 || !$title instanceof Title )
return false;
- }
+
$this->title =& $title;
$this->pattern = $pattern;
$ns = $title->getNamespace();
@@ -215,6 +217,23 @@ class LogReader {
return $this->title->getPrefixedText();
}
}
+
+ /**
+ * Is there at least one row?
+ *
+ * @return bool
+ */
+ public function hasRows() {
+ # Little hack...
+ $limit = $this->limit;
+ $this->limit = 1;
+ $res = $this->db->query( $this->getQuery() );
+ $this->limit = $limit;
+ $ret = $this->db->numRows( $res ) > 0;
+ $this->db->freeResult( $res );
+ return $ret;
+ }
+
}
/**
@@ -222,19 +241,25 @@ class LogReader {
* @addtogroup SpecialPage
*/
class LogViewer {
+ const NO_ACTION_LINK = 1;
+
/**
* @var LogReader $reader
*/
var $reader;
var $numResults = 0;
+ var $flags = 0;
/**
* @param LogReader &$reader where to get our data from
+ * @param integer $flags Bitwise combination of flags:
+ * self::NO_ACTION_LINK Don't show restore/unblock/block links
*/
- function LogViewer( &$reader ) {
+ function LogViewer( &$reader, $flags = 0 ) {
global $wgUser;
$this->skin = $wgUser->getSkin();
$this->reader =& $reader;
+ $this->flags = $flags;
}
/**
@@ -325,7 +350,7 @@ class LogViewer {
* @private
*/
function logLine( $s ) {
- global $wgLang, $wgUser;;
+ global $wgLang, $wgUser, $wgContLang;
$skin = $wgUser->getSkin();
$title = Title::makeTitle( $s->log_namespace, $s->log_title );
$time = $wgLang->timeanddate( wfTimestamp(TS_MW, $s->log_timestamp), true );
@@ -340,42 +365,47 @@ class LogViewer {
}
$userLink = $this->skin->userLink( $s->log_user, $s->user_name ) . $this->skin->userToolLinksRedContribs( $s->log_user, $s->user_name );
- $comment = $this->skin->commentBlock( $s->log_comment );
+ $comment = $wgContLang->getDirMark() . $this->skin->commentBlock( $s->log_comment );
$paramArray = LogPage::extractParams( $s->log_params );
$revert = '';
// show revertmove link
- if ( $s->log_type == 'move' && isset( $paramArray[0] ) ) {
- $destTitle = Title::newFromText( $paramArray[0] );
- if ( $destTitle ) {
- $revert = '(' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Movepage' ),
- wfMsg( 'revertmove' ),
- 'wpOldTitle=' . urlencode( $destTitle->getPrefixedDBkey() ) .
- '&wpNewTitle=' . urlencode( $title->getPrefixedDBkey() ) .
- '&wpReason=' . urlencode( wfMsgForContent( 'revertmove' ) ) .
- '&wpMovetalk=0' ) . ')';
+ if ( !( $this->flags & self::NO_ACTION_LINK ) ) {
+ if ( $s->log_type == 'move' && isset( $paramArray[0] ) ) {
+ $destTitle = Title::newFromText( $paramArray[0] );
+ if ( $destTitle ) {
+ $revert = '(' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Movepage' ),
+ wfMsg( 'revertmove' ),
+ 'wpOldTitle=' . urlencode( $destTitle->getPrefixedDBkey() ) .
+ '&wpNewTitle=' . urlencode( $title->getPrefixedDBkey() ) .
+ '&wpReason=' . urlencode( wfMsgForContent( 'revertmove' ) ) .
+ '&wpMovetalk=0' ) . ')';
+ }
+ // show undelete link
+ } elseif ( $s->log_action == 'delete' && $wgUser->isAllowed( 'delete' ) ) {
+ $revert = '(' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Undelete' ),
+ wfMsg( 'undeletebtn' ) ,
+ 'target='. urlencode( $title->getPrefixedDBkey() ) ) . ')';
+
+ // show unblock link
+ } elseif ( $s->log_action == 'block' && $wgUser->isAllowed( 'block' ) ) {
+ $revert = '(' . $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Ipblocklist' ),
+ wfMsg( 'unblocklink' ),
+ 'action=unblock&ip=' . urlencode( $s->log_title ) ) . ')';
+ // show change protection link
+ } elseif ( ( $s->log_action == 'protect' || $s->log_action == 'modify' ) && $wgUser->isAllowed( 'protect' ) ) {
+ $revert = '(' . $skin->makeKnownLinkObj( $title, wfMsg( 'protect_change' ), 'action=unprotect' ) . ')';
+ // show user tool links for self created users
+ // TODO: The extension should be handling this, get it out of core!
+ } elseif ( $s->log_action == 'create2' ) {
+ if( isset( $paramArray[0] ) ) {
+ $revert = $this->skin->userToolLinks( $paramArray[0], $s->log_title, true );
+ } else {
+ # Fall back to a blue contributions link
+ $revert = $this->skin->userToolLinks( 1, $s->log_title );
+ }
+ # Suppress $comment from old entries, not needed and can contain incorrect links
+ $comment = '';
}
- // show undelete link
- } elseif ( $s->log_action == 'delete' && $wgUser->isAllowed( 'delete' ) ) {
- $revert = '(' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Undelete' ),
- wfMsg( 'undeletebtn' ) ,
- 'target='. urlencode( $title->getPrefixedDBkey() ) ) . ')';
-
- // show unblock link
- } elseif ( $s->log_action == 'block' && $wgUser->isAllowed( 'block' ) ) {
- $revert = '(' . $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Ipblocklist' ),
- wfMsg( 'unblocklink' ),
- 'action=unblock&ip=' . urlencode( $s->log_title ) ) . ')';
- // show change protection link
- } elseif ( $s->log_action == 'protect' && $wgUser->isAllowed( 'protect' ) ) {
- $revert = '(' . $skin->makeKnownLink( $title->getPrefixedDBkey() ,
- wfMsg( 'protect_change' ),
- 'action=unprotect' ) . ')';
- // show user tool links for self created users
- } elseif ( $s->log_action == 'create2' ) {
- $revert = $this->skin->userToolLinksRedContribs( $s->log_user, $s->log_title );
- // do not show $comment for self created accounts. It includes wrong user tool links:
- // 'blockip' for users w/o block allowance and broken links for very long usernames (bug 4756)
- $comment = '';
}
$action = LogPage::actionText( $s->log_type, $s->log_action, $title, $this->skin, $paramArray, true, true );
@@ -497,4 +527,4 @@ class LogViewer {
}
-?>
+
diff --git a/includes/SpecialLonelypages.php b/includes/SpecialLonelypages.php
index 430af7a7..e652f9d4 100644
--- a/includes/SpecialLonelypages.php
+++ b/includes/SpecialLonelypages.php
@@ -15,7 +15,7 @@ class LonelyPagesPage extends PageQueryPage {
return "Lonelypages";
}
function getPageHeader() {
- return '<p>' . wfMsg('lonelypagestext') . '</p>';
+ return wfMsgExt( 'lonelypagestext', array( 'parse' ) );
}
function sortDescending() {
@@ -57,4 +57,4 @@ function wfSpecialLonelypages() {
return $lpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialLongpages.php b/includes/SpecialLongpages.php
index 40659889..a8a1e199 100644
--- a/includes/SpecialLongpages.php
+++ b/includes/SpecialLongpages.php
@@ -30,4 +30,4 @@ function wfSpecialLongpages() {
$lpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialMIMEsearch.php b/includes/SpecialMIMEsearch.php
index d50efc02..c89c1af6 100644
--- a/includes/SpecialMIMEsearch.php
+++ b/includes/SpecialMIMEsearch.php
@@ -66,7 +66,7 @@ class MIMEsearchPage extends QueryPage {
$text = $wgContLang->convert( $nt->getText() );
$plink = $skin->makeLink( $nt->getPrefixedText(), $text );
- $download = $skin->makeMediaLink( $nt->getText(), 'fuck me!', wfMsgHtml( 'download' ) );
+ $download = $skin->makeMediaLinkObj( $nt, wfMsgHtml( 'download' ) );
$bytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
$wgLang->formatNum( $result->img_size ) );
$dimensions = wfMsgHtml( 'widthheight', $wgLang->formatNum( $result->img_width ),
@@ -87,33 +87,16 @@ function wfSpecialMIMEsearch( $par = null ) {
$mime = isset( $par ) ? $par : $wgRequest->getText( 'mime' );
$wgOut->addHTML(
- wfElement( 'form',
+ Xml::openElement( 'form',
array(
'id' => 'specialmimesearch',
'method' => 'get',
'action' => $wgTitle->escapeLocalUrl()
- ),
- null
+ )
) .
- wfOpenElement( 'label' ) .
- wfMsgHtml( 'mimetype' ) .
- wfElement( 'input', array(
- 'type' => 'text',
- 'size' => 20,
- 'name' => 'mime',
- 'value' => $mime
- ),
- ''
- ) .
- ' ' .
- wfElement( 'input', array(
- 'type' => 'submit',
- 'value' => wfMsg( 'ilsubmit' )
- ),
- ''
- ) .
- wfCloseElement( 'label' ) .
- wfCloseElement( 'form' )
+ Xml::inputLabel( wfMsg( 'mimetype' ), 'mime', 'mime', 20, $mime ) .
+ Xml::submitButton( wfMsg( 'ilsubmit' ) ) .
+ Xml::closeElement( 'form' )
);
list( $major, $minor ) = wfSpecialMIMEsearchParse( $mime );
@@ -155,4 +138,4 @@ function wfSpecialMIMEsearchValidType( $type ) {
return in_array( $type, $types );
}
-?>
+
diff --git a/includes/SpecialMostcategories.php b/includes/SpecialMostcategories.php
index df2b9adf..589b96ee 100644
--- a/includes/SpecialMostcategories.php
+++ b/includes/SpecialMostcategories.php
@@ -38,6 +38,7 @@ class MostcategoriesPage extends QueryPage {
function formatResult( $skin, $result ) {
global $wgLang;
$title = Title::makeTitleSafe( $result->namespace, $result->title );
+ if ( !$title instanceof Title ) { throw new MWException('Invalid title in database'); }
$count = wfMsgExt( 'ncategories', array( 'parsemag', 'escape' ), $wgLang->formatNum( $result->value ) );
$link = $skin->makeKnownLinkObj( $title, $title->getText() );
return wfSpecialList( $link, $count );
@@ -55,4 +56,4 @@ function wfSpecialMostcategories() {
$wpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialMostimages.php b/includes/SpecialMostimages.php
index 9d16f389..beb42fc1 100644
--- a/includes/SpecialMostimages.php
+++ b/includes/SpecialMostimages.php
@@ -52,4 +52,4 @@ function wfSpecialMostimages() {
$wpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialMostlinked.php b/includes/SpecialMostlinked.php
index ab089cf8..b4de0a0e 100644
--- a/includes/SpecialMostlinked.php
+++ b/includes/SpecialMostlinked.php
@@ -90,4 +90,4 @@ function wfSpecialMostlinked() {
$wpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialMostlinkedcategories.php b/includes/SpecialMostlinkedcategories.php
index 725e5b39..d0a99b3b 100644
--- a/includes/SpecialMostlinkedcategories.php
+++ b/includes/SpecialMostlinkedcategories.php
@@ -72,4 +72,4 @@ function wfSpecialMostlinkedCategories() {
$wpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialMostlinkedtemplates.php b/includes/SpecialMostlinkedtemplates.php
new file mode 100644
index 00000000..e7e7afcc
--- /dev/null
+++ b/includes/SpecialMostlinkedtemplates.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * Special page lists templates with a large number of
+ * transclusion links, i.e. "most used" templates
+ *
+ * @addtogroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+class SpecialMostlinkedtemplates extends QueryPage {
+
+ /**
+ * Name of the report
+ *
+ * @return string
+ */
+ public function getName() {
+ return 'Mostlinkedtemplates';
+ }
+
+ /**
+ * Is this report expensive, i.e should it be cached?
+ *
+ * @return bool
+ */
+ public function isExpensive() {
+ return true;
+ }
+
+ /**
+ * Is there a feed available?
+ *
+ * @return bool
+ */
+ public function isSyndicated() {
+ return false;
+ }
+
+ /**
+ * Sort the results in descending order?
+ *
+ * @return bool
+ */
+ public function sortDescending() {
+ return true;
+ }
+
+ /**
+ * Generate SQL for the report
+ *
+ * @return string
+ */
+ public function getSql() {
+ $dbr = wfGetDB( DB_SLAVE );
+ $templatelinks = $dbr->tableName( 'templatelinks' );
+ $name = $dbr->addQuotes( $this->getName() );
+ return "SELECT {$name} AS type,
+ " . NS_TEMPLATE . " AS namespace,
+ tl_title AS title,
+ COUNT(*) AS value
+ FROM {$templatelinks}
+ WHERE tl_namespace = " . NS_TEMPLATE . "
+ GROUP BY 1, 2, 3";
+ }
+
+ /**
+ * Pre-cache page existence to speed up link generation
+ *
+ * @param Database $dbr Database connection
+ * @param int $res Result pointer
+ */
+ public function preprocessResults( $dbr, $res ) {
+ $batch = new LinkBatch();
+ while( $row = $dbr->fetchObject( $res ) ) {
+ $title = Title::makeTitleSafe( $row->namespace, $row->title );
+ $batch->addObj( $title );
+ }
+ $batch->execute();
+ if( $dbr->numRows( $res ) > 0 )
+ $dbr->dataSeek( $res, 0 );
+ }
+
+ /**
+ * Format a result row
+ *
+ * @param Skin $skin Skin to use for UI elements
+ * @param object $result Result row
+ * @return string
+ */
+ public function formatResult( $skin, $result ) {
+ $title = Title::makeTitleSafe( $result->namespace, $result->title );
+ if( $title instanceof Title ) {
+ return wfSpecialList(
+ $skin->makeLinkObj( $title ),
+ $this->makeWlhLink( $title, $skin, $result )
+ );
+ } else {
+ $tsafe = htmlspecialchars( $result->title );
+ return "Invalid title in result set; {$tsafe}";
+ }
+ }
+
+ /**
+ * Make a "what links here" link for a given title
+ *
+ * @param Title $title Title to make the link for
+ * @param Skin $skin Skin to use
+ * @param object $result Result row
+ * @return string
+ */
+ private function makeWlhLink( $title, $skin, $result ) {
+ global $wgLang;
+ $wlh = SpecialPage::getTitleFor( 'Whatlinkshere' );
+ $label = wfMsgExt( 'nlinks', array( 'parsemag', 'escape' ),
+ $wgLang->formatNum( $result->value ) );
+ return $skin->makeKnownLinkObj( $wlh, $label, 'target=' . $title->getPrefixedUrl() );
+ }
+
+}
+
+/**
+ * Execution function
+ *
+ * @param mixed $par Parameters passed to the page
+ */
+function wfSpecialMostlinkedtemplates( $par = false ) {
+ list( $limit, $offset ) = wfCheckLimits();
+ $mlt = new SpecialMostlinkedtemplates();
+ $mlt->doQuery( $offset, $limit );
+}
+
diff --git a/includes/SpecialMostrevisions.php b/includes/SpecialMostrevisions.php
index 59157056..9479a583 100644
--- a/includes/SpecialMostrevisions.php
+++ b/includes/SpecialMostrevisions.php
@@ -63,4 +63,4 @@ function wfSpecialMostrevisions() {
$wpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialMovepage.php b/includes/SpecialMovepage.php
index d8f01874..cfc434ae 100644
--- a/includes/SpecialMovepage.php
+++ b/includes/SpecialMovepage.php
@@ -12,7 +12,7 @@ function wfSpecialMovepage( $par = null ) {
# Check rights
if ( !$wgUser->isAllowed( 'move' ) ) {
- $wgOut->showErrorPage( 'movenologin', 'movenologintext' );
+ $wgOut->showPermissionsErrorPage( array( $wgUser->isAnon() ? 'movenologintext' : 'movenotallowed' ) );
return;
}
@@ -105,14 +105,10 @@ class MovePageForm {
if ( $err == 'articleexists' && $wgUser->isAllowed( 'delete' ) ) {
$wgOut->addWikiText( wfMsg( 'delete_and_move_text', $encNewTitle ) );
$movepagebtn = wfMsgHtml( 'delete_and_move' );
- $confirmText = wfMsgHtml( 'delete_and_move_confirm' );
$submitVar = 'wpDeleteAndMove';
$confirm = "
<tr>
- <td align='$end'>
- <input type='checkbox' name='wpConfirm' id='wpConfirm' value=\"true\" />
- </td>
- <td align='$start'><label for='wpConfirm'>{$confirmText}</label></td>
+ <td></td><td>" . Xml::checkLabel( wfMsg( 'delete_and_move_confirm' ), 'wpConfirm', 'wpConfirm' ) . "</td>
</tr>";
$err = '';
} else {
@@ -131,7 +127,6 @@ class MovePageForm {
$movearticle = wfMsgHtml( 'movearticle' );
$newtitle = wfMsgHtml( 'newtitle' );
- $movetalk = wfMsgHtml( 'movetalk' );
$movereason = wfMsgHtml( 'movereason' );
$titleObj = SpecialPage::getTitleFor( 'Movepage' );
@@ -149,18 +144,18 @@ class MovePageForm {
<form id=\"movepage\" method=\"post\" action=\"{$action}\">
<table border='0'>
<tr>
- <td align='$end'>{$movearticle}:</td>
+ <td align='$end'>{$movearticle}</td>
<td align='$start'><strong>{$oldTitle}</strong></td>
</tr>
<tr>
- <td align='$end'><label for='wpNewTitle'>{$newtitle}:</label></td>
+ <td align='$end'><label for='wpNewTitle'>{$newtitle}</label></td>
<td align='$start'>
<input type='text' size='40' name='wpNewTitle' id='wpNewTitle' value=\"{$encNewTitle}\" />
<input type='hidden' name=\"wpOldTitle\" value=\"{$encOldTitle}\" />
</td>
</tr>
<tr>
- <td align='$end' valign='top'><br /><label for='wpReason'>{$movereason}:</label></td>
+ <td align='$end' valign='top'><br /><label for='wpReason'>{$movereason}</label></td>
<td align='$start' valign='top'><br />
<textarea cols='60' rows='2' name='wpReason' id='wpReason'>{$encReason}</textarea>
</td>
@@ -169,20 +164,16 @@ class MovePageForm {
if ( $considerTalk ) {
$wgOut->addHTML( "
<tr>
- <td align='$end'>
- <input type='checkbox' id=\"wpMovetalk\" name=\"wpMovetalk\"{$moveTalkChecked} value=\"1\" />
- </td>
- <td><label for=\"wpMovetalk\">{$movetalk}</label></td>
+ <td></td><td>" . Xml::checkLabel( wfMsg( 'movetalk' ), 'wpMovetalk', 'wpMovetalk', $moveTalkChecked ) . "</td>
</tr>" );
}
-
+
$watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) || $ot->userIsWatching();
$watch = '<tr>';
- $watch .= "<td align=\"$end\">" . Xml::check( 'wpWatch', $watchChecked, array( 'id' => 'watch' ) ) . '</td>';
- $watch .= '<td>' . Xml::label( wfMsg( 'move-watch' ), 'watch' ) . '</td>';
+ $watch .= '<td></td><td>' . Xml::checkLabel( wfMsg( 'move-watch' ), 'wpWatch', 'watch', $watchChecked ) . '</td>';
$watch .= '</tr>';
$wgOut->addHtml( $watch );
-
+
$wgOut->addHTML( "
{$confirm}
<tr>
@@ -275,32 +266,38 @@ class MovePageForm {
}
function showSuccess() {
- global $wgOut, $wgRequest, $wgRawHtml;
+ global $wgOut, $wgRequest, $wgUser;
+
+ $old = Title::newFromText( $wgRequest->getVal( 'oldtitle' ) );
+ $new = Title::newFromText( $wgRequest->getVal( 'newtitle' ) );
+
+ if( is_null( $old ) || is_null( $new ) ) {
+ throw new ErrorPageError( 'badtitle', 'badtitletext' );
+ }
$wgOut->setPagetitle( wfMsg( 'movepage' ) );
$wgOut->setSubtitle( wfMsg( 'pagemovedsub' ) );
- $oldText = wfEscapeWikiText( $wgRequest->getVal('oldtitle') );
- $newText = wfEscapeWikiText( $wgRequest->getVal('newtitle') );
- $talkmoved = $wgRequest->getVal('talkmoved');
+ $talkmoved = $wgRequest->getVal( 'talkmoved' );
+ $oldUrl = $old->getFullUrl( 'redirect=no' );
+ $newUrl = $new->getFullURl();
+ $oldText = $old->getPrefixedText();
+ $newText = $new->getPrefixedText();
+ $oldLink = "<span class='plainlinks'>[$oldUrl $oldText]</span>";
+ $newLink = "<span class='plainlinks'>[$newUrl $newText]</span>";
- $text = wfMsg( 'pagemovedtext', $oldText, $newText );
-
- $allowHTML = $wgRawHtml;
- $wgRawHtml = false;
- $wgOut->addWikiText( $text );
- $wgRawHtml = $allowHTML;
+ $s = wfMsg( 'movepage-moved', $oldLink, $newLink, $oldText, $newText );
if ( $talkmoved == 1 ) {
- $wgOut->addWikiText( wfMsg( 'talkpagemoved' ) );
+ $s .= "\n\n" . wfMsg( 'talkpagemoved' );
} elseif( 'articleexists' == $talkmoved ) {
- $wgOut->addWikiText( wfMsg( 'talkexists' ) );
+ $s .= "\n\n" . wfMsg( 'talkexists' );
} else {
- $oldTitle = Title::newFromText( $oldText );
- if ( isset( $oldTitle ) && !$oldTitle->isTalkPage() && $talkmoved != 'notalkpage' ) {
- $wgOut->addWikiText( wfMsg( 'talkpagenotmoved', wfMsg( $talkmoved ) ) );
+ if( !$old->isTalkPage() && $talkmoved != 'notalkpage' ) {
+ $s .= "\n\n" . wfMsg( 'talkpagenotmoved', wfMsg( $talkmoved ) );
}
}
+ $wgOut->addWikiText( $s );
}
function showLogFragment( $title, &$out ) {
@@ -311,4 +308,4 @@ class MovePageForm {
}
}
-?>
+
diff --git a/includes/SpecialNewimages.php b/includes/SpecialNewimages.php
index 72b169b1..f81a70f4 100644
--- a/includes/SpecialNewimages.php
+++ b/includes/SpecialNewimages.php
@@ -135,10 +135,9 @@ function wfSpecialNewimages( $par, $specialPage ) {
$ut = $s->img_user_text;
$nt = Title::newFromText( $name, NS_IMAGE );
- $img = new Image( $nt );
$ul = $sk->makeLinkObj( Title::makeTitle( NS_USER, $ut ), $ut );
- $gallery->add( $img, "$ul<br />\n<i>".$wgLang->timeanddate( $s->img_timestamp, true )."</i><br />\n" );
+ $gallery->add( $nt, "$ul<br />\n<i>".$wgLang->timeanddate( $s->img_timestamp, true )."</i><br />\n" );
$timestamp = wfTimestamp( TS_MW, $s->img_timestamp );
if( empty( $firstTimestamp ) ) {
@@ -205,4 +204,4 @@ function wfSpecialNewimages( $par, $specialPage ) {
}
}
-?>
+
diff --git a/includes/SpecialNewpages.php b/includes/SpecialNewpages.php
index 48037a73..abd5e018 100644
--- a/includes/SpecialNewpages.php
+++ b/includes/SpecialNewpages.php
@@ -36,12 +36,19 @@ class NewPagesPage extends QueryPage {
}
}
+ private function makeNamespaceWhere() {
+ return $this->namespace !== 'all'
+ ? ' AND rc_namespace = ' . intval( $this->namespace )
+ : '';
+ }
+
function getSQL() {
global $wgUser, $wgUseRCPatrol;
$usepatrol = ( $wgUseRCPatrol && $wgUser->isAllowed( 'patrol' ) ) ? 1 : 0;
$dbr = wfGetDB( DB_SLAVE );
list( $recentchanges, $page ) = $dbr->tableNamesN( 'recentchanges', 'page' );
+ $nsfilter = $this->makeNamespaceWhere();
$uwhere = $this->makeUserWhere( $dbr );
# FIXME: text will break with compression
@@ -62,7 +69,8 @@ class NewPagesPage extends QueryPage {
page_latest as rev_id
FROM $recentchanges,$page
WHERE rc_cur_id=page_id AND rc_new=1
- AND rc_namespace=" . $this->namespace . " AND page_is_redirect=0
+ {$nsfilter}
+ AND page_is_redirect = 0
{$uwhere}";
}
@@ -130,11 +138,13 @@ class NewPagesPage extends QueryPage {
* @return string
*/
function getPageHeader() {
+ global $wgScript;
$self = SpecialPage::getTitleFor( $this->getName() );
- $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
+ $form = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
+ $form .= Xml::hidden( 'title', $self->getPrefixedDBkey() );
# Namespace selector
$form .= '<table><tr><td align="right">' . Xml::label( wfMsg( 'namespace' ), 'namespace' ) . '</td>';
- $form .= '<td>' . Xml::namespaceSelector( $this->namespace ) . '</td></tr>';
+ $form .= '<td>' . Xml::namespaceSelector( $this->namespace, 'all' ) . '</td></tr>';
# Username filter
$form .= '<tr><td align="right">' . Xml::label( wfMsg( 'newpages-username' ), 'mw-np-username' ) . '</td>';
$form .= '<td>' . Xml::input( 'username', 30, $this->username, array( 'id' => 'mw-np-username' ) ) . '</td></tr>';
@@ -186,7 +196,7 @@ function wfSpecialNewpages($par, $specialPage) {
}
}
} else {
- if( $ns = $wgRequest->getInt( 'namespace', 0 ) )
+ if( $ns = $wgRequest->getText( 'namespace', NS_MAIN ) )
$namespace = $ns;
if( $un = $wgRequest->getText( 'username' ) )
$username = $un;
@@ -199,6 +209,4 @@ function wfSpecialNewpages($par, $specialPage) {
if ( ! $npp->doFeed( $wgRequest->getVal( 'feed' ), $limit ) )
$npp->doQuery( $offset, $limit, $shownavigation );
-}
-
-?>
+} \ No newline at end of file
diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php
index cf882509..89fd15bb 100644
--- a/includes/SpecialPage.php
+++ b/includes/SpecialPage.php
@@ -94,12 +94,14 @@ class SpecialPage
'Uncategorizedpages' => array( 'SpecialPage', 'Uncategorizedpages' ),
'Uncategorizedcategories' => array( 'SpecialPage', 'Uncategorizedcategories' ),
'Uncategorizedimages' => array( 'SpecialPage', 'Uncategorizedimages' ),
+ 'Uncategorizedtemplates' => array( 'SpecialPage', 'Uncategorizedtemplates' ),
'Unusedcategories' => array( 'SpecialPage', 'Unusedcategories' ),
'Unusedimages' => array( 'SpecialPage', 'Unusedimages' ),
'Wantedpages' => array( 'IncludableSpecialPage', 'Wantedpages' ),
'Wantedcategories' => array( 'SpecialPage', 'Wantedcategories' ),
'Mostlinked' => array( 'SpecialPage', 'Mostlinked' ),
'Mostlinkedcategories' => array( 'SpecialPage', 'Mostlinkedcategories' ),
+ 'Mostlinkedtemplates' => array( 'SpecialPage', 'Mostlinkedtemplates' ),
'Mostcategories' => array( 'SpecialPage', 'Mostcategories' ),
'Mostimages' => array( 'SpecialPage', 'Mostimages' ),
'Mostrevisions' => array( 'SpecialPage', 'Mostrevisions' ),
@@ -177,7 +179,7 @@ class SpecialPage
}
if( $wgEmailAuthentication ) {
- self::$mList['Confirmemail'] = array( 'UnlistedSpecialPage', 'Confirmemail' );
+ self::$mList['Confirmemail'] = 'EmailConfirmation';
}
# Add extension special pages
@@ -275,6 +277,30 @@ class SpecialPage
}
/**
+ * Check if a given name exist as a special page or as a special page alias
+ * @param $name string: name of a special page
+ * @return boolean: true if a special page exists with this name
+ */
+ static function exists( $name ) {
+ global $wgContLang;
+ if ( !self::$mListInitialised ) {
+ self::initList();
+ }
+ if( !self::$mAliases ) {
+ self::initAliasList();
+ }
+
+ # Remove special pages inline parameters:
+ $bits = explode( '/', $name );
+ $name = $wgContLang->caseFold($bits[0]);
+
+ return
+ array_key_exists( $name, self::$mList )
+ or array_key_exists( $name, self::$mAliases )
+ ;
+ }
+
+ /**
* Find the object with a given name and return it (or NULL)
* @static
* @param string $name
@@ -798,4 +824,4 @@ class SpecialMycontributions extends UnlistedSpecialPage {
}
}
-?>
+
diff --git a/includes/SpecialPopularpages.php b/includes/SpecialPopularpages.php
index cd2f60e7..af0ed269 100644
--- a/includes/SpecialPopularpages.php
+++ b/includes/SpecialPopularpages.php
@@ -66,4 +66,4 @@ function wfSpecialPopularpages() {
return $ppp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialPreferences.php b/includes/SpecialPreferences.php
index 5ca818cd..a36be289 100644
--- a/includes/SpecialPreferences.php
+++ b/includes/SpecialPreferences.php
@@ -97,6 +97,8 @@ class PreferencesForm {
if ( !preg_match( '/^[a-z\-]*$/', $this->mUserLanguage ) ) {
$this->mUserLanguage = 'nolanguage';
}
+
+ wfRunHooks( "InitPreferencesForm", array( $this, $request ) );
}
function execute() {
@@ -211,19 +213,23 @@ class PreferencesForm {
if ( '' != $this->mNewpass && $wgAuth->allowPasswordChange() ) {
if ( $this->mNewpass != $this->mRetypePass ) {
+ wfRunHooks( "PrefsPasswordAudit", array( $wgUser, $this->mNewpass, 'badretype' ) );
$this->mainPrefsForm( 'error', wfMsg( 'badretype' ) );
return;
}
if (!$wgUser->checkPassword( $this->mOldpass )) {
+ wfRunHooks( "PrefsPasswordAudit", array( $wgUser, $this->mNewpass, 'wrongpassword' ) );
$this->mainPrefsForm( 'error', wfMsg( 'wrongpassword' ) );
return;
}
try {
$wgUser->setPassword( $this->mNewpass );
+ wfRunHooks( "PrefsPasswordAudit", array( $wgUser, $this->mNewpass, 'success' ) );
$this->mNewpass = $this->mOldpass = $this->mRetypePass = '';
} catch( PasswordError $e ) {
+ wfRunHooks( "PrefsPasswordAudit", array( $wgUser, $this->mNewpass, 'error' ) );
$this->mainPrefsForm( 'error', $e->getMessage() );
return;
}
@@ -237,11 +243,18 @@ class PreferencesForm {
}
# Validate the signature and clean it up as needed
- if( $this->mToggles['fancysig'] ) {
+ global $wgMaxSigChars;
+ if( mb_strlen( $this->mNick ) > $wgMaxSigChars ) {
+ global $wgLang;
+ $this->mainPrefsForm( 'error',
+ wfMsg( 'badsiglength', $wgLang->formatNum( $wgMaxSigChars ) ) );
+ return;
+ } elseif( $this->mToggles['fancysig'] ) {
if( Parser::validateSig( $this->mNick ) !== false ) {
$this->mNick = $wgParser->cleanSig( $this->mNick );
} else {
$this->mainPrefsForm( 'error', wfMsg( 'badsig' ) );
+ return;
}
} else {
// When no fancy sig used, make sure ~{3,5} get removed.
@@ -287,9 +300,17 @@ class PreferencesForm {
$wgUser->setOption( $tname, $tvalue );
}
if (!$wgAuth->updateExternalDB($wgUser)) {
- $this->mainPrefsForm( wfMsg( 'externaldberror' ) );
+ $this->mainPrefsForm( 'error', wfMsg( 'externaldberror' ) );
return;
}
+
+ $msg = '';
+ if ( !wfRunHooks( "SavePreferences", array( $this, $wgUser, &$msg ) ) ) {
+ print "(($msg))";
+ $this->mainPrefsForm( 'error', $msg );
+ return;
+ }
+
$wgUser->setCookies();
$wgUser->saveSettings();
@@ -321,6 +342,9 @@ class PreferencesForm {
$wgUser->setCookies();
$wgUser->saveSettings();
}
+ if( $oldadr != $newadr ) {
+ wfRunHooks( "PrefsEmailAudit", array( $wgUser, $oldadr, $newadr ) );
+ }
}
if( $needRedirect && $error === false ) {
@@ -381,6 +405,8 @@ class PreferencesForm {
$this->mSearchNs[$i] = $wgUser->getOption( 'searchNs'.$i );
}
}
+
+ wfRunHooks( "ResetPreferences", array( $this, $wgUser ) );
}
/**
@@ -442,6 +468,38 @@ class PreferencesForm {
}
/**
+ * Helper function for user information panel
+ * @param $td1 label for an item
+ * @param $td2 item or null
+ * @param $td3 optional help or null
+ * @return xhtml block
+ */
+ function tableRow( $td1, $td2 = null, $td3 = null ) {
+ global $wgContLang;
+
+ $align['align'] = $wgContLang->isRtl() ? 'right' : 'left';
+
+ if ( is_null( $td3 ) ) {
+ $td3 = '';
+ } else {
+ $td3 = Xml::tags( 'tr', null,
+ Xml::tags( 'td', array( 'colspan' => '2' ), $td3 )
+ );
+ }
+
+ if ( is_null( $td2 ) ) {
+ $td1 = Xml::tags( 'td', $align + array( 'colspan' => '2' ), $td1 );
+ $td2 = '';
+ } else {
+ $td1 = Xml::tags( 'td', $align, $td1 );
+ $td2 = Xml::tags( 'td', $align, $td2 );
+ }
+
+ return Xml::tags( 'tr', null, $td1 . $td2 ). $td3 . "\n";
+
+ }
+
+ /**
* @access private
*/
function mainPrefsForm( $status , $message = '' ) {
@@ -457,6 +515,8 @@ class PreferencesForm {
$wgOut->setArticleRelated( false );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
+ $wgOut->disallowUserJs(); # Prevent hijacked user scripts from sniffing passwords etc.
+
if ( $this->mSuccess || 'success' == $status ) {
$wgOut->addWikitext( '<div class="successbox"><strong>'. wfMsg( 'savedprefs' ) . '</strong></div>' );
} else if ( 'error' == $status ) {
@@ -484,12 +544,7 @@ class PreferencesForm {
$this->mUsedToggles[ 'ccmeonemails' ] = true;
$this->mUsedToggles[ 'uselivepreview' ] = true;
- # Enotif
- # <FIXME>
- $this->mUserEmail = htmlspecialchars( $this->mUserEmail );
- $this->mRealName = htmlspecialchars( $this->mRealName );
- $rawNick = $this->mNick;
- $this->mNick = htmlspecialchars( $this->mNick );
+
if ( !$this->mEmailFlag ) { $emfc = 'checked="checked"'; }
else { $emfc = ''; }
@@ -503,7 +558,7 @@ class PreferencesForm {
$skin = $wgUser->getSkin();
$emailauthenticated = wfMsg('emailnotauthenticated').'<br />' .
$skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Confirmemail' ),
- wfMsg( 'emailconfirmlink' ) );
+ wfMsg( 'emailconfirmlink' ) ) . '<br />';
}
} else {
$emailauthenticated = '';
@@ -511,7 +566,7 @@ class PreferencesForm {
}
if ($this->mUserEmail == '') {
- $emailauthenticated = wfMsg( 'noemailprefs' );
+ $emailauthenticated = wfMsg( 'noemailprefs' ) . '<br />';
}
$ps = $this->namespacesCheckboxes();
@@ -527,93 +582,85 @@ class PreferencesForm {
$wgOut->addHTML( "<div id='preferences'>" );
# User data
- #
- $wgOut->addHTML( "<fieldset>\n<legend>" . wfMsg('prefs-personal') . "</legend>\n<table>\n");
+ $wgOut->addHTML(
+ Xml::openElement( 'fieldset ' ) .
+ Xml::element( 'legend', null, wfMsg('prefs-personal') ) .
+ Xml::openElement( 'table' ) .
+ $this->tableRow( Xml::element( 'h2', null, wfMsg( 'prefs-personal' ) ) )
+ );
$userInformationHtml =
- $this->addRow(
- wfMsg( 'username'),
- $wgUser->getName()
- ) .
- $this->addRow(
- wfMsg( 'uid' ),
- $wgUser->getID()
+ $this->tableRow( wfMsgHtml( 'username' ), htmlspecialchars( $wgUser->getName() ) ) .
+ $this->tableRow( wfMsgHtml( 'uid' ), htmlspecialchars( $wgUser->getID() ) ) .
+ $this->tableRow(
+ wfMsgHtml( 'prefs-edits' ),
+ $wgLang->formatNum( User::edits( $wgUser->getId() ) )
);
-
+
if( wfRunHooks( 'PreferencesUserInformationPanel', array( $this, &$userInformationHtml ) ) ) {
$wgOut->addHtml( $userInformationHtml );
}
-
- if ($wgAllowRealName) {
+ if ( $wgAllowRealName ) {
$wgOut->addHTML(
- $this->addRow(
- '<label for="wpRealName">' . wfMsg('yourrealname') . '</label>',
- "<input type='text' name='wpRealName' id='wpRealName' value=\"{$this->mRealName}\" size='25' />"
+ $this->tableRow(
+ Xml::label( wfMsg('yourrealname'), 'wpRealName' ),
+ Xml::input( 'wpRealName', 25, $this->mRealName, array( 'id' => 'wpRealName' ) ),
+ Xml::tags('div', array( 'class' => 'prefsectiontip' ),
+ wfMsgExt( 'prefs-help-realname', 'parseinline' )
+ )
)
);
}
- if ($wgEnableEmail) {
+ if ( $wgEnableEmail ) {
$wgOut->addHTML(
- $this->addRow(
- '<label for="wpUserEmail">' . wfMsg( 'youremail' ) . '</label>',
- "<input type='text' name='wpUserEmail' id='wpUserEmail' value=\"{$this->mUserEmail}\" size='25' />"
+ $this->tableRow(
+ Xml::label( wfMsg('youremail'), 'wpUserEmail' ),
+ Xml::input( 'wpUserEmail', 25, $this->mUserEmail, array( 'id' => 'wpUserEmail' ) ),
+ Xml::tags('div', array( 'class' => 'prefsectiontip' ),
+ wfMsgExt( 'prefs-help-email', 'parseinline' )
+ )
)
);
}
- global $wgParser;
- if( !empty( $this->mToggles['fancysig'] ) &&
- false === $wgParser->validateSig( $rawNick ) ) {
- $invalidSig = $this->addRow(
+ global $wgParser, $wgMaxSigChars;
+ if( mb_strlen( $this->mNick ) > $wgMaxSigChars ) {
+ $invalidSig = $this->tableRow(
+ '&nbsp;',
+ Xml::element( 'span', array( 'class' => 'error' ),
+ wfMsg( 'badsiglength', $wgLang->formatNum( $wgMaxSigChars ) ) )
+ );
+ } elseif( !empty( $this->mToggles['fancysig'] ) &&
+ false === $wgParser->validateSig( $this->mNick ) ) {
+ $invalidSig = $this->tableRow(
'&nbsp;',
- '<span class="error">' . wfMsgHtml( 'badsig' ) . '<span>'
+ Xml::element( 'span', array( 'class' => 'error' ), wfMsg( 'badsig' ) )
);
} else {
$invalidSig = '';
}
$wgOut->addHTML(
- $this->addRow(
- '<label for="wpNick">' . wfMsg( 'yournick' ) . '</label>',
- "<input type='text' name='wpNick' id='wpNick' value=\"{$this->mNick}\" size='25' />"
+ $this->tableRow(
+ Xml::label( wfMsg( 'yournick' ), 'wpNick' ),
+ Xml::input( 'wpNick', 25, $this->mNick,
+ array(
+ 'id' => 'wpNick',
+ // Note: $wgMaxSigChars is enforced in Unicode characters,
+ // both on the backend and now in the browser.
+ // Badly-behaved requests may still try to submit
+ // an overlong string, however.
+ 'maxlength' => $wgMaxSigChars ) )
) .
$invalidSig .
- # FIXME: The <input> part should be where the &nbsp; is, getToggle() needs
- # to be changed to out return its output in two parts. -ævar
- $this->addRow(
- '&nbsp;',
- $this->getToggle( 'fancysig' )
- )
+ $this->tableRow( '&nbsp;', $this->getToggle( 'fancysig' ) )
);
- /**
- * Make sure the site language is in the list; a custom language code
- * might not have a defined name...
- */
- $languages = Language::getLanguageNames( true );
- if( !array_key_exists( $wgContLanguageCode, $languages ) ) {
- $languages[$wgContLanguageCode] = $wgContLanguageCode;
- }
- ksort( $languages );
-
- /**
- * If a bogus value is set, default to the content language.
- * Otherwise, no default is selected and the user ends up
- * with an Afrikaans interface since it's first in the list.
- */
- $selectedLang = isset( $languages[$this->mUserLanguage] ) ? $this->mUserLanguage : $wgContLanguageCode;
- $options = "\n";
- foreach( $languages as $code => $name ) {
- $selected = ($code == $selectedLang);
- $options .= Xml::option( "$code - $name", $code, $selected ) . "\n";
- }
+ list( $lsLabel, $lsSelect) = Xml::languageSelector( $this->mUserLanguage );
$wgOut->addHTML(
- $this->addRow(
- '<label for="wpUserLanguage">' . wfMsg('yourlanguage') . '</label>',
- "<select name='wpUserLanguage' id='wpUserLanguage'>$options</select>"
- )
+ $this->tableRow( $lsLabel, $lsSelect )
);
/* see if there are multiple language variants to choose from*/
@@ -621,6 +668,7 @@ class PreferencesForm {
$variants = $wgContLang->getVariants();
$variantArray = array();
+ $languages = Language::getLanguageNames( true );
foreach($variants as $v) {
$v = str_replace( '_', '-', strtolower($v));
if( array_key_exists( $v, $languages ) ) {
@@ -637,69 +685,74 @@ class PreferencesForm {
if(count($variantArray) > 1) {
$wgOut->addHtml(
- $this->addRow( wfMsg( 'yourvariant' ),
- "<select name='wpUserVariant'>$options</select>" )
+ $this->tableRow(
+ Xml::label( wfMsg( 'yourvariant' ), 'wpUserVariant' ),
+ Xml::tags( 'select',
+ array( 'name' => 'wpUserVariant', 'id' => 'wpUserVariant' ),
+ $options
+ )
+ )
);
}
}
- $wgOut->addHTML('</table>');
# Password
- if( $wgAuth->allowPasswordChange() ) {
- $this->mOldpass = htmlspecialchars( $this->mOldpass );
- $this->mNewpass = htmlspecialchars( $this->mNewpass );
- $this->mRetypePass = htmlspecialchars( $this->mRetypePass );
-
- $wgOut->addHTML( '<fieldset><legend>' . wfMsg( 'changepassword' ) . '</legend><table>');
+ if( $wgAuth->allowPasswordChange() ) {
$wgOut->addHTML(
- $this->addRow(
- '<label for="wpOldpass">' . wfMsg( 'oldpassword' ) . '</label>',
- "<input type='password' name='wpOldpass' id='wpOldpass' value=\"{$this->mOldpass}\" size='20' />"
+ $this->tableRow( Xml::element( 'h2', null, wfMsg( 'changepassword' ) ) ) .
+ $this->tableRow(
+ Xml::label( wfMsg( 'oldpassword' ), 'wpOldpass' ),
+ Xml::password( 'wpOldpass', 25, $this->mOldpass, array( 'id' => 'wpOldpass' ) )
) .
- $this->addRow(
- '<label for="wpNewpass">' . wfMsg( 'newpassword' ) . '</label>',
- "<input type='password' name='wpNewpass' id='wpNewpass' value=\"{$this->mNewpass}\" size='20' />"
+ $this->tableRow(
+ Xml::label( wfMsg( 'newpassword' ), 'wpNewpass' ),
+ Xml::password( 'wpNewpass', 25, $this->mNewpass, array( 'id' => 'wpNewpass' ) )
) .
- $this->addRow(
- '<label for="wpRetypePass">' . wfMsg( 'retypenew' ) . '</label>',
- "<input type='password' name='wpRetypePass' id='wpRetypePass' value=\"{$this->mRetypePass}\" size='20' />"
+ $this->tableRow(
+ Xml::label( wfMsg( 'retypenew' ), 'wpRetypePass' ),
+ Xml::password( 'wpRetypePass', 25, $this->mRetypePass, array( 'id' => 'wpRetypePass' ) )
) .
- "</table>\n" .
- $this->getToggle( "rememberpassword" ) . "</fieldset>\n\n" );
+ Xml::tags( 'tr', null,
+ Xml::tags( 'td', array( 'colspan' => '2' ),
+ $this->getToggle( "rememberpassword" )
+ )
+ )
+ );
}
# <FIXME>
# Enotif
- if ($wgEnableEmail) {
- $wgOut->addHTML( '<fieldset><legend>' . wfMsg( 'email' ) . '</legend>' );
+ if ( $wgEnableEmail ) {
+
+ $moreEmail = '';
+ if ($wgEnableUserEmail) {
+ $emf = wfMsg( 'allowemail' );
+ $disabled = $disableEmailPrefs ? ' disabled="disabled"' : '';
+ $moreEmail =
+ "<input type='checkbox' $emfc $disabled value='1' name='wpEmailFlag' id='wpEmailFlag' /> <label for='wpEmailFlag'>$emf</label>";
+ }
+
+
$wgOut->addHTML(
+ $this->tableRow( Xml::element( 'h2', null, wfMsg( 'email' ) ) ) .
+ $this->tableRow(
$emailauthenticated.
$enotifrevealaddr.
$enotifwatchlistpages.
$enotifusertalkpages.
- $enotifminoredits );
- if ($wgEnableUserEmail) {
- $emf = wfMsg( 'allowemail' );
- $disabled = $disableEmailPrefs ? ' disabled="disabled"' : '';
- $wgOut->addHTML(
- "<div><input type='checkbox' $emfc $disabled value='1' name='wpEmailFlag' id='wpEmailFlag' /> <label for='wpEmailFlag'>$emf</label></div>" );
- }
- $wgOut->addHtml( $this->getToggle( 'ccmeonemails' ) );
-
- $wgOut->addHTML( '</fieldset>' );
+ $enotifminoredits.
+ $moreEmail.
+ $this->getToggle( 'ccmeonemails' )
+ )
+ );
}
# </FIXME>
- # Show little "help" tips for the real name and email address options
- if( $wgAllowRealName || $wgEnableEmail ) {
- if( $wgAllowRealName )
- $tips[] = wfMsg( 'prefs-help-realname' );
- if( $wgEnableEmail )
- $tips[] = wfMsg( 'prefs-help-email' );
- $wgOut->addHtml( '<div class="prefsectiontip">' . implode( '<br />', $tips ) . '</div>' );
- }
+ $wgOut->addHTML(
+ Xml::closeElement( 'table' ) .
+ Xml::closeElement( 'fieldset' )
+ );
- $wgOut->addHTML( '</fieldset>' );
# Quickbar
#
@@ -753,8 +806,12 @@ class PreferencesForm {
if( $wgUseTeX ) {
$wgOut->addHTML( "<fieldset>\n<legend>" . wfMsg('math') . '</legend>' );
foreach ( $mathopts as $k => $v ) {
- $checked = $k == $this->mMath ? ' checked="checked"' : '';
- $wgOut->addHTML( "<div><label><input type='radio' name='wpMath' value=\"$k\"$checked /> ".wfMsg($v)."</label></div>\n" );
+ $checked = ($k == $this->mMath);
+ $wgOut->addHTML(
+ Xml::openElement( 'div' ) .
+ Xml::radioLabel( wfMsg( $v ), 'wpMath', $k, "mw-sp-math-$k", $checked ) .
+ Xml::closeElement( 'div' ) . "\n"
+ );
}
$wgOut->addHTML( "</fieldset>\n\n" );
}
@@ -928,8 +985,8 @@ class PreferencesForm {
# Misc
#
$wgOut->addHTML('<fieldset><legend>' . wfMsg('prefs-misc') . '</legend>');
- $wgOut->addHTML( wfInputLabel( wfMsg( 'stubthreshold' ),
- 'wpStubs', 'wpStubs', 6, $this->mStubs ) );
+ $wgOut->addHtml( '<label for="wpStubs">' . wfMsg( 'stub-threshold' ) . '</label>&nbsp;' );
+ $wgOut->addHtml( Xml::input( 'wpStubs', 6, $this->mStubs, array( 'id' => 'wpStubs' ) ) );
$msgUnderline = htmlspecialchars( wfMsg ( 'tog-underline' ) );
$msgUnderlinenever = htmlspecialchars( wfMsg ( 'underline-never' ) );
$msgUnderlinealways = htmlspecialchars( wfMsg ( 'underline-always' ) );
@@ -953,7 +1010,9 @@ class PreferencesForm {
}
$wgOut->addHTML( '</fieldset>' );
- $token = $wgUser->editToken();
+ wfRunHooks( "RenderPreferencesForm", array( $this, $wgOut ) );
+
+ $token = htmlspecialchars( $wgUser->editToken() );
$skin = $wgUser->getSkin();
$wgOut->addHTML( "
<div id='prefsubmit'>
@@ -964,11 +1023,13 @@ class PreferencesForm {
</div>
- <input type='hidden' name='wpEditToken' value='{$token}' />
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
</div></form>\n" );
- $wgOut->addWikiText( '<div class="prefcache">' . wfMsg('clearyourcache') . '</div>' );
+ $wgOut->addHtml( Xml::tags( 'div', array( 'class' => "prefcache" ),
+ wfMsgExt( 'clearyourcache', 'parseinline' ) )
+ );
}
}
-?>
+
diff --git a/includes/SpecialPrefixindex.php b/includes/SpecialPrefixindex.php
index b7c51d49..6bb26d67 100644
--- a/includes/SpecialPrefixindex.php
+++ b/includes/SpecialPrefixindex.php
@@ -15,20 +15,14 @@ function wfSpecialPrefixIndex( $par=NULL, $specialPage ) {
$from = $wgRequest->getVal( 'from' );
$prefix = $wgRequest->getVal( 'prefix' );
$namespace = $wgRequest->getInt( 'namespace' );
-
$namespaces = $wgContLang->getNamespaces();
$indexPage = new SpecialPrefixIndex();
- if( !in_array($namespace, array_keys($namespaces)) )
- $namespace = 0;
-
- $wgOut->setPagetitle( $namespace > 0 ?
- wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
- wfMsg( 'allarticles' )
- );
-
-
+ $wgOut->setPagetitle( ( $namespace > 0 && in_array( $namespace, array_keys( $namespaces ) ) )
+ ? wfMsg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) )
+ : wfMsg( 'allarticles' )
+ );
if ( isset($par) ) {
$indexPage->showChunk( $namespace, $par, $specialPage->including(), $from );
@@ -67,9 +61,15 @@ function showChunk( $namespace = NS_MAIN, $prefix, $including = false, $from = n
$fromList = $this->getNamespaceKeyAndText($namespace, $from);
$prefixList = $this->getNamespaceKeyAndText($namespace, $prefix);
+ $namespaces = $wgContLang->getNamespaces();
+ $align = $wgContLang->isRtl() ? 'left' : 'right';
if ( !$prefixList || !$fromList ) {
$out = wfMsgWikiHtml( 'allpagesbadtitle' );
+ } elseif ( !in_array( $namespace, array_keys( $namespaces ) ) ) {
+ // Show errormessage and reset to NS_MAIN
+ $out = wfMsgExt( 'allpages-bad-ns', array( 'parseinline' ), $namespace );
+ $namespace = NS_MAIN;
} else {
list( $namespace, $prefixKey, $prefix ) = $prefixList;
list( /* $fromNs */, $fromKey, $from ) = $fromList;
@@ -127,8 +127,8 @@ function showChunk( $namespace = NS_MAIN, $prefix, $including = false, $from = n
} else {
$nsForm = $this->namespaceForm ( $namespace, $prefix );
$out2 = '<table style="background: inherit;" width="100%" cellpadding="0" cellspacing="0" border="0">';
- $out2 .= '<tr valign="top"><td align="left">' . $nsForm;
- $out2 .= '</td><td align="right" style="font-size: smaller; margin-bottom: 1em;">' .
+ $out2 .= '<tr valign="top"><td>' . $nsForm;
+ $out2 .= '</td><td align="' . $align . '" style="font-size: smaller; margin-bottom: 1em;">' .
$sk->makeKnownLink( $wgContLang->specialPage( $this->name ),
wfMsg ( 'allpages' ) );
if ( isset($dbr) && $dbr && ($n == $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
@@ -146,4 +146,4 @@ function showChunk( $namespace = NS_MAIN, $prefix, $including = false, $from = n
}
}
-?>
+
diff --git a/includes/SpecialProtectedpages.php b/includes/SpecialProtectedpages.php
index 91b138ff..122ca8fc 100644
--- a/includes/SpecialProtectedpages.php
+++ b/includes/SpecialProtectedpages.php
@@ -9,6 +9,10 @@
* @addtogroup SpecialPage
*/
class ProtectedPagesForm {
+
+ protected $IdLevel = 'level';
+ protected $IdType = 'type';
+
function showList( $msg = '' ) {
global $wgOut, $wgRequest;
@@ -22,14 +26,15 @@ class ProtectedPagesForm {
Title::purgeExpiredRestrictions();
}
- $type = $wgRequest->getVal( 'type' );
- $level = $wgRequest->getVal( 'level' );
- $minsize = $wgRequest->getIntOrNull( 'minsize' );
+ $type = $wgRequest->getVal( $this->IdType );
+ $level = $wgRequest->getVal( $this->IdLevel );
+ $sizetype = $wgRequest->getVal( 'sizetype' );
+ $size = $wgRequest->getIntOrNull( 'size' );
$NS = $wgRequest->getIntOrNull( 'namespace' );
- $pager = new ProtectedPagesPager( $this, array(), $type, $level, $NS, $minsize );
+ $pager = new ProtectedPagesPager( $this, array(), $type, $level, $NS, $sizetype, $size );
- $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $minsize ) );
+ $wgOut->addHTML( $this->showOptions( $NS, $type, $level, $sizetype, $size ) );
if ( $pager->getNumRows() ) {
$s = $pager->getNavigationBar();
@@ -38,7 +43,7 @@ class ProtectedPagesForm {
"</ul>";
$s .= $pager->getNavigationBar();
} else {
- $s = '<p>' . wfMsgHTML( 'protectedpagesempty' ) . '</p>';
+ $s = '<p>' . wfMsgHtml( 'protectedpagesempty' ) . '</p>';
}
$wgOut->addHTML( $s );
}
@@ -65,6 +70,10 @@ class ProtectedPagesForm {
$description_items[] = $protType;
+ if ( $row->pr_cascade ) {
+ $description_items[] = wfMsg( 'protect-summary-cascade' );
+ }
+
$expiry_description = ''; $stxt = '';
if ( $row->pr_expiry != 'infinity' && strlen($row->pr_expiry) ) {
@@ -93,7 +102,7 @@ class ProtectedPagesForm {
* @param $minsize int
* @private
*/
- function showOptions( $namespace, $type='edit', $level, $minsize ) {
+ function showOptions( $namespace, $type='edit', $level, $sizetype, $size ) {
global $wgScript;
$action = htmlspecialchars( $wgScript );
$title = SpecialPage::getTitleFor( 'ProtectedPages' );
@@ -101,26 +110,40 @@ class ProtectedPagesForm {
return "<form action=\"$action\" method=\"get\">\n" .
'<fieldset>' .
Xml::element( 'legend', array(), wfMsg( 'protectedpages' ) ) .
- Xml::hidden( 'title', $special ) . "\n" .
- $this->getNamespaceMenu( $namespace ) . "\n" .
- $this->getTypeMenu( $type ) . "\n" .
+ Xml::hidden( 'title', $special ) . "&nbsp;\n" .
+ $this->getNamespaceMenu( $namespace ) . "&nbsp;\n" .
+ $this->getTypeMenu( $type ) . "&nbsp;\n" .
$this->getLevelMenu( $level ) . "<br/>\n" .
- $this->getSizeLimit( $minsize ) . "\n" .
- Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
+ $this->getSizeLimit( $sizetype, $size ) . "\n" .
+ "&nbsp;" . Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
"</fieldset></form>";
}
- function getNamespaceMenu( $namespace=NULL ) {
- return "<label for='namespace'>" . wfMsgHtml('namespace') . "</label>" . HTMLnamespaceselector($namespace, '');
+ /**
+ * Prepare the namespace filter drop-down; standard namespace
+ * selector, sans the MediaWiki namespace
+ *
+ * @param mixed $namespace Pre-select namespace
+ * @return string
+ */
+ function getNamespaceMenu( $namespace = null ) {
+ return Xml::label( wfMsg( 'namespace' ), 'namespace' )
+ . '&nbsp;'
+ . Xml::namespaceSelector( $namespace, '' );
}
-
+
/**
* @return string Formatted HTML
* @private
*/
- function getSizeLimit( $minsize=0 ) {
- $out = Xml::input('minsize', 9, $minsize, array( 'id' => 'minsize' ) );
- return "<label for='minsize'>" . wfMsgHtml('minimum-size') . "</label>: " . $out;
+ function getSizeLimit( $sizetype, $size ) {
+ $out = Xml::radio( 'sizetype', 'min', ($sizetype=='min'), array('id' => 'wpmin') );
+ $out .= Xml::label( wfMsg("minimum-size"), 'wpmin' );
+ $out .= "&nbsp;".Xml::radio( 'sizetype', 'max', ($sizetype=='max'), array('id' => 'wpmax') );
+ $out .= Xml::label( wfMsg("maximum-size"), 'wpmax' );
+ $out .= "&nbsp;".Xml::input('size', 9, $size, array( 'id' => 'wpsize' ) );
+ $out .= ' '.wfMsgHtml('pagesize');
+ return $out;
}
/**
@@ -128,28 +151,28 @@ class ProtectedPagesForm {
* @private
*/
function getTypeMenu( $pr_type ) {
- global $wgRestrictionTypes, $wgUser;
+ global $wgRestrictionTypes;
- $out = "<select name='type'>\n";
$m = array(); // Temporary array
+ $options = array();
// First pass to load the log names
foreach( $wgRestrictionTypes as $type ) {
- $text = wfMsgHtml("restriction-$type");
+ $text = wfMsg("restriction-$type");
$m[$text] = $type;
}
- // Second pass to sort by name
- ksort($m);
-
// Third pass generates sorted XHTML content
foreach( $m as $text => $type ) {
$selected = ($type == $pr_type );
- $out .= Xml::option( $text, $type, $selected ) . "\n";
+ $options[] = Xml::option( $text, $type, $selected ) . "\n";
}
- $out .= '</select>';
- return "<label for='type'>" . wfMsgHtml('restriction-type') . "</label>: " . $out;
+ return
+ Xml::label( wfMsg('restriction-type') , $this->IdType ) . '&nbsp;' .
+ Xml::tags( 'select',
+ array( 'id' => $this->IdType, 'name' => $this->IdType ),
+ implode( "\n", $options ) );
}
/**
@@ -157,30 +180,30 @@ class ProtectedPagesForm {
* @private
*/
function getLevelMenu( $pr_level ) {
- global $wgRestrictionLevels, $wgUser;
-
- $out = "<select name='level'>\n";
- $m = array( wfMsgHtml('restriction-level-all') => 0 ); // Temporary array
+ global $wgRestrictionLevels;
+
+ $m = array( wfMsg('restriction-level-all') => 0 ); // Temporary array
+ $options = array();
// First pass to load the log names
foreach( $wgRestrictionLevels as $type ) {
if ( $type !='' && $type !='*') {
- $text = wfMsgHtml("restriction-level-$type");
+ $text = wfMsg("restriction-level-$type");
$m[$text] = $type;
}
}
- // Second pass to sort by name
- ksort($m);
-
// Third pass generates sorted XHTML content
foreach( $m as $text => $type ) {
$selected = ($type == $pr_level );
- $out .= Xml::option( $text, $type, $selected ) . "\n";
+ $options[] = Xml::option( $text, $type, $selected );
}
- $out .= '</select>';
- return "<label for='level'>" . wfMsgHtml('restriction-level') . "</label>: " . $out;
+ return
+ Xml::label( wfMsg('restriction-level') , $this->IdLevel ) . '&nbsp;' .
+ Xml::tags( 'select',
+ array( 'id' => $this->IdLevel, 'name' => $this->IdLevel ),
+ implode( "\n", $options ) );
}
}
@@ -188,16 +211,17 @@ class ProtectedPagesForm {
* @todo document
* @addtogroup Pager
*/
-class ProtectedPagesPager extends ReverseChronologicalPager {
+class ProtectedPagesPager extends AlphabeticPager {
public $mForm, $mConds;
- function __construct( $form, $conds = array(), $type, $level, $namespace, $minsize ) {
+ function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0 ) {
$this->mForm = $form;
$this->mConds = $conds;
$this->type = ( $type ) ? $type : 'edit';
$this->level = $level;
$this->namespace = $namespace;
- $this->minsize = intval($minsize);
+ $this->sizetype = $sizetype;
+ $this->size = intval($size);
parent::__construct();
}
@@ -208,8 +232,7 @@ class ProtectedPagesPager extends ReverseChronologicalPager {
$lb = new LinkBatch;
while ( $row = $this->mResult->fetchObject() ) {
- $name = str_replace( ' ', '_', $row->page_title );
- $lb->add( $row->page_namespace, $name );
+ $lb->add( $row->page_namespace, $row->page_title );
}
$lb->execute();
@@ -218,7 +241,6 @@ class ProtectedPagesPager extends ReverseChronologicalPager {
}
function formatRow( $row ) {
- $block = new Block;
return $this->mForm->formatRow( $row );
}
@@ -226,17 +248,22 @@ class ProtectedPagesPager extends ReverseChronologicalPager {
$conds = $this->mConds;
$conds[] = 'pr_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
$conds[] = 'page_id=pr_page';
- $conds[] = 'page_len>=' . $this->minsize;
$conds[] = 'pr_type=' . $this->mDb->addQuotes( $this->type );
- if ( $this->level )
+
+ if( $this->sizetype=='min' ) {
+ $conds[] = 'page_len>=' . $this->size;
+ } else if( $this->sizetype=='max' ) {
+ $conds[] = 'page_len<=' . $this->size;
+ }
+
+ if( $this->level )
$conds[] = 'pr_level=' . $this->mDb->addQuotes( $this->level );
- if ( !is_null($this->namespace) )
+ if( !is_null($this->namespace) )
$conds[] = 'page_namespace=' . $this->mDb->addQuotes( $this->namespace );
return array(
'tables' => array( 'page_restrictions', 'page' ),
- 'fields' => 'max(pr_id) AS pr_id,page_namespace,page_title,page_len,pr_type,pr_level,pr_expiry',
- 'conds' => $conds,
- 'options' => array( 'GROUP BY' => 'page_namespace,page_title,pr_level,pr_expiry,page_len,pr_type' ),
+ 'fields' => 'pr_id,page_namespace,page_title,page_len,pr_type,pr_level,pr_expiry,pr_cascade',
+ 'conds' => $conds
);
}
@@ -250,11 +277,10 @@ class ProtectedPagesPager extends ReverseChronologicalPager {
*/
function wfSpecialProtectedpages() {
- list( $limit, $offset ) = wfCheckLimits();
-
$ppForm = new ProtectedPagesForm();
$ppForm->showList();
}
-?>
+
+
diff --git a/includes/SpecialRandompage.php b/includes/SpecialRandompage.php
index e6c4abe8..42734274 100644
--- a/includes/SpecialRandompage.php
+++ b/includes/SpecialRandompage.php
@@ -105,4 +105,4 @@ class RandomPage {
}
}
-?>
+
diff --git a/includes/SpecialRandomredirect.php b/includes/SpecialRandomredirect.php
index 75a6b81d..b7aa3e49 100644
--- a/includes/SpecialRandomredirect.php
+++ b/includes/SpecialRandomredirect.php
@@ -30,4 +30,4 @@ function wfSpecialRandomredirect( $par = null ) {
$wgOut->redirect( $title->getFullUrl( 'redirect=no' ) );
}
-?>
+
diff --git a/includes/SpecialRecentchanges.php b/includes/SpecialRecentchanges.php
index 84444e62..7565481b 100644
--- a/includes/SpecialRecentchanges.php
+++ b/includes/SpecialRecentchanges.php
@@ -269,8 +269,6 @@ function wfSpecialRecentchanges( $par, $specialPage ) {
}
function rcFilterByCategories ( &$rows , $categories , $any ) {
- require_once ( 'Categoryfinder.php' ) ;
-
# Filter categories
$cats = array () ;
foreach ( $categories AS $cat ) {
@@ -685,12 +683,12 @@ function rcFormatDiffRow( $title, $oldid, $newid, $timestamp, $comment ) {
*/
function rcApplyDiffStyle( $text ) {
$styles = array(
- 'diff' => 'background-color: white;',
- 'diff-otitle' => 'background-color: white;',
- 'diff-ntitle' => 'background-color: white;',
- 'diff-addedline' => 'background: #cfc; font-size: smaller;',
- 'diff-deletedline' => 'background: #ffa; font-size: smaller;',
- 'diff-context' => 'background: #eee; font-size: smaller;',
+ 'diff' => 'background-color: white; color:black;',
+ 'diff-otitle' => 'background-color: white; color:black;',
+ 'diff-ntitle' => 'background-color: white; color:black;',
+ 'diff-addedline' => 'background: #cfc; color:black; font-size: smaller;',
+ 'diff-deletedline' => 'background: #ffa; color:black; font-size: smaller;',
+ 'diff-context' => 'background: #eee; color:black; font-size: smaller;',
'diffchange' => 'color: red; font-weight: bold; text-decoration: none;',
);
@@ -702,4 +700,4 @@ function rcApplyDiffStyle( $text ) {
return $text;
}
-?>
+
diff --git a/includes/SpecialRecentchangeslinked.php b/includes/SpecialRecentchangeslinked.php
index 14508d3a..2a8ac32d 100644
--- a/includes/SpecialRecentchangeslinked.php
+++ b/includes/SpecialRecentchangeslinked.php
@@ -35,6 +35,7 @@ function wfSpecialRecentchangeslinked( $par = NULL ) {
}
$id = $nt->getArticleId();
+ $wgOut->setPageTitle( wfMsg( 'recentchangeslinked-title', $nt->getPrefixedText() ) );
$wgOut->setSubtitle( htmlspecialchars( wfMsg( 'rclsub', $nt->getPrefixedText() ) ) );
if ( ! $days ) {
@@ -171,4 +172,4 @@ $GROUPBY
$wgOut->addHTML( $s );
}
-?>
+
diff --git a/includes/SpecialResetpass.php b/includes/SpecialResetpass.php
index dc1e53c4..281a78b6 100644
--- a/includes/SpecialResetpass.php
+++ b/includes/SpecialResetpass.php
@@ -74,6 +74,8 @@ class PasswordResetForm extends SpecialPage {
function showForm() {
global $wgOut, $wgUser, $wgRequest;
+
+ $wgOut->disallowUserJs();
$self = SpecialPage::getTitleFor( 'Resetpass' );
$form =
@@ -160,4 +162,4 @@ class PasswordResetForm extends SpecialPage {
}
}
-?>
+
diff --git a/includes/SpecialRevisiondelete.php b/includes/SpecialRevisiondelete.php
index 5c70d5ae..34e9dfbc 100644
--- a/includes/SpecialRevisiondelete.php
+++ b/includes/SpecialRevisiondelete.php
@@ -272,4 +272,4 @@ class RevisionDeleter {
}
}
-?>
+
diff --git a/includes/SpecialSearch.php b/includes/SpecialSearch.php
index fdaa8541..3fc8bab4 100644
--- a/includes/SpecialSearch.php
+++ b/includes/SpecialSearch.php
@@ -173,7 +173,8 @@ class SpecialSearch {
SpecialPage::getTitleFor( 'Search' ),
wfArrayToCGI(
$this->powerSearchOptions(),
- array( 'search' => $term ) ) );
+ array( 'search' => $term ) ),
+ ($num < $this->limit) );
$wgOut->addHTML( "<br />{$prevnext}\n" );
}
@@ -184,6 +185,7 @@ class SpecialSearch {
} else {
$wgOut->addWikiText( '==' . wfMsg( 'notitlematches' ) . "==\n" );
}
+ $titleMatches->free();
}
if( $textMatches ) {
@@ -194,6 +196,7 @@ class SpecialSearch {
# Don't show the 'no text matches' if we received title matches
$wgOut->addWikiText( '==' . wfMsg( 'notextmatches' ) . "==\n" );
}
+ $textMatches->free();
}
if ( $num == 0 ) {
@@ -320,6 +323,14 @@ class SpecialSearch {
$contextchars = $wgUser->getOption( 'contextchars', 50 );
$link = $sk->makeKnownLinkObj( $t );
+
+ //If page content is not readable, just return the title.
+ //This is not quite safe, but better than showing excerpts from non-readable pages
+ //Note that hiding the entry entirely would screw up paging.
+ if (!$t->userCanRead()) {
+ return "<li>{$link}</li>\n";
+ }
+
$revision = Revision::newFromTitle( $t );
$text = $revision->getText();
$size = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
@@ -403,4 +414,4 @@ class SpecialSearch {
}
}
-?>
+
diff --git a/includes/SpecialShortpages.php b/includes/SpecialShortpages.php
index 72b093e0..973656dd 100644
--- a/includes/SpecialShortpages.php
+++ b/includes/SpecialShortpages.php
@@ -89,4 +89,4 @@ function wfSpecialShortpages() {
return $spp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialSpecialpages.php b/includes/SpecialSpecialpages.php
index bb202358..a893966c 100644
--- a/includes/SpecialSpecialpages.php
+++ b/includes/SpecialSpecialpages.php
@@ -8,7 +8,9 @@
*
*/
function wfSpecialSpecialpages() {
- global $wgOut, $wgUser;
+ global $wgOut, $wgUser, $wgMessageCache;
+
+ $wgMessageCache->loadAllMessages();
$wgOut->setRobotpolicy( 'index,nofollow' );
$sk = $wgUser->getSkin();
@@ -56,4 +58,4 @@ function wfSpecialSpecialpages_gen($pages,$heading,$sk) {
$wgOut->addHTML( "</ul>\n" );
}
-?>
+
diff --git a/includes/SpecialStatistics.php b/includes/SpecialStatistics.php
index 1c9e0ab6..a29811da 100644
--- a/includes/SpecialStatistics.php
+++ b/includes/SpecialStatistics.php
@@ -1,18 +1,19 @@
<?php
+
/**
-*
-* @addtogroup SpecialPage
-*/
+ * Special page lists various statistics, including the contents of
+ * `site_stats`, plus page view details if enabled
+ *
+ * @addtogroup SpecialPage
+ */
/**
-* constructor
-*/
-function wfSpecialStatistics() {
+ * Show the special page
+ *
+ * @param mixed $par (not used)
+ */
+function wfSpecialStatistics( $par = '' ) {
global $wgOut, $wgLang, $wgRequest;
- $fname = 'wfSpecialStatistics';
-
- $action = $wgRequest->getVal( 'action' );
-
$dbr = wfGetDB( DB_SLAVE );
$views = SiteStats::views();
@@ -21,18 +22,18 @@ function wfSpecialStatistics() {
$images = SiteStats::images();
$total = SiteStats::pages();
$users = SiteStats::users();
+ $admins = SiteStats::admins();
+ $numJobs = SiteStats::jobs();
- $admins = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), $fname );
- $numJobs = $dbr->estimateRowCount('job');
-
- if ($action == 'raw') {
+ if( $wgRequest->getVal( 'action' ) == 'raw' ) {
$wgOut->disable();
header( 'Pragma: nocache' );
echo "total=$total;good=$good;views=$views;edits=$edits;users=$users;admins=$admins;images=$images;jobs=$numJobs\n";
return;
} else {
- $text = '==' . wfMsg( 'sitestats' ) . "==\n" ;
- $text .= wfMsgExt( 'sitestatstext', array ( 'parsemag' ),
+ $text = "__NOTOC__\n";
+ $text .= '==' . wfMsg( 'sitestats' ) . "==\n";
+ $text .= wfMsgExt( 'sitestatstext', array( 'parsemag' ),
$wgLang->formatNum( $total ),
$wgLang->formatNum( $good ),
$wgLang->formatNum( $views ),
@@ -41,44 +42,52 @@ function wfSpecialStatistics() {
$wgLang->formatNum( sprintf( '%.2f', $edits ? $views / $edits : 0 ) ),
$wgLang->formatNum( $numJobs ),
$wgLang->formatNum( $images )
- );
-
- $text .= "\n==" . wfMsg( 'userstats' ) . "==\n";
+ )."\n";
+ $text .= "==" . wfMsg( 'userstats' ) . "==\n";
$text .= wfMsgExt( 'userstatstext', array ( 'parsemag' ),
$wgLang->formatNum( $users ),
$wgLang->formatNum( $admins ),
'[[' . wfMsgForContent( 'grouppage-sysop' ) . ']]', # TODO somehow remove, kept for backwards compatibility
$wgLang->formatNum( sprintf( '%.2f', $admins / $users * 100 ) ),
User::makeGroupLinkWiki( 'sysop' )
- );
-
- $wgOut->addWikiText( $text );
+ )."\n";
global $wgDisableCounters, $wgMiserMode, $wgUser, $wgLang, $wgContLang;
if( !$wgDisableCounters && !$wgMiserMode ) {
- $page = $dbr->tableName( 'page' );
- $sql = "SELECT page_namespace, page_title, page_counter FROM {$page} WHERE page_is_redirect = 0 AND page_counter > 0 ORDER BY page_counter DESC";
- $sql = $dbr->limitResult($sql, 10, 0);
- $res = $dbr->query( $sql, $fname );
- if( $res ) {
- $wgOut->addHtml( '<h2>' . wfMsgHtml( 'statistics-mostpopular' ) . '</h2>' );
- $skin = $wgUser->getSkin();
- $wgOut->addHtml( '<ol>' );
- while( $row = $dbr->fetchObject( $res ) ) {
- $link = $skin->makeKnownLinkObj( Title::makeTitleSafe( $row->page_namespace, $row->page_title ) );
- $dirmark = $wgContLang->getDirMark();
- $wgOut->addHtml( '<li>' . $link . $dirmark . ' [' . $wgLang->formatNum( $row->page_counter ) . ']</li>' );
+ $res = $dbr->select(
+ 'page',
+ array(
+ 'page_namespace',
+ 'page_title',
+ 'page_counter',
+ ),
+ array(
+ 'page_is_redirect' => 0,
+ 'page_counter > 0',
+ ),
+ __METHOD__,
+ array(
+ 'ORDER BY' => 'page_counter DESC',
+ 'LIMIT' => 10,
+ )
+ );
+ if( $res->numRows() > 0 ) {
+ $text .= "==" . wfMsg( 'statistics-mostpopular' ) . "==\n";
+ while( $row = $res->fetchObject() ) {
+ $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
+ if( $title instanceof Title )
+ $text .= '* [[:' . $title->getPrefixedText() . ']] (' . $wgLang->formatNum( $row->page_counter ) . ")\n";
}
- $wgOut->addHtml( '</ol>' );
- $dbr->freeResult( $res );
+ $res->free();
}
}
$footer = wfMsg( 'statistics-footer' );
if( !wfEmptyMsg( 'statistics-footer', $footer ) && $footer != '' )
- $wgOut->addWikiText( $footer );
-
+ $text .= "\n" . $footer;
+
+ $wgOut->addWikiText( $text );
}
-}
-?>
+
+} \ No newline at end of file
diff --git a/includes/SpecialUncategorizedcategories.php b/includes/SpecialUncategorizedcategories.php
index e02c9bbd..67f87aa8 100644
--- a/includes/SpecialUncategorizedcategories.php
+++ b/includes/SpecialUncategorizedcategories.php
@@ -34,4 +34,4 @@ function wfSpecialUncategorizedcategories() {
return $lpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialUncategorizedimages.php b/includes/SpecialUncategorizedimages.php
index 22e34669..23deefe8 100644
--- a/includes/SpecialUncategorizedimages.php
+++ b/includes/SpecialUncategorizedimages.php
@@ -44,4 +44,4 @@ function wfSpecialUncategorizedimages() {
return $uip->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialUncategorizedpages.php b/includes/SpecialUncategorizedpages.php
index 408ac726..b26f6d93 100644
--- a/includes/SpecialUncategorizedpages.php
+++ b/includes/SpecialUncategorizedpages.php
@@ -54,4 +54,4 @@ function wfSpecialUncategorizedpages() {
return $lpp->doQuery( $offset, $limit );
}
-?>
+
diff --git a/includes/SpecialUncategorizedtemplates.php b/includes/SpecialUncategorizedtemplates.php
new file mode 100644
index 00000000..fb785e00
--- /dev/null
+++ b/includes/SpecialUncategorizedtemplates.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Special page lists all uncategorised pages in the
+ * template namespace
+ *
+ * @addtogroup SpecialPage
+ * @author Rob Church <robchur@gmail.com>
+ */
+class UncategorizedTemplatesPage extends UncategorizedPagesPage {
+
+ var $requestedNamespace = NS_TEMPLATE;
+
+ public function getName() {
+ return 'Uncategorizedtemplates';
+ }
+
+}
+
+/**
+ * Main execution point
+ *
+ * @param mixed $par Parameter passed to the page
+ */
+function wfSpecialUncategorizedtemplates() {
+ list( $limit, $offset ) = wfCheckLimits();
+ $utp = new UncategorizedTemplatesPage();
+ $utp->doQuery( $offset, $limit );
+}
+
+
diff --git a/includes/SpecialUndelete.php b/includes/SpecialUndelete.php
index 8e740f6d..5678a81e 100644
--- a/includes/SpecialUndelete.php
+++ b/includes/SpecialUndelete.php
@@ -23,6 +23,7 @@ function wfSpecialUndelete( $par ) {
*/
class PageArchive {
protected $title;
+ var $fileStatus;
function __construct( $title ) {
if( is_null( $title ) ) {
@@ -269,8 +270,9 @@ class PageArchive {
$restoreFiles = $restoreAll || !empty( $fileVersions );
if( $restoreFiles && $this->title->getNamespace() == NS_IMAGE ) {
- $img = new Image( $this->title );
- $filesRestored = $img->restore( $fileVersions );
+ $img = wfLocalFile( $this->title );
+ $this->fileStatus = $img->restore( $fileVersions );
+ $filesRestored = $this->fileStatus->successCount;
} else {
$filesRestored = 0;
}
@@ -280,7 +282,7 @@ class PageArchive {
} else {
$textRestored = 0;
}
-
+
// Touch the log!
global $wgContLang;
$log = new LogPage( 'delete' );
@@ -303,8 +305,12 @@ class PageArchive {
if( trim( $comment ) != '' )
$reason .= ": {$comment}";
$log->addEntry( 'restore', $this->title, $reason );
-
- return true;
+
+ if ( $this->fileStatus && !$this->fileStatus->ok ) {
+ return false;
+ } else {
+ return true;
+ }
}
/**
@@ -319,18 +325,13 @@ class PageArchive {
* @return int number of revisions restored
*/
private function undeleteRevisions( $timestamps ) {
- global $wgDBtype;
-
$restoreAll = empty( $timestamps );
$dbw = wfGetDB( DB_MASTER );
- $page = $dbw->tableName( 'archive' );
# Does this page already exist? We'll have to update it...
$article = new Article( $this->title );
- $options = ( $wgDBtype == 'postgres' )
- ? '' // pg doesn't support this?
- : 'FOR UPDATE';
+ $options = 'FOR UPDATE';
$page = $dbw->selectRow( 'page',
array( 'page_id', 'page_latest' ),
array( 'page_namespace' => $this->title->getNamespace(),
@@ -422,11 +423,10 @@ class PageArchive {
}
if( $revision ) {
- # FIXME: Update latest if newer as well...
- if( $newid ) {
- // Attach the latest revision to the page...
- $article->updateRevisionOn( $dbw, $revision, $previousRevId );
-
+ // Attach the latest revision to the page...
+ $wasnew = $article->updateIfNewerOn( $dbw, $revision, $previousRevId );
+
+ if( $newid || $wasnew ) {
// Update site stats, link tables, etc
$article->createUpdates( $revision );
}
@@ -453,6 +453,7 @@ class PageArchive {
return $restored;
}
+ function getFileStatus() { return $this->fileStatus; }
}
/**
@@ -602,17 +603,24 @@ class UndeleteForm {
$archive = new PageArchive( $this->mTargetObj );
$rev = $archive->getRevision( $timestamp );
- $wgOut->setPageTitle( wfMsg( 'undeletepage' ) );
- $link = $skin->makeKnownLinkObj( $self, htmlspecialchars( $this->mTargetObj->getPrefixedText() ),
- 'target=' . $this->mTargetObj->getPrefixedUrl() );
- $wgOut->addHtml( '<p>' . wfMsgHtml( 'undelete-revision', $link,
- htmlspecialchars( $wgLang->timeAndDate( $timestamp ) ) ) . '</p>' );
-
if( !$rev ) {
- $wgOut->addWikiText( wfMsg( 'undeleterevision-missing' ) );
+ $wgOut->addWikiTexT( wfMsg( 'undeleterevision-missing' ) );
return;
}
+ $wgOut->setPageTitle( wfMsg( 'undeletepage' ) );
+
+ $link = $skin->makeKnownLinkObj(
+ $self,
+ htmlspecialchars( $this->mTargetObj->getPrefixedText() ),
+ 'target=' . $this->mTargetObj->getPrefixedUrl()
+ );
+ $time = htmlspecialchars( $wgLang->timeAndDate( $timestamp ) );
+ $user = $skin->userLink( $rev->getUser(), $rev->getUserText() )
+ . $skin->userToolLinks( $rev->getUser(), $rev->getUserText() );
+
+ $wgOut->addHtml( '<p>' . wfMsgHtml( 'undelete-revision', $link, $time, $user ) . '</p>' );
+
wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) );
if( $this->mPreview ) {
@@ -622,7 +630,7 @@ class UndeleteForm {
$wgOut->addHtml(
wfElement( 'textarea', array(
- 'readonly' => true,
+ 'readonly' => 'readonly',
'cols' => intval( $wgUser->getOption( 'cols' ) ),
'rows' => intval( $wgUser->getOption( 'rows' ) ) ),
$rev->getText() . "\n" ) .
@@ -673,7 +681,7 @@ class UndeleteForm {
}
/* private */ function showHistory() {
- global $wgLang, $wgUser, $wgOut;
+ global $wgLang, $wgContLang, $wgUser, $wgOut;
$sk = $wgUser->getSkin();
if ( $this->mAllowed ) {
@@ -699,10 +707,10 @@ class UndeleteForm {
# List all stored revisions
$revisions = $archive->listRevisions();
$files = $archive->listFiles();
-
+
$haveRevisions = $revisions && $revisions->numRows() > 0;
$haveFiles = $files && $files->numRows() > 0;
-
+
# Batch existence check on user and talk pages
if( $haveRevisions ) {
$batch = new LinkBatch();
@@ -727,7 +735,7 @@ class UndeleteForm {
$titleObj = SpecialPage::getTitleFor( "Undelete" );
$action = $titleObj->getLocalURL( "action=submit" );
# Start the form here
- $top = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'undelete' ) );
+ $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'undelete' ) );
$wgOut->addHtml( $top );
}
@@ -736,24 +744,48 @@ class UndeleteForm {
$logViewer = new LogViewer(
new LogReader(
new FauxRequest(
- array( 'page' => $this->mTargetObj->getPrefixedText(),
- 'type' => 'delete' ) ) ) );
+ array(
+ 'page' => $this->mTargetObj->getPrefixedText(),
+ 'type' => 'delete'
+ )
+ )
+ ), LogViewer::NO_ACTION_LINK
+ );
$logViewer->showList( $wgOut );
-
+
if( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
# Format the user-visible controls (comment field, submission button)
# in a nice little table
- $table = '<fieldset><table><tr>';
- $table .= '<td colspan="2">' . wfMsgWikiHtml( 'undeleteextrahelp' ) . '</td></tr><tr>';
- $table .= '<td align="right"><strong>' . wfMsgHtml( 'undeletecomment' ) . '</strong></td>';
- $table .= '<td>' . wfInput( 'wpComment', 50, $this->mComment ) . '</td>';
- $table .= '</tr><tr><td>&nbsp;</td><td>';
- $table .= wfSubmitButton( wfMsg( 'undeletebtn' ), array( 'name' => 'restore' ) );
- $table .= wfElement( 'input', array( 'type' => 'reset', 'value' => wfMsg( 'undeletereset' ) ) );
- $table .= '</td></tr></table></fieldset>';
+ $align = $wgContLang->isRtl() ? 'left' : 'right';
+ $table =
+ Xml::openElement( 'fieldset' ) .
+ Xml::openElement( 'table' ) .
+ "<tr>
+ <td colspan='2'>" .
+ wfMsgWikiHtml( 'undeleteextrahelp' ) .
+ "</td>
+ </tr>
+ <tr>
+ <td align='$align'>" .
+ Xml::label( wfMsg( 'undeletecomment' ), 'wpComment' ) .
+ "</td>
+ <td>" .
+ Xml::input( 'wpComment', 50, $this->mComment ) .
+ "</td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>" .
+ Xml::submitButton( wfMsg( 'undeletebtn' ), array( 'name' => 'restore', 'id' => 'mw-undelete-submit' ) ) .
+ Xml::element( 'input', array( 't