summaryrefslogtreecommitdiff
path: root/includes/api
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2007-01-11 19:06:07 +0000
committerPierre Schmitz <pierre@archlinux.de>2007-01-11 19:06:07 +0000
commita58285fd06c8113c45377c655dd43cef6337e815 (patch)
treedfe31d3d12652352fe44890b4811eda0728faefb /includes/api
parent20194986f6638233732ba1fc3e838f117d3cc9ea (diff)
Aktualisierung auf MediaWiki 1.9.0
Diffstat (limited to 'includes/api')
-rw-r--r--includes/api/ApiBase.php200
-rw-r--r--includes/api/ApiFeedWatchlist.php125
-rw-r--r--includes/api/ApiFormatBase.php109
-rw-r--r--includes/api/ApiFormatJson.php23
-rw-r--r--includes/api/ApiFormatPhp.php54
-rw-r--r--includes/api/ApiFormatWddx.php89
-rw-r--r--includes/api/ApiFormatXml.php36
-rw-r--r--includes/api/ApiFormatYaml.php5
-rw-r--r--includes/api/ApiFormatYaml_spyc.php9
-rw-r--r--includes/api/ApiLogin.php6
-rw-r--r--includes/api/ApiMain.php294
-rw-r--r--includes/api/ApiOpenSearch.php109
-rw-r--r--includes/api/ApiPageSet.php138
-rw-r--r--includes/api/ApiQuery.php58
-rw-r--r--includes/api/ApiQueryAllpages.php114
-rw-r--r--includes/api/ApiQueryBacklinks.php358
-rw-r--r--includes/api/ApiQueryBase.php273
-rw-r--r--includes/api/ApiQueryInfo.php13
-rw-r--r--includes/api/ApiQueryLogEvents.php173
-rw-r--r--includes/api/ApiQueryRecentChanges.php187
-rw-r--r--includes/api/ApiQueryRevisions.php194
-rw-r--r--includes/api/ApiQuerySiteinfo.php11
-rw-r--r--includes/api/ApiQueryUserContributions.php175
-rw-r--r--includes/api/ApiQueryWatchlist.php234
-rw-r--r--includes/api/ApiResult.php64
25 files changed, 2589 insertions, 462 deletions
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index f578f41b..1a9c1e3d 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -35,6 +35,11 @@ abstract class ApiBase {
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
+
private $mMainModule, $mModuleName, $mParamPrefix;
/**
@@ -42,7 +47,7 @@ abstract class ApiBase {
*/
public function __construct($mainModule, $moduleName, $paramPrefix = '') {
$this->mMainModule = $mainModule;
- $this->mModuleName = $moduleName;
+ $this->mModuleName = $moduleName;
$this->mParamPrefix = $paramPrefix;
}
@@ -51,12 +56,22 @@ abstract class ApiBase {
*/
public abstract function execute();
- /**
- * Get the name of the query being executed by this instance
- */
- public function getModuleName() {
- return $this->mModuleName;
- }
+ /**
+ * Get the name of the module being executed by this instance
+ */
+ public function getModuleName() {
+ return $this->mModuleName;
+ }
+
+ /**
+ * Get the name of the module as shown in the profiler log
+ */
+ public function getModuleProfileName($db = false) {
+ if ($db)
+ return 'API:' . $this->mModuleName . '-DB';
+ else
+ return 'API:' . $this->mModuleName;
+ }
/**
* Get main module
@@ -91,6 +106,15 @@ abstract class ApiBase {
}
/**
+ * 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.
+ */
+ public function getCustomPrinter() {
+ return null;
+ }
+
+ /**
* Generates help message for this module, or false if there is no description
*/
public function makeHelpMsg() {
@@ -126,8 +150,17 @@ abstract class ApiBase {
if ($this->getMain()->getShowVersions()) {
$versions = $this->getVersion();
- if (is_array($versions))
+ $pattern = '(\$.*) ([0-9a-z_]+\.php) (.*\$)';
+ $replacement = '\\0' . "\n " . 'http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/api/\\2';
+
+ if (is_array($versions)) {
+ foreach ($versions as &$v)
+ $v = eregi_replace($pattern, $replacement, $v);
$versions = implode("\n ", $versions);
+ }
+ else
+ $versions = eregi_replace($pattern, $replacement, $versions);
+
$msg .= "Version:\n $versions\n";
}
}
@@ -141,10 +174,32 @@ abstract class ApiBase {
$paramsDescription = $this->getParamDescription();
$msg = '';
- foreach (array_keys($params) as $paramName) {
+ $paramPrefix = "\n" . str_repeat(' ', 19);
+ foreach ($params as $paramName => $paramSettings) {
$desc = isset ($paramsDescription[$paramName]) ? $paramsDescription[$paramName] : '';
if (is_array($desc))
- $desc = implode("\n" . str_repeat(' ', 19), $desc);
+ $desc = implode($paramPrefix, $desc);
+
+ @ $type = $paramSettings[self :: PARAM_TYPE];
+ if (isset ($type)) {
+ if (isset ($paramSettings[self :: PARAM_ISMULTI]))
+ $prompt = 'Values (separate with \'|\'): ';
+ else
+ $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());
+ }
+ }
+
+ $default = is_array($paramSettings) ? (isset ($paramSettings[self :: PARAM_DFLT]) ? $paramSettings[self :: PARAM_DFLT] : null) : $paramSettings;
+ if (!is_null($default) && $default !== false)
+ $desc .= $paramPrefix . "Default: $default";
+
$msg .= sprintf(" %-14s - %s\n", $this->encodeParamName($paramName), $desc);
}
return $msg;
@@ -180,7 +235,7 @@ abstract class ApiBase {
protected function getParamDescription() {
return false;
}
-
+
/**
* This method mangles parameter name based on the prefix supplied to the constructor.
* Override this method to change parameter name during runtime
@@ -213,13 +268,26 @@ abstract class ApiBase {
return $this->getParameterFromSettings($paramName, $paramSettings);
}
+ public static function getValidNamespaces() {
+ static $mValidNamespaces = null;
+ if (is_null($mValidNamespaces)) {
+
+ global $wgContLang;
+ $mValidNamespaces = array ();
+ foreach (array_keys($wgContLang->getNamespaces()) as $ns) {
+ if ($ns >= 0)
+ $mValidNamespaces[] = $ns;
+ }
+ }
+ return $mValidNamespaces;
+ }
+
/**
* Using the settings determine the value for the given parameter
* @param $paramName String: parameter name
* @param $paramSettings Mixed: default value or an array of settings using PARAM_* constants.
- */
+ */
protected function getParameterFromSettings($paramName, $paramSettings) {
- global $wgRequest;
// Some classes may decide to change parameter names
$paramName = $this->encodeParamName($paramName);
@@ -248,48 +316,58 @@ abstract class ApiBase {
ApiBase :: dieDebug(__METHOD__, "Boolean param $paramName's default is set to '$default'");
}
- $value = $wgRequest->getCheck($paramName);
- } else
- $value = $wgRequest->getVal($paramName, $default);
+ $value = $this->getMain()->getRequest()->getCheck($paramName);
+ } else {
+ $value = $this->getMain()->getRequest()->getVal($paramName, $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);
// More validation only when choices were not given
// choices were validated in parseMultiValue()
- if (!is_array($type) && isset ($value)) {
-
- switch ($type) {
- case 'NULL' : // nothing to do
- break;
- case 'string' : // nothing to do
- break;
- case 'integer' : // Force everything using intval()
- $value = is_array($value) ? array_map('intval', $value) : intval($value);
- 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 ($multi)
- ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
- $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]);
- break;
- case 'boolean' :
- if ($multi)
- ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
- break;
- case 'timestamp' :
- if ($multi)
- ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
- if (!preg_match('/^[0-9]{14}$/', $value))
- $this->dieUsage("Invalid value '$value' for timestamp parameter $paramName", "badtimestamp_{$valueName}");
- break;
- default :
- ApiBase :: dieDebug(__METHOD__, "Param $paramName's type is unknown - $type");
-
+ if (isset ($value)) {
+ if (!is_array($type)) {
+ switch ($type) {
+ case 'NULL' : // nothing to do
+ break;
+ case 'string' : // nothing to do
+ break;
+ case 'integer' : // Force everything using intval()
+ $value = is_array($value) ? array_map('intval', $value) : intval($value);
+ 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 ($multi)
+ ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
+ $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]);
+ break;
+ case 'boolean' :
+ if ($multi)
+ ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
+ break;
+ case 'timestamp' :
+ if ($multi)
+ ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $paramName");
+ $value = wfTimestamp(TS_UNIX, $value);
+ if ($value === 0)
+ $this->dieUsage("Invalid value '$value' for timestamp parameter $paramName", "badtimestamp_{$paramName}");
+ $value = wfTimestamp(TS_MW, $value);
+ break;
+ default :
+ ApiBase :: dieDebug(__METHOD__, "Param $paramName's type is unknown - $type");
+ }
}
+
+ // There should never be any duplicate values in a list
+ if (is_array($value))
+ $value = array_unique($value);
}
return $value;
@@ -314,7 +392,7 @@ abstract class ApiBase {
if (is_array($allowedValues)) {
$unknownValues = array_diff($valuesList, $allowedValues);
if ($unknownValues) {
- $this->dieUsage('Unrecognised value' . (count($unknownValues) > 1 ? "s '" : " '") . implode("', '", $unknownValues) . "' for parameter '$valueName'", "unknown_$valueName");
+ $this->dieUsage('Unrecognised value' . (count($unknownValues) > 1 ? "s" : "") . " for parameter '$valueName'", "unknown_$valueName");
}
}
@@ -325,8 +403,6 @@ 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) {
- global $wgUser;
-
if ($value < $min) {
$this->dieUsage("$varname may not be less than $min (set to $value)", $varname);
}
@@ -345,7 +421,7 @@ abstract class ApiBase {
* Call main module's error handler
*/
public function dieUsage($description, $errorCode, $httpRespCode = 0) {
- $this->getMain()->mainDieUsage($description, $this->encodeParamName($errorCode), $httpRespCode);
+ throw new UsageException($description, $this->encodeParamName($errorCode), $httpRespCode);
}
/**
@@ -367,6 +443,7 @@ abstract class ApiBase {
if ($this->mTimeIn !== 0)
ApiBase :: dieDebug(__METHOD__, 'called twice without calling profileOut()');
$this->mTimeIn = microtime(true);
+ wfProfileIn($this->getModuleProfileName());
}
/**
@@ -380,6 +457,19 @@ abstract class ApiBase {
$this->mModuleTime += microtime(true) - $this->mTimeIn;
$this->mTimeIn = 0;
+ wfProfileOut($this->getModuleProfileName());
+ }
+
+ /**
+ * When modules crash, sometimes it is needed to do a profileOut() regardless
+ * of the profiling state the module was in. This method does such cleanup.
+ */
+ public function safeProfileOut() {
+ if ($this->mTimeIn !== 0) {
+ if ($this->mDBTimeIn !== 0)
+ $this->profileDBOut();
+ $this->profileOut();
+ }
}
/**
@@ -405,6 +495,7 @@ abstract class ApiBase {
if ($this->mDBTimeIn !== 0)
ApiBase :: dieDebug(__METHOD__, 'called twice without calling profileDBOut()');
$this->mDBTimeIn = microtime(true);
+ wfProfileIn($this->getModuleProfileName(true));
}
/**
@@ -421,6 +512,7 @@ abstract class ApiBase {
$this->mDBTime += $time;
$this->getMain()->mDBTime += $time;
+ wfProfileOut($this->getModuleProfileName(true));
}
/**
@@ -433,9 +525,9 @@ abstract class ApiBase {
}
public abstract function getVersion();
-
+
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiBase.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiBase.php 17880 2006-11-23 08:25:56Z nickj $';
}
}
?> \ No newline at end of file
diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php
new file mode 100644
index 00000000..7d1c1519
--- /dev/null
+++ b/includes/api/ApiFeedWatchlist.php
@@ -0,0 +1,125 @@
+<?php
+
+
+/*
+ * Created on Oct 13, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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 ("ApiBase.php");
+}
+
+class ApiFeedWatchlist extends ApiBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ public function getCustomPrinter() {
+ return new ApiFormatFeedWrapper($this->getMain());
+ }
+
+ 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);
+ }
+
+ private function createFeedItem($info) {
+ $titleStr = $info['title'];
+ $title = Title :: newFromText($titleStr);
+ $titleUrl = $title->getFullUrl();
+ $comment = isset( $info['comment'] ) ? $info['comment'] : null;
+ $timestamp = $info['timestamp'];
+ $user = $info['user'];
+
+ $completeText = "$comment ($user)";
+
+ return new FeedItem($titleStr, $completeText, $titleUrl, $timestamp, $user);
+ }
+
+ protected function getAllowedParams() {
+ global $wgFeedClasses;
+ $feedFormatNames = array_keys($wgFeedClasses);
+ return array (
+ 'feedformat' => array (
+ ApiBase :: PARAM_DFLT => 'rss',
+ ApiBase :: PARAM_TYPE => $feedFormatNames
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'feedformat' => 'The format of the feed'
+ );
+ }
+
+ protected function getDescription() {
+ return 'This module returns a watchlist feed';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=feedwatchlist'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiFeedWatchlist.php 17987 2006-11-29 05:45:03Z nickj $';
+ }
+}
+?>
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index 6f5b4aca..611960d3 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -75,32 +75,40 @@ abstract class ApiFormatBase extends ApiBase {
function initPrinter($isError) {
$isHtml = $this->getIsHtml();
$mime = $isHtml ? 'text/html' : $this->getMimeType();
+
+ // Some printers (ex. Feed) do their own header settings,
+ // in which case $mime will be set to null
+ if (is_null($mime))
+ return; // skip any initialization
+
header("Content-Type: $mime; charset=utf-8;");
if ($isHtml) {
?>
- <html>
- <head>
- <title>MediaWiki API</title>
- </head>
- <body>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title>MediaWiki API</title>
+</head>
+<body>
<?php
if (!$isError) {
?>
- <br/>
- <small>
- This result is being shown in <?=$this->mFormat?> format,
- which might not be suitable for your application.<br/>
- See <a href='api.php'>API help</a> for more information.<br/>
- </small>
+<br/>
+<small>
+You are looking at the HTML representation of the <?=$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.
+</small>
<?php
}
?>
- <pre>
+<pre>
<?php
@@ -113,8 +121,10 @@ abstract class ApiFormatBase extends ApiBase {
public function closePrinter() {
if ($this->getIsHtml()) {
?>
- </pre>
- </body>
+
+</pre>
+</body>
+</html>
<?php
@@ -134,9 +144,10 @@ abstract class ApiFormatBase extends ApiBase {
*/
protected function formatHTML($text) {
// encode all tags as safe blue strings
- $text = ereg_replace('\<([^>]+)\>', '<font color=blue>&lt;\1&gt;</font>', $text);
+ $text = ereg_replace('\<([^>]+)\>', '<span style="color:blue;">&lt;\1&gt;</span>', $text);
// identify URLs
- $text = ereg_replace("[a-zA-Z]+://[^ '()<\n]+", '<a href="\\0">\\0</a>', $text);
+ $protos = "http|https|ftp|gopher";
+ $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);
// make strings inside * bold
@@ -151,11 +162,71 @@ abstract class ApiFormatBase extends ApiBase {
* Returns usage examples for this format.
*/
protected function getExamples() {
- return 'api.php?action=query&meta=siteinfo&si=namespaces&format=' . $this->getModuleName();
+ return 'api.php?action=query&meta=siteinfo&siprop=namespaces&format=' . $this->getModuleName();
+ }
+
+ protected function getDescription() {
+ return $this->getIsHtml() ? ' (pretty-print in HTML)' : '';
}
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 17374 2006-11-03 06:53:47Z yurik $';
+ }
+}
+
+/**
+ * This printer is used to wrap an instance of the Feed class
+ */
+class ApiFormatFeedWrapper extends ApiFormatBase {
+
+ public function __construct($main) {
+ parent :: __construct($main, 'feed');
+ }
+
+ /**
+ * Call this method to initialize output data
+ */
+ public static function setResult($result, $feed, $feedItems) {
+ // Store output in the Result data.
+ // This way we can check during execution if any error has occured
+ $data = & $result->getData();
+ $data['_feed'] = $feed;
+ $data['_feeditems'] = $feedItems;
+ }
+
+ /**
+ * Feed does its own headers
+ */
+ public function getMimeType() {
+ return null;
+ }
+
+ /**
+ * Optimization - no need to sanitize data that will not be needed
+ */
+ public function getNeedsRawData() {
+ return true;
+ }
+
+ public function execute() {
+ $data = $this->getResultData();
+ if (isset ($data['_feed']) && isset ($data['_feeditems'])) {
+ $feed = $data['_feed'];
+ $items = $data['_feeditems'];
+
+ $feed->outHeader();
+ foreach ($items as & $item)
+ $feed->outItem($item);
+ $feed->outFooter();
+ } else {
+ // Error has occured, print something usefull
+ // TODO: make this error more informative using ApiBase :: dieDebug() or similar
+ wfHttpError(500, 'Internal Server Error', '');
+ }
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiFormatBase.php 17374 2006-11-03 06:53:47Z yurik $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index fdc29cf2..45c735c8 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -31,26 +31,39 @@ if (!defined('MEDIAWIKI')) {
class ApiFormatJson extends ApiFormatBase {
+ private $mIsRaw;
+
public function __construct($main, $format) {
parent :: __construct($main, $format);
+ $this->mIsRaw = ($format === 'rawfm');
}
public function getMimeType() {
return 'application/json';
}
+ public function getNeedsRawData() {
+ return $this->mIsRaw;
+ }
+
public function execute() {
- require ('ApiFormatJson_json.php');
- $json = new Services_JSON();
- $this->printText($json->encode($this->getResultData(), true));
+ if (!function_exists('json_encode') || $this->getIsHtml()) {
+ $json = new Services_JSON();
+ $this->printText($json->encode($this->getResultData(), $this->getIsHtml()));
+ } else {
+ $this->printText(json_encode($this->getResultData()));
+ }
}
protected function getDescription() {
- return 'Output data in JSON format';
+ if ($this->mIsRaw)
+ return 'Output data with the debuging elements in JSON format' . parent :: getDescription();
+ else
+ return 'Output data in JSON format' . parent :: getDescription();
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatJson.php 16725 2006-10-01 21:20:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatJson.php 17374 2006-11-03 06:53:47Z yurik $';
}
}
?> \ No newline at end of file
diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php
new file mode 100644
index 00000000..938ba032
--- /dev/null
+++ b/includes/api/ApiFormatPhp.php
@@ -0,0 +1,54 @@
+<?php
+
+
+/*
+ * Created on Oct 22, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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 ('ApiFormatBase.php');
+}
+
+class ApiFormatPhp extends ApiFormatBase {
+
+ public function __construct($main, $format) {
+ parent :: __construct($main, $format);
+ }
+
+ public function getMimeType() {
+ return 'application/vnd.php.serialized';
+ }
+
+ public function execute() {
+ $this->printText(serialize($this->getResultData()));
+ }
+
+ protected function getDescription() {
+ return 'Output data in serialized PHP format' . parent :: getDescription();
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiFormatPhp.php 17374 2006-11-03 06:53:47Z yurik $';
+ }
+}
+?> \ No newline at end of file
diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php
new file mode 100644
index 00000000..e97b996c
--- /dev/null
+++ b/includes/api/ApiFormatWddx.php
@@ -0,0 +1,89 @@
+<?php
+
+
+/*
+ * Created on Oct 22, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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 ('ApiFormatBase.php');
+}
+
+class ApiFormatWddx extends ApiFormatBase {
+
+ public function __construct($main, $format) {
+ parent :: __construct($main, $format);
+ }
+
+ public function getMimeType() {
+ return 'text/xml';
+ }
+
+ public function execute() {
+ if (function_exists('wddx_serialize_value')) {
+ $this->printText(wddx_serialize_value($this->getResultData()));
+ } else {
+ $this->printText('<?xml version="1.0" encoding="utf-8"?>');
+ $this->printText('<wddxPacket version="1.0"><header/><data>');
+ $this->slowWddxPrinter($this->getResultData());
+ $this->printText('</data></wddxPacket>');
+ }
+ }
+
+ /**
+ * Recursivelly go through the object and output its data in WDDX format.
+ */
+ function slowWddxPrinter($elemValue) {
+ switch (gettype($elemValue)) {
+ case 'array' :
+ $this->printText('<struct>');
+ foreach ($elemValue as $subElemName => $subElemValue) {
+ $this->printText(wfElement('var', array (
+ 'name' => $subElemName
+ ), null));
+ $this->slowWddxPrinter($subElemValue);
+ $this->printText('</var>');
+ }
+ $this->printText('</struct>');
+ break;
+ case 'integer' :
+ case 'double' :
+ $this->printText(wfElement('number', null, $elemValue));
+ break;
+ case 'string' :
+ $this->printText(wfElement('string', null, $elemValue));
+ break;
+ default :
+ ApiBase :: dieDebug(__METHOD__, 'Unknown type ' . gettype($elemValue));
+ }
+ }
+
+ protected function getDescription() {
+ return 'Output data in WDDX format' . parent :: getDescription();
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiFormatWddx.php 17374 2006-11-03 06:53:47Z yurik $';
+ }
+}
+?> \ No newline at end of file
diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php
index 6aa08e00..2326ba42 100644
--- a/includes/api/ApiFormatXml.php
+++ b/includes/api/ApiFormatXml.php
@@ -31,6 +31,8 @@ if (!defined('MEDIAWIKI')) {
class ApiFormatXml extends ApiFormatBase {
+ private $mRootElemName = 'api';
+
public function __construct($main, $format) {
parent :: __construct($main, $format);
}
@@ -42,18 +44,14 @@ class ApiFormatXml extends ApiFormatBase {
public function getNeedsRawData() {
return true;
}
+
+ public function setRootElement($rootElemName) {
+ $this->mRootElemName = $rootElemName;
+ }
public function execute() {
- $xmlindent = null;
- extract($this->extractRequestParams());
-
- if ($xmlindent || $this->getIsHtml())
- $xmlindent = -2;
- else
- $xmlindent = null;
-
$this->printText('<?xml version="1.0" encoding="utf-8"?>');
- $this->recXmlPrint('api', $this->getResultData(), $xmlindent);
+ $this->recXmlPrint($this->mRootElemName, $this->getResultData(), $this->getIsHtml() ? -2 : null);
}
/**
@@ -98,8 +96,6 @@ class ApiFormatXml extends ApiFormatBase {
$subElements = array ();
foreach ($elemValue as $subElemId => & $subElemValue) {
if (gettype($subElemId) === 'integer') {
- if (!is_array($subElemValue))
- ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has a scalar indexed value.");
$indElements[] = $subElemValue;
unset ($elemValue[$subElemId]);
} elseif (is_array($subElemValue)) {
@@ -109,7 +105,7 @@ class ApiFormatXml extends ApiFormatBase {
}
if (is_null($subElemIndName) && !empty ($indElements))
- ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has integer keys without _element value");
+ ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has integer keys without _element value. Use ApiResult::setIndexedTagName().");
if (!empty ($subElements) && !empty ($indElements) && !is_null($subElemContent))
ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has content and subelements");
@@ -139,23 +135,11 @@ class ApiFormatXml extends ApiFormatBase {
}
}
protected function getDescription() {
- return 'Output data in XML format';
- }
-
- protected function getAllowedParams() {
- return array (
- 'xmlindent' => false
- );
- }
-
- protected function getParamDescription() {
- return array (
- 'xmlindent' => 'Enable XML indentation'
- );
+ return 'Output data in XML format' . parent :: getDescription();
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatXml.php 16725 2006-10-01 21:20:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatXml.php 17374 2006-11-03 06:53:47Z yurik $';
}
}
?> \ No newline at end of file
diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php
index bd74f01a..2371903f 100644
--- a/includes/api/ApiFormatYaml.php
+++ b/includes/api/ApiFormatYaml.php
@@ -40,16 +40,15 @@ class ApiFormatYaml extends ApiFormatBase {
}
public function execute() {
- require ('ApiFormatYaml_spyc.php');
$this->printText(Spyc :: YAMLDump($this->getResultData()));
}
protected function getDescription() {
- return 'Output data in YAML format';
+ return 'Output data in YAML format' . parent :: getDescription();
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatYaml.php 16725 2006-10-01 21:20:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiFormatYaml.php 17374 2006-11-03 06:53:47Z yurik $';
}
}
?> \ No newline at end of file
diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php
index 05a39e23..1ec8af48 100644
--- a/includes/api/ApiFormatYaml_spyc.php
+++ b/includes/api/ApiFormatYaml_spyc.php
@@ -463,6 +463,7 @@
* @param string $line A line from the YAML file
*/
function _getIndent($line) {
+ $match = array();
preg_match('/^\s{1,}/',$line,$match);
if (!empty($match[0])) {
$indent = substr_count($match[0],' ');
@@ -500,6 +501,7 @@
} elseif (preg_match('/^(.+):/',$line,$key)) {
// It's a key/value pair most likely
// If the key is in double quotes pull it out
+ $matches = array();
if (preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
$value = trim(str_replace($matches[1],'',$line));
$key = $matches[2];
@@ -529,6 +531,7 @@
* @return mixed
*/
function _toType($value) {
+ $matches = array();
if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) {
$value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches));
$value = preg_replace('/\\\\"/','"',$value);
@@ -596,6 +599,7 @@
// Check for strings
$regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
+ $strings = array();
if (preg_match_all($regex,$inline,$strings)) {
$saved_strings[] = $strings[0][0];
$inline = preg_replace($regex,'YAMLString',$inline);
@@ -603,12 +607,14 @@
unset($regex);
// Check for sequences
+ $seqs = array();
if (preg_match_all('/\[(.+)\]/U',$inline,$seqs)) {
$inline = preg_replace('/\[(.+)\]/U','YAMLSeq',$inline);
$seqs = $seqs[0];
}
// Check for mappings
+ $maps = array();
if (preg_match_all('/{(.+)}/U',$inline,$maps)) {
$inline = preg_replace('/{(.+)}/U','YAMLMap',$inline);
$maps = $maps[0];
@@ -704,6 +710,7 @@
function _linkRef(&$n,$key,$k = NULL,$v = NULL) {
if (empty($k) && empty($v)) {
// Look for &refs
+ $matches = array();
if (preg_match('/^&([^ ]+)/',$n->data[$key],$matches)) {
// Flag the node so we know it's a reference
$this->_allNodes[$n->id]->ref = substr($matches[0],1);
@@ -837,7 +844,7 @@
$ret = array();
foreach($keys as $key) {
- list($unused,$val) = each($vals);
+ list( /* unused */ ,$val) = each($vals);
// This is the good part! If a key already exists, but it's part of a
// sequence (an int), just keep addin numbers until we find a fresh one.
if (isset($ret[$key]) and is_int($key)) {
diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php
index 2aa571c1..d9697dc3 100644
--- a/includes/api/ApiLogin.php
+++ b/includes/api/ApiLogin.php
@@ -89,8 +89,8 @@ class ApiLogin extends ApiBase {
protected function getAllowedParams() {
return array (
- 'name' => '',
- 'password' => '',
+ 'name' => null,
+ 'password' => null,
'domain' => null
);
}
@@ -116,7 +116,7 @@ class ApiLogin extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiLogin.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiLogin.php 17065 2006-10-17 02:11:29Z yurik $';
}
}
?>
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 046d7d7c..606f022b 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -29,36 +29,79 @@ if (!defined('MEDIAWIKI')) {
require_once ('ApiBase.php');
}
+/**
+ * This is the main API class, used for both external and internal processing.
+ */
class ApiMain extends ApiBase {
+ /**
+ * When no format parameter is given, this format will be used
+ */
+ const API_DEFAULT_FORMAT = 'xmlfm';
+
+ /**
+ * List of available modules: action name => module class
+ */
+ private static $Modules = array (
+ 'help' => 'ApiHelp',
+ 'login' => 'ApiLogin',
+ 'opensearch' => 'ApiOpenSearch',
+ 'feedwatchlist' => 'ApiFeedWatchlist',
+ 'query' => 'ApiQuery'
+ );
+
+ /**
+ * List of available formats: format name => format class
+ */
+ private static $Formats = array (
+ 'json' => 'ApiFormatJson',
+ 'jsonfm' => 'ApiFormatJson',
+ 'php' => 'ApiFormatPhp',
+ 'phpfm' => 'ApiFormatPhp',
+ 'wddx' => 'ApiFormatWddx',
+ 'wddxfm' => 'ApiFormatWddx',
+ 'xml' => 'ApiFormatXml',
+ 'xmlfm' => 'ApiFormatXml',
+ 'yaml' => 'ApiFormatYaml',
+ 'yamlfm' => 'ApiFormatYaml',
+ 'rawfm' => 'ApiFormatJson'
+ );
+
private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames;
- private $mApiStartTime, $mResult, $mShowVersions, $mEnableWrite;
+ private $mResult, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage;
/**
* Constructor
- * $apiStartTime - time of the originating call for profiling purposes
- * $modules - an array of actions (keys) and classes that handle them (values)
+ * @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
*/
- public function __construct($apiStartTime, $modules, $formats, $enableWrite) {
+ public function __construct($request, $enableWrite = false) {
+
+ $this->mInternalMode = ($request instanceof FauxRequest);
+
// Special handling for the main module: $parent === $this
- parent :: __construct($this, 'main');
+ parent :: __construct($this, $this->mInternalMode ? 'main_int' : 'main');
+
+ $this->mModules = self :: $Modules;
+ $this->mModuleNames = array_keys($this->mModules); // todo: optimize
+ $this->mFormats = self :: $Formats;
+ $this->mFormatNames = array_keys($this->mFormats); // todo: optimize
- $this->mModules = $modules;
- $this->mModuleNames = array_keys($modules);
- $this->mFormats = $formats;
- $this->mFormatNames = array_keys($formats);
- $this->mApiStartTime = $apiStartTime;
$this->mResult = new ApiResult($this);
$this->mShowVersions = false;
$this->mEnableWrite = $enableWrite;
+
+ $this->mRequest = & $request;
+
+ $this->mSquidMaxage = 0;
}
- public function & getResult() {
- return $this->mResult;
+ public function & getRequest() {
+ return $this->mRequest;
}
- public function getShowVersions() {
- return $this->mShowVersions;
+ public function getResult() {
+ return $this->mResult;
}
public function requestWriteMode() {
@@ -67,94 +110,180 @@ class ApiMain extends ApiBase {
'statement is included in the site\'s LocalSettings.php file', 'readonly');
}
- protected function getAllowedParams() {
- return array (
- 'format' => array (
- ApiBase :: PARAM_DFLT => API_DEFAULT_FORMAT,
- ApiBase :: PARAM_TYPE => $this->mFormatNames
- ),
- 'action' => array (
- ApiBase :: PARAM_DFLT => 'help',
- ApiBase :: PARAM_TYPE => $this->mModuleNames
- ),
- 'version' => false
- );
+ public function setCacheMaxAge($maxage) {
+ $this->mSquidMaxage = $maxage;
}
- protected function getParamDescription() {
- return array (
- 'format' => 'The format of the output',
- 'action' => 'What action you would like to perform',
- 'version' => 'When showing help, include version for each module'
- );
+ public function createPrinterByName($format) {
+ return new $this->mFormats[$format] ($this, $format);
}
public function execute() {
$this->profileIn();
- $action = $format = $version = null;
- try {
- extract($this->extractRequestParams());
- $this->mShowVersions = $version;
+ if ($this->mInternalMode)
+ $this->executeAction();
+ else
+ $this->executeActionWithErrorHandling();
+ $this->profileOut();
+ }
- // Create an appropriate printer
- $this->mPrinter = new $this->mFormats[$format] ($this, $format);
+ protected function executeActionWithErrorHandling() {
- // Instantiate and execute module requested by the user
- $module = new $this->mModules[$action] ($this, $action);
- $module->profileIn();
- $module->execute();
- $module->profileOut();
- $this->printResult(false);
+ // In case an error occurs during data output,
+ // this clear the output buffer and print just the error information
+ ob_start();
- } catch (UsageException $e) {
+ try {
+ $this->executeAction();
+ } catch (Exception $e) {
+ //
+ // Handle any kind of exception by outputing properly formatted error message.
+ // If this fails, an unhandled exception should be thrown so that global error
+ // handler will process and log it.
+ //
+
+ // Error results should not be cached
+ $this->setCacheMaxAge(0);
// Printer may not be initialized if the extractRequestParams() fails for the main module
- if (!isset ($this->mPrinter))
- $this->mPrinter = new $this->mFormats[API_DEFAULT_FORMAT] ($this, API_DEFAULT_FORMAT);
+ if (!isset ($this->mPrinter)) {
+ $this->mPrinter = $this->createPrinterByName(self :: API_DEFAULT_FORMAT);
+ if ($this->mPrinter->getNeedsRawData())
+ $this->getResult()->setRawMode();
+ }
+
+ if ($e instanceof UsageException) {
+ //
+ // User entered incorrect parameters - print usage screen
+ //
+ $errMessage = array (
+ 'code' => $e->getCodeString(), 'info' => $e->getMessage());
+ ApiResult :: setContent($errMessage, $this->makeHelpMsg());
+
+ } else {
+ //
+ // Something is seriously wrong
+ //
+ $errMessage = array (
+ 'code' => 'internal_api_error',
+ 'info' => "Exception Caught: {$e->getMessage()}"
+ );
+ 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();
+ }
+
+ /**
+ * Execute the actual module, without any error handling
+ */
+ protected function executeAction() {
+ $action = $format = $version = null;
+ extract($this->extractRequestParams());
+ $this->mShowVersions = $version;
+
+ // Instantiate the module requested by the user
+ $module = new $this->mModules[$action] ($this, $action);
+
+ if (!$this->mInternalMode) {
+
+ // See if custom printer is used
+ $this->mPrinter = $module->getCustomPrinter();
+ if (is_null($this->mPrinter)) {
+ // Create an appropriate printer
+ $this->mPrinter = $this->createPrinterByName($format);
+ }
+
+ if ($this->mPrinter->getNeedsRawData())
+ $this->getResult()->setRawMode();
+ }
+
+ // Execute
+ $module->profileIn();
+ $module->execute();
+ $module->profileOut();
+
+ if (!$this->mInternalMode) {
+ // Print result data
+ $this->printResult(false);
}
- $this->profileOut();
}
/**
* Internal printer
*/
- private function printResult($isError) {
+ protected function printResult($isError) {
$printer = $this->mPrinter;
$printer->profileIn();
$printer->initPrinter($isError);
- if (!$printer->getNeedsRawData())
- $this->getResult()->SanitizeData();
$printer->execute();
$printer->closePrinter();
$printer->profileOut();
}
+ protected function getAllowedParams() {
+ return array (
+ 'format' => array (
+ ApiBase :: PARAM_DFLT => ApiMain :: API_DEFAULT_FORMAT,
+ ApiBase :: PARAM_TYPE => $this->mFormatNames
+ ),
+ 'action' => array (
+ ApiBase :: PARAM_DFLT => 'help',
+ ApiBase :: PARAM_TYPE => $this->mModuleNames
+ ),
+ 'version' => false
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'format' => 'The format of the output',
+ 'action' => 'What action you would like to perform',
+ 'version' => 'When showing help, include version for each module'
+ );
+ }
+
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.',
''
);
}
-
- public function mainDieUsage($description, $errorCode, $httpRespCode = 0) {
- $this->mResult->Reset();
- if ($httpRespCode === 0)
- header($errorCode, true);
- else
- header($errorCode, true, $httpRespCode);
-
- $data = array (
- 'code' => $errorCode,
- 'info' => $description
+
+ 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'
);
- ApiResult :: setContent($data, $this->makeHelpMsg());
- $this->mResult->addValue(null, 'error', $data);
-
- throw new UsageException($description, $errorCode);
}
/**
@@ -167,7 +296,7 @@ class ApiMain extends ApiBase {
$astriks = str_repeat('*** ', 10);
$msg .= "\n\n$astriks Modules $astriks\n\n";
- foreach ($this->mModules as $moduleName => $moduleClass) {
+ foreach( $this->mModules as $moduleName => $unused ) {
$msg .= "* action=$moduleName *";
$module = new $this->mModules[$moduleName] ($this, $moduleName);
$msg2 = $module->makeHelpMsg();
@@ -177,14 +306,17 @@ class ApiMain extends ApiBase {
}
$msg .= "\n$astriks Formats $astriks\n\n";
- foreach ($this->mFormats as $moduleName => $moduleClass) {
- $msg .= "* format=$moduleName *";
- $module = new $this->mFormats[$moduleName] ($this, $moduleName);
+ foreach( $this->mFormats as $formatName => $unused ) {
+ $msg .= "* format=$formatName *";
+ $module = $this->createPrinterByName($formatName);
$msg2 = $module->makeHelpMsg();
if ($msg2 !== false)
$msg .= $msg2;
$msg .= "\n";
}
+
+ $msg .= "\n*** Credits: ***\n " . implode("\n ", $this->getCredits()) . "\n";
+
return $msg;
}
@@ -198,12 +330,17 @@ class ApiMain extends ApiBase {
return $this->mIsBot;
}
+ public function getShowVersions() {
+ return $this->mShowVersions;
+ }
+
public function getVersion() {
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiMain.php 16820 2006-10-06 01:02:14Z yurik $';
+ $vers[] = __CLASS__ . ': $Id: ApiMain.php 17987 2006-11-29 05:45:03Z nickj $';
$vers[] = ApiBase :: getBaseVersion();
$vers[] = ApiFormatBase :: getBaseVersion();
$vers[] = ApiQueryBase :: getBaseVersion();
+ $vers[] = ApiFormatFeedWrapper :: getVersion(); // not accessible with format=xxx
return $vers;
}
}
@@ -213,14 +350,17 @@ class ApiMain extends ApiBase {
*/
class UsageException extends Exception {
- private $codestr;
+ private $mCodestr;
- public function __construct($message, $codestr) {
- parent :: __construct($message);
- $this->codestr = $codestr;
+ public function __construct($message, $codestr, $code = 0) {
+ parent :: __construct($message, $code);
+ $this->mCodestr = $codestr;
+ }
+ public function getCodeString() {
+ return $this->mCodestr;
}
public function __toString() {
- return "{$this->codestr}: {$this->message}";
+ return "{$this->getCodeString()}: {$this->getMessage()}";
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php
new file mode 100644
index 00000000..a5a13a7b
--- /dev/null
+++ b/includes/api/ApiOpenSearch.php
@@ -0,0 +1,109 @@
+<?php
+
+
+/*
+ * Created on Oct 13, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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 ("ApiBase.php");
+}
+
+class ApiOpenSearch extends ApiBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ public function getCustomPrinter() {
+ return $this->getMain()->createPrinterByName('json');
+ }
+
+ public function execute() {
+ $search = null;
+ extract($this->ExtractRequestParams());
+
+ // Open search results may be stored for a very long time
+ $this->getMain()->setCacheMaxAge(1200);
+
+ $title = Title :: newFromText($search);
+ if(!$title)
+ return; // Return empty result
+
+ // Prepare nested request
+ $params = new FauxRequest(array (
+ 'action' => 'query',
+ 'list' => 'allpages',
+ 'apnamespace' => $title->getNamespace(),
+ 'aplimit' => 10,
+ 'apprefix' => $title->getDBkey()
+ ));
+
+ // Execute
+ $module = new ApiMain($params);
+ $module->execute();
+
+ // Get resulting data
+ $data = $module->getResultData();
+
+ // Reformat useful data for future printing by JSON engine
+ $srchres = array ();
+ foreach ($data['query']['allpages'] as & $pageinfo) {
+ // Note: this data will no be printable by the xml engine
+ // because it does not support lists of unnamed items
+ $srchres[] = $pageinfo['title'];
+ }
+
+ // Set top level elements
+ $result = $this->getResult();
+ $result->addValue(null, 0, $search);
+ $result->addValue(null, 1, $srchres);
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'search' => null
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'search' => 'Search string'
+ );
+ }
+
+ protected function getDescription() {
+ return 'This module implements OpenSearch protocol';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=opensearch&search=Te'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiOpenSearch.php 17880 2006-11-23 08:25:56Z nickj $';
+ }
+}
+?> \ No newline at end of file
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index d2384b39..4728a9f8 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -32,8 +32,9 @@ if (!defined('MEDIAWIKI')) {
class ApiPageSet extends ApiQueryBase {
private $mAllPages; // [ns][dbkey] => page_id or 0 when missing
- private $mGoodTitles, $mMissingTitles, $mMissingPageIDs, $mRedirectTitles, $mNormalizedTitles;
+ private $mTitles, $mGoodTitles, $mMissingTitles, $mMissingPageIDs, $mRedirectTitles, $mNormalizedTitles;
private $mResolveRedirects, $mPendingRedirectIDs;
+ private $mGoodRevIDs, $mMissingRevIDs;
private $mRequestedPageFields;
@@ -41,11 +42,14 @@ class ApiPageSet extends ApiQueryBase {
parent :: __construct($query, __CLASS__);
$this->mAllPages = array ();
+ $this->mTitles = array();
$this->mGoodTitles = array ();
$this->mMissingTitles = array ();
$this->mMissingPageIDs = array ();
$this->mRedirectTitles = array ();
$this->mNormalizedTitles = array ();
+ $this->mGoodRevIDs = array();
+ $this->mMissingRevIDs = array();
$this->mRequestedPageFields = array ();
$this->mResolveRedirects = $resolveRedirects;
@@ -86,6 +90,21 @@ class ApiPageSet extends ApiQueryBase {
}
/**
+ * All Title objects provided.
+ * @return array of Title objects
+ */
+ public function getTitles() {
+ return $this->mTitles;
+ }
+
+ /**
+ * Returns the number of unique pages (not revisions) in the set.
+ */
+ public function getTitleCount() {
+ return count($this->mTitles);
+ }
+
+ /**
* Title objects that were found in the database.
* @return array page_id (int) => Title (obj)
*/
@@ -94,10 +113,10 @@ class ApiPageSet extends ApiQueryBase {
}
/**
- * Returns the number of unique pages (not revisions) in the set.
+ * Returns the number of found unique pages (not revisions) in the set.
*/
public function getGoodTitleCount() {
- return count($this->getGoodTitles());
+ return count($this->mGoodTitles);
}
/**
@@ -135,16 +154,25 @@ class ApiPageSet extends ApiQueryBase {
/**
* Get the list of revision IDs (requested with revids= parameter)
+ * @return array revID (int) => pageID (int)
*/
public function getRevisionIDs() {
- $this->dieUsage(__METHOD__ . ' is not implemented', 'notimplemented');
+ return $this->mGoodRevIDs;
+ }
+
+ /**
+ * Revision IDs that were not found in the database
+ * @return array of revision IDs
+ */
+ public function getMissingRevisionIDs() {
+ return $this->mMissingRevIDs;
}
/**
* Returns the number of revisions (requested with revids= parameter)
*/
public function getRevisionCount() {
- return 0; // TODO: implement
+ return count($this->getRevisionIDs());
}
/**
@@ -178,6 +206,8 @@ class ApiPageSet extends ApiQueryBase {
$this->initFromPageIds($pageids);
break;
case 'revids' :
+ if($this->mResolveRedirects)
+ $this->dieUsage('revids may not be used with redirect resolution', 'params');
$this->initFromRevIDs($revids);
break;
default :
@@ -216,23 +246,39 @@ class ApiPageSet extends ApiQueryBase {
}
/**
+ * Initialize PageSet from a list of Revision IDs
+ */
+ public function populateFromRevisionIDs($revIDs) {
+ $this->profileIn();
+ $revIDs = array_map('intval', $revIDs); // paranoia
+ $this->initFromRevIDs($revIDs);
+ $this->profileOut();
+ }
+
+ /**
* Extract all requested fields from the row received from the database
*/
public function processDbRow($row) {
- $pageId = intval($row->page_id);
-
+
// Store Title object in various data structures
$title = Title :: makeTitle($row->page_namespace, $row->page_title);
- $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
+
+ // skip any pages that user has no rights to read
+ if ($title->userCanRead()) {
- if ($this->mResolveRedirects && $row->page_is_redirect == '1') {
- $this->mPendingRedirectIDs[$pageId] = $title;
- } else {
- $this->mGoodTitles[$pageId] = $title;
+ $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;
}
-
- foreach ($this->mRequestedPageFields as $fieldName => & $fieldValues)
- $fieldValues[$pageId] = $row-> $fieldName;
}
public function finishPageSetGeneration() {
@@ -256,10 +302,13 @@ class ApiPageSet extends ApiQueryBase {
* #6 Repeat from step #1
*/
private function initFromTitles($titles) {
- $db = $this->getDB();
// Get validated and normalized title objects
$linkBatch = $this->processTitlesStrArray($titles);
+ if($linkBatch->isEmpty())
+ return;
+
+ $db = & $this->getDB();
$set = $linkBatch->constructSet('page', $db);
// Get pageIDs data from the `page` table
@@ -275,12 +324,15 @@ class ApiPageSet extends ApiQueryBase {
}
private function initFromPageIds($pageids) {
- $db = $this->getDB();
-
+ if(empty($pageids))
+ return;
+
$set = array (
'page_id' => $pageids
);
+ $db = & $this->getDB();
+
// Get pageIDs data from the `page` table
$this->profileDBIn();
$res = $db->select('page', $this->getPageTableFields(), $set, __METHOD__);
@@ -306,7 +358,7 @@ class ApiPageSet extends ApiQueryBase {
*/
private function initFromQueryResult($db, $res, &$remaining = null, $processTitles = null) {
if (!is_null($remaining) && is_null($processTitles))
- $this->dieDebug('Missing $processTitles parameter when $remaining is provided');
+ ApiBase :: dieDebug(__METHOD__, 'Missing $processTitles parameter when $remaining is provided');
while ($row = $db->fetchObject($res)) {
@@ -330,9 +382,11 @@ class ApiPageSet extends ApiQueryBase {
if($processTitles) {
// The remaining titles in $remaining are non-existant pages
foreach ($remaining as $ns => $dbkeys) {
- foreach ($dbkeys as $dbkey => $nothing) {
- $this->mMissingTitles[] = Title :: makeTitle($ns, $dbkey);
+ foreach ( $dbkeys as $dbkey => $unused ) {
+ $title = Title :: makeTitle($ns, $dbkey);
+ $this->mMissingTitles[] = $title;
$this->mAllPages[$ns][$dbkey] = 0;
+ $this->mTitles[] = $title;
}
}
}
@@ -348,13 +402,43 @@ class ApiPageSet extends ApiQueryBase {
}
private function initFromRevIDs($revids) {
- $this->dieUsage(__METHOD__ . ' is not implemented', 'notimplemented');
+
+ if(empty($revids))
+ return;
+
+ $db = & $this->getDB();
+ $pageids = array();
+ $remaining = array_flip($revids);
+
+ $tables = array('revision');
+ $fields = array('rev_id','rev_page');
+ $where = array('rev_deleted' => 0, 'rev_id' => $revids);
+
+ // Get pageIDs data from the `page` table
+ $this->profileDBIn();
+ $res = $db->select( $tables, $fields, $where, __METHOD__ );
+ while ( $row = $db->fetchObject( $res ) ) {
+ $revid = intval($row->rev_id);
+ $pageid = intval($row->rev_page);
+ $this->mGoodRevIDs[$revid] = $pageid;
+ $pageids[$pageid] = '';
+ unset($remaining[$revid]);
+ }
+ $db->freeResult( $res );
+ $this->profileDBOut();
+
+ $this->mMissingRevIDs = array_keys($remaining);
+
+ // Populate all the page information
+ if($this->mResolveRedirects)
+ ApiBase :: dieDebug(__METHOD__, 'revids may not be used with redirect resolution');
+ $this->initFromPageIds(array_keys($pageids));
}
private function resolvePendingRedirects() {
if($this->mResolveRedirects) {
- $db = $this->getDB();
+ $db = & $this->getDB();
$pageFlds = $this->getPageTableFields();
// Repeat until all redirects have been resolved
@@ -386,7 +470,7 @@ class ApiPageSet extends ApiQueryBase {
private function getRedirectTargets() {
$linkBatch = new LinkBatch();
- $db = $this->getDB();
+ $db = & $this->getDB();
// find redirect targets for all redirect pages
$this->profileDBIn();
@@ -443,7 +527,7 @@ class ApiPageSet extends ApiQueryBase {
// All IDs must exist in the page table
if (!empty($this->mPendingRedirectIDs[$plfrom]))
- $this->dieDebug('Invalid redirect IDs were found');
+ ApiBase :: dieDebug(__METHOD__, 'Invalid redirect IDs were found');
return $linkBatch;
}
@@ -508,7 +592,7 @@ class ApiPageSet extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiPageSet.php 16820 2006-10-06 01:02:14Z yurik $';
+ return __CLASS__ . ': $Id: ApiPageSet.php 17929 2006-11-25 17:11:58Z tstarling $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index 985bde63..e7b7f351 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -45,15 +45,19 @@ class ApiQuery extends ApiBase {
// 'templates' => 'ApiQueryTemplates',
private $mQueryListModules = array (
- 'allpages' => 'ApiQueryAllpages'
+ 'allpages' => 'ApiQueryAllpages',
+ 'logevents' => 'ApiQueryLogEvents',
+ 'watchlist' => 'ApiQueryWatchlist',
+ 'recentchanges' => 'ApiQueryRecentChanges',
+ 'backlinks' => 'ApiQueryBacklinks',
+ 'embeddedin' => 'ApiQueryBacklinks',
+ 'imagelinks' => 'ApiQueryBacklinks',
+ 'usercontribs' => 'ApiQueryContributions'
);
- // 'backlinks' => 'ApiQueryBacklinks',
// 'categorymembers' => 'ApiQueryCategorymembers',
// 'embeddedin' => 'ApiQueryEmbeddedin',
// 'imagelinks' => 'ApiQueryImagelinks',
- // 'logevents' => 'ApiQueryLogevents',
// 'recentchanges' => 'ApiQueryRecentchanges',
- // 'usercontribs' => 'ApiQueryUsercontribs',
// 'users' => 'ApiQueryUsers',
// 'watchlist' => 'ApiQueryWatchlist',
@@ -75,9 +79,12 @@ class ApiQuery extends ApiBase {
$this->mAllowedGenerators = array_merge($this->mListModuleNames, $this->mPropModuleNames);
}
- public function getDB() {
- if (!isset ($this->mSlaveDB))
+ public function & getDB() {
+ if (!isset ($this->mSlaveDB)) {
+ $this->profileDBIn();
$this->mSlaveDB = & wfGetDB(DB_SLAVE);
+ $this->profileDBOut();
+ }
return $this->mSlaveDB;
}
@@ -151,6 +158,7 @@ class ApiQuery extends ApiBase {
private function outputGeneralPageInfo() {
$pageSet = $this->getPageSet();
+ $result = $this->getResult();
// Title normalizations
$normValues = array ();
@@ -162,8 +170,8 @@ class ApiQuery extends ApiBase {
}
if (!empty ($normValues)) {
- ApiResult :: setIndexedTagName($normValues, 'n');
- $this->getResult()->addValue('query', 'normalized', $normValues);
+ $result->setIndexedTagName($normValues, 'n');
+ $result->addValue('query', 'normalized', $normValues);
}
// Show redirect information
@@ -176,8 +184,23 @@ class ApiQuery extends ApiBase {
}
if (!empty ($redirValues)) {
- ApiResult :: setIndexedTagName($redirValues, 'r');
- $this->getResult()->addValue('query', 'redirects', $redirValues);
+ $result->setIndexedTagName($redirValues, 'r');
+ $result->addValue('query', 'redirects', $redirValues);
+ }
+
+ //
+ // Missing revision elements
+ //
+ $missingRevIDs = $pageSet->getMissingRevisionIDs();
+ if (!empty ($missingRevIDs)) {
+ $revids = array ();
+ foreach ($missingRevIDs as $revid) {
+ $revids[$revid] = array (
+ 'revid' => $revid
+ );
+ }
+ $result->setIndexedTagName($revids, 'rev');
+ $result->addValue('query', 'badrevids', $revids);
}
//
@@ -195,7 +218,7 @@ class ApiQuery extends ApiBase {
// Report any missing page ids
foreach ($pageSet->getMissingPageIDs() as $pageid) {
$pages[$pageid] = array (
- 'id' => $pageid,
+ 'pageid' => $pageid,
'missing' => ''
);
}
@@ -203,12 +226,13 @@ class ApiQuery extends ApiBase {
// Output general page information for found titles
foreach ($pageSet->getGoodTitles() as $pageid => $title) {
$pages[$pageid] = array (
- 'ns' => $title->getNamespace(), 'title' => $title->getPrefixedText(), 'id' => $pageid);
+ 'pageid' => $pageid,
+ 'ns' => $title->getNamespace(), 'title' => $title->getPrefixedText());
}
if (!empty ($pages)) {
- ApiResult :: setIndexedTagName($pages, 'page');
- $this->getResult()->addValue('query', 'pages', $pages);
+ $result->setIndexedTagName($pages, 'page');
+ $result->addValue('query', 'pages', $pages);
}
}
@@ -238,13 +262,13 @@ class ApiQuery extends ApiBase {
// execute current pageSet to get the data for the generator module
$this->mPageSet->execute();
-
+
// populate resultPageSet with the generator output
$generator->profileIn();
$generator->executeGenerator($resultPageSet);
$resultPageSet->finishPageSetGeneration();
$generator->profileOut();
-
+
// Swap the resulting pageset back in
$this->mPageSet = $resultPageSet;
}
@@ -346,7 +370,7 @@ class ApiQuery extends ApiBase {
public function getVersion() {
$psModule = new ApiPageSet($this);
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiQuery.php 16820 2006-10-06 01:02:14Z yurik $';
+ $vers[] = __CLASS__ . ': $Id: ApiQuery.php 17374 2006-11-03 06:53:47Z yurik $';
$vers[] = $psModule->getVersion();
return $vers;
}
diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php
index 51330d62..9c076e65 100644
--- a/includes/api/ApiQueryAllpages.php
+++ b/includes/api/ApiQueryAllpages.php
@@ -42,95 +42,84 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
public function executeGenerator($resultPageSet) {
if ($resultPageSet->isResolvingRedirects())
$this->dieUsage('Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator', 'params');
-
+
$this->run($resultPageSet);
}
private function run($resultPageSet = null) {
- $limit = $from = $namespace = $filterredir = null;
- extract($this->extractRequestParams());
- $db = $this->getDB();
+ wfProfileIn($this->getModuleProfileName() . '-getDB');
+ $db = & $this->getDB();
+ wfProfileOut($this->getModuleProfileName() . '-getDB');
- $where = array (
- 'page_namespace' => $namespace
- );
-
- if (isset ($from)) {
- $where[] = 'page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($from));
- }
-
- if ($filterredir === 'redirects') {
- $where['page_is_redirect'] = 1;
- }
- elseif ($filterredir === 'nonredirects') {
- $where['page_is_redirect'] = 0;
- }
+ wfProfileIn($this->getModuleProfileName() . '-parseParams');
+ $limit = $from = $namespace = $filterredir = $prefix = null;
+ extract($this->extractRequestParams());
+
+ $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 (is_null($resultPageSet)) {
- $fields = array (
+ $this->addFields(array (
'page_id',
'page_namespace',
'page_title'
- );
+ ));
} else {
- $fields = $resultPageSet->getPageTableFields();
+ $this->addFields($resultPageSet->getPageTableFields());
}
- $this->profileDBIn();
- $res = $db->select('page', $fields, $where, __CLASS__ . '::' . __METHOD__, array (
- 'USE INDEX' => 'name_title',
- 'LIMIT' => $limit +1,
- 'ORDER BY' => 'page_namespace, page_title'
- ));
- $this->profileDBOut();
+ $this->addOption('USE INDEX', 'name_title');
+ $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...
- $msg = array (
- 'continue' => $this->encodeParamName('from'
- ) . '=' . ApiQueryBase :: keyToTitle($row->page_title));
- $this->getResult()->addValue('query-status', 'allpages', $msg);
+ $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->page_title));
break;
}
- $title = Title :: makeTitle($row->page_namespace, $row->page_title);
- // skip any pages that user has no rights to read
- if ($title->userCanRead()) {
-
- if (is_null($resultPageSet)) {
- $id = intval($row->page_id);
- $data[] = $id; // in generator mode, just assemble a list of page IDs.
- } else {
- $resultPageSet->processDbRow($row);
- }
+ if (is_null($resultPageSet)) {
+ $vals = $this->addRowInfo('page', $row);
+ if ($vals)
+ $data[intval($row->page_id)] = $vals;
+ } else {
+ $resultPageSet->processDbRow($row);
}
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- ApiResult :: setIndexedTagName($data, 'p');
- $this->getResult()->addValue('query', 'allpages', $data);
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, 'p');
+ $result->addValue('query', $this->getModuleName(), $data);
}
+
+ wfProfileOut($this->getModuleProfileName() . '-saveResults');
}
protected function getAllowedParams() {
-
- global $wgContLang;
- $validNamespaces = array ();
- foreach (array_keys($wgContLang->getNamespaces()) as $ns) {
- if ($ns >= 0)
- $validNamespaces[] = $ns; // strval($ns);
- }
-
return array (
'from' => null,
+ 'prefix' => null,
'namespace' => array (
ApiBase :: PARAM_DFLT => 0,
- ApiBase :: PARAM_TYPE => $validNamespaces
+ ApiBase :: PARAM_TYPE => 'namespace'
),
'filterredir' => array (
ApiBase :: PARAM_DFLT => 'all',
@@ -144,8 +133,8 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
ApiBase :: PARAM_MIN => 1,
- ApiBase :: PARAM_MAX1 => 500,
- ApiBase :: PARAM_MAX2 => 5000
+ ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
)
);
}
@@ -153,9 +142,10 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
protected function getParamDescription() {
return array (
'from' => 'The page title to start enumerating from.',
- 'namespace' => 'The namespace to enumerate. Default 0 (Main).',
- 'filterredir' => 'Which pages to list: "all" (default), "redirects", or "nonredirects"',
- 'limit' => 'How many total pages to return'
+ 'prefix' => 'Search for all page titles that begin with this value.',
+ 'namespace' => 'The namespace to enumerate.',
+ 'filterredir' => 'Which pages to list.',
+ 'limit' => 'How many total pages to return.'
);
}
@@ -166,8 +156,8 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
protected function getExamples() {
return array (
'Simple Use',
- ' api.php?action=query&list=allpages',
- ' api.php?action=query&list=allpages&apfrom=B&aplimit=5',
+ ' Show a list of pages starting at the letter "B"',
+ ' api.php?action=query&list=allpages&apfrom=B',
'Using as Generator',
' Show info about 4 pages starting at the letter "T"',
' api.php?action=query&generator=allpages&gaplimit=4&gapfrom=T&prop=info',
@@ -177,7 +167,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllpages.php 16820 2006-10-06 01:02:14Z yurik $';
+ return __CLASS__ . ': $Id: ApiQueryAllpages.php 17880 2006-11-23 08:25:56Z nickj $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php
new file mode 100644
index 00000000..413068f8
--- /dev/null
+++ b/includes/api/ApiQueryBacklinks.php
@@ -0,0 +1,358 @@
+<?php
+
+
+/*
+ * Created on Oct 16, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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");
+}
+
+class ApiQueryBacklinks extends ApiQueryGeneratorBase {
+
+ private $rootTitle, $contRedirs, $contLevel, $contTitle, $contID;
+
+ // output element name, database column field prefix, database table
+ private $backlinksSettings = array (
+ 'backlinks' => array (
+ 'code' => 'bl',
+ 'prefix' => 'pl',
+ 'linktbl' => 'pagelinks'
+ ),
+ 'embeddedin' => array (
+ 'code' => 'ei',
+ 'prefix' => 'tl',
+ 'linktbl' => 'templatelinks'
+ ),
+ 'imagelinks' => array (
+ 'code' => 'il',
+ 'prefix' => 'il',
+ 'linktbl' => 'imagelinks'
+ )
+ );
+
+ public function __construct($query, $moduleName) {
+ $code = $prefix = $linktbl = null;
+ extract($this->backlinksSettings[$moduleName]);
+
+ parent :: __construct($query, $moduleName, $code);
+ $this->bl_ns = $prefix . '_namespace';
+ $this->bl_from = $prefix . '_from';
+ $this->bl_tables = array (
+ $linktbl,
+ 'page'
+ );
+ $this->bl_code = $code;
+
+ $this->hasNS = $moduleName !== 'imagelinks';
+ if ($this->hasNS) {
+ $this->bl_title = $prefix . '_title';
+ $this->bl_sort = "{$this->bl_ns}, {$this->bl_title}, {$this->bl_from}";
+ $this->bl_fields = array (
+ $this->bl_ns,
+ $this->bl_title
+ );
+ } else {
+ $this->bl_title = $prefix . '_to';
+ $this->bl_sort = "{$this->bl_title}, {$this->bl_from}";
+ $this->bl_fields = array (
+ $this->bl_title
+ );
+ }
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+ $continue = $namespace = $redirect = $limit = null;
+ extract($this->extractRequestParams());
+
+ if ($redirect)
+ ApiBase :: dieDebug(__METHOD__, 'Redirect is not yet been implemented', 'notimplemented');
+
+ $this->processContinue($continue, $redirect);
+
+ $this->addFields($this->bl_fields);
+ if (is_null($resultPageSet))
+ $this->addFields(array (
+ 'page_id',
+ 'page_namespace',
+ 'page_title'
+ ));
+ else
+ $this->addFields($resultPageSet->getPageTableFields()); // will include page_id
+
+ $this->addTables($this->bl_tables);
+ $this->addWhere($this->bl_from . '=page_id');
+
+ 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);
+
+ if ($redirect)
+ $this->addWhereFld('page_is_redirect', 0);
+
+ $db = & $this->getDB();
+ if (!is_null($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
+ $this->addWhere($this->bl_from . '>=' . $plfrm);
+ } else {
+ $ns = $this->contTitle->getNamespace();
+ $t = $db->addQuotes($this->contTitle->getDBkey());
+ $whereWithoutNS = "{$this->bl_title}>$t OR ({$this->bl_title}=$t AND {$this->bl_from}>=$plfrm))";
+
+ if ($this->hasNS)
+ $this->addWhere("{$this->bl_ns}>$ns OR ({$this->bl_ns}=$ns AND ($whereWithoutNS)");
+ else
+ $this->addWhere($whereWithoutNS);
+ }
+ }
+
+ $res = $this->select(__METHOD__);
+
+ $count = 0;
+ $data = array ();
+ 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...
+ if ($redirect) {
+ $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);
+ $this->setContinueEnumParameter('continue', $continue);
+ break;
+ }
+
+ if (is_null($resultPageSet)) {
+ $vals = $this->addRowInfo('page', $row);
+ if ($vals)
+ $data[intval($row->page_id)] = $vals;
+ } else {
+ $resultPageSet->processDbRow($row);
+ }
+ }
+ $db->freeResult($res);
+
+ if (is_null($resultPageSet)) {
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, $this->bl_code);
+ $result->addValue('query', $this->getModuleName(), $data);
+ }
+ }
+
+ protected function processContinue($continue, $redirect) {
+ $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);
+
+ // 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
+ }
+
+ // only image titles are allowed for the root
+ if (!$this->hasNS && $this->rootTitle->getNamespace() !== NS_IMAGE)
+ $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) {
+ //
+ // expected redirect-mode parameter:
+ // ns|db_key|step|level|ns|db_key|id
+ // ns+db_key -- the root title
+ // step = 1 or 2 - which step to continue from - 1-titles, 2-redirects
+ // level -- how many levels to follow before starting enumerating.
+ // if level > 0 -- ns+title to continue from, otherwise skip these
+ // id = last page_id to continue from
+ //
+ if (count($continueList) > 4) {
+ $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()) {
+
+ $step = intval($continueList[2]);
+ if ($step === 1 || $step === 2) {
+ $this->contRedirs = ($step === 2);
+
+ $level = intval($continueList[3]);
+ if ($level !== 0 || $continueList[3] === '0') {
+ $this->contLevel = $level;
+
+ if ($level === 0) {
+ if (count($continueList) === 5) {
+ $contID = intval($continueList[4]);
+ if ($contID !== 0 || $continueList[4] === '0') {
+ $this->contID = $contID;
+ return; // done
+ }
+ }
+ } else {
+ if (count($continueList) === 7) {
+ $contNs = intval($continueList[4]);
+ if (($contNs !== 0 || $continueList[4] === '0') && !empty ($continueList[5])) {
+ $this->contTitle = Title :: makeTitleSafe($contNs, $continueList[5]);
+
+ $contID = intval($continueList[6]);
+ if ($contID !== 0 || $continueList[6] === '0') {
+ $this->contID = $contID;
+ return; // done
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ //
+ // expected non-redirect-mode parameter:
+ // ns|db_key|id
+ // ns+db_key -- the root title
+ // id = last page_id to continue from
+ //
+ if (count($continueList) === 3) {
+ $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()) {
+
+ $contID = intval($continueList[2]);
+ if ($contID !== 0) {
+ $this->contID = $contID;
+ return; // done
+ }
+ }
+ }
+ }
+ }
+
+ $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue");
+ }
+
+ protected function getContinueStr($lastPageID) {
+ return $this->rootTitle->getNamespace() .
+ '|' . $this->rootTitle->getDBkey() .
+ '|' . $lastPageID;
+ }
+
+ protected function getContinueRedirStr($isRedirPhase, $level, $ns, $title, $lastPageID) {
+ return $this->rootTitle->getNamespace() .
+ '|' . $this->rootTitle->getDBkey() .
+ '|' . ($isRedirPhase ? 1 : 2) .
+ '|' . $level .
+ ($level > 0 ? ('|' . $ns . '|' . $title) : '') .
+ '|' . $lastPageID;
+ }
+
+ protected function getAllowedParams() {
+
+ return array (
+ 'continue' => null,
+ 'namespace' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => 'namespace'
+ ),
+ 'redirect' => false,
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'continue' => 'When more results are available, use this to continue.',
+ 'namespace' => 'The namespace to enumerate.',
+ 'redirect' => 'If linking page is a redirect, find all pages that link to that redirect (not implemented)',
+ 'limit' => 'How many total pages to return.'
+ );
+ }
+
+ protected function getDescription() {
+ switch ($this->getModuleName()) {
+ case 'backlinks' :
+ return 'Find all pages that link to the given page';
+ case 'embeddedin' :
+ return 'Find all pages that embed (transclude) the given title';
+ case 'imagelinks' :
+ return 'Find all pages that use the given image title.';
+ default :
+ ApiBase :: dieDebug(__METHOD__, 'Unknown module name');
+ }
+ }
+
+ 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"
+ ),
+ 'embeddedin' => array (
+ "api.php?action=query&list=embeddedin&titles=Template:Stub",
+ "api.php?action=query&generator=embeddedin&titles=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"
+ )
+ );
+
+ return $examples[$this->getModuleName()];
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryBacklinks.php 17880 2006-11-23 08:25:56Z nickj $';
+ }
+}
+?> \ No newline at end of file
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php
index 574f742e..ae4edf98 100644
--- a/includes/api/ApiQueryBase.php
+++ b/includes/api/ApiQueryBase.php
@@ -31,11 +31,260 @@ if (!defined('MEDIAWIKI')) {
abstract class ApiQueryBase extends ApiBase {
- private $mQueryModule;
-
+ private $mQueryModule, $tables, $where, $fields, $options;
+
public function __construct($query, $moduleName, $paramPrefix = '') {
parent :: __construct($query->getMain(), $moduleName, $paramPrefix);
$this->mQueryModule = $query;
+ $this->resetQueryParams();
+ }
+
+ protected function resetQueryParams() {
+ $this->tables = array ();
+ $this->where = array ();
+ $this->fields = array ();
+ $this->options = array ();
+ }
+
+ protected function addTables($value) {
+ if (is_array($value))
+ $this->tables = array_merge($this->tables, $value);
+ else
+ $this->tables[] = $value;
+ }
+
+ protected function addFields($value) {
+ if (is_array($value))
+ $this->fields = array_merge($this->fields, $value);
+ else
+ $this->fields[] = $value;
+ }
+
+ protected function addFieldsIf($value, $condition) {
+ if ($condition) {
+ $this->addFields($value);
+ return true;
+ }
+ return false;
+ }
+
+ protected function addWhere($value) {
+ if (is_array($value))
+ $this->where = array_merge($this->where, $value);
+ else
+ $this->where[] = $value;
+ }
+
+ protected function addWhereIf($value, $condition) {
+ if ($condition) {
+ $this->addWhere($value);
+ return true;
+ }
+ return false;
+ }
+
+ protected function addWhereFld($field, $value) {
+ if (!is_null($value))
+ $this->where[$field] = $value;
+ }
+
+ protected function addWhereRange($field, $dir, $start, $end) {
+ $isDirNewer = ($dir === 'newer');
+ $after = ($isDirNewer ? '>=' : '<=');
+ $before = ($isDirNewer ? '<=' : '>=');
+ $db = $this->getDB();
+
+ if (!is_null($start))
+ $this->addWhere($field . $after . $db->addQuotes($start));
+
+ if (!is_null($end))
+ $this->addWhere($field . $before . $db->addQuotes($end));
+
+ $this->addOption('ORDER BY', $field . ($isDirNewer ? '' : ' DESC'));
+ }
+
+ protected function addOption($name, $value = null) {
+ if (is_null($value))
+ $this->options[] = $name;
+ else
+ $this->options[$name] = $value;
+ }
+
+ protected function select($method) {
+
+ // getDB has its own profileDBIn/Out calls
+ $db = $this->getDB();
+
+ $this->profileDBIn();
+ $res = $db->select($this->tables, $this->fields, $this->where, $method, $this->options);
+ $this->profileDBOut();
+
+ 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;
}
/**
@@ -46,12 +295,19 @@ abstract class ApiQueryBase extends ApiBase {
}
/**
- * Get the main Query module
+ * Get the main Query module
*/
public function getQuery() {
return $this->mQueryModule;
}
+ protected function setContinueEnumParameter($paramName, $paramValue) {
+ $msg = array (
+ $this->encodeParamName($paramName
+ ) => $paramValue);
+ $this->getResult()->addValue('query-continue', $this->getModuleName(), $msg);
+ }
+
/**
* Get the Query database connection (readonly)
*/
@@ -67,6 +323,11 @@ abstract class ApiQueryBase extends ApiBase {
return $this->mQueryModule->getPageSet();
}
+ /**
+ * This is a very simplistic utility function
+ * to convert a non-namespaced title string to a db key.
+ * It will replace all ' ' with '_'
+ */
public static function titleToKey($title) {
return str_replace(' ', '_', $title);
}
@@ -76,7 +337,7 @@ abstract class ApiQueryBase extends ApiBase {
}
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiQueryBase.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiQueryBase.php 17987 2006-11-29 05:45:03Z nickj $';
}
}
@@ -86,7 +347,7 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase {
public function __construct($query, $moduleName, $paramPrefix = '') {
parent :: __construct($query, $moduleName, $paramPrefix);
- $mIsGenerator = false;
+ $this->mIsGenerator = false;
}
public function setGeneratorMode() {
@@ -109,4 +370,4 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase {
*/
public abstract function executeGenerator($resultPageSet);
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index de651b00..d93d37a2 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -46,14 +46,17 @@ class ApiQueryInfo extends ApiQueryBase {
$pageSet = $this->getPageSet();
$titles = $pageSet->getGoodTitles();
- $result = & $this->getResult();
+ $result = $this->getResult();
$pageIsRedir = $pageSet->getCustomField('page_is_redirect');
$pageTouched = $pageSet->getCustomField('page_touched');
$pageLatest = $pageSet->getCustomField('page_latest');
- foreach ($titles as $pageid => $title) {
- $pageInfo = array ('touched' => $pageTouched[$pageid], 'lastrevid' => $pageLatest[$pageid]);
+ foreach ( $titles as $pageid => $unused ) {
+ $pageInfo = array (
+ 'touched' => wfTimestamp(TS_ISO_8601, $pageTouched[$pageid]),
+ 'lastrevid' => intval($pageLatest[$pageid])
+ );
if ($pageIsRedir[$pageid])
$pageInfo['redirect'] = '';
@@ -76,7 +79,7 @@ class ApiQueryInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryInfo.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiQueryInfo.php 17929 2006-11-25 17:11:58Z tstarling $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
new file mode 100644
index 00000000..243f96fa
--- /dev/null
+++ b/includes/api/ApiQueryLogEvents.php
@@ -0,0 +1,173 @@
+<?php
+
+
+/*
+ * Created on Oct 16, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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');
+}
+
+class ApiQueryLogEvents extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'le');
+ }
+
+ public function execute() {
+ $limit = $type = $start = $end = $dir = $user = $title = null;
+ extract($this->extractRequestParams());
+
+ $db = & $this->getDB();
+
+ list($tbl_logging, $tbl_page, $tbl_user) = $db->tableNamesN('logging', 'page', 'user');
+
+ $this->addOption('STRAIGHT_JOIN');
+ $this->addTables("$tbl_logging LEFT OUTER JOIN $tbl_page ON " .
+ "log_namespace=page_namespace AND log_title=page_title " .
+ "INNER JOIN $tbl_user ON user_id=log_user");
+
+ $this->addFields(array (
+ 'log_type',
+ 'log_action',
+ 'log_timestamp',
+ 'log_user',
+ 'user_name',
+ 'log_namespace',
+ 'log_title',
+ 'page_id',
+ 'log_comment',
+ 'log_params'
+ ));
+
+ $this->addWhereFld('log_type', $type);
+ $this->addWhereRange('log_timestamp', $dir, $start, $end);
+ $this->addOption('LIMIT', $limit +1);
+
+ if (!is_null($user)) {
+ $userid = $db->selectField('user', 'user_id', array (
+ 'user_name' => $user
+ ));
+ if (!$userid)
+ $this->dieUsage("User name $user not found", 'param_user');
+ $this->addWhereFld('log_user', $userid);
+ }
+
+ if (!is_null($title)) {
+ $titleObj = Title :: newFromText($title);
+ if (is_null($titleObj))
+ $this->dieUsage("Bad title value '$title'", 'param_title');
+ $this->addWhereFld('log_namespace', $titleObj->getNamespace());
+ $this->addWhereFld('log_title', $titleObj->getDBkey());
+ }
+
+ $data = array ();
+ $count = 0;
+ $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->log_timestamp);
+ break;
+ }
+
+ $vals = $this->addRowInfo('log', $row);
+ if($vals)
+ $data[] = $vals;
+ }
+ $db->freeResult($res);
+
+ $this->getResult()->setIndexedTagName($data, 'item');
+ $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'type' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'block',
+ 'protect',
+ 'rights',
+ 'delete',
+ 'upload',
+ 'move',
+ 'import',
+ 'renameuser',
+ 'newusers',
+ 'makebot'
+ )
+ ),
+ 'start' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ 'end' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ 'dir' => array (
+ ApiBase :: PARAM_DFLT => 'older',
+ ApiBase :: PARAM_TYPE => array (
+ 'newer',
+ 'older'
+ )
+ ),
+ 'user' => null,
+ 'title' => null,
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'type' => 'Filter log entries to only this type(s)',
+ 'start' => 'The timestamp to start enumerating from.',
+ 'end' => 'The timestamp to end enumerating.',
+ 'dir' => 'In which direction to enumerate.',
+ 'user' => 'Filter entries to those made by the given user.',
+ 'title' => 'Filter entries to those related to a page.',
+ 'limit' => 'How many total event entries to return.'
+ );
+ }
+
+ protected function getDescription() {
+ return 'Get events from logs.';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=logevents'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryLogEvents.php 17952 2006-11-27 08:36:57Z nickj $';
+ }
+}
+?>
diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php
new file mode 100644
index 00000000..38f51b05
--- /dev/null
+++ b/includes/api/ApiQueryRecentChanges.php
@@ -0,0 +1,187 @@
+<?php
+
+
+/*
+ * Created on Oct 19, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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');
+}
+
+class ApiQueryRecentChanges extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'rc');
+ }
+
+ public function execute() {
+ $limit = $prop = $namespace = $show = $dir = $start = $end = null;
+ extract($this->extractRequestParams());
+
+ $this->addTables('recentchanges');
+ $this->addWhereRange('rc_timestamp', $dir, $start, $end);
+ $this->addWhereFld('rc_namespace', $namespace);
+
+ if (!is_null($show)) {
+ $show = array_flip($show);
+ if ((isset ($show['minor']) && isset ($show['!minor'])) || (isset ($show['bot']) && isset ($show['!bot'])) || (isset ($show['anon']) && isset ($show['!anon'])))
+ $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show');
+
+ $this->addWhereIf('rc_minor = 0', isset ($show['!minor']));
+ $this->addWhereIf('rc_minor != 0', isset ($show['minor']));
+ $this->addWhereIf('rc_bot = 0', isset ($show['!bot']));
+ $this->addWhereIf('rc_bot != 0', isset ($show['bot']));
+ $this->addWhereIf('rc_user = 0', isset ($show['anon']));
+ $this->addWhereIf('rc_user != 0', isset ($show['!anon']));
+ }
+
+ $this->addFields(array (
+ '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'
+ ));
+
+ 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->addOption('LIMIT', $limit +1);
+ $this->addOption('USE INDEX', 'rc_timestamp');
+
+ $data = array ();
+ $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);
+ break;
+ }
+
+ $vals = $this->addRowInfo('rc', $row);
+ if ($vals)
+ $data[] = $vals;
+ }
+ $db->freeResult($res);
+
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, 'rc');
+ $result->addValue('query', $this->getModuleName(), $data);
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'start' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ 'end' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ '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_TYPE => array (
+ 'user',
+ 'comment',
+ 'flags'
+ )
+ ),
+ 'show' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'minor',
+ '!minor',
+ 'bot',
+ '!bot',
+ 'anon',
+ '!anon'
+ )
+ ),
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'start' => 'The timestamp to start enumerating from.',
+ 'end' => 'The timestamp to end enumerating.',
+ 'dir' => 'In which direction to enumerate.',
+ 'namespace' => 'Filter log entries to only this namespace(s)',
+ 'prop' => 'Include additional pieces of information',
+ 'show' => array (
+ 'Show only items that meet this criteria.',
+ 'For example, to see only minor edits done by logged-in users, set show=minor|!anon'
+ ),
+ 'limit' => 'How many total pages to return.'
+ );
+ }
+
+ protected function getDescription() {
+ return 'Enumerate recent changes';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=recentchanges'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 17880 2006-11-23 08:25:56Z nickj $';
+ }
+}
+?> \ No newline at end of file
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php
index f6097bad..3f678ff7 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -39,16 +39,11 @@ class ApiQueryRevisions extends ApiQueryBase {
$limit = $startid = $endid = $start = $end = $dir = $prop = null;
extract($this->extractRequestParams());
- $db = $this->getDB();
-
- // true when ordered by timestamp from older to newer, false otherwise
- $dirNewer = ($dir === 'newer');
-
// 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 = ($limit !== 0 || $startid !== 0 || $endid !== 0 || $dirNewer || isset ($start) || isset ($end));
+ $enumRevMode = (!is_null($limit) || !is_null($startid) || !is_null($endid) || $dir === 'newer' || !is_null($start) || !is_null($end));
$pageSet = $this->getPageSet();
$pageCount = $pageSet->getGoodTitleCount();
@@ -58,57 +53,38 @@ class ApiQueryRevisions extends ApiQueryBase {
if ($revCount === 0 && $pageCount === 0)
return;
- if ($revCount > 0 && $pageCount > 0)
- $this->dieUsage('The revids= parameter may not be used with titles, pageids, or generator options.', 'revids');
-
if ($revCount > 0 && $enumRevMode)
$this->dieUsage('The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).', 'revids');
- if ($revCount === 0 && $pageCount > 1 && $enumRevMode)
+ 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');
- $tables = array (
- 'revision'
- );
- $fields = array (
+ $this->addTables('revision');
+ $this->addFields(array (
'rev_id',
'rev_page',
'rev_text_id',
'rev_minor_edit'
- );
- $conds = array (
- 'rev_deleted' => 0
- );
- $options = array ();
-
- $showTimestamp = $showUser = $showComment = $showContent = false;
- if (isset ($prop)) {
- foreach ($prop as $p) {
- switch ($p) {
- case 'timestamp' :
- $fields[] = 'rev_timestamp';
- $showTimestamp = true;
- break;
- case 'user' :
- $fields[] = 'rev_user';
- $fields[] = 'rev_user_text';
- $showUser = true;
- break;
- case 'comment' :
- $fields[] = 'rev_comment';
- $showComment = true;
- break;
- case 'content' :
- $tables[] = 'text';
- $conds[] = 'rev_text_id=old_id';
- $fields[] = 'old_id';
- $fields[] = 'old_text';
- $fields[] = 'old_flags';
- $showContent = true;
- break;
- default :
- ApiBase :: dieDebug(__METHOD__, "unknown prop $p");
- }
+ ));
+ $this->addWhere('rev_deleted=0');
+
+ $showContent = false;
+
+ 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;
}
}
@@ -118,10 +94,10 @@ class ApiQueryRevisions extends ApiQueryBase {
if ($enumRevMode) {
// This is mostly to prevent parameter errors (and optimize sql?)
- if ($startid !== 0 && isset ($start))
+ if (!is_null($startid) && !is_null($start))
$this->dieUsage('start and startid cannot be used together', 'badparams');
- if ($endid !== 0 && isset ($end))
+ if (!is_null($endid) && !is_null($end))
$this->dieUsage('end and endid cannot be used together', 'badparams');
// This code makes an assumption that sorting by rev_id and rev_timestamp produces
@@ -130,40 +106,30 @@ class ApiQueryRevisions extends ApiQueryBase {
// Switching to rev_id removes the potential problem of having more than
// one row with the same timestamp for the same page.
// The order needs to be the same as start parameter to avoid SQL filesort.
- $options['ORDER BY'] = ($startid !== 0 ? 'rev_id' : 'rev_timestamp') . ($dirNewer ? '' : ' DESC');
-
- $before = ($dirNewer ? '<=' : '>=');
- $after = ($dirNewer ? '>=' : '<=');
- if ($startid !== 0)
- $conds[] = 'rev_id' . $after . intval($startid);
- if ($endid !== 0)
- $conds[] = 'rev_id' . $before . intval($endid);
- if (isset ($start))
- $conds[] = 'rev_timestamp' . $after . $db->addQuotes($start);
- if (isset ($end))
- $conds[] = 'rev_timestamp' . $before . $db->addQuotes($end);
+ if (is_null($startid))
+ $this->addWhereRange('rev_timestamp', $dir, $start, $end);
+ else
+ $this->addWhereRange('rev_id', $dir, $startid, $endid);
// must manually initialize unset limit
- if (!isset ($limit))
+ if (is_null($limit))
$limit = 10;
-
$this->validateLimit($this->encodeParamName('limit'), $limit, 1, $userMax, $botMax);
// There is only one ID, use it
- $conds['rev_page'] = array_pop(array_keys($pageSet->getGoodTitles()));
-
+ $this->addWhereFld('rev_page', current(array_keys($pageSet->getGoodTitles())));
}
elseif ($pageCount > 0) {
// When working in multi-page non-enumeration mode,
// limit to the latest revision only
- $tables[] = 'page';
- $conds[] = 'page_id=rev_page';
- $conds[] = 'page_latest=rev_id';
+ $this->addTables('page');
+ $this->addWhere('page_id=rev_page');
+ $this->addWhere('page_latest=rev_id');
$this->validateLimit('page_count', $pageCount, 1, $userMax, $botMax);
// Get all page IDs
- $conds['page_id'] = array_keys($pageSet->getGoodTitles());
+ $this->addWhereFld('page_id', array_keys($pageSet->getGoodTitles()));
$limit = $pageCount; // assumption testing -- we should never get more then $pageCount rows.
}
@@ -171,72 +137,51 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->validateLimit('rev_count', $revCount, 1, $userMax, $botMax);
// Get all revision IDs
- $conds['rev_id'] = array_keys($pageSet->getRevisionIDs());
+ $this->addWhereFld('rev_id', array_keys($pageSet->getRevisionIDs()));
$limit = $revCount; // assumption testing -- we should never get more then $revCount rows.
} else
ApiBase :: dieDebug(__METHOD__, 'param validation?');
- $options['LIMIT'] = $limit +1;
-
- $this->profileDBIn();
- $res = $db->select($tables, $fields, $conds, __METHOD__, $options);
- $this->profileDBOut();
+ $this->addOption('LIMIT', $limit +1);
$data = array ();
$count = 0;
+ $res = $this->select(__METHOD__);
+
+ $db = & $this->getDB();
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...
if (!$enumRevMode)
ApiBase :: dieDebug(__METHOD__, 'Got more rows then expected'); // bug report
-
- $startStr = 'startid=' . $row->rev_id;
- $msg = array (
- 'continue' => $startStr
- );
- $this->getResult()->addValue('query-status', 'revisions', $msg);
+ $this->setContinueEnumParameter('startid', $row->rev_id);
break;
}
- $vals = array (
- 'revid' => intval($row->rev_id
- ), 'oldid' => intval($row->rev_text_id));
-
- if ($row->rev_minor_edit) {
- $vals['minor'] = '';
- }
-
- if ($showTimestamp)
- $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rev_timestamp);
-
- if ($showUser) {
- $vals['user'] = $row->rev_user_text;
- if (!$row->rev_user)
- $vals['anon'] = '';
- }
-
- if ($showComment)
- $vals['comment'] = $row->rev_comment;
+ $vals = $this->addRowInfo('rev', $row);
+ if ($vals) {
+ if ($showContent)
+ ApiResult :: setContent($vals, Revision :: getRevisionText($row));
- if ($showContent) {
- ApiResult :: setContent($vals, Revision :: getRevisionText($row));
+ $this->getResult()->addValue(array (
+ 'query',
+ 'pages',
+ intval($row->rev_page
+ ), 'revisions'), intval($row->rev_id), $vals);
}
-
- $this->getResult()->addValue(array (
- 'query',
- 'pages',
- intval($row->rev_page
- ), 'revisions'), intval($row->rev_id), $vals);
}
$db->freeResult($res);
- // Ensure that all revisions are shown as '<r>' elements
- $data = & $this->getResultData();
- foreach ($data['query']['pages'] as & $page) {
- if (is_array($page) && array_key_exists('revisions', $page)) {
- ApiResult :: setIndexedTagName($page['revisions'], 'rev');
+ // Ensure that all revisions are shown as '<rev>' elements
+ $result = $this->getResult();
+ if ($result->getIsRawMode()) {
+ $data = $result->getData();
+ foreach ($data['query']['pages'] as & $page) {
+ if (is_array($page) && array_key_exists('revisions', $page)) {
+ $result->setIndexedTagName($page['revisions'], 'rev');
+ }
}
}
}
@@ -253,14 +198,17 @@ class ApiQueryRevisions extends ApiQueryBase {
)
),
'limit' => array (
- ApiBase :: PARAM_DFLT => 0,
ApiBase :: PARAM_TYPE => 'limit',
- ApiBase :: PARAM_MIN => 0,
- ApiBase :: PARAM_MAX1 => 50,
- ApiBase :: PARAM_MAX2 => 500
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_SML1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_SML2
+ ),
+ 'startid' => array (
+ ApiBase :: PARAM_TYPE => 'integer'
+ ),
+ 'endid' => array (
+ ApiBase :: PARAM_TYPE => 'integer'
),
- 'startid' => 0,
- 'endid' => 0,
'start' => array (
ApiBase :: PARAM_TYPE => 'timestamp'
),
@@ -279,7 +227,7 @@ class ApiQueryRevisions extends ApiQueryBase {
protected function getParamDescription() {
return array (
- 'prop' => 'Which properties to get for each revision: user|timestamp|comment|content',
+ 'prop' => 'Which properties to get for each revision.',
'limit' => 'limit how many revisions will be returned (enum)',
'startid' => 'from which revision id to start enumeration (enum)',
'endid' => 'stop revision enumeration on this revid (enum)',
@@ -314,7 +262,7 @@ class ApiQueryRevisions extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRevisions.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiQueryRevisions.php 17374 2006-11-03 06:53:47Z yurik $';
}
}
-?> \ No newline at end of file
+?>
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index 27c3f187..9e8c11ff 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -44,7 +44,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
case 'general' :
- global $wgSitename, $wgVersion, $wgCapitalLinks;
+ global $wgSitename, $wgVersion, $wgCapitalLinks, $wgRightsCode, $wgRightsText;
$data = array ();
$mainPage = Title :: newFromText(wfMsgForContent('mainpage'));
$data['mainpage'] = $mainPage->getText();
@@ -52,6 +52,9 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$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);
break;
@@ -65,10 +68,10 @@ class ApiQuerySiteinfo extends ApiQueryBase {
);
ApiResult :: setContent($data[$ns], $title);
}
- ApiResult :: setIndexedTagName($data, 'ns');
+ $this->getResult()->setIndexedTagName($data, 'ns');
$this->getResult()->addValue('query', $p, $data);
break;
-
+
default :
ApiBase :: dieDebug(__METHOD__, "Unknown prop=$p");
}
@@ -107,7 +110,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 17265 2006-10-27 03:50:34Z yurik $';
}
}
?> \ No newline at end of file
diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php
new file mode 100644
index 00000000..4f63cadb
--- /dev/null
+++ b/includes/api/ApiQueryUserContributions.php
@@ -0,0 +1,175 @@
+<?php
+
+
+/*
+ * Created on Oct 16, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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');
+}
+
+class ApiQueryContributions extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'uc');
+ }
+
+ public function execute() {
+
+ //Blank all our variables
+ $limit = $user = $start = $end = $dir = null;
+
+ //Get our parameters out
+ extract($this->extractRequestParams());
+
+ //Get a database instance
+ $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 );
+
+ $this->addOption('LIMIT', $limit + 1);
+
+ //Initialise some variables
+ $data = array ();
+ $count = 0;
+
+ //Do the actual query.
+ $res = $this->select( __METHOD__ );
+
+ //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);
+ 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);
+ }
+ }
+
+ //Free the database record so the connection can get on with other stuff
+ $db->freeResult($res);
+
+ //And send the whole shebang out as output.
+ $this->getResult()->setIndexedTagName($data, 'item');
+ $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ }
+
+ 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_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'start' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ 'end' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ 'user' => null,
+ 'dir' => array (
+ ApiBase :: PARAM_DFLT => 'older',
+ ApiBase :: PARAM_TYPE => array (
+ 'newer',
+ 'older'
+ )
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'limit' => 'The maximum number of contributions to return.',
+ '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).'
+ );
+ }
+
+ protected function getDescription() {
+ return 'Get edits by a user..';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=usercontribs&ucuser=YurikBot'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryUserContributions.php 17952 2006-11-27 08:36:57Z nickj $';
+ }
+}
+?>
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
new file mode 100644
index 00000000..67564d62
--- /dev/null
+++ b/includes/api/ApiQueryWatchlist.php
@@ -0,0 +1,234 @@
+<?php
+
+
+/*
+ * Created on Sep 25, 2006
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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');
+}
+
+class ApiQueryWatchlist extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'wl');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+ global $wgUser;
+
+ if (!$wgUser->isLoggedIn())
+ $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
+
+ $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');
+
+ $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));
+
+ if ($patrol) {
+ global $wgUseRCPatrol, $wgUser;
+ if (!$wgUseRCPatrol || !$wgUser->isAllowed('patrol'))
+ $this->dieUsage('patrol property is not available', 'patrol');
+ }
+ }
+
+ if (is_null($resultPageSet)) {
+ $this->addFields(array (
+ 'rc_cur_id',
+ '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);
+ }
+ elseif ($allrev) {
+ $this->addFields(array (
+ 'rc_this_oldid',
+ 'rc_namespace',
+ 'rc_title',
+ 'rc_timestamp'
+ ));
+ } else {
+ $this->addFields(array (
+ 'rc_cur_id',
+ 'rc_namespace',
+ 'rc_title',
+ 'rc_timestamp'
+ ));
+ }
+
+ $this->addTables(array (
+ 'watchlist',
+ 'page',
+ 'recentchanges'
+ ));
+
+ $userId = $wgUser->getID();
+ $this->addWhere(array (
+ 'wl_namespace = rc_namespace',
+ 'wl_title = rc_title',
+ 'rc_cur_id = page_id',
+ 'wl_user' => $userId
+ ));
+ $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->addOption('LIMIT', $limit +1);
+
+ $data = array ();
+ $count = 0;
+ $res = $this->select(__METHOD__);
+
+ $db = $this->getDB();
+ 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);
+ break;
+ }
+
+ if (is_null($resultPageSet)) {
+ $vals = $this->addRowInfo('rc', $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);
+ }
+ }
+ }
+ }
+
+ $db->freeResult($res);
+
+ if (is_null($resultPageSet)) {
+ $this->getResult()->setIndexedTagName($data, 'item');
+ $this->getResult()->addValue('query', $this->getModuleName(), $data);
+ }
+ elseif ($allrev) {
+ $resultPageSet->populateFromRevisionIDs($data);
+ } else {
+ $resultPageSet->populateFromPageIDs($data);
+ }
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'allrev' => false,
+ 'start' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ 'end' => array (
+ ApiBase :: PARAM_TYPE => 'timestamp'
+ ),
+ 'namespace' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => 'namespace'
+ ),
+ 'dir' => array (
+ ApiBase :: PARAM_DFLT => 'older',
+ ApiBase :: PARAM_TYPE => array (
+ 'newer',
+ 'older'
+ )
+ ),
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'prop' => array (
+ APIBase :: PARAM_ISMULTI => true,
+ APIBase :: PARAM_TYPE => array (
+ 'user',
+ 'comment',
+ 'timestamp',
+ 'patrol'
+ )
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'allrev' => 'Include multiple revisions of the same page within given timeframe.',
+ 'start' => 'The timestamp to start enumerating from.',
+ 'end' => 'The timestamp to end enumerating.',
+ 'namespace' => 'Filter changes to only the given namespace(s).',
+ 'dir' => 'In which direction to enumerate pages.',
+ 'limit' => 'How many total pages to return per request.',
+ 'prop' => 'Which additional items to get (non-generator mode only).'
+ );
+ }
+
+ protected function getDescription() {
+ return '';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=watchlist',
+ 'api.php?action=query&list=watchlist&wlallrev',
+ '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 17987 2006-11-29 05:45:03Z nickj $';
+ }
+}
+?>
diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php
index 67fbf41e..c9bfcfb9 100644
--- a/includes/api/ApiResult.php
+++ b/includes/api/ApiResult.php
@@ -31,21 +31,34 @@ if (!defined('MEDIAWIKI')) {
class ApiResult extends ApiBase {
- private $mData;
+ private $mData, $mIsRawMode;
/**
* Constructor
*/
public function __construct($main) {
parent :: __construct($main, 'result');
- $this->Reset();
+ $this->mIsRawMode = false;
+ $this->reset();
}
- public function Reset() {
+ public function reset() {
$this->mData = array ();
}
+
+ /**
+ * Call this function when special elements such as '_element'
+ * are needed by the formatter, for example in XML printing.
+ */
+ public function setRawMode() {
+ $this->mIsRawMode = true;
+ }
+
+ public function getIsRawMode() {
+ return $this->mIsRawMode;
+ }
- function & getData() {
+ public function & getData() {
return $this->mData;
}
@@ -73,11 +86,19 @@ class ApiResult extends ApiBase {
/**
* Adds the content element to the array.
* Use this function instead of hardcoding the '*' element.
+ * @param string $subElemName when present, content element is created as a sub item of the arr.
+ * Use this parameter to create elements in format <elem>text</elem> without attributes
*/
- public static function setContent(& $arr, $value) {
+ public static function setContent(& $arr, $value, $subElemName = null) {
if (is_array($value))
ApiBase :: dieDebug(__METHOD__, 'Bad parameter');
- ApiResult :: setElement($arr, '*', $value);
+ if (is_null($subElemName)) {
+ ApiResult :: setElement($arr, '*', $value);
+ } else {
+ if (!isset ($arr[$subElemName]))
+ $arr[$subElemName] = array ();
+ ApiResult :: setElement($arr[$subElemName], '*', $value);
+ }
}
// public static function makeContentElement($tag, $value) {
@@ -89,10 +110,13 @@ class ApiResult extends ApiBase {
* In case the array contains indexed values (in addition to named),
* all indexed values will have the given tag name.
*/
- public static function setIndexedTagName(& $arr, $tag) {
- // Do not use setElement() as it is ok to call this more than once
+ public function setIndexedTagName(& $arr, $tag) {
+ // In raw mode, add the '_element', otherwise just ignore
+ if (!$this->getIsRawMode())
+ return;
if ($arr === null || $tag === null || !is_array($arr) || is_array($tag))
ApiBase :: dieDebug(__METHOD__, 'Bad parameter');
+ // Do not use setElement() as it is ok to call this more than once
$arr['_element'] = $tag;
}
@@ -105,7 +129,7 @@ class ApiResult extends ApiBase {
$data = & $this->getData();
- if (isset ($path)) {
+ if (!is_null($path)) {
if (is_array($path)) {
foreach ($path as $p) {
if (!isset ($data[$p]))
@@ -122,32 +146,12 @@ class ApiResult extends ApiBase {
ApiResult :: setElement($data, $name, $value);
}
- /**
- * Recursivelly removes any elements from the array that begin with an '_'.
- * The content element '*' is the only special element that is left.
- * Use this method when the entire data object gets sent to the user.
- */
- public function SanitizeData() {
- ApiResult :: SanitizeDataInt($this->mData);
- }
-
- private static function SanitizeDataInt(& $data) {
- foreach ($data as $key => & $value) {
- if ($key[0] === '_') {
- unset ($data[$key]);
- }
- elseif (is_array($value)) {
- ApiResult :: SanitizeDataInt($value);
- }
- }
- }
-
public function execute() {
ApiBase :: dieDebug(__METHOD__, 'execute() is not supported on Result object');
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiResult.php 16757 2006-10-03 05:41:55Z yurik $';
+ return __CLASS__ . ': $Id: ApiResult.php 17076 2006-10-18 05:35:24Z yurik $';
}
}
?> \ No newline at end of file