summaryrefslogtreecommitdiff
path: root/includes/api
diff options
context:
space:
mode:
Diffstat (limited to 'includes/api')
-rw-r--r--includes/api/ApiBase.php134
-rw-r--r--includes/api/ApiBlock.php10
-rw-r--r--includes/api/ApiDelete.php117
-rw-r--r--includes/api/ApiEditPage.php299
-rw-r--r--includes/api/ApiEmailUser.php114
-rw-r--r--includes/api/ApiExpandTemplates.php38
-rw-r--r--includes/api/ApiFeedWatchlist.php33
-rw-r--r--includes/api/ApiFormatBase.php51
-rw-r--r--includes/api/ApiFormatDbg.php5
-rw-r--r--includes/api/ApiFormatJson.php7
-rw-r--r--includes/api/ApiFormatJson_json.php19
-rw-r--r--includes/api/ApiFormatPhp.php5
-rw-r--r--includes/api/ApiFormatTxt.php5
-rw-r--r--includes/api/ApiFormatWddx.php5
-rw-r--r--includes/api/ApiFormatXml.php34
-rw-r--r--includes/api/ApiFormatYaml.php5
-rw-r--r--includes/api/ApiFormatYaml_spyc.php235
-rw-r--r--includes/api/ApiHelp.php7
-rw-r--r--includes/api/ApiLogin.php73
-rw-r--r--includes/api/ApiLogout.php10
-rw-r--r--includes/api/ApiMain.php150
-rw-r--r--includes/api/ApiMove.php50
-rw-r--r--includes/api/ApiOpenSearch.php22
-rw-r--r--includes/api/ApiPageSet.php200
-rw-r--r--includes/api/ApiParamInfo.php11
-rw-r--r--includes/api/ApiParse.php83
-rw-r--r--includes/api/ApiProtect.php15
-rw-r--r--includes/api/ApiQuery.php67
-rw-r--r--includes/api/ApiQueryAllCategories.php63
-rw-r--r--includes/api/ApiQueryAllLinks.php49
-rw-r--r--includes/api/ApiQueryAllUsers.php81
-rw-r--r--includes/api/ApiQueryAllimages.php205
-rw-r--r--includes/api/ApiQueryAllmessages.php25
-rw-r--r--includes/api/ApiQueryAllpages.php48
-rw-r--r--includes/api/ApiQueryBacklinks.php371
-rw-r--r--includes/api/ApiQueryBase.php208
-rw-r--r--includes/api/ApiQueryBlocks.php59
-rw-r--r--includes/api/ApiQueryCategories.php73
-rw-r--r--includes/api/ApiQueryCategoryInfo.php91
-rw-r--r--includes/api/ApiQueryCategoryMembers.php75
-rw-r--r--includes/api/ApiQueryDeletedrevs.php30
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php65
-rw-r--r--includes/api/ApiQueryExternalLinks.php51
-rw-r--r--includes/api/ApiQueryImageInfo.php162
-rw-r--r--includes/api/ApiQueryImages.php70
-rw-r--r--includes/api/ApiQueryInfo.php344
-rw-r--r--includes/api/ApiQueryLangLinks.php61
-rw-r--r--includes/api/ApiQueryLinks.php78
-rw-r--r--includes/api/ApiQueryLogEvents.php57
-rw-r--r--includes/api/ApiQueryRandom.php16
-rw-r--r--includes/api/ApiQueryRecentChanges.php120
-rw-r--r--includes/api/ApiQueryRevisions.php183
-rw-r--r--includes/api/ApiQuerySearch.php19
-rw-r--r--includes/api/ApiQuerySiteinfo.php304
-rw-r--r--includes/api/ApiQueryUserContributions.php69
-rw-r--r--includes/api/ApiQueryUserInfo.php18
-rw-r--r--includes/api/ApiQueryUsers.php47
-rw-r--r--includes/api/ApiQueryWatchlist.php29
-rw-r--r--includes/api/ApiResult.php36
-rw-r--r--includes/api/ApiRollback.php13
-rw-r--r--includes/api/ApiUnblock.php11
-rw-r--r--includes/api/ApiUndelete.php17
62 files changed, 3423 insertions, 1529 deletions
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index 3a7b5099..732adae1 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -26,15 +26,15 @@
/**
* This abstract class implements many basic API functions, and is the base of all API classes.
* The class functions are divided into several areas of functionality:
- *
+ *
* Module parameters: Derived classes can define getAllowedParams() to specify which parameters to expect,
* how to parse and validate them.
- *
+ *
* Profiling: various methods to allow keeping tabs on various tasks and their time costs
- *
+ *
* Self-documentation: code to allow api to document its own state.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
abstract class ApiBase {
@@ -68,18 +68,18 @@ abstract class ApiBase {
*****************************************************************************/
/**
- * Evaluates the parameters, performs the requested query, and sets up the
- * result. Concrete implementations of ApiBase must override this method to
+ * Evaluates the parameters, performs the requested query, and sets up the
+ * result. Concrete implementations of ApiBase must override this method to
* provide whatever functionality their module offers. Implementations must
* not produce any output on their own and are not expected to handle any
- * errors.
+ * errors.
*
* The execute method will be invoked directly by ApiMain immediately before
* the result of the module is output. Aside from the constructor, implementations
* should assume that no other methods will be called externally on the module
* before the result is processed.
*
- * The result data should be stored in the result object referred to by
+ * The result data should be stored in the result object referred to by
* "getResult()". Refer to ApiResult.php for details on populating a result
* object.
*/
@@ -93,21 +93,21 @@ abstract class ApiBase {
public abstract function getVersion();
/**
- * Get the name of the module being executed by this instance
+ * Get the name of the module being executed by this instance
*/
public function getModuleName() {
return $this->mModuleName;
}
/**
- * Get parameter prefix (usually two letters or an empty string).
+ * Get parameter prefix (usually two letters or an empty string).
*/
public function getModulePrefix() {
return $this->mModulePrefix;
- }
+ }
/**
- * Get the name of the module as shown in the profiler log
+ * Get the name of the module as shown in the profiler log
*/
public function getModuleProfileName($db = false) {
if ($db)
@@ -124,7 +124,7 @@ abstract class ApiBase {
}
/**
- * Returns true if this module is the main module ($this === $this->mMainModule),
+ * Returns true if this module is the main module ($this === $this->mMainModule),
* false otherwise.
*/
public function isMain() {
@@ -151,10 +151,17 @@ abstract class ApiBase {
}
/**
- * Set warning section for this module. Users should monitor this section to
+ * Set warning section for this module. Users should monitor this section to
* notice any changes in API.
*/
public function setWarning($warning) {
+ # If there is a warning already, append it to the existing one
+ $data =& $this->getResult()->getData();
+ if(isset($data['warnings'][$this->getModuleName()]))
+ {
+ $warning = "{$data['warnings'][$this->getModuleName()]['*']}\n$warning";
+ unset($data['warnings'][$this->getModuleName()]);
+ }
$msg = array();
ApiResult :: setContent($msg, $warning);
$this->getResult()->addValue('warnings', $this->getModuleName(), $msg);
@@ -163,7 +170,7 @@ 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.
+ * A value of null means the default format will be used.
*/
public function getCustomPrinter() {
return null;
@@ -186,6 +193,9 @@ abstract class ApiBase {
);
$msg = $lnPrfx . implode($lnPrfx, $msg) . "\n";
+ if ($this->mustBePosted())
+ $msg .= "\nThis module only accepts POST requests.\n";
+
// Parameters
$paramsMsg = $this->makeHelpMsgParameters();
if ($paramsMsg !== false) {
@@ -207,7 +217,7 @@ abstract class ApiBase {
$versions = $this->getVersion();
$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);
@@ -223,7 +233,7 @@ abstract class ApiBase {
return $msg;
}
- /**
+ /**
* Generates the parameter descriptions for this module, to be displayed in the
* module's help.
*/
@@ -239,7 +249,7 @@ abstract class ApiBase {
if (is_array($desc))
$desc = implode($paramPrefix, $desc);
- $type = $paramSettings[self :: PARAM_TYPE];
+ $type = isset($paramSettings[self :: PARAM_TYPE])? $paramSettings[self :: PARAM_TYPE] : null;
if (isset ($type)) {
if (isset ($paramSettings[self :: PARAM_ISMULTI]))
$prompt = 'Values (separate with \'|\'): ';
@@ -274,7 +284,7 @@ abstract class ApiBase {
$intRangeStr = "The value must be no more than {$paramSettings[self :: PARAM_MAX]}";
else
$intRangeStr = "The value must be between {$paramSettings[self :: PARAM_MIN]} and {$paramSettings[self :: PARAM_MAX]}";
-
+
$desc .= $paramPrefix . $intRangeStr;
}
break;
@@ -324,7 +334,7 @@ abstract class ApiBase {
/**
* This method mangles parameter name based on the prefix supplied to the constructor.
- * Override this method to change parameter name during runtime
+ * Override this method to change parameter name during runtime
*/
public function encodeParamName($paramName) {
return $this->mModulePrefix . $paramName;
@@ -348,12 +358,12 @@ abstract class ApiBase {
}
/**
- * Get a value for the given parameter
+ * Get a value for the given parameter
*/
- protected function getParameter($paramName) {
+ protected function getParameter($paramName, $parseMaxLimit = true) {
$params = $this->getAllowedParams();
$paramSettings = $params[$paramName];
- return $this->getParameterFromSettings($paramName, $paramSettings);
+ return $this->getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit);
}
/**
@@ -435,7 +445,7 @@ abstract class ApiBase {
$value = is_array($value) ? array_map('intval', $value) : intval($value);
$min = isset ($paramSettings[self :: PARAM_MIN]) ? $paramSettings[self :: PARAM_MIN] : null;
$max = isset ($paramSettings[self :: PARAM_MAX]) ? $paramSettings[self :: PARAM_MAX] : null;
-
+
if (!is_null($min) || !is_null($max)) {
$values = is_array($value) ? $value : array($value);
foreach ($values as $v) {
@@ -495,24 +505,41 @@ abstract class ApiBase {
/**
* Return an array of values that were given in a 'a|b|c' notation,
* after it optionally validates them against the list allowed values.
- *
+ *
* @param valueName - The name of the parameter (for error reporting)
* @param value - The value being parsed
* @param allowMultiple - Can $value contain more than one value separated by '|'?
* @param allowedValues - An array of values to check against. If null, all values are accepted.
- * @return (allowMultiple ? an_array_of_values : a_single_value)
+ * @return (allowMultiple ? an_array_of_values : a_single_value)
*/
protected function parseMultiValue($valueName, $value, $allowMultiple, $allowedValues) {
- $valuesList = explode('|', $value);
+ if( trim($value) === "" )
+ return array();
+ $sizeLimit = $this->mMainModule->canApiHighLimits() ? 501 : 51;
+ $valuesList = explode('|', $value,$sizeLimit);
+ if( count($valuesList) == $sizeLimit ) {
+ $junk = array_pop($valuesList); // kill last jumbled param
+ }
if (!$allowMultiple && count($valuesList) != 1) {
$possibleValues = is_array($allowedValues) ? "of '" . implode("', '", $allowedValues) . "'" : '';
$this->dieUsage("Only one $possibleValues is allowed for parameter '$valueName'", "multival_$valueName");
}
if (is_array($allowedValues)) {
- $unknownValues = array_diff($valuesList, $allowedValues);
- if ($unknownValues) {
- $this->dieUsage('Unrecognised value' . (count($unknownValues) > 1 ? "s" : "") . " for parameter '$valueName'", "unknown_$valueName");
+ # Check for unknown values
+ $unknown = array_diff($valuesList, $allowedValues);
+ if(!empty($unknown))
+ {
+ if($allowMultiple)
+ {
+ $s = count($unknown) > 1 ? "s" : "";
+ $vals = implode(", ", $unknown);
+ $this->setWarning("Unrecognized value$s for parameter '$valueName': $vals");
+ }
+ else
+ $this->dieUsage("Unrecognized value for parameter '$valueName': {$valuesList[0]}", "unknown_$valueName");
}
+ # Now throw them out
+ $valuesList = array_intersect($valuesList, $allowedValues);
}
return $allowMultiple ? $valuesList : $valuesList[0];
@@ -544,12 +571,12 @@ abstract class ApiBase {
}
/**
- * Call main module's error handler
+ * Call main module's error handler
*/
public function dieUsage($description, $errorCode, $httpRespCode = 0) {
throw new UsageException($description, $this->encodeParamName($errorCode), $httpRespCode);
}
-
+
/**
* Array that maps message keys to error messages. $1 and friends are replaced.
*/
@@ -557,7 +584,7 @@ abstract class ApiBase {
// This one MUST be present, or dieUsageMsg() will recurse infinitely
'unknownerror' => array('code' => 'unknownerror', 'info' => "Unknown error: ``\$1''"),
'unknownerror-nocode' => array('code' => 'unknownerror', 'info' => 'Unknown error'),
-
+
// Messages from Title::getUserPermissionsErrors()
'ns-specialprotected' => array('code' => 'unsupportednamespace', 'info' => "Pages in the Special namespace can't be edited"),
'protectedinterface' => array('code' => 'protectednamespace-interface', 'info' => "You're not allowed to edit interface messages"),
@@ -578,11 +605,11 @@ abstract class ApiBase {
'confirmedittext' => array('code' => 'confirmemail', 'info' => "You must confirm your e-mail address before you can edit"),
'blockedtext' => array('code' => 'blocked', 'info' => "You have been blocked from editing"),
'autoblockedtext' => array('code' => 'autoblocked', 'info' => "Your IP address has been blocked automatically, because it was used by a blocked user"),
-
+
// Miscellaneous interface messages
'actionthrottledtext' => array('code' => 'ratelimited', 'info' => "You've exceeded your rate limit. Please wait some time and try again"),
'alreadyrolled' => array('code' => 'alreadyrolled', 'info' => "The page you tried to rollback was already rolled back"),
- 'cantrollback' => array('code' => 'onlyauthor', 'info' => "The page you tried to rollback only has one author"),
+ 'cantrollback' => array('code' => 'onlyauthor', 'info' => "The page you tried to rollback only has one author"),
'readonlytext' => array('code' => 'readonly', 'info' => "The wiki is currently in read-only mode"),
'sessionfailure' => array('code' => 'badtoken', 'info' => "Invalid token"),
'cannotdelete' => array('code' => 'cantdelete', 'info' => "Couldn't delete ``\$1''. Maybe it was deleted already by someone else"),
@@ -593,6 +620,8 @@ abstract class ApiBase {
'protectedpage' => array('code' => 'protectedpage', 'info' => "You don't have permission to perform this move"),
'hookaborted' => array('code' => 'hookaborted', 'info' => "The modification you tried to make was aborted by an extension hook"),
'cantmove-titleprotected' => array('code' => 'protectedtitle', 'info' => "The destination article has been protected from creation"),
+ 'imagenocrossnamespace' => array('code' => 'nonfilenamespace', 'info' => "Can't move a file to a non-file namespace"),
+ 'imagetypemismatch' => array('code' => 'filetypemismatch', 'info' => "The new file extension doesn't match its type"),
// 'badarticleerror' => shouldn't happen
// 'badtitletext' => shouldn't happen
'ip_range_invalid' => array('code' => 'invalidrange', 'info' => "Invalid IP range"),
@@ -603,7 +632,7 @@ abstract class ApiBase {
'ipb_already_blocked' => array('code' => 'alreadyblocked', 'info' => "The user you tried to block was already blocked"),
'ipb_blocked_as_range' => array('code' => 'blockedasrange', 'info' => "IP address ``\$1'' was blocked as part of range ``\$2''. You can't unblock the IP invidually, but you can unblock the range as a whole."),
'ipb_cant_unblock' => array('code' => 'cantunblock', 'info' => "The block you specified was not found. It may have been unblocked already"),
-
+
// API-specific messages
'missingparam' => array('code' => 'no$1', 'info' => "The \$1 parameter must be set"),
'invalidtitle' => array('code' => 'invalidtitle', 'info' => "Bad title ``\$1''"),
@@ -616,12 +645,28 @@ abstract class ApiBase {
'canthide' => array('code' => 'canthide', 'info' => "You don't have permission to hide user names from the block log"),
'cantblock-email' => array('code' => 'cantblock-email', 'info' => "You don't have permission to block users from sending e-mail through the wiki"),
'unblock-notarget' => array('code' => 'notarget', 'info' => "Either the id or the user parameter must be set"),
- 'unblock-idanduser' => array('code' => 'idanduser', 'info' => "The id and user parameters can\'t be used together"),
+ 'unblock-idanduser' => array('code' => 'idanduser', 'info' => "The id and user parameters can't be used together"),
'cantunblock' => array('code' => 'permissiondenied', 'info' => "You don't have permission to unblock users"),
'cannotundelete' => array('code' => 'cantundelete', 'info' => "Couldn't undelete: the requested revisions may not exist, or may have been undeleted already"),
'permdenied-undelete' => array('code' => 'permissiondenied', 'info' => "You don't have permission to restore deleted revisions"),
+ 'createonly-exists' => array('code' => 'articleexists', 'info' => "The article you tried to create has been created already"),
+ 'nocreate-missing' => array('code' => 'missingtitle', 'info' => "The article you tried to edit doesn't exist"),
+
+ // ApiEditPage messages
+ 'noimageredirect-anon' => array('code' => 'noimageredirect-anon', 'info' => "Anonymous users can't create image redirects"),
+ 'noimageredirect-logged' => array('code' => 'noimageredirect', 'info' => "You don't have permission to create image redirects"),
+ 'spamdetected' => array('code' => 'spamdetected', 'info' => "Your edit was refused because it contained a spam fragment: ``\$1''"),
+ 'filtered' => array('code' => 'filtered', 'info' => "The filter callback function refused your edit"),
+ 'contenttoobig' => array('code' => 'contenttoobig', 'info' => "The content you supplied exceeds the article size limit of \$1 bytes"),
+ 'noedit-anon' => array('code' => 'noedit-anon', 'info' => "Anonymous users can't edit pages"),
+ 'noedit' => array('code' => 'noedit', 'info' => "You don't have permission to edit pages"),
+ 'wasdeleted' => array('code' => 'pagedeleted', 'info' => "The page has been deleted since you fetched its timestamp"),
+ 'blankpage' => array('code' => 'emptypage', 'info' => "Creating new, empty pages is not allowed"),
+ 'editconflict' => array('code' => 'editconflict', 'info' => "Edit conflict detected"),
+ 'hashcheckfailed' => array('code' => 'badmd5', 'info' => "The supplied MD5 hash was incorrect"),
+ 'missingtext' => array('code' => 'notext', 'info' => "One of the text, appendtext and prependtext parameters must be set"),
);
-
+
/**
* Output the error message related to a certain array
* @param array $error Element of a getUserPermissionsErrors()
@@ -654,7 +699,7 @@ abstract class ApiBase {
public function isEditMode() {
return false;
}
-
+
/**
* Indicates whether this module must be called with a POST request
*/
@@ -694,7 +739,7 @@ abstract class ApiBase {
/**
* 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.
+ * of the profiling state the module was in. This method does such cleanup.
*/
public function safeProfileOut() {
if ($this->mTimeIn !== 0) {
@@ -755,7 +800,7 @@ abstract class ApiBase {
ApiBase :: dieDebug(__METHOD__, 'called without calling profileDBOut() first');
return $this->mDBTime;
}
-
+
public static function debugPrint($value, $name = 'unknown', $backtrace = false) {
print "\n\n<pre><b>Debuging value '$name':</b>\n\n";
var_export($value);
@@ -769,7 +814,6 @@ abstract class ApiBase {
* Returns a String that identifies the version of this class.
*/
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiBase.php 31259 2008-02-25 14:14:55Z catrope $';
- }
+ return __CLASS__ . ': $Id: ApiBase.php 36309 2008-06-15 20:37:28Z catrope $';
+ }
}
-
diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php
index e5c238ae..34813bf7 100644
--- a/includes/api/ApiBlock.php
+++ b/includes/api/ApiBlock.php
@@ -31,7 +31,7 @@ if (!defined('MEDIAWIKI')) {
* API module that facilitates the blocking of users. Requires API write mode
* to be enabled.
*
- * @addtogroup API
+ * @ingroup API
*/
class ApiBlock extends ApiBase {
@@ -87,17 +87,15 @@ class ApiBlock extends ApiBase {
$form->BlockEmail = $params['noemail'];
$form->BlockHideName = $params['hidename'];
- $dbw = wfGetDb(DB_MASTER);
- $dbw->begin();
+ $userID = $expiry = null;
$retval = $form->doBlock($userID, $expiry);
if(!empty($retval))
// We don't care about multiple errors, just report one of them
$this->dieUsageMsg($retval);
- $dbw->commit();
$res['user'] = $params['user'];
$res['userID'] = $userID;
- $res['expiry'] = ($expiry == Block::infinity() ? 'infinite' : $expiry);
+ $res['expiry'] = ($expiry == Block::infinity() ? 'infinite' : wfTimestamp(TS_ISO_8601, $expiry));
$res['reason'] = $params['reason'];
if($params['anononly'])
$res['anononly'] = '';
@@ -159,6 +157,6 @@ class ApiBlock extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiBlock.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiBlock.php 35388 2008-05-27 10:18:28Z catrope $';
}
}
diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php
index cd747e7e..06592d46 100644
--- a/includes/api/ApiDelete.php
+++ b/includes/api/ApiDelete.php
@@ -29,10 +29,10 @@ if (!defined('MEDIAWIKI')) {
/**
- * API module that facilitates deleting pages. The API eqivalent of action=delete.
+ * API module that facilitates deleting pages. The API eqivalent of action=delete.
* Requires API write mode to be enabled.
*
- * @addtogroup API
+ * @ingroup API
*/
class ApiDelete extends ApiBase {
@@ -42,16 +42,16 @@ class ApiDelete extends ApiBase {
/**
* Extracts the title, token, and reason from the request parameters and invokes
- * the local delete() function with these as arguments. It does not make use of
- * the delete function specified by Article.php. If the deletion succeeds, the
- * details of the article deleted and the reason for deletion are added to the
+ * the local delete() function with these as arguments. It does not make use of
+ * the delete function specified by Article.php. If the deletion succeeds, the
+ * details of the article deleted and the reason for deletion are added to the
* result object.
*/
public function execute() {
global $wgUser;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
-
+
$titleObj = NULL;
if(!isset($params['title']))
$this->dieUsageMsg(array('missingparam', 'title'));
@@ -64,21 +64,45 @@ class ApiDelete extends ApiBase {
if(!$titleObj->exists())
$this->dieUsageMsg(array('notanarticle'));
- $articleObj = new Article($titleObj);
$reason = (isset($params['reason']) ? $params['reason'] : NULL);
- $dbw = wfGetDb(DB_MASTER);
- $dbw->begin();
- $retval = self::delete($articleObj, $params['token'], $reason);
-
- if(!empty($retval))
- // We don't care about multiple errors, just report one of them
- $this->dieUsageMsg(current($retval));
+ if ($titleObj->getNamespace() == NS_IMAGE) {
+ $retval = self::deletefile($params['token'], $titleObj, $params['oldimage'], $reason, false);
+ if(!empty($retval))
+ // We don't care about multiple errors, just report one of them
+ $this->dieUsageMsg(current($retval));
+ } else {
+ $articleObj = new Article($titleObj);
+ $retval = self::delete($articleObj, $params['token'], $reason);
+
+ if(!empty($retval))
+ // We don't care about multiple errors, just report one of them
+ $this->dieUsageMsg(current($retval));
+
+ if($params['watch'] || $wgUser->getOption('watchdeletion'))
+ $articleObj->doWatch();
+ else if($params['unwatch'])
+ $articleObj->doUnwatch();
+ }
- $dbw->commit();
$r = array('title' => $titleObj->getPrefixedText(), 'reason' => $reason);
$this->getResult()->addValue(null, $this->getModuleName(), $r);
}
+ private static function getPermissionsError(&$title, $token) {
+ global $wgUser;
+ // Check wiki readonly
+ if (wfReadOnly()) return array(array('readonlytext'));
+
+ // Check permissions
+ $errors = $title->getUserPermissionsErrors('delete', $wgUser);
+ if (count($errors) > 0) return $errors;
+
+ // Check token
+ if(!$wgUser->matchEditToken($token))
+ return array(array('sessionfailure'));
+ return array();
+ }
+
/**
* We have our own delete() function, since Article.php's implementation is split in two phases
*
@@ -90,41 +114,67 @@ class ApiDelete extends ApiBase {
public static function delete(&$article, $token, &$reason = NULL)
{
global $wgUser;
-
- // Check permissions
- $errors = $article->mTitle->getUserPermissionsErrors('delete', $wgUser);
- if(!empty($errors))
- return $errors;
- if(wfReadOnly())
- return array(array('readonlytext'));
- if($wgUser->isBlocked())
- return array(array('blocked'));
-
- // Check token
- if(!$wgUser->matchEditToken($token))
- return array(array('sessionfailure'));
+
+ $errors = self::getPermissionsError($article->getTitle(), $token);
+ if (count($errors)) return $errors;
// Auto-generate a summary, if necessary
if(is_null($reason))
{
+ # Need to pass a throwaway variable because generateReason expects
+ # a reference
+ $hasHistory = false;
$reason = $article->generateReason($hasHistory);
if($reason === false)
return array(array('cannotdelete'));
}
+
+ if (!wfRunHooks('ArticleDelete', array(&$article, &$wgUser, &$reason)))
+ $this->dieUsageMsg(array('hookaborted'));
// Luckily, Article.php provides a reusable delete function that does the hard work for us
- if($article->doDeleteArticle($reason))
+ if($article->doDeleteArticle($reason)) {
+ wfRunHooks('ArticleDeleteComplete', array(&$article, &$wgUser, $reason, $article->getId()));
return array();
+ }
return array(array('cannotdelete', $article->mTitle->getPrefixedText()));
}
+
+ public static function deleteFile($token, &$title, $oldimage, &$reason = NULL, $suppress = false)
+ {
+ $errors = self::getPermissionsError($title, $token);
+ if (count($errors)) return $errors;
+
+ if( $oldimage && !FileDeleteForm::isValidOldSpec($oldimage) )
+ return array(array('invalidoldimage'));
+
+ $file = wfFindFile($title, false, FileRepo::FIND_IGNORE_REDIRECT);
+ $oldfile = false;
+
+ if( $oldimage )
+ $oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $oldimage );
+
+ if( !FileDeleteForm::haveDeletableFile($file, $oldfile, $oldimage) )
+ return array(array('nofile'));
+
+ $status = FileDeleteForm::doDelete( $title, $file, $oldimage, $reason, $suppress );
+
+ if( !$status->isGood() )
+ return array(array('cannotdelete', $title->getPrefixedText()));
+
+ return array();
+ }
public function mustBePosted() { return true; }
-
+
public function getAllowedParams() {
return array (
'title' => null,
'token' => null,
'reason' => null,
+ 'watch' => false,
+ 'unwatch' => false,
+ 'oldimage' => null
);
}
@@ -132,7 +182,10 @@ class ApiDelete extends ApiBase {
return array (
'title' => 'Title of the page you want to delete.',
'token' => 'A delete token previously retrieved through prop=info',
- 'reason' => 'Reason for the deletion. If not set, an automatically generated reason will be used.'
+ 'reason' => 'Reason for the deletion. If not set, an automatically generated reason will be used.',
+ 'watch' => 'Add the page to your watchlist',
+ 'unwatch' => 'Remove the page from your watchlist',
+ 'oldimage' => 'The name of the old image to delete as provided by iiprop=archivename'
);
}
@@ -150,6 +203,6 @@ class ApiDelete extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiDelete.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiDelete.php 35350 2008-05-26 12:15:21Z simetrical $';
}
}
diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php
new file mode 100644
index 00000000..d10432f3
--- /dev/null
+++ b/includes/api/ApiEditPage.php
@@ -0,0 +1,299 @@
+<?php
+
+/*
+ * Created on August 16, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2007 Iker Labarga <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiBase.php");
+}
+
+/**
+ * A query module to list all external URLs found on a given set of pages.
+ *
+ * @ingroup API
+ */
+class ApiEditPage extends ApiBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName);
+ }
+
+ public function execute() {
+ global $wgUser;
+ $this->getMain()->requestWriteMode();
+
+ $params = $this->extractRequestParams();
+ if(is_null($params['title']))
+ $this->dieUsageMsg(array('missingparam', 'title'));
+ if(is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']))
+ $this->dieUsageMsg(array('missingtext'));
+ if(is_null($params['token']))
+ $this->dieUsageMsg(array('missingparam', 'token'));
+ if(!$wgUser->matchEditToken($params['token']))
+ $this->dieUsageMsg(array('sessionfailure'));
+
+ $titleObj = Title::newFromText($params['title']);
+ if(!$titleObj)
+ $this->dieUsageMsg(array('invalidtitle', $params['title']));
+
+ if($params['createonly'] && $titleObj->exists())
+ $this->dieUsageMsg(array('createonly-exists'));
+ if($params['nocreate'] && !$titleObj->exists())
+ $this->dieUsageMsg(array('nocreate-missing'));
+
+ // Now let's check whether we're even allowed to do this
+ $errors = $titleObj->getUserPermissionsErrors('edit', $wgUser);
+ if(!$titleObj->exists())
+ $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $wgUser));
+ if(!empty($errors))
+ $this->dieUsageMsg($errors[0]);
+
+ $articleObj = new Article($titleObj);
+ $toMD5 = $params['text'];
+ if(!is_null($params['appendtext']) || !is_null($params['prependtext']))
+ {
+ $content = $articleObj->getContent();
+ $params['text'] = $params['prependtext'] . $content . $params['appendtext'];
+ $toMD5 = $params['prependtext'] . $params['appendtext'];
+ }
+
+ # See if the MD5 hash checks out
+ if(isset($params['md5']))
+ if(md5($toMD5) !== $params['md5'])
+ $this->dieUsageMsg(array('hashcheckfailed'));
+
+ $ep = new EditPage($articleObj);
+ // EditPage wants to parse its stuff from a WebRequest
+ // That interface kind of sucks, but it's workable
+ $reqArr = array('wpTextbox1' => $params['text'],
+ 'wpEdittoken' => $params['token'],
+ 'wpIgnoreBlankSummary' => ''
+ );
+ if(!is_null($params['summary']))
+ $reqArr['wpSummary'] = $params['summary'];
+ # Watch out for basetimestamp == ''
+ # wfTimestamp() treats it as NOW, almost certainly causing an edit conflict
+ if(!is_null($params['basetimestamp']) && $params['basetimestamp'] != '')
+ $reqArr['wpEdittime'] = wfTimestamp(TS_MW, $params['basetimestamp']);
+ else
+ $reqArr['wpEdittime'] = $articleObj->getTimestamp();
+ # Fake wpStartime
+ $reqArr['wpStarttime'] = $reqArr['wpEdittime'];
+ if($params['minor'] || (!$params['notminor'] && $wgUser->getOption('minordefault')))
+ $reqArr['wpMinoredit'] = '';
+ if($params['recreate'])
+ $reqArr['wpRecreate'] = '';
+ if(!is_null($params['section']))
+ {
+ $section = intval($params['section']);
+ if($section == 0 && $params['section'] != '0' && $params['section'] != 'new')
+ $this->dieUsage("The section parameter must be set to an integer or 'new'", "invalidsection");
+ $reqArr['wpSection'] = $params['section'];
+ }
+
+ if($params['watch'])
+ $watch = true;
+ else if($params['unwatch'])
+ $watch = false;
+ else if($titleObj->userIsWatching())
+ $watch = true;
+ else if($wgUser->getOption('watchdefault'))
+ $watch = true;
+ else if($wgUser->getOption('watchcreations') && !$titleObj->exists())
+ $watch = true;
+ else
+ $watch = false;
+ if($watch)
+ $reqArr['wpWatchthis'] = '';
+
+ $req = new FauxRequest($reqArr, true);
+ $ep->importFormData($req);
+
+ # Run hooks
+ # Handle CAPTCHA parameters
+ global $wgRequest;
+ if(isset($params['captchaid']))
+ $wgRequest->data['wpCaptchaId'] = $params['captchaid'];
+ if(isset($params['captchaword']))
+ $wgRequest->data['wpCaptchaWord'] = $params['captchaword'];
+ $r = array();
+ if(!wfRunHooks('APIEditBeforeSave', array(&$ep, $ep->textbox1, &$r)))
+ {
+ if(!empty($r))
+ {
+ $r['result'] = "Failure";
+ $this->getResult()->addValue(null, $this->getModuleName(), $r);
+ return;
+ }
+ else
+ $this->dieUsageMsg(array('hookaborted'));
+ }
+
+ # Do the actual save
+ $oldRevId = $articleObj->getRevIdFetched();
+ $result = null;
+ # *Something* is setting $wgTitle to a title corresponding to "Msg",
+ # but that breaks API mode detection through is_null($wgTitle)
+ global $wgTitle;
+ $wgTitle = null;
+ # Fake $wgRequest for some hooks inside EditPage
+ # FIXME: This interface SUCKS
+ $oldRequest = $wgRequest;
+ $wgRequest = $req;
+
+ $retval = $ep->internalAttemptSave($result, $wgUser->isAllowed('bot') && $params['bot']);
+ $wgRequest = $oldRequest;
+ switch($retval)
+ {
+ case EditPage::AS_HOOK_ERROR:
+ case EditPage::AS_HOOK_ERROR_EXPECTED:
+ $this->dieUsageMsg(array('hookaborted'));
+ case EditPage::AS_IMAGE_REDIRECT_ANON:
+ $this->dieUsageMsg(array('noimageredirect-anon'));
+ case EditPage::AS_IMAGE_REDIRECT_LOGGED:
+ $this->dieUsageMsg(array('noimageredirect-logged'));
+ case EditPage::AS_SPAM_ERROR:
+ $this->dieUsageMsg(array('spamdetected', $result['spam']));
+ case EditPage::AS_FILTERING:
+ $this->dieUsageMsg(array('filtered'));
+ case EditPage::AS_BLOCKED_PAGE_FOR_USER:
+ $this->dieUsageMsg(array('blockedtext'));
+ case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED:
+ case EditPage::AS_CONTENT_TOO_BIG:
+ global $wgMaxArticleSize;
+ $this->dieUsageMsg(array('contenttoobig', $wgMaxArticleSize));
+ case EditPage::AS_READ_ONLY_PAGE_ANON:
+ $this->dieUsageMsg(array('noedit-anon'));
+ case EditPage::AS_READ_ONLY_PAGE_LOGGED:
+ $this->dieUsageMsg(array('noedit'));
+ case EditPage::AS_READ_ONLY_PAGE:
+ $this->dieUsageMsg(array('readonlytext'));
+ case EditPage::AS_RATE_LIMITED:
+ $this->dieUsageMsg(array('actionthrottledtext'));
+ case EditPage::AS_ARTICLE_WAS_DELETED:
+ $this->dieUsageMsg(array('wasdeleted'));
+ case EditPage::AS_NO_CREATE_PERMISSION:
+ $this->dieUsageMsg(array('nocreate-loggedin'));
+ case EditPage::AS_BLANK_ARTICLE:
+ $this->dieUsageMsg(array('blankpage'));
+ case EditPage::AS_CONFLICT_DETECTED:
+ $this->dieUsageMsg(array('editconflict'));
+ #case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary
+ #case EditPage::AS_TEXTBOX_EMPTY: Can't happen since we don't do sections
+ case EditPage::AS_END:
+ # This usually means some kind of race condition
+ # or DB weirdness occurred. Throw an unknown error here.
+ $this->dieUsageMsg(array('unknownerror', 'AS_END'));
+ case EditPage::AS_SUCCESS_NEW_ARTICLE:
+ $r['new'] = '';
+ case EditPage::AS_SUCCESS_UPDATE:
+ $r['result'] = "Success";
+ $r['pageid'] = $titleObj->getArticleID();
+ $r['title'] = $titleObj->getPrefixedText();
+ $newRevId = $titleObj->getLatestRevId();
+ if($newRevId == $oldRevId)
+ $r['nochange'] = '';
+ else
+ {
+ $r['oldrevid'] = $oldRevId;
+ $r['newrevid'] = $newRevId;
+ }
+ break;
+ default:
+ $this->dieUsageMsg(array('unknownerror', $retval));
+ }
+ $this->getResult()->addValue(null, $this->getModuleName(), $r);
+ }
+
+ public function mustBePosted() {
+ return true;
+ }
+
+ protected function getDescription() {
+ return 'Create and edit pages.';
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'title' => null,
+ 'section' => null,
+ 'text' => null,
+ 'token' => null,
+ 'summary' => null,
+ 'minor' => false,
+ 'notminor' => false,
+ 'bot' => false,
+ 'basetimestamp' => null,
+ 'recreate' => false,
+ 'createonly' => false,
+ 'nocreate' => false,
+ 'captchaword' => null,
+ 'captchaid' => null,
+ 'watch' => false,
+ 'unwatch' => false,
+ 'md5' => null,
+ 'prependtext' => null,
+ 'appendtext' => null,
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'title' => 'Page title',
+ 'section' => 'Section number. 0 for the top section, \'new\' for a new section',
+ 'text' => 'Page content',
+ 'token' => 'Edit token. You can get one of these through prop=info',
+ 'summary' => 'Edit summary. Also section title when section=new',
+ 'minor' => 'Minor edit',
+ 'notminor' => 'Non-minor edit',
+ 'bot' => 'Mark this edit as bot',
+ 'basetimestamp' => array('Timestamp of the base revision (gotten through prop=revisions&rvprop=timestamp).',
+ 'Used to detect edit conflicts; leave unset to ignore conflicts.'
+ ),
+ 'recreate' => 'Override any errors about the article having been deleted in the meantime',
+ 'createonly' => 'Don\'t edit the page if it exists already',
+ 'nocreate' => 'Throw an error if the page doesn\'t exist',
+ 'watch' => 'Add the page to your watchlist',
+ 'unwatch' => 'Remove the page from your watchlist',
+ 'captchaid' => 'CAPTCHA ID from previous request',
+ 'captchaword' => 'Answer to the CAPTCHA',
+ 'md5' => array( 'The MD5 hash of the text parameter, or the prependtext and appendtext parameters concatenated.',
+ 'If set, the edit won\'t be done unless the hash is correct'),
+ 'prependtext' => array( 'Add this text to the beginning of the page. Overrides text.',
+ 'Don\'t use together with section: that won\'t do what you expect.'),
+ 'appendtext' => 'Add this text to the end of the page. Overrides text',
+ );
+ }
+
+ protected function getExamples() {
+ return array (
+ "Edit a page (anonymous user):",
+ " api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\"
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiEditPage.php 36309 2008-06-15 20:37:28Z catrope $';
+ }
+}
diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php
new file mode 100644
index 00000000..7e083536
--- /dev/null
+++ b/includes/api/ApiEmailUser.php
@@ -0,0 +1,114 @@
+<?php
+
+/*
+ * Created on June 1, 2008
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2008 Bryan Tong Minh <Bryan.TongMinh@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");
+}
+
+
+/**
+ * @ingroup API
+ */
+class ApiEmailUser extends ApiBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ public function execute() {
+ global $wgUser;
+ $this->getMain()->requestWriteMode();
+ $params = $this->extractRequestParams();
+
+ // Check required parameters
+ if ( !isset( $params['target'] ) )
+ $this->dieUsageMsg( array( 'missingparam', 'target' ) );
+ if ( !isset( $params['text'] ) )
+ $this->dieUsageMsg( array( 'missingparam', 'text' ) );
+ if ( !isset( $params['token'] ) )
+ $this->dieUsageMsg( array( 'missingparam', 'token' ) );
+
+ // Validate target
+ $targetUser = EmailUserForm::validateEmailTarget( $params['target'] );
+ if ( !( $targetUser instanceof User ) )
+ $this->dieUsageMsg( array( $targetUser[0] ) );
+
+ // Check permissions
+ $error = EmailUserForm::getPermissionsError( $wgUser, $params['token'] );
+ if ( $error )
+ $this->dieUsageMsg( array( $error[0] ) );
+
+
+ $form = new EmailUserForm( $targetUser, $params['text'], $params['subject'], $params['ccme'] );
+ $retval = $form->doSubmit();
+ if ( is_null( $retval ) )
+ $result = array( 'result' => 'Success' );
+ else
+ $result = array( 'result' => 'Failure',
+ 'message' => $retval->getMessage() );
+
+ $this->getResult()->addValue( null, $this->getModuleName(), $result );
+ }
+
+ public function mustBePosted() { return true; }
+
+ public function getAllowedParams() {
+ return array (
+ 'target' => null,
+ 'subject' => null,
+ 'text' => null,
+ 'token' => null,
+ 'ccme' => false,
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'target' => 'User to send email to',
+ 'subject' => 'Subject header',
+ 'text' => 'Mail body',
+ // FIXME: How to properly get a token?
+ 'token' => 'A token previously acquired via prop=info',
+ 'ccme' => 'Send a copy of this mail to me',
+ );
+ }
+
+ public function getDescription() {
+ return array(
+ 'Emails a user.'
+ );
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=emailuser&target=WikiSysop&text=Content'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: $';
+ }
+}
+ \ No newline at end of file
diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php
index 278896fa..397aece3 100644
--- a/includes/api/ApiExpandTemplates.php
+++ b/includes/api/ApiExpandTemplates.php
@@ -33,7 +33,7 @@ if (!defined('MEDIAWIKI')) {
* any templates in a provided string, and returns the result of this expansion
* to the caller.
*
- * @addtogroup API
+ * @ingroup API
*/
class ApiExpandTemplates extends ApiBase {
@@ -43,22 +43,35 @@ class ApiExpandTemplates extends ApiBase {
public function execute() {
// Get parameters
- $params = $this->extractRequestParams();
- $text = $params['text'];
- $title = $params['title'];
+ extract( $this->extractRequestParams() );
$retval = '';
//Create title for parser
- $title_obj = Title :: newFromText($params['title']);
+ $title_obj = Title :: newFromText( $title );
if(!$title_obj)
- $title_obj = Title :: newFromText("API"); // Default title is "API". For example, ExpandTemplates uses "ExpendTemplates" for it
+ $title_obj = Title :: newFromText( "API" ); // Default title is "API". For example, ExpandTemplates uses "ExpendTemplates" for it
+
+ $result = $this->getResult();
// Parse text
global $wgParser;
- $retval = $wgParser->preprocess( $text, $title_obj, new ParserOptions() );
+ $options = new ParserOptions();
+ if ( $generatexml )
+ {
+ $wgParser->startExternalParse( $title_obj, $options, OT_PREPROCESS );
+ $dom = $wgParser->preprocessToDom( $text );
+ if ( is_callable( array( $dom, 'saveXML' ) ) ) {
+ $xml = $dom->saveXML();
+ } else {
+ $xml = $dom->__toString();
+ }
+ $xml_result = array();
+ $result->setContent( $xml_result, $xml );
+ $result->addValue( null, 'parsetree', $xml_result);
+ }
+ $retval = $wgParser->preprocess( $text, $title_obj, $options );
// Return result
- $result = $this->getResult();
$retval_array = array();
$result->setContent( $retval_array, $retval );
$result->addValue( null, $this->getModuleName(), $retval_array );
@@ -66,10 +79,11 @@ class ApiExpandTemplates extends ApiBase {
public function getAllowedParams() {
return array (
- 'title' => array(
+ 'title' => array(
ApiBase :: PARAM_DFLT => 'API',
),
- 'text' => null
+ 'text' => null,
+ 'generatexml' => false,
);
}
@@ -77,6 +91,7 @@ class ApiExpandTemplates extends ApiBase {
return array (
'text' => 'Wikitext to convert',
'title' => 'Title of page',
+ 'generatexml' => 'Generate XML parse tree',
);
}
@@ -91,7 +106,6 @@ class ApiExpandTemplates extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiExpandTemplates.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiExpandTemplates.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php
index 9b17b9d3..109b6552 100644
--- a/includes/api/ApiFeedWatchlist.php
+++ b/includes/api/ApiFeedWatchlist.php
@@ -32,8 +32,8 @@ if (!defined('MEDIAWIKI')) {
* This action allows users to get their watchlist items in RSS/Atom formats.
* When executed, it performs a nested call to the API to get the needed data,
* and formats it in a proper format.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiFeedWatchlist extends ApiBase {
@@ -53,15 +53,15 @@ class ApiFeedWatchlist extends ApiBase {
* Wrap the result as an RSS/Atom feed.
*/
public function execute() {
-
- global $wgFeedClasses, $wgSitename, $wgContLanguageCode;
+
+ global $wgFeedClasses, $wgFeedLimit, $wgSitename, $wgContLanguageCode;
try {
$params = $this->extractRequestParams();
-
+
// limit to the number of hours going from now back
$endTime = wfTimestamp(TS_MW, time() - intval($params['hours'] * 60 * 60));
-
+
$dbr = wfGetDB( DB_SLAVE );
// Prepare parameters for nested request
$fauxReqArr = array (
@@ -72,7 +72,7 @@ class ApiFeedWatchlist extends ApiBase {
'wlprop' => 'title|user|comment|timestamp',
'wldir' => 'older', // reverse order - from newest to oldest
'wlend' => $dbr->timestamp($endTime), // stop at this time
- 'wllimit' => 50
+ 'wllimit' => (50 > $wgFeedLimit) ? $wgFeedLimit : 50
);
// Check for 'allrev' parameter, and if found, show all revisions to each page on wl.
@@ -80,35 +80,35 @@ class ApiFeedWatchlist extends ApiBase {
// Create the request
$fauxReq = new FauxRequest ( $fauxReqArr );
-
+
// Execute
$module = new ApiMain($fauxReq);
$module->execute();
// Get data array
$data = $module->getResultData();
-
+
$feedItems = array ();
foreach ($data['query']['watchlist'] as $info) {
$feedItems[] = $this->createFeedItem($info);
}
-
+
$feedTitle = $wgSitename . ' - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']';
$feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl();
-
+
$feed = new $wgFeedClasses[$params['feedformat']] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl);
-
+
ApiFormatFeedWrapper :: setResult($this->getResult(), $feed, $feedItems);
} catch (Exception $e) {
// Error results should not be cached
$this->getMain()->setCacheMaxAge(0);
-
+
$feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']';
$feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl();
-
- $feedFormat = isset($params['feedformat']) ? $params['feedformat'] : 'rss';
+
+ $feedFormat = isset($params['feedformat']) ? $params['feedformat'] : 'rss';
$feed = new $wgFeedClasses[$feedFormat] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl);
@@ -175,7 +175,6 @@ class ApiFeedWatchlist extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFeedWatchlist.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiFeedWatchlist.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index 768a18ac..db58fe52 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -30,12 +30,12 @@ if (!defined('MEDIAWIKI')) {
/**
* This is the abstract base class for API formatters.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
abstract class ApiFormatBase extends ApiBase {
- private $mIsHtml, $mFormat, $mUnescapeAmps, $mHelp;
+ private $mIsHtml, $mFormat, $mUnescapeAmps, $mHelp, $mCleared;
/**
* Create a new instance of the formatter.
@@ -50,6 +50,7 @@ abstract class ApiFormatBase extends ApiBase {
else
$this->mFormat = $format;
$this->mFormat = strtoupper($this->mFormat);
+ $this->mCleared = false;
}
/**
@@ -62,7 +63,7 @@ abstract class ApiFormatBase extends ApiBase {
/**
* If formatter outputs data results as is, the results must first be sanitized.
* An XML formatter on the other hand uses special tags, such as "_element" for special handling,
- * and thus needs to override this function to return true.
+ * and thus needs to override this function to return true.
*/
public function getNeedsRawData() {
return false;
@@ -82,8 +83,8 @@ abstract class ApiFormatBase extends ApiBase {
/**
* Returns true when an HTML filtering printer should be used.
- * The default implementation assumes that formats ending with 'fm'
- * should be formatted in HTML.
+ * The default implementation assumes that formats ending with 'fm'
+ * should be formatted in HTML.
*/
public function getIsHtml() {
return $this->mIsHtml;
@@ -111,7 +112,7 @@ abstract class ApiFormatBase extends ApiBase {
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
-<?php if ($this->mUnescapeAmps) {
+<?php if ($this->mUnescapeAmps) {
?> <title>MediaWiki API</title>
<?php } else {
?> <title>MediaWiki API Result</title>
@@ -127,7 +128,7 @@ abstract class ApiFormatBase extends ApiBase {
<small>
You are looking at the HTML representation of the <?php echo( $this->mFormat ); ?> format.<br/>
HTML is good for debugging, but probably is not suitable for your application.<br/>
-See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
+See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
<a href='<?php echo( $script ); ?>'>API help</a> for more information.
</small>
<?php
@@ -166,7 +167,17 @@ See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
if ($this->getIsHtml())
echo $this->formatHTML($text);
else
+ {
+ // For non-HTML output, clear all errors that might have been
+ // displayed if display_errors=On
+ // Do this only once, of course
+ if(!$this->mCleared)
+ {
+ ob_clean();
+ $this->mCleared = true;
+ }
echo $text;
+ }
}
/**
@@ -175,7 +186,7 @@ See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
public function setHelp( $help = true ) {
$this->mHelp = true;
}
-
+
/**
* Prety-print various elements in HTML format, such as xml tags and URLs.
* This method also replaces any '<' with &lt;
@@ -188,16 +199,17 @@ See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
$text = preg_replace('/\&lt;(!--.*?--|.*?)\&gt;/', '<span style="color:blue;">&lt;\1&gt;</span>', $text);
// identify URLs
$protos = "http|https|ftp|gopher";
- $text = ereg_replace("($protos)://[^ \\'\"()<\n]+", '<a href="\\0">\\0</a>', $text);
+ # This regex hacks around bug 13218 (&quot; included in the URL)
+ $text = preg_replace("#(($protos)://.*?)(&quot;)?([ \\'\"()<\n])#", '<a href="\\1">\\1</a>\\3\\4', $text);
// identify requests to api.php
- $text = ereg_replace("api\\.php\\?[^ \\()<\n\t]+", '<a href="\\0">\\0</a>', $text);
+ $text = preg_replace("#api\\.php\\?[^ \\()<\n\t]+#", '<a href="\\0">\\0</a>', $text);
if( $this->mHelp ) {
// make strings inside * bold
$text = ereg_replace("\\*[^<>\n]+\\*", '<b>\\0</b>', $text);
// make strings inside $ italic
$text = ereg_replace("\\$[^<>\n]+\\$", '<b><i>\\0</i></b>', $text);
}
-
+
/* Temporary fix for bad links in help messages. As a special case,
* XML-escaped metachars are de-escaped one level in the help message
* for legibility. Should be removed once we have completed a fully-html
@@ -220,13 +232,13 @@ See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
}
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 36153 2008-06-10 15:20:22Z tstarling $';
}
}
/**
- * This printer is used to wrap an instance of the Feed class
- * @addtogroup API
+ * This printer is used to wrap an instance of the Feed class
+ * @ingroup API
*/
class ApiFormatFeedWrapper extends ApiFormatBase {
@@ -275,13 +287,12 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
$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', '');
+ // Error has occured, print something useful
+ ApiBase::dieDebug( __METHOD__, 'Invalid feed class/item' );
}
}
-
+
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 36153 2008-06-10 15:20:22Z tstarling $';
}
}
diff --git a/includes/api/ApiFormatDbg.php b/includes/api/ApiFormatDbg.php
index f0fc5e91..254c140b 100644
--- a/includes/api/ApiFormatDbg.php
+++ b/includes/api/ApiFormatDbg.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiFormatDbg extends ApiFormatBase {
@@ -53,7 +53,6 @@ class ApiFormatDbg extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatPhp.php 23531 2007-06-29 01:19:14Z simetrical $';
+ return __CLASS__ . ': $Id: ApiFormatDbg.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index 852a64b6..42156849 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiFormatJson extends ApiFormatBase {
@@ -54,7 +54,7 @@ class ApiFormatJson extends ApiFormatBase {
$params = $this->extractRequestParams();
$callback = $params['callback'];
if(!is_null($callback)) {
- $prefix = ereg_replace("[^_A-Za-z0-9]", "", $callback ) . "(";
+ $prefix = preg_replace("/[^][.\\'\\\"_A-Za-z0-9]/", "", $callback ) . "(";
$suffix = ")";
}
@@ -86,7 +86,6 @@ class ApiFormatJson extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatJson.php 31484 2008-03-03 05:46:20Z brion $';
+ return __CLASS__ . ': $Id: ApiFormatJson.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFormatJson_json.php b/includes/api/ApiFormatJson_json.php
index a8c649c3..87d7086e 100644
--- a/includes/api/ApiFormatJson_json.php
+++ b/includes/api/ApiFormatJson_json.php
@@ -45,12 +45,12 @@
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
-* @addtogroup API
+* @ingroup API
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
-* @version CVS: $Id: JSON.php,v 1.30 2006/03/08 16:10:20 migurski Exp $
+* @version CVS: $Id: ApiFormatJson_json.php 35098 2008-05-20 17:13:28Z ialex $
* @license http://www.opensource.org/licenses/bsd-license.php
* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
@@ -111,7 +111,7 @@ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
* $value = $json->decode($input);
* </code>
*
- * @addtogroup API
+ * @ingroup API
*/
class Services_JSON
{
@@ -257,7 +257,7 @@ class Services_JSON
*/
function encode2($var)
{
- if ($this->pretty) {
+ if ($this->pretty) {
$close = "\n" . str_repeat("\t", $this->indent);
$open = $close . "\t";
$mid = ',' . $open;
@@ -426,7 +426,7 @@ class Services_JSON
$this->indent++;
$elements = array_map(array($this, 'encode2'), $var);
$this->indent--;
-
+
foreach($elements as $element) {
if(Services_JSON::isError($element)) {
return $element;
@@ -703,7 +703,7 @@ class Services_JSON
// element in an associative array,
// for now
$parts = array();
-
+
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// "name":value pair
$key = $this->decode($parts[1]);
@@ -815,7 +815,7 @@ class Services_JSON
if (class_exists('PEAR_Error')) {
/**
- * @addtogroup API
+ * @ingroup API
*/
class Services_JSON_Error extends PEAR_Error
{
@@ -830,7 +830,7 @@ if (class_exists('PEAR_Error')) {
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
- * @addtogroup API
+ * @ingroup API
*/
class Services_JSON_Error
{
@@ -840,7 +840,4 @@ if (class_exists('PEAR_Error')) {
}
}
-
}
-
-
diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php
index f830d8e1..163d3028 100644
--- a/includes/api/ApiFormatPhp.php
+++ b/includes/api/ApiFormatPhp.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiFormatPhp extends ApiFormatBase {
@@ -50,7 +50,6 @@ class ApiFormatPhp extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatPhp.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiFormatPhp.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFormatTxt.php b/includes/api/ApiFormatTxt.php
index c4c45f68..5f608d5c 100644
--- a/includes/api/ApiFormatTxt.php
+++ b/includes/api/ApiFormatTxt.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiFormatTxt extends ApiFormatBase {
@@ -53,7 +53,6 @@ class ApiFormatTxt extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatPhp.php 23531 2007-06-29 01:19:14Z simetrical $';
+ return __CLASS__ . ': $Id: ApiFormatTxt.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php
index 22a0e482..0909539e 100644
--- a/includes/api/ApiFormatWddx.php
+++ b/includes/api/ApiFormatWddx.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiFormatWddx extends ApiFormatBase {
@@ -85,7 +85,6 @@ class ApiFormatWddx extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatWddx.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiFormatWddx.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php
index d39e8049..d35eb3e9 100644
--- a/includes/api/ApiFormatXml.php
+++ b/includes/api/ApiFormatXml.php
@@ -29,11 +29,12 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiFormatXml extends ApiFormatBase {
private $mRootElemName = 'api';
+ private $mDoubleQuote = false;
public function __construct($main, $format) {
parent :: __construct($main, $format);
@@ -46,12 +47,15 @@ class ApiFormatXml extends ApiFormatBase {
public function getNeedsRawData() {
return true;
}
-
+
public function setRootElement($rootElemName) {
$this->mRootElemName = $rootElemName;
}
public function execute() {
+ $params = $this->extractRequestParams();
+ $this->mDoubleQuote = $params['xmldoublequote'];
+
$this->printText('<?xml version="1.0" encoding="utf-8"?>');
$this->recXmlPrint($this->mRootElemName, $this->getResultData(), $this->getIsHtml() ? -2 : null);
}
@@ -79,9 +83,10 @@ class ApiFormatXml extends ApiFormatBase {
switch (gettype($elemValue)) {
case 'array' :
-
if (isset ($elemValue['*'])) {
$subElemContent = $elemValue['*'];
+ if ($this->mDoubleQuote)
+ $subElemContent = $this->doubleQuote($subElemContent);
unset ($elemValue['*']);
} else {
$subElemContent = null;
@@ -97,6 +102,9 @@ class ApiFormatXml extends ApiFormatBase {
$indElements = array ();
$subElements = array ();
foreach ($elemValue as $subElemId => & $subElemValue) {
+ if (is_string($subElemValue) && $this->mDoubleQuote)
+ $subElemValue = $this->doubleQuote($subElemValue);
+
if (gettype($subElemId) === 'integer') {
$indElements[] = $subElemValue;
unset ($elemValue[$subElemId]);
@@ -136,12 +144,28 @@ class ApiFormatXml extends ApiFormatBase {
break;
}
}
+ private function doubleQuote( $text ) {
+ return Sanitizer::encodeAttribute( $text );
+ }
+
+ public function getAllowedParams() {
+ return array (
+ 'xmldoublequote' => false
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'xmldoublequote' => 'If specified, double quotes all attributes and content.',
+ );
+ }
+
+
public function getDescription() {
return 'Output data in XML format' . parent :: getDescription();
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatXml.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiFormatXml.php 37075 2008-07-04 22:44:57Z brion $';
}
}
-
diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php
index 5e15aee6..cc255c63 100644
--- a/includes/api/ApiFormatYaml.php
+++ b/includes/api/ApiFormatYaml.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiFormatYaml extends ApiFormatBase {
@@ -50,7 +50,6 @@ class ApiFormatYaml extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatYaml.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiFormatYaml.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php
index b2973b8c..c0d4093e 100644
--- a/includes/api/ApiFormatYaml_spyc.php
+++ b/includes/api/ApiFormatYaml_spyc.php
@@ -1,5 +1,5 @@
<?php
- /**
+ /**
* Spyc -- A Simple PHP YAML Class
* @version 0.2.3 -- 2006-02-04
* @author Chris Wanstrath <chris@ozmm.org>
@@ -8,29 +8,29 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
- /**
+ /**
* A node, used by Spyc for parsing YAML.
- * @addtogroup API
+ * @ingroup API
*/
class YAMLNode {
/**#@+
* @access public
* @var string
- */
+ */
var $parent;
var $id;
/**#@-*/
- /**
+ /**
* @access public
* @var mixed
*/
var $data;
- /**
+ /**
* @access public
* @var int
*/
var $indent;
- /**
+ /**
* @access public
* @var bool
*/
@@ -58,17 +58,17 @@
* $parser = new Spyc;
* $array = $parser->load($file);
* </code>
- * @addtogroup API
+ * @ingroup API
*/
class Spyc {
-
+
/**
* Load YAML into a PHP array statically
*
- * The load method, when supplied with a YAML stream (string or file),
- * will do its best to convert YAML in a file into a PHP array. Pretty
+ * The load method, when supplied with a YAML stream (string or file),
+ * will do its best to convert YAML in a file into a PHP array. Pretty
* simple.
- * Usage:
+ * Usage:
* <code>
* $array = Spyc::YAMLLoad('lucky.yml');
* print_r($array);
@@ -81,7 +81,7 @@
$spyc = new Spyc;
return $spyc->load($input);
}
-
+
/**
* Dump YAML from PHP array statically
*
@@ -90,7 +90,7 @@
* save the returned string as nothing.yml and pass it around.
*
* Oh, and you can decide how big the indent is and what the wordwrap
- * for folding is. Pretty cool -- just pass in 'false' for either if
+ * for folding is. Pretty cool -- just pass in 'false' for either if
* you want to use the default.
*
* Indent's default is 2 spaces, wordwrap's default is 40 characters. And
@@ -100,20 +100,20 @@
* @static
* @return string
* @param array $array PHP array
- * @param int $indent Pass in false to use the default, which is 2
+ * @param int $indent Pass in false to use the default, which is 2
* @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
*/
public static function YAMLDump($array,$indent = false,$wordwrap = false) {
$spyc = new Spyc;
return $spyc->dump($array,$indent,$wordwrap);
}
-
+
/**
* Load YAML into a PHP array from an instantiated object
*
- * The load method, when supplied with a YAML stream (string or file path),
+ * The load method, when supplied with a YAML stream (string or file path),
* will do its best to convert the YAML into a PHP array. Pretty simple.
- * Usage:
+ * Usage:
* <code>
* $parser = new Spyc;
* $array = $parser->load('lucky.yml');
@@ -126,7 +126,7 @@
function load($input) {
// See what type of input we're talking about
// If it's not a file, assume it's a string
- if (!empty($input) && (strpos($input, "\n") === false)
+ if (!empty($input) && (strpos($input, "\n") === false)
&& file_exists($input)) {
$yaml = file($input);
} else {
@@ -139,7 +139,7 @@
$this->_lastNode = $base->id;
$this->_inBlock = false;
$this->_isInline = false;
-
+
foreach ($yaml as $linenum => $line) {
$ifchk = trim($line);
@@ -149,7 +149,7 @@
' with a tab. YAML only recognizes spaces. Please reformat.';
die($err);
}
-
+
if ($this->_inBlock === false && empty($ifchk)) {
continue;
} elseif ($this->_inBlock == true && empty($ifchk)) {
@@ -159,7 +159,7 @@
// Create a new node and get its indent
$node = new YAMLNode;
$node->indent = $this->_getIndent($line);
-
+
// Check where the node lies in the hierarchy
if ($this->_lastIndent == $node->indent) {
// If we're in a block, add the text to the parent's data
@@ -172,16 +172,16 @@
$node->parent = $this->_allNodes[$this->_lastNode]->parent;
}
}
- } elseif ($this->_lastIndent < $node->indent) {
+ } elseif ($this->_lastIndent < $node->indent) {
if ($this->_inBlock === true) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= trim($line).$this->_blockEnd;
} elseif ($this->_inBlock === false) {
// The current node's parent is the previous node
$node->parent = $this->_lastNode;
-
- // If the value of the last node's data was > or | we need to
- // start blocking i.e. taking in all lines as a text value until
+
+ // If the value of the last node's data was > or | we need to
+ // start blocking i.e. taking in all lines as a text value until
// we drop our indent.
$parent =& $this->_allNodes[$node->parent];
$this->_allNodes[$node->parent]->children = true;
@@ -190,7 +190,7 @@
if ($chk === '>') {
$this->_inBlock = true;
$this->_blockEnd = ' ';
- $parent->data[key($parent->data)] =
+ $parent->data[key($parent->data)] =
str_replace('>','',$parent->data[key($parent->data)]);
$parent->data[key($parent->data)] .= trim($line).' ';
$this->_allNodes[$node->parent]->children = false;
@@ -198,7 +198,7 @@
} elseif ($chk === '|') {
$this->_inBlock = true;
$this->_blockEnd = "\n";
- $parent->data[key($parent->data)] =
+ $parent->data[key($parent->data)] =
str_replace('|','',$parent->data[key($parent->data)]);
$parent->data[key($parent->data)] .= trim($line)."\n";
$this->_allNodes[$node->parent]->children = false;
@@ -212,11 +212,11 @@
$this->_inBlock = false;
if ($this->_blockEnd = "\n") {
$last =& $this->_allNodes[$this->_lastNode];
- $last->data[key($last->data)] =
+ $last->data[key($last->data)] =
trim($last->data[key($last->data)]);
}
}
-
+
// We don't know the parent of the node so we have to find it
// foreach ($this->_allNodes as $n) {
foreach ($this->_indentSort[$node->indent] as $n) {
@@ -225,7 +225,7 @@
}
}
}
-
+
if ($this->_inBlock === false) {
// Set these properties with information from our current node
$this->_lastIndent = $node->indent;
@@ -239,13 +239,13 @@
$this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id];
// Add a reference to the node in a References array if this node
// has a YAML reference in it.
- if (
+ if (
( (is_array($node->data)) &&
isset($node->data[key($node->data)]) &&
(!is_array($node->data[key($node->data)])) )
&&
- ( (preg_match('/^&([^ ]+)/',$node->data[key($node->data)]))
- ||
+ ( (preg_match('/^&([^ ]+)/',$node->data[key($node->data)]))
+ ||
(preg_match('/^\*([^ ]+)/',$node->data[key($node->data)])) )
) {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
@@ -256,9 +256,9 @@
) {
// Incomplete reference making code. Ugly, needs cleaned up.
foreach ($node->data[key($node->data)] as $d) {
- if ( !is_array($d) &&
- ( (preg_match('/^&([^ ]+)/',$d))
- ||
+ if ( !is_array($d) &&
+ ( (preg_match('/^&([^ ]+)/',$d))
+ ||
(preg_match('/^\*([^ ]+)/',$d)) )
) {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
@@ -269,15 +269,15 @@
}
}
unset($node);
-
+
// Here we travel through node-space and pick out references (& and *)
$this->_linkReferences();
-
+
// Build the PHP array out of node-space
$trunk = $this->_buildArray();
return $trunk;
}
-
+
/**
* Dump PHP array to YAML
*
@@ -286,7 +286,7 @@
* save the returned string as tasteful.yml and pass it around.
*
* Oh, and you can decide how big the indent is and what the wordwrap
- * for folding is. Pretty cool -- just pass in 'false' for either if
+ * for folding is. Pretty cool -- just pass in 'false' for either if
* you want to use the default.
*
* Indent's default is 2 spaces, wordwrap's default is 40 characters. And
@@ -295,7 +295,7 @@
* @access public
* @return string
* @param array $array PHP array
- * @param int $indent Pass in false to use the default, which is 2
+ * @param int $indent Pass in false to use the default, which is 2
* @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
*/
function dump($array,$indent = false,$wordwrap = false) {
@@ -308,29 +308,29 @@
} else {
$this->_dumpIndent = $indent;
}
-
+
if ($wordwrap === false or !is_numeric($wordwrap)) {
$this->_dumpWordWrap = 40;
} else {
$this->_dumpWordWrap = $wordwrap;
}
-
+
// New YAML document
$string = "---\n";
-
+
// Start at the base of the array and move through it.
foreach ($array as $key => $value) {
$string .= $this->_yamlize($key,$value,0);
}
return $string;
}
-
+
/**** Private Properties ****/
-
+
/**#@+
* @access private
* @var mixed
- */
+ */
var $_haveRefs;
var $_allNodes;
var $_lastIndent;
@@ -342,7 +342,7 @@
/**#@-*/
/**** Private Methods ****/
-
+
/**
* Attempts to convert a key / value array item to YAML
* @access private
@@ -350,7 +350,7 @@
* @param $key The name of the key
* @param $value The value of the item
* @param $indent The indent of the current node
- */
+ */
function _yamlize($key,$value,$indent) {
if (is_array($value)) {
// It has children. What to do?
@@ -366,14 +366,14 @@
}
return $string;
}
-
+
/**
* Attempts to convert an array to YAML
* @access private
* @return string
* @param $array The array you want to convert
* @param $indent The indent of the current level
- */
+ */
function _yamlizeArray($array,$indent) {
if (is_array($array)) {
$string = '';
@@ -395,9 +395,15 @@
function _needLiteral($value) {
# Check whether the string contains # or : or begins with any of:
# [ - ? , [ ] { } ! * & | > ' " % @ ` ]
- return (bool)(preg_match("/[#:]/", $value) || preg_match("/^[-?,[\]{}!*&|>'\"%@`]/", $value));
+ # or is a number or contains newlines
+ return (bool)(gettype($value) == "string" &&
+ (is_numeric($value) ||
+ strpos($value, "\n") ||
+ preg_match("/[#:]/", $value) ||
+ preg_match("/^[-?,[\]{}!*&|>'\"%@`]/", $value)));
+
}
-
+
/**
* Returns YAML from a key and a value
* @access private
@@ -405,23 +411,29 @@
* @param $key The name of the key
* @param $value The value of the item
* @param $indent The indent of the current node
- */
+ */
function _dumpNode($key,$value,$indent) {
// do some folding here, for blocks
- if (strpos($value,"\n") || $this->_needLiteral($value)) {
+ if ($this->_needLiteral($value)) {
$value = $this->_doLiteralBlock($value,$indent);
- } else {
+ } else {
$value = $this->_doFolding($value,$indent);
}
-
+
$spaces = str_repeat(' ',$indent);
if (is_int($key)) {
// It's a sequence
- $string = $spaces.'- '.$value."\n";
+ if ($value)
+ $string = $spaces.'- '.$value."\n";
+ else
+ $string = $spaces . "-\n";
} else {
- // It's mapped
- $string = $spaces.$key.': '.$value."\n";
+ // It's mapped
+ if ($value)
+ $string = $spaces.$key.': '.$value."\n";
+ else
+ $string = $spaces . $key . ":\n";
}
return $string;
}
@@ -430,9 +442,9 @@
* Creates a literal block for dumping
* @access private
* @return string
- * @param $value
+ * @param $value
* @param $indent int The value of the indent
- */
+ */
function _doLiteralBlock($value,$indent) {
$exploded = explode("\n",$value);
$newValue = '|';
@@ -443,7 +455,7 @@
}
return $newValue;
}
-
+
/**
* Folds a string of text, if necessary
* @access private
@@ -455,7 +467,7 @@
if ($this->_dumpWordWrap === 0) {
return $value;
}
-
+
if (strlen($value) > $this->_dumpWordWrap) {
$indent += $this->_dumpIndent;
$indent = str_repeat(' ',$indent);
@@ -464,9 +476,9 @@
}
return $value;
}
-
+
/* Methods used in loading */
-
+
/**
* Finds and returns the indentation of a YAML line
* @access private
@@ -491,7 +503,7 @@
* @param string $line A line from the YAML file
*/
function _parseLine($line) {
- $line = trim($line);
+ $line = trim($line);
$array = array();
@@ -534,7 +546,7 @@
}
return $array;
}
-
+
/**
* Finds the type of the passed value, returns the value as the new type.
* @access private
@@ -543,7 +555,7 @@
*/
function _toType($value) {
$matches = array();
- if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) {
+ if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) {
$value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches));
$value = preg_replace('/\\\\"/','"',$value);
} elseif (preg_match('/^\\[(.+)\\]$/',$value,$matches)) {
@@ -551,7 +563,7 @@
// Take out strings sequences and mappings
$explode = $this->_inlineEscape($matches[1]);
-
+
// Propogate value array
$value = array();
foreach ($explode as $v) {
@@ -581,10 +593,10 @@
$value = NULL;
} elseif (ctype_digit($value)) {
$value = (int)$value;
- } elseif (in_array(strtolower($value),
+ } elseif (in_array(strtolower($value),
array('true', 'on', '+', 'yes', 'y'))) {
$value = TRUE;
- } elseif (in_array(strtolower($value),
+ } elseif (in_array(strtolower($value),
array('false', 'off', '-', 'no', 'n'))) {
$value = FALSE;
} elseif (is_numeric($value)) {
@@ -593,10 +605,10 @@
// Just a normal string, right?
$value = trim(preg_replace('/#(.+)$/','',$value));
}
-
+
return $value;
}
-
+
/**
* Used in inlines to check for more inlines or quoted strings
* @access private
@@ -607,13 +619,13 @@
// While pure sequences seem to be nesting just fine,
// pure mappings and mappings with sequences inside can't go very
// deep. This needs to be fixed.
-
- // Check for strings
+
+ // 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);
+ $inline = preg_replace($regex,'YAMLString',$inline);
}
unset($regex);
@@ -623,14 +635,14 @@
$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];
}
-
+
$explode = explode(', ',$inline);
// Re-add the strings
@@ -654,7 +666,7 @@
}
}
}
-
+
// Re-add the mappings
if (!empty($maps)) {
$i = 0;
@@ -668,7 +680,7 @@
return $explode;
}
-
+
/**
* Builds the PHP array from all the YAML nodes we've gathered
* @access private
@@ -690,10 +702,10 @@
$trunk = $this->_array_kmerge($trunk,$n->data);
}
}
-
+
return $trunk;
}
-
+
/**
* Traverses node-space and sets references (& and *) accordingly
* @access private
@@ -705,7 +717,7 @@
if (!empty($node->data)) {
$key = key($node->data);
// If it's an array, don't check.
- if (is_array($node->data[$key])) {
+ if (is_array($node->data[$key])) {
foreach ($node->data[$key] as $k => $v) {
$this->_linkRef($node,$key,$k,$v);
}
@@ -713,11 +725,11 @@
$this->_linkRef($node,$key);
}
}
- }
+ }
}
return true;
}
-
+
function _linkRef(&$n,$key,$k = NULL,$v = NULL) {
if (empty($k) && empty($v)) {
// Look for &refs
@@ -725,7 +737,7 @@
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);
- $this->_allNodes[$n->id]->data[$key] =
+ $this->_allNodes[$n->id]->data[$key] =
substr($n->data[$key],strlen($matches[0])+1);
// Look for *refs
} elseif (preg_match('/^\*([^ ]+)/',$n->data[$key],$matches)) {
@@ -737,7 +749,7 @@
if (preg_match('/^&([^ ]+)/',$v,$matches)) {
// Flag the node so we know it's a reference
$this->_allNodes[$n->id]->ref = substr($matches[0],1);
- $this->_allNodes[$n->id]->data[$key][$k] =
+ $this->_allNodes[$n->id]->data[$key][$k] =
substr($v,strlen($matches[0])+1);
// Look for *refs
} elseif (preg_match('/^\*([^ ]+)/',$v,$matches)) {
@@ -747,7 +759,7 @@
}
}
}
-
+
/**
* Finds the children of a node and aids in the building of the PHP array
* @access private
@@ -770,7 +782,7 @@
}
return $return;
}
-
+
/**
* Turns a node's data and its children's data into a PHP array
*
@@ -824,7 +836,7 @@
}
return true;
}
-
+
/**
* Merges arrays and maintains numeric keys.
@@ -832,29 +844,29 @@
* An ever-so-slightly modified version of the array_kmerge() function posted
* to php.net by mail at nospam dot iaindooley dot com on 2004-04-08.
*
- * http://us3.php.net/manual/en/function.array-merge.php#41394
+ * http://www.php.net/manual/en/function.array-merge.php#41394
*
* @access private
* @param array $arr1
* @param array $arr2
* @return array
*/
- function _array_kmerge($arr1,$arr2) {
- if(!is_array($arr1))
- $arr1 = array();
+ function _array_kmerge($arr1,$arr2) {
+ if(!is_array($arr1))
+ $arr1 = array();
if(!is_array($arr2))
- $arr2 = array();
-
- $keys1 = array_keys($arr1);
- $keys2 = array_keys($arr2);
- $keys = array_merge($keys1,$keys2);
- $vals1 = array_values($arr1);
- $vals2 = array_values($arr2);
- $vals = array_merge($vals1,$vals2);
- $ret = array();
-
- foreach($keys as $key) {
+ $arr2 = array();
+
+ $keys1 = array_keys($arr1);
+ $keys2 = array_keys($arr2);
+ $keys = array_merge($keys1,$keys2);
+ $vals1 = array_values($arr1);
+ $vals2 = array_values($arr2);
+ $vals = array_merge($vals1,$vals2);
+ $ret = array();
+
+ foreach($keys as $key) {
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.
@@ -862,11 +874,10 @@
while (array_key_exists($key, $ret)) {
$key++;
}
- }
- $ret[$key] = $val;
- }
+ }
+ $ret[$key] = $val;
+ }
- return $ret;
+ return $ret;
}
}
-
diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php
index 47a45ea1..4ccb5acf 100644
--- a/includes/api/ApiHelp.php
+++ b/includes/api/ApiHelp.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* This is a simple class to handle action=help
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiHelp extends ApiBase {
@@ -57,7 +57,6 @@ class ApiHelp extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiHelp.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiHelp.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php
index 3e66ed79..a45390c4 100644
--- a/includes/api/ApiLogin.php
+++ b/includes/api/ApiLogin.php
@@ -32,10 +32,10 @@ if (!defined('MEDIAWIKI')) {
/**
* Unit to authenticate log-in attempts to the current wiki.
*
- * @addtogroup API
+ * @ingroup API
*/
class ApiLogin extends ApiBase {
-
+
/**
* Time (in seconds) a user must wait after submitting
* a bad login (will be multiplied by the THROTTLE_FACTOR for each bad attempt)
@@ -47,12 +47,12 @@ class ApiLogin extends ApiBase {
* attempts is increased every failed attempt.
*/
const THROTTLE_FACTOR = 2;
-
+
/**
- * The maximum number of failed logins after which the wait increase stops.
+ * The maximum number of failed logins after which the wait increase stops.
*/
const THOTTLE_MAX_COUNT = 10;
-
+
public function __construct($main, $action) {
parent :: __construct($main, $action, 'lg');
}
@@ -104,10 +104,15 @@ class ApiLogin extends ApiBase {
$wgUser->setOption('rememberpassword', 1);
$wgUser->setCookies();
+ // Run hooks. FIXME: split back and frontend from this hook.
+ // FIXME: This hook should be placed in the backend
+ $injected_html = '';
+ wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
+
$result['result'] = 'Success';
- $result['lguserid'] = $_SESSION['wsUserID'];
- $result['lgusername'] = $_SESSION['wsUserName'];
- $result['lgtoken'] = $_SESSION['wsToken'];
+ $result['lguserid'] = $wgUser->getId();
+ $result['lgusername'] = $wgUser->getName();
+ $result['lgtoken'] = $wgUser->getToken();
$result['cookieprefix'] = $wgCookiePrefix;
$result['sessionid'] = session_id();
break;
@@ -130,34 +135,39 @@ class ApiLogin extends ApiBase {
case LoginForm :: EMPTY_PASS :
$result['result'] = 'EmptyPass';
break;
+ case LoginForm :: CREATE_BLOCKED :
+ $result['result'] = 'CreateBlocked';
+ $result['details'] = 'Your IP address is blocked from account creation';
+ break;
default :
ApiBase :: dieDebug(__METHOD__, 'Unhandled case value');
}
- if ($result['result'] != 'Success') {
- $result['wait'] = $this->cacheBadLogin();
- $result['details'] = "Please wait " . self::THROTTLE_TIME . " seconds before next log-in attempt";
+ if ($result['result'] != 'Success' && !isset( $result['details'] ) ) {
+ $delay = $this->cacheBadLogin();
+ $result['wait'] = $delay;
+ $result['details'] = "Please wait " . $delay . " seconds before next log-in attempt";
}
// if we were allowed to try to login, memcache is fine
-
+
$this->getResult()->addValue(null, 'login', $result);
}
-
+
/**
- * Caches a bad-login attempt associated with the host and with an
- * expiry of $this->mLoginThrottle. These are cached by a key
+ * Caches a bad-login attempt associated with the host and with an
+ * expiry of $this->mLoginThrottle. These are cached by a key
* separate from that used by the captcha system--as such, logging
* in through the standard interface will get you a legal session
* and cookies to prove it, but will not remove this entry.
*
- * Returns the number of seconds until next login attempt will be allowed.
+ * Returns the number of seconds until next login attempt will be allowed.
*
* @access private
*/
private function cacheBadLogin() {
global $wgMemc;
-
+
$key = $this->getMemCacheKey();
$val = $wgMemc->get( $key );
@@ -167,24 +177,24 @@ class ApiLogin extends ApiBase {
} else {
$val['count'] = 1 + $val['count'];
}
-
+
$delay = ApiLogin::calculateDelay($val['count']);
-
+
$wgMemc->delete($key);
// Cache expiration should be the maximum timeout - to prevent a "try and wait" attack
- $wgMemc->add( $key, $val, ApiLogin::calculateDelay(ApiLogin::THOTTLE_MAX_COUNT) );
-
+ $wgMemc->add( $key, $val, ApiLogin::calculateDelay(ApiLogin::THOTTLE_MAX_COUNT) );
+
return $delay;
}
-
+
/**
- * How much time the client must wait before it will be
+ * How much time the client must wait before it will be
* allowed to try to log-in next.
* The return value is 0 if no wait is required.
*/
private function getNextLoginTimeout() {
global $wgMemc;
-
+
$val = $wgMemc->get($this->getMemCacheKey());
$elapse = (time() - $val['lastReqTime']); // in seconds
@@ -192,7 +202,7 @@ class ApiLogin extends ApiBase {
return $canRetryIn < 0 ? 0 : $canRetryIn;
}
-
+
/**
* Based on the number of previously attempted logins, returns
* the delay (in seconds) when the next login attempt will be allowed.
@@ -204,10 +214,10 @@ class ApiLogin extends ApiBase {
$count = $count > self::THOTTLE_MAX_COUNT ? self::THOTTLE_MAX_COUNT : $count;
return self::THROTTLE_TIME + self::THROTTLE_TIME * ($count - 1) * self::THROTTLE_FACTOR;
- }
+ }
/**
- * Internal cache key for badlogin checks. Robbed from the
+ * Internal cache key for badlogin checks. Robbed from the
* ConfirmEdit extension and modified to use a key unique to the
* API login.3
*
@@ -217,7 +227,7 @@ class ApiLogin extends ApiBase {
private function getMemCacheKey() {
return wfMemcKey( 'apilogin', 'badlogin', 'ip', wfGetIP() );
}
-
+
public function mustBePosted() { return true; }
public function getAllowedParams() {
@@ -241,11 +251,11 @@ class ApiLogin extends ApiBase {
'This module is used to login and get the authentication tokens. ',
'In the event of a successful log-in, a cookie will be attached',
'to your session. In the event of a failed log-in, you will not ',
- 'be able to attempt another log-in through this method for 60 seconds.',
+ 'be able to attempt another log-in through this method for 5 seconds.',
'This is to prevent password guessing by automated password crackers.'
);
}
-
+
protected function getExamples() {
return array(
'api.php?action=login&lgname=user&lgpassword=password'
@@ -253,7 +263,6 @@ class ApiLogin extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiLogin.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiLogin.php 35565 2008-05-29 19:23:37Z btongminh $';
}
}
-
diff --git a/includes/api/ApiLogout.php b/includes/api/ApiLogout.php
index d578acf3..694c9e3c 100644
--- a/includes/api/ApiLogout.php
+++ b/includes/api/ApiLogout.php
@@ -29,10 +29,10 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * API module to allow users to log out of the wiki. API equivalent of
+ * API module to allow users to log out of the wiki. API equivalent of
* Special:Userlogout.
*
- * @addtogroup API
+ * @ingroup API
*/
class ApiLogout extends ApiBase {
@@ -43,6 +43,10 @@ class ApiLogout extends ApiBase {
public function execute() {
global $wgUser;
$wgUser->logout();
+
+ // Give extensions to do something after user logout
+ $injected_html = '';
+ wfRunHooks( 'UserLogoutComplete', array(&$wgUser, &$injected_html) );
}
public function getAllowedParams() {
@@ -66,6 +70,6 @@ class ApiLogout extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id$';
+ return __CLASS__ . ': $Id: ApiLogout.php 35294 2008-05-24 20:44:49Z btongminh $';
}
}
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 874e531c..cce4c3e7 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -29,6 +29,10 @@ if (!defined('MEDIAWIKI')) {
}
/**
+ * @defgroup API API
+ */
+
+/**
* This is the main API class, used for both external and internal processing.
* When executed, it will create the requested formatter object,
* instantiate and execute an object associated with the needed action,
@@ -37,9 +41,9 @@ if (!defined('MEDIAWIKI')) {
*
* To use API from another application, run it using FauxRequest object, in which
* case any internal exceptions will not be handled but passed up to the caller.
- * After successful execution, use getResult() for the resulting data.
- *
- * @addtogroup API
+ * After successful execution, use getResult() for the resulting data.
+ *
+ * @ingroup API
*/
class ApiMain extends ApiBase {
@@ -62,7 +66,7 @@ class ApiMain extends ApiBase {
'help' => 'ApiHelp',
'paraminfo' => 'ApiParamInfo',
);
-
+
private static $WriteModules = array (
'rollback' => 'ApiRollback',
'delete' => 'ApiDelete',
@@ -71,8 +75,8 @@ class ApiMain extends ApiBase {
'block' => 'ApiBlock',
'unblock' => 'ApiUnblock',
'move' => 'ApiMove',
- #'changerights' => 'ApiChangeRights'
- # Disabled for now
+ 'edit' => 'ApiEditPage',
+ 'emailuser' => 'ApiEmailUser',
);
/**
@@ -113,25 +117,25 @@ class ApiMain extends ApiBase {
parent :: __construct($this, $this->mInternalMode ? 'main_int' : 'main');
if (!$this->mInternalMode) {
-
+
// Impose module restrictions.
- // If the current user cannot read,
+ // If the current user cannot read,
// Remove all modules other than login
global $wgUser;
-
+
if( $request->getVal( 'callback' ) !== null ) {
// JSON callback allows cross-site reads.
// For safety, strip user credentials.
wfDebug( "API: stripping user credentials for JSON callback\n" );
$wgUser = new User();
}
-
+
if (!$wgUser->isAllowed('read')) {
self::$Modules = array(
'login' => self::$Modules['login'],
'logout' => self::$Modules['logout'],
'help' => self::$Modules['help'],
- );
+ );
}
}
@@ -150,7 +154,8 @@ class ApiMain extends ApiBase {
$this->mRequest = & $request;
- $this->mSquidMaxage = 0;
+ $this->mSquidMaxage = -1; // flag for executeActionWithErrorHandling()
+ $this->mCommit = false;
}
/**
@@ -175,12 +180,19 @@ class ApiMain extends ApiBase {
}
/**
- * This method will simply cause an error if the write mode was disabled for this api.
+ * This method will simply cause an error if the write mode was disabled
+ * or if the current user doesn't have the right to use it
*/
public function requestWriteMode() {
+ global $wgUser;
if (!$this->mEnableWrite)
- $this->dieUsage('Editing of this site is disabled. Make sure the $wgEnableWriteAPI=true; ' .
- 'statement is included in the site\'s LocalSettings.php file', 'noapiwrite');
+ $this->dieUsage('Editing of this wiki through the API' .
+ ' is disabled. Make sure the $wgEnableWriteAPI=true; ' .
+ 'statement is included in the wiki\'s ' .
+ 'LocalSettings.php file', 'noapiwrite');
+ if (!$wgUser->isAllowed('writeapi'))
+ $this->dieUsage('You\'re not allowed to edit this ' .
+ 'wiki through the API', 'writeapidenied');
}
/**
@@ -198,7 +210,7 @@ class ApiMain extends ApiBase {
}
/**
- * Execute api request. Any errors will be handled if the API was called by the remote client.
+ * Execute api request. Any errors will be handled if the API was called by the remote client.
*/
public function execute() {
$this->profileIn();
@@ -206,6 +218,7 @@ class ApiMain extends ApiBase {
$this->executeAction();
else
$this->executeActionWithErrorHandling();
+
$this->profileOut();
}
@@ -247,11 +260,22 @@ class ApiMain extends ApiBase {
$this->printResult(true);
}
+ global $wgRequest;
+ if($this->mSquidMaxage == -1)
+ {
+ # Nobody called setCacheMaxAge(), use the (s)maxage parameters
+ $smaxage = $wgRequest->getVal('smaxage', 0);
+ $maxage = $wgRequest->getVal('maxage', 0);
+ }
+ else
+ $smaxage = $maxage = $this->mSquidMaxage;
+
// 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;
+ $exp = min($smaxage, $maxage);
+ $expires = ($exp == 0 ? 1 : time() + $exp);
header('Expires: ' . wfTimestamp(TS_RFC2822, $expires));
- header('Cache-Control: s-maxage=' . $this->mSquidMaxage . ', must-revalidate, max-age=0');
+ header('Cache-Control: s-maxage=' . $smaxage . ', must-revalidate, max-age=' . $maxage);
if($this->mPrinter->getIsHtml())
echo wfReportTime();
@@ -261,10 +285,10 @@ class ApiMain extends ApiBase {
/**
* Replace the result data with the information about an exception.
- * Returns the error code
+ * Returns the error code
*/
protected function substituteResultWithError($e) {
-
+
// Printer may not be initialized if the extractRequestParams() fails for the main module
if (!isset ($this->mPrinter)) {
// The printer has not been created yet. Try to manually get formatter value.
@@ -284,20 +308,27 @@ class ApiMain extends ApiBase {
$errMessage = array (
'code' => $e->getCodeString(),
'info' => $e->getMessage());
-
+
// Only print the help message when this is for the developer, not runtime
if ($this->mPrinter->getIsHtml() || $this->mAction == 'help')
ApiResult :: setContent($errMessage, $this->makeHelpMsg());
} else {
+ global $wgShowSQLErrors, $wgShowExceptionDetails;
//
// Something is seriously wrong
//
+ if ( ( $e instanceof DBQueryError ) && !$wgShowSQLErrors ) {
+ $info = "Database query error";
+ } else {
+ $info = "Exception Caught: {$e->getMessage()}";
+ }
+
$errMessage = array (
'code' => 'internal_api_error_'. get_class($e),
- 'info' => "Exception Caught: {$e->getMessage()}"
+ 'info' => $info,
);
- ApiResult :: setContent($errMessage, "\n\n{$e->getTraceAsString()}\n\n");
+ ApiResult :: setContent($errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : "" );
}
$this->getResult()->reset();
@@ -318,12 +349,12 @@ class ApiMain extends ApiBase {
// Instantiate the module requested by the user
$module = new $this->mModules[$this->mAction] ($this, $this->mAction);
-
+
if( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) {
// Check for maxlag
- global $wgLoadBalancer, $wgShowHostnames;
+ global $wgShowHostnames;
$maxLag = $params['maxlag'];
- list( $host, $lag ) = $wgLoadBalancer->getMaxLag();
+ list( $host, $lag ) = wfGetLB()->getMaxLag();
if ( $lag > $maxLag ) {
if( $wgShowHostnames ) {
ApiBase :: dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' );
@@ -333,7 +364,7 @@ class ApiMain extends ApiBase {
return;
}
}
-
+
if (!$this->mInternalMode) {
// Ignore mustBePosted() for internal calls
if($module->mustBePosted() && !$this->mRequest->wasPosted())
@@ -367,13 +398,12 @@ class ApiMain extends ApiBase {
protected function printResult($isError) {
$printer = $this->mPrinter;
$printer->profileIn();
-
+
/* If the help message is requested in the default (xmlfm) format,
* tell the printer not to escape ampersands so that our links do
* not break. */
- $params = $this->extractRequestParams();
- $printer->setUnescapeAmps ( ( $this->mAction == 'help' || $isError )
- && $params['format'] == ApiMain::API_DEFAULT_FORMAT );
+ $printer->setUnescapeAmps ( ( $this->mAction == 'help' || $isError )
+ && $this->getParameter('format') == ApiMain::API_DEFAULT_FORMAT );
$printer->initPrinter($isError);
@@ -399,6 +429,14 @@ class ApiMain extends ApiBase {
'maxlag' => array (
ApiBase :: PARAM_TYPE => 'integer'
),
+ 'smaxage' => array (
+ ApiBase :: PARAM_TYPE => 'integer',
+ ApiBase :: PARAM_DFLT => 0
+ ),
+ 'maxage' => array (
+ ApiBase :: PARAM_TYPE => 'integer',
+ ApiBase :: PARAM_DFLT => 0
+ ),
);
}
@@ -410,7 +448,9 @@ class ApiMain extends ApiBase {
'format' => 'The format of the output',
'action' => 'What action you would like to perform',
'version' => 'When showing help, include version for each module',
- 'maxlag' => 'Maximum lag'
+ 'maxlag' => 'Maximum lag',
+ 'smaxage' => 'Set the s-maxage header to this many seconds. Errors are never cached',
+ 'maxage' => 'Set the max-age header to this many seconds. Errors are never cached',
);
}
@@ -444,13 +484,17 @@ class ApiMain extends ApiBase {
'',
);
}
-
+
/**
* Returns an array of strings with credits for the API
*/
protected function getCredits() {
return array(
- 'This API is being implemented by Roan Kattouw <Firstname>.<Lastname>@home.nl',
+ 'API developers:',
+ ' Roan Kattouw <Firstname>.<Lastname>@home.nl (lead developer Sep 2007-present)',
+ ' Victor Vasiliev - vasilvv at gee mail dot com',
+ ' Yuri Astrakhan <Firstname><Lastname>@gmail.com (creator, lead developer Sep 2006-Sep 2007)',
+ '',
'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org',
'or file a bug report at http://bugzilla.wikimedia.org/'
);
@@ -460,7 +504,7 @@ class ApiMain extends ApiBase {
* Override the parent to generate help messages for all available modules.
*/
public function makeHelpMsg() {
-
+
$this->mPrinter->setHelp();
// Use parent to make default message for the main module
@@ -486,9 +530,9 @@ class ApiMain extends ApiBase {
$msg .= $msg2;
$msg .= "\n";
}
-
+
$msg .= "\n*** Credits: ***\n " . implode("\n ", $this->getCredits()) . "\n";
-
+
return $msg;
}
@@ -496,15 +540,15 @@ class ApiMain extends ApiBase {
public static function makeHelpMsgHeader($module, $paramName) {
$modulePrefix = $module->getModulePrefix();
if (!empty($modulePrefix))
- $modulePrefix = "($modulePrefix) ";
-
+ $modulePrefix = "($modulePrefix) ";
+
return "* $paramName={$module->getModuleName()} $modulePrefix*";
- }
+ }
private $mIsBot = null;
private $mIsSysop = null;
private $mCanApiHighLimits = null;
-
+
/**
* Returns true if the currently logged in user is a bot, false otherwise
* OBSOLETE, use canApiHighLimits() instead
@@ -516,7 +560,7 @@ class ApiMain extends ApiBase {
}
return $this->mIsBot;
}
-
+
/**
* Similar to isBot(), this method returns true if the logged in user is
* a sysop, and false if not.
@@ -530,7 +574,11 @@ class ApiMain extends ApiBase {
return $this->mIsSysop;
}
-
+
+ /**
+ * Check whether the current user is allowed to use high limits
+ * @return bool
+ */
public function canApiHighLimits() {
if (!isset($this->mCanApiHighLimits)) {
global $wgUser;
@@ -540,6 +588,10 @@ class ApiMain extends ApiBase {
return $this->mCanApiHighLimits;
}
+ /**
+ * Check whether the user wants us to show version information in the API help
+ * @return bool
+ */
public function getShowVersions() {
return $this->mShowVersions;
}
@@ -551,7 +603,7 @@ class ApiMain extends ApiBase {
public function getVersion() {
$vers = array ();
$vers[] = 'MediaWiki ' . SpecialVersion::getVersion();
- $vers[] = __CLASS__ . ': $Id: ApiMain.php 31484 2008-03-03 05:46:20Z brion $';
+ $vers[] = __CLASS__ . ': $Id: ApiMain.php 37349 2008-07-08 20:53:41Z catrope $';
$vers[] = ApiBase :: getBaseVersion();
$vers[] = ApiFormatBase :: getBaseVersion();
$vers[] = ApiQueryBase :: getBaseVersion();
@@ -561,7 +613,7 @@ class ApiMain extends ApiBase {
/**
* Add or overwrite a module in this ApiMain instance. Intended for use by extending
- * classes who wish to add their own modules to their lexicon or override the
+ * classes who wish to add their own modules to their lexicon or override the
* behavior of inherent ones.
*
* @access protected
@@ -583,7 +635,7 @@ class ApiMain extends ApiBase {
protected function addFormat( $fmtName, $fmtClass ) {
$this->mFormats[$fmtName] = $fmtClass;
}
-
+
/**
* Get the array mapping module names to class names
*/
@@ -595,8 +647,8 @@ class ApiMain extends ApiBase {
/**
* This exception will be thrown when dieUsage is called to stop module execution.
* The exception handling code will print a help screen explaining how this API may be used.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class UsageException extends Exception {
@@ -613,5 +665,3 @@ class UsageException extends Exception {
return "{$this->getCodeString()}: {$this->getMessage()}";
}
}
-
-
diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php
index a8c39c9a..8687bdcd 100644
--- a/includes/api/ApiMove.php
+++ b/includes/api/ApiMove.php
@@ -29,21 +29,21 @@ if (!defined('MEDIAWIKI')) {
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiMove extends ApiBase {
public function __construct($main, $action) {
parent :: __construct($main, $action);
}
-
+
public function execute() {
global $wgUser;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
if(is_null($params['reason']))
$params['reason'] = '';
-
+
$titleObj = NULL;
if(!isset($params['from']))
$this->dieUsageMsg(array('missingparam', 'from'));
@@ -69,7 +69,7 @@ class ApiMove extends ApiBase {
// Run getUserPermissionsErrors() here so we get message arguments too,
// rather than just a message key. The latter is troublesome for messages
// that use arguments.
- // FIXME: moveTo() should really return an array, requires some
+ // FIXME: moveTo() should really return an array, requires some
// refactoring of other code, though (mainly SpecialMovepage.php)
$errors = array_merge($fromTitle->getUserPermissionsErrors('move', $wgUser),
$fromTitle->getUserPermissionsErrors('edit', $wgUser),
@@ -79,16 +79,19 @@ class ApiMove extends ApiBase {
// We don't care about multiple errors, just report one of them
$this->dieUsageMsg(current($errors));
- $dbw = wfGetDB(DB_MASTER);
- $dbw->begin();
+ $hookErr = null;
+
$retval = $fromTitle->moveTo($toTitle, true, $params['reason'], !$params['noredirect']);
if($retval !== true)
- $this->dieUsageMsg(array($retval));
+ {
+ # FIXME: Title::moveTo() sometimes returns a string
+ $this->dieUsageMsg(reset($retval));
+ }
$r = array('from' => $fromTitle->getPrefixedText(), 'to' => $toTitle->getPrefixedText(), 'reason' => $params['reason']);
- if(!$params['noredirect'])
+ if(!$params['noredirect'] || !$wgUser->isAllowed('suppressredirect'))
$r['redirectcreated'] = '';
-
+
if($params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage())
{
// We need to move the talk page as well
@@ -104,14 +107,25 @@ class ApiMove extends ApiBase {
{
$r['talkmove-error-code'] = ApiBase::$messageMap[$retval]['code'];
$r['talkmove-error-info'] = ApiBase::$messageMap[$retval]['info'];
- }
+ }
+ }
+
+ # Watch pages
+ if($params['watch'] || $wgUser->getOption('watchmoves'))
+ {
+ $wgUser->addWatch($fromTitle);
+ $wgUser->addWatch($toTitle);
+ }
+ else if($params['unwatch'])
+ {
+ $wgUser->removeWatch($fromTitle);
+ $wgUser->removeWatch($toTitle);
}
- $dbw->commit(); // Make sure all changes are really written to the DB
$this->getResult()->addValue(null, $this->getModuleName(), $r);
}
-
+
public function mustBePosted() { return true; }
-
+
public function getAllowedParams() {
return array (
'from' => null,
@@ -119,7 +133,9 @@ class ApiMove extends ApiBase {
'token' => null,
'reason' => null,
'movetalk' => false,
- 'noredirect' => false
+ 'noredirect' => false,
+ 'watch' => false,
+ 'unwatch' => false
);
}
@@ -130,7 +146,9 @@ class ApiMove extends ApiBase {
'token' => 'A move token previously retrieved through prop=info',
'reason' => 'Reason for the move (optional).',
'movetalk' => 'Move the talk page, if it exists.',
- 'noredirect' => 'Don\'t create a redirect'
+ 'noredirect' => 'Don\'t create a redirect',
+ 'watch' => 'Add the page and the redirect to your watchlist',
+ 'unwatch' => 'Remove the page and the redirect from your watchlist'
);
}
@@ -147,6 +165,6 @@ class ApiMove extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiMove.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiMove.php 35619 2008-05-30 19:59:47Z btongminh $';
}
}
diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php
index f4b600fe..2da92059 100644
--- a/includes/api/ApiOpenSearch.php
+++ b/includes/api/ApiOpenSearch.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiOpenSearch extends ApiBase {
@@ -45,11 +45,12 @@ class ApiOpenSearch extends ApiBase {
$params = $this->extractRequestParams();
$search = $params['search'];
$limit = $params['limit'];
-
+ $namespaces = $params['namespace'];
+
// Open search results may be stored for a very long time
$this->getMain()->setCacheMaxAge(1200);
-
- $srchres = PrefixSearch::titleSearch( $search, $limit );
+
+ $srchres = PrefixSearch::titleSearch( $search, $limit, $namespaces );
// Set top level elements
$result = $this->getResult();
@@ -66,14 +67,20 @@ class ApiOpenSearch extends ApiBase {
ApiBase :: PARAM_MIN => 1,
ApiBase :: PARAM_MAX => 100,
ApiBase :: PARAM_MAX2 => 100
- )
+ ),
+ 'namespace' => array(
+ ApiBase :: PARAM_DFLT => NS_MAIN,
+ ApiBase :: PARAM_TYPE => 'namespace',
+ ApiBase :: PARAM_ISMULTI => true
+ ),
);
}
public function getParamDescription() {
return array (
'search' => 'Search string',
- 'limit' => 'Maximum amount of results to return'
+ 'limit' => 'Maximum amount of results to return',
+ 'namespace' => 'Namespaces to search',
);
}
@@ -88,7 +95,6 @@ class ApiOpenSearch extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiOpenSearch.php 30275 2008-01-30 01:07:49Z brion $';
+ return __CLASS__ . ': $Id: ApiOpenSearch.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index 185c0c59..e09cb285 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -33,17 +33,18 @@ if (!defined('MEDIAWIKI')) {
* Initially, when the client passes in titles=, pageids=, or revisions= parameter,
* an instance of the ApiPageSet class will normalize titles,
* determine if the pages/revisions exist, and prefetch any additional data page data requested.
- *
+ *
* When generator is used, the result of the generator will become the input for the
* second instance of this class, and all subsequent actions will go use the second instance
- * for all their work.
- *
- * @addtogroup API
+ * for all their work.
+ *
+ * @ingroup API
*/
class ApiPageSet extends ApiQueryBase {
- private $mAllPages; // [ns][dbkey] => page_id or 0 when missing
- private $mTitles, $mGoodTitles, $mMissingTitles, $mMissingPageIDs, $mRedirectTitles;
+ private $mAllPages; // [ns][dbkey] => page_id or negative when missing
+ private $mTitles, $mGoodTitles, $mMissingTitles, $mInvalidTitles;
+ private $mMissingPageIDs, $mRedirectTitles;
private $mNormalizedTitles, $mInterwikiTitles;
private $mResolveRedirects, $mPendingRedirectIDs;
private $mGoodRevIDs, $mMissingRevIDs;
@@ -58,6 +59,7 @@ class ApiPageSet extends ApiQueryBase {
$this->mTitles = array();
$this->mGoodTitles = array ();
$this->mMissingTitles = array ();
+ $this->mInvalidTitles = array ();
$this->mMissingPageIDs = array ();
$this->mRedirectTitles = array ();
$this->mNormalizedTitles = array ();
@@ -69,7 +71,7 @@ class ApiPageSet extends ApiQueryBase {
$this->mResolveRedirects = $resolveRedirects;
if($resolveRedirects)
$this->mPendingRedirectIDs = array();
-
+
$this->mFakePageId = -1;
}
@@ -107,8 +109,9 @@ class ApiPageSet extends ApiQueryBase {
}
/**
- * Returns an array [ns][dbkey] => page_id for all requested titles
- * page_id is a unique negative number in case title was not found
+ * Returns an array [ns][dbkey] => page_id for all requested titles.
+ * page_id is a unique negative number in case title was not found.
+ * Invalid titles will also have negative page IDs and will be in namespace 0
*/
public function getAllTitlesByNamespace() {
return $this->mAllPages;
@@ -154,6 +157,15 @@ class ApiPageSet extends ApiQueryBase {
}
/**
+ * Titles that were deemed invalid by Title::newFromText()
+ * The array's index will be unique and negative for each item
+ * @return array of strings (not Title objects)
+ */
+ public function getInvalidTitles() {
+ return $this->mInvalidTitles;
+ }
+
+ /**
* Page IDs that were not found in the database
* @return array of page IDs
*/
@@ -170,18 +182,18 @@ class ApiPageSet extends ApiQueryBase {
}
/**
- * Get a list of title normalizations - maps the title given
+ * Get a list of title normalizations - maps the title given
* with its normalized version.
- * @return array raw_prefixed_title (string) => prefixed_title (string)
+ * @return array raw_prefixed_title (string) => prefixed_title (string)
*/
public function getNormalizedTitles() {
return $this->mNormalizedTitles;
}
/**
- * Get a list of interwiki titles - maps the title given
+ * Get a list of interwiki titles - maps the title given
* with to the interwiki prefix.
- * @return array raw_prefixed_title (string) => interwiki_prefix (string)
+ * @return array raw_prefixed_title (string) => interwiki_prefix (string)
*/
public function getInterwikiTitles() {
return $this->mInterwikiTitles;
@@ -293,11 +305,11 @@ class ApiPageSet extends ApiQueryBase {
* Extract all requested fields from the row received from the database
*/
public function processDbRow($row) {
-
+
// Store Title object in various data structures
$title = Title :: makeTitle($row->page_namespace, $row->page_title);
-
- $pageId = intval($row->page_id);
+
+ $pageId = intval($row->page_id);
$this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
$this->mTitles[] = $title;
@@ -310,26 +322,26 @@ class ApiPageSet extends ApiQueryBase {
foreach ($this->mRequestedPageFields as $fieldName => & $fieldValues)
$fieldValues[$pageId] = $row-> $fieldName;
}
-
+
public function finishPageSetGeneration() {
$this->profileIn();
$this->resolvePendingRedirects();
$this->profileOut();
}
-
+
/**
* This method populates internal variables with page information
* based on the given array of title strings.
- *
+ *
* Steps:
* #1 For each title, get data from `page` table
* #2 If page was not found in the DB, store it as missing
- *
+ *
* Additionally, when resolving redirects:
* #3 If no more redirects left, stop.
* #4 For each redirect, get its links from `pagelinks` table.
* #5 Substitute the original LinkBatch object with the new list
- * #6 Repeat from step #1
+ * #6 Repeat from step #1
*/
private function initFromTitles($titles) {
@@ -337,7 +349,7 @@ class ApiPageSet extends ApiQueryBase {
$linkBatch = $this->processTitlesArray($titles);
if($linkBatch->isEmpty())
return;
-
+
$db = $this->getDB();
$set = $linkBatch->constructSet('page', $db);
@@ -368,13 +380,14 @@ class ApiPageSet extends ApiQueryBase {
$this->profileDBIn();
$res = $db->select('page', $this->getPageTableFields(), $set, __METHOD__);
$this->profileDBOut();
-
- $this->initFromQueryResult($db, $res, array_flip($pageids), false); // process PageIDs
+
+ $remaining = array_flip($pageids);
+ $this->initFromQueryResult($db, $res, $remaining, false); // process PageIDs
// Resolve any found redirects
$this->resolvePendingRedirects();
}
-
+
/**
* Iterate through the result of the query on 'page' table,
* and for each row create and store title object and save any extra fields requested.
@@ -390,7 +403,7 @@ class ApiPageSet extends ApiQueryBase {
private function initFromQueryResult($db, $res, &$remaining = null, $processTitles = null) {
if (!is_null($remaining) && is_null($processTitles))
ApiBase :: dieDebug(__METHOD__, 'Missing $processTitles parameter when $remaining is provided');
-
+
while ($row = $db->fetchObject($res)) {
$pageId = intval($row->page_id);
@@ -402,12 +415,12 @@ class ApiPageSet extends ApiQueryBase {
else
unset ($remaining[$pageId]);
}
-
+
// Store any extra fields requested by modules
$this->processDbRow($row);
}
$db->freeResult($res);
-
+
if(isset($remaining)) {
// Any items left in the $remaining list are added as missing
if($processTitles) {
@@ -437,15 +450,15 @@ class ApiPageSet extends ApiQueryBase {
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__ );
@@ -472,27 +485,27 @@ class ApiPageSet extends ApiQueryBase {
if($this->mResolveRedirects) {
$db = $this->getDB();
$pageFlds = $this->getPageTableFields();
-
+
// Repeat until all redirects have been resolved
// The infinite loop is prevented by keeping all known pages in $this->mAllPages
- while (!empty ($this->mPendingRedirectIDs)) {
-
+ while (!empty ($this->mPendingRedirectIDs)) {
+
// Resolve redirects by querying the pagelinks table, and repeat the process
// Create a new linkBatch object for the next pass
$linkBatch = $this->getRedirectTargets();
-
+
if ($linkBatch->isEmpty())
break;
-
+
$set = $linkBatch->constructSet('page', $db);
if(false === $set)
break;
-
+
// Get pageIDs data from the `page` table
$this->profileDBIn();
$res = $db->select('page', $pageFlds, $set, __METHOD__);
$this->profileDBOut();
-
+
// Hack: get the ns:titles stored in array(ns => array(titles)) format
$this->initFromQueryResult($db, $res, $linkBatch->data, true);
}
@@ -500,76 +513,55 @@ class ApiPageSet extends ApiQueryBase {
}
private function getRedirectTargets() {
-
- $linkBatch = new LinkBatch();
+ $lb = new LinkBatch();
$db = $this->getDB();
- // find redirect targets for all redirect pages
$this->profileDBIn();
- $res = $db->select('pagelinks', array (
- 'pl_from',
- 'pl_namespace',
- 'pl_title'
- ), array (
- 'pl_from' => array_keys($this->mPendingRedirectIDs
- )), __METHOD__);
+ $res = $db->select('redirect', array(
+ 'rd_from',
+ 'rd_namespace',
+ 'rd_title'
+ ), array('rd_from' => array_keys($this->mPendingRedirectIDs)),
+ __METHOD__
+ );
$this->profileDBOut();
- while ($row = $db->fetchObject($res)) {
-
- $plfrom = intval($row->pl_from);
-
- // Bug 7304 workaround
- // ( http://bugzilla.wikipedia.org/show_bug.cgi?id=7304 )
- // A redirect page may have more than one link.
- // This code will only use the first link returned.
- if (isset ($this->mPendingRedirectIDs[$plfrom])) { // remove line when bug 7304 is fixed
-
- $titleStrFrom = $this->mPendingRedirectIDs[$plfrom]->getPrefixedText();
- $titleStrTo = Title :: makeTitle($row->pl_namespace, $row->pl_title)->getPrefixedText();
- unset ($this->mPendingRedirectIDs[$plfrom]); // remove line when bug 7304 is fixed
-
- // Avoid an infinite loop by checking if we have already processed this target
- if (!isset ($this->mAllPages[$row->pl_namespace][$row->pl_title])) {
- $linkBatch->add($row->pl_namespace, $row->pl_title);
- }
- } else {
- // This redirect page has more than one link.
- // This is very slow, but safer until bug 7304 is resolved
- $title = Title :: newFromID($plfrom);
- $titleStrFrom = $title->getPrefixedText();
-
+ while($row = $db->fetchObject($res))
+ {
+ $rdfrom = intval($row->rd_from);
+ $from = $this->mPendingRedirectIDs[$rdfrom]->getPrefixedText();
+ $to = Title::makeTitle($row->rd_namespace, $row->rd_title)->getPrefixedText();
+ unset($this->mPendingRedirectIDs[$rdfrom]);
+ if(!isset($this->mAllPages[$row->rd_namespace][$row->rd_title]))
+ $lb->add($row->rd_namespace, $row->rd_title);
+ $this->mRedirectTitles[$from] = $to;
+ }
+ $db->freeResult($res);
+ if(!empty($this->mPendingRedirectIDs))
+ {
+ # We found pages that aren't in the redirect table
+ # Add them
+ foreach($this->mPendingRedirectIDs as $id => $title)
+ {
$article = new Article($title);
- $text = $article->getContent();
- $titleTo = Title :: newFromRedirect($text);
- $titleStrTo = $titleTo->getPrefixedText();
-
- if (is_null($titleStrTo))
- ApiBase :: dieDebug(__METHOD__, 'Bug7304 workaround: redir target from {$title->getPrefixedText()} not found');
-
- // Avoid an infinite loop by checking if we have already processed this target
- if (!isset ($this->mAllPages[$titleTo->getNamespace()][$titleTo->getDBkey()])) {
- $linkBatch->addObj($titleTo);
- }
+ $rt = $article->insertRedirect();
+ if(!$rt)
+ # What the hell. Let's just ignore this
+ continue;
+ $lb->addObj($rt);
+ $this->mRedirectTitles[$title->getPrefixedText()] = $rt->getPrefixedText();
+ unset($this->mPendingRedirectIDs[$id]);
}
-
- $this->mRedirectTitles[$titleStrFrom] = $titleStrTo;
}
- $db->freeResult($res);
-
- // All IDs must exist in the page table
- if (!empty($this->mPendingRedirectIDs[$plfrom]))
- ApiBase :: dieDebug(__METHOD__, 'Invalid redirect IDs were found');
-
- return $linkBatch;
+ return $lb;
}
/**
* Given an array of title strings, convert them into Title objects.
* Alternativelly, an array of Title objects may be given.
- * This method validates access rights for the title,
+ * This method validates access rights for the title,
* and appends normalization values to the output.
- *
+ *
* @return LinkBatch of title objects.
*/
private function processTitlesArray($titles) {
@@ -577,16 +569,21 @@ class ApiPageSet extends ApiQueryBase {
$linkBatch = new LinkBatch();
foreach ($titles as $title) {
-
+
$titleObj = is_string($title) ? Title :: newFromText($title) : $title;
if (!$titleObj)
- $this->dieUsage("bad title", 'invalidtitle');
-
+ {
+ # Handle invalid titles gracefully
+ $this->mAllpages[0][$title] = $this->mFakePageId;
+ $this->mInvalidTitles[$this->mFakePageId] = $title;
+ $this->mFakePageId--;
+ continue; // There's nothing else we can do
+ }
$iw = $titleObj->getInterwiki();
if (!empty($iw)) {
// This title is an interwiki link.
$this->mInterwikiTitles[$titleObj->getPrefixedText()] = $iw;
- } else {
+ } else {
// Validation
if ($titleObj->getNamespace() < 0)
@@ -594,7 +591,7 @@ class ApiPageSet extends ApiQueryBase {
$linkBatch->addObj($titleObj);
}
-
+
// Make sure we remember the original title that was given to us
// This way the caller can correlate new titles with the originally requested,
// i.e. namespace is localized or capitalization is different
@@ -605,7 +602,7 @@ class ApiPageSet extends ApiQueryBase {
return $linkBatch;
}
-
+
protected function getAllowedParams() {
return array (
'titles' => array (
@@ -631,7 +628,6 @@ class ApiPageSet extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiPageSet.php 24935 2007-08-20 08:13:16Z nickj $';
+ return __CLASS__ . ': $Id: ApiPageSet.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php
index 7de22252..77ce514f 100644
--- a/includes/api/ApiParamInfo.php
+++ b/includes/api/ApiParamInfo.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiParamInfo extends ApiBase {
@@ -55,7 +55,7 @@ class ApiParamInfo extends ApiBase {
$obj = new $modArr[$m]($this->getMain(), $m);
$a = $this->getClassInfo($obj);
$a['name'] = $m;
- $r['modules'][] = $a;
+ $r['modules'][] = $a;
}
$result->setIndexedTagName($r['modules'], 'module');
}
@@ -106,7 +106,7 @@ class ApiParamInfo extends ApiBase {
$retval['parameters'][] = $a;
continue;
}
-
+
if(isset($p[ApiBase::PARAM_DFLT]))
$a['default'] = $p[ApiBase::PARAM_DFLT];
if(isset($p[ApiBase::PARAM_ISMULTI]))
@@ -131,7 +131,7 @@ class ApiParamInfo extends ApiBase {
$result->setIndexedTagName($retval['parameters'], 'param');
return $retval;
}
-
+
public function getAllowedParams() {
return array (
'modules' => array(
@@ -161,7 +161,6 @@ class ApiParamInfo extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiParse.php 29810 2008-01-15 21:33:08Z catrope $';
+ return __CLASS__ . ': $Id: ApiParamInfo.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php
index 21a21e8d..4dcc94b6 100644
--- a/includes/api/ApiParse.php
+++ b/includes/api/ApiParse.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiParse extends ApiBase {
@@ -43,31 +43,56 @@ class ApiParse extends ApiBase {
$text = $params['text'];
$title = $params['title'];
$page = $params['page'];
+ $oldid = $params['oldid'];
if(!is_null($page) && (!is_null($text) || $title != "API"))
$this->dieUsage("The page parameter cannot be used together with the text and title parameters", 'params');
$prop = array_flip($params['prop']);
-
+ $revid = false;
+
global $wgParser, $wgUser;
- if(!is_null($page)) {
- $titleObj = Title::newFromText($page);
- if(!$titleObj)
- $this->dieUsageMsg(array('missingtitle', $page));
-
- // Try the parser cache first
- $articleObj = new Article($titleObj);
- $pcache =& ParserCache::singleton();
- $p_result = $pcache->get($articleObj, $wgUser);
- if(!$p_result) {
- $p_result = $wgParser->parse($articleObj->getContent(), $titleObj, new ParserOptions());
- global $wgUseParserCache;
- if($wgUseParserCache)
- $pcache->save($p_result, $articleObj, $wgUser);
+ $popts = new ParserOptions();
+ $popts->setTidy(true);
+ $popts->enableLimitReport();
+ if(!is_null($oldid) || !is_null($page))
+ {
+ if(!is_null($oldid))
+ {
+ # Don't use the parser cache
+ $rev = Revision::newFromID($oldid);
+ if(!$rev)
+ $this->dieUsage("There is no revision ID $oldid", 'missingrev');
+ if(!$rev->userCan(Revision::DELETED_TEXT))
+ $this->dieUsage("You don't have permission to view deleted revisions", 'permissiondenied');
+ $text = $rev->getRawText();
+ $titleObj = $rev->getTitle();
+ $p_result = $wgParser->parse($text, $titleObj, $popts);
}
- } else {
+ else
+ {
+ $titleObj = Title::newFromText($page);
+ if(!$titleObj)
+ $this->dieUsage("The page you specified doesn't exist", 'missingtitle');
+
+ // Try the parser cache first
+ $articleObj = new Article($titleObj);
+ if(isset($prop['revid']))
+ $oldid = $articleObj->getRevIdFetched();
+ $pcache = ParserCache::singleton();
+ $p_result = $pcache->get($articleObj, $wgUser);
+ if(!$p_result) {
+ $p_result = $wgParser->parse($articleObj->getContent(), $titleObj, $popts);
+ global $wgUseParserCache;
+ if($wgUseParserCache)
+ $pcache->save($p_result, $articleObj, $wgUser);
+ }
+ }
+ }
+ else
+ {
$titleObj = Title::newFromText($title);
if(!$titleObj)
$titleObj = Title::newFromText("API");
- $p_result = $wgParser->parse($text, $titleObj, new ParserOptions());
+ $p_result = $wgParser->parse($text, $titleObj, $popts);
}
// Return result
@@ -91,6 +116,8 @@ class ApiParse extends ApiBase {
$result_array['externallinks'] = array_keys($p_result->getExternalLinks());
if(isset($prop['sections']))
$result_array['sections'] = $p_result->getSections();
+ if(!is_null($oldid))
+ $result_array['revid'] = $oldid;
$result_mapping = array(
'langlinks' => 'll',
@@ -104,7 +131,7 @@ class ApiParse extends ApiBase {
$this->setIndexedTagNames( $result_array, $result_mapping );
$result->addValue( null, $this->getModuleName(), $result_array );
}
-
+
private function formatLangLinks( $links ) {
$result = array();
foreach( $links as $link ) {
@@ -116,7 +143,7 @@ class ApiParse extends ApiBase {
}
return $result;
}
-
+
private function formatCategoryLinks( $links ) {
$result = array();
foreach( $links as $link => $sortkey ) {
@@ -127,7 +154,7 @@ class ApiParse extends ApiBase {
}
return $result;
}
-
+
private function formatLinks( $links ) {
$result = array();
foreach( $links as $ns => $nslinks ) {
@@ -142,7 +169,7 @@ class ApiParse extends ApiBase {
}
return $result;
}
-
+
private function setIndexedTagNames( &$array, $mapping ) {
foreach( $mapping as $key => $name ) {
if( isset( $array[$key] ) )
@@ -152,13 +179,14 @@ class ApiParse extends ApiBase {
public function getAllowedParams() {
return array (
- 'title' => array(
+ 'title' => array(
ApiBase :: PARAM_DFLT => 'API',
),
'text' => null,
'page' => null,
+ 'oldid' => null,
'prop' => array(
- ApiBase :: PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections',
+ ApiBase :: PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid',
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array(
'text',
@@ -168,7 +196,8 @@ class ApiParse extends ApiBase {
'templates',
'images',
'externallinks',
- 'sections'
+ 'sections',
+ 'revid'
)
)
);
@@ -179,6 +208,7 @@ class ApiParse extends ApiBase {
'text' => 'Wikitext to parse',
'title' => 'Title of page the text belongs to',
'page' => 'Parse the content of this page. Cannot be used together with text and title',
+ 'oldid' => 'Parse the content of this revision. Overrides page',
'prop' => array('Which pieces of information to get.',
'NOTE: Section tree is only generated if there are more than 4 sections, or if the __TOC__ keyword is present'
),
@@ -196,7 +226,6 @@ class ApiParse extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiParse.php 30262 2008-01-29 14:47:27Z catrope $';
+ return __CLASS__ . ': $Id: ApiParse.php 36983 2008-07-03 15:01:50Z catrope $';
}
}
-
diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php
index 40a4b73d..30bcfdbc 100644
--- a/includes/api/ApiProtect.php
+++ b/includes/api/ApiProtect.php
@@ -28,7 +28,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiProtect extends ApiBase {
@@ -40,7 +40,7 @@ class ApiProtect extends ApiBase {
global $wgUser;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
-
+
$titleObj = NULL;
if(!isset($params['title']))
$this->dieUsageMsg(array('missingparam', 'title'));
@@ -55,12 +55,12 @@ class ApiProtect extends ApiBase {
$titleObj = Title::newFromText($params['title']);
if(!$titleObj)
$this->dieUsageMsg(array('invalidtitle', $params['title']));
-
+
$errors = $titleObj->getUserPermissionsErrors('protect', $wgUser);
if(!empty($errors))
// We don't care about multiple errors, just report one of them
$this->dieUsageMsg(current($errors));
-
+
if(in_array($params['expiry'], array('infinite', 'indefinite', 'never')))
$expiry = Block::infinity();
else
@@ -68,7 +68,7 @@ class ApiProtect extends ApiBase {
$expiry = strtotime($params['expiry']);
if($expiry < 0 || $expiry == false)
$this->dieUsageMsg(array('invalidexpiry'));
-
+
$expiry = wfTimestamp(TS_MW, $expiry);
if($expiry < wfTimestampNow())
$this->dieUsageMsg(array('pastexpiry'));
@@ -85,8 +85,6 @@ class ApiProtect extends ApiBase {
$this->dieUsageMsg(array('missingtitles-createonly'));
}
- $dbw = wfGetDb(DB_MASTER);
- $dbw->begin();
if($titleObj->exists()) {
$articleObj = new Article($titleObj);
$ok = $articleObj->updateRestrictions($protections, $params['reason'], $params['cascade'], $expiry);
@@ -96,7 +94,6 @@ class ApiProtect extends ApiBase {
// This is very weird. Maybe the article was deleted or the user was blocked/desysopped in the meantime?
// Just throw an unknown error in this case, as it's very likely to be a race condition
$this->dieUsageMsg(array());
- $dbw->commit();
$res = array('title' => $titleObj->getPrefixedText(), 'reason' => $params['reason']);
if($expiry == Block::infinity())
$res['expiry'] = 'infinity';
@@ -149,6 +146,6 @@ class ApiProtect extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiProtect.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiProtect.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index 29abd859..f4a2402f 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -33,11 +33,11 @@ if (!defined('MEDIAWIKI')) {
* it will create a list of titles to work on (an instance of the ApiPageSet object)
* instantiate and execute various property/list/meta modules,
* and assemble all resulting data into a single ApiResult object.
- *
+ *
* In the generator mode, a generator will be first executed to populate a second ApiPageSet object,
* and that object will be used for all subsequent modules.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQuery extends ApiBase {
@@ -55,9 +55,11 @@ class ApiQuery extends ApiBase {
'templates' => 'ApiQueryLinks',
'categories' => 'ApiQueryCategories',
'extlinks' => 'ApiQueryExternalLinks',
+ 'categoryinfo' => 'ApiQueryCategoryInfo',
);
private $mQueryListModules = array (
+ 'allimages' => 'ApiQueryAllimages',
'allpages' => 'ApiQueryAllpages',
'alllinks' => 'ApiQueryAllLinks',
'allcategories' => 'ApiQueryAllCategories',
@@ -90,7 +92,7 @@ class ApiQuery extends ApiBase {
public function __construct($main, $action) {
parent :: __construct($main, $action);
- // Allow custom modules to be added in LocalSettings.php
+ // Allow custom modules to be added in LocalSettings.php
global $wgApiQueryPropModules, $wgApiQueryListModules, $wgApiQueryMetaModules;
self :: appendUserModules($this->mQueryPropModules, $wgApiQueryPropModules);
self :: appendUserModules($this->mQueryListModules, $wgApiQueryListModules);
@@ -122,7 +124,7 @@ class ApiQuery extends ApiBase {
public function getDB() {
if (!isset ($this->mSlaveDB)) {
$this->profileDBIn();
- $this->mSlaveDB = wfGetDB(DB_SLAVE);
+ $this->mSlaveDB = wfGetDB(DB_SLAVE,'api');
$this->profileDBOut();
}
return $this->mSlaveDB;
@@ -130,9 +132,9 @@ class ApiQuery extends ApiBase {
/**
* Get the query database connection with the given name.
- * If no such connection has been requested before, it will be created.
- * Subsequent calls with the same $name will return the same connection
- * as the first, regardless of $db or $groups new values.
+ * If no such connection has been requested before, it will be created.
+ * Subsequent calls with the same $name will return the same connection
+ * as the first, regardless of $db or $groups new values.
*/
public function getNamedDB($name, $db, $groups) {
if (!array_key_exists($name, $this->mNamedDB)) {
@@ -149,7 +151,7 @@ class ApiQuery extends ApiBase {
public function getPageSet() {
return $this->mPageSet;
}
-
+
/**
* Get the array mapping module names to class names
*/
@@ -161,17 +163,17 @@ class ApiQuery extends ApiBase {
* Query execution happens in the following steps:
* #1 Create a PageSet object with any pages requested by the user
* #2 If using generator, execute it to get a new PageSet object
- * #3 Instantiate all requested modules.
+ * #3 Instantiate all requested modules.
* This way the PageSet object will know what shared data is required,
- * and minimize DB calls.
+ * and minimize DB calls.
* #4 Output all normalization and redirect resolution information
* #5 Execute all requested modules
*/
public function execute() {
-
+
$this->params = $this->extractRequestParams();
$this->redirects = $this->params['redirects'];
-
+
//
// Create PageSet
//
@@ -186,7 +188,7 @@ class ApiQuery extends ApiBase {
$this->InstantiateModules($modules, 'meta', $this->mQueryMetaModules);
//
- // If given, execute generator to substitute user supplied data with generated data.
+ // If given, execute generator to substitute user supplied data with generated data.
//
if (isset ($this->params['generator'])) {
$this->executeGeneratorModule($this->params['generator'], $modules);
@@ -210,21 +212,21 @@ class ApiQuery extends ApiBase {
$module->profileOut();
}
}
-
+
/**
* Query modules may optimize data requests through the $this->getPageSet() object
* by adding extra fields from the page table.
- * This function will gather all the extra request fields from the modules.
+ * This function will gather all the extra request fields from the modules.
*/
private function addCustomFldsToPageSet($modules, $pageSet) {
- // Query all requested modules.
+ // Query all requested modules.
foreach ($modules as $module) {
$module->requestExtraData($pageSet);
}
}
/**
- * Create instances of all modules requested by the client
+ * Create instances of all modules requested by the client
*/
private function InstantiateModules(&$modules, $param, $moduleList) {
$list = $this->params[$param];
@@ -235,7 +237,7 @@ class ApiQuery extends ApiBase {
/**
* Appends an element for each page in the current pageSet with the most general
- * information (id, title), plus any title normalizations and missing title/pageids/revids.
+ * information (id, title), plus any title normalizations and missing or invalid title/pageids/revids.
*/
private function outputGeneralPageInfo() {
@@ -255,7 +257,7 @@ class ApiQuery extends ApiBase {
$result->setIndexedTagName($normValues, 'n');
$result->addValue('query', 'normalized', $normValues);
}
-
+
// Interwiki titles
$intrwValues = array ();
foreach ($pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr) {
@@ -269,12 +271,12 @@ class ApiQuery extends ApiBase {
$result->setIndexedTagName($intrwValues, 'i');
$result->addValue('query', 'interwiki', $intrwValues);
}
-
+
// Show redirect information
$redirValues = array ();
foreach ($pageSet->getRedirectTitles() as $titleStrFrom => $titleStrTo) {
$redirValues[] = array (
- 'from' => $titleStrFrom,
+ 'from' => strval($titleStrFrom),
'to' => $titleStrTo
);
}
@@ -311,7 +313,9 @@ class ApiQuery extends ApiBase {
$vals['missing'] = '';
$pages[$fakeId] = $vals;
}
-
+ // Report any invalid titles
+ foreach ($pageSet->getInvalidTitles() as $fakeId => $title)
+ $pages[$fakeId] = array('title' => $title, 'invalid' => '');
// Report any missing page ids
foreach ($pageSet->getMissingPageIDs() as $pageid) {
$pages[$pageid] = array (
@@ -329,7 +333,7 @@ class ApiQuery extends ApiBase {
}
if (!empty ($pages)) {
-
+
if ($this->params['indexpageids']) {
$pageIDs = array_keys($pages);
// json treats all map keys as strings - converting to match
@@ -337,14 +341,14 @@ class ApiQuery extends ApiBase {
$result->setIndexedTagName($pageIDs, 'id');
$result->addValue('query', 'pageids', $pageIDs);
}
-
+
$result->setIndexedTagName($pages, 'page');
$result->addValue('query', 'pages', $pages);
}
}
/**
- * For generator mode, execute generator, and use its output as new pageSet
+ * For generator mode, execute generator, and use its output as new pageSet
*/
protected function executeGeneratorModule($generatorName, $modules) {
@@ -357,7 +361,7 @@ class ApiQuery extends ApiBase {
ApiBase :: dieDebug(__METHOD__, "Unknown generator=$generatorName");
}
- // Generator results
+ // Generator results
$resultPageSet = new ApiPageSet($this, $this->redirects);
// Create and execute the generator
@@ -386,7 +390,7 @@ class ApiQuery extends ApiBase {
/**
* Returns the list of allowed parameters for this module.
- * Qurey module also lists all ApiPageSet parameters as its own.
+ * Qurey module also lists all ApiPageSet parameters as its own.
*/
public function getAllowedParams() {
return array (
@@ -423,12 +427,14 @@ class ApiQuery extends ApiBase {
$this->mAllowedGenerators = array(); // Will be repopulated
$astriks = str_repeat('--- ', 8);
+ $astriks2 = str_repeat('*** ', 10);
$msg .= "\n$astriks Query: Prop $astriks\n\n";
$msg .= $this->makeHelpMsgHelper($this->mQueryPropModules, 'prop');
$msg .= "\n$astriks Query: List $astriks\n\n";
$msg .= $this->makeHelpMsgHelper($this->mQueryListModules, 'list');
$msg .= "\n$astriks Query: Meta $astriks\n\n";
$msg .= $this->makeHelpMsgHelper($this->mQueryMetaModules, 'meta');
+ $msg .= "\n\n$astriks2 Modules: continuation $astriks2\n\n";
// Perform the base call last because the $this->mAllowedGenerators
// will be updated inside makeHelpMsgHelper()
@@ -469,7 +475,7 @@ class ApiQuery extends ApiBase {
$psModule = new ApiPageSet($this);
return $psModule->makeHelpMsgParameters() . parent :: makeHelpMsgParameters();
}
-
+
// @todo should work correctly
public function shouldCheckMaxlag() {
return true;
@@ -503,9 +509,8 @@ class ApiQuery extends ApiBase {
public function getVersion() {
$psModule = new ApiPageSet($this);
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiQuery.php 30222 2008-01-28 19:05:26Z catrope $';
+ $vers[] = __CLASS__ . ': $Id: ApiQuery.php 35098 2008-05-20 17:13:28Z ialex $';
$vers[] = $psModule->getVersion();
return $vers;
}
}
-
diff --git a/includes/api/ApiQueryAllCategories.php b/includes/api/ApiQueryAllCategories.php
index 84494876..3ff42c88 100644
--- a/includes/api/ApiQueryAllCategories.php
+++ b/includes/api/ApiQueryAllCategories.php
@@ -31,8 +31,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to enumerate all categories, even the ones that don't have
* category pages.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryAllCategories extends ApiQueryGeneratorBase {
@@ -53,44 +53,58 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
$db = $this->getDB();
$params = $this->extractRequestParams();
- $this->addTables('categorylinks');
- $this->addFields('cl_to');
-
+ $this->addTables('category');
+ $this->addFields('cat_title');
+
if (!is_null($params['from']))
- $this->addWhere('cl_to>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
+ $this->addWhere('cat_title>=' . $db->addQuotes($this->titleToKey($params['from'])));
if (isset ($params['prefix']))
- $this->addWhere("cl_to LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("cat_title LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
$this->addOption('LIMIT', $params['limit']+1);
- $this->addOption('ORDER BY', 'cl_to' . ($params['dir'] == 'descending' ? ' DESC' : ''));
- $this->addOption('DISTINCT');
+ $this->addOption('ORDER BY', 'cat_title' . ($params['dir'] == 'descending' ? ' DESC' : ''));
+
+ $prop = array_flip($params['prop']);
+ $this->addFieldsIf( array( 'cat_pages', 'cat_subcats', 'cat_files' ), isset($prop['size']) );
+ $this->addFieldsIf( 'cat_hidden', isset($prop['hidden']) );
$res = $this->select(__METHOD__);
$pages = array();
+ $categories = array();
+ $result = $this->getResult();
$count = 0;
while ($row = $db->fetchObject($res)) {
if (++ $count > $params['limit']) {
// We've reached the one extra which shows that there are additional cats to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->cl_to));
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->cat_title));
break;
}
-
+
// Normalize titles
- $titleObj = Title::makeTitle(NS_CATEGORY, $row->cl_to);
+ $titleObj = Title::makeTitle(NS_CATEGORY, $row->cat_title);
if(!is_null($resultPageSet))
$pages[] = $titleObj->getPrefixedText();
- else
- // Don't show "Category:" everywhere in non-generator mode
- $pages[] = $titleObj->getText();
+ else {
+ $item = array();
+ $result->setContent( $item, $titleObj->getText() );
+ if( isset( $prop['size'] ) ) {
+ $item['size'] = $row->cat_pages;
+ $item['pages'] = $row->cat_pages - $row->cat_subcats - $row->cat_files;
+ $item['files'] = $row->cat_files;
+ $item['subcats'] = $row->cat_subcats;
+ }
+ if( isset( $prop['hidden'] ) && $row->cat_hidden )
+ $item['hidden'] = '';
+ $categories[] = $item;
+ }
}
$db->freeResult($res);
if (is_null($resultPageSet)) {
- $result = $this->getResult();
- $result->setIndexedTagName($pages, 'c');
- $result->addValue('query', $this->getModuleName(), $pages);
+ $result->setIndexedTagName($categories, 'c');
+ $result->addValue('query', $this->getModuleName(), $categories);
} else {
$resultPageSet->populateFromTitles($pages);
}
@@ -113,7 +127,12 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
ApiBase :: PARAM_MIN => 1,
ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
- )
+ ),
+ 'prop' => array (
+ ApiBase :: PARAM_TYPE => array( 'size', 'hidden' ),
+ ApiBase :: PARAM_DFLT => '',
+ ApiBase :: PARAM_ISMULTI => true
+ ),
);
}
@@ -122,7 +141,8 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
'from' => 'The category to start enumerating from.',
'prefix' => 'Search for all category titles that begin with this value.',
'dir' => 'Direction to sort in.',
- 'limit' => 'How many categories to return.'
+ 'limit' => 'How many categories to return.',
+ 'prop' => 'Which properties to get',
);
}
@@ -132,11 +152,12 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
protected function getExamples() {
return array (
+ 'api.php?action=query&list=allcategories&acprop=size',
'api.php?action=query&generator=allcategories&gacprefix=List&prop=info',
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllLinks.php 28216 2007-12-06 18:33:18Z vasilievvv $';
+ return __CLASS__ . ': $Id: ApiQueryAllCategories.php 36790 2008-06-29 22:26:23Z catrope $';
}
}
diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php
index d5b80644..aefbb725 100644
--- a/includes/api/ApiQueryAllLinks.php
+++ b/includes/api/ApiQueryAllLinks.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to enumerate links from all pages together.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryAllLinks extends ApiQueryGeneratorBase {
@@ -67,26 +67,37 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$this->addTables('pagelinks');
$this->addWhereFld('pl_namespace', $params['namespace']);
+ if (!is_null($params['from']) && !is_null($params['continue']))
+ $this->dieUsage('alcontinue and alfrom cannot be used together', 'params');
+ if (!is_null($params['continue']))
+ {
+ $arr = explode('|', $params['continue']);
+ if(count($arr) != 2)
+ $this->dieUsage("Invalid continue parameter", 'badcontinue');
+ $params['from'] = $arr[0]; // Handled later
+ $id = intval($arr[1]);
+ $this->addWhere("pl_from >= $id");
+ }
+
if (!is_null($params['from']))
- $this->addWhere('pl_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
+ $this->addWhere('pl_title>=' . $db->addQuotes($this->titleToKey($params['from'])));
if (isset ($params['prefix']))
- $this->addWhere("pl_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("pl_title LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
- if (is_null($resultPageSet)) {
- $this->addFields(array (
- 'pl_namespace',
- 'pl_title'
- ));
- $this->addFieldsIf('pl_from', $fld_ids);
- } else {
- $this->addFields('pl_from');
- $pageids = array();
- }
+ $this->addFields(array (
+ 'pl_namespace',
+ 'pl_title',
+ 'pl_from'
+ ));
$this->addOption('USE INDEX', 'pl_namespace');
$limit = $params['limit'];
$this->addOption('LIMIT', $limit+1);
- $this->addOption('ORDER BY', 'pl_namespace, pl_title');
+ # Only order by pl_namespace if it isn't constant in the WHERE clause
+ if(count($params['namespace']) != 1)
+ $this->addOption('ORDER BY', 'pl_namespace, pl_title');
+ else
+ $this->addOption('ORDER BY', 'pl_title');
$res = $this->select(__METHOD__);
@@ -96,7 +107,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->pl_title));
+ $this->setContinueEnumParameter('continue', $this->keyToTitle($row->pl_title) . "|" . $row->pl_from);
break;
}
@@ -127,6 +138,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
public function getAllowedParams() {
return array (
+ 'continue' => null,
'from' => null,
'prefix' => null,
'unique' => false,
@@ -159,7 +171,8 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
'unique' => 'Only show unique links. Cannot be used with generator or prop=ids',
'prop' => 'What pieces of information to include',
'namespace' => 'The namespace to enumerate.',
- 'limit' => 'How many total links to return.'
+ 'limit' => 'How many total links to return.',
+ 'continue' => 'When more results are available, use this to continue.',
);
}
@@ -174,6 +187,6 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllLinks.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllLinks.php 37258 2008-07-07 14:48:40Z catrope $';
}
}
diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php
index e055b3c5..dd0e98a8 100644
--- a/includes/api/ApiQueryAllUsers.php
+++ b/includes/api/ApiQueryAllUsers.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to enumerate all registered users.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryAllUsers extends ApiQueryBase {
@@ -46,26 +46,27 @@ class ApiQueryAllUsers extends ApiQueryBase {
$prop = $params['prop'];
if (!is_null($prop)) {
$prop = array_flip($prop);
+ $fld_blockinfo = isset($prop['blockinfo']);
$fld_editcount = isset($prop['editcount']);
$fld_groups = isset($prop['groups']);
$fld_registration = isset($prop['registration']);
- } else {
- $fld_editcount = $fld_groups = $fld_registration = false;
+ } else {
+ $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = false;
}
$limit = $params['limit'];
- $tables = $db->tableName('user');
-
+ $this->addTables('user', 'u1');
+
if( !is_null( $params['from'] ) )
- $this->addWhere( 'user_name >= ' . $db->addQuotes( self::keyToTitle( $params['from'] ) ) );
-
+ $this->addWhere( 'u1.user_name >= ' . $db->addQuotes( $this->keyToTitle( $params['from'] ) ) );
+
if( isset( $params['prefix'] ) )
- $this->addWhere( 'user_name LIKE "' . $db->escapeLike( self::keyToTitle( $params['prefix'] ) ) . '%"' );
+ $this->addWhere( 'u1.user_name LIKE "' . $db->escapeLike( $this->keyToTitle( $params['prefix'] ) ) . '%"' );
if (!is_null($params['group'])) {
// Filter only users that belong to a given group
- $tblName = $db->tableName('user_groups');
- $tables = "$tables INNER JOIN $tblName ug1 ON ug1.ug_user=user_id";
+ $this->addTables('user_groups', 'ug1');
+ $this->addWhere('ug1.ug_user=u1.user_id');
$this->addWhereFld('ug1.ug_group', $params['group']);
}
@@ -75,23 +76,30 @@ class ApiQueryAllUsers extends ApiQueryBase {
$groupCount = count(User::getAllGroups());
$sqlLimit = $limit+$groupCount+1;
- $tblName = $db->tableName('user_groups');
- $tables = "$tables LEFT JOIN $tblName ug2 ON ug2.ug_user=user_id";
+ $this->addTables('user_groups', 'ug2');
+ $tname = $this->getAliasedName('user_groups', 'ug2');
+ $this->addJoinConds(array($tname => array('LEFT JOIN', 'ug2.ug_user=u1.user_id')));
$this->addFields('ug2.ug_group ug_group2');
} else {
$sqlLimit = $limit+1;
}
-
- if ($fld_registration)
- $this->addFields('user_registration');
+ if ($fld_blockinfo) {
+ $this->addTables('ipblocks');
+ $this->addTables('user', 'u2');
+ $u2 = $this->getAliasedName('user', 'u2');
+ $this->addJoinConds(array(
+ 'ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'),
+ $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id')));
+ $this->addFields(array('ipb_reason', 'u2.user_name blocker_name'));
+ }
$this->addOption('LIMIT', $sqlLimit);
- $this->addTables($tables);
- $this->addFields('user_name');
- $this->addFieldsIf('user_editcount', $fld_editcount);
+ $this->addFields('u1.user_name');
+ $this->addFieldsIf('u1.user_editcount', $fld_editcount);
+ $this->addFieldsIf('u1.user_registration', $fld_registration);
- $this->addOption('ORDER BY', 'user_name');
+ $this->addOption('ORDER BY', 'u1.user_name');
$res = $this->select(__METHOD__);
@@ -100,7 +108,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
$lastUserData = false;
$lastUser = false;
$result = $this->getResult();
-
+
//
// This loop keeps track of the last entry.
// For each new row, if the new row is for different user then the last, the last entry is added to results.
@@ -109,49 +117,53 @@ class ApiQueryAllUsers extends ApiQueryBase {
// to make sure all rows that belong to the same user are received.
//
while (true) {
-
+
$row = $db->fetchObject($res);
$count++;
-
+
if (!$row || $lastUser != $row->user_name) {
// Save the last pass's user data
if (is_array($lastUserData))
$data[] = $lastUserData;
-
+
// No more rows left
if (!$row)
break;
if ($count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->user_name));
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->user_name));
break;
}
// Record new user's data
$lastUser = $row->user_name;
$lastUserData = array( 'name' => $lastUser );
+ if ($fld_blockinfo) {
+ $lastUserData['blockedby'] = $row->blocker_name;
+ $lastUserData['blockreason'] = $row->ipb_reason;
+ }
if ($fld_editcount)
$lastUserData['editcount'] = intval($row->user_editcount);
if ($fld_registration)
$lastUserData['registration'] = wfTimestamp(TS_ISO_8601, $row->user_registration);
-
+
}
-
+
if ($sqlLimit == $count) {
// BUG! database contains group name that User::getAllGroups() does not return
// TODO: should handle this more gracefully
- ApiBase :: dieDebug(__METHOD__,
+ ApiBase :: dieDebug(__METHOD__,
'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function');
}
-
+
// Add user's group info
if ($fld_groups && !is_null($row->ug_group2)) {
$lastUserData['groups'][] = $row->ug_group2;
$result->setIndexedTagName($lastUserData['groups'], 'g');
}
}
-
+
$db->freeResult($res);
$result->setIndexedTagName($data, 'u');
@@ -166,11 +178,12 @@ class ApiQueryAllUsers extends ApiQueryBase {
ApiBase :: PARAM_TYPE => User::getAllGroups()
),
'prop' => array (
- ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array (
- 'editcount',
+ 'blockinfo',
'groups',
- 'registration',
+ 'editcount',
+ 'registration'
)
),
'limit' => array (
@@ -206,6 +219,6 @@ class ApiQueryAllUsers extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllUsers.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllUsers.php 36790 2008-06-29 22:26:23Z catrope $';
}
}
diff --git a/includes/api/ApiQueryAllimages.php b/includes/api/ApiQueryAllimages.php
new file mode 100644
index 00000000..26cbc368
--- /dev/null
+++ b/includes/api/ApiQueryAllimages.php
@@ -0,0 +1,205 @@
+<?php
+
+/*
+ * Created on Mar 16, 2008
+ *
+ * API for MediaWiki 1.12+
+ *
+ * Copyright (C) 2008 Vasiliev Victor vasilvv@gmail.com,
+ * based on ApiQueryAllpages.php
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ('ApiQueryBase.php');
+}
+
+/**
+ * Query module to enumerate all available pages.
+ *
+ * @ingroup API
+ */
+class ApiQueryAllimages extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'ai');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ if ($resultPageSet->isResolvingRedirects())
+ $this->dieUsage('Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator', 'params');
+
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+ $repo = RepoGroup::singleton()->getLocalRepo();
+ if ( !$repo instanceof LocalRepo )
+ $this->dieUsage('Local file repository does not support querying all images', 'unsupportedrepo');
+
+ $db = $this->getDB();
+
+ $params = $this->extractRequestParams();
+
+ // Image filters
+ if (!is_null($params['from']))
+ $this->addWhere('img_name>=' . $db->addQuotes($this->titleToKey($params['from'])));
+ if (isset ($params['prefix']))
+ $this->addWhere("img_name LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
+
+ if (isset ($params['minsize'])) {
+ $this->addWhere('img_size>=' . intval($params['minsize']));
+ }
+
+ if (isset ($params['maxsize'])) {
+ $this->addWhere('img_size<=' . intval($params['maxsize']));
+ }
+
+ $sha1 = false;
+ if( isset( $params['sha1'] ) ) {
+ $sha1 = wfBaseConvert( $params['sha1'], 16, 36, 31 );
+ } elseif( isset( $params['sha1base36'] ) ) {
+ $sha1 = $params['sha1base36'];
+ }
+ if( $sha1 ) {
+ $this->addWhere( 'img_sha1=' . $db->addQuotes( $sha1 ) );
+ }
+
+ $this->addTables('image');
+
+ $prop = array_flip($params['prop']);
+ $this->addFields( LocalFile::selectFields() );
+
+ $limit = $params['limit'];
+ $this->addOption('LIMIT', $limit+1);
+ $this->addOption('ORDER BY', 'img_name' .
+ ($params['dir'] == 'descending' ? ' DESC' : ''));
+
+ $res = $this->select(__METHOD__);
+
+ $data = array ();
+ $count = 0;
+ $result = $this->getResult();
+ while ($row = $db->fetchObject($res)) {
+ if (++ $count > $limit) {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ // TODO: Security issue - if the user has no right to view next title, it will still be shown
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->img_name));
+ break;
+ }
+
+ if (is_null($resultPageSet)) {
+ $file = $repo->newFileFromRow( $row );
+
+ $data[] = ApiQueryImageInfo::getInfo( $file, $prop, $result );
+ } else {
+ $data[] = Title::makeTitle( NS_IMAGE, $row->img_name );
+ }
+ }
+ $db->freeResult($res);
+
+ if (is_null($resultPageSet)) {
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, 'img');
+ $result->addValue('query', $this->getModuleName(), $data);
+ } else {
+ $resultPageSet->populateFromTitles( $data );
+ }
+ }
+
+ public function getAllowedParams() {
+ return array (
+ 'from' => null,
+ 'prefix' => null,
+ 'minsize' => array (
+ ApiBase :: PARAM_TYPE => 'integer',
+ ),
+ 'maxsize' => array (
+ ApiBase :: PARAM_TYPE => 'integer',
+ ),
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'dir' => array (
+ ApiBase :: PARAM_DFLT => 'ascending',
+ ApiBase :: PARAM_TYPE => array (
+ 'ascending',
+ 'descending'
+ )
+ ),
+ 'sha1' => null,
+ 'sha1base36' => null,
+ 'prop' => array (
+ ApiBase :: PARAM_TYPE => array(
+ 'timestamp',
+ 'user',
+ 'comment',
+ 'url',
+ 'size',
+ 'dimensions', // Obsolete
+ 'mime',
+ 'sha1',
+ 'metadata'
+ ),
+ ApiBase :: PARAM_DFLT => 'timestamp|url',
+ ApiBase :: PARAM_ISMULTI => true
+ )
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'from' => 'The image title to start enumerating from.',
+ 'prefix' => 'Search for all image titles that begin with this value.',
+ 'dir' => 'The direction in which to list',
+ 'minsize' => 'Limit to images with at least this many bytes',
+ 'maxsize' => 'Limit to images with at most this many bytes',
+ 'limit' => 'How many total images to return.',
+ 'sha1' => 'SHA1 hash of image',
+ 'sha1base36' => 'SHA1 hash of image in base 36 (used in MediaWiki)',
+ 'prop' => 'Which properties to get',
+ );
+ }
+
+ public function getDescription() {
+ return 'Enumerate all images sequentially';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'Simple Use',
+ ' Show a list of images starting at the letter "B"',
+ ' api.php?action=query&list=allimages&aifrom=B',
+ 'Using as Generator',
+ ' Show info about 4 images starting at the letter "T"',
+ ' api.php?action=query&generator=allimages&gailimit=4&gaifrom=T&prop=imageinfo',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryAllimages.php 37909 2008-07-22 13:26:15Z catrope $';
+ }
+}
diff --git a/includes/api/ApiQueryAllmessages.php b/includes/api/ApiQueryAllmessages.php
index b7c86a91..06683379 100644
--- a/includes/api/ApiQueryAllmessages.php
+++ b/includes/api/ApiQueryAllmessages.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query action to return messages from site message cache
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryAllmessages extends ApiQueryBase {
@@ -42,13 +42,13 @@ class ApiQueryAllmessages extends ApiQueryBase {
public function execute() {
global $wgMessageCache;
$params = $this->extractRequestParams();
-
+
if(!is_null($params['lang']))
{
global $wgLang;
$wgLang = Language::factory($params['lang']);
}
-
+
//Determine which messages should we print
$messages_target = array();
@@ -60,7 +60,7 @@ class ApiQueryAllmessages extends ApiQueryBase {
} else {
$messages_target = explode( '|', $params['messages'] );
}
-
+
//Filter messages
if( isset( $params['filter'] ) ) {
$messages_filtered = array();
@@ -72,12 +72,9 @@ class ApiQueryAllmessages extends ApiQueryBase {
$messages_target = $messages_filtered;
}
- $wgMessageCache->disableTransform();
-
//Get all requested messages
$messages = array();
foreach( $messages_target as $message ) {
- $message = trim( $message ); //Message list can be formatted like "msg1 | msg2 | msg3", so let's trim() it
$messages[$message] = wfMsg( $message );
}
@@ -87,7 +84,11 @@ class ApiQueryAllmessages extends ApiQueryBase {
foreach( $messages as $name => $value ) {
$message = array();
$message['name'] = $name;
- $result->setContent( $message, $value );
+ if( wfEmptyMsg( $name, $value ) ) {
+ $message['missing'] = '';
+ } else {
+ $result->setContent( $message, $value );
+ }
$messages_out[] = $message;
}
$result->setIndexedTagName( $messages_out, 'message' );
@@ -107,8 +108,8 @@ class ApiQueryAllmessages extends ApiQueryBase {
public function getParamDescription() {
return array (
'messages' => 'Which messages to output. "*" means all messages',
- 'filter' => 'Return only messages that contains specified string',
- 'lang' => 'Language code',
+ 'filter' => 'Return only messages that contain this string',
+ 'lang' => 'Return messages in this language',
);
}
@@ -124,6 +125,6 @@ class ApiQueryAllmessages extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllmessages.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllmessages.php 37504 2008-07-10 14:28:09Z catrope $';
}
}
diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php
index 280d1de2..39490fe7 100644
--- a/includes/api/ApiQueryAllpages.php
+++ b/includes/api/ApiQueryAllpages.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to enumerate all available pages.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryAllpages extends ApiQueryGeneratorBase {
@@ -55,27 +55,29 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$db = $this->getDB();
$params = $this->extractRequestParams();
-
+
// Page filters
+ $this->addTables('page');
if (!$this->addWhereIf('page_is_redirect = 1', $params['filterredir'] === 'redirects'))
$this->addWhereIf('page_is_redirect = 0', $params['filterredir'] === 'nonredirects');
$this->addWhereFld('page_namespace', $params['namespace']);
- if (!is_null($params['from']))
- $this->addWhere('page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
+ $dir = ($params['dir'] == 'descending' ? 'older' : 'newer');
+ $from = (is_null($params['from']) ? null : $this->titleToKey($params['from']));
+ $this->addWhereRange('page_title', $dir, $from, null);
if (isset ($params['prefix']))
- $this->addWhere("page_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("page_title LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
$forceNameTitleIndex = true;
if (isset ($params['minsize'])) {
$this->addWhere('page_len>=' . intval($params['minsize']));
$forceNameTitleIndex = false;
}
-
+
if (isset ($params['maxsize'])) {
$this->addWhere('page_len<=' . intval($params['maxsize']));
$forceNameTitleIndex = false;
}
-
+
// Page protection filtering
if (isset ($params['prtype'])) {
$this->addTables('page_restrictions');
@@ -86,7 +88,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$prlevel = $params['prlevel'];
if (!is_null($prlevel) && $prlevel != '' && $prlevel != '*')
$this->addWhereFld('pr_level', $prlevel);
-
+
$this->addOption('DISTINCT');
$forceNameTitleIndex = false;
@@ -94,20 +96,16 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
} else if (isset ($params['prlevel'])) {
$this->dieUsage('prlevel may not be used without prtype', 'params');
}
-
+
if($params['filterlanglinks'] == 'withoutlanglinks') {
- $pageName = $this->getDB()->tableName('page');
- $llName = $this->getDB()->tableName('langlinks');
- $tables = "$pageName LEFT JOIN $llName ON page_id=ll_from";
+ $this->addTables('langlinks');
+ $this->addJoinConds(array('langlinks' => array('LEFT JOIN', 'page_id=ll_from')));
$this->addWhere('ll_from IS NULL');
- $this->addTables($tables);
$forceNameTitleIndex = false;
} else if($params['filterlanglinks'] == 'withlanglinks') {
- $this->addTables(array('page', 'langlinks'));
+ $this->addTables('langlinks');
$this->addWhere('page_id=ll_from');
- $forceNameTitleIndex = false;
- } else {
- $this->addTables('page');
+ $forceNameTitleIndex = false;
}
if ($forceNameTitleIndex)
$this->addOption('USE INDEX', 'name_title');
@@ -124,9 +122,6 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$limit = $params['limit'];
$this->addOption('LIMIT', $limit+1);
- $this->addOption('ORDER BY', 'page_namespace, page_title' .
- ($params['dir'] == 'descending' ? ' DESC' : ''));
-
$res = $this->select(__METHOD__);
$data = array ();
@@ -135,7 +130,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->page_title));
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->page_title));
break;
}
@@ -160,7 +155,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
public function getAllowedParams() {
global $wgRestrictionTypes, $wgRestrictionLevels;
-
+
return array (
'from' => null,
'prefix' => null,
@@ -178,10 +173,10 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
),
'minsize' => array (
ApiBase :: PARAM_TYPE => 'integer',
- ),
+ ),
'maxsize' => array (
ApiBase :: PARAM_TYPE => 'integer',
- ),
+ ),
'prtype' => array (
ApiBase :: PARAM_TYPE => $wgRestrictionTypes,
ApiBase :: PARAM_ISMULTI => true
@@ -249,7 +244,6 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllpages.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllpages.php 37775 2008-07-17 09:26:01Z brion $';
}
}
-
diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php
index 1ca5c33a..fea058f3 100644
--- a/includes/api/ApiQueryBacklinks.php
+++ b/includes/api/ApiQueryBacklinks.php
@@ -29,18 +29,18 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * This is three-in-one module to query:
+ * This is a three-in-one module to query:
* * backlinks - links pointing to the given page,
* * embeddedin - what pages transclude the given page within themselves,
* * imageusage - what pages use the given image
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryBacklinks extends ApiQueryGeneratorBase {
- private $params, $rootTitle, $contRedirs, $contLevel, $contTitle, $contID;
+ private $params, $rootTitle, $contRedirs, $contLevel, $contTitle, $contID, $redirID;
- // output element name, database column field prefix, database table
+ // output element name, database column field prefix, database table
private $backlinksSettings = array (
'backlinks' => array (
'code' => 'bl',
@@ -66,10 +66,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
parent :: __construct($query, $moduleName, $code);
$this->bl_ns = $prefix . '_namespace';
$this->bl_from = $prefix . '_from';
- $this->bl_tables = array (
- $linktbl,
- 'page'
- );
+ $this->bl_table = $linktbl;
$this->bl_code = $code;
$this->hasNS = $moduleName !== 'imageusage';
@@ -97,207 +94,219 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->run($resultPageSet);
}
- private function run($resultPageSet = null) {
- $this->params = $this->extractRequestParams();
-
- $redirect = $this->params['redirect'];
- if ($redirect)
- $this->dieDebug('Redirect has not been implemented', 'notimplemented');
-
- $this->processContinue();
-
- $this->addFields($this->bl_fields);
- if (is_null($resultPageSet))
- $this->addFields(array (
- 'page_id',
- 'page_namespace',
- 'page_title'
- ));
+ private function prepareFirstQuery($resultPageSet = null) {
+ /* SELECT page_id, page_title, page_namespace, page_is_redirect
+ * FROM pagelinks, page WHERE pl_from=page_id
+ * AND pl_title='Foo' AND pl_namespace=0
+ * LIMIT 11 ORDER BY pl_from
+ */
+ $db = $this->getDb();
+ $this->addTables(array('page', $this->bl_table));
+ $this->addWhere("{$this->bl_from}=page_id");
+ if(is_null($resultPageSet))
+ $this->addFields(array('page_id', 'page_title', 'page_namespace'));
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->addFields($resultPageSet->getPageTableFields());
+ $this->addFields('page_is_redirect');
+ $this->addWhereFld($this->bl_title, $this->rootTitle->getDbKey());
+ if($this->hasNS)
$this->addWhereFld($this->bl_ns, $this->rootTitle->getNamespace());
- $this->addWhereFld($this->bl_title, $this->rootTitle->getDBkey());
$this->addWhereFld('page_namespace', $this->params['namespace']);
-
+ if(!is_null($this->contID))
+ $this->addWhere("page_id>={$this->contID}");
if($this->params['filterredir'] == 'redirects')
$this->addWhereFld('page_is_redirect', 1);
if($this->params['filterredir'] == 'nonredirects')
$this->addWhereFld('page_is_redirect', 0);
+ $this->addOption('LIMIT', $this->params['limit'] + 1);
+ $this->addOption('ORDER BY', $this->bl_from);
+ }
- $limit = $this->params['limit'];
- $this->addOption('LIMIT', $limit +1);
+ private function prepareSecondQuery($resultPageSet = null) {
+ /* SELECT page_id, page_title, page_namespace, page_is_redirect, pl_title, pl_namespace
+ * FROM pagelinks, page WHERE pl_from=page_id
+ * AND (pl_title='Foo' AND pl_namespace=0) OR (pl_title='Bar' AND pl_namespace=1)
+ * LIMIT 11 ORDER BY pl_namespace, pl_title, pl_from
+ */
+ $db = $this->getDb();
+ $this->addTables(array('page', $this->bl_table));
+ $this->addWhere("{$this->bl_from}=page_id");
+ if(is_null($resultPageSet))
+ $this->addFields(array('page_id', 'page_title', 'page_namespace', 'page_is_redirect'));
+ else
+ $this->addFields($resultPageSet->getPageTableFields());
+ $this->addFields($this->bl_title);
+ if($this->hasNS)
+ $this->addFields($this->bl_ns);
+ $titleWhere = '';
+ foreach($this->redirTitles as $t)
+ $titleWhere .= ($titleWhere != '' ? " OR " : '') .
+ "({$this->bl_title} = ".$db->addQuotes($t->getDBKey()).
+ ($this->hasNS ? " AND {$this->bl_ns} = '{$t->getNamespace()}'" : "") .
+ ")";
+ $this->addWhere($titleWhere);
+ $this->addWhereFld('page_namespace', $this->params['namespace']);
+ if(!is_null($this->redirID))
+ $this->addWhere("page_id>={$this->redirID}");
+ if($this->params['filterredir'] == 'redirects')
+ $this->addWhereFld('page_is_redirect', 1);
+ if($this->params['filterredir'] == 'nonredirects')
+ $this->addWhereFld('page_is_redirect', 0);
+ $this->addOption('LIMIT', $this->params['limit'] + 1);
$this->addOption('ORDER BY', $this->bl_sort);
+ }
- $db = $this->getDB();
- if (!is_null($this->params['continue'])) {
- $plfrm = intval($this->contID);
- if ($this->contLevel == 0) {
- // For the first level, there is only one target title, so no need for complex filtering
- $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);
- }
+ private function run($resultPageSet = null) {
+ $this->params = $this->extractRequestParams(false);
+ $this->redirect = isset($this->params['redirect']) && $this->params['redirect'];
+ $userMax = ( $this->redirect ? ApiBase::LIMIT_BIG1/2 : ApiBase::LIMIT_BIG1 );
+ $botMax = ( $this->redirect ? ApiBase::LIMIT_BIG2/2 : ApiBase::LIMIT_BIG2 );
+ if( $this->params['limit'] == 'max' ) {
+ $this->params['limit'] = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
+ $this->getResult()->addValue( 'limits', $this->getModuleName(), $this->params['limit'] );
}
+ $this->processContinue();
+ $this->prepareFirstQuery($resultPageSet);
+
+ $db = $this->getDB();
$res = $this->select(__METHOD__);
$count = 0;
- $data = array ();
+ $this->data = array ();
+ $this->continueStr = null;
+ $this->redirTitles = array();
while ($row = $db->fetchObject($res)) {
- if (++ $count > $limit) {
+ if (++ $count > $this->params['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);
- // TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter('continue', $continue);
+ // Continue string preserved in case the redirect query doesn't pass the limit
+ $this->continueStr = $this->getContinueStr($row->page_id);
break;
}
- if (is_null($resultPageSet)) {
- $vals = $this->extractRowInfo($row);
- if ($vals)
- $data[] = $vals;
- } else {
+ if (is_null($resultPageSet))
+ $this->extractRowInfo($row);
+ else
+ {
+ if($row->page_is_redirect)
+ $this->redirTitles[] = Title::makeTitle($row->page_namespace, $row->page_title);
$resultPageSet->processDbRow($row);
}
}
$db->freeResult($res);
+ if($this->redirect && !empty($this->redirTitles))
+ {
+ $this->resetQueryParams();
+ $this->prepareSecondQuery($resultPageSet);
+ $res = $this->select(__METHOD__);
+ $count = 0;
+ while($row = $db->fetchObject($res))
+ {
+ if(++$count > $this->params['limit'])
+ {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ // We need to keep the parent page of this redir in
+ if($this->hasNS)
+ $contTitle = Title::makeTitle($row->{$this->bl_ns}, $row->{$this->bl_title});
+ else
+ $contTitle = Title::makeTitle(NS_IMAGE, $row->{$this->bl_title});
+ $this->continueStr = $this->getContinueRedirStr($contTitle->getArticleID(), $row->page_id);
+ break;
+ }
+
+ if(is_null($resultPageSet))
+ $this->extractRedirRowInfo($row);
+ else
+ $resultPageSet->processDbRow($row);
+ }
+ $db->freeResult($res);
+ }
+ if(!is_null($this->continueStr))
+ $this->setContinueEnumParameter('continue', $this->continueStr);
+
if (is_null($resultPageSet)) {
+ $resultData = array();
+ foreach($this->data as $ns => $a)
+ foreach($a as $title => $arr)
+ $resultData[$arr['pageid']] = $arr;
$result = $this->getResult();
- $result->setIndexedTagName($data, $this->bl_code);
- $result->addValue('query', $this->getModuleName(), $data);
+ $result->setIndexedTagName($resultData, $this->bl_code);
+ $result->addValue('query', $this->getModuleName(), $resultData);
}
}
private function extractRowInfo($row) {
+ if(!isset($this->data[$row->page_namespace][$row->page_title])) {
+ $this->data[$row->page_namespace][$row->page_title]['pageid'] = $row->page_id;
+ ApiQueryBase::addTitleInfo($this->data[$row->page_namespace][$row->page_title], Title::makeTitle($row->page_namespace, $row->page_title));
+ if($row->page_is_redirect)
+ {
+ $this->data[$row->page_namespace][$row->page_title]['redirect'] = '';
+ $this->redirTitles[] = Title::makeTitle($row->page_namespace, $row->page_title);
+ }
+ }
+ }
- $vals = array();
- $vals['pageid'] = intval($row->page_id);
- ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->page_namespace, $row->page_title));
-
- return $vals;
+ private function extractRedirRowInfo($row)
+ {
+ $a['pageid'] = $row->page_id;
+ ApiQueryBase::addTitleInfo($a, Title::makeTitle($row->page_namespace, $row->page_title));
+ if($row->page_is_redirect)
+ $a['redirect'] = '';
+ $ns = $this->hasNS ? $row->{$this->bl_ns} : NS_IMAGE;
+ $this->data[$ns][$row->{$this->bl_title}]['redirlinks'][] = $a;
+ $this->getResult()->setIndexedTagName($this->data[$ns][$row->{$this->bl_title}]['redirlinks'], $this->bl_code);
}
protected function processContinue() {
- $pageSet = $this->getPageSet();
- $count = $pageSet->getTitleCount();
-
- if (!is_null($this->params['continue'])) {
+ if (!is_null($this->params['continue']))
$this->parseContinueParam();
-
- // Skip all completed links
-
- } else {
- $title = $this->params['title'];
- if (!is_null($title)) {
- $this->rootTitle = Title :: newFromText($title);
- } else { // This case is obsolete. Will support this for a while
- if ($count !== 1)
- $this->dieUsage("The {$this->getModuleName()} query requires one title to start", 'bad_title_count');
- $this->rootTitle = current($pageSet->getTitles()); // only one title there
- $this->setWarning('Using titles parameter is obsolete for this list. Use ' . $this->encodeParamName('title') . ' instead.');
+ else {
+ if ( $this->params['title'] !== "" ) {
+ $title = Title::newFromText( $this->params['title'] );
+ if ( !$title ) {
+ $this->dieUsageMsg(array('invalidtitle', $this->params['title']));
+ } else {
+ $this->rootTitle = $title;
+ }
+ } else {
+ $this->dieUsageMsg(array('missingparam', 'title'));
}
}
- // only image titles are allowed for the root
+ // only image titles are allowed for the root in imageinfo mode
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() {
$continueList = explode('|', $this->params['continue']);
- if ($this->params['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) {
-
- $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) {
-
- $contID = intval($continueList[2]);
- if ($contID !== 0) {
- $this->contID = $contID;
- return; // done
- }
- }
- }
- }
- }
+ // expected format:
+ // ns | key | id1 [| id2]
+ // ns+key: root title
+ // id1: first-level page ID to continue from
+ // id2: second-level page ID to continue from
+
+ // null stuff out now so we know what's set and what isn't
+ $this->rootTitle = $this->contID = $this->redirID = null;
+ $rootNs = intval($continueList[0]);
+ if($rootNs === 0 && $continueList[0] !== '0')
+ // Illegal continue parameter
+ $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue");
+ $this->rootTitle = Title::makeTitleSafe($rootNs, $continueList[1]);
+ if(!$this->rootTitle)
+ $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue");
+ $contID = intval($continueList[2]);
+ if($contID === 0 && $continueList[2] !== '0')
+ $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue");
+ $this->contID = $contID;
+ $redirID = intval(@$continueList[3]);
+ if($redirID === 0 && @$continueList[3] !== '0')
+ // This one isn't required
+ return;
+ $this->redirID = $redirID;
- $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue");
}
protected function getContinueStr($lastPageID) {
@@ -306,18 +315,12 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
'|' . $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 getContinueRedirStr($lastPageID, $lastRedirID) {
+ return $this->getContinueStr($lastPageID) . '|' . $lastRedirID;
}
public function getAllowedParams() {
-
- return array (
+ $retval = array (
'title' => null,
'continue' => null,
'namespace' => array (
@@ -332,7 +335,6 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
'nonredirects'
)
),
- 'redirect' => false,
'limit' => array (
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
@@ -341,17 +343,27 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
)
);
+ if($this->getModuleName() == 'embeddedin')
+ return $retval;
+ $retval['redirect'] = false;
+ return $retval;
}
public function getParamDescription() {
- return array (
+ $retval = array (
'title' => 'Title to search. If null, titles= parameter will be used instead, but will be obsolete soon.',
'continue' => 'When more results are available, use this to continue.',
'namespace' => 'The namespace to enumerate.',
- 'filterredir' => 'How to filter for redirects',
- 'redirect' => 'If linking page is a redirect, find all pages that link to that redirect (not implemented)',
- 'limit' => 'How many total pages to return.'
+ 'filterredir' => 'How to filter for redirects'
);
+ if($this->getModuleName() != 'embeddedin')
+ return array_merge($retval, array(
+ 'redirect' => 'If linking page is a redirect, find all pages that link to that redirect as well. Maximum limit is halved.',
+ 'limit' => "How many total pages to return. If {$this->bl_code}redirect is enabled, limit applies to each level separately."
+ ));
+ return array_merge($retval, array(
+ 'limit' => "How many total pages to return."
+ ));
}
public function getDescription() {
@@ -387,7 +399,6 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryBacklinks.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryBacklinks.php 37504 2008-07-10 14:28:09Z catrope $';
}
}
-
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php
index 031e3c02..f392186b 100644
--- a/includes/api/ApiQueryBase.php
+++ b/includes/api/ApiQueryBase.php
@@ -31,12 +31,12 @@ if (!defined('MEDIAWIKI')) {
/**
* This is a base class for all Query modules.
* It provides some common functionality such as constructing various SQL queries.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
abstract class ApiQueryBase extends ApiBase {
- private $mQueryModule, $mDb, $tables, $where, $fields, $options;
+ private $mQueryModule, $mDb, $tables, $where, $fields, $options, $join_conds;
public function __construct($query, $moduleName, $paramPrefix = '') {
parent :: __construct($query->getMain(), $moduleName, $paramPrefix);
@@ -45,13 +45,22 @@ abstract class ApiQueryBase extends ApiBase {
$this->resetQueryParams();
}
+ /**
+ * Blank the internal arrays with query parameters
+ */
protected function resetQueryParams() {
$this->tables = array ();
$this->where = array ();
$this->fields = array ();
$this->options = array ();
+ $this->join_conds = array ();
}
+ /**
+ * Add a set of tables to the internal array
+ * @param mixed $tables Table name or array of table names
+ * @param mixed $alias Table alias, or null for no alias. Cannot be used with multiple tables
+ */
protected function addTables($tables, $alias = null) {
if (is_array($tables)) {
if (!is_null($alias))
@@ -59,11 +68,38 @@ abstract class ApiQueryBase extends ApiBase {
$this->tables = array_merge($this->tables, $tables);
} else {
if (!is_null($alias))
- $tables = $this->getDB()->tableName($tables) . ' ' . $alias;
+ $tables = $this->getAliasedName($tables, $alias);
$this->tables[] = $tables;
}
}
+
+ /**
+ * Get the SQL for a table name with alias
+ * @param string $table Table name
+ * @param string $alias Alias
+ * @return string SQL
+ */
+ protected function getAliasedName($table, $alias) {
+ return $this->getDB()->tableName($table) . ' ' . $alias;
+ }
+
+ /**
+ * Add a set of JOIN conditions to the internal array
+ *
+ * JOIN conditions are formatted as array( tablename => array(jointype, conditions)
+ * e.g. array('page' => array('LEFT JOIN', 'page_id=rev_page'))
+ * @param array $join_conds JOIN conditions
+ */
+ protected function addJoinConds($join_conds) {
+ if(!is_array($join_conds))
+ ApiBase::dieDebug(__METHOD__, 'Join conditions have to be arrays');
+ $this->join_conds = array_merge($this->join_conds, $join_conds);
+ }
+ /**
+ * Add a set of fields to select to the internal array
+ * @param mixed $value Field name or array of field names
+ */
protected function addFields($value) {
if (is_array($value))
$this->fields = array_merge($this->fields, $value);
@@ -71,6 +107,12 @@ abstract class ApiQueryBase extends ApiBase {
$this->fields[] = $value;
}
+ /**
+ * Same as addFields(), but add the fields only if a condition is met
+ * @param mixed $value See addFields()
+ * @param bool $condition If false, do nothing
+ * @return bool $condition
+ */
protected function addFieldsIf($value, $condition) {
if ($condition) {
$this->addFields($value);
@@ -79,6 +121,15 @@ abstract class ApiQueryBase extends ApiBase {
return false;
}
+ /**
+ * Add a set of WHERE clauses to the internal array.
+ * Clauses can be formatted as 'foo=bar' or array('foo' => 'bar'),
+ * the latter only works if the value is a constant (i.e. not another field)
+ *
+ * For example, array('foo=bar', 'baz' => 3, 'bla' => 'foo') translates
+ * to "foo=bar AND baz='3' AND bla='foo'"
+ * @param mixed $value String or array
+ */
protected function addWhere($value) {
if (is_array($value))
$this->where = array_merge($this->where, $value);
@@ -86,6 +137,12 @@ abstract class ApiQueryBase extends ApiBase {
$this->where[] = $value;
}
+ /**
+ * Same as addWhere(), but add the WHERE clauses only if a condition is met
+ * @param mixed $value See addWhere()
+ * @param bool $condition If false, do nothing
+ * @return bool $condition
+ */
protected function addWhereIf($value, $condition) {
if ($condition) {
$this->addWhere($value);
@@ -94,11 +151,24 @@ abstract class ApiQueryBase extends ApiBase {
return false;
}
+ /**
+ * Equivalent to addWhere(array($field => $value))
+ * @param string $field Field name
+ * @param string $value Value; ignored if nul;
+ */
protected function addWhereFld($field, $value) {
if (!is_null($value))
$this->where[$field] = $value;
}
+ /**
+ * Add a WHERE clause corresponding to a range, and an ORDER BY
+ * clause to sort in the right direction
+ * @param string $field Field name
+ * @param string $dir If 'newer', sort in ascending order, otherwise sort in descending order
+ * @param string $start Value to start the list at. If $dir == 'newer' this is the lower boundary, otherwise it's the upper boundary
+ * @param string $end Value to end the list at. If $dir == 'newer' this is the upper boundary, otherwise it's the lower boundary
+ */
protected function addWhereRange($field, $dir, $start, $end) {
$isDirNewer = ($dir === 'newer');
$after = ($isDirNewer ? '>=' : '<=');
@@ -110,11 +180,19 @@ abstract class ApiQueryBase extends ApiBase {
if (!is_null($end))
$this->addWhere($field . $before . $db->addQuotes($end));
-
+
+ $order = $field . ($isDirNewer ? '' : ' DESC');
if (!isset($this->options['ORDER BY']))
- $this->addOption('ORDER BY', $field . ($isDirNewer ? '' : ' DESC'));
+ $this->addOption('ORDER BY', $order);
+ else
+ $this->addOption('ORDER BY', $this->options['ORDER BY'] . ', ' . $order);
}
+ /**
+ * Add an option such as LIMIT or USE INDEX
+ * @param string $name Option name
+ * @param string $value Option value
+ */
protected function addOption($name, $value = null) {
if (is_null($value))
$this->options[] = $name;
@@ -122,39 +200,71 @@ abstract class ApiQueryBase extends ApiBase {
$this->options[$name] = $value;
}
+ /**
+ * Execute a SELECT query based on the values in the internal arrays
+ * @param string $method Function the query should be attributed to. You should usually use __METHOD__ here
+ * @return ResultWrapper
+ */
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);
+ $res = $db->select($this->tables, $this->fields, $this->where, $method, $this->options, $this->join_conds);
$this->profileDBOut();
return $res;
}
+ /**
+ * Estimate the row count for the SELECT query that would be run if we
+ * called select() right now, and check if it's acceptable.
+ * @return bool true if acceptable, false otherwise
+ */
+ protected function checkRowCount() {
+ $db = $this->getDB();
+ $this->profileDBIn();
+ $rowcount = $db->estimateRowCount($this->tables, $this->fields, $this->where, __METHOD__, $this->options);
+ $this->profileDBOut();
+
+ global $wgAPIMaxDBRows;
+ if($rowcount > $wgAPIMaxDBRows)
+ return false;
+ return true;
+ }
+
+ /**
+ * Add information (title and namespace) about a Title object to a result array
+ * @param array $arr Result array à la ApiResult
+ * @param Title $title Title object
+ * @param string $prefix Module prefix
+ */
public static function addTitleInfo(&$arr, $title, $prefix='') {
$arr[$prefix . 'ns'] = intval($title->getNamespace());
$arr[$prefix . 'title'] = $title->getPrefixedText();
}
-
+
/**
* Override this method to request extra fields from the pageSet
* using $pageSet->requestField('fieldName')
+ * @param ApiPageSet $pageSet
*/
public function requestExtraData($pageSet) {
}
/**
* Get the main Query module
+ * @return ApiQuery
*/
public function getQuery() {
return $this->mQueryModule;
}
/**
- * Add sub-element under the page element with the given pageId.
+ * Add a sub-element under the page element with the given page ID
+ * @param int $pageId Page ID
+ * @param array $data Data array à la ApiResult
*/
protected function addPageSubItems($pageId, $data) {
$result = $this->getResult();
@@ -164,19 +274,21 @@ abstract class ApiQueryBase extends ApiBase {
$data);
}
+ /**
+ * Set a query-continue value
+ * @param $paramName Parameter name
+ * @param $paramValue Parameter value
+ */
protected function setContinueEnumParameter($paramName, $paramValue) {
-
+
$paramName = $this->encodeParamName($paramName);
$msg = array( $paramName => $paramValue );
-
-// This is an alternative continue format as a part of the URL string
-// ApiResult :: setContent($msg, $paramName . '=' . urlencode($paramValue));
-
$this->getResult()->addValue('query-continue', $this->getModuleName(), $msg);
}
/**
* Get the Query database connection (readonly)
+ * @return Database
*/
protected function getDB() {
if (is_null($this->mDb))
@@ -186,57 +298,62 @@ abstract class ApiQueryBase extends ApiBase {
/**
* Selects the query database connection with the given name.
- * If no such connection has been requested before, it will be created.
- * Subsequent calls with the same $name will return the same connection
- * as the first, regardless of $db or $groups new values.
+ * If no such connection has been requested before, it will be created.
+ * Subsequent calls with the same $name will return the same connection
+ * as the first, regardless of $db or $groups new values.
+ * @param string $name Name to assign to the database connection
+ * @param int $db One of the DB_* constants
+ * @param array $groups Query groups
+ * @return Database
*/
public function selectNamedDB($name, $db, $groups) {
- $this->mDb = $this->getQuery()->getNamedDB($name, $db, $groups);
+ $this->mDb = $this->getQuery()->getNamedDB($name, $db, $groups);
}
/**
* Get the PageSet object to work on
- * @return ApiPageSet data
+ * @return ApiPageSet
*/
protected function getPageSet() {
return $this->getQuery()->getPageSet();
}
/**
- * This is a very simplistic utility function
- * to convert a non-namespaced title string to a db key.
- * It will replace all ' ' with '_'
+ * Convert a title to a DB key
+ * @param string $title Page title with spaces
+ * @return string Page title with underscores
*/
- public static function titleToKey($title) {
- return str_replace(' ', '_', $title);
+ public function titleToKey($title) {
+ $t = Title::newFromText($title);
+ if(!$t)
+ $this->dieUsageMsg(array('invalidtitle', $title));
+ return $t->getDbKey();
}
- public static function keyToTitle($key) {
- return str_replace('_', ' ', $key);
+ /**
+ * The inverse of titleToKey()
+ * @param string $key Page title with underscores
+ * @return string Page title with spaces
+ */
+ public function keyToTitle($key) {
+ $t = Title::newFromDbKey($key);
+ # This really shouldn't happen but we gotta check anyway
+ if(!$t)
+ $this->dieUsageMsg(array('invalidtitle', $key));
+ return $t->getPrefixedText();
}
- public function getTokenFlag($tokenArr, $action) {
- if ($this->getMain()->getRequest()->getVal('callback') !== null) {
- // Don't do any session-specific data.
- return false;
- }
- if (in_array($action, $tokenArr)) {
- global $wgUser;
- if ($wgUser->isAllowed($action))
- return true;
- else
- $this->dieUsage("Action '$action' is not allowed for the current user", 'permissiondenied');
- }
- return false;
- }
-
+ /**
+ * Get version string for use in the API help output
+ * @return string
+ */
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiQueryBase.php 31484 2008-03-03 05:46:20Z brion $';
+ return __CLASS__ . ': $Id: ApiQueryBase.php 37083 2008-07-05 11:18:50Z catrope $';
}
}
/**
- * @addtogroup API
+ * @ingroup API
*/
abstract class ApiQueryGeneratorBase extends ApiQueryBase {
@@ -247,6 +364,10 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase {
$this->mIsGenerator = false;
}
+ /**
+ * Switch this module to generator mode. By default, generator mode is
+ * switched off and the module acts like a normal query module.
+ */
public function setGeneratorMode() {
$this->mIsGenerator = true;
}
@@ -267,4 +388,3 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase {
*/
public abstract function executeGenerator($resultPageSet);
}
-
diff --git a/includes/api/ApiQueryBlocks.php b/includes/api/ApiQueryBlocks.php
index 165792b5..ebe87908 100644
--- a/includes/api/ApiQueryBlocks.php
+++ b/includes/api/ApiQueryBlocks.php
@@ -30,10 +30,12 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to enumerate all available pages.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryBlocks extends ApiQueryBase {
+
+ var $users;
public function __construct($query, $moduleName) {
parent :: __construct($query, $moduleName, 'bk');
@@ -47,6 +49,9 @@ class ApiQueryBlocks extends ApiQueryBase {
global $wgUser;
$params = $this->extractRequestParams();
+ if(isset($params['users']) && isset($params['ip']))
+ $this->dieUsage('bkusers and bkip cannot be used together', 'usersandip');
+
$prop = array_flip($params['prop']);
$fld_id = isset($prop['id']);
$fld_user = isset($prop['user']);
@@ -66,7 +71,7 @@ class ApiQueryBlocks extends ApiQueryBase {
if($fld_id)
$this->addFields('ipb_id');
if($fld_user)
- $this->addFields(array('ipb_address', 'ipb_user'));
+ $this->addFields(array('ipb_address', 'ipb_user', 'ipb_auto'));
if($fld_by)
{
$this->addTables('user');
@@ -89,8 +94,32 @@ class ApiQueryBlocks extends ApiQueryBase {
if(isset($params['ids']))
$this->addWhere(array('ipb_id' => $params['ids']));
if(isset($params['users']))
- $this->addWhere(array('ipb_address' => $params['users']));
- if(!$wgUser->isAllowed('oversight'))
+ {
+ foreach((array)$params['users'] as $u)
+ $this->prepareUsername($u);
+ $this->addWhere(array('ipb_address' => $this->usernames));
+ }
+ if(isset($params['ip']))
+ {
+ list($ip, $range) = IP::parseCIDR($params['ip']);
+ if($ip && $range)
+ {
+ # We got a CIDR range
+ if($range < 16)
+ $this->dieUsage('CIDR ranges broader than /16 are not accepted', 'cidrtoobroad');
+ $lower = wfBaseConvert($ip, 10, 16, 8, false);
+ $upper = wfBaseConvert($ip + pow(2, 32 - $range) - 1, 10, 16, 8, false);
+ }
+ else
+ $lower = $upper = IP::toHex($params['ip']);
+ $prefix = substr($lower, 0, 4);
+ $this->addWhere(array(
+ "ipb_range_start LIKE '$prefix%'",
+ "ipb_range_start <= '$lower'",
+ "ipb_range_end >= '$upper'"
+ ));
+ }
+ if(!$wgUser->isAllowed('suppress'))
$this->addWhere(array('ipb_deleted' => 0));
// Purge expired entries on one in every 10 queries
@@ -152,6 +181,18 @@ class ApiQueryBlocks extends ApiQueryBase {
$result->setIndexedTagName($data, 'block');
$result->addValue('query', $this->getModuleName(), $data);
}
+
+ protected function prepareUsername($user)
+ {
+ if(!$user)
+ $this->dieUsage('User parameter may not be empty', 'param_user');
+ $name = User::isIP($user)
+ ? $user
+ : User::getCanonicalName($user, 'valid');
+ if($name === false)
+ $this->dieUsage("User name {$user} is not valid", 'param_user');
+ $this->usernames[] = $name;
+ }
protected function convertHexIP($ip)
{
@@ -188,6 +229,7 @@ class ApiQueryBlocks extends ApiQueryBase {
'users' => array(
ApiBase :: PARAM_ISMULTI => true
),
+ 'ip' => null,
'limit' => array(
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
@@ -219,6 +261,8 @@ class ApiQueryBlocks extends ApiQueryBase {
'dir' => 'The direction in which to enumerate',
'ids' => 'Pipe-separated list of block IDs to list (optional)',
'users' => 'Pipe-separated list of users to search for (optional)',
+ 'ip' => array( 'Get all blocks applying to this IP or CIDR range, including range blocks.',
+ 'Cannot be used together with bkusers. CIDR ranges broader than /16 are not accepted.'),
'limit' => 'The maximum amount of blocks to list',
'prop' => 'Which properties to get',
);
@@ -229,11 +273,12 @@ class ApiQueryBlocks extends ApiQueryBase {
}
protected function getExamples() {
- return array (
+ return array ( 'api.php?action=query&list=blocks',
+ 'api.php?action=query&list=blocks&bkusers=Alice|Bob'
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryBlocks.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryBlocks.php 37892 2008-07-21 21:37:11Z catrope $';
}
}
diff --git a/includes/api/ApiQueryCategories.php b/includes/api/ApiQueryCategories.php
index 63d42bfa..51492d63 100644
--- a/includes/api/ApiQueryCategories.php
+++ b/includes/api/ApiQueryCategories.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query module to enumerate categories the set of pages belong to.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryCategories extends ApiQueryGeneratorBase {
@@ -59,8 +59,8 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
'cl_from',
'cl_to'
));
-
- $fld_sortkey = false;
+
+ $fld_sortkey = $fld_timestamp = false;
if (!is_null($prop)) {
foreach($prop as $p) {
switch ($p) {
@@ -68,24 +68,51 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
$this->addFields('cl_sortkey');
$fld_sortkey = true;
break;
+ case 'timestamp':
+ $this->addFields('cl_timestamp');
+ $fld_timestamp = true;
+ break;
default :
ApiBase :: dieDebug(__METHOD__, "Unknown prop=$p");
}
}
}
-
+
$this->addTables('categorylinks');
$this->addWhereFld('cl_from', array_keys($this->getPageSet()->getGoodTitles()));
- $this->addOption('ORDER BY', "cl_from, cl_to");
+ if(!is_null($params['continue'])) {
+ $cont = explode('|', $params['continue']);
+ if(count($cont) != 2)
+ $this->dieUsage("Invalid continue param. You should pass the " .
+ "original value returned by the previous query", "_badcontinue");
+ $clfrom = intval($cont[0]);
+ $clto = $this->getDb()->strencode($this->titleToKey($cont[1]));
+ $this->addWhere("cl_from > $clfrom OR ".
+ "(cl_from = $clfrom AND ".
+ "cl_to >= '$clto')");
+ }
+ # Don't order by cl_from if it's constant in the WHERE clause
+ if(count($this->getPageSet()->getGoodTitles()) == 1)
+ $this->addOption('ORDER BY', 'cl_to');
+ else
+ $this->addOption('ORDER BY', "cl_from, cl_to");
$db = $this->getDB();
$res = $this->select(__METHOD__);
if (is_null($resultPageSet)) {
-
+
$data = array();
- $lastId = 0; // database has no ID 0
+ $lastId = 0; // database has no ID 0
+ $count = 0;
while ($row = $db->fetchObject($res)) {
+ if (++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('continue', $row->cl_from .
+ '|' . $this->keyToTitle($row->cl_to));
+ break;
+ }
if ($lastId != $row->cl_from) {
if($lastId != 0) {
$this->addPageSubItems($lastId, $data);
@@ -93,13 +120,15 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
}
$lastId = $row->cl_from;
}
-
+
$title = Title :: makeTitle(NS_CATEGORY, $row->cl_to);
-
+
$vals = array();
ApiQueryBase :: addTitleInfo($vals, $title);
if ($fld_sortkey)
$vals['sortkey'] = $row->cl_sortkey;
+ if ($fld_timestamp)
+ $vals['timestamp'] = $row->cl_timestamp;
$data[] = $vals;
}
@@ -112,6 +141,14 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
$titles = array();
while ($row = $db->fetchObject($res)) {
+ if (++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('continue', $row->cl_from .
+ '|' . $this->keyToTitle($row->cl_to));
+ break;
+ }
+
$titles[] = Title :: makeTitle(NS_CATEGORY, $row->cl_to);
}
$resultPageSet->populateFromTitles($titles);
@@ -126,14 +163,25 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array (
'sortkey',
+ 'timestamp',
)
- )
+ ),
+ 'limit' => array(
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'continue' => null,
);
}
public function getParamDescription() {
return array (
'prop' => 'Which additional properties to get for each category.',
+ 'limit' => 'How many categories to return',
+ 'continue' => 'When more results are available, use this to continue',
);
}
@@ -151,7 +199,6 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryCategories.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryCategories.php 37909 2008-07-22 13:26:15Z catrope $';
}
}
-
diff --git a/includes/api/ApiQueryCategoryInfo.php b/includes/api/ApiQueryCategoryInfo.php
new file mode 100644
index 00000000..f809bb15
--- /dev/null
+++ b/includes/api/ApiQueryCategoryInfo.php
@@ -0,0 +1,91 @@
+<?php
+
+/*
+ * Created on May 13, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * This query adds <categories> subelement to all pages with the list of images embedded into those pages.
+ *
+ * @ingroup API
+ */
+class ApiQueryCategoryInfo extends ApiQueryBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'ci');
+ }
+
+ public function execute() {
+ $alltitles = $this->getPageSet()->getAllTitlesByNamespace();
+ $categories = $alltitles[NS_CATEGORY];
+ if(empty($categories))
+ return;
+
+ $titles = $this->getPageSet()->getGoodTitles() +
+ $this->getPageSet()->getMissingTitles();
+ $cattitles = array();
+ foreach($categories as $c)
+ {
+ $t = $titles[$c];
+ $cattitles[$c] = $t->getDbKey();
+ }
+
+ $this->addTables('category');
+ $this->addFields(array('cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'cat_hidden'));
+ $this->addWhere(array('cat_title' => $cattitles));
+
+ $db = $this->getDB();
+ $res = $this->select(__METHOD__);
+
+ $data = array();
+ $catids = array_flip($cattitles);
+ while($row = $db->fetchObject($res))
+ {
+ $vals = array();
+ $vals['size'] = $row->cat_pages;
+ $vals['pages'] = $row->cat_pages - $row->cat_subcats - $row->cat_files;
+ $vals['files'] = $row->cat_files;
+ $vals['subcats'] = $row->cat_subcats;
+ if($row->cat_hidden)
+ $vals['hidden'] = '';
+ $this->addPageSubItems($catids[$row->cat_title], $vals);
+ }
+ $db->freeResult($res);
+ }
+
+ public function getDescription() {
+ return 'Returns information about the given categories';
+ }
+
+ protected function getExamples() {
+ return "api.php?action=query&prop=categoryinfo&titles=Category:Foo|Category:Bar";
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryCategoryInfo.php 37504 2008-07-10 14:28:09Z catrope $';
+ }
+}
diff --git a/includes/api/ApiQueryCategoryMembers.php b/includes/api/ApiQueryCategoryMembers.php
index e831f291..3909b213 100644
--- a/includes/api/ApiQueryCategoryMembers.php
+++ b/includes/api/ApiQueryCategoryMembers.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query module to enumerate pages that belong to a category.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
@@ -51,19 +51,13 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
- if (is_null($params['category'])) {
- if (is_null($params['title']))
- $this->dieUsage("Either the cmcategory or the cmtitle parameter is required", 'notitle');
- else
- $categoryTitle = Title::newFromText($params['title']);
- } else if(is_null($params['title']))
- $categoryTitle = Title::makeTitleSafe(NS_CATEGORY, $params['category']);
- else
- $this->dieUsage("The cmcategory and cmtitle parameters can't be used together", 'titleandcategory');
+ if ( !isset($params['title']) || is_null($params['title']) )
+ $this->dieUsage("The cmtitle parameter is required", 'notitle');
+ $categoryTitle = Title::newFromText($params['title']);
if ( is_null( $categoryTitle ) || $categoryTitle->getNamespace() != NS_CATEGORY )
$this->dieUsage("The category name you entered is not valid", 'invalidcategory');
-
+
$prop = array_flip($params['prop']);
$fld_ids = isset($prop['ids']);
$fld_title = isset($prop['title']);
@@ -78,26 +72,29 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
$this->addFields(array('cl_from', 'cl_sortkey'));
}
- $this->addFieldsIf('cl_timestamp', $fld_timestamp);
- $this->addTables(array('page','categorylinks')); // must be in this order for 'USE INDEX'
+ $this->addFieldsIf('cl_timestamp', $fld_timestamp || $params['sort'] == 'timestamp');
+ $this->addTables(array('page','categorylinks')); // must be in this order for 'USE INDEX'
// Not needed after bug 10280 is applied to servers
if($params['sort'] == 'timestamp')
{
$this->addOption('USE INDEX', 'cl_timestamp');
- $this->addOption('ORDER BY', 'cl_to, cl_timestamp' . ($params['dir'] == 'desc' ? ' DESC' : ''));
+ // cl_timestamp will be added by addWhereRange() later
+ $this->addOption('ORDER BY', 'cl_to');
}
else
{
+ $dir = ($params['dir'] == 'desc' ? ' DESC' : '');
$this->addOption('USE INDEX', 'cl_sortkey');
- $this->addOption('ORDER BY', 'cl_to, cl_sortkey' . ($params['dir'] == 'desc' ? ' DESC' : '') . ', cl_from');
+ $this->addOption('ORDER BY', 'cl_to, cl_sortkey' . $dir . ', cl_from' . $dir);
}
$this->addWhere('cl_from=page_id');
- $this->setContinuation($params['continue']);
+ $this->setContinuation($params['continue'], $params['dir']);
$this->addWhereFld('cl_to', $categoryTitle->getDBkey());
$this->addWhereFld('page_namespace', $params['namespace']);
- $this->addWhereRange('cl_timestamp', ($params['dir'] == 'asc' ? 'newer' : 'older'), $params['start'], $params['end']);
-
+ if($params['sort'] == 'timestamp')
+ $this->addWhereRange('cl_timestamp', ($params['dir'] == 'asc' ? 'newer' : 'older'), $params['start'], $params['end']);
+
$limit = $params['limit'];
$this->addOption('LIMIT', $limit +1);
@@ -111,16 +108,19 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
// TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter('continue', $this->getContinueStr($row, $lastSortKey));
+ if ($params['sort'] == 'timestamp')
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->cl_timestamp));
+ else
+ $this->setContinueEnumParameter('continue', $this->getContinueStr($row, $lastSortKey));
break;
}
- $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys
-
+ $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys
+
if (is_null($resultPageSet)) {
$vals = array();
if ($fld_ids)
- $vals['pageid'] = intval($row->page_id);
+ $vals['pageid'] = intval($row->page_id);
if ($fld_title) {
$title = Title :: makeTitle($row->page_namespace, $row->page_title);
$vals['ns'] = intval($title->getNamespace());
@@ -142,47 +142,48 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
$this->getResult()->addValue('query', $this->getModuleName(), $data);
}
}
-
+
private function getContinueStr($row, $lastSortKey) {
$ret = $row->cl_sortkey . '|';
if ($row->cl_sortkey == $lastSortKey) // duplicate sort key, add cl_from
$ret .= $row->cl_from;
return $ret;
}
-
+
/**
- * Add DB WHERE clause to continue previous query based on 'continue' parameter
+ * Add DB WHERE clause to continue previous query based on 'continue' parameter
*/
- private function setContinuation($continue) {
+ private function setContinuation($continue, $dir) {
if (is_null($continue))
return; // This is not a continuation request
-
+
$continueList = explode('|', $continue);
$hasError = count($continueList) != 2;
$from = 0;
if (!$hasError && strlen($continueList[1]) > 0) {
$from = intval($continueList[1]);
- $hasError = ($from == 0);
+ $hasError = ($from == 0);
}
-
+
if ($hasError)
$this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "badcontinue");
$encSortKey = $this->getDB()->addQuotes($continueList[0]);
$encFrom = $this->getDB()->addQuotes($from);
+
+ $op = ($dir == 'desc' ? '<' : '>');
if ($from != 0) {
// Duplicate sort key continue
- $this->addWhere( "cl_sortkey>$encSortKey OR (cl_sortkey=$encSortKey AND cl_from>=$encFrom)" );
+ $this->addWhere( "cl_sortkey$op$encSortKey OR (cl_sortkey=$encSortKey AND cl_from$op=$encFrom)" );
} else {
- $this->addWhere( "cl_sortkey>=$encSortKey" );
+ $this->addWhere( "cl_sortkey$op=$encSortKey" );
}
}
public function getAllowedParams() {
return array (
'title' => null,
- 'category' => null, // DEPRECATED, will be removed in early March
'prop' => array (
ApiBase :: PARAM_DFLT => 'ids|title',
ApiBase :: PARAM_ISMULTI => true,
@@ -235,11 +236,10 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
'namespace' => 'Only include pages in these namespaces',
'sort' => 'Property to sort by',
'dir' => 'In which direction to sort',
- 'start' => 'Timestamp to start listing from',
- 'end' => 'Timestamp to end listing at',
+ 'start' => 'Timestamp to start listing from. Can only be used with cmsort=timestamp',
+ 'end' => 'Timestamp to end listing at. Can only be used with cmsort=timestamp',
'continue' => 'For large categories, give the value retured from previous query',
'limit' => 'The maximum number of pages to return.',
- 'category' => 'DEPRECATED. Like title, but without the Category: prefix.',
);
}
@@ -257,7 +257,6 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 30670 2008-02-07 15:17:42Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php
index 1b7fbdb0..8368896d 100644
--- a/includes/api/ApiQueryDeletedrevs.php
+++ b/includes/api/ApiQueryDeletedrevs.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to enumerate all available pages.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryDeletedrevs extends ApiQueryBase {
@@ -87,11 +87,16 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
// Check limits
$userMax = $fld_content ? ApiBase :: LIMIT_SML1 : ApiBase :: LIMIT_BIG1;
$botMax = $fld_content ? ApiBase :: LIMIT_SML2 : ApiBase :: LIMIT_BIG2;
+
+ $limit = $params['limit'];
+
if( $limit == 'max' ) {
$limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
- $this->getResult()->addValue( 'limits', 'limit', $limit );
+ $this->getResult()->addValue( 'limits', $this->getModuleName(), $limit );
}
- $this->validateLimit('limit', $params['limit'], 1, $userMax, $botMax);
+
+ $this->validateLimit('limit', $limit, 1, $userMax, $botMax);
+
if($fld_token)
// Undelete tokens are identical for all pages, so we cache one here
$token = $wgUser->editToken();
@@ -104,17 +109,15 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$this->addWhere($where);
}
- $this->addOption('LIMIT', $params['limit'] + 1);
+ $this->addOption('LIMIT', $limit + 1);
$this->addWhereRange('ar_timestamp', $params['dir'], $params['start'], $params['end']);
- if(isset($params['namespace']))
- $this->addWhereFld('ar_namespace', $params['namespace']);
$res = $this->select(__METHOD__);
$pages = array();
$count = 0;
// First populate the $pages array
while($row = $db->fetchObject($res))
{
- if($count++ == $params['limit'])
+ if(++$count > $limit)
{
// We've had enough
$this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp));
@@ -178,10 +181,6 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
),
ApiBase :: PARAM_DFLT => 'older'
),
- 'namespace' => array(
- ApiBase :: PARAM_ISMULTI => true,
- ApiBase :: PARAM_TYPE => 'namespace'
- ),
'limit' => array(
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
@@ -210,7 +209,6 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
'start' => 'The timestamp to start enumerating from',
'end' => 'The timestamp to stop enumerating at',
'dir' => 'The direction in which to enumerate',
- 'namespace' => 'The namespaces to search in',
'limit' => 'The maximum amount of revisions to list',
'prop' => 'Which properties to get'
);
@@ -222,14 +220,14 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
protected function getExamples() {
return array (
- 'List the first 50 deleted revisions in the Category and Category talk namespaces',
- ' api.php?action=query&list=deletedrevs&drdir=newer&drlimit=50&drnamespace=14|15',
+ 'List the first 50 deleted revisions',
+ ' api.php?action=query&list=deletedrevs&drdir=newer&drlimit=50',
'List the last deleted revisions of Main Page and Talk:Main Page, with content:',
' api.php?action=query&list=deletedrevs&titles=Main%20Page|Talk:Main%20Page&drprop=user|comment|content'
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 37502 2008-07-10 14:13:11Z catrope $';
}
}
diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php
index 896a0171..8ffb7246 100644
--- a/includes/api/ApiQueryExtLinksUsage.php
+++ b/includes/api/ApiQueryExtLinksUsage.php
@@ -29,7 +29,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
@@ -51,43 +51,53 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
$protocol = $params['protocol'];
$query = $params['query'];
- if (is_null($query))
- $this->dieUsage('Missing required query parameter', 'params');
-
+
// Find the right prefix
global $wgUrlProtocols;
- foreach ($wgUrlProtocols as $p) {
- if( substr( $p, 0, strlen( $protocol ) ) === $protocol ) {
- $protocol = $p;
- break;
+ if(!is_null($protocol) && !empty($protocol) && !in_array($protocol, $wgUrlProtocols))
+ {
+ foreach ($wgUrlProtocols as $p) {
+ if( substr( $p, 0, strlen( $protocol ) ) === $protocol ) {
+ $protocol = $p;
+ break;
+ }
}
}
-
- $likeQuery = LinkFilter::makeLike($query , $protocol);
- if (!$likeQuery)
- $this->dieUsage('Invalid query', 'bad_query');
- $likeQuery = substr($likeQuery, 0, strpos($likeQuery,'%')+1);
+ else
+ $protocol = null;
- $this->addTables(array('page','externallinks')); // must be in this order for 'USE INDEX'
+ $db = $this->getDb();
+ $this->addTables(array('page','externallinks')); // must be in this order for 'USE INDEX'
$this->addOption('USE INDEX', 'el_index');
-
- $db = $this->getDB();
$this->addWhere('page_id=el_from');
- $this->addWhere('el_index LIKE ' . $db->addQuotes( $likeQuery ));
$this->addWhereFld('page_namespace', $params['namespace']);
+ if(!is_null($query) || $query != '')
+ {
+ if(is_null($protocol))
+ $protocol = 'http://';
+
+ $likeQuery = LinkFilter::makeLike($query, $protocol);
+ if (!$likeQuery)
+ $this->dieUsage('Invalid query', 'bad_query');
+ $likeQuery = substr($likeQuery, 0, strpos($likeQuery,'%')+1);
+ $this->addWhere('el_index LIKE ' . $db->addQuotes( $likeQuery ));
+ }
+ else if(!is_null($protocol))
+ $this->addWhere('el_index LIKE ' . $db->addQuotes( "$protocol%" ));
+
$prop = array_flip($params['prop']);
$fld_ids = isset($prop['ids']);
$fld_title = isset($prop['title']);
$fld_url = isset($prop['url']);
-
+
if (is_null($resultPageSet)) {
$this->addFields(array (
'page_id',
'page_namespace',
'page_title'
));
- $this->addFieldsIf('el_to', $fld_url);
+ $this->addFieldsIf('el_to', $fld_url);
} else {
$this->addFields($resultPageSet->getPageTableFields());
}
@@ -105,7 +115,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
while ($row = $db->fetchObject($res)) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter('offset', $offset+$limit+1);
+ $this->setContinueEnumParameter('offset', $offset+$limit);
break;
}
@@ -136,11 +146,11 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
public function getAllowedParams() {
global $wgUrlProtocols;
- $protocols = array();
+ $protocols = array('');
foreach ($wgUrlProtocols as $p) {
$protocols[] = substr($p, 0, strpos($p,':'));
}
-
+
return array (
'prop' => array (
ApiBase :: PARAM_ISMULTI => true,
@@ -156,7 +166,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
),
'protocol' => array (
ApiBase :: PARAM_TYPE => $protocols,
- ApiBase :: PARAM_DFLT => 'http',
+ ApiBase :: PARAM_DFLT => '',
),
'query' => null,
'namespace' => array (
@@ -177,10 +187,11 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
return array (
'prop' => 'What pieces of information to include',
'offset' => 'Used for paging. Use the value returned for "continue"',
- 'protocol' => 'Protocol of the url',
- 'query' => 'Search string without protocol. See [[Special:LinkSearch]]',
+ 'protocol' => array( 'Protocol of the url. If empty and euquery set, the protocol is http.',
+ 'Leave both this and euquery empty to list all external links'),
+ 'query' => 'Search string without protocol. See [[Special:LinkSearch]]. Leave empty to list all external links',
'namespace' => 'The page namespace(s) to enumerate.',
- 'limit' => 'How many entries to return.'
+ 'limit' => 'How many pages to return.'
);
}
@@ -195,6 +206,6 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 37909 2008-07-22 13:26:15Z catrope $';
}
}
diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php
index 07183910..a24f15d8 100644
--- a/includes/api/ApiQueryExternalLinks.php
+++ b/includes/api/ApiQueryExternalLinks.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query module to list all external URLs found on a given set of pages.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryExternalLinks extends ApiQueryBase {
@@ -40,21 +40,37 @@ class ApiQueryExternalLinks extends ApiQueryBase {
}
public function execute() {
+ if ( $this->getPageSet()->getGoodTitleCount() == 0 )
+ return;
+ $params = $this->extractRequestParams();
$this->addFields(array (
'el_from',
'el_to'
));
-
+
$this->addTables('externallinks');
$this->addWhereFld('el_from', array_keys($this->getPageSet()->getGoodTitles()));
+ # Don't order by el_from if it's constant in the WHERE clause
+ if(count($this->getPageSet()->getGoodTitles()) != 1)
+ $this->addOption('ORDER BY', 'el_from');
+ $this->addOption('LIMIT', $params['limit'] + 1);
+ if(!is_null($params['offset']))
+ $this->addOption('OFFSET', $params['offset']);
$db = $this->getDB();
$res = $this->select(__METHOD__);
-
+
$data = array();
- $lastId = 0; // database has no ID 0
+ $lastId = 0; // database has no ID 0
+ $count = 0;
while ($row = $db->fetchObject($res)) {
+ if (++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('offset', @$params['offset'] + $params['limit']);
+ break;
+ }
if ($lastId != $row->el_from) {
if($lastId != 0) {
$this->addPageSubItems($lastId, $data);
@@ -62,7 +78,7 @@ class ApiQueryExternalLinks extends ApiQueryBase {
}
$lastId = $row->el_from;
}
-
+
$entry = array();
ApiResult :: setContent($entry, $row->el_to);
$data[] = $entry;
@@ -75,6 +91,26 @@ class ApiQueryExternalLinks extends ApiQueryBase {
$db->freeResult($res);
}
+ public function getAllowedParams() {
+ return array(
+ 'limit' => array(
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'offset' => null,
+ );
+ }
+
+ public function getParamDescription () {
+ return array(
+ 'limit' => 'How many links to return',
+ 'offset' => 'When more results are available, use this to continue',
+ );
+ }
+
public function getDescription() {
return 'Returns all external urls (not interwikies) from the given page(s)';
}
@@ -87,7 +123,6 @@ class ApiQueryExternalLinks extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryExternalLinks.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryExternalLinks.php 37270 2008-07-07 17:32:22Z catrope $';
}
}
-
diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php
index 3714ccf6..33ff1d3f 100644
--- a/includes/api/ApiQueryImageInfo.php
+++ b/includes/api/ApiQueryImageInfo.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query action to get image information and upload history.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryImageInfo extends ApiQueryBase {
@@ -42,65 +42,63 @@ class ApiQueryImageInfo extends ApiQueryBase {
public function execute() {
$params = $this->extractRequestParams();
- $prop = array_flip($params['prop']);
- $this->fld_timestamp = isset($prop['timestamp']);
- $this->fld_user = isset($prop['user']);
- $this->fld_comment = isset($prop['comment']);
- $this->fld_url = isset($prop['url']);
- $this->fld_size = isset($prop['size']);
- $this->fld_sha1 = isset($prop['sha1']);
- $this->fld_metadata = isset($prop['metadata']);
-
+ $prop = array_flip($params['prop']);
+
if($params['urlheight'] != -1 && $params['urlwidth'] == -1)
$this->dieUsage("iiurlheight cannot be used without iiurlwidth", 'iiurlwidth');
- $this->scale = ($params['urlwidth'] != -1);
- $this->urlwidth = $params['urlwidth'];
- $this->urlheight = $params['urlheight'];
+
+ if ( $params['urlwidth'] != -1 ) {
+ $scale = array();
+ $scale['width'] = $params['urlwidth'];
+ $scale['height'] = $params['urlheight'];
+ } else {
+ $scale = null;
+ }
$pageIds = $this->getPageSet()->getAllTitlesByNamespace();
if (!empty($pageIds[NS_IMAGE])) {
- foreach ($pageIds[NS_IMAGE] as $dbKey => $pageId) {
-
- $title = Title :: makeTitle(NS_IMAGE, $dbKey);
- $img = wfFindFile($title);
-
+
+ $result = $this->getResult();
+ $images = RepoGroup::singleton()->findFiles( array_keys( $pageIds[NS_IMAGE] ) );
+ foreach ( $images as $img ) {
$data = array();
- if ( !$img ) {
- $repository = '';
- } else {
-
- $repository = $img->getRepoName();
-
- // Get information about the current version first
- // Check that the current version is within the start-end boundaries
- if((is_null($params['start']) || $img->getTimestamp() <= $params['start']) &&
- (is_null($params['end']) || $img->getTimestamp() >= $params['end'])) {
- $data[] = $this->getInfo($img);
- }
-
- // Now get the old revisions
- // Get one more to facilitate query-continue functionality
- $count = count($data);
- $oldies = $img->getHistory($params['limit'] - $count + 1, $params['start'], $params['end']);
- foreach($oldies as $oldie) {
- if(++$count > $params['limit']) {
- // We've reached the extra one which shows that there are additional pages to be had. Stop here...
- // Only set a query-continue if there was only one title
- if(count($pageIds[NS_IMAGE]) == 1)
- $this->setContinueEnumParameter('start', $oldie->getTimestamp());
- break;
- }
- $data[] = $this->getInfo($oldie);
+
+ // Get information about the current version first
+ // Check that the current version is within the start-end boundaries
+ if((is_null($params['start']) || $img->getTimestamp() <= $params['start']) &&
+ (is_null($params['end']) || $img->getTimestamp() >= $params['end'])) {
+ $data[] = self::getInfo( $img, $prop, $result, $scale );
+ }
+
+ // Now get the old revisions
+ // Get one more to facilitate query-continue functionality
+ $count = count($data);
+ $oldies = $img->getHistory($params['limit'] - $count + 1, $params['start'], $params['end']);
+ foreach($oldies as $oldie) {
+ if(++$count > $params['limit']) {
+ // We've reached the extra one which shows that there are additional pages to be had. Stop here...
+ // Only set a query-continue if there was only one title
+ if(count($pageIds[NS_IMAGE]) == 1)
+ $this->setContinueEnumParameter('start', $oldie->getTimestamp());
+ break;
}
+ $data[] = self::getInfo( $oldie, $prop, $result );
}
- $this->getResult()->addValue(array(
- 'query', 'pages', intval($pageId)),
- 'imagerepository', $repository
+ $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ];
+ $result->addValue(
+ array( 'query', 'pages', intval( $pageId ) ),
+ 'imagerepository', $img->getRepoName()
);
- if (!empty($data))
- $this->addPageSubItems($pageId, $data);
+ $this->addPageSubItems($pageId, $data);
}
+
+ $missing = array_diff( array_keys( $pageIds[NS_IMAGE] ), array_keys( $images ) );
+ foreach ( $missing as $title )
+ $result->addValue(
+ array( 'query', 'pages', intval( $pageIds[NS_IMAGE][$title] ) ),
+ 'imagerepository', ''
+ );
}
}
@@ -109,41 +107,48 @@ class ApiQueryImageInfo extends ApiQueryBase {
* @param File f The image
* @return array Result array
*/
- protected function getInfo($f) {
+ static function getInfo($file, $prop, $result, $scale = null) {
$vals = array();
- if($this->fld_timestamp)
- $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $f->getTimestamp());
- if($this->fld_user) {
- $vals['user'] = $f->getUser();
- if(!$f->getUser('id'))
+ if( isset( $prop['timestamp'] ) )
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $file->getTimestamp());
+ if( isset( $prop['user'] ) ) {
+ $vals['user'] = $file->getUser();
+ if( !$file->getUser( 'id' ) )
$vals['anon'] = '';
}
- if($this->fld_size) {
- $vals['size'] = intval($f->getSize());
- $vals['width'] = intval($f->getWidth());
- $vals['height'] = intval($f->getHeight());
+ if( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) {
+ $vals['size'] = intval( $file->getSize() );
+ $vals['width'] = intval( $file->getWidth() );
+ $vals['height'] = intval( $file->getHeight() );
}
- if($this->fld_url) {
- if($this->scale && !$f->isOld()) {
- $thumb = $f->getThumbnail($this->urlwidth, $this->urlheight);
- if($thumb)
+ if( isset( $prop['url'] ) ) {
+ if( !is_null( $scale ) && !$file->isOld() ) {
+ $thumb = $file->getThumbnail( $scale['width'], $scale['height'] );
+ if( $thumb )
{
- $vals['thumburl'] = $thumb->getURL();
+ $vals['thumburl'] = wfExpandUrl( $thumb->getURL() );
$vals['thumbwidth'] = $thumb->getWidth();
$vals['thumbheight'] = $thumb->getHeight();
}
}
- $vals['url'] = $f->getURL();
+ $vals['url'] = $file->getFullURL();
+ $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl() );
}
- if($this->fld_comment)
- $vals['comment'] = $f->getDescription();
- if($this->fld_sha1)
- $vals['sha1'] = wfBaseConvert($f->getSha1(), 36, 16, 40);
- if($this->fld_metadata) {
- $metadata = unserialize($f->getMetadata());
- $vals['metadata'] = $metadata ? $metadata : null;
- $this->getResult()->setIndexedTagName_recursive($vals['metadata'], 'meta');
+ if( isset( $prop['comment'] ) )
+ $vals['comment'] = $file->getDescription();
+ if( isset( $prop['sha1'] ) )
+ $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 );
+ if( isset( $prop['metadata'] ) ) {
+ $metadata = $file->getMetadata();
+ $vals['metadata'] = $metadata ? unserialize( $metadata ) : null;
+ $result->setIndexedTagName_recursive( $vals['metadata'], 'meta' );
}
+ if( isset( $prop['mime'] ) )
+ $vals['mime'] = $file->getMimeType();
+
+ if( isset( $prop['archivename'] ) && $file->isOld() )
+ $vals['archivename'] = $file->getArchiveName();
+
return $vals;
}
@@ -159,7 +164,9 @@ class ApiQueryImageInfo extends ApiQueryBase {
'url',
'size',
'sha1',
- 'metadata'
+ 'mime',
+ 'metadata',
+ 'archivename'
)
),
'limit' => array(
@@ -192,7 +199,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
'limit' => 'How many image revisions to return',
'start' => 'Timestamp to start listing from',
'end' => 'Timestamp to stop listing at',
- 'urlwidth' => 'If iiprop=url is set, a URL to an image scaled to this width will be returned. Only the current version of the image can be scaled.',
+ 'urlwidth' => array('If iiprop=url is set, a URL to an image scaled to this width will be returned.',
+ 'Only the current version of the image can be scaled.'),
'urlheight' => 'Similar to iiurlwidth. Cannot be used without iiurlwidth',
);
}
@@ -211,6 +219,6 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryImageInfo.php 30665 2008-02-07 12:21:48Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryImageInfo.php 37504 2008-07-10 14:28:09Z catrope $';
}
}
diff --git a/includes/api/ApiQueryImages.php b/includes/api/ApiQueryImages.php
index f7405374..32c4e1b0 100644
--- a/includes/api/ApiQueryImages.php
+++ b/includes/api/ApiQueryImages.php
@@ -29,9 +29,9 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * This query adds <images> subelement to all pages with the list of images embedded into those pages.
- *
- * @addtogroup API
+ * This query adds an <images> subelement to all pages with the list of images embedded into those pages.
+ *
+ * @ingroup API
*/
class ApiQueryImages extends ApiQueryGeneratorBase {
@@ -52,6 +52,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
if ($this->getPageSet()->getGoodTitleCount() == 0)
return; // nothing to do
+ $params = $this->extractRequestParams();
$this->addFields(array (
'il_from',
'il_to'
@@ -59,16 +60,40 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
$this->addTables('imagelinks');
$this->addWhereFld('il_from', array_keys($this->getPageSet()->getGoodTitles()));
- $this->addOption('ORDER BY', "il_from, il_to");
+ if(!is_null($params['continue'])) {
+ $cont = explode('|', $params['continue']);
+ if(count($cont) != 2)
+ $this->dieUsage("Invalid continue param. You should pass the " .
+ "original value returned by the previous query", "_badcontinue");
+ $ilfrom = intval($cont[0]);
+ $ilto = $this->getDb()->strencode($this->titleToKey($cont[1]));
+ $this->addWhere("il_from > $ilfrom OR ".
+ "(il_from = $ilfrom AND ".
+ "il_to >= '$ilto')");
+ }
+ # Don't order by il_from if it's constant in the WHERE clause
+ if(count($this->getPageSet()->getGoodTitles()) == 1)
+ $this->addOption('ORDER BY', 'il_to');
+ else
+ $this->addOption('ORDER BY', 'il_from, il_to');
+ $this->addOption('LIMIT', $params['limit'] + 1);
$db = $this->getDB();
$res = $this->select(__METHOD__);
if (is_null($resultPageSet)) {
-
+
$data = array();
- $lastId = 0; // database has no ID 0
+ $lastId = 0; // database has no ID 0
+ $count = 0;
while ($row = $db->fetchObject($res)) {
+ if (++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('continue', $row->il_from .
+ '|' . $this->keyToTitle($row->il_to));
+ break;
+ }
if ($lastId != $row->il_from) {
if($lastId != 0) {
$this->addPageSubItems($lastId, $data);
@@ -76,7 +101,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
}
$lastId = $row->il_from;
}
-
+
$vals = array();
ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle(NS_IMAGE, $row->il_to));
$data[] = $vals;
@@ -89,7 +114,15 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
} else {
$titles = array();
+ $count = 0;
while ($row = $db->fetchObject($res)) {
+ if (++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('continue', $row->il_from .
+ '|' . $this->keyToTitle($row->il_to));
+ break;
+ }
$titles[] = Title :: makeTitle(NS_IMAGE, $row->il_to);
}
$resultPageSet->populateFromTitles($titles);
@@ -98,6 +131,26 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
$db->freeResult($res);
}
+ public function getAllowedParams() {
+ return array(
+ 'limit' => array(
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'continue' => null,
+ );
+ }
+
+ public function getParamDescription () {
+ return array(
+ 'limit' => 'How many images to return',
+ 'continue' => 'When more results are available, use this to continue',
+ );
+ }
+
public function getDescription() {
return 'Returns all images contained on the given page(s)';
}
@@ -112,7 +165,6 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryImages.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryImages.php 37535 2008-07-10 21:20:43Z catrope $';
}
}
-
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index 2dee22b0..9c6487b3 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query module to show basic page information.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryInfo extends ApiQueryBase {
@@ -49,27 +49,123 @@ class ApiQueryInfo extends ApiQueryBase {
$pageSet->requestField('page_len');
}
+ protected function getTokenFunctions() {
+ // tokenname => function
+ // function prototype is func($pageid, $title)
+ // should return token or false
+
+ // Don't call the hooks twice
+ if(isset($this->tokenFunctions))
+ return $this->tokenFunctions;
+
+ // If we're in JSON callback mode, no tokens can be obtained
+ if(!is_null($this->getMain()->getRequest()->getVal('callback')))
+ return array();
+
+ $this->tokenFunctions = array(
+ 'edit' => array( 'ApiQueryInfo', 'getEditToken' ),
+ 'delete' => array( 'ApiQueryInfo', 'getDeleteToken' ),
+ 'protect' => array( 'ApiQueryInfo', 'getProtectToken' ),
+ 'move' => array( 'ApiQueryInfo', 'getMoveToken' ),
+ 'block' => array( 'ApiQueryInfo', 'getBlockToken' ),
+ 'unblock' => array( 'ApiQueryInfo', 'getUnblockToken' )
+ );
+ wfRunHooks('APIQueryInfoTokens', array(&$this->tokenFunctions));
+ return $this->tokenFunctions;
+ }
+
+ public static function getEditToken($pageid, $title)
+ {
+ // We could check for $title->userCan('edit') here,
+ // but that's too expensive for this purpose
+ global $wgUser;
+ if(!$wgUser->isAllowed('edit'))
+ return false;
+
+ // The edit token is always the same, let's exploit that
+ static $cachedEditToken = null;
+ if(!is_null($cachedEditToken))
+ return $cachedEditToken;
+
+ $cachedEditToken = $wgUser->editToken();
+ return $cachedEditToken;
+ }
+
+ public static function getDeleteToken($pageid, $title)
+ {
+ global $wgUser;
+ if(!$wgUser->isAllowed('delete'))
+ return false;
+
+ static $cachedDeleteToken = null;
+ if(!is_null($cachedDeleteToken))
+ return $cachedDeleteToken;
+
+ $cachedDeleteToken = $wgUser->editToken();
+ return $cachedDeleteToken;
+ }
+
+ public static function getProtectToken($pageid, $title)
+ {
+ global $wgUser;
+ if(!$wgUser->isAllowed('protect'))
+ return false;
+
+ static $cachedProtectToken = null;
+ if(!is_null($cachedProtectToken))
+ return $cachedProtectToken;
+
+ $cachedProtectToken = $wgUser->editToken();
+ return $cachedProtectToken;
+ }
+
+ public static function getMoveToken($pageid, $title)
+ {
+ global $wgUser;
+ if(!$wgUser->isAllowed('move'))
+ return false;
+
+ static $cachedMoveToken = null;
+ if(!is_null($cachedMoveToken))
+ return $cachedMoveToken;
+
+ $cachedMoveToken = $wgUser->editToken();
+ return $cachedMoveToken;
+ }
+
+ public static function getBlockToken($pageid, $title)
+ {
+ global $wgUser;
+ if(!$wgUser->isAllowed('block'))
+ return false;
+
+ static $cachedBlockToken = null;
+ if(!is_null($cachedBlockToken))
+ return $cachedBlockToken;
+
+ $cachedBlockToken = $wgUser->editToken();
+ return $cachedBlockToken;
+ }
+
+ public static function getUnblockToken($pageid, $title)
+ {
+ // Currently, this is exactly the same as the block token
+ return self::getBlockToken($pageid, $title);
+ }
+
public function execute() {
global $wgUser;
$params = $this->extractRequestParams();
- $fld_protection = false;
+ $fld_protection = $fld_talkid = $fld_subjectid = false;
if(!is_null($params['prop'])) {
$prop = array_flip($params['prop']);
$fld_protection = isset($prop['protection']);
+ $fld_talkid = isset($prop['talkid']);
+ $fld_subjectid = isset($prop['subjectid']);
}
- if(!is_null($params['token'])) {
- $token = $params['token'];
- $tok_edit = $this->getTokenFlag($token, 'edit');
- $tok_delete = $this->getTokenFlag($token, 'delete');
- $tok_protect = $this->getTokenFlag($token, 'protect');
- $tok_move = $this->getTokenFlag($token, 'move');
- }
- else
- // Fix E_NOTICEs about unset variables
- $token = $tok_edit = $tok_delete = $tok_protect = $tok_move = null;
-
+
$pageSet = $this->getPageSet();
$titles = $pageSet->getGoodTitles();
$missing = $pageSet->getMissingTitles();
@@ -101,7 +197,64 @@ class ApiQueryInfo extends ApiQueryBase {
$protections[$row->pr_page][] = $a;
}
$db->freeResult($res);
+
+ $imageIds = array();
+ foreach ($titles as $id => $title)
+ if ($title->getNamespace() == NS_IMAGE)
+ $imageIds[] = $id;
+ // To avoid code duplication
+ $cascadeTypes = array(
+ array(
+ 'prefix' => 'tl',
+ 'table' => 'templatelinks',
+ 'ns' => 'tl_namespace',
+ 'title' => 'tl_title',
+ 'ids' => array_diff(array_keys($titles), $imageIds)
+ ),
+ array(
+ 'prefix' => 'il',
+ 'table' => 'imagelinks',
+ 'ns' => NS_IMAGE,
+ 'title' => 'il_to',
+ 'ids' => $imageIds
+ )
+ );
+
+ foreach ($cascadeTypes as $type)
+ {
+ if (count($type['ids']) != 0) {
+ $this->resetQueryParams();
+ $this->addTables(array('page_restrictions', $type['table']));
+ $this->addTables('page', 'page_source');
+ $this->addTables('page', 'page_target');
+ $this->addFields(array('pr_type', 'pr_level', 'pr_expiry',
+ 'page_target.page_id AS page_target_id',
+ 'page_source.page_namespace AS page_source_namespace',
+ 'page_source.page_title AS page_source_title'));
+ $this->addWhere(array("{$type['prefix']}_from = pr_page",
+ 'page_target.page_namespace = '.$type['ns'],
+ 'page_target.page_title = '.$type['title'],
+ 'page_source.page_id = pr_page'
+ ));
+ $this->addWhereFld('pr_cascade', 1);
+ $this->addWhereFld('page_target.page_id', $type['ids']);
+
+ $res = $this->select(__METHOD__);
+ while($row = $db->fetchObject($res)) {
+ $source = Title::makeTitle($row->page_source_namespace, $row->page_source_title);
+ $a = array(
+ 'type' => $row->pr_type,
+ 'level' => $row->pr_level,
+ 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ),
+ 'source' => $source->getPrefixedText()
+ );
+ $protections[$row->page_target_id][] = $a;
+ }
+ $db->freeResult($res);
+ }
+ }
}
+
// We don't need to check for pt stuff if there are no nonexistent titles
if($fld_protection && !empty($missing))
{
@@ -114,13 +267,107 @@ class ApiQueryInfo extends ApiQueryBase {
$res = $this->select(__METHOD__);
$prottitles = array();
while($row = $db->fetchObject($res)) {
- $prottitles[$row->pt_namespace][$row->pt_title] = array(
+ $prottitles[$row->pt_namespace][$row->pt_title][] = array(
'type' => 'create',
'level' => $row->pt_create_perm,
'expiry' => Block::decodeExpiry($row->pt_expiry, TS_ISO_8601)
);
}
$db->freeResult($res);
+
+ $images = array();
+ $others = array();
+ foreach ($missing as $title)
+ if ($title->getNamespace() == NS_IMAGE)
+ $images[] = $title->getDbKey();
+ else
+ $others[] = $title;
+
+ if (count($others) != 0) {
+ $lb = new LinkBatch($others);
+ $this->resetQueryParams();
+ $this->addTables(array('page_restrictions', 'page', 'templatelinks'));
+ $this->addFields(array('pr_type', 'pr_level', 'pr_expiry',
+ 'page_title', 'page_namespace',
+ 'tl_title', 'tl_namespace'));
+ $this->addWhere($lb->constructSet('tl', $db));
+ $this->addWhere('pr_page = page_id');
+ $this->addWhere('pr_page = tl_from');
+ $this->addWhereFld('pr_cascade', 1);
+
+ $res = $this->select(__METHOD__);
+ while($row = $db->fetchObject($res)) {
+ $source = Title::makeTitle($row->page_namespace, $row->page_title);
+ $a = array(
+ 'type' => $row->pr_type,
+ 'level' => $row->pr_level,
+ 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ),
+ 'source' => $source->getPrefixedText()
+ );
+ $prottitles[$row->tl_namespace][$row->tl_title][] = $a;
+ }
+ $db->freeResult($res);
+ }
+
+ if (count($images) != 0) {
+ $this->resetQueryParams();
+ $this->addTables(array('page_restrictions', 'page', 'imagelinks'));
+ $this->addFields(array('pr_type', 'pr_level', 'pr_expiry',
+ 'page_title', 'page_namespace', 'il_to'));
+ $this->addWhere('pr_page = page_id');
+ $this->addWhere('pr_page = il_from');
+ $this->addWhereFld('pr_cascade', 1);
+ $this->addWhereFld('il_to', $images);
+
+ $res = $this->select(__METHOD__);
+ while($row = $db->fetchObject($res)) {
+ $source = Title::makeTitle($row->page_namespace, $row->page_title);
+ $a = array(
+ 'type' => $row->pr_type,
+ 'level' => $row->pr_level,
+ 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ),
+ 'source' => $source->getPrefixedText()
+ );
+ $prottitles[NS_IMAGE][$row->il_to][] = $a;
+ }
+ $db->freeResult($res);
+ }
+ }
+
+ // Run the talkid/subjectid query
+ if($fld_talkid || $fld_subjectid)
+ {
+ $talktitles = $subjecttitles =
+ $talkids = $subjectids = array();
+ $everything = array_merge($titles, $missing);
+ foreach($everything as $t)
+ {
+ if(MWNamespace::isTalk($t->getNamespace()))
+ {
+ if($fld_subjectid)
+ $subjecttitles[] = $t->getSubjectPage();
+ }
+ else if($fld_talkid)
+ $talktitles[] = $t->getTalkPage();
+ }
+ if(!empty($talktitles) || !empty($subjecttitles))
+ {
+ // Construct a custom WHERE clause that matches
+ // all titles in $talktitles and $subjecttitles
+ $lb = new LinkBatch(array_merge($talktitles, $subjecttitles));
+ $this->resetQueryParams();
+ $this->addTables('page');
+ $this->addFields(array('page_title', 'page_namespace', 'page_id'));
+ $this->addWhere($lb->constructSet('page', $db));
+ $res = $this->select(__METHOD__);
+ while($row = $db->fetchObject($res))
+ {
+ if(MWNamespace::isTalk($row->page_namespace))
+ $talkids[MWNamespace::getSubject($row->page_namespace)][$row->page_title] = $row->page_id;
+ else
+ $subjectids[MWNamespace::getTalk($row->page_namespace)][$row->page_title] = $row->page_id;
+ }
+ }
}
foreach ( $titles as $pageid => $title ) {
@@ -137,18 +384,18 @@ class ApiQueryInfo extends ApiQueryBase {
if ($pageIsNew[$pageid])
$pageInfo['new'] = '';
- if (!is_null($token)) {
- // Currently all tokens are generated the same way, but it might change
- if ($tok_edit)
- $pageInfo['edittoken'] = $wgUser->editToken();
- if ($tok_delete)
- $pageInfo['deletetoken'] = $wgUser->editToken();
- if ($tok_protect)
- $pageInfo['protecttoken'] = $wgUser->editToken();
- if ($tok_move)
- $pageInfo['movetoken'] = $wgUser->editToken();
+ if (!is_null($params['token'])) {
+ $tokenFunctions = $this->getTokenFunctions();
+ foreach($params['token'] as $t)
+ {
+ $val = call_user_func($tokenFunctions[$t], $pageid, $title);
+ if($val === false)
+ $this->setWarning("Action '$t' is not allowed for the current user");
+ else
+ $pageInfo[$t . 'token'] = $val;
+ }
}
-
+
if($fld_protection) {
if (isset($protections[$pageid])) {
$pageInfo['protection'] = $protections[$pageid];
@@ -186,6 +433,10 @@ class ApiQueryInfo extends ApiQueryBase {
}
}
}
+ if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDbKey()]))
+ $pageInfo['talkid'] = $talkids[$title->getNamespace()][$title->getDbKey()];
+ if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDbKey()]))
+ $pageInfo['subjectid'] = $subjectids[$title->getNamespace()][$title->getDbKey()];
$result->addValue(array (
'query',
@@ -195,24 +446,34 @@ class ApiQueryInfo extends ApiQueryBase {
// Get edit/protect tokens and protection data for missing titles if requested
// Delete and move tokens are N/A for missing titles anyway
- if($tok_edit || $tok_protect || $fld_protection)
+ if(!is_null($params['token']) || $fld_protection || $fld_talkid || $fld_subjectid)
{
$res = &$result->getData();
foreach($missing as $pageid => $title) {
- if($tok_edit)
- $res['query']['pages'][$pageid]['edittoken'] = $wgUser->editToken();
- if($tok_protect)
- $res['query']['pages'][$pageid]['protecttoken'] = $wgUser->editToken();
+ if(!is_null($params['token']))
+ {
+ $tokenFunctions = $this->getTokenFunctions();
+ foreach($params['token'] as $t)
+ {
+ $val = call_user_func($tokenFunctions[$t], $pageid, $title);
+ if($val !== false)
+ $res['query']['pages'][$pageid][$t . 'token'] = $val;
+ }
+ }
if($fld_protection)
{
// Apparently the XML formatting code doesn't like array(null)
// This is painful to fix, so we'll just work around it
if(isset($prottitles[$title->getNamespace()][$title->getDBkey()]))
- $res['query']['pages'][$pageid]['protection'][] = $prottitles[$title->getNamespace()][$title->getDBkey()];
+ $res['query']['pages'][$pageid]['protection'] = $prottitles[$title->getNamespace()][$title->getDBkey()];
else
$res['query']['pages'][$pageid]['protection'] = array();
$result->setIndexedTagName($res['query']['pages'][$pageid]['protection'], 'pr');
}
+ if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDbKey()]))
+ $res['query']['pages'][$pageid]['talkid'] = $talkids[$title->getNamespace()][$title->getDbKey()];
+ if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDbKey()]))
+ $res['query']['pages'][$pageid]['subjectid'] = $subjectids[$title->getNamespace()][$title->getDbKey()];
}
}
}
@@ -223,17 +484,15 @@ class ApiQueryInfo extends ApiQueryBase {
ApiBase :: PARAM_DFLT => NULL,
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array (
- 'protection'
+ 'protection',
+ 'talkid',
+ 'subjectid'
)),
'token' => array (
ApiBase :: PARAM_DFLT => NULL,
ApiBase :: PARAM_ISMULTI => true,
- ApiBase :: PARAM_TYPE => array (
- 'edit',
- 'delete',
- 'protect',
- 'move',
- )),
+ ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions())
+ )
);
}
@@ -241,7 +500,9 @@ class ApiQueryInfo extends ApiQueryBase {
return array (
'prop' => array (
'Which additional properties to get:',
- ' "protection" - List the protection level of each page'
+ ' "protection" - List the protection level of each page',
+ ' "talkid" - The page ID of the talk page for each non-talk page',
+ ' "subjectid" - The page ID of the parent page for each talk page'
),
'token' => 'Request a token to perform a data-modifying action on a page',
);
@@ -260,7 +521,6 @@ class ApiQueryInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryInfo.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryInfo.php 37191 2008-07-06 18:43:06Z brion $';
}
}
-
diff --git a/includes/api/ApiQueryLangLinks.php b/includes/api/ApiQueryLangLinks.php
index 04a930db..e7d84fc3 100644
--- a/includes/api/ApiQueryLangLinks.php
+++ b/includes/api/ApiQueryLangLinks.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query module to list all langlinks (links to correspanding foreign language pages).
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryLangLinks extends ApiQueryBase {
@@ -40,6 +40,10 @@ class ApiQueryLangLinks extends ApiQueryBase {
}
public function execute() {
+ if ( $this->getPageSet()->getGoodTitleCount() == 0 )
+ return;
+
+ $params = $this->extractRequestParams();
$this->addFields(array (
'll_from',
'll_lang',
@@ -48,14 +52,36 @@ class ApiQueryLangLinks extends ApiQueryBase {
$this->addTables('langlinks');
$this->addWhereFld('ll_from', array_keys($this->getPageSet()->getGoodTitles()));
- $this->addOption('ORDER BY', "ll_from, ll_lang");
+ if(!is_null($params['continue'])) {
+ $cont = explode('|', $params['continue']);
+ if(count($cont) != 2)
+ $this->dieUsage("Invalid continue param. You should pass the " .
+ "original value returned by the previous query", "_badcontinue");
+ $llfrom = intval($cont[0]);
+ $lllang = $this->getDb()->strencode($cont[1]);
+ $this->addWhere("ll_from > $llfrom OR ".
+ "(ll_from = $llfrom AND ".
+ "ll_lang >= '$lllang')");
+ }
+ # Don't order by ll_from if it's constant in the WHERE clause
+ if(count($this->getPageSet()->getGoodTitles()) == 1)
+ $this->addOption('ORDER BY', 'll_lang');
+ else
+ $this->addOption('ORDER BY', 'll_from, ll_lang');
+ $this->addOption('LIMIT', $params['limit'] + 1);
$res = $this->select(__METHOD__);
$data = array();
- $lastId = 0; // database has no ID 0
+ $lastId = 0; // database has no ID 0
+ $count = 0;
$db = $this->getDB();
while ($row = $db->fetchObject($res)) {
-
+ if (++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('continue', "{$row->ll_from}|{$row->ll_lang}");
+ break;
+ }
if ($lastId != $row->ll_from) {
if($lastId != 0) {
$this->addPageSubItems($lastId, $data);
@@ -64,7 +90,7 @@ class ApiQueryLangLinks extends ApiQueryBase {
$lastId = $row->ll_from;
}
- $entry = array('lang'=>$row->ll_lang);
+ $entry = array('lang' => $row->ll_lang);
ApiResult :: setContent($entry, $row->ll_title);
$data[] = $entry;
}
@@ -76,6 +102,26 @@ class ApiQueryLangLinks extends ApiQueryBase {
$db->freeResult($res);
}
+ public function getAllowedParams() {
+ return array(
+ 'limit' => array(
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'continue' => null,
+ );
+ }
+
+ public function getParamDescription () {
+ return array(
+ 'limit' => 'How many langlinks to return',
+ 'continue' => 'When more results are available, use this to continue',
+ );
+ }
+
public function getDescription() {
return 'Returns all interlanguage links from the given page(s)';
}
@@ -88,7 +134,6 @@ class ApiQueryLangLinks extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLangLinks.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryLangLinks.php 37534 2008-07-10 21:08:37Z brion $';
}
}
-
diff --git a/includes/api/ApiQueryLinks.php b/includes/api/ApiQueryLinks.php
index d77e627a..546a599d 100644
--- a/includes/api/ApiQueryLinks.php
+++ b/includes/api/ApiQueryLinks.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query module to list all wiki links on a given set of pages.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryLinks extends ApiQueryGeneratorBase {
@@ -41,7 +41,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
private $table, $prefix, $description;
public function __construct($query, $moduleName) {
-
+
switch ($moduleName) {
case self::LINKS :
$this->table = 'pagelinks';
@@ -84,16 +84,54 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
$this->addTables($this->table);
$this->addWhereFld($this->prefix . '_from', array_keys($this->getPageSet()->getGoodTitles()));
$this->addWhereFld($this->prefix . '_namespace', $params['namespace']);
- $this->addOption('ORDER BY', str_replace('pl_', $this->prefix . '_', 'pl_from, pl_namespace, pl_title'));
+
+ if(!is_null($params['continue'])) {
+ $cont = explode('|', $params['continue']);
+ if(count($cont) != 3)
+ $this->dieUsage("Invalid continue param. You should pass the " .
+ "original value returned by the previous query", "_badcontinue");
+ $plfrom = intval($cont[0]);
+ $plns = intval($cont[1]);
+ $pltitle = $this->getDb()->strencode($this->titleToKey($cont[2]));
+ $this->addWhere("{$this->prefix}_from > $plfrom OR ".
+ "({$this->prefix}_from = $plfrom AND ".
+ "({$this->prefix}_namespace > $plns OR ".
+ "({$this->prefix}_namespace = $plns AND ".
+ "{$this->prefix}_title >= '$pltitle')))");
+ }
+
+ # Here's some MySQL craziness going on: if you use WHERE foo='bar'
+ # and later ORDER BY foo MySQL doesn't notice the ORDER BY is pointless
+ # but instead goes and filesorts, because the index for foo was used
+ # already. To work around this, we drop constant fields in the WHERE
+ # clause from the ORDER BY clause
+ $order = array();
+ if(count($this->getPageSet()->getGoodTitles()) != 1)
+ $order[] = "{$this->prefix}_from";
+ if(count($params['namespace']) != 1)
+ $order[] = "{$this->prefix}_namespace";
+ $order[] = "{$this->prefix}_title";
+ $this->addOption('ORDER BY', implode(", ", $order));
+ $this->addOption('USE INDEX', "{$this->prefix}_from");
+ $this->addOption('LIMIT', $params['limit'] + 1);
$db = $this->getDB();
$res = $this->select(__METHOD__);
if (is_null($resultPageSet)) {
-
+
$data = array();
- $lastId = 0; // database has no ID 0
+ $lastId = 0; // database has no ID 0
+ $count = 0;
while ($row = $db->fetchObject($res)) {
+ if(++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('continue',
+ "{$row->pl_from}|{$row->pl_namespace}|" .
+ $this->keyToTitle($row->pl_title));
+ break;
+ }
if ($lastId != $row->pl_from) {
if($lastId != 0) {
$this->addPageSubItems($lastId, $data);
@@ -114,7 +152,16 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
} else {
$titles = array();
+ $count = 0;
while ($row = $db->fetchObject($res)) {
+ if(++$count > $params['limit']) {
+ // We've reached the one extra which shows that
+ // there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('continue',
+ "{$row->pl_from}|{$row->pl_namespace}|" .
+ $this->keyToTitle($row->pl_title));
+ break;
+ }
$titles[] = Title :: makeTitle($row->pl_namespace, $row->pl_title);
}
$resultPageSet->populateFromTitles($titles);
@@ -129,15 +176,25 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
'namespace' => array(
ApiBase :: PARAM_TYPE => 'namespace',
ApiBase :: PARAM_ISMULTI => true
- )
+ ),
+ 'limit' => array(
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'continue' => null,
);
}
public function getParamDescription()
{
return array(
- 'namespace' => "Show {$this->description}s in this namespace(s) only"
- );
+ 'namespace' => "Show {$this->description}s in this namespace(s) only",
+ 'limit' => "How many {$this->description}s to return",
+ 'continue' => 'When more results are available, use this to continue',
+ );
}
public function getDescription() {
@@ -156,7 +213,6 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLinks.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryLinks.php 37909 2008-07-22 13:26:15Z catrope $';
}
}
-
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
index e25e5275..47a526bb 100644
--- a/includes/api/ApiQueryLogEvents.php
+++ b/includes/api/ApiQueryLogEvents.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query action to List the log events, with optional filtering by various parameters.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryLogEvents extends ApiQueryBase {
@@ -40,7 +40,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
public function execute() {
- $params = $this->extractRequestParams();
+ $params = $this->extractRequestParams();
$db = $this->getDB();
$prop = $params['prop'];
@@ -54,19 +54,26 @@ class ApiQueryLogEvents extends ApiQueryBase {
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");
+ $hideLogs = LogEventsList::getExcludeClause($db);
+ if($hideLogs !== false)
+ $this->addWhere($hideLogs);
+
+ // Order is significant here
+ $this->addTables(array('user', 'page', 'logging'));
+ $this->addJoinConds(array(
+ 'page' => array('LEFT JOIN',
+ array( 'log_namespace=page_namespace',
+ 'log_title=page_title'))));
+ $this->addWhere('user_id=log_user');
+ $this->addOption('USE INDEX', array('logging' => 'times')); // default, may change
$this->addFields(array (
'log_type',
'log_action',
'log_timestamp',
));
-
- // FIXME: Fake out log_id for now until the column is live on Wikimedia
- // $this->addFieldsIf('log_id', $this->fld_ids);
+
+ $this->addFieldsIf('log_id', $this->fld_ids);
$this->addFieldsIf('page_id', $this->fld_ids);
$this->addFieldsIf('log_user', $this->fld_user);
$this->addFieldsIf('user_name', $this->fld_user);
@@ -74,10 +81,14 @@ class ApiQueryLogEvents extends ApiQueryBase {
$this->addFieldsIf('log_title', $this->fld_title);
$this->addFieldsIf('log_comment', $this->fld_comment);
$this->addFieldsIf('log_params', $this->fld_details);
-
$this->addWhereFld('log_deleted', 0);
- $this->addWhereFld('log_type', $params['type']);
+
+ if( !is_null($params['type']) ) {
+ $this->addWhereFld('log_type', $params['type']);
+ $this->addOption('USE INDEX', array('logging' => array('type_time')));
+ }
+
$this->addWhereRange('log_timestamp', $params['dir'], $params['start'], $params['end']);
$limit = $params['limit'];
@@ -91,6 +102,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
if (!$userid)
$this->dieUsage("User name $user not found", 'param_user');
$this->addWhereFld('log_user', $userid);
+ $this->addOption('USE INDEX', array('logging' => array('user_time','page_time')));
}
$title = $params['title'];
@@ -100,6 +112,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
$this->dieUsage("Bad title value '$title'", 'param_title');
$this->addWhereFld('log_namespace', $titleObj->getNamespace());
$this->addWhereFld('log_title', $titleObj->getDBkey());
+ $this->addOption('USE INDEX', array('logging' => array('user_time','page_time')));
}
$data = array ();
@@ -126,26 +139,24 @@ class ApiQueryLogEvents extends ApiQueryBase {
$vals = array();
if ($this->fld_ids) {
- // FIXME: Fake out log_id for now until the column is live on Wikimedia
- // $vals['logid'] = intval($row->log_id);
- $vals['logid'] = 0;
+ $vals['logid'] = intval($row->log_id);
$vals['pageid'] = intval($row->page_id);
}
-
+
if ($this->fld_title) {
$title = Title :: makeTitle($row->log_namespace, $row->log_title);
ApiQueryBase :: addTitleInfo($vals, $title);
}
-
+
if ($this->fld_type) {
$vals['type'] = $row->log_type;
$vals['action'] = $row->log_action;
}
-
+
if ($this->fld_details && $row->log_params !== '') {
$params = explode("\n", $row->log_params);
switch ($row->log_type) {
- case 'move':
+ case 'move':
if (isset ($params[0])) {
$title = Title :: newFromText($params[0]);
if ($title) {
@@ -175,7 +186,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
$params = null;
break;
}
-
+
if (isset($params)) {
$this->getResult()->setIndexedTagName($params, 'param');
$vals = array_merge($vals, $params);
@@ -193,7 +204,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
if ($this->fld_comment && !empty ($row->log_comment)) {
$vals['comment'] = $row->log_comment;
}
-
+
return $vals;
}
@@ -215,7 +226,6 @@ class ApiQueryLogEvents extends ApiQueryBase {
)
),
'type' => array (
- ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => $wgLogTypes
),
'start' => array (
@@ -267,7 +277,6 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLogEvents.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryLogEvents.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiQueryRandom.php b/includes/api/ApiQueryRandom.php
index b8282098..046157a6 100644
--- a/includes/api/ApiQueryRandom.php
+++ b/includes/api/ApiQueryRandom.php
@@ -30,24 +30,24 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to get list of random pages
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
-
+
class ApiQueryRandom extends ApiQueryGeneratorBase {
public function __construct($query, $moduleName) {
parent :: __construct($query, $moduleName, 'rn');
}
-
+
public function execute() {
$this->run();
}
-
+
public function executeGenerator($resultPageSet) {
$this->run($resultPageSet);
}
-
+
protected function prepareQuery($randstr, $limit, $namespace, &$resultPageSet) {
$this->resetQueryParams();
$this->addTables('page');
@@ -104,7 +104,7 @@ if (!defined('MEDIAWIKI')) {
if(is_null($resultPageSet)) {
$result->setIndexedTagName($data, 'page');
$result->addValue('query', $this->getModuleName(), $data);
- }
+ }
}
private function extractRowInfo($row) {
@@ -115,7 +115,7 @@ if (!defined('MEDIAWIKI')) {
$vals['id'] = $row->page_id;
return $vals;
}
-
+
public function getAllowedParams() {
return array (
'namespace' => array(
diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php
index 44093854..2b8c6a92 100644
--- a/includes/api/ApiQueryRecentChanges.php
+++ b/includes/api/ApiQueryRecentChanges.php
@@ -31,8 +31,8 @@ if (!defined('MEDIAWIKI')) {
/**
* A query action to enumerate the recent changes that were done to the wiki.
* Various filters are supported.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryRecentChanges extends ApiQueryBase {
@@ -43,26 +43,48 @@ class ApiQueryRecentChanges extends ApiQueryBase {
private $fld_comment = false, $fld_user = false, $fld_flags = false,
$fld_timestamp = false, $fld_title = false, $fld_ids = false,
$fld_sizes = false;
-
+
/**
* Generates and outputs the result of this query based upon the provided parameters.
*/
public function execute() {
/* Initialize vars */
- $limit = $prop = $namespace = $show = $type = $dir = $start = $end = null;
-
+ $limit = $prop = $namespace = $titles = $show = $type = $dir = $start = $end = null;
+
/* Get the parameters of the request. */
extract($this->extractRequestParams());
/* Build our basic query. Namely, something along the lines of:
- * SELECT * from recentchanges WHERE rc_timestamp > $start
- * AND rc_timestamp < $end AND rc_namespace = $namespace
+ * SELECT * FROM recentchanges WHERE rc_timestamp > $start
+ * AND rc_timestamp < $end AND rc_namespace = $namespace
* AND rc_deleted = '0'
*/
+ $db = $this->getDB();
$this->addTables('recentchanges');
+ $this->addOption('USE INDEX', array('recentchanges' => 'rc_timestamp'));
$this->addWhereRange('rc_timestamp', $dir, $start, $end);
$this->addWhereFld('rc_namespace', $namespace);
$this->addWhereFld('rc_deleted', 0);
+ if(!empty($titles))
+ {
+ $lb = new LinkBatch;
+ foreach($titles as $t)
+ {
+ $obj = Title::newFromText($t);
+ $lb->addObj($obj);
+ if($obj->getNamespace() < 0)
+ {
+ // LinkBatch refuses these, but we need them anyway
+ if(!array_key_exists($obj->getNamespace(), $lb->data))
+ $lb->data[$obj->getNamespace()] = array();
+ $lb->data[$obj->getNamespace()][$obj->getDbKey()] = 1;
+ }
+ }
+ $where = $lb->constructSet('rc', $this->getDb());
+ if($where != '')
+ $this->addWhere($where);
+ }
+
if(!is_null($type))
$this->addWhereFld('rc_type', $this->parseRCType($type));
@@ -70,12 +92,19 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$show = array_flip($show);
/* Check for conflicting parameters. */
- if ((isset ($show['minor']) && isset ($show['!minor']))
- || (isset ($show['bot']) && isset ($show['!bot']))
- || (isset ($show['anon']) && isset ($show['!anon']))) {
-
+ if ((isset ($show['minor']) && isset ($show['!minor']))
+ || (isset ($show['bot']) && isset ($show['!bot']))
+ || (isset ($show['anon']) && isset ($show['!anon']))
+ || (isset ($show['redirect']) && isset ($show['!redirect']))
+ || (isset ($show['patrolled']) && isset ($show['!patrolled']))) {
+
$this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show');
}
+
+ // Check permissions
+ global $wgUser;
+ if((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->isAllowed('patrol'))
+ $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied');
/* Add additional conditions to query depending upon parameters. */
$this->addWhereIf('rc_minor = 0', isset ($show['!minor']));
@@ -84,6 +113,11 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$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->addWhereIf('rc_patrolled = 0', isset($show['!patrolled']));
+ $this->addWhereIf('rc_patrolled != 0', isset($show['patrolled']));
+ $this->addWhereIf('page_is_redirect = 1', isset ($show['redirect']));
+ // Don't throw log entries out the window here
+ $this->addWhereIf('page_is_redirect = 0 OR page_is_redirect IS NULL', isset ($show['!redirect']));
}
/* Add the fields we're concerned with to out query. */
@@ -108,13 +142,19 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$this->fld_title = isset ($prop['title']);
$this->fld_ids = isset ($prop['ids']);
$this->fld_sizes = isset ($prop['sizes']);
+ $this->fld_redirect = isset($prop['redirect']);
+ $this->fld_patrolled = isset($prop['patrolled']);
+
+ global $wgUser;
+ if($this->fld_patrolled && !$wgUser->isAllowed('patrol'))
+ $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied');
/* Add fields to our query if they are specified as a needed parameter. */
- $this->addFieldsIf('rc_id', $this->fld_ids);
- $this->addFieldsIf('rc_cur_id', $this->fld_ids);
- $this->addFieldsIf('rc_this_oldid', $this->fld_ids);
- $this->addFieldsIf('rc_last_oldid', $this->fld_ids);
- $this->addFieldsIf('rc_comment', $this->fld_comment);
+ $this->addFieldsIf('rc_id', $this->fld_ids);
+ $this->addFieldsIf('rc_cur_id', $this->fld_ids);
+ $this->addFieldsIf('rc_this_oldid', $this->fld_ids);
+ $this->addFieldsIf('rc_last_oldid', $this->fld_ids);
+ $this->addFieldsIf('rc_comment', $this->fld_comment);
$this->addFieldsIf('rc_user', $this->fld_user);
$this->addFieldsIf('rc_user_text', $this->fld_user);
$this->addFieldsIf('rc_minor', $this->fld_flags);
@@ -122,15 +162,18 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$this->addFieldsIf('rc_new', $this->fld_flags);
$this->addFieldsIf('rc_old_len', $this->fld_sizes);
$this->addFieldsIf('rc_new_len', $this->fld_sizes);
+ $this->addFieldsIf('rc_patrolled', $this->fld_patrolled);
+ if($this->fld_redirect || isset($show['redirect']) || isset($show['!redirect']))
+ {
+ $this->addTables('page');
+ $this->addJoinConds(array('page' => array('LEFT JOIN', array('rc_namespace=page_namespace', 'rc_title=page_title'))));
+ $this->addFields('page_is_redirect');
+ }
}
-
- /* Specify the limit for our query. It's $limit+1 because we (possibly) need to
+ /* Specify the limit for our query. It's $limit+1 because we (possibly) need to
* generate a "continue" parameter, to allow paging. */
$this->addOption('LIMIT', $limit +1);
- /* Specify the index to use in the query as rc_timestamp, instead of rc_revid (default). */
- $this->addOption('USE INDEX', 'rc_timestamp');
-
$data = array ();
$count = 0;
@@ -148,7 +191,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
/* Extract the data from a single row. */
$vals = $this->extractRowInfo($row);
-
+
/* Add that row's data to our final output. */
if($vals)
$data[] = $vals;
@@ -240,9 +283,17 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$vals['comment'] = $row->rc_comment;
}
+ if ($this->fld_redirect)
+ if($row->page_is_redirect)
+ $vals['redirect'] = '';
+
+ /* Add the patrolled flag */
+ if ($this->fld_patrolled && $row->rc_patrolled == 1)
+ $vals['patrolled'] = '';
+
return $vals;
}
-
+
private function parseRCType($type)
{
if(is_array($type))
@@ -258,7 +309,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
case 'new': return RC_NEW;
case 'log': return RC_LOG;
}
- }
+ }
public function getAllowedParams() {
return array (
@@ -279,6 +330,9 @@ class ApiQueryRecentChanges extends ApiQueryBase {
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => 'namespace'
),
+ 'titles' => array(
+ ApiBase :: PARAM_ISMULTI => true
+ ),
'prop' => array (
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_DFLT => 'title|timestamp|ids',
@@ -289,7 +343,9 @@ class ApiQueryRecentChanges extends ApiQueryBase {
'timestamp',
'title',
'ids',
- 'sizes'
+ 'sizes',
+ 'redirect',
+ 'patrolled'
)
),
'show' => array (
@@ -300,7 +356,11 @@ class ApiQueryRecentChanges extends ApiQueryBase {
'bot',
'!bot',
'anon',
- '!anon'
+ '!anon',
+ 'redirect',
+ '!redirect',
+ 'patrolled',
+ '!patrolled'
)
),
'limit' => array (
@@ -314,7 +374,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array (
'edit',
- 'new',
+ 'new',
'log'
)
)
@@ -327,13 +387,14 @@ class ApiQueryRecentChanges extends ApiQueryBase {
'end' => 'The timestamp to end enumerating.',
'dir' => 'In which direction to enumerate.',
'namespace' => 'Filter log entries to only this namespace(s)',
+ 'titles' => 'Filter log entries to only these page titles',
'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'
),
'type' => 'Which types of changes to show.',
- 'limit' => 'How many total pages to return.'
+ 'limit' => 'How many total changes to return.'
);
}
@@ -348,7 +409,6 @@ class ApiQueryRecentChanges extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 37909 2008-07-22 13:26:15Z catrope $';
}
}
-
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php
index e22d3b30..1fd2d7c6 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -30,10 +30,10 @@ if (!defined('MEDIAWIKI')) {
/**
* A query action to enumerate revisions of a given page, or show top revisions of multiple pages.
- * Various pieces of information may be shown - flags, comments, and the actual wiki markup of the rev.
- * In the enumeration mode, ranges of revisions may be requested and filtered.
- *
- * @addtogroup API
+ * Various pieces of information may be shown - flags, comments, and the actual wiki markup of the rev.
+ * In the enumeration mode, ranges of revisions may be requested and filtered.
+ *
+ * @ingroup API
*/
class ApiQueryRevisions extends ApiQueryBase {
@@ -44,16 +44,45 @@ class ApiQueryRevisions extends ApiQueryBase {
private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false,
$fld_comment = false, $fld_user = false, $fld_content = false;
+ protected function getTokenFunctions() {
+ // tokenname => function
+ // function prototype is func($pageid, $title, $rev)
+ // should return token or false
+
+ // Don't call the hooks twice
+ if(isset($this->tokenFunctions))
+ return $this->tokenFunctions;
+
+ // If we're in JSON callback mode, no tokens can be obtained
+ if(!is_null($this->getMain()->getRequest()->getVal('callback')))
+ return array();
+
+ $this->tokenFunctions = array(
+ 'rollback' => array( 'ApiQueryRevisions','getRollbackToken' )
+ );
+ wfRunHooks('APIQueryRevisionsTokens', array(&$this->tokenFunctions));
+ return $this->tokenFunctions;
+ }
+
+ public static function getRollbackToken($pageid, $title, $rev)
+ {
+ global $wgUser;
+ if(!$wgUser->isAllowed('rollback'))
+ return false;
+ return $wgUser->editToken(array($title->getPrefixedText(),
+ $rev->getUserText()));
+ }
+
public function execute() {
- $limit = $startid = $endid = $start = $end = $dir = $prop = $user = $excludeuser = $token = null;
+ $limit = $startid = $endid = $start = $end = $dir = $prop = $user = $excludeuser = $expandtemplates = $section = $token = null;
extract($this->extractRequestParams(false));
// 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 extremely
- // difficult to manage continuations and require additional SQL indexes
+ // Enumerating revisions on multiple pages make it extremely
+ // difficult to manage continuations and require additional SQL indexes
$enumRevMode = (!is_null($user) || !is_null($excludeuser) || !is_null($limit) || !is_null($startid) || !is_null($endid) || $dir === 'newer' || !is_null($start) || !is_null($end));
-
+
$pageSet = $this->getPageSet();
$pageCount = $pageSet->getGoodTitleCount();
@@ -70,35 +99,26 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->dieUsage('titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start and end parameters may only be used on a single page.', 'multpages');
$this->addTables('revision');
- $this->addWhere('rev_deleted=0');
+ $this->addFields( Revision::selectFields() );
$prop = array_flip($prop);
- // These field are needed regardless of the client requesting them
- $this->addFields('rev_id');
- $this->addFields('rev_page');
-
// Optional fields
$this->fld_ids = isset ($prop['ids']);
// $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed?
- $this->fld_flags = $this->addFieldsIf('rev_minor_edit', isset ($prop['flags']));
- $this->fld_timestamp = $this->addFieldsIf('rev_timestamp', isset ($prop['timestamp']));
- $this->fld_comment = $this->addFieldsIf('rev_comment', isset ($prop['comment']));
- $this->fld_size = $this->addFieldsIf('rev_len', isset ($prop['size']));
- $this->tok_rollback = false; // Prevent PHP undefined property notice
- if(!is_null($token))
- {
- $this->tok_rollback = $this->getTokenFlag($token, 'rollback');
+ $this->fld_flags = isset ($prop['flags']);
+ $this->fld_timestamp = isset ($prop['timestamp']);
+ $this->fld_comment = isset ($prop['comment']);
+ $this->fld_size = isset ($prop['size']);
+ $this->fld_user = isset ($prop['user']);
+ $this->token = $token;
+
+ if ( !is_null($this->token) || ( $this->fld_content && $this->expandTemplates ) || $pageCount > 0) {
+ $this->addTables( 'page' );
+ $this->addWhere('page_id=rev_page');
+ $this->addFields( Revision::selectPageFields() );
}
- if (isset ($prop['user'])) {
- $this->addFields('rev_user');
- $this->addFields('rev_user_text');
- $this->fld_user = true;
- }
- else if($this->tok_rollback)
- $this->addFields('rev_user_text');
-
if (isset ($prop['content'])) {
// For each page we will request, the user must have read rights for that page
@@ -112,12 +132,15 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->addTables('text');
$this->addWhere('rev_text_id=old_id');
$this->addFields('old_id');
- $this->addFields('old_text');
- $this->addFields('old_flags');
+ $this->addFields( Revision::selectTextFields() );
$this->fld_content = true;
-
+
$this->expandTemplates = $expandtemplates;
+ if(isset($section))
+ $this->section = $section;
+ else
+ $this->section = false;
}
$userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
@@ -137,13 +160,13 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->dieUsage('end and endid cannot be used together', 'badparams');
if(!is_null($user) && !is_null( $excludeuser))
- $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
-
+ $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
+
// This code makes an assumption that sorting by rev_id and rev_timestamp produces
// the same result. This way users may request revisions starting at a given time,
// but to page through results use the rev_id returned after each page.
- // Switching to rev_id removes the potential problem of having more than
- // one row with the same timestamp for the same page.
+ // 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.
if (is_null($startid) && is_null($endid))
@@ -158,7 +181,7 @@ class ApiQueryRevisions extends ApiQueryBase {
// There is only one ID, use it
$this->addWhereFld('rev_page', current(array_keys($pageSet->getGoodTitles())));
-
+
if(!is_null($user)) {
$this->addWhereFld('rev_user_text', $user);
} elseif (!is_null( $excludeuser)) {
@@ -177,7 +200,6 @@ class ApiQueryRevisions extends ApiQueryBase {
elseif ($pageCount > 0) {
// When working in multi-page non-enumeration mode,
// limit to the latest revision only
- $this->addTables('page');
$this->addWhere('page_id=rev_page');
$this->addWhere('page_latest=rev_id');
$this->validateLimit('page_count', $pageCount, 1, $userMax, $botMax);
@@ -207,17 +229,18 @@ class ApiQueryRevisions extends ApiQueryBase {
break;
}
+ $revision = new Revision( $row );
$this->getResult()->addValue(
array (
'query',
'pages',
- intval($row->rev_page),
+ $revision->getPage(),
'revisions'),
null,
- $this->extractRowInfo($row));
+ $this->extractRowInfo( $revision ));
}
$db->freeResult($res);
-
+
// Ensure that all revisions are shown as '<rev>' elements
$result = $this->getResult();
if ($result->getIsRawMode()) {
@@ -230,53 +253,70 @@ class ApiQueryRevisions extends ApiQueryBase {
}
}
- private function extractRowInfo($row) {
+ private function extractRowInfo( $revision ) {
$vals = array ();
if ($this->fld_ids) {
- $vals['revid'] = intval($row->rev_id);
+ $vals['revid'] = $revision->getId();
// $vals['oldid'] = intval($row->rev_text_id); // todo: should this be exposed?
}
-
- if ($this->fld_flags && $row->rev_minor_edit)
+
+ if ($this->fld_flags && $revision->isMinor())
$vals['minor'] = '';
if ($this->fld_user) {
- $vals['user'] = $row->rev_user_text;
- if (!$row->rev_user)
+ $vals['user'] = $revision->getUserText();
+ if (!$revision->getUser())
$vals['anon'] = '';
}
if ($this->fld_timestamp) {
- $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rev_timestamp);
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp());
}
-
- if ($this->fld_size && !is_null($row->rev_len)) {
- $vals['size'] = intval($row->rev_len);
+
+ if ($this->fld_size && !is_null($revision->getSize())) {
+ $vals['size'] = $revision->getSize();
}
- if ($this->fld_comment && !empty ($row->rev_comment)) {
- $vals['comment'] = $row->rev_comment;
+ if ($this->fld_comment) {
+ $comment = $revision->getComment();
+ if (!empty($comment))
+ $vals['comment'] = $comment;
}
-
- if($this->tok_rollback || ($this->fld_content && $this->expandTemplates))
- $title = Title::newFromID($row->rev_page);
-
- if($this->tok_rollback) {
- global $wgUser;
- $vals['rollbacktoken'] = $wgUser->editToken(array($title->getPrefixedText(), $row->rev_user_text));
+
+ if(!is_null($this->token) || ($this->fld_content && $this->expandTemplates))
+ $title = $revision->getTitle();
+
+ if(!is_null($this->token))
+ {
+ $tokenFunctions = $this->getTokenFunctions();
+ foreach($this->token as $t)
+ {
+ $val = call_user_func($tokenFunctions[$t], $title->getArticleID(), $title, $revision);
+ if($val === false)
+ $this->setWarning("Action '$t' is not allowed for the current user");
+ else
+ $vals[$t . 'token'] = $val;
+ }
}
-
-
+
if ($this->fld_content) {
- $text = Revision :: getRevisionText($row);
+ global $wgParser;
+ $text = $revision->getText();
+ # Expand templates after getting section content because
+ # template-added sections don't count and Parser::preprocess()
+ # will have less input
+ if ($this->section !== false) {
+ $text = $wgParser->getSection( $text, $this->section, false);
+ if($text === false)
+ $this->dieUsage("There is no section {$this->section} in r".$revision->getId(), 'nosuchsection');
+ }
if ($this->expandTemplates) {
- global $wgParser;
$text = $wgParser->preprocess( $text, $title, new ParserOptions() );
}
ApiResult :: setContent($vals, $text);
- }
+ }
return $vals;
}
@@ -326,12 +366,13 @@ class ApiQueryRevisions extends ApiQueryBase {
'excludeuser' => array(
ApiBase :: PARAM_TYPE => 'user'
),
-
+
'expandtemplates' => false,
+ 'section' => array(
+ ApiBase :: PARAM_TYPE => 'integer'
+ ),
'token' => array(
- ApiBase :: PARAM_TYPE => array(
- 'rollback'
- ),
+ ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()),
ApiBase :: PARAM_ISMULTI => true
),
);
@@ -349,6 +390,7 @@ class ApiQueryRevisions extends ApiQueryBase {
'user' => 'only include revisions made by user',
'excludeuser' => 'exclude revisions made by user',
'expandtemplates' => 'expand templates in revision content',
+ 'section' => 'only retrieve the content of this section',
'token' => 'Which tokens to obtain for each revision',
);
}
@@ -382,7 +424,6 @@ class ApiQueryRevisions extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRevisions.php 31259 2008-02-25 14:14:55Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryRevisions.php 37300 2008-07-08 08:42:27Z btongminh $';
}
}
-
diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php
index b15f36ce..84a2ec63 100644
--- a/includes/api/ApiQuerySearch.php
+++ b/includes/api/ApiQuerySearch.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to perform full text search within wiki titles and content
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQuerySearch extends ApiQueryGeneratorBase {
@@ -52,7 +52,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
$limit = $params['limit'];
- $query = $params['search'];
+ $query = $params['search'];
if (is_null($query) || empty($query))
$this->dieUsage("empty search string is not allowed", 'param-search');
@@ -60,11 +60,14 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
$search->setLimitOffset( $limit+1, $params['offset'] );
$search->setNamespaces( $params['namespace'] );
$search->showRedirects = $params['redirects'];
-
+
if ($params['what'] == 'text')
$matches = $search->searchText( $query );
else
$matches = $search->searchTitle( $query );
+ if (is_null($matches))
+ $this->dieUsage("{$params['what']} search is disabled",
+ "search-{$params['what']}-disabled");
$data = array ();
$count = 0;
@@ -75,6 +78,9 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
break;
}
+ // Silently skip broken titles
+ if ($result->isBrokenTitle()) continue;
+
$title = $result->getTitle();
if (is_null($resultPageSet)) {
$data[] = array(
@@ -100,7 +106,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
'namespace' => array (
ApiBase :: PARAM_DFLT => 0,
ApiBase :: PARAM_TYPE => 'namespace',
- ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_ISMULTI => true,
),
'what' => array (
ApiBase :: PARAM_DFLT => 'title',
@@ -145,7 +151,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQuerySearch.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQuerySearch.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
-
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index 81af7997..1fd3b888 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -23,218 +23,278 @@
* http://www.gnu.org/copyleft/gpl.html
*/
-if (!defined('MEDIAWIKI')) {
+if( !defined('MEDIAWIKI') ) {
// Eclipse helper - will be ignored in production
- require_once ('ApiQueryBase.php');
+ require_once( 'ApiQueryBase.php' );
}
/**
* A query action to return meta information about the wiki site.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQuerySiteinfo extends ApiQueryBase {
- public function __construct($query, $moduleName) {
- parent :: __construct($query, $moduleName, 'si');
+ public function __construct( $query, $moduleName ) {
+ parent :: __construct( $query, $moduleName, 'si' );
}
public function execute() {
-
$params = $this->extractRequestParams();
-
- foreach ($params['prop'] as $p) {
- switch ($p) {
- default :
- ApiBase :: dieDebug(__METHOD__, "Unknown prop=$p");
- case 'general' :
- $this->appendGeneralInfo($p);
+ foreach( $params['prop'] as $p )
+ {
+ switch ( $p )
+ {
+ case 'general':
+ $this->appendGeneralInfo( $p );
+ break;
+ case 'namespaces':
+ $this->appendNamespaces( $p );
break;
- case 'namespaces' :
- $this->appendNamespaces($p);
+ case 'namespacealiases':
+ $this->appendNamespaceAliases( $p );
break;
- case 'namespacealiases' :
- $this->appendNamespaceAliases($p);
+ case 'specialpagealiases':
+ $this->appendSpecialPageAliases( $p );
break;
- case 'interwikimap' :
- $filteriw = isset($params['filteriw']) ? $params['filteriw'] : false;
- $this->appendInterwikiMap($p, $filteriw);
+ case 'interwikimap':
+ $filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
+ $this->appendInterwikiMap( $p, $filteriw );
break;
- case 'dbrepllag' :
- $this->appendDbReplLagInfo($p, $params['showalldb']);
+ case 'dbrepllag':
+ $this->appendDbReplLagInfo( $p, $params['showalldb'] );
break;
- case 'statistics' :
- $this->appendStatistics($p);
+ case 'statistics':
+ $this->appendStatistics( $p );
break;
+ case 'usergroups':
+ $this->appendUserGroups( $p );
+ break;
+ default :
+ ApiBase :: dieDebug( __METHOD__, "Unknown prop=$p" );
}
}
}
- protected function appendGeneralInfo($property) {
- global $wgSitename, $wgVersion, $wgCapitalLinks, $wgRightsCode, $wgRightsText, $wgLanguageCode, $IP;
-
- $data = array ();
+ protected function appendGeneralInfo( $property ) {
+ global $wgSitename, $wgVersion, $wgCapitalLinks, $wgRightsCode, $wgRightsText, $wgContLang;
+ global $wgLanguageCode, $IP, $wgEnableWriteAPI, $wgLang, $wgLocaltimezone, $wgLocalTZoffset;
+
+ $data = array();
$mainPage = Title :: newFromText(wfMsgForContent('mainpage'));
- $data['mainpage'] = $mainPage->getText();
+ $data['mainpage'] = $mainPage->getPrefixedText();
$data['base'] = $mainPage->getFullUrl();
$data['sitename'] = $wgSitename;
$data['generator'] = "MediaWiki $wgVersion";
- $svn = SpecialVersion::getSvnRevision ( $IP );
- if ( $svn ) $data['rev'] = $svn;
+ $svn = SpecialVersion::getSvnRevision( $IP );
+ if( $svn )
+ $data['rev'] = $svn;
$data['case'] = $wgCapitalLinks ? 'first-letter' : 'case-sensitive'; // 'case-insensitive' option is reserved for future
- if (isset($wgRightsCode))
+
+ if( isset( $wgRightsCode ) )
$data['rightscode'] = $wgRightsCode;
$data['rights'] = $wgRightsText;
$data['lang'] = $wgLanguageCode;
+ if( $wgContLang->isRTL() )
+ $data['rtl'] = '';
+ $data['fallback8bitEncoding'] = $wgLang->fallback8bitEncoding();
- $this->getResult()->addValue('query', $property, $data);
+ if( wfReadOnly() )
+ $data['readonly'] = '';
+ if( $wgEnableWriteAPI )
+ $data['writeapi'] = '';
+
+ $tz = $wgLocaltimezone;
+ $offset = $wgLocalTZoffset;
+ if( is_null( $tz ) ) {
+ $tz = 'UTC';
+ $offset = 0;
+ } elseif( is_null( $offset ) ) {
+ $offset = 0;
+ }
+ $data['timezone'] = $tz;
+ $data['timeoffset'] = $offset;
+
+ $this->getResult()->addValue( 'query', $property, $data );
}
-
- protected function appendNamespaces($property) {
+
+ protected function appendNamespaces( $property ) {
global $wgContLang;
-
- $data = array ();
- foreach ($wgContLang->getFormattedNamespaces() as $ns => $title) {
- $data[$ns] = array (
+ $data = array();
+ foreach( $wgContLang->getFormattedNamespaces() as $ns => $title )
+ {
+ $data[$ns] = array(
'id' => $ns
);
- ApiResult :: setContent($data[$ns], $title);
+ ApiResult :: setContent( $data[$ns], $title );
+ if( MWNamespace::hasSubpages($ns) )
+ $data[$ns]['subpages'] = '';
}
-
- $this->getResult()->setIndexedTagName($data, 'ns');
- $this->getResult()->addValue('query', $property, $data);
+
+ $this->getResult()->setIndexedTagName( $data, 'ns' );
+ $this->getResult()->addValue( 'query', $property, $data );
}
-
- protected function appendNamespaceAliases($property) {
+
+ protected function appendNamespaceAliases( $property ) {
global $wgNamespaceAliases;
-
- $data = array ();
- foreach ($wgNamespaceAliases as $title => $ns) {
- $item = array (
+ $data = array();
+ foreach( $wgNamespaceAliases as $title => $ns ) {
+ $item = array(
'id' => $ns
);
- ApiResult :: setContent($item, strtr($title, '_', ' '));
+ ApiResult :: setContent( $item, strtr( $title, '_', ' ' ) );
$data[] = $item;
}
-
- $this->getResult()->setIndexedTagName($data, 'ns');
- $this->getResult()->addValue('query', $property, $data);
+
+ $this->getResult()->setIndexedTagName( $data, 'ns' );
+ $this->getResult()->addValue( 'query', $property, $data );
}
-
- protected function appendInterwikiMap($property, $filter) {
- $this->resetQueryParams();
- $this->addTables('interwiki');
- $this->addFields(array('iw_prefix', 'iw_local', 'iw_url'));
-
- if($filter === 'local') {
- $this->addWhere('iw_local = 1');
- } elseif($filter === '!local') {
- $this->addWhere('iw_local = 0');
- } elseif($filter !== false) {
- ApiBase :: dieDebug(__METHOD__, "Unknown filter=$filter");
+ protected function appendSpecialPageAliases( $property ) {
+ global $wgLang;
+ $data = array();
+ foreach( $wgLang->getSpecialPageAliases() as $specialpage => $aliases )
+ {
+ $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
+ $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
+ $data[] = $arr;
}
+ $this->getResult()->setIndexedTagName( $data, 'specialpage' );
+ $this->getResult()->addValue( 'query', $property, $data );
+ }
+
+ protected function appendInterwikiMap( $property, $filter ) {
+ $this->resetQueryParams();
+ $this->addTables( 'interwiki' );
+ $this->addFields( array( 'iw_prefix', 'iw_local', 'iw_url' ) );
+
+ if( $filter === 'local' )
+ $this->addWhere( 'iw_local = 1' );
+ elseif( $filter === '!local' )
+ $this->addWhere( 'iw_local = 0' );
+ elseif( $filter !== false )
+ ApiBase :: dieDebug( __METHOD__, "Unknown filter=$filter" );
+
+ $this->addOption( 'ORDER BY', 'iw_prefix' );
- $this->addOption('ORDER BY', 'iw_prefix');
-
$db = $this->getDB();
- $res = $this->select(__METHOD__);
+ $res = $this->select( __METHOD__ );
$data = array();
- while($row = $db->fetchObject($res))
+ $langNames = Language::getLanguageNames();
+ while( $row = $db->fetchObject($res) )
{
+ $val = array();
$val['prefix'] = $row->iw_prefix;
- if ($row->iw_local == '1')
+ if( $row->iw_local == '1' )
$val['local'] = '';
// $val['trans'] = intval($row->iw_trans); // should this be exposed?
+ if( isset( $langNames[$row->iw_prefix] ) )
+ $val['language'] = $langNames[$row->iw_prefix];
$val['url'] = $row->iw_url;
-
+
$data[] = $val;
}
- $db->freeResult($res);
-
- $this->getResult()->setIndexedTagName($data, 'iw');
- $this->getResult()->addValue('query', $property, $data);
+ $db->freeResult( $res );
+
+ $this->getResult()->setIndexedTagName( $data, 'iw' );
+ $this->getResult()->addValue( 'query', $property, $data );
}
-
- protected function appendDbReplLagInfo($property, $includeAll) {
- global $wgLoadBalancer, $wgShowHostnames;
+ protected function appendDbReplLagInfo( $property, $includeAll ) {
+ global $wgShowHostnames;
$data = array();
-
- if ($includeAll) {
- if (!$wgShowHostnames)
+ if( $includeAll ) {
+ if ( !$wgShowHostnames )
$this->dieUsage('Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied');
-
- global $wgDBservers;
- $lags = $wgLoadBalancer->getLagTimes();
+
+ $lb = wfGetLB();
+ $lags = $lb->getLagTimes();
foreach( $lags as $i => $lag ) {
- $data[] = array (
- 'host' => $wgDBservers[$i]['host'],
- 'lag' => $lag);
+ $data[] = array(
+ 'host' => $lb->getServerName( $i ),
+ 'lag' => $lag
+ );
}
} else {
- list( $host, $lag ) = $wgLoadBalancer->getMaxLag();
- $data[] = array (
+ list( $host, $lag ) = wfGetLB()->getMaxLag();
+ $data[] = array(
'host' => $wgShowHostnames ? $host : '',
- 'lag' => $lag);
- }
+ 'lag' => $lag
+ );
+ }
$result = $this->getResult();
- $result->setIndexedTagName($data, 'db');
- $result->addValue('query', $property, $data);
- }
-
- protected function appendStatistics($property) {
- $data = array ();
- $data['pages'] = intval(SiteStats::pages());
- $data['articles'] = intval(SiteStats::articles());
- $data['views'] = intval(SiteStats::views());
- $data['edits'] = intval(SiteStats::edits());
- $data['images'] = intval(SiteStats::images());
- $data['users'] = intval(SiteStats::users());
- $data['admins'] = intval(SiteStats::admins());
- $data['jobs'] = intval(SiteStats::jobs());
- $this->getResult()->addValue('query', $property, $data);
- }
-
+ $result->setIndexedTagName( $data, 'db' );
+ $result->addValue( 'query', $property, $data );
+ }
+
+ protected function appendStatistics( $property ) {
+ $data = array();
+ $data['pages'] = intval( SiteStats::pages() );
+ $data['articles'] = intval( SiteStats::articles() );
+ $data['views'] = intval( SiteStats::views() );
+ $data['edits'] = intval( SiteStats::edits() );
+ $data['images'] = intval( SiteStats::images() );
+ $data['users'] = intval( SiteStats::users() );
+ $data['admins'] = intval( SiteStats::admins() );
+ $data['jobs'] = intval( SiteStats::jobs() );
+ $this->getResult()->addValue( 'query', $property, $data );
+ }
+
+ protected function appendUserGroups( $property ) {
+ global $wgGroupPermissions;
+ $data = array();
+ foreach( $wgGroupPermissions as $group => $permissions ) {
+ $arr = array( 'name' => $group, 'rights' => array_keys( $permissions, true ) );
+ $this->getResult()->setIndexedTagName( $arr['rights'], 'permission' );
+ $data[] = $arr;
+ }
+
+ $this->getResult()->setIndexedTagName( $data, 'group' );
+ $this->getResult()->addValue( 'query', $property, $data );
+ }
+
public function getAllowedParams() {
- return array (
-
- 'prop' => array (
+ return array(
+ 'prop' => array(
ApiBase :: PARAM_DFLT => 'general',
ApiBase :: PARAM_ISMULTI => true,
- ApiBase :: PARAM_TYPE => array (
+ ApiBase :: PARAM_TYPE => array(
'general',
'namespaces',
'namespacealiases',
+ 'specialpagealiases',
'interwikimap',
'dbrepllag',
'statistics',
- )),
-
- 'filteriw' => array (
- ApiBase :: PARAM_TYPE => array (
+ 'usergroups',
+ )
+ ),
+ 'filteriw' => array(
+ ApiBase :: PARAM_TYPE => array(
'local',
'!local',
- )),
-
+ )
+ ),
'showalldb' => false,
);
}
public function getParamDescription() {
- return array (
- 'prop' => array (
+ return array(
+ 'prop' => array(
'Which sysinfo properties to get:',
' "general" - Overall system information',
' "namespaces" - List of registered namespaces (localized)',
' "namespacealiases" - List of registered namespace aliases',
+ ' "specialpagealiases" - List of special page aliases',
' "statistics" - Returns site statistics',
' "interwikimap" - Returns interwiki map (optionally filtered)',
' "dbrepllag" - Returns database server with the highest replication lag',
+ ' "usergroups" - Returns user groups and the associated permissions',
),
'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
'showalldb' => 'List all database servers, not just the one lagging the most',
@@ -254,6 +314,6 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 30484 2008-02-03 19:29:59Z btongminh $';
+ return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 37034 2008-07-04 09:21:11Z vasilievvv $';
}
}
diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php
index 57d51cdb..c477acdb 100644
--- a/includes/api/ApiQueryUserContributions.php
+++ b/includes/api/ApiQueryUserContributions.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* This query action adds a list of a specified user's contributions to the output.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryContributions extends ApiQueryBase {
@@ -48,7 +48,7 @@ class ApiQueryContributions extends ApiQueryBase {
// Parse some parameters
$this->params = $this->extractRequestParams();
- $prop = array_flip($this->params['prop']);
+ $prop = array_flip($this->params['prop']);
$this->fld_ids = isset($prop['ids']);
$this->fld_title = isset($prop['title']);
$this->fld_comment = isset($prop['comment']);
@@ -59,12 +59,20 @@ class ApiQueryContributions extends ApiQueryBase {
$this->selectNamedDB('contributions', DB_SLAVE, 'contributions');
$db = $this->getDB();
- // Prepare query
- $this->usernames = array();
- if(!is_array($this->params['user']))
- $this->params['user'] = array($this->params['user']);
- foreach($this->params['user'] as $u)
- $this->prepareUsername($u);
+ if(isset($this->params['userprefix']))
+ {
+ $this->prefixMode = true;
+ $this->userprefix = $this->params['userprefix'];
+ }
+ else
+ {
+ $this->usernames = array();
+ if(!is_array($this->params['user']))
+ $this->params['user'] = array($this->params['user']);
+ foreach($this->params['user'] as $u)
+ $this->prepareUsername($u);
+ $this->prefixMode = false;
+ }
$this->prepareQuery();
//Do the actual query.
@@ -114,7 +122,7 @@ class ApiQueryContributions extends ApiQueryBase {
$this->dieUsage( 'User parameter may not be empty', 'param_user' );
}
}
-
+
/**
* Prepares the query and returns the limit of rows requested
*/
@@ -122,14 +130,20 @@ class ApiQueryContributions extends ApiQueryBase {
//We're after the revision table, and the corresponding page row for
//anything we retrieve.
- list ($tbl_page, $tbl_revision) = $this->getDB()->tableNamesN('page', 'revision');
- $this->addTables("$tbl_revision LEFT OUTER JOIN $tbl_page ON page_id=rev_page");
-
+ $this->addTables(array('revision', 'page'));
+ $this->addWhere('page_id=rev_page');
+
$this->addWhereFld('rev_deleted', 0);
// We only want pages by the specified users.
- $this->addWhereFld( 'rev_user_text', $this->usernames );
+ if($this->prefixMode)
+ $this->addWhere("rev_user_text LIKE '" . $this->getDb()->escapeLike($this->userprefix) . "%'");
+ else
+ $this->addWhereFld( 'rev_user_text', $this->usernames );
// ... and in the specified timeframe.
- $this->addWhereRange('rev_timestamp',
+ // Ensure the same sort order for rev_user_text and rev_timestamp
+ // so our query is indexed
+ $this->addWhereRange('rev_user_text', $this->params['dir'], null, null);
+ $this->addWhereRange('rev_timestamp',
$this->params['dir'], $this->params['start'], $this->params['end'] );
$this->addWhereFld('page_namespace', $this->params['namespace']);
@@ -146,22 +160,23 @@ class ApiQueryContributions extends ApiQueryBase {
// Mandatory fields: timestamp allows request continuation
// ns+title checks if the user has access rights for this page
- // user_text is necessary if multiple users were specified
+ // user_text is necessary if multiple users were specified
$this->addFields(array(
'rev_timestamp',
'page_namespace',
'page_title',
'rev_user_text',
));
-
+
$this->addFieldsIf('rev_page', $this->fld_ids);
- $this->addFieldsIf('rev_id', $this->fld_ids);
+ $this->addFieldsIf('rev_id', $this->fld_ids || $this->fld_flags);
+ $this->addFieldsIf('page_latest', $this->fld_flags);
// $this->addFieldsIf('rev_text_id', $this->fld_ids); // Should this field be exposed?
$this->addFieldsIf('rev_comment', $this->fld_comment);
$this->addFieldsIf('rev_minor_edit', $this->fld_flags);
$this->addFieldsIf('page_is_new', $this->fld_flags);
}
-
+
/**
* Extract fields from the database row and append them to a result array
*/
@@ -172,12 +187,12 @@ class ApiQueryContributions extends ApiQueryBase {
$vals['user'] = $row->rev_user_text;
if ($this->fld_ids) {
$vals['pageid'] = intval($row->rev_page);
- $vals['revid'] = intval($row->rev_id);
+ $vals['revid'] = intval($row->rev_id);
// $vals['textid'] = intval($row->rev_text_id); // todo: Should this field be exposed?
}
-
+
if ($this->fld_title)
- ApiQueryBase :: addTitleInfo($vals,
+ ApiQueryBase :: addTitleInfo($vals,
Title :: makeTitle($row->page_namespace, $row->page_title));
if ($this->fld_timestamp)
@@ -188,6 +203,8 @@ class ApiQueryContributions extends ApiQueryBase {
$vals['new'] = '';
if ($row->rev_minor_edit)
$vals['minor'] = '';
+ if ($row->page_latest == $row->rev_id)
+ $vals['top'] = '';
}
if ($this->fld_comment && !empty ($row->rev_comment))
@@ -214,6 +231,7 @@ class ApiQueryContributions extends ApiQueryBase {
'user' => array (
ApiBase :: PARAM_ISMULTI => true
),
+ 'userprefix' => null,
'dir' => array (
ApiBase :: PARAM_DFLT => 'older',
ApiBase :: PARAM_TYPE => array (
@@ -252,6 +270,7 @@ class ApiQueryContributions extends ApiQueryBase {
'start' => 'The start timestamp to return from.',
'end' => 'The end timestamp to return to.',
'user' => 'The user to retrieve contributions for.',
+ 'userprefix' => 'Retrieve contibutions for all users whose names begin with this value. Overrides ucuser.',
'dir' => 'The direction to search (older or newer).',
'namespace' => 'Only list contributions in these namespaces',
'prop' => 'Include additional pieces of information',
@@ -265,12 +284,12 @@ class ApiQueryContributions extends ApiQueryBase {
protected function getExamples() {
return array (
- 'api.php?action=query&list=usercontribs&ucuser=YurikBot'
+ 'api.php?action=query&list=usercontribs&ucuser=YurikBot',
+ 'api.php?action=query&list=usercontribs&ucuserprefix=217.121.114.',
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUserContributions.php 30578 2008-02-05 15:40:58Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryUserContributions.php 37383 2008-07-09 11:44:49Z btongminh $';
}
}
-
diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php
index 010d9f4f..2d55a352 100644
--- a/includes/api/ApiQueryUserInfo.php
+++ b/includes/api/ApiQueryUserInfo.php
@@ -30,8 +30,8 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to get information about the currently logged-in user
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryUserInfo extends ApiQueryBase {
@@ -52,7 +52,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
$r = $this->getCurrentUserInfo();
$result->addValue("query", $this->getModuleName(), $r);
}
-
+
protected function getCurrentUserInfo() {
global $wgUser;
$result = $this->getResult();
@@ -67,7 +67,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
$vals['blockedby'] = User::whoIs($wgUser->blockedBy());
$vals['blockreason'] = $wgUser->blockedFor();
}
- }
+ }
if (isset($this->prop['hasmsg']) && $wgUser->getNewtalk()) {
$vals['messages'] = '';
}
@@ -90,13 +90,13 @@ class ApiQueryUserInfo extends ApiQueryBase {
}
return $vals;
}
-
+
protected function getRateLimits()
{
global $wgUser, $wgRateLimits;
if(!$wgUser->isPingLimitable())
return array(); // No limits
-
+
// Find out which categories we belong to
$categories = array();
if($wgUser->isAnon())
@@ -110,7 +110,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
if(!$wgUser->isAnon())
$categories[] = 'newbie';
}
-
+
// Now get the actual limits
$retval = array();
foreach($wgRateLimits as $action => $limits)
@@ -121,7 +121,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
$retval[$action][$cat]['seconds'] = $limits[$cat][1];
}
return $retval;
- }
+ }
public function getAllowedParams() {
return array (
@@ -168,6 +168,6 @@ class ApiQueryUserInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUserInfo.php 30395 2008-02-01 14:46:46Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryUserInfo.php 35186 2008-05-22 16:39:43Z brion $';
}
}
diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php
index 144bfba2..a8147567 100644
--- a/includes/api/ApiQueryUsers.php
+++ b/includes/api/ApiQueryUsers.php
@@ -30,10 +30,10 @@ if (!defined('MEDIAWIKI')) {
/**
* Query module to get information about a list of users
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
-
+
class ApiQueryUsers extends ApiQueryBase {
public function __construct($query, $moduleName) {
@@ -50,7 +50,7 @@ if (!defined('MEDIAWIKI')) {
} else {
$this->prop = array();
}
-
+
if(is_array($params['users'])) {
$r = $this->getOtherUsersInfo($params['users']);
$result->setIndexedTagName($r, 'user');
@@ -63,38 +63,44 @@ if (!defined('MEDIAWIKI')) {
// Canonicalize user names
foreach($users as $u) {
$n = User::getCanonicalName($u);
- if($n === false)
+ if($n === false || $n === '')
$retval[] = array('name' => $u, 'invalid' => '');
else
$goodNames[] = $n;
}
+ if(empty($goodNames))
+ return $retval;
$db = $this->getDb();
- $userTable = $db->tableName('user');
- $tables = "$userTable AS u1";
+ $this->addTables('user', 'u1');
$this->addFields('u1.user_name');
$this->addWhereFld('u1.user_name', $goodNames);
$this->addFieldsIf('u1.user_editcount', isset($this->prop['editcount']));
-
+ $this->addFieldsIf('u1.user_registration', isset($this->prop['registration']));
+
if(isset($this->prop['groups'])) {
- $ug = $db->tableName('user_groups');
- $tables = "$tables LEFT JOIN $ug ON ug_user=u1.user_id";
+ $this->addTables('user_groups');
+ $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id')));
$this->addFields('ug_group');
}
if(isset($this->prop['blockinfo'])) {
- $ipb = $db->tableName('ipblocks');
- $tables = "$tables LEFT JOIN $ipb ON ipb_user=u1.user_id";
- $tables = "$tables LEFT JOIN $userTable AS u2 ON ipb_by=u2.user_id";
- $this->addFields(array('ipb_reason', 'u2.user_name AS blocker_name'));
+ $this->addTables('ipblocks');
+ $this->addTables('user', 'u2');
+ $u2 = $this->getAliasedName('user', 'u2');
+ $this->addJoinConds(array(
+ 'ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'),
+ $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id')));
+ $this->addFields(array('ipb_reason', 'u2.user_name blocker_name'));
}
- $this->addTables($tables);
-
+
$data = array();
$res = $this->select(__METHOD__);
while(($r = $db->fetchObject($res))) {
$data[$r->user_name]['name'] = $r->user_name;
if(isset($this->prop['editcount']))
$data[$r->user_name]['editcount'] = $r->user_editcount;
+ if(isset($this->prop['registration']))
+ $data[$r->user_name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $r->user_registration);
if(isset($this->prop['groups']))
// This row contains only one group, others will be added from other rows
if(!is_null($r->ug_group))
@@ -105,7 +111,7 @@ if (!defined('MEDIAWIKI')) {
$data[$r->user_name]['blockreason'] = $r->ipb_reason;
}
}
-
+
// Second pass: add result data to $retval
foreach($goodNames as $u) {
if(!isset($data[$u]))
@@ -116,7 +122,7 @@ if (!defined('MEDIAWIKI')) {
$retval[] = $data[$u];
}
}
- return $retval;
+ return $retval;
}
public function getAllowedParams() {
@@ -127,7 +133,8 @@ if (!defined('MEDIAWIKI')) {
ApiBase :: PARAM_TYPE => array (
'blockinfo',
'groups',
- 'editcount'
+ 'editcount',
+ 'registration'
)
),
'users' => array(
@@ -157,6 +164,6 @@ if (!defined('MEDIAWIKI')) {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUserInfo.php 30128 2008-01-24 17:59:07Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryUsers.php 38183 2008-07-29 12:58:04Z rotem $';
}
}
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index 91a0c951..d17e83f6 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -31,8 +31,8 @@ if (!defined('MEDIAWIKI')) {
/**
* This query action allows clients to retrieve a list of recently modified pages
* that are part of the logged-in user's watchlist.
- *
- * @addtogroup API
+ *
+ * @ingroup API
*/
class ApiQueryWatchlist extends ApiQueryGeneratorBase {
@@ -50,7 +50,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
private $fld_ids = false, $fld_title = false, $fld_patrol = false, $fld_flags = false,
$fld_timestamp = false, $fld_user = false, $fld_comment = false, $fld_sizes = false;
-
+
private function run($resultPageSet = null) {
global $wgUser, $wgDBtype;
@@ -122,7 +122,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'recentchanges'
));
- $userId = $wgUser->getID();
+ $userId = $wgUser->getId();
$this->addWhere(array (
'wl_namespace = rc_namespace',
'wl_title = rc_title',
@@ -134,15 +134,15 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$this->addWhereRange('rc_timestamp', $dir, $start, $end);
$this->addWhereFld('wl_namespace', $namespace);
$this->addWhereIf('rc_this_oldid=page_latest', !$allrev);
-
+
if (!is_null($show)) {
$show = array_flip($show);
/* Check for conflicting parameters. */
- if ((isset ($show['minor']) && isset ($show['!minor']))
- || (isset ($show['bot']) && isset ($show['!bot']))
+ 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');
}
@@ -155,7 +155,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$this->addWhereIf('rc_user != 0', isset ($show['!anon']));
}
-
+
# This is an index optimization for mysql, as done in the Special:Watchlist page
$this->addWhereIf("rc_timestamp > ''", !isset ($start) && !isset ($end) && $wgDBtype == 'mysql');
@@ -205,9 +205,9 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
if ($this->fld_ids) {
$vals['pageid'] = intval($row->rc_cur_id);
- $vals['revid'] = intval($row->rc_this_oldid);
+ $vals['revid'] = intval($row->rc_this_oldid);
}
-
+
if ($this->fld_title)
ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->rc_namespace, $row->rc_title));
@@ -305,7 +305,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'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.',
+ 'limit' => 'How many total results to return per request.',
'prop' => 'Which additional items to get (non-generator mode only).',
'show' => array (
'Show only items that meet this criteria.',
@@ -315,7 +315,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
public function getDescription() {
- return '';
+ return "Get all recent changes to pages in the logged in user's watchlist";
}
protected function getExamples() {
@@ -329,7 +329,6 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryWatchlist.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryWatchlist.php 37909 2008-07-22 13:26:15Z catrope $';
}
}
-
diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php
index ffab51ef..9e798d35 100644
--- a/includes/api/ApiResult.php
+++ b/includes/api/ApiResult.php
@@ -33,17 +33,17 @@ if (!defined('MEDIAWIKI')) {
* It simply wraps a nested array() structure, adding some functions to simplify array's modifications.
* As various modules execute, they add different pieces of information to this result,
* structuring it as it will be given to the client.
- *
+ *
* Each subarray may either be a dictionary - key-value pairs with unique keys,
* or lists, where the items are added using $data[] = $value notation.
- *
+ *
* There are two special key values that change how XML output is generated:
* '_element' This key sets the tag name for the rest of the elements in the current array.
* It is only inserted if the formatter returned true for getNeedsRawData()
* '*' This key has special meaning only to the XML formatter, and is outputed as is
- * for all others. In XML it becomes the content of the current element.
- *
- * @addtogroup API
+ * for all others. In XML it becomes the content of the current element.
+ *
+ * @ingroup API
*/
class ApiResult extends ApiBase {
@@ -64,15 +64,15 @@ class ApiResult extends ApiBase {
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.
+ * 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;
}
-
+
/**
* Returns true if the result is being created for the formatter that requested raw data.
*/
@@ -139,7 +139,7 @@ class ApiResult extends ApiBase {
// Do not use setElement() as it is ok to call this more than once
$arr['_element'] = $tag;
}
-
+
/**
* Calls setIndexedTagName() on $arr and each sub-array
*/
@@ -147,7 +147,7 @@ class ApiResult extends ApiBase {
{
if(!is_array($arr))
return;
- foreach($arr as $a)
+ foreach($arr as &$a)
{
if(!is_array($a))
continue;
@@ -160,7 +160,7 @@ class ApiResult extends ApiBase {
* Add value to the output data at the given path.
* Path is an indexed array, each element specifing the branch at which to add the new value
* Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value
- * If $name is empty, the $value is added as a next list element data[] = $value
+ * If $name is empty, the $value is added as a next list element data[] = $value
*/
public function addValue($path, $name, $value) {
@@ -191,7 +191,7 @@ class ApiResult extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiResult.php 26855 2007-10-20 18:27:39Z catrope $';
+ return __CLASS__ . ': $Id: ApiResult.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
@@ -199,17 +199,17 @@ class ApiResult extends ApiBase {
if (!function_exists('array_intersect_key')) {
function array_intersect_key($isec, $keys) {
$argc = func_num_args();
-
+
if ($argc > 2) {
for ($i = 1; !empty($isec) && $i < $argc; $i++) {
$arr = func_get_arg($i);
-
+
foreach (array_keys($isec) as $key) {
- if (!isset($arr[$key]))
+ if (!isset($arr[$key]))
unset($isec[$key]);
}
}
-
+
return $isec;
} else {
$res = array();
@@ -217,7 +217,7 @@ if (!function_exists('array_intersect_key')) {
if (isset($keys[$key]))
$res[$key] = $isec[$key];
}
-
+
return $res;
}
}
diff --git a/includes/api/ApiRollback.php b/includes/api/ApiRollback.php
index d714f99c..3739f694 100644
--- a/includes/api/ApiRollback.php
+++ b/includes/api/ApiRollback.php
@@ -28,7 +28,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiRollback extends ApiBase {
@@ -40,7 +40,7 @@ class ApiRollback extends ApiBase {
global $wgUser;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
-
+
$titleObj = NULL;
if(!isset($params['title']))
$this->dieUsageMsg(array('missingparam', 'title'));
@@ -62,15 +62,12 @@ class ApiRollback extends ApiBase {
$articleObj = new Article($titleObj);
$summary = (isset($params['summary']) ? $params['summary'] : "");
$details = null;
- $dbw = wfGetDb(DB_MASTER);
- $dbw->begin();
$retval = $articleObj->doRollback($username, $summary, $params['token'], $params['markbot'], $details);
if(!empty($retval))
// We don't care about multiple errors, just report one of them
$this->dieUsageMsg(current($retval));
- $dbw->commit();
$current = $target = $summary = NULL;
extract($details);
@@ -85,9 +82,9 @@ class ApiRollback extends ApiBase {
$this->getResult()->addValue(null, $this->getModuleName(), $info);
}
-
+
public function mustBePosted() { return true; }
-
+
public function getAllowedParams() {
return array (
'title' => null,
@@ -123,6 +120,6 @@ class ApiRollback extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiRollback.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiRollback.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php
index afbd3f0e..d6a02a2a 100644
--- a/includes/api/ApiUnblock.php
+++ b/includes/api/ApiUnblock.php
@@ -31,7 +31,7 @@ if (!defined('MEDIAWIKI')) {
* API module that facilitates the unblocking of users. Requires API write mode
* to be enabled.
*
- * @addtogroup API
+ * @ingroup API
*/
class ApiUnblock extends ApiBase {
@@ -41,7 +41,7 @@ class ApiUnblock extends ApiBase {
/**
* Unblocks the specified user or provides the reason the unblock failed.
- */
+ */
public function execute() {
global $wgUser;
$this->getMain()->requestWriteMode();
@@ -70,19 +70,16 @@ class ApiUnblock extends ApiBase {
$id = $params['id'];
$user = $params['user'];
$reason = (is_null($params['reason']) ? '' : $params['reason']);
- $dbw = wfGetDb(DB_MASTER);
- $dbw->begin();
$retval = IPUnblockForm::doUnblock($id, $user, $reason, $range);
if(!empty($retval))
$this->dieUsageMsg($retval);
- $dbw->commit();
$res['id'] = $id;
$res['user'] = $user;
$res['reason'] = $reason;
$this->getResult()->addValue(null, $this->getModuleName(), $res);
}
-
+
public function mustBePosted() { return true; }
public function getAllowedParams() {
@@ -119,6 +116,6 @@ class ApiUnblock extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiUnblock.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiUnblock.php 35098 2008-05-20 17:13:28Z ialex $';
}
}
diff --git a/includes/api/ApiUndelete.php b/includes/api/ApiUndelete.php
index b27841a8..e054a70e 100644
--- a/includes/api/ApiUndelete.php
+++ b/includes/api/ApiUndelete.php
@@ -28,7 +28,7 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * @addtogroup API
+ * @ingroup API
*/
class ApiUndelete extends ApiBase {
@@ -40,7 +40,7 @@ class ApiUndelete extends ApiBase {
global $wgUser;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
-
+
$titleObj = NULL;
if(!isset($params['title']))
$this->dieUsageMsg(array('missingparam', 'title'));
@@ -61,6 +61,8 @@ class ApiUndelete extends ApiBase {
$this->dieUsageMsg(array('invalidtitle', $params['title']));
// Convert timestamps
+ if(!isset($params['timestamps']))
+ $params['timestamps'] = array();
if(!is_array($params['timestamps']))
$params['timestamps'] = array($params['timestamps']);
foreach($params['timestamps'] as $i => $ts)
@@ -73,16 +75,19 @@ class ApiUndelete extends ApiBase {
if(!is_array($retval))
$this->dieUsageMsg(array('cannotundelete'));
- $dbw->commit();
+ if($retval[1])
+ wfRunHooks( 'FileUndeleteComplete',
+ array($titleObj, array(), $wgUser, $params['reason']) );
+
$info['title'] = $titleObj->getPrefixedText();
$info['revisions'] = $retval[0];
$info['fileversions'] = $retval[1];
$info['reason'] = $retval[2];
$this->getResult()->addValue(null, $this->getModuleName(), $info);
}
-
+
public function mustBePosted() { return true; }
-
+
public function getAllowedParams() {
return array (
'title' => null,
@@ -118,6 +123,6 @@ class ApiUndelete extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiUndelete.php 30222 2008-01-28 19:05:26Z catrope $';
+ return __CLASS__ . ': $Id: ApiUndelete.php 35348 2008-05-26 10:51:31Z catrope $';
}
}