summaryrefslogtreecommitdiff
path: root/includes/api
diff options
context:
space:
mode:
Diffstat (limited to 'includes/api')
-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
39 files changed, 3928 insertions, 809 deletions
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index c4218825..b324c52f 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,16 @@
*/
/**
- * @todo Document - e.g. Provide top-level description of this class.
+ * This abstract class implements many basic API functions, and is the base of all API classes.
+ * The class functions are divided into several areas of functionality:
+ *
+ * Module parameters: Derived classes can define getAllowedParams() to specify which parameters to expect,
+ * how to parse and validate them.
+ *
+ * Profiling: various methods to allow keeping tabs on various tasks and their time costs
+ *
+ * Self-documentation: code to allow api to document its own state.
+ *
* @addtogroup API
*/
abstract class ApiBase {
@@ -34,24 +43,24 @@ abstract class ApiBase {
const PARAM_DFLT = 0;
const PARAM_ISMULTI = 1;
const PARAM_TYPE = 2;
- const PARAM_MAX1 = 3;
+ const PARAM_MAX = 3;
const PARAM_MAX2 = 4;
const PARAM_MIN = 5;
- const LIMIT_BIG1 = 500; // Fast query, user's limit
- const LIMIT_BIG2 = 5000; // Fast query, bot's limit
- const LIMIT_SML1 = 50; // Slow query, user's limit
- const LIMIT_SML2 = 500; // Slow query, bot's limit
+ const LIMIT_BIG1 = 500; // Fast query, std user limit
+ const LIMIT_BIG2 = 5000; // Fast query, bot/sysop limit
+ const LIMIT_SML1 = 50; // Slow query, std user limit
+ const LIMIT_SML2 = 500; // Slow query, bot/sysop limit
- private $mMainModule, $mModuleName, $mParamPrefix;
+ private $mMainModule, $mModuleName, $mModulePrefix;
/**
* Constructor
*/
- public function __construct($mainModule, $moduleName, $paramPrefix = '') {
+ public function __construct($mainModule, $moduleName, $modulePrefix = '') {
$this->mMainModule = $mainModule;
$this->mModuleName = $moduleName;
- $this->mParamPrefix = $paramPrefix;
+ $this->mModulePrefix = $modulePrefix;
}
/**
@@ -67,6 +76,13 @@ abstract class ApiBase {
}
/**
+ * Get parameter prefix (usually two letters or an empty string).
+ */
+ public function getModulePrefix() {
+ return $this->mModulePrefix;
+ }
+
+ /**
* Get the name of the module as shown in the profiler log
*/
public function getModuleProfileName($db = false) {
@@ -109,6 +125,15 @@ abstract class ApiBase {
}
/**
+ * Set warning section for this module. Users should monitor this section to notice any changes in API.
+ */
+ public function setWarning($warning) {
+ $msg = array();
+ ApiResult :: setContent($msg, $warning);
+ $this->getResult()->addValue('warnings', $this->getModuleName(), $msg);
+ }
+
+ /**
* If the module may only be used with a certain format module,
* it should override this method to return an instance of that formatter.
* A value of null means the default format will be used.
@@ -191,11 +216,38 @@ abstract class ApiBase {
$prompt = 'One value: ';
if (is_array($type)) {
- $desc .= $paramPrefix . $prompt . implode(', ', $type);
- }
- elseif ($type == 'namespace') {
- // Special handling because namespaces are type-limited, yet they are not given
- $desc .= $paramPrefix . $prompt . implode(', ', ApiBase :: getValidNamespaces());
+ $choices = array();
+ $nothingPrompt = false;
+ foreach ($type as $t)
+ if ($t=='')
+ $nothingPrompt = 'Can be empty, or ';
+ else
+ $choices[] = $t;
+ $desc .= $paramPrefix . $nothingPrompt . $prompt . implode(', ', $choices);
+ } else {
+ switch ($type) {
+ case 'namespace':
+ // Special handling because namespaces are type-limited, yet they are not given
+ $desc .= $paramPrefix . $prompt . implode(', ', ApiBase :: getValidNamespaces());
+ break;
+ case 'limit':
+ $desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]} ({$paramSettings[self :: PARAM_MAX2]} for bots) allowed.";
+ break;
+ case 'integer':
+ $hasMin = isset($paramSettings[self :: PARAM_MIN]);
+ $hasMax = isset($paramSettings[self :: PARAM_MAX]);
+ if ($hasMin || $hasMax) {
+ if (!$hasMax)
+ $intRangeStr = "The value must be no less than {$paramSettings[self :: PARAM_MIN]}";
+ elseif (!$hasMin)
+ $intRangeStr = "The value must be no more than {$paramSettings[self :: PARAM_MAX]}";
+ else
+ $intRangeStr = "The value must be between {$paramSettings[self :: PARAM_MIN]} and {$paramSettings[self :: PARAM_MAX]}";
+
+ $desc .= $paramPrefix . $intRangeStr;
+ }
+ break;
+ }
}
}
@@ -244,7 +296,7 @@ abstract class ApiBase {
* Override this method to change parameter name during runtime
*/
public function encodeParamName($paramName) {
- return $this->mParamPrefix . $paramName;
+ return $this->mModulePrefix . $paramName;
}
/**
@@ -293,7 +345,7 @@ abstract class ApiBase {
protected function getParameterFromSettings($paramName, $paramSettings) {
// Some classes may decide to change parameter names
- $paramName = $this->encodeParamName($paramName);
+ $encParamName = $this->encodeParamName($paramName);
if (!is_array($paramSettings)) {
$default = $paramSettings;
@@ -316,19 +368,19 @@ abstract class ApiBase {
if ($type == 'boolean') {
if (isset ($default) && $default !== false) {
// Having a default value of anything other than 'false' is pointless
- ApiBase :: dieDebug(__METHOD__, "Boolean param $paramName's default is set to '$default'");
+ ApiBase :: dieDebug(__METHOD__, "Boolean param $encParamName's default is set to '$default'");
}
- $value = $this->getMain()->getRequest()->getCheck($paramName);
+ $value = $this->getMain()->getRequest()->getCheck($encParamName);
} else {
- $value = $this->getMain()->getRequest()->getVal($paramName, $default);
+ $value = $this->getMain()->getRequest()->getVal($encParamName, $default);
if (isset ($value) && $type == 'namespace')
$type = ApiBase :: getValidNamespaces();
}
if (isset ($value) && ($multi || is_array($type)))
- $value = $this->parseMultiValue($paramName, $value, $multi, is_array($type) ? $type : null);
+ $value = $this->parseMultiValue($encParamName, $value, $multi, is_array($type) ? $type : null);
// More validation only when choices were not given
// choices were validated in parseMultiValue()
@@ -339,32 +391,48 @@ abstract class ApiBase {
break;
case 'string' : // nothing to do
break;
- case 'integer' : // Force everything using intval()
+ case 'integer' : // Force everything using intval() and optionally validate limits
+
$value = is_array($value) ? array_map('intval', $value) : intval($value);
+ $min = isset ($paramSettings[self :: PARAM_MIN]) ? $paramSettings[self :: PARAM_MIN] : null;
+ $max = isset ($paramSettings[self :: PARAM_MAX]) ? $paramSettings[self :: PARAM_MAX] : null;
+
+ if (!is_null($min) || !is_null($max)) {
+ $values = is_array($value) ? $value : array($value);
+ foreach ($values as $v) {
+ $this->validateLimit($paramName, $v, $min, $max);
+ }
+ }
break;
case 'limit' :
- if (!isset ($paramSettings[self :: PARAM_MAX1]) || !isset ($paramSettings[self :: PARAM_MAX2]))
- ApiBase :: dieDebug(__METHOD__, "MAX1 or MAX2 are not defined for the limit $paramName");
+ if (!isset ($paramSettings[self :: PARAM_MAX]) || !isset ($paramSettings[self :: PARAM_MAX2]))
+ ApiBase :: dieDebug(__METHOD__, "MAX1 or MAX2 are not defined for the limit $encParamName");
if ($multi)
- ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
+ ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName");
$min = isset ($paramSettings[self :: PARAM_MIN]) ? $paramSettings[self :: PARAM_MIN] : 0;
$value = intval($value);
- $this->validateLimit($paramName, $value, $min, $paramSettings[self :: PARAM_MAX1], $paramSettings[self :: PARAM_MAX2]);
+ $this->validateLimit($paramName, $value, $min, $paramSettings[self :: PARAM_MAX], $paramSettings[self :: PARAM_MAX2]);
break;
case 'boolean' :
if ($multi)
- ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
+ ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName");
break;
case 'timestamp' :
if ($multi)
- ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
+ ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName");
$value = wfTimestamp(TS_UNIX, $value);
if ($value === 0)
- $this->dieUsage("Invalid value '$value' for timestamp parameter $paramName", "badtimestamp_{$paramName}");
+ $this->dieUsage("Invalid value '$value' for timestamp parameter $encParamName", "badtimestamp_{$encParamName}");
$value = wfTimestamp(TS_MW, $value);
break;
+ case 'user' :
+ $title = Title::makeTitleSafe( NS_USER, $value );
+ if ( is_null( $title ) )
+ $this->dieUsage("Invalid value for user parameter $encParamName", "baduser_{$encParamName}");
+ $value = $title->getText();
+ break;
default :
- ApiBase :: dieDebug(__METHOD__, "Param $paramName's type is unknown - $type");
+ ApiBase :: dieDebug(__METHOD__, "Param $encParamName's type is unknown - $type");
}
}
@@ -405,19 +473,26 @@ abstract class ApiBase {
/**
* Validate the value against the minimum and user/bot maximum limits. Prints usage info on failure.
*/
- function validateLimit($varname, $value, $min, $max, $botMax) {
- if ($value < $min) {
- $this->dieUsage("$varname may not be less than $min (set to $value)", $varname);
+ function validateLimit($paramName, $value, $min, $max, $botMax = null) {
+ if (!is_null($min) && $value < $min) {
+ $this->dieUsage($this->encodeParamName($paramName) . " may not be less than $min (set to $value)", $paramName);
}
- if ($this->getMain()->isBot()) {
- if ($value > $botMax) {
- $this->dieUsage("$varname may not be over $botMax (set to $value) for bots", $varname);
+ // Minimum is always validated, whereas maximum is checked only if not running in internal call mode
+ if ($this->getMain()->isInternalMode())
+ return;
+
+ // Optimization: do not check user's bot status unless really needed -- skips db query
+ // assumes $botMax >= $max
+ if (!is_null($max) && $value > $max) {
+ if (!is_null($botMax) && ($this->getMain()->isBot() || $this->getMain()->isSysop())) {
+ if ($value > $botMax) {
+ $this->dieUsage($this->encodeParamName($paramName) . " may not be over $botMax (set to $value) for bots or sysops", $paramName);
+ }
+ } else {
+ $this->dieUsage($this->encodeParamName($paramName) . " may not be over $max (set to $value) for users", $paramName);
}
}
- elseif ($value > $max) {
- $this->dieUsage("$varname may not be over $max (set to $value) for users", $varname);
- }
}
/**
@@ -526,11 +601,19 @@ abstract class ApiBase {
ApiBase :: dieDebug(__METHOD__, 'called without calling profileDBOut() first');
return $this->mDBTime;
}
+
+ public static function debugPrint($value, $name = 'unknown', $backtrace = false) {
+ print "\n\n<pre><b>Debuging value '$name':</b>\n\n";
+ var_export($value);
+ if ($backtrace)
+ print "\n" . wfBacktrace();
+ print "\n</pre>\n";
+ }
public abstract function getVersion();
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiBase.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiBase.php 24934 2007-08-20 08:04:12Z nickj $';
}
}
-?>
+
diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php
index 7918ee0e..b2f6ceff 100644
--- a/includes/api/ApiFeedWatchlist.php
+++ b/includes/api/ApiFeedWatchlist.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,10 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This action allows users to get their watchlist items in RSS/Atom formats.
+ * When executed, it performs a nested call to the API to get the needed data,
+ * and formats it in a proper format.
+ *
* @addtogroup API
*/
class ApiFeedWatchlist extends ApiBase {
@@ -37,47 +41,81 @@ class ApiFeedWatchlist extends ApiBase {
parent :: __construct($main, $action);
}
+ /**
+ * This module uses a custom feed wrapper printer.
+ */
public function getCustomPrinter() {
return new ApiFormatFeedWrapper($this->getMain());
}
+ /**
+ * Make a nested call to the API to request watchlist items in the last $hours.
+ * Wrap the result as an RSS/Atom feed.
+ */
public function execute() {
- $feedformat = null;
- extract($this->extractRequestParams());
-
- // limit to 1 day
- $startTime = wfTimestamp(TS_MW, time() - intval(1 * 86400));
-
- // Prepare nested request
- $params = new FauxRequest(array (
- 'action' => 'query',
- 'meta' => 'siteinfo',
- 'siprop' => 'general',
- 'list' => 'watchlist',
- 'wlprop' => 'user|comment|timestamp',
- 'wlstart' => $startTime,
- 'wllimit' => 50
- ));
-
- // Execute
- $module = new ApiMain($params);
- $module->execute();
-
- // Get data array
- $data = $module->getResultData();
-
- $feedItems = array ();
- foreach ($data['query']['watchlist'] as $info) {
- $feedItems[] = $this->createFeedItem($info);
- }
-
+
global $wgFeedClasses, $wgSitename, $wgContLanguageCode;
- $feedTitle = $wgSitename . ' - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']';
- $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl();
-
- $feed = new $wgFeedClasses[$feedformat] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl);
- ApiFormatFeedWrapper :: setResult($this->getResult(), $feed, $feedItems);
+ try {
+ $params = $this->extractRequestParams();
+
+ // limit to the number of hours going from now back
+ $endTime = wfTimestamp(TS_MW, time() - intval($params['hours'] * 60 * 60));
+
+ // Prepare nested request
+ $fauxReq = new FauxRequest(array (
+ 'action' => 'query',
+ 'meta' => 'siteinfo',
+ 'siprop' => 'general',
+ 'list' => 'watchlist',
+ 'wlprop' => 'title|user|comment|timestamp',
+ 'wldir' => 'older', // reverse order - from newest to oldest
+ 'wlend' => $endTime, // stop at this time
+ 'wllimit' => 50
+ ));
+
+ // Execute
+ $module = new ApiMain($fauxReq);
+ $module->execute();
+
+ // Get data array
+ $data = $module->getResultData();
+
+ $feedItems = array ();
+ foreach ($data['query']['watchlist'] as $info) {
+ $feedItems[] = $this->createFeedItem($info);
+ }
+
+ $feedTitle = $wgSitename . ' - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']';
+ $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl();
+
+ $feed = new $wgFeedClasses[$params['feedformat']] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl);
+
+ ApiFormatFeedWrapper :: setResult($this->getResult(), $feed, $feedItems);
+
+ } catch (Exception $e) {
+
+ // Error results should not be cached
+ $this->getMain()->setCacheMaxAge(0);
+
+ $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']';
+ $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl();
+
+ $feedFormat = isset($params['feedformat']) ? $params['feedformat'] : 'rss';
+ $feed = new $wgFeedClasses[$feedFormat] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl);
+
+
+ if ($e instanceof UsageException) {
+ $errorCode = $e->getCodeString();
+ } else {
+ // Something is seriously wrong
+ $errorCode = 'internal_api_error';
+ }
+
+ $errorText = $e->getMessage();
+ $feedItems[] = new FeedItem("Error ($errorCode)", $errorText, "", "", "");
+ ApiFormatFeedWrapper :: setResult($this->getResult(), $feed, $feedItems);
+ }
}
private function createFeedItem($info) {
@@ -100,13 +138,20 @@ class ApiFeedWatchlist extends ApiBase {
'feedformat' => array (
ApiBase :: PARAM_DFLT => 'rss',
ApiBase :: PARAM_TYPE => $feedFormatNames
+ ),
+ 'hours' => array (
+ ApiBase :: PARAM_DFLT => 24,
+ ApiBase :: PARAM_TYPE => 'integer',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => 72,
)
);
}
protected function getParamDescription() {
return array (
- 'feedformat' => 'The format of the feed'
+ 'feedformat' => 'The format of the feed',
+ 'hours' => 'List pages modified within this many hours from now'
);
}
@@ -121,7 +166,7 @@ class ApiFeedWatchlist extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFeedWatchlist.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFeedWatchlist.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index 782a4161..861310d2 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This is the abstract base class for API formatters.
+ *
* @addtogroup API
*/
abstract class ApiFormatBase extends ApiBase {
@@ -36,7 +38,8 @@ abstract class ApiFormatBase extends ApiBase {
private $mIsHtml, $mFormat;
/**
- * Constructor
+ * Create a new instance of the formatter.
+ * If the format name ends with 'fm', wrap its output in the proper HTML.
*/
public function __construct($main, $format) {
parent :: __construct($main, $format);
@@ -56,6 +59,11 @@ abstract class ApiFormatBase extends ApiBase {
*/
public abstract function getMimeType();
+ /**
+ * If formatter outputs data results as is, the results must first be sanitized.
+ * An XML formatter on the other hand uses special tags, such as "_element" for special handling,
+ * and thus needs to override this function to return true.
+ */
public function getNeedsRawData() {
return false;
}
@@ -77,6 +85,7 @@ abstract class ApiFormatBase extends ApiBase {
function initPrinter($isError) {
$isHtml = $this->getIsHtml();
$mime = $isHtml ? 'text/html' : $this->getMimeType();
+ $script = wfScript( 'api' );
// Some printers (ex. Feed) do their own header settings,
// in which case $mime will be set to null
@@ -96,14 +105,14 @@ abstract class ApiFormatBase extends ApiBase {
<?php
- if (!$isError) {
+ if( !$isError ) {
?>
<br/>
<small>
-You are looking at the HTML representation of the <?=$this->mFormat?> format.<br/>
+You are looking at the HTML representation of the <?php echo( $this->mFormat ); ?> format.<br/>
HTML is good for debugging, but probably is not suitable for your application.<br/>
-Please see "format" parameter documentation at the <a href='api.php'>API help</a>
-for more information.
+See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
+<a href='<?php echo( $script ); ?>'>API help</a> for more information.
</small>
<?php
@@ -133,6 +142,10 @@ for more information.
}
}
+ /**
+ * The main format printing function. Call it to output the result string to the user.
+ * This function will automatically output HTML when format name ends in 'fm'.
+ */
public function printText($text) {
if ($this->getIsHtml())
echo $this->formatHTML($text);
@@ -152,9 +165,9 @@ for more information.
$text = preg_replace('/\&lt;(!--.*?--|.*?)\&gt;/', '<span style="color:blue;">&lt;\1&gt;</span>', $text);
// identify URLs
$protos = "http|https|ftp|gopher";
- $text = ereg_replace("($protos)://[^ '\"()<\n]+", '<a href="\\0">\\0</a>', $text);
+ $text = ereg_replace("($protos)://[^ \\'\"()<\n]+", '<a href="\\0">\\0</a>', $text);
// identify requests to api.php
- $text = ereg_replace("api\\.php\\?[^ ()<\n\t]+", '<a href="\\0">\\0</a>', $text);
+ $text = ereg_replace("api\\.php\\?[^ \\()<\n\t]+", '<a href="\\0">\\0</a>', $text);
// make strings inside * bold
$text = ereg_replace("\\*[^<>\n]+\\*", '<b>\\0</b>', $text);
// make strings inside $ italic
@@ -175,7 +188,7 @@ for more information.
}
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 25746 2007-09-10 21:36:51Z brion $';
}
}
@@ -190,7 +203,7 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
}
/**
- * Call this method to initialize output data
+ * Call this method to initialize output data. See self::execute()
*/
public static function setResult($result, $feed, $feedItems) {
// Store output in the Result data.
@@ -214,6 +227,11 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
return true;
}
+ /**
+ * This class expects the result data to be in a custom format set by self::setResult()
+ * $result['_feed'] - an instance of one of the $wgFeedClasses classes
+ * $result['_feeditems'] - an array of FeedItem instances
+ */
public function execute() {
$data = $this->getResultData();
if (isset ($data['_feed']) && isset ($data['_feeditems'])) {
@@ -232,7 +250,6 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 25746 2007-09-10 21:36:51Z brion $';
}
}
-?>
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index dd1847c4..ed9bd938 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -49,14 +49,35 @@ class ApiFormatJson extends ApiFormatBase {
}
public function execute() {
+ $prefix = $suffix = "";
+
+ $params = $this->extractRequestParams();
+ $callback = $params['callback'];
+ if(!is_null($callback)) {
+ $prefix = ereg_replace("[^_A-Za-z0-9]", "", $callback ) . "(";
+ $suffix = ")";
+ }
+
if (!function_exists('json_encode') || $this->getIsHtml()) {
$json = new Services_JSON();
- $this->printText($json->encode($this->getResultData(), $this->getIsHtml()));
+ $this->printText($prefix . $json->encode($this->getResultData(), $this->getIsHtml()) . $suffix);
} else {
- $this->printText(json_encode($this->getResultData()));
+ $this->printText($prefix . json_encode($this->getResultData()) . $suffix);
}
}
+ protected function getAllowedParams() {
+ return array (
+ 'callback' => null
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'callback' => 'If specified, wraps the output into a given function call',
+ );
+ }
+
protected function getDescription() {
if ($this->mIsRaw)
return 'Output data with the debuging elements in JSON format' . parent :: getDescription();
@@ -65,7 +86,7 @@ class ApiFormatJson extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatJson.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFormatJson.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+
diff --git a/includes/api/ApiFormatJson_json.php b/includes/api/ApiFormatJson_json.php
index 2cd87930..a8c649c3 100644
--- a/includes/api/ApiFormatJson_json.php
+++ b/includes/api/ApiFormatJson_json.php
@@ -843,4 +843,4 @@ if (class_exists('PEAR_Error')) {
}
-?>
+
diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php
index add63362..766d7041 100644
--- a/includes/api/ApiFormatPhp.php
+++ b/includes/api/ApiFormatPhp.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ class ApiFormatPhp extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatPhp.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFormatPhp.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+
diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php
index bc720490..0ddfac73 100644
--- a/includes/api/ApiFormatWddx.php
+++ b/includes/api/ApiFormatWddx.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -85,7 +85,7 @@ class ApiFormatWddx extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatWddx.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFormatWddx.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+
diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php
index 7d54b441..02647923 100644
--- a/includes/api/ApiFormatXml.php
+++ b/includes/api/ApiFormatXml.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -141,7 +141,7 @@ class ApiFormatXml extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatXml.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFormatXml.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+
diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php
index 0107eb2b..400c0a4b 100644
--- a/includes/api/ApiFormatYaml.php
+++ b/includes/api/ApiFormatYaml.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ class ApiFormatYaml extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatYaml.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiFormatYaml.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+
diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php
index a67bbb22..b3ccff0f 100644
--- a/includes/api/ApiFormatYaml_spyc.php
+++ b/includes/api/ApiFormatYaml_spyc.php
@@ -857,4 +857,4 @@
return $ret;
}
}
-?>
+
diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php
index 7c5144fd..9f1e88ea 100644
--- a/includes/api/ApiHelp.php
+++ b/includes/api/ApiHelp.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This is a simple class to handle action=help
+ *
* @addtogroup API
*/
class ApiHelp extends ApiBase {
@@ -51,7 +53,7 @@ class ApiHelp extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiHelp.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiHelp.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+
diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php
index 147d37a1..af68b29d 100644
--- a/includes/api/ApiLogin.php
+++ b/includes/api/ApiLogin.php
@@ -5,7 +5,8 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006-2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
+ * Daniel Cannon (cannon dot danielc at gmail dot com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,18 +30,60 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * Unit to authenticate log-in attempts to the current wiki.
+ *
* @addtogroup API
*/
class ApiLogin extends ApiBase {
-
+
+ /**
+ * Time (in seconds) a user must wait after submitting
+ * a bad login (will be multiplied by the THROTTLE_FACTOR for each bad attempt)
+ */
+ const THROTTLE_TIME = 1;
+
+ /**
+ * The factor by which the wait-time in between authentication
+ * attempts is increased every failed attempt.
+ */
+ const THROTTLE_FACTOR = 2;
+
+ /**
+ * The maximum number of failed logins after which the wait increase stops.
+ */
+ const THOTTLE_MAX_COUNT = 10;
+
public function __construct($main, $action) {
parent :: __construct($main, $action, 'lg');
}
+ /**
+ * Executes the log-in attempt using the parameters passed. If
+ * the log-in succeeeds, it attaches a cookie to the session
+ * and outputs the user id, username, and session token. If a
+ * log-in fails, as the result of a bad password, a nonexistant
+ * user, or any other reason, the host is cached with an expiry
+ * and no log-in attempts will be accepted until that expiry
+ * is reached. The expiry is $this->mLoginThrottle.
+ *
+ * @access public
+ */
public function execute() {
$name = $password = $domain = null;
extract($this->extractRequestParams());
+ $result = array ();
+
+ // Make sure noone is trying to guess the password brut-force
+ $nextLoginIn = $this->getNextLoginTimeout();
+ if ($nextLoginIn > 0) {
+ $result['result'] = 'NeedToWait';
+ $result['details'] = "Please wait $nextLoginIn seconds before next log-in attempt";
+ $result['wait'] = $nextLoginIn;
+ $this->getResult()->addValue(null, 'login', $result);
+ return;
+ }
+
$params = new FauxRequest(array (
'wpName' => $name,
'wpPassword' => $password,
@@ -48,8 +91,6 @@ class ApiLogin extends ApiBase {
'wpRemember' => ''
));
- $result = array ();
-
$loginForm = new LoginForm($params);
switch ($loginForm->authenticateUserData()) {
case LoginForm :: SUCCESS :
@@ -86,9 +127,89 @@ class ApiLogin extends ApiBase {
ApiBase :: dieDebug(__METHOD__, 'Unhandled case value');
}
+ if ($result['result'] != 'Success') {
+ $result['wait'] = $this->cacheBadLogin();
+ }
+ // if we were allowed to try to login, memcache is fine
+
$this->getResult()->addValue(null, 'login', $result);
}
+
+ /**
+ * Caches a bad-login attempt associated with the host and with an
+ * expiry of $this->mLoginThrottle. These are cached by a key
+ * separate from that used by the captcha system--as such, logging
+ * in through the standard interface will get you a legal session
+ * and cookies to prove it, but will not remove this entry.
+ *
+ * Returns the number of seconds until next login attempt will be allowed.
+ *
+ * @access private
+ */
+ private function cacheBadLogin() {
+ global $wgMemc;
+
+ $key = $this->getMemCacheKey();
+ $val = $wgMemc->get( $key );
+
+ $val['lastReqTime'] = time();
+ if (!isset($val['count'])) {
+ $val['count'] = 1;
+ } else {
+ $val['count'] = 1 + $val['count'];
+ }
+
+ $delay = ApiLogin::calculateDelay($val['count']);
+
+ $wgMemc->delete($key);
+ // Cache expiration should be the maximum timeout - to prevent a "try and wait" attack
+ $wgMemc->add( $key, $val, ApiLogin::calculateDelay(ApiLogin::THOTTLE_MAX_COUNT) );
+
+ return $delay;
+ }
+
+ /**
+ * How much time the client must wait before it will be
+ * allowed to try to log-in next.
+ * The return value is 0 if no wait is required.
+ */
+ private function getNextLoginTimeout() {
+ global $wgMemc;
+
+ $val = $wgMemc->get($this->getMemCacheKey());
+
+ $elapse = (time() - $val['lastReqTime']); // in seconds
+ $canRetryIn = ApiLogin::calculateDelay($val['count']) - $elapse;
+
+ return $canRetryIn < 0 ? 0 : $canRetryIn;
+ }
+
+ /**
+ * Based on the number of previously attempted logins, returns
+ * the delay (in seconds) when the next login attempt will be allowed.
+ */
+ private static function calculateDelay($count) {
+ // Defensive programming
+ $count = intval($count);
+ $count = $count < 1 ? 1 : $count;
+ $count = $count > self::THOTTLE_MAX_COUNT ? self::THOTTLE_MAX_COUNT : $count;
+
+ return self::THROTTLE_TIME + self::THROTTLE_TIME * ($count - 1) * self::THROTTLE_FACTOR;
+ }
+
+ /**
+ * Internal cache key for badlogin checks. Robbed from the
+ * ConfirmEdit extension and modified to use a key unique to the
+ * API login.3
+ *
+ * @return string
+ * @access private
+ */
+ private function getMemCacheKey() {
+ return wfMemcKey( 'apilogin', 'badlogin', 'ip', wfGetIP() );
+ }
+
protected function getAllowedParams() {
return array (
'name' => null,
@@ -107,7 +228,11 @@ class ApiLogin extends ApiBase {
protected function getDescription() {
return array (
- 'This module is used to login and get the authentication tokens.'
+ 'This module is used to login and get the authentication tokens. ',
+ 'In the event of a successful log-in, a cookie will be attached',
+ 'to your session. In the event of a failed log-in, you will not ',
+ 'be able to attempt another log-in through this method for 60 seconds.',
+ 'This is to prevent password guessing by automated password crackers.'
);
}
@@ -118,7 +243,7 @@ class ApiLogin extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiLogin.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiLogin.php 24695 2007-08-09 09:53:05Z yurik $';
}
}
-?>
+
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 9a6b0f83..31870449 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,7 +29,16 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * This is the main API class, used for both external and internal processing.
+ * This is the main API class, used for both external and internal processing.
+ * When executed, it will create the requested formatter object,
+ * instantiate and execute an object associated with the needed action,
+ * and use formatter to print results.
+ * In case of an exception, an error message will be printed using the same formatter.
+ *
+ * To use API from another application, run it using FauxRequest object, in which
+ * case any internal exceptions will not be handled but passed up to the caller.
+ * After successful execution, use getResult() for the resulting data.
+ *
* @addtogroup API
*/
class ApiMain extends ApiBase {
@@ -43,11 +52,11 @@ class ApiMain extends ApiBase {
* List of available modules: action name => module class
*/
private static $Modules = array (
- 'help' => 'ApiHelp',
'login' => 'ApiLogin',
+ 'query' => 'ApiQuery',
'opensearch' => 'ApiOpenSearch',
'feedwatchlist' => 'ApiFeedWatchlist',
- 'query' => 'ApiQuery'
+ 'help' => 'ApiHelp',
);
/**
@@ -68,10 +77,11 @@ class ApiMain extends ApiBase {
);
private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames;
- private $mResult, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage;
+ private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage;
/**
- * Constructor
+ * Constructs an instance of ApiMain that utilizes the module and format specified by $request.
+ *
* @param $request object - if this is an instance of FauxRequest, errors are thrown and no printing occurs
* @param $enableWrite bool should be set to true if the api may modify data
*/
@@ -82,7 +92,23 @@ class ApiMain extends ApiBase {
// Special handling for the main module: $parent === $this
parent :: __construct($this, $this->mInternalMode ? 'main_int' : 'main');
- $this->mModules = self :: $Modules;
+ if (!$this->mInternalMode) {
+
+ // Impose module restrictions.
+ // If the current user cannot read,
+ // Remove all modules other than login
+ global $wgUser;
+ if (!$wgUser->isAllowed('read')) {
+ self::$Modules = array(
+ 'login' => self::$Modules['login'],
+ 'help' => self::$Modules['help']
+ );
+ }
+ }
+
+ global $wgAPIModules; // extension modules
+ $this->mModules = $wgAPIModules + self :: $Modules;
+
$this->mModuleNames = array_keys($this->mModules); // todo: optimize
$this->mFormats = self :: $Formats;
$this->mFormatNames = array_keys($this->mFormats); // todo: optimize
@@ -96,28 +122,53 @@ class ApiMain extends ApiBase {
$this->mSquidMaxage = 0;
}
- public function & getRequest() {
+ /**
+ * Return true if the API was started by other PHP code using FauxRequest
+ */
+ public function isInternalMode() {
+ return $this->mInternalMode;
+ }
+
+ /**
+ * Return the request object that contains client's request
+ */
+ public function getRequest() {
return $this->mRequest;
}
+ /**
+ * Get the ApiResult object asscosiated with current request
+ */
public function getResult() {
return $this->mResult;
}
+ /**
+ * This method will simply cause an error if the write mode was disabled for this api.
+ */
public function requestWriteMode() {
if (!$this->mEnableWrite)
$this->dieUsage('Editing of this site is disabled. Make sure the $wgEnableWriteAPI=true; ' .
'statement is included in the site\'s LocalSettings.php file', 'readonly');
}
+ /**
+ * Set how long the response should be cached.
+ */
public function setCacheMaxAge($maxage) {
$this->mSquidMaxage = $maxage;
}
+ /**
+ * Create an instance of an output formatter by its name
+ */
public function createPrinterByName($format) {
return new $this->mFormats[$format] ($this, $format);
}
+ /**
+ * Execute api request. Any errors will be handled if the API was called by the remote client.
+ */
public function execute() {
$this->profileIn();
if ($this->mInternalMode)
@@ -127,10 +178,14 @@ class ApiMain extends ApiBase {
$this->profileOut();
}
+ /**
+ * Execute an action, and in case of an error, erase whatever partial results
+ * have been accumulated, and replace it with an error message and a help screen.
+ */
protected function executeActionWithErrorHandling() {
// In case an error occurs during data output,
- // this clear the output buffer and print just the error information
+ // clear the output buffer and print just the error information
ob_start();
try {
@@ -142,12 +197,51 @@ class ApiMain extends ApiBase {
// handler will process and log it.
//
+ $errCode = $this->substituteResultWithError($e);
+
// Error results should not be cached
$this->setCacheMaxAge(0);
+ $headerStr = 'MediaWiki-API-Error: ' . $errCode;
+ if ($e->getCode() === 0)
+ header($headerStr, true);
+ else
+ header($headerStr, true, $e->getCode());
+
+ // Reset and print just the error message
+ ob_clean();
+
+ // If the error occured during printing, do a printer->profileOut()
+ $this->mPrinter->safeProfileOut();
+ $this->printResult(true);
+ }
+
+ // Set the cache expiration at the last moment, as any errors may change the expiration.
+ // if $this->mSquidMaxage == 0, the expiry time is set to the first second of unix epoch
+ $expires = $this->mSquidMaxage == 0 ? 1 : time() + $this->mSquidMaxage;
+ header('Expires: ' . wfTimestamp(TS_RFC2822, $expires));
+ header('Cache-Control: s-maxage=' . $this->mSquidMaxage . ', must-revalidate, max-age=0');
+
+ if($this->mPrinter->getIsHtml())
+ echo wfReportTime();
+
+ ob_end_flush();
+ }
+
+ /**
+ * Replace the result data with the information about an exception.
+ * Returns the error code
+ */
+ protected function substituteResultWithError($e) {
+
// Printer may not be initialized if the extractRequestParams() fails for the main module
if (!isset ($this->mPrinter)) {
- $this->mPrinter = $this->createPrinterByName(self :: API_DEFAULT_FORMAT);
+ // The printer has not been created yet. Try to manually get formatter value.
+ $value = $this->getRequest()->getVal('format', self::API_DEFAULT_FORMAT);
+ if (!in_array($value, $this->mFormatNames))
+ $value = self::API_DEFAULT_FORMAT;
+
+ $this->mPrinter = $this->createPrinterByName($value);
if ($this->mPrinter->getNeedsRawData())
$this->getResult()->setRawMode();
}
@@ -157,8 +251,12 @@ class ApiMain extends ApiBase {
// User entered incorrect parameters - print usage screen
//
$errMessage = array (
- 'code' => $e->getCodeString(), 'info' => $e->getMessage());
- ApiResult :: setContent($errMessage, $this->makeHelpMsg());
+ 'code' => $e->getCodeString(),
+ 'info' => $e->getMessage());
+
+ // Only print the help message when this is for the developer, not runtime
+ if ($this->mPrinter->getIsHtml() || $this->mAction == 'help')
+ ApiResult :: setContent($errMessage, $this->makeHelpMsg());
} else {
//
@@ -171,41 +269,24 @@ class ApiMain extends ApiBase {
ApiResult :: setContent($errMessage, "\n\n{$e->getTraceAsString()}\n\n");
}
- $headerStr = 'MediaWiki-API-Error: ' . $errMessage['code'];
- if ($e->getCode() === 0)
- header($headerStr, true);
- else
- header($headerStr, true, $e->getCode());
-
- // Reset and print just the error message
- ob_clean();
$this->getResult()->reset();
$this->getResult()->addValue(null, 'error', $errMessage);
- // If the error occured during printing, do a printer->profileOut()
- $this->mPrinter->safeProfileOut();
- $this->printResult(true);
- }
-
- // Set the cache expiration at the last moment, as any errors may change the expiration.
- // if $this->mSquidMaxage == 0, the expiry time is set to the first second of unix epoch
- $expires = $this->mSquidMaxage == 0 ? 1 : time() + $this->mSquidMaxage;
- header('Expires: ' . wfTimestamp(TS_RFC2822, $expires));
- header('Cache-Control: s-maxage=' . $this->mSquidMaxage . ', must-revalidate, max-age=0');
-
- ob_end_flush();
+ return $errMessage['code'];
}
/**
* Execute the actual module, without any error handling
*/
protected function executeAction() {
- $action = $format = $version = null;
- extract($this->extractRequestParams());
- $this->mShowVersions = $version;
+
+ $params = $this->extractRequestParams();
+
+ $this->mShowVersions = $params['version'];
+ $this->mAction = $params['action'];
// Instantiate the module requested by the user
- $module = new $this->mModules[$action] ($this, $action);
+ $module = new $this->mModules[$this->mAction] ($this, $this->mAction);
if (!$this->mInternalMode) {
@@ -213,7 +294,7 @@ class ApiMain extends ApiBase {
$this->mPrinter = $module->getCustomPrinter();
if (is_null($this->mPrinter)) {
// Create an appropriate printer
- $this->mPrinter = $this->createPrinterByName($format);
+ $this->mPrinter = $this->createPrinterByName($params['format']);
}
if ($this->mPrinter->getNeedsRawData())
@@ -232,7 +313,7 @@ class ApiMain extends ApiBase {
}
/**
- * Internal printer
+ * Print results using the current printer
*/
protected function printResult($isError) {
$printer = $this->mPrinter;
@@ -243,6 +324,9 @@ class ApiMain extends ApiBase {
$printer->profileOut();
}
+ /**
+ * See ApiBase for description.
+ */
protected function getAllowedParams() {
return array (
'format' => array (
@@ -257,6 +341,9 @@ class ApiMain extends ApiBase {
);
}
+ /**
+ * See ApiBase for description.
+ */
protected function getParamDescription() {
return array (
'format' => 'The format of the output',
@@ -265,24 +352,44 @@ class ApiMain extends ApiBase {
);
}
+ /**
+ * See ApiBase for description.
+ */
protected function getDescription() {
return array (
'',
- 'This API allows programs to access various functions of MediaWiki software.',
- 'For more details see API Home Page @ http://meta.wikimedia.org/wiki/API',
'',
- 'Status: ALPHA -- all features shown on this page should be working,',
- ' but the API is still in active development, and may change at any time.',
- ' Make sure you monitor changes to this page, wikitech-l mailing list,',
- ' or the source code in the includes/api directory for any changes.',
- ''
+ '******************************************************************',
+ '** **',
+ '** This is an auto-generated MediaWiki API documentation page **',
+ '** **',
+ '** Documentation and Examples: **',
+ '** http://www.mediawiki.org/wiki/API **',
+ '** **',
+ '******************************************************************',
+ '',
+ 'Status: All features shown on this page should be working, but the API',
+ ' is still in active development, and may change at any time.',
+ ' Make sure to monitor our mailing list for any updates.',
+ '',
+ 'Documentation: http://www.mediawiki.org/wiki/API',
+ 'Mailing list: http://lists.wikimedia.org/mailman/listinfo/mediawiki-api',
+ 'Bugs & Requests: http://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts',
+ '',
+ '',
+ '',
+ '',
+ '',
);
}
+ /**
+ * Returns an array of strings with credits for the API
+ */
protected function getCredits() {
return array(
- 'This API is being implemented by Yuri Astrakhan [[User:Yurik]] / FirstnameLastname@gmail.com',
- 'Please leave your comments and suggestions at http://meta.wikimedia.org/wiki/API'
+ 'This API is being implemented by Yuri Astrakhan [[User:Yurik]] / <Firstname><Lastname>@gmail.com',
+ 'Please leave your comments and suggestions at http://www.mediawiki.org/wiki/API'
);
}
@@ -297,8 +404,8 @@ class ApiMain extends ApiBase {
$astriks = str_repeat('*** ', 10);
$msg .= "\n\n$astriks Modules $astriks\n\n";
foreach( $this->mModules as $moduleName => $unused ) {
- $msg .= "* action=$moduleName *";
$module = new $this->mModules[$moduleName] ($this, $moduleName);
+ $msg .= self::makeHelpMsgHeader($module, 'action');
$msg2 = $module->makeHelpMsg();
if ($msg2 !== false)
$msg .= $msg2;
@@ -307,8 +414,8 @@ class ApiMain extends ApiBase {
$msg .= "\n$astriks Formats $astriks\n\n";
foreach( $this->mFormats as $formatName => $unused ) {
- $msg .= "* format=$formatName *";
$module = $this->createPrinterByName($formatName);
+ $msg .= self::makeHelpMsgHeader($module, 'format');
$msg2 = $module->makeHelpMsg();
if ($msg2 !== false)
$msg .= $msg2;
@@ -321,7 +428,21 @@ class ApiMain extends ApiBase {
return $msg;
}
+ public static function makeHelpMsgHeader($module, $paramName) {
+ $modulePrefix = $module->getModulePrefix();
+ if (!empty($modulePrefix))
+ $modulePrefix = "($modulePrefix) ";
+
+ return "* $paramName={$module->getModuleName()} $modulePrefix*";
+ }
+
private $mIsBot = null;
+
+ private $mIsSysop = null;
+
+ /**
+ * Returns true if the currently logged in user is a bot, false otherwise
+ */
public function isBot() {
if (!isset ($this->mIsBot)) {
global $wgUser;
@@ -329,24 +450,69 @@ class ApiMain extends ApiBase {
}
return $this->mIsBot;
}
+
+ /**
+ * Similar to isBot(), this method returns true if the logged in user is
+ * a sysop, and false if not.
+ */
+ public function isSysop() {
+ if (!isset ($this->mIsSysop)) {
+ global $wgUser;
+ $this->mIsSysop = in_array( 'sysop', $wgUser->getGroups());
+ }
+
+ return $this->mIsSysop;
+ }
public function getShowVersions() {
return $this->mShowVersions;
}
+ /**
+ * Returns the version information of this file, plus it includes
+ * the versions for all files that are not callable proper API modules
+ */
public function getVersion() {
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiMain.php 21402 2007-04-20 08:55:14Z nickj $';
+ $vers[] = 'MediaWiki ' . SpecialVersion::getVersion();
+ $vers[] = __CLASS__ . ': $Id: ApiMain.php 25364 2007-08-31 15:23:48Z tstarling $';
$vers[] = ApiBase :: getBaseVersion();
$vers[] = ApiFormatBase :: getBaseVersion();
$vers[] = ApiQueryBase :: getBaseVersion();
$vers[] = ApiFormatFeedWrapper :: getVersion(); // not accessible with format=xxx
return $vers;
}
+
+ /**
+ * Add or overwrite a module in this ApiMain instance. Intended for use by extending
+ * classes who wish to add their own modules to their lexicon or override the
+ * behavior of inherent ones.
+ *
+ * @access protected
+ * @param $mdlName String The identifier for this module.
+ * @param $mdlClass String The class where this module is implemented.
+ */
+ protected function addModule( $mdlName, $mdlClass ) {
+ $this->mModules[$mdlName] = $mdlClass;
+ }
+
+ /**
+ * Add or overwrite an output format for this ApiMain. Intended for use by extending
+ * classes who wish to add to or modify current formatters.
+ *
+ * @access protected
+ * @param $fmtName The identifier for this format.
+ * @param $fmtClass The class implementing this format.
+ */
+ protected function addFormat( $fmtName, $fmtClass ) {
+ $this->mFormats[$fmtName] = $fmtClass;
+ }
}
/**
* This exception will be thrown when dieUsage is called to stop module execution.
+ * The exception handling code will print a help screen explaining how this API may be used.
+ *
* @addtogroup API
*/
class UsageException extends Exception {
@@ -364,4 +530,4 @@ class UsageException extends Exception {
return "{$this->getCodeString()}: {$this->getMessage()}";
}
}
-?>
+
diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php
index 77f8b889..8484b163 100644
--- a/includes/api/ApiOpenSearch.php
+++ b/includes/api/ApiOpenSearch.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -42,8 +42,8 @@ class ApiOpenSearch extends ApiBase {
}
public function execute() {
- $search = null;
- extract($this->ExtractRequestParams());
+ $params = $this->extractRequestParams();
+ $search = $params['search'];
// Open search results may be stored for a very long time
$this->getMain()->setCacheMaxAge(1200);
@@ -53,7 +53,7 @@ class ApiOpenSearch extends ApiBase {
return; // Return empty result
// Prepare nested request
- $params = new FauxRequest(array (
+ $req = new FauxRequest(array (
'action' => 'query',
'list' => 'allpages',
'apnamespace' => $title->getNamespace(),
@@ -62,7 +62,7 @@ class ApiOpenSearch extends ApiBase {
));
// Execute
- $module = new ApiMain($params);
+ $module = new ApiMain($req);
$module->execute();
// Get resulting data
@@ -105,7 +105,7 @@ class ApiOpenSearch extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiOpenSearch.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiOpenSearch.php 24099 2007-07-15 00:52:35Z yurik $';
}
}
-?>
+
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index dea87b88..185c0c59 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,14 +29,25 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This class contains a list of pages that the client has requested.
+ * Initially, when the client passes in titles=, pageids=, or revisions= parameter,
+ * an instance of the ApiPageSet class will normalize titles,
+ * determine if the pages/revisions exist, and prefetch any additional data page data requested.
+ *
+ * When generator is used, the result of the generator will become the input for the
+ * second instance of this class, and all subsequent actions will go use the second instance
+ * for all their work.
+ *
* @addtogroup API
*/
class ApiPageSet extends ApiQueryBase {
private $mAllPages; // [ns][dbkey] => page_id or 0 when missing
- private $mTitles, $mGoodTitles, $mMissingTitles, $mMissingPageIDs, $mRedirectTitles, $mNormalizedTitles;
+ private $mTitles, $mGoodTitles, $mMissingTitles, $mMissingPageIDs, $mRedirectTitles;
+ private $mNormalizedTitles, $mInterwikiTitles;
private $mResolveRedirects, $mPendingRedirectIDs;
private $mGoodRevIDs, $mMissingRevIDs;
+ private $mFakePageId;
private $mRequestedPageFields;
@@ -50,6 +61,7 @@ class ApiPageSet extends ApiQueryBase {
$this->mMissingPageIDs = array ();
$this->mRedirectTitles = array ();
$this->mNormalizedTitles = array ();
+ $this->mInterwikiTitles = array ();
$this->mGoodRevIDs = array();
$this->mMissingRevIDs = array();
@@ -57,6 +69,8 @@ class ApiPageSet extends ApiQueryBase {
$this->mResolveRedirects = $resolveRedirects;
if($resolveRedirects)
$this->mPendingRedirectIDs = array();
+
+ $this->mFakePageId = -1;
}
public function isResolvingRedirects() {
@@ -88,7 +102,16 @@ class ApiPageSet extends ApiQueryBase {
if ($this->mResolveRedirects)
$pageFlds['page_is_redirect'] = null;
- return array_keys(array_merge($pageFlds, $this->mRequestedPageFields));
+ $pageFlds = array_merge($pageFlds, $this->mRequestedPageFields);
+ return array_keys($pageFlds);
+ }
+
+ /**
+ * Returns an array [ns][dbkey] => page_id for all requested titles
+ * page_id is a unique negative number in case title was not found
+ */
+ public function getAllTitlesByNamespace() {
+ return $this->mAllPages;
}
/**
@@ -123,6 +146,7 @@ class ApiPageSet extends ApiQueryBase {
/**
* Title objects that were NOT found in the database.
+ * The array's index will be negative for each item
* @return array of Title objects
*/
public function getMissingTitles() {
@@ -155,6 +179,15 @@ class ApiPageSet extends ApiQueryBase {
}
/**
+ * Get a list of interwiki titles - maps the title given
+ * with to the interwiki prefix.
+ * @return array raw_prefixed_title (string) => interwiki_prefix (string)
+ */
+ public function getInterwikiTitles() {
+ return $this->mInterwikiTitles;
+ }
+
+ /**
* Get the list of revision IDs (requested with revids= parameter)
* @return array revID (int) => pageID (int)
*/
@@ -233,7 +266,6 @@ class ApiPageSet extends ApiQueryBase {
*/
public function populateFromPageIDs($pageIDs) {
$this->profileIn();
- $pageIDs = array_map('intval', $pageIDs); // paranoia
$this->initFromPageIds($pageIDs);
$this->profileOut();
}
@@ -265,22 +297,18 @@ class ApiPageSet extends ApiQueryBase {
// Store Title object in various data structures
$title = Title :: makeTitle($row->page_namespace, $row->page_title);
- // skip any pages that user has no rights to read
- if ($title->userCanRead()) {
-
- $pageId = intval($row->page_id);
- $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
- $this->mTitles[] = $title;
-
- if ($this->mResolveRedirects && $row->page_is_redirect == '1') {
- $this->mPendingRedirectIDs[$pageId] = $title;
- } else {
- $this->mGoodTitles[$pageId] = $title;
- }
-
- foreach ($this->mRequestedPageFields as $fieldName => & $fieldValues)
- $fieldValues[$pageId] = $row-> $fieldName;
+ $pageId = intval($row->page_id);
+ $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
+ $this->mTitles[] = $title;
+
+ if ($this->mResolveRedirects && $row->page_is_redirect == '1') {
+ $this->mPendingRedirectIDs[$pageId] = $title;
+ } else {
+ $this->mGoodTitles[$pageId] = $title;
}
+
+ foreach ($this->mRequestedPageFields as $fieldName => & $fieldValues)
+ $fieldValues[$pageId] = $row-> $fieldName;
}
public function finishPageSetGeneration() {
@@ -306,7 +334,7 @@ class ApiPageSet extends ApiQueryBase {
private function initFromTitles($titles) {
// Get validated and normalized title objects
- $linkBatch = $this->processTitlesStrArray($titles);
+ $linkBatch = $this->processTitlesArray($titles);
if($linkBatch->isEmpty())
return;
@@ -328,7 +356,8 @@ class ApiPageSet extends ApiQueryBase {
private function initFromPageIds($pageids) {
if(empty($pageids))
return;
-
+
+ $pageids = array_map('intval', $pageids); // paranoia
$set = array (
'page_id' => $pageids
);
@@ -386,8 +415,9 @@ class ApiPageSet extends ApiQueryBase {
foreach ($remaining as $ns => $dbkeys) {
foreach ( $dbkeys as $dbkey => $unused ) {
$title = Title :: makeTitle($ns, $dbkey);
- $this->mMissingTitles[] = $title;
- $this->mAllPages[$ns][$dbkey] = 0;
+ $this->mAllPages[$ns][$dbkey] = $this->mFakePageId;
+ $this->mMissingTitles[$this->mFakePageId] = $title;
+ $this->mFakePageId--;
$this->mTitles[] = $title;
}
}
@@ -536,39 +566,46 @@ class ApiPageSet extends ApiQueryBase {
/**
* Given an array of title strings, convert them into Title objects.
+ * Alternativelly, an array of Title objects may be given.
* This method validates access rights for the title,
* and appends normalization values to the output.
*
* @return LinkBatch of title objects.
*/
- private function processTitlesStrArray($titles) {
+ private function processTitlesArray($titles) {
$linkBatch = new LinkBatch();
- foreach ($titles as $titleString) {
- $titleObj = Title :: newFromText($titleString);
-
- // Validation
+ foreach ($titles as $title) {
+
+ $titleObj = is_string($title) ? Title :: newFromText($title) : $title;
if (!$titleObj)
- $this->dieUsage("bad title $titleString", 'invalidtitle');
- if ($titleObj->getNamespace() < 0)
- $this->dieUsage("No support for special page $titleString has been implemented", 'unsupportednamespace');
- if (!$titleObj->userCanRead())
- $this->dieUsage("No read permission for $titleString", 'titleaccessdenied');
+ $this->dieUsage("bad title", 'invalidtitle');
- $linkBatch->addObj($titleObj);
+ $iw = $titleObj->getInterwiki();
+ if (!empty($iw)) {
+ // This title is an interwiki link.
+ $this->mInterwikiTitles[$titleObj->getPrefixedText()] = $iw;
+ } else {
+ // Validation
+ if ($titleObj->getNamespace() < 0)
+ $this->dieUsage("No support for special pages has been implemented", 'unsupportednamespace');
+
+ $linkBatch->addObj($titleObj);
+ }
+
// Make sure we remember the original title that was given to us
// This way the caller can correlate new titles with the originally requested,
// i.e. namespace is localized or capitalization is different
- if ($titleString !== $titleObj->getPrefixedText()) {
- $this->mNormalizedTitles[$titleString] = $titleObj->getPrefixedText();
+ if (is_string($title) && $title !== $titleObj->getPrefixedText()) {
+ $this->mNormalizedTitles[$title] = $titleObj->getPrefixedText();
}
}
return $linkBatch;
}
-
+
protected function getAllowedParams() {
return array (
'titles' => array (
@@ -594,7 +631,7 @@ class ApiPageSet extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiPageSet.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiPageSet.php 24935 2007-08-20 08:13:16Z nickj $';
}
}
-?>
+
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index 6ee05085..76dbb338 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,49 +29,67 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This is the main query class. It behaves similar to ApiMain: based on the parameters given,
+ * it will create a list of titles to work on (an instance of the ApiPageSet object)
+ * instantiate and execute various property/list/meta modules,
+ * and assemble all resulting data into a single ApiResult object.
+ *
+ * In the generator mode, a generator will be first executed to populate a second ApiPageSet object,
+ * and that object will be used for all subsequent modules.
+ *
* @addtogroup API
*/
class ApiQuery extends ApiBase {
private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames;
private $mPageSet;
+ private $params, $redirect;
private $mQueryPropModules = array (
'info' => 'ApiQueryInfo',
- 'revisions' => 'ApiQueryRevisions'
+ 'revisions' => 'ApiQueryRevisions',
+ 'links' => 'ApiQueryLinks',
+ 'langlinks' => 'ApiQueryLangLinks',
+ 'images' => 'ApiQueryImages',
+ 'imageinfo' => 'ApiQueryImageInfo',
+ 'templates' => 'ApiQueryLinks',
+ 'categories' => 'ApiQueryCategories',
+ 'extlinks' => 'ApiQueryExternalLinks',
);
- // 'categories' => 'ApiQueryCategories',
- // 'imageinfo' => 'ApiQueryImageinfo',
- // 'langlinks' => 'ApiQueryLanglinks',
- // 'links' => 'ApiQueryLinks',
- // 'templates' => 'ApiQueryTemplates',
private $mQueryListModules = array (
'allpages' => 'ApiQueryAllpages',
- 'logevents' => 'ApiQueryLogEvents',
- 'watchlist' => 'ApiQueryWatchlist',
- 'recentchanges' => 'ApiQueryRecentChanges',
+ 'alllinks' => 'ApiQueryAllLinks',
+ 'allusers' => 'ApiQueryAllUsers',
'backlinks' => 'ApiQueryBacklinks',
+ 'categorymembers' => 'ApiQueryCategoryMembers',
'embeddedin' => 'ApiQueryBacklinks',
- 'imagelinks' => 'ApiQueryBacklinks',
- 'usercontribs' => 'ApiQueryContributions'
+ 'imageusage' => 'ApiQueryBacklinks',
+ 'logevents' => 'ApiQueryLogEvents',
+ 'recentchanges' => 'ApiQueryRecentChanges',
+ 'search' => 'ApiQuerySearch',
+ 'usercontribs' => 'ApiQueryContributions',
+ 'watchlist' => 'ApiQueryWatchlist',
+ 'exturlusage' => 'ApiQueryExtLinksUsage',
);
- // 'categorymembers' => 'ApiQueryCategorymembers',
- // 'embeddedin' => 'ApiQueryEmbeddedin',
- // 'imagelinks' => 'ApiQueryImagelinks',
- // 'recentchanges' => 'ApiQueryRecentchanges',
- // 'users' => 'ApiQueryUsers',
- // 'watchlist' => 'ApiQueryWatchlist',
private $mQueryMetaModules = array (
- 'siteinfo' => 'ApiQuerySiteinfo'
+ 'siteinfo' => 'ApiQuerySiteinfo',
+ 'userinfo' => 'ApiQueryUserInfo',
);
- // 'userinfo' => 'ApiQueryUserinfo',
private $mSlaveDB = null;
+ private $mNamedDB = array();
public function __construct($main, $action) {
parent :: __construct($main, $action);
+
+ // Allow custom modules to be added in LocalSettings.php
+ global $wgApiQueryPropModules, $wgApiQueryListModules, $wgApiQueryMetaModules;
+ self :: appendUserModules($this->mQueryPropModules, $wgApiQueryPropModules);
+ self :: appendUserModules($this->mQueryListModules, $wgApiQueryListModules);
+ self :: appendUserModules($this->mQueryMetaModules, $wgApiQueryMetaModules);
+
$this->mPropModuleNames = array_keys($this->mQueryPropModules);
$this->mListModuleNames = array_keys($this->mQueryListModules);
$this->mMetaModuleNames = array_keys($this->mQueryMetaModules);
@@ -81,6 +99,20 @@ class ApiQuery extends ApiBase {
$this->mAllowedGenerators = array_merge($this->mListModuleNames, $this->mPropModuleNames);
}
+ /**
+ * Helper function to append any add-in modules to the list
+ */
+ private static function appendUserModules(&$modules, $newModules) {
+ if (is_array( $newModules )) {
+ foreach ( $newModules as $moduleName => $moduleClass) {
+ $modules[$moduleName] = $moduleClass;
+ }
+ }
+ }
+
+ /**
+ * Gets a default slave database connection object
+ */
public function getDB() {
if (!isset ($this->mSlaveDB)) {
$this->profileDBIn();
@@ -90,6 +122,24 @@ class ApiQuery extends ApiBase {
return $this->mSlaveDB;
}
+ /**
+ * Get the query database connection with the given name.
+ * If no such connection has been requested before, it will be created.
+ * Subsequent calls with the same $name will return the same connection
+ * as the first, regardless of $db or $groups new values.
+ */
+ public function getNamedDB($name, $db, $groups) {
+ if (!array_key_exists($name, $this->mNamedDB)) {
+ $this->profileDBIn();
+ $this->mNamedDB[$name] = wfGetDB($db, $groups);
+ $this->profileDBOut();
+ }
+ return $this->mNamedDB[$name];
+ }
+
+ /**
+ * Gets the set of pages the user has requested (or generated)
+ */
public function getPageSet() {
return $this->mPageSet;
}
@@ -105,42 +155,33 @@ class ApiQuery extends ApiBase {
* #5 Execute all requested modules
*/
public function execute() {
- $prop = $list = $meta = $generator = $redirects = null;
- extract($this->extractRequestParams());
-
+
+ $this->params = $this->extractRequestParams();
+ $this->redirects = $this->params['redirects'];
+
//
// Create PageSet
//
- $this->mPageSet = new ApiPageSet($this, $redirects);
-
- // Instantiate required modules
- $modules = array ();
- if (isset ($prop))
- foreach ($prop as $moduleName)
- $modules[] = new $this->mQueryPropModules[$moduleName] ($this, $moduleName);
- if (isset ($list))
- foreach ($list as $moduleName)
- $modules[] = new $this->mQueryListModules[$moduleName] ($this, $moduleName);
- if (isset ($meta))
- foreach ($meta as $moduleName)
- $modules[] = new $this->mQueryMetaModules[$moduleName] ($this, $moduleName);
-
- // Modules may optimize data requests through the $this->getPageSet() object
- // Execute all requested modules.
- foreach ($modules as $module) {
- $module->requestExtraData();
- }
+ $this->mPageSet = new ApiPageSet($this, $this->redirects);
//
- // If given, execute generator to substitute user supplied data with generated data.
+ // Instantiate requested modules
//
- if (isset ($generator))
- $this->executeGeneratorModule($generator, $redirects);
+ $modules = array ();
+ $this->InstantiateModules($modules, 'prop', $this->mQueryPropModules);
+ $this->InstantiateModules($modules, 'list', $this->mQueryListModules);
+ $this->InstantiateModules($modules, 'meta', $this->mQueryMetaModules);
//
- // Populate page information for the given pageSet
+ // If given, execute generator to substitute user supplied data with generated data.
//
- $this->mPageSet->execute();
+ if (isset ($this->params['generator'])) {
+ $this->executeGeneratorModule($this->params['generator'], $modules);
+ } else {
+ // Append custom fields and populate page/revision information
+ $this->addCustomFldsToPageSet($modules, $this->mPageSet);
+ $this->mPageSet->execute();
+ }
//
// Record page information (title, namespace, if exists, etc)
@@ -156,7 +197,33 @@ class ApiQuery extends ApiBase {
$module->profileOut();
}
}
+
+ /**
+ * Query modules may optimize data requests through the $this->getPageSet() object
+ * by adding extra fields from the page table.
+ * This function will gather all the extra request fields from the modules.
+ */
+ private function addCustomFldsToPageSet($modules, $pageSet) {
+ // Query all requested modules.
+ foreach ($modules as $module) {
+ $module->requestExtraData($pageSet);
+ }
+ }
+
+ /**
+ * Create instances of all modules requested by the client
+ */
+ private function InstantiateModules(&$modules, $param, $moduleList) {
+ $list = $this->params[$param];
+ if (isset ($list))
+ foreach ($list as $moduleName)
+ $modules[] = new $moduleList[$moduleName] ($this, $moduleName);
+ }
+ /**
+ * Appends an element for each page in the current pageSet with the most general
+ * information (id, title), plus any title normalizations and missing title/pageids/revids.
+ */
private function outputGeneralPageInfo() {
$pageSet = $this->getPageSet();
@@ -175,7 +242,21 @@ class ApiQuery extends ApiBase {
$result->setIndexedTagName($normValues, 'n');
$result->addValue('query', 'normalized', $normValues);
}
+
+ // Interwiki titles
+ $intrwValues = array ();
+ foreach ($pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr) {
+ $intrwValues[] = array (
+ 'title' => $rawTitleStr,
+ 'iw' => $interwikiStr
+ );
+ }
+ if (!empty ($intrwValues)) {
+ $result->setIndexedTagName($intrwValues, 'i');
+ $result->addValue('query', 'interwiki', $intrwValues);
+ }
+
// Show redirect information
$redirValues = array ();
foreach ($pageSet->getRedirectTitles() as $titleStrFrom => $titleStrTo) {
@@ -211,10 +292,11 @@ class ApiQuery extends ApiBase {
$pages = array ();
// Report any missing titles
- $fakepageid = -1;
- foreach ($pageSet->getMissingTitles() as $title) {
- $pages[$fakepageid--] = array (
- 'ns' => $title->getNamespace(), 'title' => $title->getPrefixedText(), 'missing' => '');
+ foreach ($pageSet->getMissingTitles() as $fakeId => $title) {
+ $vals = array();
+ ApiQueryBase :: addTitleInfo($vals, $title);
+ $vals['missing'] = '';
+ $pages[$fakeId] = $vals;
}
// Report any missing page ids
@@ -227,32 +309,43 @@ class ApiQuery extends ApiBase {
// Output general page information for found titles
foreach ($pageSet->getGoodTitles() as $pageid => $title) {
- $pages[$pageid] = array (
- 'pageid' => $pageid,
- 'ns' => $title->getNamespace(), 'title' => $title->getPrefixedText());
+ $vals = array();
+ $vals['pageid'] = $pageid;
+ ApiQueryBase :: addTitleInfo($vals, $title);
+ $pages[$pageid] = $vals;
}
if (!empty ($pages)) {
+
+ if ($this->params['indexpageids']) {
+ $pageIDs = array_keys($pages);
+ // json treats all map keys as strings - converting to match
+ $pageIDs = array_map('strval', $pageIDs);
+ $result->setIndexedTagName($pageIDs, 'id');
+ $result->addValue('query', 'pageids', $pageIDs);
+ }
+
$result->setIndexedTagName($pages, 'page');
$result->addValue('query', 'pages', $pages);
}
}
- protected function executeGeneratorModule($generatorName, $redirects) {
+ /**
+ * For generator mode, execute generator, and use its output as new pageSet
+ */
+ protected function executeGeneratorModule($generatorName, $modules) {
// Find class that implements requested generator
if (isset ($this->mQueryListModules[$generatorName])) {
$className = $this->mQueryListModules[$generatorName];
- }
- elseif (isset ($this->mQueryPropModules[$generatorName])) {
+ } elseif (isset ($this->mQueryPropModules[$generatorName])) {
$className = $this->mQueryPropModules[$generatorName];
} else {
ApiBase :: dieDebug(__METHOD__, "Unknown generator=$generatorName");
}
- // Use current pageset as the result, and create a new one just for the generator
- $resultPageSet = $this->mPageSet;
- $this->mPageSet = new ApiPageSet($this, $redirects);
+ // Generator results
+ $resultPageSet = new ApiPageSet($this, $this->redirects);
// Create and execute the generator
$generator = new $className ($this, $generatorName);
@@ -260,9 +353,12 @@ class ApiQuery extends ApiBase {
$this->dieUsage("Module $generatorName cannot be used as a generator", "badgenerator");
$generator->setGeneratorMode();
- $generator->requestExtraData();
- // execute current pageSet to get the data for the generator module
+ // Add any additional fields modules may need
+ $generator->requestExtraData($this->mPageSet);
+ $this->addCustomFldsToPageSet($modules, $resultPageSet);
+
+ // Populate page information with the original user input
$this->mPageSet->execute();
// populate resultPageSet with the generator output
@@ -275,6 +371,10 @@ class ApiQuery extends ApiBase {
$this->mPageSet = $resultPageSet;
}
+ /**
+ * Returns the list of allowed parameters for this module.
+ * Qurey module also lists all ApiPageSet parameters as its own.
+ */
protected function getAllowedParams() {
return array (
'prop' => array (
@@ -292,7 +392,8 @@ class ApiQuery extends ApiBase {
'generator' => array (
ApiBase :: PARAM_TYPE => $this->mAllowedGenerators
),
- 'redirects' => false
+ 'redirects' => false,
+ 'indexpageids' => false,
);
}
@@ -301,12 +402,12 @@ class ApiQuery extends ApiBase {
*/
public function makeHelpMsg() {
- // Use parent to make default message for the query module
- $msg = parent :: makeHelpMsg();
+ $msg = '';
// Make sure the internal object is empty
// (just in case a sub-module decides to optimize during instantiation)
$this->mPageSet = null;
+ $this->mAllowedGenerators = array(); // Will be repopulated
$astriks = str_repeat('--- ', 8);
$msg .= "\n$astriks Query: Prop $astriks\n\n";
@@ -316,21 +417,32 @@ class ApiQuery extends ApiBase {
$msg .= "\n$astriks Query: Meta $astriks\n\n";
$msg .= $this->makeHelpMsgHelper($this->mQueryMetaModules, 'meta');
+ // Perform the base call last because the $this->mAllowedGenerators
+ // will be updated inside makeHelpMsgHelper()
+ // Use parent to make default message for the query module
+ $msg = parent :: makeHelpMsg() . $msg;
+
return $msg;
}
+ /**
+ * For all modules in $moduleList, generate help messages and join them together
+ */
private function makeHelpMsgHelper($moduleList, $paramName) {
$moduleDscriptions = array ();
foreach ($moduleList as $moduleName => $moduleClass) {
- $msg = "* $paramName=$moduleName *";
$module = new $moduleClass ($this, $moduleName, null);
+
+ $msg = ApiMain::makeHelpMsgHeader($module, $paramName);
$msg2 = $module->makeHelpMsg();
if ($msg2 !== false)
$msg .= $msg2;
- if ($module instanceof ApiQueryGeneratorBase)
+ if ($module instanceof ApiQueryGeneratorBase) {
+ $this->mAllowedGenerators[] = $moduleName;
$msg .= "Generator:\n This module may be used as a generator\n";
+ }
$moduleDscriptions[] = $msg;
}
@@ -351,7 +463,8 @@ class ApiQuery extends ApiBase {
'list' => 'Which lists to get',
'meta' => 'Which meta data to get about the site',
'generator' => 'Use the output of a list as the input for other prop/list/meta items',
- 'redirects' => 'Automatically resolve redirects'
+ 'redirects' => 'Automatically resolve redirects',
+ 'indexpageids' => 'Include an additional pageids section listing all returned page IDs.'
);
}
@@ -372,9 +485,9 @@ class ApiQuery extends ApiBase {
public function getVersion() {
$psModule = new ApiPageSet($this);
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiQuery.php 21402 2007-04-20 08:55:14Z nickj $';
+ $vers[] = __CLASS__ . ': $Id: ApiQuery.php 24494 2007-07-31 17:53:37Z yurik $';
$vers[] = $psModule->getVersion();
return $vers;
}
}
-?>
+
diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php
new file mode 100644
index 00000000..17f24b65
--- /dev/null
+++ b/includes/api/ApiQueryAllLinks.php
@@ -0,0 +1,179 @@
+<?php
+
+/*
+ * Created on July 7, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ('ApiQueryBase.php');
+}
+
+/**
+ * Query module to enumerate links from all pages together.
+ *
+ * @addtogroup API
+ */
+class ApiQueryAllLinks extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'al');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ $db = $this->getDB();
+ $params = $this->extractRequestParams();
+
+ $prop = array_flip($params['prop']);
+ $fld_ids = isset($prop['ids']);
+ $fld_title = isset($prop['title']);
+
+ if ($params['unique']) {
+ if (!is_null($resultPageSet))
+ $this->dieUsage($this->getModuleName() . ' cannot be used as a generator in unique links mode', 'params');
+ if ($fld_ids)
+ $this->dieUsage($this->getModuleName() . ' cannot return corresponding page ids in unique links mode', 'params');
+ $this->addOption('DISTINCT');
+ }
+
+ $this->addTables('pagelinks');
+ $this->addWhereFld('pl_namespace', $params['namespace']);
+
+ if (!is_null($params['from']))
+ $this->addWhere('pl_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
+ if (isset ($params['prefix']))
+ $this->addWhere("pl_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+
+ if (is_null($resultPageSet)) {
+ $this->addFields(array (
+ 'pl_namespace',
+ 'pl_title'
+ ));
+ $this->addFieldsIf('pl_from', $fld_ids);
+ } else {
+ $this->addFields('pl_from');
+ $pageids = array();
+ }
+
+ $this->addOption('USE INDEX', 'pl_namespace');
+ $limit = $params['limit'];
+ $this->addOption('LIMIT', $limit+1);
+ $this->addOption('ORDER BY', 'pl_namespace, pl_title');
+
+ $res = $this->select(__METHOD__);
+
+ $data = array ();
+ $count = 0;
+ while ($row = $db->fetchObject($res)) {
+ if (++ $count > $limit) {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ // TODO: Security issue - if the user has no right to view next title, it will still be shown
+ $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->pl_title));
+ break;
+ }
+
+ if (is_null($resultPageSet)) {
+ $vals = array();
+ if ($fld_ids)
+ $vals['fromid'] = intval($row->pl_from);
+ if ($fld_title) {
+ $title = Title :: makeTitle($row->pl_namespace, $row->pl_title);
+ $vals['ns'] = intval($title->getNamespace());
+ $vals['title'] = $title->getPrefixedText();
+ }
+ $data[] = $vals;
+ } else {
+ $pageids[] = $row->pl_from;
+ }
+ }
+ $db->freeResult($res);
+
+ if (is_null($resultPageSet)) {
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, 'l');
+ $result->addValue('query', $this->getModuleName(), $data);
+ } else {
+ $resultPageSet->populateFromPageIDs($pageids);
+ }
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'from' => null,
+ 'prefix' => null,
+ 'unique' => false,
+ 'prop' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_DFLT => 'title',
+ ApiBase :: PARAM_TYPE => array (
+ 'ids',
+ 'title'
+ )
+ ),
+ 'namespace' => array (
+ ApiBase :: PARAM_DFLT => 0,
+ ApiBase :: PARAM_TYPE => 'namespace'
+ ),
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'from' => 'The page title to start enumerating from.',
+ 'prefix' => 'Search for all page titles that begin with this value.',
+ 'unique' => 'Only show unique links. Cannot be used with generator or prop=ids',
+ 'prop' => 'What pieces of information to include',
+ 'namespace' => 'The namespace to enumerate.',
+ 'limit' => 'How many total links to return.'
+ );
+ }
+
+ protected function getDescription() {
+ return 'Enumerate all links that point to a given namespace';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=alllinks&alunique&alfrom=B',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryAllLinks.php 24453 2007-07-30 08:09:15Z yurik $';
+ }
+}
diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php
new file mode 100644
index 00000000..92bcc1a1
--- /dev/null
+++ b/includes/api/ApiQueryAllUsers.php
@@ -0,0 +1,204 @@
+<?php
+
+/*
+ * Created on July 7, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ('ApiQueryBase.php');
+}
+
+/**
+ * Query module to enumerate all registered users.
+ *
+ * @addtogroup API
+ */
+class ApiQueryAllUsers extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'au');
+ }
+
+ public function execute() {
+ $db = $this->getDB();
+ $params = $this->extractRequestParams();
+
+ $prop = $params['prop'];
+ if (!is_null($prop)) {
+ $prop = array_flip($prop);
+ $fld_editcount = isset($prop['editcount']);
+ $fld_groups = isset($prop['groups']);
+ } else {
+ $fld_editcount = $fld_groups = false;
+ }
+
+ $limit = $params['limit'];
+ $tables = $db->tableName('user');
+
+ if( !is_null( $params['from'] ) )
+ $this->addWhere( 'user_name >= ' . $db->addQuotes( self::keyToTitle( $params['from'] ) ) );
+
+ if( isset( $params['prefix'] ) )
+ $this->addWhere( 'user_name LIKE "' . $db->escapeLike( self::keyToTitle( $params['prefix'] ) ) . '%"' );
+
+ if (!is_null($params['group'])) {
+ // Filter only users that belong to a given group
+ $tblName = $db->tableName('user_groups');
+ $tables = "$tables INNER JOIN $tblName ug1 ON ug1.ug_user=user_id";
+ $this->addWhereFld('ug1.ug_group', $params['group']);
+ }
+
+ if ($fld_groups) {
+ // Show the groups the given users belong to
+ // request more than needed to avoid not getting all rows that belong to one user
+ $groupCount = count(User::getAllGroups());
+ $sqlLimit = $limit+$groupCount+1;
+
+ $tblName = $db->tableName('user_groups');
+ $tables = "$tables LEFT JOIN $tblName ug2 ON ug2.ug_user=user_id";
+ $this->addFields('ug2.ug_group ug_group2');
+ } else {
+ $sqlLimit = $limit+1;
+ }
+
+ $this->addOption('LIMIT', $sqlLimit);
+ $this->addTables($tables);
+
+ $this->addFields('user_name');
+ $this->addFieldsIf('user_editcount', $fld_editcount);
+
+ $this->addOption('ORDER BY', 'user_name');
+
+ $res = $this->select(__METHOD__);
+
+ $data = array ();
+ $count = 0;
+ $lastUserData = false;
+ $lastUser = false;
+ $result = $this->getResult();
+
+ //
+ // This loop keeps track of the last entry.
+ // For each new row, if the new row is for different user then the last, the last entry is added to results.
+ // Otherwise, the group of the new row is appended to the last entry.
+ // The setContinue... is more complex because of this, and takes into account the higher sql limit
+ // to make sure all rows that belong to the same user are received.
+ //
+ while (true) {
+
+ $row = $db->fetchObject($res);
+ $count++;
+
+ if (!$row || $lastUser != $row->user_name) {
+ // Save the last pass's user data
+ if (is_array($lastUserData))
+ $data[] = $lastUserData;
+
+ // No more rows left
+ if (!$row)
+ break;
+
+ if ($count > $limit) {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->user_name));
+ break;
+ }
+
+ // Record new user's data
+ $lastUser = $row->user_name;
+ $lastUserData = array( 'name' => $lastUser );
+ if ($fld_editcount)
+ $lastUserData['editcount'] = intval($row->user_editcount);
+
+ }
+
+ if ($sqlLimit == $count) {
+ // BUG! database contains group name that User::getAllGroups() does not return
+ // TODO: should handle this more gracefully
+ ApiBase :: dieDebug(__METHOD__,
+ 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function');
+ }
+
+ // Add user's group info
+ if ($fld_groups && !is_null($row->ug_group2)) {
+ $lastUserData['groups'][] = $row->ug_group2;
+ $result->setIndexedTagName($lastUserData['groups'], 'g');
+ }
+ }
+
+ $db->freeResult($res);
+
+ $result->setIndexedTagName($data, 'u');
+ $result->addValue('query', $this->getModuleName(), $data);
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'from' => null,
+ 'prefix' => null,
+ 'group' => array(
+ ApiBase :: PARAM_TYPE => User::getAllGroups()
+ ),
+ 'prop' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'editcount',
+ 'groups',
+ )
+ ),
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'from' => 'The user name to start enumerating from.',
+ 'prefix' => 'Search for all page titles that begin with this value.',
+ 'group' => 'Limit users to a given group name',
+ 'prop' => array(
+ 'What pieces of information to include.',
+ '`groups` property uses more server resources and may return fewer results than the limit.'),
+ 'limit' => 'How many total user names to return.',
+ );
+ }
+
+ protected function getDescription() {
+ return 'Enumerate all registered users';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=allusers&aufrom=Y',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryAllUsers.php 24870 2007-08-17 13:01:35Z robchurch $';
+ }
+}
diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php
index 494f7707..d9715b1a 100644
--- a/includes/api/ApiQueryAllpages.php
+++ b/includes/api/ApiQueryAllpages.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * Query module to enumerate all available pages.
+ *
* @addtogroup API
*/
class ApiQueryAllpages extends ApiQueryGeneratorBase {
@@ -50,22 +52,51 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
private function run($resultPageSet = null) {
- wfProfileIn($this->getModuleProfileName() . '-getDB');
$db = $this->getDB();
- wfProfileOut($this->getModuleProfileName() . '-getDB');
-
- wfProfileIn($this->getModuleProfileName() . '-parseParams');
- $limit = $from = $namespace = $filterredir = $prefix = null;
- extract($this->extractRequestParams());
+ $params = $this->extractRequestParams();
+
+ // Page filters
+ if (!$this->addWhereIf('page_is_redirect = 1', $params['filterredir'] === 'redirects'))
+ $this->addWhereIf('page_is_redirect = 0', $params['filterredir'] === 'nonredirects');
+ $this->addWhereFld('page_namespace', $params['namespace']);
+ if (!is_null($params['from']))
+ $this->addWhere('page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
+ if (isset ($params['prefix']))
+ $this->addWhere("page_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+
+ $forceNameTitleIndex = true;
+ if (isset ($params['minsize'])) {
+ $this->addWhere('page_len>=' . intval($params['minsize']));
+ $forceNameTitleIndex = false;
+ }
+
+ if (isset ($params['maxsize'])) {
+ $this->addWhere('page_len<=' . intval($params['maxsize']));
+ $forceNameTitleIndex = false;
+ }
+
+ // Page protection filtering
+ if (isset ($params['prtype'])) {
+ $this->addTables('page_restrictions');
+ $this->addWhere('page_id=pr_page');
+ $this->addWhere('pr_expiry>' . $db->addQuotes($db->timestamp()));
+ $this->addWhereFld('pr_type', $params['prtype']);
+
+ $prlevel = $params['prlevel'];
+ if (!is_null($prlevel) && $prlevel != '' && $prlevel != '*')
+ $this->addWhereFld('pr_level', $prlevel);
+
+ $forceNameTitleIndex = false;
+
+ } else if (isset ($params['prlevel'])) {
+ $this->dieUsage('prlevel may not be used without prtype', 'params');
+ }
+
$this->addTables('page');
- if (!$this->addWhereIf('page_is_redirect = 1', $filterredir === 'redirects'))
- $this->addWhereIf('page_is_redirect = 0', $filterredir === 'nonredirects');
- $this->addWhereFld('page_namespace', $namespace);
- if (isset ($from))
- $this->addWhere('page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($from)));
- if (isset ($prefix))
- $this->addWhere("page_title LIKE '{$db->strencode(ApiQueryBase :: titleToKey($prefix))}%'");
+ if ($forceNameTitleIndex)
+ $this->addOption('USE INDEX', 'name_title');
+
if (is_null($resultPageSet)) {
$this->addFields(array (
@@ -77,29 +108,28 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$this->addFields($resultPageSet->getPageTableFields());
}
- $this->addOption('USE INDEX', 'name_title');
- $this->addOption('LIMIT', $limit +1);
+ $limit = $params['limit'];
+ $this->addOption('LIMIT', $limit+1);
$this->addOption('ORDER BY', 'page_namespace, page_title');
- wfProfileOut($this->getModuleProfileName() . '-parseParams');
-
$res = $this->select(__METHOD__);
- wfProfileIn($this->getModuleProfileName() . '-saveResults');
-
$data = array ();
$count = 0;
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ // TODO: Security issue - if the user has no right to view next title, it will still be shown
$this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->page_title));
break;
}
if (is_null($resultPageSet)) {
- $vals = $this->addRowInfo('page', $row);
- if ($vals)
- $data[intval($row->page_id)] = $vals;
+ $title = Title :: makeTitle($row->page_namespace, $row->page_title);
+ $data[] = array(
+ 'pageid' => intval($row->page_id),
+ 'ns' => intval($title->getNamespace()),
+ 'title' => $title->getPrefixedText());
} else {
$resultPageSet->processDbRow($row);
}
@@ -111,17 +141,17 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$result->setIndexedTagName($data, 'p');
$result->addValue('query', $this->getModuleName(), $data);
}
-
- wfProfileOut($this->getModuleProfileName() . '-saveResults');
}
protected function getAllowedParams() {
+ global $wgRestrictionTypes, $wgRestrictionLevels;
+
return array (
'from' => null,
'prefix' => null,
'namespace' => array (
ApiBase :: PARAM_DFLT => 0,
- ApiBase :: PARAM_TYPE => 'namespace'
+ ApiBase :: PARAM_TYPE => 'namespace',
),
'filterredir' => array (
ApiBase :: PARAM_DFLT => 'all',
@@ -131,11 +161,23 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
'nonredirects'
)
),
+ 'minsize' => array (
+ ApiBase :: PARAM_TYPE => 'integer',
+ ),
+ 'maxsize' => array (
+ ApiBase :: PARAM_TYPE => 'integer',
+ ),
+ 'prtype' => array (
+ ApiBase :: PARAM_TYPE => $wgRestrictionTypes,
+ ),
+ 'prlevel' => array (
+ ApiBase :: PARAM_TYPE => $wgRestrictionLevels,
+ ),
'limit' => array (
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
)
);
@@ -147,6 +189,10 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
'prefix' => 'Search for all page titles that begin with this value.',
'namespace' => 'The namespace to enumerate.',
'filterredir' => 'Which pages to list.',
+ 'minsize' => 'Limit to pages with at least this many bytes',
+ 'maxsize' => 'Limit to pages with at most this many bytes',
+ 'prtype' => 'Limit to protected pages only',
+ 'prlevel' => 'The protection level (must be used with apprtype= parameter)',
'limit' => 'How many total pages to return.'
);
}
@@ -169,7 +215,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllpages.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryAllpages.php 24694 2007-08-09 08:41:58Z yurik $';
}
}
-?>
+
diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php
index 1a6783a9..a676b4bf 100644
--- a/includes/api/ApiQueryBacklinks.php
+++ b/includes/api/ApiQueryBacklinks.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,11 +29,16 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This is three-in-one module to query:
+ * * backlinks - links pointing to the given page,
+ * * embeddedin - what pages transclude the given page within themselves,
+ * * imageusage - what pages use the given image
+ *
* @addtogroup API
*/
class ApiQueryBacklinks extends ApiQueryGeneratorBase {
- private $rootTitle, $contRedirs, $contLevel, $contTitle, $contID;
+ private $params, $rootTitle, $contRedirs, $contLevel, $contTitle, $contID;
// output element name, database column field prefix, database table
private $backlinksSettings = array (
@@ -47,8 +52,8 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
'prefix' => 'tl',
'linktbl' => 'templatelinks'
),
- 'imagelinks' => array (
- 'code' => 'il',
+ 'imageusage' => array (
+ 'code' => 'iu',
'prefix' => 'il',
'linktbl' => 'imagelinks'
)
@@ -67,7 +72,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
);
$this->bl_code = $code;
- $this->hasNS = $moduleName !== 'imagelinks';
+ $this->hasNS = $moduleName !== 'imageusage';
if ($this->hasNS) {
$this->bl_title = $prefix . '_title';
$this->bl_sort = "{$this->bl_ns}, {$this->bl_title}, {$this->bl_from}";
@@ -93,13 +98,13 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
private function run($resultPageSet = null) {
- $continue = $namespace = $redirect = $limit = null;
- extract($this->extractRequestParams());
-
+ $this->params = $this->extractRequestParams();
+
+ $redirect = $this->params['redirect'];
if ($redirect)
- ApiBase :: dieDebug(__METHOD__, 'Redirect is not yet been implemented', 'notimplemented');
+ $this->dieDebug('Redirect has not been implemented', 'notimplemented');
- $this->processContinue($continue, $redirect);
+ $this->processContinue();
$this->addFields($this->bl_fields);
if (is_null($resultPageSet))
@@ -117,15 +122,19 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if ($this->hasNS)
$this->addWhereFld($this->bl_ns, $this->rootTitle->getNamespace());
$this->addWhereFld($this->bl_title, $this->rootTitle->getDBkey());
- $this->addWhereFld('page_namespace', $namespace);
- $this->addOption('LIMIT', $limit +1);
- $this->addOption('ORDER BY', $this->bl_sort);
+ $this->addWhereFld('page_namespace', $this->params['namespace']);
- if ($redirect)
+ if($this->params['filterredir'] == 'redirects')
+ $this->addWhereFld('page_is_redirect', 1);
+ if($this->params['filterredir'] == 'nonredirects')
$this->addWhereFld('page_is_redirect', 0);
+ $limit = $this->params['limit'];
+ $this->addOption('LIMIT', $limit +1);
+ $this->addOption('ORDER BY', $this->bl_sort);
+
$db = $this->getDB();
- if (!is_null($continue)) {
+ if (!is_null($this->params['continue'])) {
$plfrm = intval($this->contID);
if ($this->contLevel == 0) {
// For the first level, there is only one target title, so no need for complex filtering
@@ -150,48 +159,61 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
if ($redirect) {
- $ns = $row-> {
- $this->bl_ns };
- $t = $row-> {
- $this->bl_title };
+ $ns = $row-> { $this->bl_ns };
+ $t = $row-> { $this->bl_title };
$continue = $this->getContinueRedirStr(false, 0, $ns, $t, $row->page_id);
} else
$continue = $this->getContinueStr($row->page_id);
+ // TODO: Security issue - if the user has no right to view next title, it will still be shown
$this->setContinueEnumParameter('continue', $continue);
break;
}
if (is_null($resultPageSet)) {
- $vals = $this->addRowInfo('page', $row);
+ $vals = $this->extractRowInfo($row);
if ($vals)
- $data[intval($row->page_id)] = $vals;
+ $data[] = $vals;
} else {
$resultPageSet->processDbRow($row);
}
}
$db->freeResult($res);
- if (is_null($resultPageSet)) {
+ if (is_null($resultPageSet) && !empty($data)) {
$result = $this->getResult();
$result->setIndexedTagName($data, $this->bl_code);
$result->addValue('query', $this->getModuleName(), $data);
}
}
- protected function processContinue($continue, $redirect) {
+ private function extractRowInfo($row) {
+
+ $vals = array();
+ $vals['pageid'] = intval($row->page_id);
+ ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->page_namespace, $row->page_title));
+
+ return $vals;
+ }
+
+ protected function processContinue() {
$pageSet = $this->getPageSet();
$count = $pageSet->getTitleCount();
- if (!is_null($continue)) {
- if ($count !== 0)
- $this->dieUsage("When continuing the {$this->getModuleName()} query, no other titles may be provided", 'titles_on_continue');
- $this->parseContinueParam($continue, $redirect);
+
+ if (!is_null($this->params['continue'])) {
+ $this->parseContinueParam();
// Skip all completed links
} else {
- if ($count !== 1)
- $this->dieUsage("The {$this->getModuleName()} query requires one title to start", 'bad_title_count');
- $this->rootTitle = current($pageSet->getTitles()); // only one title there
+ $title = $this->params['title'];
+ if (!is_null($title)) {
+ $this->rootTitle = Title :: newFromText($title);
+ } else { // This case is obsolete. Will support this for a while
+ if ($count !== 1)
+ $this->dieUsage("The {$this->getModuleName()} query requires one title to start", 'bad_title_count');
+ $this->rootTitle = current($pageSet->getTitles()); // only one title there
+ $this->setWarning('Using titles parameter is obsolete for this list. Use ' . $this->encodeParamName('title') . ' instead.');
+ }
}
// only image titles are allowed for the root
@@ -199,9 +221,9 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->dieUsage("The title for {$this->getModuleName()} query must be an image", 'bad_image_title');
}
- protected function parseContinueParam($continue, $redirect) {
- $continueList = explode('|', $continue);
- if ($redirect) {
+ protected function parseContinueParam() {
+ $continueList = explode('|', $this->params['continue']);
+ if ($this->params['redirect']) {
//
// expected redirect-mode parameter:
// ns|db_key|step|level|ns|db_key|id
@@ -215,7 +237,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$rootNs = intval($continueList[0]);
if (($rootNs !== 0 || $continueList[0] === '0') && !empty ($continueList[1])) {
$this->rootTitle = Title :: makeTitleSafe($rootNs, $continueList[1]);
- if ($this->rootTitle && $this->rootTitle->userCanRead()) {
+ if ($this->rootTitle) {
$step = intval($continueList[2]);
if ($step === 1 || $step === 2) {
@@ -263,7 +285,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$rootNs = intval($continueList[0]);
if (($rootNs !== 0 || $continueList[0] === '0') && !empty ($continueList[1])) {
$this->rootTitle = Title :: makeTitleSafe($rootNs, $continueList[1]);
- if ($this->rootTitle && $this->rootTitle->userCanRead()) {
+ if ($this->rootTitle) {
$contID = intval($continueList[2]);
if ($contID !== 0) {
@@ -296,17 +318,26 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
protected function getAllowedParams() {
return array (
+ 'title' => null,
'continue' => null,
'namespace' => array (
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => 'namespace'
),
+ 'filterredir' => array(
+ ApiBase :: PARAM_DFLT => 'all',
+ ApiBase :: PARAM_TYPE => array(
+ 'all',
+ 'redirects',
+ 'nonredirects'
+ )
+ ),
'redirect' => false,
'limit' => array (
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
)
);
@@ -314,8 +345,10 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
protected function getParamDescription() {
return array (
+ 'title' => 'Title to search. If null, titles= parameter will be used instead, but will be obsolete soon.',
'continue' => 'When more results are available, use this to continue.',
'namespace' => 'The namespace to enumerate.',
+ 'filterredir' => 'How to filter for redirects',
'redirect' => 'If linking page is a redirect, find all pages that link to that redirect (not implemented)',
'limit' => 'How many total pages to return.'
);
@@ -327,7 +360,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
return 'Find all pages that link to the given page';
case 'embeddedin' :
return 'Find all pages that embed (transclude) the given title';
- case 'imagelinks' :
+ case 'imageusage' :
return 'Find all pages that use the given image title.';
default :
ApiBase :: dieDebug(__METHOD__, 'Unknown module name');
@@ -337,16 +370,16 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
protected function getExamples() {
static $examples = array (
'backlinks' => array (
- "api.php?action=query&list=backlinks&titles=Main%20Page",
- "api.php?action=query&generator=backlinks&titles=Main%20Page&prop=info"
+ "api.php?action=query&list=backlinks&bltitle=Main%20Page",
+ "api.php?action=query&generator=backlinks&gbltitle=Main%20Page&prop=info"
),
'embeddedin' => array (
- "api.php?action=query&list=embeddedin&titles=Template:Stub",
- "api.php?action=query&generator=embeddedin&titles=Template:Stub&prop=info"
+ "api.php?action=query&list=embeddedin&eititle=Template:Stub",
+ "api.php?action=query&generator=embeddedin&geititle=Template:Stub&prop=info"
),
- 'imagelinks' => array (
- "api.php?action=query&list=imagelinks&titles=Image:Albert%20Einstein%20Head.jpg",
- "api.php?action=query&generator=imagelinks&titles=Image:Albert%20Einstein%20Head.jpg&prop=info"
+ 'imageusage' => array (
+ "api.php?action=query&list=imageusage&iutitle=Image:Albert%20Einstein%20Head.jpg",
+ "api.php?action=query&generator=imageusage&giutitle=Image:Albert%20Einstein%20Head.jpg&prop=info"
)
);
@@ -354,7 +387,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryBacklinks.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryBacklinks.php 25476 2007-09-04 14:44:46Z catrope $';
}
}
-?>
+
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php
index da07bb6c..28adb415 100644
--- a/includes/api/ApiQueryBase.php
+++ b/includes/api/ApiQueryBase.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,15 +29,19 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This is a base class for all Query modules.
+ * It provides some common functionality such as constructing various SQL queries.
+ *
* @addtogroup API
*/
abstract class ApiQueryBase extends ApiBase {
- private $mQueryModule, $tables, $where, $fields, $options;
+ private $mQueryModule, $mDb, $tables, $where, $fields, $options;
public function __construct($query, $moduleName, $paramPrefix = '') {
parent :: __construct($query->getMain(), $moduleName, $paramPrefix);
$this->mQueryModule = $query;
+ $this->mDb = null;
$this->resetQueryParams();
}
@@ -48,11 +52,16 @@ abstract class ApiQueryBase extends ApiBase {
$this->options = array ();
}
- protected function addTables($value) {
- if (is_array($value))
- $this->tables = array_merge($this->tables, $value);
- else
- $this->tables[] = $value;
+ protected function addTables($tables, $alias = null) {
+ if (is_array($tables)) {
+ if (!is_null($alias))
+ ApiBase :: dieDebug(__METHOD__, 'Multiple table aliases not supported');
+ $this->tables = array_merge($this->tables, $tables);
+ } else {
+ if (!is_null($alias))
+ $tables = $this->getDB()->tableName($tables) . ' ' . $alias;
+ $this->tables[] = $tables;
+ }
}
protected function addFields($value) {
@@ -124,176 +133,16 @@ abstract class ApiQueryBase extends ApiBase {
return $res;
}
- protected function addRowInfo($prefix, $row) {
-
- $vals = array ();
-
- // ID
- if ( isset( $row-> { $prefix . '_id' } ) )
- $vals[$prefix . 'id'] = intval( $row-> { $prefix . '_id' } );
-
- // Title
- $title = ApiQueryBase :: addRowInfo_title($row, $prefix . '_namespace', $prefix . '_title');
- if ($title) {
- if (!$title->userCanRead())
- return false;
- $vals['ns'] = $title->getNamespace();
- $vals['title'] = $title->getPrefixedText();
- }
-
- switch ($prefix) {
-
- case 'page' :
- // page_is_redirect
- @ $tmp = $row->page_is_redirect;
- if ($tmp)
- $vals['redirect'] = '';
-
- break;
-
- case 'rc' :
- // PageId
- @ $tmp = $row->rc_cur_id;
- if (!is_null($tmp))
- $vals['pageid'] = intval($tmp);
-
- @ $tmp = $row->rc_this_oldid;
- if (!is_null($tmp))
- $vals['revid'] = intval($tmp);
-
- if ( isset( $row->rc_last_oldid ) )
- $vals['old_revid'] = intval( $row->rc_last_oldid );
-
- $title = ApiQueryBase :: addRowInfo_title($row, 'rc_moved_to_ns', 'rc_moved_to_title');
- if ($title) {
- if (!$title->userCanRead())
- return false;
- $vals['new_ns'] = $title->getNamespace();
- $vals['new_title'] = $title->getPrefixedText();
- }
-
- if ( isset( $row->rc_patrolled ) )
- $vals['patrolled'] = '';
-
- break;
-
- case 'log' :
- // PageId
- @ $tmp = $row->page_id;
- if (!is_null($tmp))
- $vals['pageid'] = intval($tmp);
-
- if ($row->log_params !== '') {
- $params = explode("\n", $row->log_params);
- if ($row->log_type == 'move' && isset ($params[0])) {
- $newTitle = Title :: newFromText($params[0]);
- if ($newTitle) {
- $vals['new_ns'] = $newTitle->getNamespace();
- $vals['new_title'] = $newTitle->getPrefixedText();
- $params = null;
- }
- }
-
- if (!empty ($params)) {
- $this->getResult()->setIndexedTagName($params, 'param');
- $vals = array_merge($vals, $params);
- }
- }
-
- break;
-
- case 'rev' :
- // PageID
- @ $tmp = $row->rev_page;
- if (!is_null($tmp))
- $vals['pageid'] = intval($tmp);
- }
-
- // Type
- @ $tmp = $row-> {
- $prefix . '_type' };
- if (!is_null($tmp))
- $vals['type'] = $tmp;
-
- // Action
- @ $tmp = $row-> {
- $prefix . '_action' };
- if (!is_null($tmp))
- $vals['action'] = $tmp;
-
- // Old ID
- @ $tmp = $row-> {
- $prefix . '_text_id' };
- if (!is_null($tmp))
- $vals['oldid'] = intval($tmp);
-
- // User Name / Anon IP
- @ $tmp = $row-> {
- $prefix . '_user_text' };
- if (is_null($tmp))
- @ $tmp = $row->user_name;
- if (!is_null($tmp)) {
- $vals['user'] = $tmp;
- @ $tmp = !$row-> {
- $prefix . '_user' };
- if (!is_null($tmp) && $tmp)
- $vals['anon'] = '';
- }
-
- // Bot Edit
- @ $tmp = $row-> {
- $prefix . '_bot' };
- if (!is_null($tmp) && $tmp)
- $vals['bot'] = '';
-
- // New Edit
- @ $tmp = $row-> {
- $prefix . '_new' };
- if (is_null($tmp))
- @ $tmp = $row-> {
- $prefix . '_is_new' };
- if (!is_null($tmp) && $tmp)
- $vals['new'] = '';
-
- // Minor Edit
- @ $tmp = $row-> {
- $prefix . '_minor_edit' };
- if (is_null($tmp))
- @ $tmp = $row-> {
- $prefix . '_minor' };
- if (!is_null($tmp) && $tmp)
- $vals['minor'] = '';
-
- // Timestamp
- @ $tmp = $row-> {
- $prefix . '_timestamp' };
- if (!is_null($tmp))
- $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $tmp);
-
- // Comment
- @ $tmp = $row-> {
- $prefix . '_comment' };
- if (!empty ($tmp)) // optimize bandwidth
- $vals['comment'] = $tmp;
-
- return $vals;
- }
-
- private static function addRowInfo_title($row, $nsfld, $titlefld) {
- if ( isset( $row-> $nsfld ) ) {
- $ns = $row-> $nsfld;
- @ $title = $row-> $titlefld;
- if (!empty ($title))
- return Title :: makeTitle($ns, $title);
- }
- return false;
+ public static function addTitleInfo(&$arr, $title, $prefix='') {
+ $arr[$prefix . 'ns'] = intval($title->getNamespace());
+ $arr[$prefix . 'title'] = $title->getPrefixedText();
}
-
+
/**
* Override this method to request extra fields from the pageSet
- * using $this->getPageSet()->requestField('fieldName')
+ * using $pageSet->requestField('fieldName')
*/
- public function requestExtraData() {
+ public function requestExtraData($pageSet) {
}
/**
@@ -303,10 +152,25 @@ abstract class ApiQueryBase extends ApiBase {
return $this->mQueryModule;
}
+ /**
+ * Add sub-element under the page element with the given pageId.
+ */
+ protected function addPageSubItems($pageId, $data) {
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, $this->getModulePrefix());
+ $result->addValue(array ('query', 'pages', intval($pageId)),
+ $this->getModuleName(),
+ $data);
+ }
+
protected function setContinueEnumParameter($paramName, $paramValue) {
- $msg = array (
- $this->encodeParamName($paramName
- ) => $paramValue);
+
+ $paramName = $this->encodeParamName($paramName);
+ $msg = array( $paramName => $paramValue );
+
+// This is an alternative continue format as a part of the URL string
+// ApiResult :: setContent($msg, $paramName . '=' . urlencode($paramValue));
+
$this->getResult()->addValue('query-continue', $this->getModuleName(), $msg);
}
@@ -314,7 +178,19 @@ abstract class ApiQueryBase extends ApiBase {
* Get the Query database connection (readonly)
*/
protected function getDB() {
- return $this->getQuery()->getDB();
+ if (is_null($this->mDb))
+ $this->mDb = $this->getQuery()->getDB();
+ return $this->mDb;
+ }
+
+ /**
+ * Selects the query database connection with the given name.
+ * If no such connection has been requested before, it will be created.
+ * Subsequent calls with the same $name will return the same connection
+ * as the first, regardless of $db or $groups new values.
+ */
+ public function selectNamedDB($name, $db, $groups) {
+ $this->mDb = $this->getQuery()->getNamedDB($name, $db, $groups);
}
/**
@@ -322,7 +198,7 @@ abstract class ApiQueryBase extends ApiBase {
* @return ApiPageSet data
*/
protected function getPageSet() {
- return $this->mQueryModule->getPageSet();
+ return $this->getQuery()->getPageSet();
}
/**
@@ -338,8 +214,19 @@ abstract class ApiQueryBase extends ApiBase {
return str_replace('_', ' ', $key);
}
+ public function getTokenFlag($tokenArr, $action) {
+ if (in_array($action, $tokenArr)) {
+ global $wgUser;
+ if ($wgUser->isAllowed($action))
+ return true;
+ else
+ $this->dieUsage("Action '$action' is not allowed for the current user", 'permissiondenied');
+ }
+ return false;
+ }
+
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiQueryBase.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryBase.php 24533 2007-08-01 22:46:22Z yurik $';
}
}
@@ -375,4 +262,4 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase {
*/
public abstract function executeGenerator($resultPageSet);
}
-?>
+
diff --git a/includes/api/ApiQueryCategories.php b/includes/api/ApiQueryCategories.php
new file mode 100644
index 00000000..42bc1c38
--- /dev/null
+++ b/includes/api/ApiQueryCategories.php
@@ -0,0 +1,157 @@
+<?php
+
+/*
+ * Created on May 13, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * A query module to enumerate categories the set of pages belong to.
+ *
+ * @addtogroup API
+ */
+class ApiQueryCategories extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'cl');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ if ($this->getPageSet()->getGoodTitleCount() == 0)
+ return; // nothing to do
+
+ $params = $this->extractRequestParams();
+ $prop = $params['prop'];
+
+ $this->addFields(array (
+ 'cl_from',
+ 'cl_to'
+ ));
+
+ $fld_sortkey = false;
+ if (!is_null($prop)) {
+ foreach($prop as $p) {
+ switch ($p) {
+ case 'sortkey':
+ $this->addFields('cl_sortkey');
+ $fld_sortkey = true;
+ break;
+ default :
+ ApiBase :: dieDebug(__METHOD__, "Unknown prop=$p");
+ }
+ }
+ }
+
+ $this->addTables('categorylinks');
+ $this->addWhereFld('cl_from', array_keys($this->getPageSet()->getGoodTitles()));
+ $this->addOption('ORDER BY', "cl_from, cl_to");
+
+ $db = $this->getDB();
+ $res = $this->select(__METHOD__);
+
+ if (is_null($resultPageSet)) {
+
+ $data = array();
+ $lastId = 0; // database has no ID 0
+ while ($row = $db->fetchObject($res)) {
+ if ($lastId != $row->cl_from) {
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ $data = array();
+ }
+ $lastId = $row->cl_from;
+ }
+
+ $title = Title :: makeTitle(NS_CATEGORY, $row->cl_to);
+
+ $vals = array();
+ ApiQueryBase :: addTitleInfo($vals, $title);
+ if ($fld_sortkey)
+ $vals['sortkey'] = $row->cl_sortkey;
+
+ $data[] = $vals;
+ }
+
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ }
+
+ } else {
+
+ $titles = array();
+ while ($row = $db->fetchObject($res)) {
+ $titles[] = Title :: makeTitle(NS_CATEGORY, $row->cl_to);
+ }
+ $resultPageSet->populateFromTitles($titles);
+ }
+
+ $db->freeResult($res);
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'prop' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'sortkey',
+ )
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'prop' => 'Which additional properties to get for each category.',
+ );
+ }
+
+ protected function getDescription() {
+ return 'List all categories the page(s) belong to';
+ }
+
+ protected function getExamples() {
+ return array (
+ "Get a list of categories [[Albert Einstein]] belongs to:",
+ " api.php?action=query&prop=categories&titles=Albert%20Einstein",
+ "Get information about all categories used in the [[Albert Einstein]]:",
+ " api.php?action=query&generator=categories&titles=Albert%20Einstein&prop=info"
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryCategories.php 24092 2007-07-14 19:04:31Z yurik $';
+ }
+}
+
diff --git a/includes/api/ApiQueryCategoryMembers.php b/includes/api/ApiQueryCategoryMembers.php
new file mode 100644
index 00000000..58a454a5
--- /dev/null
+++ b/includes/api/ApiQueryCategoryMembers.php
@@ -0,0 +1,238 @@
+<?php
+
+/*
+ * Created on June 14, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * A query module to enumerate pages that belong to a category.
+ *
+ * @addtogroup API
+ */
+class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'cm');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ $params = $this->extractRequestParams();
+
+ $category = $params['category'];
+ if (is_null($category))
+ $this->dieUsage("Category parameter is required", 'param_category');
+ $categoryTitle = Title::makeTitleSafe( NS_CATEGORY, $category );
+ if ( is_null( $categoryTitle ) )
+ $this->dieUsage("Category name $category is not valid", 'param_category');
+
+ $prop = array_flip($params['prop']);
+ $fld_ids = isset($prop['ids']);
+ $fld_title = isset($prop['title']);
+ $fld_sortkey = isset($prop['sortkey']);
+ $fld_timestamp = isset($prop['timestamp']);
+
+ if (is_null($resultPageSet)) {
+ $this->addFields(array('cl_from', 'cl_sortkey', 'page_namespace', 'page_title'));
+ $this->addFieldsIf('page_id', $fld_ids);
+ } else {
+ $this->addFields($resultPageSet->getPageTableFields()); // will include page_ id, ns, title
+ $this->addFields(array('cl_from', 'cl_sortkey'));
+ }
+
+ $this->addFieldsIf('cl_timestamp', $fld_timestamp);
+ $this->addTables(array('page','categorylinks')); // must be in this order for 'USE INDEX'
+ // Not needed after bug 10280 is applied to servers
+ if($params['sort'] == 'timestamp')
+ {
+ $this->addOption('USE INDEX', 'cl_timestamp');
+ $this->addOption('ORDER BY', 'cl_to, cl_timestamp');
+ }
+ else
+ {
+ $this->addOption('USE INDEX', 'cl_sortkey');
+ $this->addOption('ORDER BY', 'cl_to, cl_sortkey, cl_from');
+ }
+
+ $this->addWhere('cl_from=page_id');
+ $this->setContinuation($params['continue']);
+ $this->addWhereFld('cl_to', $categoryTitle->getDBkey());
+ $this->addWhereFld('page_namespace', $params['namespace']);
+
+ $limit = $params['limit'];
+ $this->addOption('LIMIT', $limit +1);
+
+ $db = $this->getDB();
+
+ $data = array ();
+ $count = 0;
+ $lastSortKey = null;
+ $res = $this->select(__METHOD__);
+ while ($row = $db->fetchObject($res)) {
+ if (++ $count > $limit) {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ // TODO: Security issue - if the user has no right to view next title, it will still be shown
+ $this->setContinueEnumParameter('continue', $this->getContinueStr($row, $lastSortKey));
+ break;
+ }
+
+ $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys
+
+ if (is_null($resultPageSet)) {
+ $vals = array();
+ if ($fld_ids)
+ $vals['pageid'] = intval($row->page_id);
+ if ($fld_title) {
+ $title = Title :: makeTitle($row->page_namespace, $row->page_title);
+ $vals['ns'] = intval($title->getNamespace());
+ $vals['title'] = $title->getPrefixedText();
+ }
+ if ($fld_sortkey)
+ $vals['sortkey'] = $row->cl_sortkey;
+ if ($fld_timestamp)
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp);
+ $data[] = $vals;
+ } else {
+ $resultPageSet->processDbRow($row);
+ }
+ }
+ $db->freeResult($res);
+
+ if (is_null($resultPageSet)) {
+ $this->getResult()->setIndexedTagName($data, 'cm');
+ $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ }
+ }
+
+ private function getContinueStr($row, $lastSortKey) {
+ $ret = $row->cl_sortkey . '|';
+ if ($row->cl_sortkey == $lastSortKey) // duplicate sort key, add cl_from
+ $ret .= $row->cl_from;
+ return $ret;
+ }
+
+ /**
+ * Add DB WHERE clause to continue previous query based on 'continue' parameter
+ */
+ private function setContinuation($continue) {
+ if (is_null($continue))
+ return; // This is not a continuation request
+
+ $continueList = explode('|', $continue);
+ $hasError = count($continueList) != 2;
+ $from = 0;
+ if (!$hasError && strlen($continueList[1]) > 0) {
+ $from = intval($continueList[1]);
+ $hasError = ($from == 0);
+ }
+
+ if ($hasError)
+ $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "badcontinue");
+
+ $encSortKey = $this->getDB()->addQuotes($continueList[0]);
+ $encFrom = $this->getDB()->addQuotes($from);
+
+ if ($from != 0) {
+ // Duplicate sort key continue
+ $this->addWhere( "cl_sortkey>$encSortKey OR (cl_sortkey=$encSortKey AND cl_from>=$encFrom)" );
+ } else {
+ $this->addWhere( "cl_sortkey>=$encSortKey" );
+ }
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'category' => null,
+ 'prop' => array (
+ ApiBase :: PARAM_DFLT => 'ids|title',
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'ids',
+ 'title',
+ 'sortkey',
+ 'timestamp',
+ )
+ ),
+ 'namespace' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => 'namespace',
+ ),
+ 'continue' => null,
+ 'limit' => array (
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'sort' => array(
+ ApiBase :: PARAM_DFLT => 'sortkey',
+ ApiBase :: PARAM_TYPE => array(
+ 'sortkey',
+ 'timestamp'
+ )
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'category' => 'Which category to enumerate (required)',
+ 'prop' => 'What pieces of information to include',
+ 'namespace' => 'Only include pages in these namespaces',
+ 'sort' => 'Property to sort by',
+ 'continue' => 'For large categories, give the value retured from previous query',
+ 'limit' => 'The maximum number of pages to return.',
+ );
+ }
+
+ protected function getDescription() {
+ return 'List all pages in a given category';
+ }
+
+ protected function getExamples() {
+ return array (
+ "Get first 10 pages in the categories [[Physics]]:",
+ " api.php?action=query&list=categorymembers&cmcategory=Physics",
+ "Get page info about first 10 pages in the categories [[Physics]]:",
+ " api.php?action=query&generator=categorymembers&gcmcategory=Physics&prop=info",
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 25474 2007-09-04 14:30:31Z catrope $';
+ }
+}
+
diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php
new file mode 100644
index 00000000..385ae65b
--- /dev/null
+++ b/includes/api/ApiQueryExtLinksUsage.php
@@ -0,0 +1,200 @@
+<?php
+
+/*
+ * Created on July 7, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ('ApiQueryBase.php');
+}
+
+/**
+ * @addtogroup API
+ */
+class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'eu');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ $params = $this->extractRequestParams();
+
+ $protocol = $params['protocol'];
+ $query = $params['query'];
+ if (is_null($query))
+ $this->dieUsage('Missing required query parameter', 'params');
+
+ // Find the right prefix
+ global $wgUrlProtocols;
+ foreach ($wgUrlProtocols as $p) {
+ if( substr( $p, 0, strlen( $protocol ) ) === $protocol ) {
+ $protocol = $p;
+ break;
+ }
+ }
+
+ $likeQuery = LinkFilter::makeLike($query , $protocol);
+ if (!$likeQuery)
+ $this->dieUsage('Invalid query', 'bad_query');
+ $likeQuery = substr($likeQuery, 0, strpos($likeQuery,'%')+1);
+
+ $this->addTables(array('page','externallinks')); // must be in this order for 'USE INDEX'
+ $this->addOption('USE INDEX', 'el_index');
+
+ $db = $this->getDB();
+ $this->addWhere('page_id=el_from');
+ $this->addWhere('el_index LIKE ' . $db->addQuotes( $likeQuery ));
+ $this->addWhereFld('page_namespace', $params['namespace']);
+
+ $prop = array_flip($params['prop']);
+ $fld_ids = isset($prop['ids']);
+ $fld_title = isset($prop['title']);
+ $fld_url = isset($prop['url']);
+
+ if (is_null($resultPageSet)) {
+ $this->addFields(array (
+ 'page_id',
+ 'page_namespace',
+ 'page_title'
+ ));
+ $this->addFieldsIf('el_to', $fld_url);
+ } else {
+ $this->addFields($resultPageSet->getPageTableFields());
+ }
+
+ $limit = $params['limit'];
+ $offset = $params['offset'];
+ $this->addOption('LIMIT', $limit +1);
+ if (isset ($offset))
+ $this->addOption('OFFSET', $offset);
+
+ $res = $this->select(__METHOD__);
+
+ $data = array ();
+ $count = 0;
+ while ($row = $db->fetchObject($res)) {
+ if (++ $count > $limit) {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('offset', $offset+$limit+1);
+ break;
+ }
+
+ if (is_null($resultPageSet)) {
+ $vals = array();
+ if ($fld_ids)
+ $vals['pageid'] = intval($row->page_id);
+ if ($fld_title) {
+ $title = Title :: makeTitle($row->page_namespace, $row->page_title);
+ $vals['ns'] = intval($title->getNamespace());
+ $vals['title'] = $title->getPrefixedText();
+ }
+ if ($fld_url)
+ $vals['url'] = $row->el_to;
+ $data[] = $vals;
+ } else {
+ $resultPageSet->processDbRow($row);
+ }
+ }
+ $db->freeResult($res);
+
+ if (is_null($resultPageSet)) {
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, $this->getModulePrefix());
+ $result->addValue('query', $this->getModuleName(), $data);
+ }
+ }
+
+ protected function getAllowedParams() {
+ global $wgUrlProtocols;
+ $protocols = array();
+ foreach ($wgUrlProtocols as $p) {
+ $protocols[] = substr($p, 0, strpos($p,':'));
+ }
+
+ return array (
+ 'prop' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_DFLT => 'ids|title|url',
+ ApiBase :: PARAM_TYPE => array (
+ 'ids',
+ 'title',
+ 'url'
+ )
+ ),
+ 'offset' => array (
+ ApiBase :: PARAM_TYPE => 'integer'
+ ),
+ 'protocol' => array (
+ ApiBase :: PARAM_TYPE => $protocols,
+ ApiBase :: PARAM_DFLT => 'http',
+ ),
+ 'query' => null,
+ 'namespace' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => 'namespace'
+ ),
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'prop' => 'What pieces of information to include',
+ 'offset' => 'Used for paging. Use the value returned for "continue"',
+ 'protocol' => 'Protocol of the url',
+ 'query' => 'Search string without protocol. See [[Special:LinkSearch]]',
+ 'namespace' => 'The page namespace(s) to enumerate.',
+ 'limit' => 'How many entries to return.'
+ );
+ }
+
+ protected function getDescription() {
+ return 'Enumerate pages that contain a given URL';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=exturlusage&euquery=www.mediawiki.org'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 24694 2007-08-09 08:41:58Z yurik $';
+ }
+}
diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php
new file mode 100644
index 00000000..440b31d6
--- /dev/null
+++ b/includes/api/ApiQueryExternalLinks.php
@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * Created on May 13, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * A query module to list all external URLs found on a given set of pages.
+ *
+ * @addtogroup API
+ */
+class ApiQueryExternalLinks extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'el');
+ }
+
+ public function execute() {
+
+ $this->addFields(array (
+ 'el_from',
+ 'el_to'
+ ));
+
+ $this->addTables('externallinks');
+ $this->addWhereFld('el_from', array_keys($this->getPageSet()->getGoodTitles()));
+
+ $db = $this->getDB();
+ $res = $this->select(__METHOD__);
+
+ $data = array();
+ $lastId = 0; // database has no ID 0
+ while ($row = $db->fetchObject($res)) {
+ if ($lastId != $row->el_from) {
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ $data = array();
+ }
+ $lastId = $row->el_from;
+ }
+
+ $entry = array();
+ ApiResult :: setContent($entry, $row->el_to);
+ $data[] = $entry;
+ }
+
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ }
+
+ $db->freeResult($res);
+ }
+
+ protected function getDescription() {
+ return 'Returns all external urls (not interwikies) from the given page(s)';
+ }
+
+ protected function getExamples() {
+ return array (
+ "Get a list of external links on the [[Main Page]]:",
+ " api.php?action=query&prop=extlinks&titles=Main%20Page",
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryExternalLinks.php 23819 2007-07-07 03:05:09Z yurik $';
+ }
+}
+
diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php
new file mode 100644
index 00000000..3d568ba1
--- /dev/null
+++ b/includes/api/ApiQueryImageInfo.php
@@ -0,0 +1,156 @@
+<?php
+
+/*
+ * Created on July 6, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ('ApiQueryBase.php');
+}
+
+/**
+ * A query action to get image information and upload history.
+ *
+ * @addtogroup API
+ */
+class ApiQueryImageInfo extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'ii');
+ }
+
+ public function execute() {
+ $params = $this->extractRequestParams();
+
+ $history = $params['history'];
+
+ $prop = array_flip($params['prop']);
+ $fld_timestamp = isset($prop['timestamp']);
+ $fld_user = isset($prop['user']);
+ $fld_comment = isset($prop['comment']);
+ $fld_url = isset($prop['url']);
+ $fld_size = isset($prop['size']);
+ $fld_sha1 = isset($prop['sha1']);
+
+ $pageIds = $this->getPageSet()->getAllTitlesByNamespace();
+ if (!empty($pageIds[NS_IMAGE])) {
+ foreach ($pageIds[NS_IMAGE] as $dbKey => $pageId) {
+
+ $title = Title :: makeTitle(NS_IMAGE, $dbKey);
+ $img = wfFindFile($title);
+
+ $data = array();
+ if ( !$img ) {
+ $repository = '';
+ } else {
+
+ $repository = $img->getRepoName();
+
+ $isCur = true;
+ while($line = $img->nextHistoryLine()) { // assignment
+ $row = get_object_vars( $line );
+ $vals = array();
+ $prefix = $isCur ? 'img' : 'oi';
+
+ if ($fld_timestamp)
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row["${prefix}_timestamp"]);
+ if ($fld_user) {
+ $vals['user'] = $row["${prefix}_user_text"];
+ if(!$row["${prefix}_user"])
+ $vals['anon'] = '';
+ }
+ if ($fld_size) {
+ $vals['size'] = intval($row["{$prefix}_size"]);
+ $vals['width'] = intval($row["{$prefix}_width"]);
+ $vals['height'] = intval($row["{$prefix}_height"]);
+ }
+ if ($fld_url)
+ $vals['url'] = $isCur ? $img->getURL() : $img->getArchiveUrl($row["oi_archive_name"]);
+ if ($fld_comment)
+ $vals['comment'] = $row["{$prefix}_description"];
+
+ if ($fld_sha1)
+ $vals['sha1'] = wfBaseConvert($row["{$prefix}_sha1"], 36, 16, 40);
+
+ $data[] = $vals;
+
+ if (!$history) // Stop after the first line.
+ break;
+
+ $isCur = false;
+ }
+
+ $img->resetHistory();
+ }
+
+ $this->getResult()->addValue(array ('query', 'pages', intval($pageId)),
+ 'imagerepository',
+ $repository);
+ if (!empty($data))
+ $this->addPageSubItems($pageId, $data);
+ }
+ }
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'prop' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_DFLT => 'timestamp|user',
+ ApiBase :: PARAM_TYPE => array (
+ 'timestamp',
+ 'user',
+ 'comment',
+ 'url',
+ 'size',
+ 'sha1'
+ )
+ ),
+ 'history' => false,
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'prop' => 'What image information to get.',
+ 'history' => 'Include upload history',
+ );
+ }
+
+ protected function getDescription() {
+ return array (
+ 'Returns image information and upload history'
+ );
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&titles=Image:Albert%20Einstein%20Head.jpg&prop=imageinfo',
+ 'api.php?action=query&titles=Image:Test.jpg&prop=imageinfo&iihistory&iiprop=timestamp|user|url',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryImageInfo.php 25456 2007-09-03 19:58:05Z catrope $';
+ }
+}
diff --git a/includes/api/ApiQueryImages.php b/includes/api/ApiQueryImages.php
new file mode 100644
index 00000000..d64a653b
--- /dev/null
+++ b/includes/api/ApiQueryImages.php
@@ -0,0 +1,118 @@
+<?php
+
+/*
+ * Created on May 13, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * This query adds <images> subelement to all pages with the list of images embedded into those pages.
+ *
+ * @addtogroup API
+ */
+class ApiQueryImages extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'im');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ if ($this->getPageSet()->getGoodTitleCount() == 0)
+ return; // nothing to do
+
+ $this->addFields(array (
+ 'il_from',
+ 'il_to'
+ ));
+
+ $this->addTables('imagelinks');
+ $this->addWhereFld('il_from', array_keys($this->getPageSet()->getGoodTitles()));
+ $this->addOption('ORDER BY', "il_from, il_to");
+
+ $db = $this->getDB();
+ $res = $this->select(__METHOD__);
+
+ if (is_null($resultPageSet)) {
+
+ $data = array();
+ $lastId = 0; // database has no ID 0
+ while ($row = $db->fetchObject($res)) {
+ if ($lastId != $row->il_from) {
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ $data = array();
+ }
+ $lastId = $row->il_from;
+ }
+
+ $vals = array();
+ ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle(NS_IMAGE, $row->il_to));
+ $data[] = $vals;
+ }
+
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ }
+
+ } else {
+
+ $titles = array();
+ while ($row = $db->fetchObject($res)) {
+ $titles[] = Title :: makeTitle(NS_IMAGE, $row->il_to);
+ }
+ $resultPageSet->populateFromTitles($titles);
+ }
+
+ $db->freeResult($res);
+ }
+
+ protected function getDescription() {
+ return 'Returns all images contained on the given page(s)';
+ }
+
+ protected function getExamples() {
+ return array (
+ "Get a list of images used in the [[Main Page]]:",
+ " api.php?action=query&prop=images&titles=Main%20Page",
+ "Get information about all images used in the [[Main Page]]:",
+ " api.php?action=query&generator=images&titles=Main%20Page&prop=info"
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryImages.php 24092 2007-07-14 19:04:31Z yurik $';
+ }
+}
+
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index 77489a5f..bebf4006 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,59 +29,167 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * A query module to show basic page information.
+ *
* @addtogroup API
*/
class ApiQueryInfo extends ApiQueryBase {
public function __construct($query, $moduleName) {
- parent :: __construct($query, $moduleName);
+ parent :: __construct($query, $moduleName, 'in');
}
- public function requestExtraData() {
- $pageSet = $this->getPageSet();
+ public function requestExtraData($pageSet) {
$pageSet->requestField('page_is_redirect');
+ $pageSet->requestField('page_is_new');
+ $pageSet->requestField('page_counter');
$pageSet->requestField('page_touched');
$pageSet->requestField('page_latest');
+ $pageSet->requestField('page_len');
}
public function execute() {
+ global $wgUser;
+
+ $params = $this->extractRequestParams();
+ $fld_protection = false;
+ if(!is_null($params['prop'])) {
+ $prop = array_flip($params['prop']);
+ $fld_protection = isset($prop['protection']);
+ }
+ if(!is_null($params['token'])) {
+ $token = $params['token'];
+ $tok_edit = $this->getTokenFlag($token, 'edit');
+ $tok_delete = $this->getTokenFlag($token, 'delete');
+ $tok_protect = $this->getTokenFlag($token, 'protect');
+ $tok_move = $this->getTokenFlag($token, 'move');
+ }
+
$pageSet = $this->getPageSet();
$titles = $pageSet->getGoodTitles();
$result = $this->getResult();
$pageIsRedir = $pageSet->getCustomField('page_is_redirect');
+ $pageIsNew = $pageSet->getCustomField('page_is_new');
+ $pageCounter = $pageSet->getCustomField('page_counter');
$pageTouched = $pageSet->getCustomField('page_touched');
$pageLatest = $pageSet->getCustomField('page_latest');
+ $pageLength = $pageSet->getCustomField('page_len');
+
+ if ($fld_protection && count($titles) > 0) {
+ $this->addTables('page_restrictions');
+ $this->addFields(array('pr_page', 'pr_type', 'pr_level', 'pr_expiry'));
+ $this->addWhereFld('pr_page', array_keys($titles));
- foreach ( $titles as $pageid => $unused ) {
+ $db = $this->getDB();
+ $res = $this->select(__METHOD__);
+ while($row = $db->fetchObject($res)) {
+ $protections[$row->pr_page][] = array(
+ 'type' => $row->pr_type,
+ 'level' => $row->pr_level,
+ 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 )
+ );
+ }
+ $db->freeResult($res);
+ }
+
+ foreach ( $titles as $pageid => $title ) {
$pageInfo = array (
'touched' => wfTimestamp(TS_ISO_8601, $pageTouched[$pageid]),
- 'lastrevid' => intval($pageLatest[$pageid])
+ 'lastrevid' => intval($pageLatest[$pageid]),
+ 'counter' => intval($pageCounter[$pageid]),
+ 'length' => intval($pageLength[$pageid]),
);
if ($pageIsRedir[$pageid])
$pageInfo['redirect'] = '';
+ if ($pageIsNew[$pageid])
+ $pageInfo['new'] = '';
+
+ if (!is_null($token)) {
+ // Currently all tokens are generated the same way, but it might change
+ if ($tok_edit)
+ $pageInfo['edittoken'] = $wgUser->editToken();
+ if ($tok_delete)
+ $pageInfo['deletetoken'] = $wgUser->editToken();
+ if ($tok_protect)
+ $pageInfo['protecttoken'] = $wgUser->editToken();
+ if ($tok_move)
+ $pageInfo['movetoken'] = $wgUser->editToken();
+ }
+
+ if($fld_protection) {
+ if (isset($protections[$pageid])) {
+ $pageInfo['protection'] = $protections[$pageid];
+ $result->setIndexedTagName($pageInfo['protection'], 'pr');
+ } else {
+ $pageInfo['protection'] = array();
+ }
+ }
+
$result->addValue(array (
'query',
'pages'
), $pageid, $pageInfo);
}
+
+ // Get edit tokens for missing titles if requested
+ // Delete, protect and move tokens are N/A for missing titles anyway
+ if($tok_edit)
+ {
+ $missing = $pageSet->getMissingTitles();
+ $res = $result->getData();
+ foreach($missing as $pageid => $title)
+ $res['query']['pages'][$pageid]['edittoken'] = $wgUser->editToken();
+ }
}
+ protected function getAllowedParams() {
+ return array (
+ 'prop' => array (
+ ApiBase :: PARAM_DFLT => NULL,
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'protection'
+ )),
+ 'token' => array (
+ ApiBase :: PARAM_DFLT => NULL,
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'edit',
+ 'delete',
+ 'protect',
+ 'move',
+ )),
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'prop' => array (
+ 'Which additional properties to get:',
+ ' "protection" - List the protection level of each page'
+ ),
+ 'token' => 'Request a token to perform a data-modifying action on a page',
+ );
+ }
+
+
protected function getDescription() {
return 'Get basic page information such as namespace, title, last touched date, ...';
}
protected function getExamples() {
return array (
- 'api.php?action=query&prop=info&titles=Main%20Page'
+ 'api.php?action=query&prop=info&titles=Main%20Page',
+ 'api.php?action=query&prop=info&inprop=protection&titles=Main%20Page'
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryInfo.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryInfo.php 25457 2007-09-03 20:17:53Z catrope $';
}
}
-?>
+
diff --git a/includes/api/ApiQueryLangLinks.php b/includes/api/ApiQueryLangLinks.php
new file mode 100644
index 00000000..ae5ff790
--- /dev/null
+++ b/includes/api/ApiQueryLangLinks.php
@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * Created on May 13, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * A query module to list all langlinks (links to correspanding foreign language pages).
+ *
+ * @addtogroup API
+ */
+class ApiQueryLangLinks extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'll');
+ }
+
+ public function execute() {
+ $this->addFields(array (
+ 'll_from',
+ 'll_lang',
+ 'll_title'
+ ));
+
+ $this->addTables('langlinks');
+ $this->addWhereFld('ll_from', array_keys($this->getPageSet()->getGoodTitles()));
+ $this->addOption('ORDER BY', "ll_from, ll_lang");
+ $res = $this->select(__METHOD__);
+
+ $data = array();
+ $lastId = 0; // database has no ID 0
+ $db = $this->getDB();
+ while ($row = $db->fetchObject($res)) {
+
+ if ($lastId != $row->ll_from) {
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ $data = array();
+ }
+ $lastId = $row->ll_from;
+ }
+
+ $entry = array('lang'=>$row->ll_lang);
+ ApiResult :: setContent($entry, $row->ll_title);
+ $data[] = $entry;
+ }
+
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ }
+
+ $db->freeResult($res);
+ }
+
+ protected function getDescription() {
+ return 'Returns all interlanguage links from the given page(s)';
+ }
+
+ protected function getExamples() {
+ return array (
+ "Get interlanguage links from the [[Main Page]]:",
+ " api.php?action=query&prop=langlinks&titles=Main%20Page&redirects",
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryLangLinks.php 23819 2007-07-07 03:05:09Z yurik $';
+ }
+}
+
diff --git a/includes/api/ApiQueryLinks.php b/includes/api/ApiQueryLinks.php
new file mode 100644
index 00000000..7ec20f44
--- /dev/null
+++ b/includes/api/ApiQueryLinks.php
@@ -0,0 +1,162 @@
+<?php
+
+/*
+ * Created on May 12, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * A query module to list all wiki links on a given set of pages.
+ *
+ * @addtogroup API
+ */
+class ApiQueryLinks extends ApiQueryGeneratorBase {
+
+ const LINKS = 'links';
+ const TEMPLATES = 'templates';
+
+ private $table, $prefix, $description;
+
+ public function __construct($query, $moduleName) {
+
+ switch ($moduleName) {
+ case self::LINKS :
+ $this->table = 'pagelinks';
+ $this->prefix = 'pl';
+ $this->description = 'link';
+ break;
+ case self::TEMPLATES :
+ $this->table = 'templatelinks';
+ $this->prefix = 'tl';
+ $this->description = 'template';
+ break;
+ default :
+ ApiBase :: dieDebug(__METHOD__, 'Unknown module name');
+ }
+
+ parent :: __construct($query, $moduleName, $this->prefix);
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ if ($this->getPageSet()->getGoodTitleCount() == 0)
+ return; // nothing to do
+
+ $params = $this->extractRequestParams();
+
+ $this->addFields(array (
+ $this->prefix . '_from pl_from',
+ $this->prefix . '_namespace pl_namespace',
+ $this->prefix . '_title pl_title'
+ ));
+
+ $this->addTables($this->table);
+ $this->addWhereFld($this->prefix . '_from', array_keys($this->getPageSet()->getGoodTitles()));
+ $this->addWhereFld($this->prefix . '_namespace', $params['namespace']);
+ $this->addOption('ORDER BY', str_replace('pl_', $this->prefix . '_', 'pl_from, pl_namespace, pl_title'));
+
+ $db = $this->getDB();
+ $res = $this->select(__METHOD__);
+
+ if (is_null($resultPageSet)) {
+
+ $data = array();
+ $lastId = 0; // database has no ID 0
+ while ($row = $db->fetchObject($res)) {
+ if ($lastId != $row->pl_from) {
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ $data = array();
+ }
+ $lastId = $row->pl_from;
+ }
+
+ $vals = array();
+ ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->pl_namespace, $row->pl_title));
+ $data[] = $vals;
+ }
+
+ if($lastId != 0) {
+ $this->addPageSubItems($lastId, $data);
+ }
+
+ } else {
+
+ $titles = array();
+ while ($row = $db->fetchObject($res)) {
+ $titles[] = Title :: makeTitle($row->pl_namespace, $row->pl_title);
+ }
+ $resultPageSet->populateFromTitles($titles);
+ }
+
+ $db->freeResult($res);
+ }
+
+ protected function getAllowedParams()
+ {
+ return array(
+ 'namespace' => array(
+ ApiBase :: PARAM_TYPE => 'namespace',
+ ApiBase :: PARAM_ISMULTI => true
+ )
+ );
+ }
+
+ protected function getParamDescription()
+ {
+ return array(
+ 'namespace' => "Show {$this->description}s in this namespace(s) only"
+ );
+ }
+
+ protected function getDescription() {
+ return "Returns all {$this->description}s from the given page(s)";
+ }
+
+ protected function getExamples() {
+ return array (
+ "Get {$this->description}s from the [[Main Page]]:",
+ " api.php?action=query&prop={$this->getModuleName()}&titles=Main%20Page",
+ "Get information about the {$this->description} pages in the [[Main Page]]:",
+ " api.php?action=query&generator={$this->getModuleName()}&titles=Main%20Page&prop=info",
+ "Get {$this->description}s from the Main Page in the User and Template namespaces:",
+ " api.php?action=query&prop={$this->getModuleName()}&titles=Main%20Page&{$this->prefix}namespace=2|10"
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryLinks.php 24092 2007-07-14 19:04:31Z yurik $';
+ }
+}
+
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
index d9f23758..0f143658 100644
--- a/includes/api/ApiQueryLogEvents.php
+++ b/includes/api/ApiQueryLogEvents.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * Query action to List the log events, with optional filtering by various parameters.
+ *
* @addtogroup API
*/
class ApiQueryLogEvents extends ApiQueryBase {
@@ -38,11 +40,18 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
public function execute() {
- $limit = $type = $start = $end = $dir = $user = $title = null;
- extract($this->extractRequestParams());
-
+ $params = $this->extractRequestParams();
$db = $this->getDB();
+ $prop = $params['prop'];
+ $this->fld_ids = in_array('ids', $prop);
+ $this->fld_title = in_array('title', $prop);
+ $this->fld_type = in_array('type', $prop);
+ $this->fld_user = in_array('user', $prop);
+ $this->fld_timestamp = in_array('timestamp', $prop);
+ $this->fld_comment = in_array('comment', $prop);
+ $this->fld_details = in_array('details', $prop);
+
list($tbl_logging, $tbl_page, $tbl_user) = $db->tableNamesN('logging', 'page', 'user');
$this->addOption('STRAIGHT_JOIN');
@@ -54,19 +63,27 @@ class ApiQueryLogEvents extends ApiQueryBase {
'log_type',
'log_action',
'log_timestamp',
- 'log_user',
- 'user_name',
- 'log_namespace',
- 'log_title',
- 'page_id',
- 'log_comment',
- 'log_params'
));
+
+ // FIXME: Fake out log_id for now until the column is live on Wikimedia
+ // $this->addFieldsIf('log_id', $this->fld_ids);
+ $this->addFieldsIf('page_id', $this->fld_ids);
+ $this->addFieldsIf('log_user', $this->fld_user);
+ $this->addFieldsIf('user_name', $this->fld_user);
+ $this->addFieldsIf('log_namespace', $this->fld_title);
+ $this->addFieldsIf('log_title', $this->fld_title);
+ $this->addFieldsIf('log_comment', $this->fld_comment);
+ $this->addFieldsIf('log_params', $this->fld_details);
+
+
+ $this->addWhereFld('log_deleted', 0);
+ $this->addWhereFld('log_type', $params['type']);
+ $this->addWhereRange('log_timestamp', $params['dir'], $params['start'], $params['end']);
- $this->addWhereFld('log_type', $type);
- $this->addWhereRange('log_timestamp', $dir, $start, $end);
+ $limit = $params['limit'];
$this->addOption('LIMIT', $limit +1);
+ $user = $params['user'];
if (!is_null($user)) {
$userid = $db->selectField('user', 'user_id', array (
'user_name' => $user
@@ -76,6 +93,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
$this->addWhereFld('log_user', $userid);
}
+ $title = $params['title'];
if (!is_null($title)) {
$titleObj = Title :: newFromText($title);
if (is_null($titleObj))
@@ -90,11 +108,11 @@ class ApiQueryLogEvents extends ApiQueryBase {
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter('start', $row->log_timestamp);
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp));
break;
}
- $vals = $this->addRowInfo('log', $row);
+ $vals = $this->extractRowInfo($row);
if($vals)
$data[] = $vals;
}
@@ -104,23 +122,102 @@ class ApiQueryLogEvents extends ApiQueryBase {
$this->getResult()->addValue('query', $this->getModuleName(), $data);
}
+ private function extractRowInfo($row) {
+ $vals = array();
+
+ if ($this->fld_ids) {
+ // FIXME: Fake out log_id for now until the column is live on Wikimedia
+ // $vals['logid'] = intval($row->log_id);
+ $vals['logid'] = 0;
+ $vals['pageid'] = intval($row->page_id);
+ }
+
+ if ($this->fld_title) {
+ $title = Title :: makeTitle($row->log_namespace, $row->log_title);
+ ApiQueryBase :: addTitleInfo($vals, $title);
+ }
+
+ if ($this->fld_type) {
+ $vals['type'] = $row->log_type;
+ $vals['action'] = $row->log_action;
+ }
+
+ if ($this->fld_details && $row->log_params !== '') {
+ $params = explode("\n", $row->log_params);
+ switch ($row->log_type) {
+ case 'move':
+ if (isset ($params[0])) {
+ $title = Title :: newFromText($params[0]);
+ if ($title) {
+ $vals2 = array();
+ ApiQueryBase :: addTitleInfo($vals2, $title, "new_");
+ $vals[$row->log_type] = $vals2;
+ $params = null;
+ }
+ }
+ break;
+ case 'patrol':
+ $vals2 = array();
+ list( $vals2['cur'], $vals2['prev'], $vals2['auto'] ) = $params;
+ $vals[$row->log_type] = $vals2;
+ $params = null;
+ break;
+ case 'rights':
+ $vals2 = array();
+ list( $vals2['old'], $vals2['new'] ) = $params;
+ $vals[$row->log_type] = $vals2;
+ $params = null;
+ break;
+ case 'block':
+ $vals2 = array();
+ list( $vals2['duration'], $vals2['flags'] ) = $params;
+ $vals[$row->log_type] = $vals2;
+ $params = null;
+ break;
+ }
+
+ if (isset($params)) {
+ $this->getResult()->setIndexedTagName($params, 'param');
+ $vals = array_merge($vals, $params);
+ }
+ }
+
+ if ($this->fld_user) {
+ $vals['user'] = $row->user_name;
+ if(!$row->log_user)
+ $vals['anon'] = '';
+ }
+ if ($this->fld_timestamp) {
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->log_timestamp);
+ }
+ if ($this->fld_comment && !empty ($row->log_comment)) {
+ $vals['comment'] = $row->log_comment;
+ }
+
+ return $vals;
+ }
+
+
protected function getAllowedParams() {
+ global $wgLogTypes;
return array (
- 'type' => array (
+ 'prop' => array (
ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_DFLT => 'ids|title|type|user|timestamp|comment|details',
ApiBase :: PARAM_TYPE => array (
- 'block',
- 'protect',
- 'rights',
- 'delete',
- 'upload',
- 'move',
- 'import',
- 'renameuser',
- 'newusers',
- 'makebot'
+ 'ids',
+ 'title',
+ 'type',
+ 'user',
+ 'timestamp',
+ 'comment',
+ 'details',
)
),
+ 'type' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => $wgLogTypes
+ ),
'start' => array (
ApiBase :: PARAM_TYPE => 'timestamp'
),
@@ -140,7 +237,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
)
);
@@ -169,7 +266,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLogEvents.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryLogEvents.php 24256 2007-07-18 21:47:09Z robchurch $';
}
}
-?>
+
diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php
index 25f7ff3e..309beaf9 100644
--- a/includes/api/ApiQueryRecentChanges.php
+++ b/includes/api/ApiQueryRecentChanges.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,9 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * A query action to enumerate the recent changes that were done to the wiki.
+ * Various filters are supported.
+ *
* @addtogroup API
*/
class ApiQueryRecentChanges extends ApiQueryBase {
@@ -37,6 +40,10 @@ class ApiQueryRecentChanges extends ApiQueryBase {
parent :: __construct($query, $moduleName, 'rc');
}
+ private $fld_comment = false, $fld_user = false, $fld_flags = false,
+ $fld_timestamp = false, $fld_title = false, $fld_ids = false,
+ $fld_sizes = false;
+
public function execute() {
$limit = $prop = $namespace = $show = $dir = $start = $end = null;
extract($this->extractRequestParams());
@@ -44,6 +51,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$this->addTables('recentchanges');
$this->addWhereRange('rc_timestamp', $dir, $start, $end);
$this->addWhereFld('rc_namespace', $namespace);
+ $this->addWhereFld('rc_deleted', 0);
if (!is_null($show)) {
$show = array_flip($show);
@@ -62,9 +70,6 @@ class ApiQueryRecentChanges extends ApiQueryBase {
'rc_timestamp',
'rc_namespace',
'rc_title',
- 'rc_cur_id',
- 'rc_this_oldid',
- 'rc_last_oldid',
'rc_type',
'rc_moved_to_ns',
'rc_moved_to_title'
@@ -72,16 +77,27 @@ class ApiQueryRecentChanges extends ApiQueryBase {
if (!is_null($prop)) {
$prop = array_flip($prop);
- $this->addFieldsIf('rc_comment', isset ($prop['comment']));
- if (isset ($prop['user'])) {
- $this->addFields('rc_user');
- $this->addFields('rc_user_text');
- }
- if (isset ($prop['flags'])) {
- $this->addFields('rc_minor');
- $this->addFields('rc_bot');
- $this->addFields('rc_new');
- }
+
+ $this->fld_comment = isset ($prop['comment']);
+ $this->fld_user = isset ($prop['user']);
+ $this->fld_flags = isset ($prop['flags']);
+ $this->fld_timestamp = isset ($prop['timestamp']);
+ $this->fld_title = isset ($prop['title']);
+ $this->fld_ids = isset ($prop['ids']);
+ $this->fld_sizes = isset ($prop['sizes']);
+
+ $this->addFieldsIf('rc_id', $this->fld_ids);
+ $this->addFieldsIf('rc_cur_id', $this->fld_ids);
+ $this->addFieldsIf('rc_this_oldid', $this->fld_ids);
+ $this->addFieldsIf('rc_last_oldid', $this->fld_ids);
+ $this->addFieldsIf('rc_comment', $this->fld_comment);
+ $this->addFieldsIf('rc_user', $this->fld_user);
+ $this->addFieldsIf('rc_user_text', $this->fld_user);
+ $this->addFieldsIf('rc_minor', $this->fld_flags);
+ $this->addFieldsIf('rc_bot', $this->fld_flags);
+ $this->addFieldsIf('rc_new', $this->fld_flags);
+ $this->addFieldsIf('rc_old_len', $this->fld_sizes);
+ $this->addFieldsIf('rc_new_len', $this->fld_sizes);
}
$this->addOption('LIMIT', $limit +1);
@@ -91,15 +107,16 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$count = 0;
$db = $this->getDB();
$res = $this->select(__METHOD__);
+
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter('start', $row->rc_timestamp);
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp));
break;
}
- $vals = $this->addRowInfo('rc', $row);
- if ($vals)
+ $vals = $this->extractRowInfo($row);
+ if($vals)
$data[] = $vals;
}
$db->freeResult($res);
@@ -109,6 +126,59 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$result->addValue('query', $this->getModuleName(), $data);
}
+ private function extractRowInfo($row) {
+ $movedToTitle = false;
+ if (!empty($row->rc_moved_to_title))
+ $movedToTitle = Title :: makeTitle($row->rc_moved_to_ns, $row->rc_moved_to_title);
+
+ $title = Title :: makeTitle($row->rc_namespace, $row->rc_title);
+ $vals = array ();
+
+ $vals['type'] = intval($row->rc_type);
+
+ if ($this->fld_title) {
+ ApiQueryBase :: addTitleInfo($vals, $title);
+ if ($movedToTitle)
+ ApiQueryBase :: addTitleInfo($vals, $movedToTitle, "new_");
+ }
+
+ if ($this->fld_ids) {
+ $vals['rcid'] = intval($row->rc_id);
+ $vals['pageid'] = intval($row->rc_cur_id);
+ $vals['revid'] = intval($row->rc_this_oldid);
+ $vals['old_revid'] = intval( $row->rc_last_oldid );
+ }
+
+ if ($this->fld_user) {
+ $vals['user'] = $row->rc_user_text;
+ if(!$row->rc_user)
+ $vals['anon'] = '';
+ }
+
+ if ($this->fld_flags) {
+ if ($row->rc_bot)
+ $vals['bot'] = '';
+ if ($row->rc_new)
+ $vals['new'] = '';
+ if ($row->rc_minor)
+ $vals['minor'] = '';
+ }
+
+ if ($this->fld_sizes) {
+ $vals['oldlen'] = intval($row->rc_old_len);
+ $vals['newlen'] = intval($row->rc_new_len);
+ }
+
+ if ($this->fld_timestamp)
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp);
+
+ if ($this->fld_comment && !empty ($row->rc_comment)) {
+ $vals['comment'] = $row->rc_comment;
+ }
+
+ return $vals;
+ }
+
protected function getAllowedParams() {
return array (
'start' => array (
@@ -130,10 +200,15 @@ class ApiQueryRecentChanges extends ApiQueryBase {
),
'prop' => array (
ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_DFLT => 'title|timestamp|ids',
ApiBase :: PARAM_TYPE => array (
'user',
'comment',
- 'flags'
+ 'flags',
+ 'timestamp',
+ 'title',
+ 'ids',
+ 'sizes'
)
),
'show' => array (
@@ -151,7 +226,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
)
);
@@ -183,7 +258,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 24100 2007-07-15 01:12:54Z yurik $';
}
}
-?>
+
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php
index fc5f6241..2672478b 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,10 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * A query action to enumerate revisions of a given page, or show top revisions of multiple pages.
+ * Various pieces of information may be shown - flags, comments, and the actual wiki markup of the rev.
+ * In the enumeration mode, ranges of revisions may be requested and filtered.
+ *
* @addtogroup API
*/
class ApiQueryRevisions extends ApiQueryBase {
@@ -37,15 +41,18 @@ class ApiQueryRevisions extends ApiQueryBase {
parent :: __construct($query, $moduleName, 'rv');
}
+ private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false,
+ $fld_comment = false, $fld_user = false, $fld_content = false;
+
public function execute() {
- $limit = $startid = $endid = $start = $end = $dir = $prop = null;
+ $limit = $startid = $endid = $start = $end = $dir = $prop = $user = $excludeuser = null;
extract($this->extractRequestParams());
// If any of those parameters are used, work in 'enumeration' mode.
// Enum mode can only be used when exactly one page is provided.
// Enumerating revisions on multiple pages make it extremelly
// difficult to manage continuations and require additional sql indexes
- $enumRevMode = (!is_null($limit) || !is_null($startid) || !is_null($endid) || $dir === 'newer' || !is_null($start) || !is_null($end));
+ $enumRevMode = (!is_null($user) || !is_null($excludeuser) || !is_null($limit) || !is_null($startid) || !is_null($endid) || $dir === 'newer' || !is_null($start) || !is_null($end));
$pageSet = $this->getPageSet();
$pageCount = $pageSet->getGoodTitleCount();
@@ -59,39 +66,50 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->dieUsage('The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).', 'revids');
if ($pageCount > 1 && $enumRevMode)
- $this->dieUsage('titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, start, and end parameters may only be used on a single page.', 'multpages');
+ $this->dieUsage('titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start, and end parameters may only be used on a single page.', 'multpages');
$this->addTables('revision');
- $this->addFields(array (
- 'rev_id',
- 'rev_page',
- 'rev_text_id',
- 'rev_minor_edit'
- ));
$this->addWhere('rev_deleted=0');
- $showContent = false;
+ $prop = array_flip($prop);
- if (!is_null($prop)) {
- $prop = array_flip($prop);
- $this->addFieldsIf('rev_timestamp', isset ($prop['timestamp']));
- $this->addFieldsIf('rev_comment', isset ($prop['comment']));
- if (isset ($prop['user'])) {
- $this->addFields('rev_user');
- $this->addFields('rev_user_text');
- }
- if (isset ($prop['content'])) {
- $this->addTables('text');
- $this->addWhere('rev_text_id=old_id');
- $this->addFields('old_id');
- $this->addFields('old_text');
- $this->addFields('old_flags');
- $showContent = true;
+ // These field are needed regardless of the client requesting them
+ $this->addFields('rev_id');
+ $this->addFields('rev_page');
+
+ // Optional fields
+ $this->fld_ids = isset ($prop['ids']);
+ // $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed?
+ $this->fld_flags = $this->addFieldsIf('rev_minor_edit', isset ($prop['flags']));
+ $this->fld_timestamp = $this->addFieldsIf('rev_timestamp', isset ($prop['timestamp']));
+ $this->fld_comment = $this->addFieldsIf('rev_comment', isset ($prop['comment']));
+ $this->fld_size = $this->addFieldsIf('rev_len', isset ($prop['size']));
+
+ if (isset ($prop['user'])) {
+ $this->addFields('rev_user');
+ $this->addFields('rev_user_text');
+ $this->fld_user = true;
+ }
+ if (isset ($prop['content'])) {
+
+ // For each page we will request, the user must have read rights for that page
+ foreach ($pageSet->getGoodTitles() as $title) {
+ if( !$title->userCanRead() )
+ $this->dieUsage(
+ 'The current user is not allowed to read ' . $title->getPrefixedText(),
+ 'accessdenied');
}
+
+ $this->addTables('text');
+ $this->addWhere('rev_text_id=old_id');
+ $this->addFields('old_id');
+ $this->addFields('old_text');
+ $this->addFields('old_flags');
+ $this->fld_content = true;
}
- $userMax = ($showContent ? 50 : 500);
- $botMax = ($showContent ? 200 : 10000);
+ $userMax = ($this->fld_content ? 50 : 500);
+ $botMax = ($this->fld_content ? 200 : 10000);
if ($enumRevMode) {
@@ -102,6 +120,9 @@ class ApiQueryRevisions extends ApiQueryBase {
if (!is_null($endid) && !is_null($end))
$this->dieUsage('end and endid cannot be used together', 'badparams');
+ if(!is_null($user) && !is_null( $excludeuser))
+ $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
+
// This code makes an assumption that sorting by rev_id and rev_timestamp produces
// the same result. This way users may request revisions starting at a given time,
// but to page through results use the rev_id returned after each page.
@@ -117,10 +138,25 @@ class ApiQueryRevisions extends ApiQueryBase {
// must manually initialize unset limit
if (is_null($limit))
$limit = 10;
- $this->validateLimit($this->encodeParamName('limit'), $limit, 1, $userMax, $botMax);
+ $this->validateLimit('limit', $limit, 1, $userMax, $botMax);
// There is only one ID, use it
$this->addWhereFld('rev_page', current(array_keys($pageSet->getGoodTitles())));
+
+ if(!is_null($user)) {
+ $this->addWhereFld('rev_user_text', $user);
+ } elseif (!is_null( $excludeuser)) {
+ $this->addWhere('rev_user_text != ' . $this->getDB()->addQuotes($excludeuser));
+ }
+ }
+ elseif ($revCount > 0) {
+ $this->validateLimit('rev_count', $revCount, 1, $userMax, $botMax);
+
+ // Get all revision IDs
+ $this->addWhereFld('rev_id', array_keys($pageSet->getRevisionIDs()));
+
+ // assumption testing -- we should never get more then $revCount rows.
+ $limit = $revCount;
}
elseif ($pageCount > 0) {
// When working in multi-page non-enumeration mode,
@@ -133,15 +169,8 @@ class ApiQueryRevisions extends ApiQueryBase {
// Get all page IDs
$this->addWhereFld('page_id', array_keys($pageSet->getGoodTitles()));
- $limit = $pageCount; // assumption testing -- we should never get more then $pageCount rows.
- }
- elseif ($revCount > 0) {
- $this->validateLimit('rev_count', $revCount, 1, $userMax, $botMax);
-
- // Get all revision IDs
- $this->addWhereFld('rev_id', array_keys($pageSet->getRevisionIDs()));
-
- $limit = $revCount; // assumption testing -- we should never get more then $revCount rows.
+ // assumption testing -- we should never get more then $pageCount rows.
+ $limit = $pageCount;
} else
ApiBase :: dieDebug(__METHOD__, 'param validation?');
@@ -158,21 +187,18 @@ class ApiQueryRevisions extends ApiQueryBase {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
if (!$enumRevMode)
ApiBase :: dieDebug(__METHOD__, 'Got more rows then expected'); // bug report
- $this->setContinueEnumParameter('startid', $row->rev_id);
+ $this->setContinueEnumParameter('startid', intval($row->rev_id));
break;
}
- $vals = $this->addRowInfo('rev', $row);
- if ($vals) {
- if ($showContent)
- ApiResult :: setContent($vals, Revision :: getRevisionText($row));
-
- $this->getResult()->addValue(array (
+ $this->getResult()->addValue(
+ array (
'query',
'pages',
- intval($row->rev_page
- ), 'revisions'), intval($row->rev_id), $vals);
- }
+ intval($row->rev_page),
+ 'revisions'),
+ null,
+ $this->extractRowInfo($row));
}
$db->freeResult($res);
@@ -188,21 +214,62 @@ class ApiQueryRevisions extends ApiQueryBase {
}
}
+ private function extractRowInfo($row) {
+
+ $vals = array ();
+
+ if ($this->fld_ids) {
+ $vals['revid'] = intval($row->rev_id);
+ // $vals['oldid'] = intval($row->rev_text_id); // todo: should this be exposed?
+ }
+
+ if ($this->fld_flags && $row->rev_minor_edit)
+ $vals['minor'] = '';
+
+ if ($this->fld_user) {
+ $vals['user'] = $row->rev_user_text;
+ if (!$row->rev_user)
+ $vals['anon'] = '';
+ }
+
+ if ($this->fld_timestamp) {
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rev_timestamp);
+ }
+
+ if ($this->fld_size && !is_null($row->rev_len)) {
+ $vals['size'] = intval($row->rev_len);
+ }
+
+ if ($this->fld_comment && !empty ($row->rev_comment)) {
+ $vals['comment'] = $row->rev_comment;
+ }
+
+ if ($this->fld_content) {
+ ApiResult :: setContent($vals, Revision :: getRevisionText($row));
+ }
+
+ return $vals;
+ }
+
protected function getAllowedParams() {
return array (
'prop' => array (
ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_DFLT => 'ids|timestamp|flags|comment|user',
ApiBase :: PARAM_TYPE => array (
+ 'ids',
+ 'flags',
'timestamp',
'user',
+ 'size',
'comment',
- 'content'
+ 'content',
)
),
'limit' => array (
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_SML1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_SML1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_SML2
),
'startid' => array (
@@ -223,6 +290,12 @@ class ApiQueryRevisions extends ApiQueryBase {
'newer',
'older'
)
+ ),
+ 'user' => array(
+ ApiBase :: PARAM_TYPE => 'user'
+ ),
+ 'excludeuser' => array(
+ ApiBase :: PARAM_TYPE => 'user'
)
);
}
@@ -235,7 +308,9 @@ class ApiQueryRevisions extends ApiQueryBase {
'endid' => 'stop revision enumeration on this revid (enum)',
'start' => 'from which revision timestamp to start enumeration (enum)',
'end' => 'enumerate up to this timestamp (enum)',
- 'dir' => 'direction of enumeration - towards "newer" or "older" revisions (enum)'
+ 'dir' => 'direction of enumeration - towards "newer" or "older" revisions (enum)',
+ 'user' => 'only include revisions made by user',
+ 'excludeuser' => 'exclude revisions made by user',
);
}
@@ -259,12 +334,16 @@ class ApiQueryRevisions extends ApiQueryBase {
'Get first 5 revisions of the "Main Page":',
' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvdir=newer',
'Get first 5 revisions of the "Main Page" made after 2006-05-01:',
- ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvdir=newer&rvstart=20060501000000'
+ ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvdir=newer&rvstart=20060501000000',
+ 'Get first 5 revisions of the "Main Page" that were not made made by anonymous user "127.0.0.1"',
+ ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvexcludeuser=127.0.0.1',
+ 'Get first 5 revisions of the "Main Page" that were made by the user "MediaWiki default"',
+ ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvuser=MediaWiki%20default',
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRevisions.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryRevisions.php 25407 2007-09-02 14:00:11Z tstarling $';
}
}
-?>
+
diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php
new file mode 100644
index 00000000..268616b1
--- /dev/null
+++ b/includes/api/ApiQuerySearch.php
@@ -0,0 +1,151 @@
+<?php
+
+/*
+ * Created on July 30, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ('ApiQueryBase.php');
+}
+
+/**
+ * Query module to perform full text search within wiki titles and content
+ *
+ * @addtogroup API
+ */
+class ApiQuerySearch extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'sr');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ $params = $this->extractRequestParams();
+
+ $limit = $params['limit'];
+ $query = $params['search'];
+ if (is_null($query) || empty($query))
+ $this->dieUsage("empty search string is not allowed", 'param-search');
+
+ $search = SearchEngine::create();
+ $search->setLimitOffset( $limit+1, $params['offset'] );
+ $search->setNamespaces( $params['namespace'] );
+ $search->showRedirects = $params['redirects'];
+
+ if ($params['what'] == 'text')
+ $matches = $search->searchText( $query );
+ else
+ $matches = $search->searchTitle( $query );
+
+ $data = array ();
+ $count = 0;
+ while( $result = $matches->next() ) {
+ if (++ $count > $limit) {
+ // We've reached the one extra which shows that there are additional items to be had. Stop here...
+ $this->setContinueEnumParameter('offset', $params['offset'] + $params['limit']);
+ break;
+ }
+
+ $title = $result->getTitle();
+ if (is_null($resultPageSet)) {
+ $data[] = array(
+ 'ns' => intval($title->getNamespace()),
+ 'title' => $title->getPrefixedText());
+ } else {
+ $data[] = $title;
+ }
+ }
+
+ if (is_null($resultPageSet)) {
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, 'p');
+ $result->addValue('query', $this->getModuleName(), $data);
+ } else {
+ $resultPageSet->populateFromTitles($data);
+ }
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'search' => null,
+ 'namespace' => array (
+ ApiBase :: PARAM_DFLT => 0,
+ ApiBase :: PARAM_TYPE => 'namespace',
+ ApiBase :: PARAM_ISMULTI => true,
+ ),
+ 'what' => array (
+ ApiBase :: PARAM_DFLT => 'title',
+ ApiBase :: PARAM_TYPE => array (
+ 'title',
+ 'text',
+ )
+ ),
+ 'redirects' => false,
+ 'offset' => 0,
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'search' => 'Search for all page titles (or content) that has this value.',
+ 'namespace' => 'The namespace(s) to enumerate.',
+ 'what' => 'Search inside the text or titles.',
+ 'redirects' => 'Include redirect pages in the search.',
+ 'offset' => 'Use this value to continue paging (return by query)',
+ 'limit' => 'How many total pages to return.'
+ );
+ }
+
+ protected function getDescription() {
+ return 'Perform a full text search';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=search&srsearch=meaning',
+ 'api.php?action=query&list=search&srwhat=text&srsearch=meaning',
+ 'api.php?action=query&generator=search&gsrsearch=meaning&prop=info',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQuerySearch.php 24453 2007-07-30 08:09:15Z yurik $';
+ }
+}
+
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index fa185c97..1fa3d8fc 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * A query action to return meta information about the wiki site.
+ *
* @addtogroup API
*/
class ApiQuerySiteinfo extends ApiQueryBase {
@@ -38,58 +40,164 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
public function execute() {
- $prop = null;
- extract($this->extractRequestParams());
- foreach ($prop as $p) {
- switch ($p) {
+ $params = $this->extractRequestParams();
+ foreach ($params['prop'] as $p) {
+ switch ($p) {
+ default :
+ ApiBase :: dieDebug(__METHOD__, "Unknown prop=$p");
case 'general' :
-
- global $wgSitename, $wgVersion, $wgCapitalLinks, $wgRightsCode, $wgRightsText;
- $data = array ();
- $mainPage = Title :: newFromText(wfMsgForContent('mainpage'));
- $data['mainpage'] = $mainPage->getText();
- $data['base'] = $mainPage->getFullUrl();
- $data['sitename'] = $wgSitename;
- $data['generator'] = "MediaWiki $wgVersion";
- $data['case'] = $wgCapitalLinks ? 'first-letter' : 'case-sensitive'; // 'case-insensitive' option is reserved for future
- if (isset($wgRightsCode))
- $data['rightscode'] = $wgRightsCode;
- $data['rights'] = $wgRightsText;
- $this->getResult()->addValue('query', $p, $data);
+ $this->appendGeneralInfo($p);
break;
-
case 'namespaces' :
-
- global $wgContLang;
- $data = array ();
- foreach ($wgContLang->getFormattedNamespaces() as $ns => $title) {
- $data[$ns] = array (
- 'id' => $ns
- );
- ApiResult :: setContent($data[$ns], $title);
- }
- $this->getResult()->setIndexedTagName($data, 'ns');
- $this->getResult()->addValue('query', $p, $data);
+ $this->appendNamespaces($p);
+ break;
+ case 'interwikimap' :
+ $filteriw = isset($params['filteriw']) ? $params['filteriw'] : false;
+ $this->appendInterwikiMap($p, $filteriw);
+ break;
+ case 'dbrepllag' :
+ $this->appendDbReplLagInfo($p, $params['showalldb']);
+ break;
+ case 'statistics' :
+ $this->appendStatistics($p);
break;
-
- default :
- ApiBase :: dieDebug(__METHOD__, "Unknown prop=$p");
}
}
}
+ protected function appendGeneralInfo($property) {
+ global $wgSitename, $wgVersion, $wgCapitalLinks, $wgRightsCode, $wgRightsText, $wgLanguageCode;
+
+ $data = array ();
+ $mainPage = Title :: newFromText(wfMsgForContent('mainpage'));
+ $data['mainpage'] = $mainPage->getText();
+ $data['base'] = $mainPage->getFullUrl();
+ $data['sitename'] = $wgSitename;
+ $data['generator'] = "MediaWiki $wgVersion";
+ $data['case'] = $wgCapitalLinks ? 'first-letter' : 'case-sensitive'; // 'case-insensitive' option is reserved for future
+ if (isset($wgRightsCode))
+ $data['rightscode'] = $wgRightsCode;
+ $data['rights'] = $wgRightsText;
+ $data['lang'] = $wgLanguageCode;
+
+ $this->getResult()->addValue('query', $property, $data);
+ }
+
+ protected function appendNamespaces($property) {
+ global $wgContLang;
+
+ $data = array ();
+ foreach ($wgContLang->getFormattedNamespaces() as $ns => $title) {
+ $data[$ns] = array (
+ 'id' => $ns
+ );
+ ApiResult :: setContent($data[$ns], $title);
+ }
+
+ $this->getResult()->setIndexedTagName($data, 'ns');
+ $this->getResult()->addValue('query', $property, $data);
+ }
+
+ protected function appendInterwikiMap($property, $filter) {
+
+ $this->resetQueryParams();
+ $this->addTables('interwiki');
+ $this->addFields(array('iw_prefix', 'iw_local', 'iw_url'));
+
+ if($filter === 'local') {
+ $this->addWhere('iw_local = 1');
+ } elseif($filter === '!local') {
+ $this->addWhere('iw_local = 0');
+ } elseif($filter !== false) {
+ ApiBase :: dieDebug(__METHOD__, "Unknown filter=$filter");
+ }
+
+ $this->addOption('ORDER BY', 'iw_prefix');
+
+ $db = $this->getDB();
+ $res = $this->select(__METHOD__);
+
+ $data = array();
+ while($row = $db->fetchObject($res))
+ {
+ $val['prefix'] = $row->iw_prefix;
+ if ($row->iw_local == '1')
+ $val['local'] = '';
+// $val['trans'] = intval($row->iw_trans); // should this be exposed?
+ $val['url'] = $row->iw_url;
+
+ $data[] = $val;
+ }
+ $db->freeResult($res);
+
+ $this->getResult()->setIndexedTagName($data, 'iw');
+ $this->getResult()->addValue('query', $property, $data);
+ }
+
+ protected function appendDbReplLagInfo($property, $includeAll) {
+ global $wgLoadBalancer, $wgShowHostnames;
+
+ $data = array();
+
+ if ($includeAll) {
+ if (!$wgShowHostnames)
+ $this->dieUsage('Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied');
+
+ global $wgDBservers;
+ $lags = $wgLoadBalancer->getLagTimes();
+ foreach( $lags as $i => $lag ) {
+ $data[] = array (
+ 'host' => $wgDBservers[$i]['host'],
+ 'lag' => $lag);
+ }
+ } else {
+ list( $host, $lag ) = $wgLoadBalancer->getMaxLag();
+ $data[] = array (
+ 'host' => $wgShowHostnames ? $host : '',
+ 'lag' => $lag);
+ }
+
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, 'db');
+ $result->addValue('query', $property, $data);
+ }
+
+ protected function appendStatistics($property) {
+ $data = array ();
+ $data['pages'] = intval(SiteStats::pages());
+ $data['articles'] = intval(SiteStats::articles());
+ $data['views'] = intval(SiteStats::views());
+ $data['edits'] = intval(SiteStats::edits());
+ $data['images'] = intval(SiteStats::images());
+ $data['users'] = intval(SiteStats::users());
+ $data['admins'] = intval(SiteStats::admins());
+ $data['jobs'] = intval(SiteStats::jobs());
+ $this->getResult()->addValue('query', $property, $data);
+ }
+
protected function getAllowedParams() {
return array (
+
'prop' => array (
ApiBase :: PARAM_DFLT => 'general',
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array (
'general',
- 'namespaces'
- )
- )
+ 'namespaces',
+ 'interwikimap',
+ 'dbrepllag',
+ 'statistics',
+ )),
+
+ 'filteriw' => array (
+ ApiBase :: PARAM_TYPE => array (
+ 'local',
+ '!local',
+ )),
+
+ 'showalldb' => false,
);
}
@@ -97,9 +205,14 @@ class ApiQuerySiteinfo extends ApiQueryBase {
return array (
'prop' => array (
'Which sysinfo properties to get:',
- ' "general" - Overall system information',
- ' "namespaces" - List of registered namespaces (localized)'
- )
+ ' "general" - Overall system information',
+ ' "namespaces" - List of registered namespaces (localized)',
+ ' "statistics" - Returns site statistics',
+ ' "interwikimap" - Returns interwiki map (optionally filtered)',
+ ' "dbrepllag" - Returns database server with the highest replication lag',
+ ),
+ 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
+ 'showalldb' => 'List all database servers, not just the one lagging the most',
);
}
@@ -108,11 +221,14 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
protected function getExamples() {
- return 'api.php?action=query&meta=siteinfo&siprop=general|namespaces';
+ return array(
+ 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|statistics',
+ 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
+ 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb',
+ );
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 25238 2007-08-28 15:37:31Z robchurch $';
}
-}
-?>
+} \ No newline at end of file
diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php
index 05bfbb20..05c3d945 100644
--- a/includes/api/ApiQueryUserContributions.php
+++ b/includes/api/ApiQueryUserContributions.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,8 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This query action adds a list of a specified user's contributions to the output.
+ *
* @addtogroup API
*/
class ApiQueryContributions extends ApiQueryBase {
@@ -37,83 +39,49 @@ class ApiQueryContributions extends ApiQueryBase {
parent :: __construct($query, $moduleName, 'uc');
}
+ private $params, $username;
+ private $fld_ids = false, $fld_title = false, $fld_timestamp = false,
+ $fld_comment = false, $fld_flags = false;
+
public function execute() {
- //Blank all our variables
- $limit = $user = $start = $end = $dir = null;
+ // Parse some parameters
+ $this->params = $this->extractRequestParams();
- //Get our parameters out
- extract($this->extractRequestParams());
+ $prop = array_flip($this->params['prop']);
+ $this->fld_ids = isset($prop['ids']);
+ $this->fld_title = isset($prop['title']);
+ $this->fld_comment = isset($prop['comment']);
+ $this->fld_flags = isset($prop['flags']);
+ $this->fld_timestamp = isset($prop['timestamp']);
- //Get a database instance
+ // TODO: if the query is going only against the revision table, should this be done?
+ $this->selectNamedDB('contributions', DB_SLAVE, 'contributions');
$db = $this->getDB();
- if (is_null($user))
- $this->dieUsage("User parameter may not be empty", 'param_user');
- $userid = $db->selectField('user', 'user_id', array (
- 'user_name' => $user
- ));
- if (!$userid)
- $this->dieUsage("User name $user not found", 'param_user');
-
- //Get the table names
- list ($tbl_page, $tbl_revision) = $db->tableNamesN('page', 'revision');
-
- //We're after the revision table, and the corresponding page row for
- //anything we retrieve.
- $this->addTables("$tbl_revision LEFT OUTER JOIN $tbl_page ON " .
- "page_id=rev_page");
-
- //We want to know the namespace, title, new-ness, and ID of a page,
- // and the id, text-id, timestamp, minor-status, summary and page
- // of a revision.
- $this->addFields(array('page_namespace', 'page_title', 'page_is_new',
- 'rev_id', 'rev_text_id', 'rev_timestamp', 'rev_minor_edit',
- 'rev_comment', 'rev_page'));
-
- // We only want pages by the specified user.
- $this->addWhereFld('rev_user_text', $user);
- // ... and in the specified timeframe.
- $this->addWhereRange('rev_timestamp', $dir, $start, $end );
+ // Prepare query
+ $this->prepareUsername();
+ $this->prepareQuery();
- $this->addOption('LIMIT', $limit + 1);
+ //Do the actual query.
+ $res = $this->select( __METHOD__ );
//Initialise some variables
$data = array ();
$count = 0;
-
- //Do the actual query.
- $res = $this->select( __METHOD__ );
+ $limit = $this->params['limit'];
//Fetch each row
while ( $row = $db->fetchObject( $res ) ) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter('start', $row->rev_timestamp);
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rev_timestamp));
break;
}
- //There's a fancy function in ApiQueryBase that does
- // most of the work for us. Use that for the page
- // and revision.
- $revvals = $this->addRowInfo('rev', $row);
- $pagevals = $this->addRowInfo('page', $row);
-
- //If we got data on the revision only, use only
- // that data.
- if($revvals && !$pagevals) {
- $data[] = $revvals;
- }
- //If we got data on the page only, use only
- // that data.
- else if($pagevals && !$revvals) {
- $data[] = $pagevals;
- }
- //... and if we got data on both the revision and
- // the page, merge the data and send it out.
- else if($pagevals && $revvals) {
- $data[] = array_merge($revvals, $pagevals);
- }
+ $vals = $this->extractRowInfo($row);
+ if ($vals)
+ $data[] = $vals;
}
//Free the database record so the connection can get on with other stuff
@@ -124,13 +92,117 @@ class ApiQueryContributions extends ApiQueryBase {
$this->getResult()->addValue('query', $this->getModuleName(), $data);
}
+ /**
+ * Validate the 'user' parameter and set the value to compare
+ * against `revision`.`rev_user_text`
+ */
+ private function prepareUsername() {
+ $user = $this->params['user'];
+ if( $user ) {
+ $name = User::isIP( $user )
+ ? $user
+ : User::getCanonicalName( $user, 'valid' );
+ if( $name === false ) {
+ $this->dieUsage( "User name {$user} is not valid", 'param_user' );
+ } else {
+ $this->username = $name;
+ }
+ } else {
+ $this->dieUsage( 'User parameter may not be empty', 'param_user' );
+ }
+ }
+
+ /**
+ * Prepares the query and returns the limit of rows requested
+ */
+ private function prepareQuery() {
+
+ //We're after the revision table, and the corresponding page row for
+ //anything we retrieve.
+ list ($tbl_page, $tbl_revision) = $this->getDB()->tableNamesN('page', 'revision');
+ $this->addTables("$tbl_revision LEFT OUTER JOIN $tbl_page ON page_id=rev_page");
+
+ $this->addWhereFld('rev_deleted', 0);
+
+ // We only want pages by the specified user.
+ $this->addWhereFld( 'rev_user_text', $this->username );
+
+ // ... and in the specified timeframe.
+ $this->addWhereRange('rev_timestamp',
+ $this->params['dir'], $this->params['start'], $this->params['end'] );
+
+ $this->addWhereFld('page_namespace', $this->params['namespace']);
+
+ $show = $this->params['show'];
+ if (!is_null($show)) {
+ $show = array_flip($show);
+ if (isset ($show['minor']) && isset ($show['!minor']))
+ $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show');
+
+ $this->addWhereIf('rev_minor_edit = 0', isset ($show['!minor']));
+ $this->addWhereIf('rev_minor_edit != 0', isset ($show['minor']));
+ }
+
+ $this->addOption('LIMIT', $this->params['limit'] + 1);
+
+ // Mandatory fields: timestamp allows request continuation
+ // ns+title checks if the user has access rights for this page
+ $this->addFields(array(
+ 'rev_timestamp',
+ 'page_namespace',
+ 'page_title',
+ ));
+
+ $this->addFieldsIf('rev_page', $this->fld_ids);
+ $this->addFieldsIf('rev_id', $this->fld_ids);
+ // $this->addFieldsIf('rev_text_id', $this->fld_ids); // Should this field be exposed?
+ $this->addFieldsIf('rev_comment', $this->fld_comment);
+ $this->addFieldsIf('rev_minor_edit', $this->fld_flags);
+
+ // These fields depend only work if the page table is joined
+ $this->addFieldsIf('page_is_new', $this->fld_flags);
+ }
+
+ /**
+ * Extract fields from the database row and append them to a result array
+ */
+ private function extractRowInfo($row) {
+
+ $vals = array();
+
+ if ($this->fld_ids) {
+ $vals['pageid'] = intval($row->rev_page);
+ $vals['revid'] = intval($row->rev_id);
+ // $vals['textid'] = intval($row->rev_text_id); // todo: Should this field be exposed?
+ }
+
+ if ($this->fld_title)
+ ApiQueryBase :: addTitleInfo($vals,
+ Title :: makeTitle($row->page_namespace, $row->page_title));
+
+ if ($this->fld_timestamp)
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rev_timestamp);
+
+ if ($this->fld_flags) {
+ if ($row->page_is_new)
+ $vals['new'] = '';
+ if ($row->rev_minor_edit)
+ $vals['minor'] = '';
+ }
+
+ if ($this->fld_comment && !empty ($row->rev_comment))
+ $vals['comment'] = $row->rev_comment;
+
+ return $vals;
+ }
+
protected function getAllowedParams() {
return array (
'limit' => array (
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
),
'start' => array (
@@ -139,14 +211,38 @@ class ApiQueryContributions extends ApiQueryBase {
'end' => array (
ApiBase :: PARAM_TYPE => 'timestamp'
),
- 'user' => null,
+ 'user' => array (
+ ApiBase :: PARAM_TYPE => 'user'
+ ),
'dir' => array (
ApiBase :: PARAM_DFLT => 'older',
ApiBase :: PARAM_TYPE => array (
'newer',
'older'
)
- )
+ ),
+ 'namespace' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => 'namespace'
+ ),
+ 'prop' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_DFLT => 'ids|title|timestamp|flags|comment',
+ ApiBase :: PARAM_TYPE => array (
+ 'ids',
+ 'title',
+ 'timestamp',
+ 'comment',
+ 'flags'
+ )
+ ),
+ 'show' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'minor',
+ '!minor',
+ )
+ ),
);
}
@@ -156,12 +252,15 @@ class ApiQueryContributions extends ApiQueryBase {
'start' => 'The start timestamp to return from.',
'end' => 'The end timestamp to return to.',
'user' => 'The user to retrieve contributions for.',
- 'dir' => 'The direction to search (older or newer).'
+ 'dir' => 'The direction to search (older or newer).',
+ 'namespace' => 'Only list contributions in these namespaces',
+ 'prop' => 'Include additional pieces of information',
+ 'show' => 'Show only items that meet this criteria, e.g. non minor edits only: show=!minor',
);
}
protected function getDescription() {
- return 'Get edits by a user..';
+ return 'Get all edits by a user';
}
protected function getExamples() {
@@ -171,7 +270,7 @@ class ApiQueryContributions extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUserContributions.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryUserContributions.php 24754 2007-08-13 18:18:18Z robchurch $';
}
}
-?>
+
diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php
new file mode 100644
index 00000000..a41b8679
--- /dev/null
+++ b/includes/api/ApiQueryUserInfo.php
@@ -0,0 +1,133 @@
+<?php
+
+/*
+ * Created on July 30, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ('ApiQueryBase.php');
+}
+
+/**
+ * Query module to get information about the currently logged-in user
+ *
+ * @addtogroup API
+ */
+class ApiQueryUserInfo extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'ui');
+ }
+
+ public function execute() {
+
+ global $wgUser;
+
+ $params = $this->extractRequestParams();
+ $result = $this->getResult();
+
+ $vals = array();
+ $vals['name'] = $wgUser->getName();
+
+ if( $wgUser->isAnon() ) $vals['anon'] = '';
+
+ if (!is_null($params['prop'])) {
+ $prop = array_flip($params['prop']);
+ if (isset($prop['blockinfo'])) {
+ if ($wgUser->isBlocked()) {
+ $vals['blockedby'] = User::whoIs($wgUser->blockedBy());
+ $vals['blockreason'] = $wgUser->blockedFor();
+ }
+ }
+ if (isset($prop['hasmsg']) && $wgUser->getNewtalk()) {
+ $vals['messages'] = '';
+ }
+ if (isset($prop['groups'])) {
+ $vals['groups'] = $wgUser->getGroups();
+ $result->setIndexedTagName($vals['groups'], 'g'); // even if empty
+ }
+ if (isset($prop['rights'])) {
+ $vals['rights'] = $wgUser->getRights();
+ $result->setIndexedTagName($vals['rights'], 'r'); // even if empty
+ }
+ }
+
+ if (!empty($params['option'])) {
+ foreach( $params['option'] as $option ) {
+ if (empty($option))
+ $this->dieUsage('Empty value is not allowed for the option parameter', 'option');
+ $vals['options'][$option] = $wgUser->getOption($option);
+ }
+ }
+
+ $result->addValue(null, $this->getModuleName(), $vals);
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'prop' => array (
+ ApiBase :: PARAM_DFLT => NULL,
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'blockinfo',
+ 'hasmsg',
+ 'groups',
+ 'rights',
+ )),
+ 'option' => array (
+ ApiBase :: PARAM_DFLT => NULL,
+ ApiBase :: PARAM_ISMULTI => true,
+ ),
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'prop' => array(
+ 'What pieces of information to include',
+ ' blockinfo - tags if the user is blocked, by whom, and for what reason',
+ ' hasmsg - adds a tag "message" if user has pending messages',
+ ' groups - lists all the groups the current user belongs to',
+ ' rights - lists of all rights the current user has',
+ ),
+ 'option' => 'A list of user preference options to get',
+ );
+ }
+
+ protected function getDescription() {
+ return 'Get information about the current user';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&meta=userinfo',
+ 'api.php?action=query&meta=userinfo&uiprop=blockinfo|groups|rights|hasmsg',
+ 'api.php?action=query&meta=userinfo&uioption=rememberpassword',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryUserInfo.php 24529 2007-08-01 20:11:29Z yurik $';
+ }
+}
+
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index 73c31abb..16586a40 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,9 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This query action allows clients to retrieve a list of recently modified pages
+ * that are part of the logged-in user's watchlist.
+ *
* @addtogroup API
*/
class ApiQueryWatchlist extends ApiQueryGeneratorBase {
@@ -45,8 +48,13 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$this->run($resultPageSet);
}
+ private $fld_ids = false, $fld_title = false, $fld_patrol = false, $fld_flags = false,
+ $fld_timestamp = false, $fld_user = false, $fld_comment = false, $fld_sizes = false;
+
private function run($resultPageSet = null) {
- global $wgUser;
+ global $wgUser, $wgDBtype;
+
+ $this->selectNamedDB('watchlist', DB_SLAVE, 'watchlist');
if (!$wgUser->isLoggedIn())
$this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
@@ -54,17 +62,20 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$allrev = $start = $end = $namespace = $dir = $limit = $prop = null;
extract($this->extractRequestParams());
- $patrol = $timestamp = $user = $comment = false;
- if (!is_null($prop)) {
- if (!is_null($resultPageSet))
- $this->dieUsage('prop parameter may not be used in a generator', 'params');
+ if (!is_null($prop) && is_null($resultPageSet)) {
+
+ $prop = array_flip($prop);
- $user = (false !== array_search('user', $prop));
- $comment = (false !== array_search('comment', $prop));
- $timestamp = (false !== array_search('timestamp', $prop)); // TODO: $timestamp not currently being used.
- $patrol = (false !== array_search('patrol', $prop));
+ $this->fld_ids = isset($prop['ids']);
+ $this->fld_title = isset($prop['title']);
+ $this->fld_flags = isset($prop['flags']);
+ $this->fld_user = isset($prop['user']);
+ $this->fld_comment = isset($prop['comment']);
+ $this->fld_timestamp = isset($prop['timestamp']);
+ $this->fld_sizes = isset($prop['sizes']);
+ $this->fld_patrol = isset($prop['patrol']);
- if ($patrol) {
+ if ($this->fld_patrol) {
global $wgUseRCPatrol, $wgUser;
if (!$wgUseRCPatrol || !$wgUser->isAllowed('patrol'))
$this->dieUsage('patrol property is not available', 'patrol');
@@ -77,15 +88,17 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'rc_this_oldid',
'rc_namespace',
'rc_title',
- 'rc_new',
- 'rc_minor',
'rc_timestamp'
));
- $this->addFieldsIf('rc_user', $user);
- $this->addFieldsIf('rc_user_text', $user);
- $this->addFieldsIf('rc_comment', $comment);
- $this->addFieldsIf('rc_patrolled', $patrol);
+ $this->addFieldsIf('rc_new', $this->fld_flags);
+ $this->addFieldsIf('rc_minor', $this->fld_flags);
+ $this->addFieldsIf('rc_user', $this->fld_user);
+ $this->addFieldsIf('rc_user_text', $this->fld_user);
+ $this->addFieldsIf('rc_comment', $this->fld_comment);
+ $this->addFieldsIf('rc_patrolled', $this->fld_patrol);
+ $this->addFieldsIf('rc_old_len', $this->fld_sizes);
+ $this->addFieldsIf('rc_new_len', $this->fld_sizes);
}
elseif ($allrev) {
$this->addFields(array (
@@ -114,12 +127,16 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'wl_namespace = rc_namespace',
'wl_title = rc_title',
'rc_cur_id = page_id',
- 'wl_user' => $userId
+ 'wl_user' => $userId,
+ 'rc_deleted' => 0,
));
+
$this->addWhereRange('rc_timestamp', $dir, $start, $end);
$this->addWhereFld('wl_namespace', $namespace);
$this->addWhereIf('rc_this_oldid=page_latest', !$allrev);
- $this->addWhereIf("rc_timestamp > ''", !isset ($start) && !isset ($end));
+
+ # This is a index optimization for mysql, as done in the Special:Watchlist page
+ $this->addWhereIf("rc_timestamp > ''", !isset ($start) && !isset ($end) && $wgDBtype == 'mysql');
$this->addOption('LIMIT', $limit +1);
@@ -131,23 +148,19 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter('start', $row->rc_timestamp);
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp));
break;
}
if (is_null($resultPageSet)) {
- $vals = $this->addRowInfo('rc', $row);
- if($vals)
+ $vals = $this->extractRowInfo($row);
+ if ($vals)
$data[] = $vals;
} else {
- $title = Title :: makeTitle($row->rc_namespace, $row->rc_title);
- // skip any pages that user has no rights to read
- if ($title->userCanRead()) {
- if ($allrev) {
- $data[] = intval($row->rc_this_oldid);
- } else {
- $data[] = intval($row->rc_cur_id);
- }
+ if ($allrev) {
+ $data[] = intval($row->rc_this_oldid);
+ } else {
+ $data[] = intval($row->rc_cur_id);
}
}
}
@@ -165,6 +178,50 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
}
+ private function extractRowInfo($row) {
+
+ $vals = array ();
+
+ if ($this->fld_ids) {
+ $vals['pageid'] = intval($row->rc_cur_id);
+ $vals['revid'] = intval($row->rc_this_oldid);
+ }
+
+ if ($this->fld_title)
+ ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->rc_namespace, $row->rc_title));
+
+ if ($this->fld_user) {
+ $vals['user'] = $row->rc_user_text;
+ if (!$row->rc_user)
+ $vals['anon'] = '';
+ }
+
+ if ($this->fld_flags) {
+ if ($row->rc_new)
+ $vals['new'] = '';
+ if ($row->rc_minor)
+ $vals['minor'] = '';
+ }
+
+ if ($this->fld_patrol && isset($row->rc_patrolled))
+ $vals['patrolled'] = '';
+
+ if ($this->fld_timestamp)
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp);
+
+ $this->addFieldsIf('rc_new_len', $this->fld_sizes);
+
+ if ($this->fld_sizes) {
+ $vals['oldlen'] = intval($row->rc_old_len);
+ $vals['newlen'] = intval($row->rc_new_len);
+ }
+
+ if ($this->fld_comment && !empty ($row->rc_comment))
+ $vals['comment'] = $row->rc_comment;
+
+ return $vals;
+ }
+
protected function getAllowedParams() {
return array (
'allrev' => false,
@@ -189,16 +246,21 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
),
'prop' => array (
APIBase :: PARAM_ISMULTI => true,
+ APIBase :: PARAM_DFLT => 'ids|title|flags',
APIBase :: PARAM_TYPE => array (
+ 'ids',
+ 'title',
+ 'flags',
'user',
'comment',
'timestamp',
- 'patrol'
+ 'patrol',
+ 'sizes',
)
)
);
@@ -223,14 +285,15 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
protected function getExamples() {
return array (
'api.php?action=query&list=watchlist',
- 'api.php?action=query&list=watchlist&wlallrev',
+ 'api.php?action=query&list=watchlist&wlprop=ids|title|timestamp|user|comment',
+ 'api.php?action=query&list=watchlist&wlallrev&wlprop=ids|title|timestamp|user|comment',
'api.php?action=query&generator=watchlist&prop=info',
'api.php?action=query&generator=watchlist&gwlallrev&prop=revisions&rvprop=timestamp|user'
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryWatchlist.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiQueryWatchlist.php 24092 2007-07-14 19:04:31Z yurik $';
}
}
-?>
+
diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php
index 79fd34a1..a318d808 100644
--- a/includes/api/ApiResult.php
+++ b/includes/api/ApiResult.php
@@ -5,7 +5,7 @@
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@gmail.com>
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,20 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * This class represents the result of the API operations.
+ * It simply wraps a nested array() structure, adding some functions to simplify array's modifications.
+ * As various modules execute, they add different pieces of information to this result,
+ * structuring it as it will be given to the client.
+ *
+ * Each subarray may either be a dictionary - key-value pairs with unique keys,
+ * or lists, where the items are added using $data[] = $value notation.
+ *
+ * There are two special key values that change how XML output is generated:
+ * '_element' This key sets the tag name for the rest of the elements in the current array.
+ * It is only inserted if the formatter returned true for getNeedsRawData()
+ * '*' This key has special meaning only to the XML formatter, and is outputed as is
+ * for all others. In XML it becomes the content of the current element.
+ *
* @addtogroup API
*/
class ApiResult extends ApiBase {
@@ -44,6 +58,9 @@ class ApiResult extends ApiBase {
$this->reset();
}
+ /**
+ * Clear the current result data.
+ */
public function reset() {
$this->mData = array ();
}
@@ -56,10 +73,16 @@ class ApiResult extends ApiBase {
$this->mIsRawMode = true;
}
+ /**
+ * Returns true if the result is being created for the formatter that requested raw data.
+ */
public function getIsRawMode() {
return $this->mIsRawMode;
}
+ /**
+ * Get result's internal data array
+ */
public function & getData() {
return $this->mData;
}
@@ -103,11 +126,6 @@ class ApiResult extends ApiBase {
}
}
- // public static function makeContentElement($tag, $value) {
- // $result = array();
- // ApiResult::setContent($result, )
- // }
- //
/**
* In case the array contains indexed values (in addition to named),
* all indexed values will have the given tag name.
@@ -125,7 +143,8 @@ class ApiResult extends ApiBase {
/**
* Add value to the output data at the given path.
* Path is an indexed array, each element specifing the branch at which to add the new value
- * Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value
+ * Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value
+ * If $name is empty, the $value is added as a next list element data[] = $value
*/
public function addValue($path, $name, $value) {
@@ -145,7 +164,10 @@ class ApiResult extends ApiBase {
}
}
- ApiResult :: setElement($data, $name, $value);
+ if (empty($name))
+ $data[] = $value; // Add list element
+ else
+ ApiResult :: setElement($data, $name, $value); // Add named element
}
public function execute() {
@@ -153,7 +175,7 @@ class ApiResult extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiResult.php 21402 2007-04-20 08:55:14Z nickj $';
+ return __CLASS__ . ': $Id: ApiResult.php 23531 2007-06-29 01:19:14Z simetrical $';
}
}
-?>
+