summaryrefslogtreecommitdiff
path: root/includes/api
diff options
context:
space:
mode:
Diffstat (limited to 'includes/api')
-rw-r--r--includes/api/ApiBase.php148
-rw-r--r--includes/api/ApiBlock.php22
-rw-r--r--includes/api/ApiDelete.php46
-rw-r--r--includes/api/ApiDisabled.php72
-rw-r--r--includes/api/ApiEditPage.php39
-rw-r--r--includes/api/ApiEmailUser.php14
-rw-r--r--includes/api/ApiExpandTemplates.php17
-rw-r--r--includes/api/ApiFormatBase.php10
-rw-r--r--includes/api/ApiFormatJson.php7
-rw-r--r--includes/api/ApiFormatJson_json.php76
-rw-r--r--includes/api/ApiFormatWddx.php58
-rw-r--r--includes/api/ApiFormatXml.php31
-rw-r--r--includes/api/ApiFormatYaml_spyc.php1115
-rw-r--r--includes/api/ApiLogin.php135
-rw-r--r--includes/api/ApiLogout.php5
-rw-r--r--includes/api/ApiMain.php72
-rw-r--r--includes/api/ApiMove.php51
-rw-r--r--includes/api/ApiPageSet.php54
-rw-r--r--includes/api/ApiParamInfo.php16
-rw-r--r--includes/api/ApiParse.php60
-rw-r--r--includes/api/ApiPatrol.php99
-rw-r--r--includes/api/ApiProtect.php83
-rw-r--r--includes/api/ApiPurge.php106
-rw-r--r--includes/api/ApiQuery.php29
-rw-r--r--includes/api/ApiQueryAllCategories.php23
-rw-r--r--includes/api/ApiQueryAllLinks.php29
-rw-r--r--includes/api/ApiQueryAllUsers.php4
-rw-r--r--includes/api/ApiQueryAllimages.php18
-rw-r--r--includes/api/ApiQueryAllpages.php50
-rw-r--r--includes/api/ApiQueryBacklinks.php56
-rw-r--r--includes/api/ApiQueryBase.php48
-rw-r--r--includes/api/ApiQueryBlocks.php42
-rw-r--r--includes/api/ApiQueryCategories.php35
-rw-r--r--includes/api/ApiQueryCategoryInfo.php21
-rw-r--r--includes/api/ApiQueryCategoryMembers.php36
-rw-r--r--includes/api/ApiQueryDeletedrevs.php4
-rw-r--r--includes/api/ApiQueryDisabled.php72
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php164
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php6
-rw-r--r--includes/api/ApiQueryImageInfo.php30
-rw-r--r--includes/api/ApiQueryImages.php8
-rw-r--r--includes/api/ApiQueryInfo.php149
-rw-r--r--includes/api/ApiQueryLangLinks.php4
-rw-r--r--includes/api/ApiQueryLinks.php10
-rw-r--r--includes/api/ApiQueryLogEvents.php103
-rw-r--r--includes/api/ApiQueryRandom.php13
-rw-r--r--includes/api/ApiQueryRecentChanges.php129
-rw-r--r--includes/api/ApiQueryRevisions.php90
-rw-r--r--includes/api/ApiQuerySearch.php37
-rw-r--r--includes/api/ApiQuerySiteinfo.php82
-rw-r--r--includes/api/ApiQueryUserContributions.php39
-rw-r--r--includes/api/ApiQueryUserInfo.php10
-rw-r--r--includes/api/ApiQueryUsers.php39
-rw-r--r--includes/api/ApiQueryWatchlist.php51
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php179
-rw-r--r--includes/api/ApiResult.php17
-rw-r--r--includes/api/ApiRollback.php27
-rw-r--r--includes/api/ApiUnblock.php8
-rw-r--r--includes/api/ApiUndelete.php6
-rw-r--r--includes/api/ApiWatch.php99
60 files changed, 2421 insertions, 1682 deletions
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index 732adae1..22144333 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -38,14 +38,15 @@
*/
abstract class ApiBase {
- // These constants allow modules to specify exactly how to treat incomming parameters.
+ // These constants allow modules to specify exactly how to treat incoming parameters.
- const PARAM_DFLT = 0;
- const PARAM_ISMULTI = 1;
- const PARAM_TYPE = 2;
- const PARAM_MAX = 3;
- const PARAM_MAX2 = 4;
- const PARAM_MIN = 5;
+ const PARAM_DFLT = 0; // Default value of the parameter
+ const PARAM_ISMULTI = 1; // Boolean, do we accept more than one item for this parameter (e.g.: titles)?
+ const PARAM_TYPE = 2; // Can be either a string type (e.g.: 'integer') or an array of allowed values
+ const PARAM_MAX = 3; // Max value allowed for a parameter. Only applies if TYPE='integer'
+ const PARAM_MAX2 = 4; // Max value allowed for a parameter for bots and sysops. Only applies if TYPE='integer'
+ const PARAM_MIN = 5; // Lowest value allowed for a parameter. Only applies if TYPE='integer'
+ const PARAM_ALLOW_DUPLICATES = 6; // Boolean, do we allow the same value to be set more than once when ISMULTI=true
const LIMIT_BIG1 = 500; // Fast query, std user limit
const LIMIT_BIG2 = 5000; // Fast query, bot/sysop limit
@@ -159,6 +160,10 @@ abstract class ApiBase {
$data =& $this->getResult()->getData();
if(isset($data['warnings'][$this->getModuleName()]))
{
+ # Don't add duplicate warnings
+ $warn_regex = preg_quote($warning, '/');
+ if(preg_match("/{$warn_regex}(\\n|$)/", $data['warnings'][$this->getModuleName()]['*']))
+ return;
$warning = "{$data['warnings'][$this->getModuleName()]['*']}\n$warning";
unset($data['warnings'][$this->getModuleName()]);
}
@@ -238,10 +243,10 @@ abstract class ApiBase {
* module's help.
*/
public function makeHelpMsgParameters() {
- $params = $this->getAllowedParams();
+ $params = $this->getFinalParams();
if ($params !== false) {
- $paramsDescription = $this->getParamDescription();
+ $paramsDescription = $this->getFinalParamDescription();
$msg = '';
$paramPrefix = "\n" . str_repeat(' ', 19);
foreach ($params as $paramName => $paramSettings) {
@@ -260,7 +265,7 @@ abstract class ApiBase {
$choices = array();
$nothingPrompt = false;
foreach ($type as $t)
- if ($t=='')
+ if ($t === '')
$nothingPrompt = 'Can be empty, or ';
else
$choices[] = $t;
@@ -319,18 +324,39 @@ abstract class ApiBase {
}
/**
- * Returns an array of allowed parameters (keys) => default value for that parameter
+ * Returns an array of allowed parameters (keys) => default value for that parameter.
+ * Don't call this function directly: use getFinalParams() to allow hooks
+ * to modify parameters as needed.
*/
protected function getAllowedParams() {
return false;
}
/**
- * Returns the description string for the given parameter.
+ * Returns an array of parameter descriptions.
+ * Don't call this functon directly: use getFinalParamDescription() to allow
+ * hooks to modify descriptions as needed.
*/
protected function getParamDescription() {
return false;
}
+
+ /**
+ * Get final list of parameters, after hooks have had
+ * a chance to tweak it as needed.
+ */
+ public function getFinalParams() {
+ $params = $this->getAllowedParams();
+ wfRunHooks('APIGetAllowedParams', array(&$this, &$params));
+ return $params;
+ }
+
+
+ public function getFinalParamDescription() {
+ $desc = $this->getParamDescription();
+ wfRunHooks('APIGetParamDescription', array(&$this, &$desc));
+ return $desc;
+ }
/**
* This method mangles parameter name based on the prefix supplied to the constructor.
@@ -343,12 +369,11 @@ abstract class ApiBase {
/**
* Using getAllowedParams(), makes an array of the values provided by the user,
* with key being the name of the variable, and value - validated value from user or default.
- * This method can be used to generate local variables using extract().
* limit=max will not be parsed if $parseMaxLimit is set to false; use this
* when the max limit is not definite, e.g. when getting revisions.
*/
public function extractRequestParams($parseMaxLimit = true) {
- $params = $this->getAllowedParams();
+ $params = $this->getFinalParams();
$results = array ();
foreach ($params as $paramName => $paramSettings)
@@ -361,10 +386,27 @@ abstract class ApiBase {
* Get a value for the given parameter
*/
protected function getParameter($paramName, $parseMaxLimit = true) {
- $params = $this->getAllowedParams();
+ $params = $this->getFinalParams();
$paramSettings = $params[$paramName];
return $this->getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit);
}
+
+ /**
+ * Die if none or more than one of a certain set of parameters is set
+ */
+ public function requireOnlyOneParameter($params) {
+ $required = func_get_args();
+ array_shift($required);
+
+ $intersection = array_intersect(array_keys(array_filter($params,
+ create_function('$x', 'return !is_null($x);')
+ )), $required);
+ if (count($intersection) > 1) {
+ $this->dieUsage('The parameters '.implode(', ', $intersection).' can not be used together', 'invalidparammix');
+ } elseif (count($intersection) == 0) {
+ $this->dieUsage('One of the parameters '.implode(', ', $required).' is required', 'missingparam');
+ }
+ }
/**
* Returns an array of the namespaces (by integer id) that exist on the
@@ -400,10 +442,12 @@ abstract class ApiBase {
$default = $paramSettings;
$multi = false;
$type = gettype($paramSettings);
+ $dupes = false;
} else {
$default = isset ($paramSettings[self :: PARAM_DFLT]) ? $paramSettings[self :: PARAM_DFLT] : null;
$multi = isset ($paramSettings[self :: PARAM_ISMULTI]) ? $paramSettings[self :: PARAM_ISMULTI] : false;
$type = isset ($paramSettings[self :: PARAM_TYPE]) ? $paramSettings[self :: PARAM_TYPE] : null;
+ $dupes = isset ($paramSettings[self:: PARAM_ALLOW_DUPLICATES]) ? $paramSettings[self :: PARAM_ALLOW_DUPLICATES] : false;
// When type is not given, and no choices, the type is the same as $default
if (!isset ($type)) {
@@ -494,8 +538,8 @@ abstract class ApiBase {
}
}
- // There should never be any duplicate values in a list
- if (is_array($value))
+ // Throw out duplicates if requested
+ if (is_array($value) && !$dupes)
$value = array_unique($value);
}
@@ -515,10 +559,10 @@ abstract class ApiBase {
protected function parseMultiValue($valueName, $value, $allowMultiple, $allowedValues) {
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
+ $sizeLimit = $this->mMainModule->canApiHighLimits() ? self::LIMIT_SML2 : self::LIMIT_SML1;
+ $valuesList = explode('|', $value, $sizeLimit + 1);
+ if( self::truncateArray($valuesList, $sizeLimit) ) {
+ $this->setWarning("Too many values supplied for parameter '$valueName': the limit is $sizeLimit");
}
if (!$allowMultiple && count($valuesList) != 1) {
$possibleValues = is_array($allowedValues) ? "of '" . implode("', '", $allowedValues) . "'" : '';
@@ -527,7 +571,7 @@ abstract class ApiBase {
if (is_array($allowedValues)) {
# Check for unknown values
$unknown = array_diff($valuesList, $allowedValues);
- if(!empty($unknown))
+ if(count($unknown))
{
if($allowMultiple)
{
@@ -569,6 +613,23 @@ abstract class ApiBase {
}
}
}
+
+ /**
+ * Truncate an array to a certain length.
+ * @param $arr array Array to truncate
+ * @param $limit int Maximum length
+ * @return bool True if the array was truncated, false otherwise
+ */
+ public static function truncateArray(&$arr, $limit)
+ {
+ $modified = false;
+ while(count($arr) > $limit)
+ {
+ $junk = array_pop($arr);
+ $modified = true;
+ }
+ return $modified;
+ }
/**
* Call main module's error handler
@@ -594,8 +655,6 @@ abstract class ApiBase {
'protectedpagetext' => array('code' => 'protectedpage', 'info' => "The ``\$1'' right is required to edit this page"),
'protect-cantedit' => array('code' => 'cantedit', 'info' => "You can't protect this page because you can't edit it"),
'badaccess-group0' => array('code' => 'permissiondenied', 'info' => "Permission denied"), // Generic permission denied message
- 'badaccess-group1' => array('code' => 'permissiondenied', 'info' => "Permission denied"), // Can't use the parameter 'cause it's wikilinked
- 'badaccess-group2' => array('code' => 'permissiondenied', 'info' => "Permission denied"),
'badaccess-groups' => array('code' => 'permissiondenied', 'info' => "Permission denied"),
'titleprotected' => array('code' => 'protectedtitle', 'info' => "This title has been protected from creation"),
'nocreate-loggedin' => array('code' => 'cantcreate', 'info' => "You don't have permission to create new pages"),
@@ -632,13 +691,21 @@ 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"),
+ 'mailnologin' => array('code' => 'cantsend', 'info' => "You're not logged in or you don't have a confirmed e-mail address, so you can't send e-mail"),
+ 'usermaildisabled' => array('code' => 'usermaildisabled', 'info' => "User email has been disabled"),
+ 'blockedemailuser' => array('code' => 'blockedfrommail', 'info' => "You have been blocked from sending e-mail"),
+ 'notarget' => array('code' => 'notarget', 'info' => "You have not specified a valid target for this action"),
+ 'noemail' => array('code' => 'noemail', 'info' => "The user has not specified a valid e-mail address, or has chosen not to receive e-mail from other users"),
+ 'rcpatroldisabled' => array('code' => 'patroldisabled', 'info' => "Patrolling is disabled on this wiki"),
+ 'markedaspatrollederror-noautopatrol' => array('code' => 'noautopatrol', 'info' => "You don't have permission to patrol your own changes"),
// API-specific messages
'missingparam' => array('code' => 'no$1', 'info' => "The \$1 parameter must be set"),
'invalidtitle' => array('code' => 'invalidtitle', 'info' => "Bad title ``\$1''"),
+ 'nosuchpageid' => array('code' => 'nosuchpageid', 'info' => "There is no page with ID \$1"),
'invaliduser' => array('code' => 'invaliduser', 'info' => "Invalid username ``\$1''"),
- 'invalidexpiry' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time"),
- 'pastexpiry' => array('code' => 'pastexpiry', 'info' => "Expiry time is in the past"),
+ 'invalidexpiry' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time ``\$1''"),
+ 'pastexpiry' => array('code' => 'pastexpiry', 'info' => "Expiry time ``\$1'' is in the past"),
'create-titleexists' => array('code' => 'create-titleexists', 'info' => "Existing titles can't be protected with 'create'"),
'missingtitle-createonly' => array('code' => 'missingtitle-createonly', 'info' => "Missing titles can only be protected with 'create'"),
'cantblock' => array('code' => 'cantblock', 'info' => "You don't have permission to block users"),
@@ -651,6 +718,12 @@ abstract class ApiBase {
'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"),
+ 'nosuchrcid' => array('code' => 'nosuchrcid', 'info' => "There is no change with rcid ``\$1''"),
+ 'cantpurge' => array('code' => 'cantpurge', 'info' => "Only users with the 'purge' right can purge pages via the API"),
+ 'protect-invalidaction' => array('code' => 'protect-invalidaction', 'info' => "Invalid protection type ``\$1''"),
+ 'protect-invalidlevel' => array('code' => 'protect-invalidlevel', 'info' => "Invalid protection level ``\$1''"),
+ 'toofewexpiries' => array('code' => 'toofewexpiries', 'info' => "\$1 expiry timestamps were provided where \$2 were needed"),
+
// ApiEditPage messages
'noimageredirect-anon' => array('code' => 'noimageredirect-anon', 'info' => "Anonymous users can't create image redirects"),
@@ -665,18 +738,33 @@ abstract class ApiBase {
'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"),
+ 'emptynewsection' => array('code' => 'emptynewsection', 'info' => 'Creating empty new sections is not possible.'),
);
/**
* Output the error message related to a certain array
- * @param array $error Element of a getUserPermissionsErrors()
+ * @param array $error Element of a getUserPermissionsErrors()-style array
*/
public function dieUsageMsg($error) {
+ $parsed = $this->parseMsg($error);
+ $this->dieUsage($parsed['code'], $parsed['info']);
+ }
+
+ /**
+ * Return the error message related to a certain array
+ * @param array $error Element of a getUserPermissionsErrors()-style array
+ * @return array('code' => code, 'info' => info)
+ */
+ public function parseMsg($error) {
$key = array_shift($error);
if(isset(self::$messageMap[$key]))
- $this->dieUsage(wfMsgReplaceArgs(self::$messageMap[$key]['info'], $error), wfMsgReplaceArgs(self::$messageMap[$key]['code'], $error));
+ return array( 'code' =>
+ wfMsgReplaceArgs(self::$messageMap[$key]['code'], $error),
+ 'info' =>
+ wfMsgReplaceArgs(self::$messageMap[$key]['info'], $error)
+ );
// If the key isn't present, throw an "unknown error"
- $this->dieUsageMsg(array('unknownerror', $key));
+ return $this->parseMsg(array('unknownerror', $key));
}
/**
@@ -814,6 +902,6 @@ abstract class ApiBase {
* Returns a String that identifies the version of this class.
*/
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiBase.php 36309 2008-06-15 20:37:28Z catrope $';
+ return __CLASS__ . ': $Id: ApiBase.php 47041 2009-02-09 14:39:41Z catrope $';
}
}
diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php
index 34813bf7..dfb11061 100644
--- a/includes/api/ApiBlock.php
+++ b/includes/api/ApiBlock.php
@@ -49,7 +49,7 @@ class ApiBlock extends ApiBase {
* of success. If it fails, the result will specify the nature of the error.
*/
public function execute() {
- global $wgUser;
+ global $wgUser, $wgBlockAllowsUTEdit;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
@@ -72,8 +72,6 @@ class ApiBlock extends ApiBase {
$this->dieUsageMsg(array('canthide'));
if($params['noemail'] && !$wgUser->isAllowed('blockemail'))
$this->dieUsageMsg(array('cantblock-email'));
- if(wfReadOnly())
- $this->dieUsageMsg(array('readonlytext'));
$form = new IPBlockForm('');
$form->BlockAddress = $params['user'];
@@ -83,13 +81,15 @@ class ApiBlock extends ApiBase {
$form->BlockOther = '';
$form->BlockAnonOnly = $params['anononly'];
$form->BlockCreateAccount = $params['nocreate'];
- $form->BlockEnableAutoBlock = $params['autoblock'];
+ $form->BlockEnableAutoblock = $params['autoblock'];
$form->BlockEmail = $params['noemail'];
$form->BlockHideName = $params['hidename'];
+ $form->BlockAllowUsertalk = $params['allowusertalk'] && $wgBlockAllowsUTEdit;
+ $form->BlockReblock = $params['reblock'];
$userID = $expiry = null;
$retval = $form->doBlock($userID, $expiry);
- if(!empty($retval))
+ if(count($retval))
// We don't care about multiple errors, just report one of them
$this->dieUsageMsg($retval);
@@ -107,6 +107,8 @@ class ApiBlock extends ApiBase {
$res['noemail'] = '';
if($params['hidename'])
$res['hidename'] = '';
+ if($params['allowusertalk'])
+ $res['allowusertalk'] = '';
$this->getResult()->addValue(null, $this->getModuleName(), $res);
}
@@ -125,13 +127,15 @@ class ApiBlock extends ApiBase {
'autoblock' => false,
'noemail' => false,
'hidename' => false,
+ 'allowusertalk' => false,
+ 'reblock' => false,
);
}
public function getParamDescription() {
return array (
'user' => 'Username, IP address or IP range you want to block',
- 'token' => 'A block token previously obtained through the gettoken parameter',
+ 'token' => 'A block token previously obtained through the gettoken parameter or prop=info',
'gettoken' => 'If set, a block token will be returned, and no other action will be taken',
'expiry' => 'Relative expiry time, e.g. \'5 months\' or \'2 weeks\'. If set to \'infinite\', \'indefinite\' or \'never\', the block will never expire.',
'reason' => 'Reason for block (optional)',
@@ -139,7 +143,9 @@ class ApiBlock extends ApiBase {
'nocreate' => 'Prevent account creation',
'autoblock' => 'Automatically block the last used IP address, and any subsequent IP addresses they try to login from',
'noemail' => 'Prevent user from sending e-mail through the wiki. (Requires the "blockemail" right.)',
- 'hidename' => 'Hide the username from the block log. (Requires the "hideuser" right.)'
+ 'hidename' => 'Hide the username from the block log. (Requires the "hideuser" right.)',
+ 'allowusertalk' => 'Allow the user to edit their own talk page (depends on $wgBlockAllowsUTEdit)',
+ 'reblock' => 'If the user is already blocked, overwrite the existing block',
);
}
@@ -157,6 +163,6 @@ class ApiBlock extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiBlock.php 35388 2008-05-27 10:18:28Z catrope $';
+ return __CLASS__ . ': $Id: ApiBlock.php 43677 2008-11-18 15:21:04Z catrope $';
}
}
diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php
index 06592d46..c0212924 100644
--- a/includes/api/ApiDelete.php
+++ b/includes/api/ApiDelete.php
@@ -52,29 +52,36 @@ class ApiDelete extends ApiBase {
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
- $titleObj = NULL;
- if(!isset($params['title']))
- $this->dieUsageMsg(array('missingparam', 'title'));
+ $this->requireOnlyOneParameter($params, 'title', 'pageid');
if(!isset($params['token']))
$this->dieUsageMsg(array('missingparam', 'token'));
- $titleObj = Title::newFromText($params['title']);
- if(!$titleObj)
- $this->dieUsageMsg(array('invalidtitle', $params['title']));
+ if(isset($params['title']))
+ {
+ $titleObj = Title::newFromText($params['title']);
+ if(!$titleObj)
+ $this->dieUsageMsg(array('invalidtitle', $params['title']));
+ }
+ else if(isset($params['pageid']))
+ {
+ $titleObj = Title::newFromID($params['pageid']);
+ if(!$titleObj)
+ $this->dieUsageMsg(array('nosuchpageid', $params['pageid']));
+ }
if(!$titleObj->exists())
$this->dieUsageMsg(array('notanarticle'));
$reason = (isset($params['reason']) ? $params['reason'] : NULL);
- if ($titleObj->getNamespace() == NS_IMAGE) {
- $retval = self::deletefile($params['token'], $titleObj, $params['oldimage'], $reason, false);
- if(!empty($retval))
+ if ($titleObj->getNamespace() == NS_FILE) {
+ $retval = self::deleteFile($params['token'], $titleObj, $params['oldimage'], $reason, false);
+ if(count($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))
+ if(count($retval))
// We don't care about multiple errors, just report one of them
$this->dieUsageMsg(current($retval));
@@ -90,8 +97,6 @@ class ApiDelete extends ApiBase {
private static function getPermissionsError(&$title, $token) {
global $wgUser;
- // Check wiki readonly
- if (wfReadOnly()) return array(array('readonlytext'));
// Check permissions
$errors = $title->getUserPermissionsErrors('delete', $wgUser);
@@ -114,8 +119,8 @@ class ApiDelete extends ApiBase {
public static function delete(&$article, $token, &$reason = NULL)
{
global $wgUser;
-
- $errors = self::getPermissionsError($article->getTitle(), $token);
+ $title = $article->getTitle();
+ $errors = self::getPermissionsError($title, $token);
if (count($errors)) return $errors;
// Auto-generate a summary, if necessary
@@ -156,7 +161,8 @@ class ApiDelete extends ApiBase {
if( !FileDeleteForm::haveDeletableFile($file, $oldfile, $oldimage) )
return array(array('nofile'));
-
+ if (is_null($reason)) # Log and RC don't like null reasons
+ $reason = '';
$status = FileDeleteForm::doDelete( $title, $file, $oldimage, $reason, $suppress );
if( !$status->isGood() )
@@ -170,6 +176,9 @@ class ApiDelete extends ApiBase {
public function getAllowedParams() {
return array (
'title' => null,
+ 'pageid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
'token' => null,
'reason' => null,
'watch' => false,
@@ -180,7 +189,8 @@ class ApiDelete extends ApiBase {
public function getParamDescription() {
return array (
- 'title' => 'Title of the page you want to delete.',
+ 'title' => 'Title of the page you want to delete. Cannot be used together with pageid',
+ 'pageid' => 'Page ID of the page you want to delete. Cannot be used together with title',
'token' => 'A delete token previously retrieved through prop=info',
'reason' => 'Reason for the deletion. If not set, an automatically generated reason will be used.',
'watch' => 'Add the page to your watchlist',
@@ -191,7 +201,7 @@ class ApiDelete extends ApiBase {
public function getDescription() {
return array(
- 'Deletes a page. You need to be logged in as a sysop to use this function, see also action=login.'
+ 'Delete a page.'
);
}
@@ -203,6 +213,6 @@ class ApiDelete extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiDelete.php 35350 2008-05-26 12:15:21Z simetrical $';
+ return __CLASS__ . ': $Id: ApiDelete.php 44541 2008-12-13 21:07:18Z mrzman $';
}
}
diff --git a/includes/api/ApiDisabled.php b/includes/api/ApiDisabled.php
new file mode 100644
index 00000000..40e38a0f
--- /dev/null
+++ b/includes/api/ApiDisabled.php
@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * Created on Sep 25, 2008
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2008 Roan Kattouw <Firstname>.<Lastname>@home.nl
+ *
+ * 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");
+}
+
+
+/**
+ * API module that dies with an error immediately.
+ *
+ * Use this to disable core modules with
+ * $wgAPIModules['modulename'] = 'ApiDisabled';
+ *
+ * To disable submodules of action=query, use ApiQueryDisabled instead
+ *
+ * @ingroup API
+ */
+class ApiDisabled extends ApiBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ public function execute() {
+ $this->dieUsage("The ``{$this->getModuleName()}'' module has been disabled.", 'moduledisabled');
+ }
+
+ public function getAllowedParams() {
+ return array ();
+ }
+
+ public function getParamDescription() {
+ return array ();
+ }
+
+ public function getDescription() {
+ return array(
+ 'This module has been disabled.'
+ );
+ }
+
+ protected function getExamples() {
+ return array ();
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiDisabled.php 41268 2008-09-25 20:50:50Z catrope $';
+ }
+}
diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php
index d10432f3..bc5dfa87 100644
--- a/includes/api/ApiEditPage.php
+++ b/includes/api/ApiEditPage.php
@@ -29,8 +29,10 @@ if (!defined('MEDIAWIKI')) {
}
/**
- * A query module to list all external URLs found on a given set of pages.
+ * A module that allows for editing and creating pages.
*
+ * Currently, this wraps around the EditPage class in an ugly way,
+ * EditPage.php should be rewritten to provide a cleaner interface
* @ingroup API
*/
class ApiEditPage extends ApiBase {
@@ -66,7 +68,7 @@ class ApiEditPage extends ApiBase {
$errors = $titleObj->getUserPermissionsErrors('edit', $wgUser);
if(!$titleObj->exists())
$errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $wgUser));
- if(!empty($errors))
+ if(count($errors))
$this->dieUsageMsg($errors[0]);
$articleObj = new Article($titleObj);
@@ -98,8 +100,11 @@ class ApiEditPage extends ApiBase {
$reqArr['wpEdittime'] = wfTimestamp(TS_MW, $params['basetimestamp']);
else
$reqArr['wpEdittime'] = $articleObj->getTimestamp();
- # Fake wpStartime
- $reqArr['wpStarttime'] = $reqArr['wpEdittime'];
+ if(!is_null($params['starttimestamp']) && $params['starttimestamp'] != '')
+ $reqArr['wpStarttime'] = wfTimestamp(TS_MW, $params['starttimestamp']);
+ else
+ # Fake wpStartime
+ $reqArr['wpStarttime'] = $reqArr['wpEdittime'];
if($params['minor'] || (!$params['notminor'] && $wgUser->getOption('minordefault')))
$reqArr['wpMinoredit'] = '';
if($params['recreate'])
@@ -111,6 +116,8 @@ class ApiEditPage extends ApiBase {
$this->dieUsage("The section parameter must be set to an integer or 'new'", "invalidsection");
$reqArr['wpSection'] = $params['section'];
}
+ else
+ $reqArr['wpSection'] = '';
if($params['watch'])
$watch = true;
@@ -134,13 +141,13 @@ class ApiEditPage extends ApiBase {
# Handle CAPTCHA parameters
global $wgRequest;
if(isset($params['captchaid']))
- $wgRequest->data['wpCaptchaId'] = $params['captchaid'];
+ $wgRequest->setVal( 'wpCaptchaId', $params['captchaid'] );
if(isset($params['captchaword']))
- $wgRequest->data['wpCaptchaWord'] = $params['captchaword'];
+ $wgRequest->setVal( 'wpCaptchaWord', $params['captchaword'] );
$r = array();
if(!wfRunHooks('APIEditBeforeSave', array(&$ep, $ep->textbox1, &$r)))
{
- if(!empty($r))
+ if(count($r))
{
$r['result'] = "Failure";
$this->getResult()->addValue(null, $this->getModuleName(), $r);
@@ -200,18 +207,24 @@ class ApiEditPage extends ApiBase {
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_TEXTBOX_EMPTY:
+ $this->dieUsageMsg(array('emptynewsection'));
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'));
+ $this->dieUsageMsg(array('unknownerror'));
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();
+ # HACK: We create a new Article object here because getRevIdFetched()
+ # refuses to be run twice, and because Title::getLatestRevId()
+ # won't fetch from the master unless we select for update, which we
+ # don't want to do.
+ $newArticle = new Article($titleObj);
+ $newRevId = $newArticle->getRevIdFetched();
if($newRevId == $oldRevId)
$r['nochange'] = '';
else
@@ -245,6 +258,7 @@ class ApiEditPage extends ApiBase {
'notminor' => false,
'bot' => false,
'basetimestamp' => null,
+ 'starttimestamp' => null,
'recreate' => false,
'createonly' => false,
'nocreate' => false,
@@ -271,6 +285,9 @@ class ApiEditPage extends ApiBase {
'basetimestamp' => array('Timestamp of the base revision (gotten through prop=revisions&rvprop=timestamp).',
'Used to detect edit conflicts; leave unset to ignore conflicts.'
),
+ 'starttimestamp' => array('Timestamp when you obtained the edit token.',
+ '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',
@@ -294,6 +311,6 @@ class ApiEditPage extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiEditPage.php 36309 2008-06-15 20:37:28Z catrope $';
+ return __CLASS__ . ': $Id: ApiEditPage.php 44394 2008-12-10 14:12:54Z catrope $';
}
}
diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php
index 7e083536..fbdf495f 100644
--- a/includes/api/ApiEmailUser.php
+++ b/includes/api/ApiEmailUser.php
@@ -39,6 +39,11 @@ class ApiEmailUser extends ApiBase {
public function execute() {
global $wgUser;
+
+ // Check whether email is enabled
+ if ( !EmailUserForm::userEmailEnabled() )
+ $this->dieUsageMsg( array( 'usermaildisabled' ) );
+
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
@@ -53,12 +58,12 @@ class ApiEmailUser extends ApiBase {
// Validate target
$targetUser = EmailUserForm::validateEmailTarget( $params['target'] );
if ( !( $targetUser instanceof User ) )
- $this->dieUsageMsg( array( $targetUser[0] ) );
+ $this->dieUsageMsg( array( $targetUser ) );
// Check permissions
$error = EmailUserForm::getPermissionsError( $wgUser, $params['token'] );
if ( $error )
- $this->dieUsageMsg( array( $error[0] ) );
+ $this->dieUsageMsg( array( $error ) );
$form = new EmailUserForm( $targetUser, $params['text'], $params['subject'], $params['ccme'] );
@@ -89,7 +94,6 @@ class ApiEmailUser extends ApiBase {
'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',
);
@@ -97,7 +101,7 @@ class ApiEmailUser extends ApiBase {
public function getDescription() {
return array(
- 'Emails a user.'
+ 'Email a user.'
);
}
@@ -108,7 +112,7 @@ class ApiEmailUser extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: $';
+ return __CLASS__ . ': $Id: ApiEmailUser.php 41269 2008-09-25 21:39:36Z catrope $';
}
}
\ No newline at end of file
diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php
index 397aece3..f4e6212a 100644
--- a/includes/api/ApiExpandTemplates.php
+++ b/includes/api/ApiExpandTemplates.php
@@ -43,23 +43,22 @@ class ApiExpandTemplates extends ApiBase {
public function execute() {
// Get parameters
- extract( $this->extractRequestParams() );
- $retval = '';
+ $params = $this->extractRequestParams();
//Create title for parser
- $title_obj = Title :: newFromText( $title );
+ $title_obj = Title :: newFromText( $params['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
$result = $this->getResult();
// Parse text
global $wgParser;
$options = new ParserOptions();
- if ( $generatexml )
+ if ( $params['generatexml'] )
{
$wgParser->startExternalParse( $title_obj, $options, OT_PREPROCESS );
- $dom = $wgParser->preprocessToDom( $text );
+ $dom = $wgParser->preprocessToDom( $params['text'] );
if ( is_callable( array( $dom, 'saveXML' ) ) ) {
$xml = $dom->saveXML();
} else {
@@ -67,9 +66,9 @@ class ApiExpandTemplates extends ApiBase {
}
$xml_result = array();
$result->setContent( $xml_result, $xml );
- $result->addValue( null, 'parsetree', $xml_result);
+ $result->addValue( null, 'parsetree', $xml_result);
}
- $retval = $wgParser->preprocess( $text, $title_obj, $options );
+ $retval = $wgParser->preprocess( $params['text'], $title_obj, $options );
// Return result
$retval_array = array();
@@ -106,6 +105,6 @@ class ApiExpandTemplates extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiExpandTemplates.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiExpandTemplates.php 44719 2008-12-17 16:34:01Z catrope $';
}
}
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index 8f08f4db..9efbbbe0 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -199,15 +199,17 @@ See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
* This method also replaces any '<' with &lt;
*/
protected function formatHTML($text) {
+ global $wgUrlProtocols;
+
// Escape everything first for full coverage
$text = htmlspecialchars($text);
// encode all comments or tags as safe blue strings
$text = preg_replace('/\&lt;(!--.*?--|.*?)\&gt;/', '<span style="color:blue;">&lt;\1&gt;</span>', $text);
// identify URLs
- $protos = "http|https|ftp|gopher";
+ $protos = implode("|", $wgUrlProtocols);
# 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);
+ $text = preg_replace("#(($protos).*?)(&quot;)?([ \\'\"()<\n])#", '<a href="\\1">\\1</a>\\3\\4', $text);
// identify requests to api.php
$text = preg_replace("#api\\.php\\?[^ \\()<\n\t]+#", '<a href="\\0">\\0</a>', $text);
if( $this->mHelp ) {
@@ -239,7 +241,7 @@ See <a href='http://www.mediawiki.org/wiki/API'>complete documentation</a>, or
}
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 44569 2008-12-14 08:31:04Z tstarling $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 43470 2008-11-14 00:30:34Z tstarling $';
}
}
@@ -300,6 +302,6 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatBase.php 44569 2008-12-14 08:31:04Z tstarling $';
+ return __CLASS__ . ': $Id: ApiFormatBase.php 43470 2008-11-14 00:30:34Z tstarling $';
}
}
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index 42156849..1d89eb18 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -58,7 +58,10 @@ class ApiFormatJson extends ApiFormatBase {
$suffix = ")";
}
- if (!function_exists('json_encode') || $this->getIsHtml()) {
+ // Some versions of PHP have a broken json_encode, see PHP bug
+ // 46944. Test encoding an affected character (U+20000) to
+ // avoid this.
+ if (!function_exists('json_encode') || $this->getIsHtml() || strtolower(json_encode("\xf0\xa0\x80\x80")) != '\ud840\udc00') {
$json = new Services_JSON();
$this->printText($prefix . $json->encode($this->getResultData(), $this->getIsHtml()) . $suffix);
} else {
@@ -86,6 +89,6 @@ class ApiFormatJson extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatJson.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiFormatJson.php 45682 2009-01-12 19:06:33Z raymond $';
}
}
diff --git a/includes/api/ApiFormatJson_json.php b/includes/api/ApiFormatJson_json.php
index 87d7086e..4b29ff56 100644
--- a/includes/api/ApiFormatJson_json.php
+++ b/includes/api/ApiFormatJson_json.php
@@ -50,7 +50,7 @@
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
-* @version CVS: $Id: ApiFormatJson_json.php 35098 2008-05-20 17:13:28Z ialex $
+* @version CVS: $Id: ApiFormatJson_json.php 45682 2009-01-12 19:06:33Z raymond $
* @license http://www.opensource.org/licenses/bsd-license.php
* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
@@ -168,6 +168,17 @@ class Services_JSON
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
+ case (0xFC00 & $bytes) == 0xD800 && strlen($utf16) >= 4 && (0xFC & ord($utf16{2})) == 0xDC:
+ // return a 4-byte UTF-8 character
+ $char = ((($bytes & 0x03FF) << 10)
+ | ((ord($utf16{2}) & 0x03) << 8)
+ | ord($utf16{3}));
+ $char += 0x10000;
+ return chr(0xF0 | (($char >> 18) & 0x07))
+ . chr(0x80 | (($char >> 12) & 0x3F))
+ . chr(0x80 | (($char >> 6) & 0x3F))
+ . chr(0x80 | ($char & 0x3F));
+
case (0xFFFF & $bytes) == $bytes:
// return a 3-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
@@ -218,6 +229,20 @@ class Services_JSON
| (0x0F & (ord($utf8{1}) >> 2)))
. chr((0xC0 & (ord($utf8{1}) << 6))
| (0x7F & ord($utf8{2})));
+
+ case 4:
+ // return a UTF-16 surrogate pair from a 4-byte UTF-8 char
+ if(ord($utf8{0}) > 0xF4) return ''; # invalid
+ $char = ((0x1C0000 & (ord($utf8{0}) << 18))
+ | (0x03F000 & (ord($utf8{1}) << 12))
+ | (0x000FC0 & (ord($utf8{2}) << 6))
+ | (0x00003F & ord($utf8{3})));
+ if($char > 0x10FFFF) return ''; # invalid
+ $char -= 0x10000;
+ return chr(0xD8 | (($char >> 18) & 0x03))
+ . chr(($char >> 10) & 0xFF)
+ . chr(0xDC | (($char >> 8) & 0x03))
+ . chr($char & 0xFF);
}
// ignoring UTF-32 for now, sorry
@@ -346,40 +371,19 @@ class Services_JSON
case (($ord_var_c & 0xF8) == 0xF0):
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ // These will always return a surrogate pair
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}));
$c += 3;
$utf16 = $this->utf82utf16($char);
- $ascii .= sprintf('\u%04s', bin2hex($utf16));
- break;
-
- case (($ord_var_c & 0xFC) == 0xF8):
- // characters U-00200000 - U-03FFFFFF, mask 111110XX
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $char = pack('C*', $ord_var_c,
- ord($var{$c + 1}),
- ord($var{$c + 2}),
- ord($var{$c + 3}),
- ord($var{$c + 4}));
- $c += 4;
- $utf16 = $this->utf82utf16($char);
- $ascii .= sprintf('\u%04s', bin2hex($utf16));
- break;
-
- case (($ord_var_c & 0xFE) == 0xFC):
- // characters U-04000000 - U-7FFFFFFF, mask 1111110X
- // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
- $char = pack('C*', $ord_var_c,
- ord($var{$c + 1}),
- ord($var{$c + 2}),
- ord($var{$c + 3}),
- ord($var{$c + 4}),
- ord($var{$c + 5}));
- $c += 5;
- $utf16 = $this->utf82utf16($char);
- $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ if($utf16 == '') {
+ $ascii .= '\ufffd';
+ } else {
+ $utf16 = str_split($utf16, 2);
+ $ascii .= sprintf('\u%04s\u%04s', bin2hex($utf16[0]), bin2hex($utf16[1]));
+ }
break;
}
}
@@ -591,6 +595,16 @@ class Services_JSON
}
break;
+ case preg_match('/\\\uD[89AB][0-9A-F]{2}\\\uD[C-F][0-9A-F]{2}/i', substr($chrs, $c, 12)):
+ // escaped unicode surrogate pair
+ $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
+ . chr(hexdec(substr($chrs, ($c + 4), 2)))
+ . chr(hexdec(substr($chrs, ($c + 8), 2)))
+ . chr(hexdec(substr($chrs, ($c + 10), 2)));
+ $utf8 .= $this->utf162utf8($utf16);
+ $c += 11;
+ break;
+
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
// single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
@@ -812,6 +826,9 @@ class Services_JSON
}
}
+
+// Hide the PEAR_Error variant from Doxygen
+/// @cond
if (class_exists('PEAR_Error')) {
/**
@@ -827,6 +844,7 @@ if (class_exists('PEAR_Error')) {
}
} else {
+/// @endcond
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php
index 0909539e..e741c16d 100644
--- a/includes/api/ApiFormatWddx.php
+++ b/includes/api/ApiFormatWddx.php
@@ -42,38 +42,62 @@ class ApiFormatWddx extends ApiFormatBase {
}
public function execute() {
- if (function_exists('wddx_serialize_value')) {
+ if (function_exists('wddx_serialize_value') && !$this->getIsHtml()) {
$this->printText(wddx_serialize_value($this->getResultData()));
} else {
- $this->printText('<?xml version="1.0" encoding="utf-8"?>');
- $this->printText('<wddxPacket version="1.0"><header/><data>');
- $this->slowWddxPrinter($this->getResultData());
- $this->printText('</data></wddxPacket>');
+ // Don't do newlines and indentation if we weren't asked
+ // for pretty output
+ $nl = ($this->getIsHtml() ? "" : "\n");
+ $indstr = " ";
+ $this->printText("<?xml version=\"1.0\"?>$nl");
+ $this->printText("<wddxPacket version=\"1.0\">$nl");
+ $this->printText("$indstr<header/>$nl");
+ $this->printText("$indstr<data>$nl");
+ $this->slowWddxPrinter($this->getResultData(), 4);
+ $this->printText("$indstr</data>$nl");
+ $this->printText("</wddxPacket>$nl");
}
}
/**
* Recursivelly go through the object and output its data in WDDX format.
*/
- function slowWddxPrinter($elemValue) {
+ function slowWddxPrinter($elemValue, $indent = 0) {
+ $indstr = ($this->getIsHtml() ? "" : str_repeat(' ', $indent));
+ $indstr2 = ($this->getIsHtml() ? "" : str_repeat(' ', $indent + 2));
+ $nl = ($this->getIsHtml() ? "" : "\n");
switch (gettype($elemValue)) {
case 'array' :
- $this->printText('<struct>');
- foreach ($elemValue as $subElemName => $subElemValue) {
- $this->printText(wfElement('var', array (
- 'name' => $subElemName
- ), null));
- $this->slowWddxPrinter($subElemValue);
- $this->printText('</var>');
+ // Check whether we've got an associative array (<struct>)
+ // or a regular array (<array>)
+ $cnt = count($elemValue);
+ if($cnt == 0 || array_keys($elemValue) === range(0, $cnt - 1)) {
+ // Regular array
+ $this->printText($indstr . Xml::element('array', array(
+ 'length' => $cnt
+ ), null) . $nl);
+ foreach($elemValue as $subElemValue)
+ $this->slowWddxPrinter($subElemValue, $indent + 2);
+ $this->printText("$indstr</array>$nl");
+ } else {
+ // Associative array (<struct>)
+ $this->printText("$indstr<struct>$nl");
+ foreach($elemValue as $subElemName => $subElemValue) {
+ $this->printText($indstr2 . Xml::element('var', array(
+ 'name' => $subElemName
+ ), null) . $nl);
+ $this->slowWddxPrinter($subElemValue, $indent + 4);
+ $this->printText("$indstr2</var>$nl");
+ }
+ $this->printText("$indstr</struct>$nl");
}
- $this->printText('</struct>');
break;
case 'integer' :
case 'double' :
- $this->printText(wfElement('number', null, $elemValue));
+ $this->printText($indstr . Xml::element('number', null, $elemValue) . $nl);
break;
case 'string' :
- $this->printText(wfElement('string', null, $elemValue));
+ $this->printText($indstr . Xml::element('string', null, $elemValue) . $nl);
break;
default :
ApiBase :: dieDebug(__METHOD__, 'Unknown type ' . gettype($elemValue));
@@ -85,6 +109,6 @@ class ApiFormatWddx extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatWddx.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiFormatWddx.php 44588 2008-12-14 19:14:21Z demon $';
}
}
diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php
index d35eb3e9..7ff57324 100644
--- a/includes/api/ApiFormatXml.php
+++ b/includes/api/ApiFormatXml.php
@@ -56,12 +56,12 @@ class ApiFormatXml extends ApiFormatBase {
$params = $this->extractRequestParams();
$this->mDoubleQuote = $params['xmldoublequote'];
- $this->printText('<?xml version="1.0" encoding="utf-8"?>');
+ $this->printText('<?xml version="1.0"?>');
$this->recXmlPrint($this->mRootElemName, $this->getResultData(), $this->getIsHtml() ? -2 : null);
}
/**
- * This method takes an array and converts it into an xml.
+ * This method takes an array and converts it to XML.
* There are several noteworthy cases:
*
* If array contains a key '_element', then the code assumes that ALL other keys are not important and replaces them with the value['_element'].
@@ -80,6 +80,7 @@ class ApiFormatXml extends ApiFormatBase {
} else {
$indstr = '';
}
+ $elemName = str_replace(' ', '_', $elemName);
switch (gettype($elemValue)) {
case 'array' :
@@ -104,6 +105,14 @@ class ApiFormatXml extends ApiFormatBase {
foreach ($elemValue as $subElemId => & $subElemValue) {
if (is_string($subElemValue) && $this->mDoubleQuote)
$subElemValue = $this->doubleQuote($subElemValue);
+
+ // Replace spaces with underscores
+ $newSubElemId = str_replace(' ', '_', $subElemId);
+ if($newSubElemId != $subElemId) {
+ $elemValue[$newSubElemId] = $subElemValue;
+ unset($elemValue[$subElemId]);
+ $subElemId = $newSubElemId;
+ }
if (gettype($subElemId) === 'integer') {
$indElements[] = $subElemValue;
@@ -114,18 +123,18 @@ class ApiFormatXml extends ApiFormatBase {
}
}
- if (is_null($subElemIndName) && !empty ($indElements))
+ if (is_null($subElemIndName) && count($indElements))
ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has integer keys without _element value. Use ApiResult::setIndexedTagName().");
- if (!empty ($subElements) && !empty ($indElements) && !is_null($subElemContent))
+ if (count($subElements) && count($indElements) && !is_null($subElemContent))
ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has content and subelements");
if (!is_null($subElemContent)) {
- $this->printText($indstr . wfElement($elemName, $elemValue, $subElemContent));
- } elseif (empty ($indElements) && empty ($subElements)) {
- $this->printText($indstr . wfElement($elemName, $elemValue));
+ $this->printText($indstr . Xml::element($elemName, $elemValue, $subElemContent));
+ } elseif (!count($indElements) && !count($subElements)) {
+ $this->printText($indstr . Xml::element($elemName, $elemValue));
} else {
- $this->printText($indstr . wfElement($elemName, $elemValue, null));
+ $this->printText($indstr . Xml::element($elemName, $elemValue, null));
foreach ($subElements as $subElemId => & $subElemValue)
$this->recXmlPrint($subElemId, $subElemValue, $indent);
@@ -133,14 +142,14 @@ class ApiFormatXml extends ApiFormatBase {
foreach ($indElements as $subElemId => & $subElemValue)
$this->recXmlPrint($subElemIndName, $subElemValue, $indent);
- $this->printText($indstr . wfCloseElement($elemName));
+ $this->printText($indstr . Xml::closeElement($elemName));
}
break;
case 'object' :
// ignore
break;
default :
- $this->printText($indstr . wfElement($elemName, null, $elemValue));
+ $this->printText($indstr . Xml::element($elemName, null, $elemValue));
break;
}
}
@@ -166,6 +175,6 @@ class ApiFormatXml extends ApiFormatBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiFormatXml.php 37075 2008-07-04 22:44:57Z brion $';
+ return __CLASS__ . ': $Id: ApiFormatXml.php 44588 2008-12-14 19:14:21Z demon $';
}
}
diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php
index c0d4093e..f16b2c8a 100644
--- a/includes/api/ApiFormatYaml_spyc.php
+++ b/includes/api/ApiFormatYaml_spyc.php
@@ -1,883 +1,234 @@
<?php
- /**
- * Spyc -- A Simple PHP YAML Class
- * @version 0.2.3 -- 2006-02-04
- * @author Chris Wanstrath <chris@ozmm.org>
- * @see http://spyc.sourceforge.net/
- * @copyright Copyright 2005-2006 Chris Wanstrath
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
- */
-
- /**
- * A node, used by Spyc for parsing YAML.
- * @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
- */
- var $children = false;
-
- /**
- * The constructor assigns the node a unique ID.
- * @access public
- * @return void
- */
- function YAMLNode() {
- $this->id = uniqid('');
- }
- }
-
- /**
- * The Simple PHP YAML Class.
- *
- * This class can be used to read a YAML file and convert its contents
- * into a PHP array. It currently supports a very limited subsection of
- * the YAML spec.
- *
- * Usage:
- * <code>
- * $parser = new Spyc;
- * $array = $parser->load($file);
- * </code>
- * @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
- * simple.
- * Usage:
- * <code>
- * $array = Spyc::YAMLLoad('lucky.yml');
- * print_r($array);
- * </code>
- * @access public
- * @return array
- * @param string $input Path of YAML file or string containing YAML
- */
- function YAMLLoad($input) {
- $spyc = new Spyc;
- return $spyc->load($input);
- }
-
- /**
- * Dump YAML from PHP array statically
- *
- * The dump method, when supplied with an array, will do its best
- * to convert the array into friendly YAML. Pretty simple. Feel free to
- * 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
- * you want to use the default.
- *
- * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
- * you can turn off wordwrap by passing in 0.
- *
- * @access public
- * @static
- * @return string
- * @param array $array PHP array
- * @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),
- * will do its best to convert the YAML into a PHP array. Pretty simple.
- * Usage:
- * <code>
- * $parser = new Spyc;
- * $array = $parser->load('lucky.yml');
- * print_r($array);
- * </code>
- * @access public
- * @return array
- * @param string $input Path of YAML file or string containing YAML
- */
- 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)
- && file_exists($input)) {
- $yaml = file($input);
- } else {
- $yaml = explode("\n",$input);
- }
- // Initiate some objects and values
- $base = new YAMLNode;
- $base->indent = 0;
- $this->_lastIndent = 0;
- $this->_lastNode = $base->id;
- $this->_inBlock = false;
- $this->_isInline = false;
-
- foreach ($yaml as $linenum => $line) {
- $ifchk = trim($line);
-
- // If the line starts with a tab (instead of a space), throw a fit.
- if (preg_match('/^(\t)+(\w+)/', $line)) {
- $err = 'ERROR: Line '. ($linenum + 1) .' in your input YAML begins'.
- ' with a tab. YAML only recognizes spaces. Please reformat.';
- die($err);
- }
-
- if ($this->_inBlock === false && empty($ifchk)) {
- continue;
- } elseif ($this->_inBlock == true && empty($ifchk)) {
- $last =& $this->_allNodes[$this->_lastNode];
- $last->data[key($last->data)] .= "\n";
- } elseif ($ifchk{0} != '#' && substr($ifchk,0,3) != '---') {
- // 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
- if ($this->_inBlock === true) {
- $parent =& $this->_allNodes[$this->_lastNode];
- $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd;
- } else {
- // The current node's parent is the same as the previous node's
- if (isset($this->_allNodes[$this->_lastNode])) {
- $node->parent = $this->_allNodes[$this->_lastNode]->parent;
- }
- }
- } 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
- // we drop our indent.
- $parent =& $this->_allNodes[$node->parent];
- $this->_allNodes[$node->parent]->children = true;
- if (is_array($parent->data)) {
- $chk = $parent->data[key($parent->data)];
- if ($chk === '>') {
- $this->_inBlock = true;
- $this->_blockEnd = ' ';
- $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;
- $this->_lastIndent = $node->indent;
- } elseif ($chk === '|') {
- $this->_inBlock = true;
- $this->_blockEnd = "\n";
- $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;
- $this->_lastIndent = $node->indent;
- }
- }
- }
- } elseif ($this->_lastIndent > $node->indent) {
- // Any block we had going is dead now
- if ($this->_inBlock === true) {
- $this->_inBlock = false;
- if ($this->_blockEnd = "\n") {
- $last =& $this->_allNodes[$this->_lastNode];
- $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) {
- if ($n->indent == $node->indent) {
- $node->parent = $n->parent;
- }
- }
- }
-
- if ($this->_inBlock === false) {
- // Set these properties with information from our current node
- $this->_lastIndent = $node->indent;
- // Set the last node
- $this->_lastNode = $node->id;
- // Parse the YAML line and return its data
- $node->data = $this->_parseLine($line);
- // Add the node to the master list
- $this->_allNodes[$node->id] = $node;
- // Add a reference to the node in an indent array
- $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 (
- ( (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)])) )
- ) {
- $this->_haveRefs[] =& $this->_allNodes[$node->id];
- } elseif (
- ( (is_array($node->data)) &&
- isset($node->data[key($node->data)]) &&
- (is_array($node->data[key($node->data)])) )
- ) {
- // Incomplete reference making code. Ugly, needs cleaned up.
- foreach ($node->data[key($node->data)] as $d) {
- if ( !is_array($d) &&
- ( (preg_match('/^&([^ ]+)/',$d))
- ||
- (preg_match('/^\*([^ ]+)/',$d)) )
- ) {
- $this->_haveRefs[] =& $this->_allNodes[$node->id];
- }
- }
- }
- }
- }
- }
- 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
- *
- * The dump method, when supplied with an array, will do its best
- * to convert the array into friendly YAML. Pretty simple. Feel free to
- * 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
- * you want to use the default.
- *
- * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
- * you can turn off wordwrap by passing in 0.
- *
- * @access public
- * @return string
- * @param array $array PHP array
- * @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) {
- // Dumps to some very clean YAML. We'll have to add some more features
- // and options soon. And better support for folding.
-
- // New features and options.
- if ($indent === false or !is_numeric($indent)) {
- $this->_dumpIndent = 2;
- } 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;
- var $_lastNode;
- var $_inBlock;
- var $_isInline;
- var $_dumpIndent;
- var $_dumpWordWrap;
- /**#@-*/
-
- /**** Private Methods ****/
-
- /**
- * Attempts to convert a key / value array item to YAML
- * @access private
- * @return string
- * @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?
- // Make it the right kind of item
- $string = $this->_dumpNode($key,NULL,$indent);
- // Add the indent
- $indent += $this->_dumpIndent;
- // Yamlize the array
- $string .= $this->_yamlizeArray($value,$indent);
- } elseif (!is_array($value)) {
- // It doesn't have children. Yip.
- $string = $this->_dumpNode($key,$value,$indent);
- }
- 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 = '';
- foreach ($array as $key => $value) {
- $string .= $this->_yamlize($key,$value,$indent);
- }
- return $string;
- } else {
- return false;
- }
- }
-
- /**
- * Find out whether a string needs to be output as a literal rather than in plain style.
- * Added by Roan Kattouw 13-03-2008
- * @param $value The string to check
- * @return bool
- */
- function _needLiteral($value) {
- # Check whether the string contains # or : or begins with any of:
- # [ - ? , [ ] { } ! * & | > ' " % @ ` ]
- # 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
- * @return string
- * @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 ($this->_needLiteral($value)) {
- $value = $this->_doLiteralBlock($value,$indent);
- } else {
- $value = $this->_doFolding($value,$indent);
- }
-
- $spaces = str_repeat(' ',$indent);
-
- if (is_int($key)) {
- // It's a sequence
- if ($value)
- $string = $spaces.'- '.$value."\n";
- else
- $string = $spaces . "-\n";
- } else {
- // It's mapped
- if ($value)
- $string = $spaces.$key.': '.$value."\n";
- else
- $string = $spaces . $key . ":\n";
- }
- return $string;
- }
-
- /**
- * Creates a literal block for dumping
- * @access private
- * @return string
- * @param $value
- * @param $indent int The value of the indent
- */
- function _doLiteralBlock($value,$indent) {
- $exploded = explode("\n",$value);
- $newValue = '|';
- $indent += $this->_dumpIndent;
- $spaces = str_repeat(' ',$indent);
- foreach ($exploded as $line) {
- $newValue .= "\n" . $spaces . trim($line);
- }
- return $newValue;
- }
-
- /**
- * Folds a string of text, if necessary
- * @access private
- * @return string
- * @param $value The string you wish to fold
- */
- function _doFolding($value,$indent) {
- // Don't do anything if wordwrap is set to 0
- if ($this->_dumpWordWrap === 0) {
- return $value;
- }
-
- if (strlen($value) > $this->_dumpWordWrap) {
- $indent += $this->_dumpIndent;
- $indent = str_repeat(' ',$indent);
- $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
- $value = ">\n".$indent.$wrapped;
- }
- return $value;
- }
-
- /* Methods used in loading */
-
- /**
- * Finds and returns the indentation of a YAML line
- * @access private
- * @return int
- * @param string $line A line from the YAML file
- */
- function _getIndent($line) {
- $match = array();
- preg_match('/^\s{1,}/',$line,$match);
- if (!empty($match[0])) {
- $indent = substr_count($match[0],' ');
- } else {
- $indent = 0;
- }
- return $indent;
- }
-
- /**
- * Parses YAML code and returns an array for a node
- * @access private
- * @return array
- * @param string $line A line from the YAML file
- */
- function _parseLine($line) {
- $line = trim($line);
-
- $array = array();
-
- if (preg_match('/^-(.*):$/',$line)) {
- // It's a mapped sequence
- $key = trim(substr(substr($line,1),0,-1));
- $array[$key] = '';
- } elseif ($line[0] == '-' && substr($line,0,3) != '---') {
- // It's a list item but not a new stream
- if (strlen($line) > 1) {
- $value = trim(substr($line,1));
- // Set the type of the value. Int, string, etc
- $value = $this->_toType($value);
- $array[] = $value;
- } else {
- $array[] = array();
- }
- } elseif (preg_match('/^(.+):/',$line,$key)) {
- // It's a key/value pair most likely
- // If the key is in double quotes pull it out
- $matches = array();
- if (preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
- $value = trim(str_replace($matches[1],'',$line));
- $key = $matches[2];
- } else {
- // Do some guesswork as to the key and the value
- $explode = explode(':',$line);
- $key = trim($explode[0]);
- array_shift($explode);
- $value = trim(implode(':',$explode));
- }
-
- // Set the type of the value. Int, string, etc
- $value = $this->_toType($value);
- if (empty($key)) {
- $array[] = $value;
- } else {
- $array[$key] = $value;
- }
- }
- return $array;
- }
-
- /**
- * Finds the type of the passed value, returns the value as the new type.
- * @access private
- * @param string $value
- * @return mixed
- */
- function _toType($value) {
- $matches = array();
- if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) {
- $value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches));
- $value = preg_replace('/\\\\"/','"',$value);
- } elseif (preg_match('/^\\[(.+)\\]$/',$value,$matches)) {
- // Inline Sequence
-
- // Take out strings sequences and mappings
- $explode = $this->_inlineEscape($matches[1]);
-
- // Propogate value array
- $value = array();
- foreach ($explode as $v) {
- $value[] = $this->_toType($v);
- }
- } elseif (strpos($value,': ')!==false && !preg_match('/^{(.+)/',$value)) {
- // It's a map
- $array = explode(': ',$value);
- $key = trim($array[0]);
- array_shift($array);
- $value = trim(implode(': ',$array));
- $value = $this->_toType($value);
- $value = array($key => $value);
- } elseif (preg_match("/{(.+)}$/",$value,$matches)) {
- // Inline Mapping
-
- // Take out strings sequences and mappings
- $explode = $this->_inlineEscape($matches[1]);
-
- // Propogate value array
- $array = array();
- foreach ($explode as $v) {
- $array = $array + $this->_toType($v);
- }
- $value = $array;
- } elseif (strtolower($value) == 'null' or $value == '' or $value == '~') {
- $value = NULL;
- } elseif (ctype_digit($value)) {
- $value = (int)$value;
- } elseif (in_array(strtolower($value),
- array('true', 'on', '+', 'yes', 'y'))) {
- $value = TRUE;
- } elseif (in_array(strtolower($value),
- array('false', 'off', '-', 'no', 'n'))) {
- $value = FALSE;
- } elseif (is_numeric($value)) {
- $value = (float)$value;
- } else {
- // 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
- * @return array
- */
- function _inlineEscape($inline) {
- // There's gotta be a cleaner way to do this...
- // 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
- $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
- $strings = array();
- if (preg_match_all($regex,$inline,$strings)) {
- $saved_strings[] = $strings[0][0];
- $inline = preg_replace($regex,'YAMLString',$inline);
- }
- unset($regex);
-
- // Check for sequences
- $seqs = array();
- if (preg_match_all('/\[(.+)\]/U',$inline,$seqs)) {
- $inline = preg_replace('/\[(.+)\]/U','YAMLSeq',$inline);
- $seqs = $seqs[0];
- }
-
- // Check for mappings
- $maps = array();
- if (preg_match_all('/{(.+)}/U',$inline,$maps)) {
- $inline = preg_replace('/{(.+)}/U','YAMLMap',$inline);
- $maps = $maps[0];
- }
-
- $explode = explode(', ',$inline);
-
- // Re-add the strings
- if (!empty($saved_strings)) {
- $i = 0;
- foreach ($explode as $key => $value) {
- if (strpos($value,'YAMLString')) {
- $explode[$key] = str_replace('YAMLString',$saved_strings[$i],$value);
- ++$i;
- }
- }
- }
-
- // Re-add the sequences
- if (!empty($seqs)) {
- $i = 0;
- foreach ($explode as $key => $value) {
- if (strpos($value,'YAMLSeq') !== false) {
- $explode[$key] = str_replace('YAMLSeq',$seqs[$i],$value);
- ++$i;
- }
- }
- }
-
- // Re-add the mappings
- if (!empty($maps)) {
- $i = 0;
- foreach ($explode as $key => $value) {
- if (strpos($value,'YAMLMap') !== false) {
- $explode[$key] = str_replace('YAMLMap',$maps[$i],$value);
- ++$i;
- }
- }
- }
-
- return $explode;
- }
-
- /**
- * Builds the PHP array from all the YAML nodes we've gathered
- * @access private
- * @return array
- */
- function _buildArray() {
- $trunk = array();
-
- if (!isset($this->_indentSort[0])) {
- return $trunk;
- }
-
- foreach ($this->_indentSort[0] as $n) {
- if (empty($n->parent)) {
- $this->_nodeArrayizeData($n);
- // Check for references and copy the needed data to complete them.
- $this->_makeReferences($n);
- // Merge our data with the big array we're building
- $trunk = $this->_array_kmerge($trunk,$n->data);
- }
- }
-
- return $trunk;
- }
-
- /**
- * Traverses node-space and sets references (& and *) accordingly
- * @access private
- * @return bool
- */
- function _linkReferences() {
- if (is_array($this->_haveRefs)) {
- foreach ($this->_haveRefs as $node) {
- if (!empty($node->data)) {
- $key = key($node->data);
- // If it's an array, don't check.
- if (is_array($node->data[$key])) {
- foreach ($node->data[$key] as $k => $v) {
- $this->_linkRef($node,$key,$k,$v);
- }
- } else {
- $this->_linkRef($node,$key);
- }
- }
- }
- }
- return true;
- }
-
- function _linkRef(&$n,$key,$k = NULL,$v = NULL) {
- if (empty($k) && empty($v)) {
- // Look for &refs
- $matches = array();
- if (preg_match('/^&([^ ]+)/',$n->data[$key],$matches)) {
- // Flag the node so we know it's a reference
- $this->_allNodes[$n->id]->ref = substr($matches[0],1);
- $this->_allNodes[$n->id]->data[$key] =
- substr($n->data[$key],strlen($matches[0])+1);
- // Look for *refs
- } elseif (preg_match('/^\*([^ ]+)/',$n->data[$key],$matches)) {
- $ref = substr($matches[0],1);
- // Flag the node as having a reference
- $this->_allNodes[$n->id]->refKey = $ref;
- }
- } elseif (!empty($k) && !empty($v)) {
- 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] =
- substr($v,strlen($matches[0])+1);
- // Look for *refs
- } elseif (preg_match('/^\*([^ ]+)/',$v,$matches)) {
- $ref = substr($matches[0],1);
- // Flag the node as having a reference
- $this->_allNodes[$n->id]->refKey = $ref;
- }
- }
- }
-
- /**
- * Finds the children of a node and aids in the building of the PHP array
- * @access private
- * @param int $nid The id of the node whose children we're gathering
- * @return array
- */
- function _gatherChildren($nid) {
- $return = array();
- $node =& $this->_allNodes[$nid];
- foreach ($this->_allNodes as $z) {
- if ($z->parent == $node->id) {
- // We found a child
- $this->_nodeArrayizeData($z);
- // Check for references
- $this->_makeReferences($z);
- // Merge with the big array we're returning
- // The big array being all the data of the children of our parent node
- $return = $this->_array_kmerge($return,$z->data);
- }
- }
- return $return;
- }
-
- /**
- * Turns a node's data and its children's data into a PHP array
- *
- * @access private
- * @param array $node The node which you want to arrayize
- * @return boolean
- */
- function _nodeArrayizeData(&$node) {
- if (is_array($node->data) && $node->children == true) {
- // This node has children, so we need to find them
- $childs = $this->_gatherChildren($node->id);
- // We've gathered all our children's data and are ready to use it
- $key = key($node->data);
- $key = empty($key) ? 0 : $key;
- // If it's an array, add to it of course
- if (is_array($node->data[$key])) {
- $node->data[$key] = $this->_array_kmerge($node->data[$key],$childs);
- } else {
- $node->data[$key] = $childs;
- }
- } elseif (!is_array($node->data) && $node->children == true) {
- // Same as above, find the children of this node
- $childs = $this->_gatherChildren($node->id);
- $node->data = array();
- $node->data[] = $childs;
- }
-
- // We edited $node by reference, so just return true
- return true;
- }
-
- /**
- * Traverses node-space and copies references to / from this object.
- * @access private
- * @param object $z A node whose references we wish to make real
- * @return bool
- */
- function _makeReferences(&$z) {
- // It is a reference
- if (isset($z->ref)) {
- $key = key($z->data);
- // Copy the data to this object for easy retrieval later
- $this->ref[$z->ref] =& $z->data[$key];
- // It has a reference
- } elseif (isset($z->refKey)) {
- if (isset($this->ref[$z->refKey])) {
- $key = key($z->data);
- // Copy the data from this object to make the node a real reference
- $z->data[$key] =& $this->ref[$z->refKey];
- }
- }
- return true;
- }
-
-
- /**
- * Merges arrays and maintains numeric keys.
- *
- * 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://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();
-
- 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) {
- list( /* unused */ ,$val) = each($vals);
- // This is the good part! If a key already exists, but it's part of a
- // sequence (an int), just keep addin numbers until we find a fresh one.
- if (isset($ret[$key]) and is_int($key)) {
- while (array_key_exists($key, $ret)) {
- $key++;
- }
- }
- $ret[$key] = $val;
- }
-
- return $ret;
- }
- }
+/**
+ * Spyc -- A Simple PHP YAML Class
+ * @version 0.2.3 -- 2006-02-04
+ * @author Chris Wanstrath <chris@ozmm.org>
+ * @see http://spyc.sourceforge.net/
+ * @copyright Copyright 2005-2006 Chris Wanstrath
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+/**
+ * The Simple PHP YAML Class.
+ *
+ * This class can be used to read a YAML file and convert its contents
+ * into a PHP array. It currently supports a very limited subsection of
+ * the YAML spec.
+ *
+ * @ingroup API
+ */
+class Spyc {
+
+ /**
+ * Dump YAML from PHP array statically
+ *
+ * The dump method, when supplied with an array, will do its best
+ * to convert the array into friendly YAML. Pretty simple. Feel free to
+ * 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
+ * you want to use the default.
+ *
+ * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
+ * you can turn off wordwrap by passing in 0.
+ *
+ * @return string
+ * @param $array Array: PHP array
+ * @param $indent Integer: Pass in false to use the default, which is 2
+ * @param $wordwrap Integer: 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);
+ }
+
+ /**
+ * Dump PHP array to YAML
+ *
+ * The dump method, when supplied with an array, will do its best
+ * to convert the array into friendly YAML. Pretty simple. Feel free to
+ * 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
+ * you want to use the default.
+ *
+ * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
+ * you can turn off wordwrap by passing in 0.
+ *
+ * @public
+ * @return string
+ * @param $array Array: PHP array
+ * @param $indent Integer: Pass in false to use the default, which is 2
+ * @param $wordwrap Integer: Pass in 0 for no wordwrap, false for default (40)
+ */
+ function dump($array,$indent = false,$wordwrap = false) {
+ // Dumps to some very clean YAML. We'll have to add some more features
+ // and options soon. And better support for folding.
+
+ // New features and options.
+ if ($indent === false or !is_numeric($indent)) {
+ $this->_dumpIndent = 2;
+ } 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 ****/
+
+ private $_haveRefs;
+ private $_allNodes;
+ private $_lastIndent;
+ private $_lastNode;
+ private $_inBlock;
+ private $_isInline;
+ private $_dumpIndent;
+ private $_dumpWordWrap;
+
+ /**** Private Methods ****/
+
+ /**
+ * Attempts to convert a key / value array item to YAML
+ * @return string
+ * @param $key The name of the key
+ * @param $value The value of the item
+ * @param $indent The indent of the current node
+ */
+ private function _yamlize($key,$value,$indent) {
+ if (is_array($value)) {
+ // It has children. What to do?
+ // Make it the right kind of item
+ $string = $this->_dumpNode($key,NULL,$indent);
+ // Add the indent
+ $indent += $this->_dumpIndent;
+ // Yamlize the array
+ $string .= $this->_yamlizeArray($value,$indent);
+ } elseif (!is_array($value)) {
+ // It doesn't have children. Yip.
+ $string = $this->_dumpNode($key,$value,$indent);
+ }
+ return $string;
+ }
+
+ /**
+ * Attempts to convert an array to YAML
+ * @return string
+ * @param $array The array you want to convert
+ * @param $indent The indent of the current level
+ */
+ private function _yamlizeArray($array,$indent) {
+ if (is_array($array)) {
+ $string = '';
+ foreach ($array as $key => $value) {
+ $string .= $this->_yamlize($key,$value,$indent);
+ }
+ return $string;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Find out whether a string needs to be output as a literal rather than in plain style.
+ * Added by Roan Kattouw 13-03-2008
+ * @param $value The string to check
+ * @return bool
+ */
+ function _needLiteral($value) {
+ # Check whether the string contains # or : or begins with any of:
+ # [ - ? , [ ] { } ! * & | > ' " % @ ` ]
+ # 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
+ * @return string
+ * @param $key The name of the key
+ * @param $value The value of the item
+ * @param $indent The indent of the current node
+ */
+ private function _dumpNode($key,$value,$indent) {
+ // do some folding here, for blocks
+ if ($this->_needLiteral($value)) {
+ $value = $this->_doLiteralBlock($value,$indent);
+ } else {
+ $value = $this->_doFolding($value,$indent);
+ }
+
+ $spaces = str_repeat(' ',$indent);
+
+ if (is_int($key)) {
+ // It's a sequence
+ if ($value !== '' && !is_null($value))
+ $string = $spaces.'- '.$value."\n";
+ else
+ $string = $spaces . "-\n";
+ } else {
+ // It's mapped
+ if ($value !== '' && !is_null($value))
+ $string = $spaces . $key . ': ' . $value . "\n";
+ else
+ $string = $spaces . $key . ":\n";
+ }
+ return $string;
+ }
+
+ /**
+ * Creates a literal block for dumping
+ * @return string
+ * @param $value
+ * @param $indent int The value of the indent
+ */
+ private function _doLiteralBlock($value,$indent) {
+ $exploded = explode("\n",$value);
+ $newValue = '|';
+ $indent += $this->_dumpIndent;
+ $spaces = str_repeat(' ',$indent);
+ foreach ($exploded as $line) {
+ $newValue .= "\n" . $spaces . trim($line);
+ }
+ return $newValue;
+ }
+
+ /**
+ * Folds a string of text, if necessary
+ * @return string
+ * @param $value The string you wish to fold
+ */
+ private function _doFolding($value,$indent) {
+ // Don't do anything if wordwrap is set to 0
+ if ($this->_dumpWordWrap === 0) {
+ return $value;
+ }
+
+ if (strlen($value) > $this->_dumpWordWrap) {
+ $indent += $this->_dumpIndent;
+ $indent = str_repeat(' ',$indent);
+ $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
+ $value = ">\n".$indent.$wrapped;
+ }
+ return $value;
+ }
+}
diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php
index a45390c4..43b30f7c 100644
--- a/includes/api/ApiLogin.php
+++ b/includes/api/ApiLogin.php
@@ -36,23 +36,6 @@ if (!defined('MEDIAWIKI')) {
*/
class ApiLogin extends ApiBase {
- /**
- * Time (in seconds) a user must wait after submitting
- * a bad login (will be multiplied by the THROTTLE_FACTOR for each bad attempt)
- */
- const THROTTLE_TIME = 5;
-
- /**
- * The factor by which the wait-time in between authentication
- * attempts is increased every failed attempt.
- */
- const THROTTLE_FACTOR = 2;
-
- /**
- * The maximum number of failed logins after which the wait increase stops.
- */
- const THOTTLE_MAX_COUNT = 10;
-
public function __construct($main, $action) {
parent :: __construct($main, $action, 'lg');
}
@@ -61,7 +44,7 @@ class ApiLogin extends ApiBase {
* Executes the log-in attempt using the parameters passed. If
* the log-in succeeeds, it attaches a cookie to the session
* and outputs the user id, username, and session token. If a
- * log-in fails, as the result of a bad password, a nonexistant
+ * log-in fails, as the result of a bad password, a nonexistent
* user, or any other reason, the host is cached with an expiry
* and no log-in attempts will be accepted until that expiry
* is reached. The expiry is $this->mLoginThrottle.
@@ -69,25 +52,14 @@ class ApiLogin extends ApiBase {
* @access public
*/
public function execute() {
- $name = $password = $domain = null;
- extract($this->extractRequestParams());
+ $params = $this->extractRequestParams();
$result = array ();
- // Make sure noone is trying to guess the password brut-force
- $nextLoginIn = $this->getNextLoginTimeout();
- if ($nextLoginIn > 0) {
- $result['result'] = 'NeedToWait';
- $result['details'] = "Please wait $nextLoginIn seconds before next log-in attempt";
- $result['wait'] = $nextLoginIn;
- $this->getResult()->addValue(null, 'login', $result);
- return;
- }
-
- $params = new FauxRequest(array (
- 'wpName' => $name,
- 'wpPassword' => $password,
- 'wpDomain' => $domain,
+ $req = new FauxRequest(array (
+ 'wpName' => $params['name'],
+ 'wpPassword' => $params['password'],
+ 'wpDomain' => $params['domain'],
'wpRemember' => ''
));
@@ -96,8 +68,8 @@ class ApiLogin extends ApiBase {
wfSetupSession();
}
- $loginForm = new LoginForm($params);
- switch ($loginForm->authenticateUserData()) {
+ $loginForm = new LoginForm($req);
+ switch ($authRes = $loginForm->authenticateUserData()) {
case LoginForm :: SUCCESS :
global $wgUser, $wgCookiePrefix;
@@ -139,95 +111,18 @@ class ApiLogin extends ApiBase {
$result['result'] = 'CreateBlocked';
$result['details'] = 'Your IP address is blocked from account creation';
break;
+ case LoginForm :: THROTTLED :
+ global $wgPasswordAttemptThrottle;
+ $result['result'] = 'Throttled';
+ $result['wait'] = $wgPasswordAttemptThrottle['seconds'];
+ break;
default :
- ApiBase :: dieDebug(__METHOD__, 'Unhandled case value');
- }
-
- if ($result['result'] != 'Success' && !isset( $result['details'] ) ) {
- $delay = $this->cacheBadLogin();
- $result['wait'] = $delay;
- $result['details'] = "Please wait " . $delay . " seconds before next log-in attempt";
+ ApiBase :: dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
}
- // if we were allowed to try to login, memcache is fine
$this->getResult()->addValue(null, 'login', $result);
}
-
- /**
- * Caches a bad-login attempt associated with the host and with an
- * expiry of $this->mLoginThrottle. These are cached by a key
- * separate from that used by the captcha system--as such, logging
- * in through the standard interface will get you a legal session
- * and cookies to prove it, but will not remove this entry.
- *
- * Returns the number of seconds until next login attempt will be allowed.
- *
- * @access private
- */
- private function cacheBadLogin() {
- global $wgMemc;
-
- $key = $this->getMemCacheKey();
- $val = $wgMemc->get( $key );
-
- $val['lastReqTime'] = time();
- if (!isset($val['count'])) {
- $val['count'] = 1;
- } else {
- $val['count'] = 1 + $val['count'];
- }
-
- $delay = ApiLogin::calculateDelay($val['count']);
-
- $wgMemc->delete($key);
- // Cache expiration should be the maximum timeout - to prevent a "try and wait" attack
- $wgMemc->add( $key, $val, ApiLogin::calculateDelay(ApiLogin::THOTTLE_MAX_COUNT) );
-
- return $delay;
- }
-
- /**
- * How much time the client must wait before it will be
- * allowed to try to log-in next.
- * The return value is 0 if no wait is required.
- */
- private function getNextLoginTimeout() {
- global $wgMemc;
-
- $val = $wgMemc->get($this->getMemCacheKey());
-
- $elapse = (time() - $val['lastReqTime']); // in seconds
- $canRetryIn = ApiLogin::calculateDelay($val['count']) - $elapse;
-
- return $canRetryIn < 0 ? 0 : $canRetryIn;
- }
-
- /**
- * Based on the number of previously attempted logins, returns
- * the delay (in seconds) when the next login attempt will be allowed.
- */
- private static function calculateDelay($count) {
- // Defensive programming
- $count = intval($count);
- $count = $count < 1 ? 1 : $count;
- $count = $count > self::THOTTLE_MAX_COUNT ? self::THOTTLE_MAX_COUNT : $count;
-
- return self::THROTTLE_TIME + self::THROTTLE_TIME * ($count - 1) * self::THROTTLE_FACTOR;
- }
-
- /**
- * Internal cache key for badlogin checks. Robbed from the
- * ConfirmEdit extension and modified to use a key unique to the
- * API login.3
- *
- * @return string
- * @access private
- */
- private function getMemCacheKey() {
- return wfMemcKey( 'apilogin', 'badlogin', 'ip', wfGetIP() );
- }
-
public function mustBePosted() { return true; }
public function getAllowedParams() {
@@ -263,6 +158,6 @@ class ApiLogin extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiLogin.php 35565 2008-05-29 19:23:37Z btongminh $';
+ return __CLASS__ . ': $Id: ApiLogin.php 45275 2009-01-01 02:02:03Z simetrical $';
}
}
diff --git a/includes/api/ApiLogout.php b/includes/api/ApiLogout.php
index 694c9e3c..8b178f6a 100644
--- a/includes/api/ApiLogout.php
+++ b/includes/api/ApiLogout.php
@@ -42,11 +42,12 @@ class ApiLogout extends ApiBase {
public function execute() {
global $wgUser;
+ $oldName = $wgUser->getName();
$wgUser->logout();
// Give extensions to do something after user logout
$injected_html = '';
- wfRunHooks( 'UserLogoutComplete', array(&$wgUser, &$injected_html) );
+ wfRunHooks( 'UserLogoutComplete', array(&$wgUser, &$injected_html, $oldName) );
}
public function getAllowedParams() {
@@ -70,6 +71,6 @@ class ApiLogout extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiLogout.php 35294 2008-05-24 20:44:49Z btongminh $';
+ return __CLASS__ . ': $Id: ApiLogout.php 43522 2008-11-15 01:23:39Z brion $';
}
}
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 2d0e278c..60d932be 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -65,6 +65,7 @@ class ApiMain extends ApiBase {
'feedwatchlist' => 'ApiFeedWatchlist',
'help' => 'ApiHelp',
'paraminfo' => 'ApiParamInfo',
+ 'purge' => 'ApiPurge',
);
private static $WriteModules = array (
@@ -77,6 +78,8 @@ class ApiMain extends ApiBase {
'move' => 'ApiMove',
'edit' => 'ApiEditPage',
'emailuser' => 'ApiEmailUser',
+ 'watch' => 'ApiWatch',
+ 'patrol' => 'ApiPatrol',
);
/**
@@ -99,6 +102,23 @@ class ApiMain extends ApiBase {
'dbg' => 'ApiFormatDbg',
'dbgfm' => 'ApiFormatDbg'
);
+
+ /**
+ * List of user roles that are specifically relevant to the API.
+ * array( 'right' => array ( 'msg' => 'Some message with a $1',
+ * 'params' => array ( $someVarToSubst ) ),
+ * );
+ */
+ private static $mRights = array('writeapi' => array(
+ 'msg' => 'Use of the write API',
+ 'params' => array()
+ ),
+ 'apihighlimits' => array(
+ 'msg' => 'Use higher limits in API queries (Slow queries: $1 results; Fast queries: $2 results). The limits for slow queries also apply to multivalue parameters.',
+ 'params' => array (ApiMain::LIMIT_SML2, ApiMain::LIMIT_BIG2)
+ )
+ );
+
private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames;
private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage;
@@ -144,9 +164,9 @@ class ApiMain extends ApiBase {
if($wgEnableWriteAPI)
$this->mModules += self::$WriteModules;
- $this->mModuleNames = array_keys($this->mModules); // todo: optimize
+ $this->mModuleNames = array_keys($this->mModules);
$this->mFormats = self :: $Formats;
- $this->mFormatNames = array_keys($this->mFormats); // todo: optimize
+ $this->mFormatNames = array_keys($this->mFormats);
$this->mResult = new ApiResult($this);
$this->mShowVersions = false;
@@ -193,6 +213,8 @@ class ApiMain extends ApiBase {
if (!$wgUser->isAllowed('writeapi'))
$this->dieUsage('You\'re not allowed to edit this ' .
'wiki through the API', 'writeapidenied');
+ if (wfReadOnly())
+ $this->dieUsageMsg(array('readonlytext'));
}
/**
@@ -206,6 +228,8 @@ class ApiMain extends ApiBase {
* Create an instance of an output formatter by its name
*/
public function createPrinterByName($format) {
+ if( !isset( $this->mFormats[$format] ) )
+ $this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' );
return new $this->mFormats[$format] ($this, $format);
}
@@ -235,6 +259,11 @@ class ApiMain extends ApiBase {
try {
$this->executeAction();
} catch (Exception $e) {
+ // Log it
+ if ( $e instanceof MWException ) {
+ wfDebugLog( 'exception', $e->getLogMessage() );
+ }
+
//
// Handle any kind of exception by outputing properly formatted error message.
// If this fails, an unhandled exception should be thrown so that global error
@@ -248,7 +277,7 @@ class ApiMain extends ApiBase {
$headerStr = 'MediaWiki-API-Error: ' . $errCode;
if ($e->getCode() === 0)
- header($headerStr, true);
+ header($headerStr);
else
header($headerStr, true, $e->getCode());
@@ -260,12 +289,11 @@ 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);
+ $smaxage = $this->getParameter('smaxage');
+ $maxage = $this->getParameter('maxage');
}
else
$smaxage = $maxage = $this->mSquidMaxage;
@@ -332,6 +360,9 @@ class ApiMain extends ApiBase {
}
$this->getResult()->reset();
+ // Re-add the id
+ if($this->mRequest->getCheck('requestid'))
+ $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
$this->getResult()->addValue(null, 'error', $errMessage);
return $errMessage['code'];
@@ -341,12 +372,19 @@ class ApiMain extends ApiBase {
* Execute the actual module, without any error handling
*/
protected function executeAction() {
+ // First add the id to the top element
+ if($this->mRequest->getCheck('requestid'))
+ $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
$params = $this->extractRequestParams();
$this->mShowVersions = $params['version'];
$this->mAction = $params['action'];
+ if( !is_string( $this->mAction ) ) {
+ $this->dieUsage( "The API requires a valid action parameter", 'unknown_action' );
+ }
+
// Instantiate the module requested by the user
$module = new $this->mModules[$this->mAction] ($this, $this->mAction);
@@ -356,6 +394,9 @@ class ApiMain extends ApiBase {
$maxLag = $params['maxlag'];
list( $host, $lag ) = wfGetLB()->getMaxLag();
if ( $lag > $maxLag ) {
+ header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) );
+ header( 'X-Database-Lag: ' . intval( $lag ) );
+ // XXX: should we return a 503 HTTP error code like wfMaxlagError() does?
if( $wgShowHostnames ) {
ApiBase :: dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' );
} else {
@@ -384,6 +425,7 @@ class ApiMain extends ApiBase {
// Execute
$module->profileIn();
$module->execute();
+ wfRunHooks('APIAfterExecute', array(&$module));
$module->profileOut();
if (!$this->mInternalMode) {
@@ -396,6 +438,7 @@ class ApiMain extends ApiBase {
* Print results using the current printer
*/
protected function printResult($isError) {
+ $this->getResult()->cleanupUTF8();
$printer = $this->mPrinter;
$printer->profileIn();
@@ -437,6 +480,7 @@ class ApiMain extends ApiBase {
ApiBase :: PARAM_TYPE => 'integer',
ApiBase :: PARAM_DFLT => 0
),
+ 'requestid' => null,
);
}
@@ -451,6 +495,7 @@ class ApiMain extends ApiBase {
'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',
+ 'requestid' => 'Request ID to distinguish requests. This will just be output back to you',
);
}
@@ -493,6 +538,7 @@ class ApiMain extends ApiBase {
'API developers:',
' Roan Kattouw <Firstname>.<Lastname>@home.nl (lead developer Sep 2007-present)',
' Victor Vasiliev - vasilvv at gee mail dot com',
+ ' Bryan Tong Minh - bryan . tongminh @ gmail . 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',
@@ -521,6 +567,14 @@ class ApiMain extends ApiBase {
$msg .= "\n";
}
+ $msg .= "\n$astriks Permissions $astriks\n\n";
+ foreach ( self :: $mRights as $right => $rightMsg ) {
+ $groups = User::getGroupsWithPermission( $right );
+ $msg .= "* " . $right . " *\n " . wfMsgReplaceArgs( $rightMsg[ 'msg' ], $rightMsg[ 'params' ] ) .
+ "\nGranted to:\n " . str_replace( "*", "all", implode( ", ", $groups ) ) . "\n";
+
+ }
+
$msg .= "\n$astriks Formats $astriks\n\n";
foreach( $this->mFormats as $formatName => $unused ) {
$module = $this->createPrinterByName($formatName);
@@ -539,7 +593,7 @@ class ApiMain extends ApiBase {
public static function makeHelpMsgHeader($module, $paramName) {
$modulePrefix = $module->getModulePrefix();
- if (!empty($modulePrefix))
+ if (strval($modulePrefix) !== '')
$modulePrefix = "($modulePrefix) ";
return "* $paramName={$module->getModuleName()} $modulePrefix*";
@@ -602,8 +656,8 @@ class ApiMain extends ApiBase {
*/
public function getVersion() {
$vers = array ();
- $vers[] = 'MediaWiki ' . SpecialVersion::getVersion();
- $vers[] = __CLASS__ . ': $Id: ApiMain.php 44569 2008-12-14 08:31:04Z tstarling $';
+ $vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/";
+ $vers[] = __CLASS__ . ': $Id: ApiMain.php 45752 2009-01-14 21:36:57Z catrope $';
$vers[] = ApiBase :: getBaseVersion();
$vers[] = ApiFormatBase :: getBaseVersion();
$vers[] = ApiQueryBase :: getBaseVersion();
diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php
index 8687bdcd..13b058c9 100644
--- a/includes/api/ApiMove.php
+++ b/includes/api/ApiMove.php
@@ -44,9 +44,7 @@ class ApiMove extends ApiBase {
if(is_null($params['reason']))
$params['reason'] = '';
- $titleObj = NULL;
- if(!isset($params['from']))
- $this->dieUsageMsg(array('missingparam', 'from'));
+ $this->requireOnlyOneParameter($params, 'from', 'fromid');
if(!isset($params['to']))
$this->dieUsageMsg(array('missingparam', 'to'));
if(!isset($params['token']))
@@ -54,9 +52,18 @@ class ApiMove extends ApiBase {
if(!$wgUser->matchEditToken($params['token']))
$this->dieUsageMsg(array('sessionfailure'));
- $fromTitle = Title::newFromText($params['from']);
- if(!$fromTitle)
- $this->dieUsageMsg(array('invalidtitle', $params['from']));
+ if(isset($params['from']))
+ {
+ $fromTitle = Title::newFromText($params['from']);
+ if(!$fromTitle)
+ $this->dieUsageMsg(array('invalidtitle', $params['from']));
+ }
+ else if(isset($params['fromid']))
+ {
+ $fromTitle = Title::newFromID($params['fromid']);
+ if(!$fromTitle)
+ $this->dieUsageMsg(array('nosuchpageid', $params['fromid']));
+ }
if(!$fromTitle->exists())
$this->dieUsageMsg(array('notanarticle'));
$fromTalk = $fromTitle->getTalkPage();
@@ -66,27 +73,10 @@ class ApiMove extends ApiBase {
$this->dieUsageMsg(array('invalidtitle', $params['to']));
$toTalk = $toTitle->getTalkPage();
- // 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
- // refactoring of other code, though (mainly SpecialMovepage.php)
- $errors = array_merge($fromTitle->getUserPermissionsErrors('move', $wgUser),
- $fromTitle->getUserPermissionsErrors('edit', $wgUser),
- $toTitle->getUserPermissionsErrors('move', $wgUser),
- $toTitle->getUserPermissionsErrors('edit', $wgUser));
- if(!empty($errors))
- // We don't care about multiple errors, just report one of them
- $this->dieUsageMsg(current($errors));
-
$hookErr = null;
-
$retval = $fromTitle->moveTo($toTitle, true, $params['reason'], !$params['noredirect']);
if($retval !== true)
- {
- # 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'] || !$wgUser->isAllowed('suppressredirect'))
@@ -105,8 +95,9 @@ class ApiMove extends ApiBase {
// We're not gonna dieUsage() on failure, since we already changed something
else
{
- $r['talkmove-error-code'] = ApiBase::$messageMap[$retval]['code'];
- $r['talkmove-error-info'] = ApiBase::$messageMap[$retval]['info'];
+ $parsed = $this->parseMsg(reset($retval));
+ $r['talkmove-error-code'] = $parsed['code'];
+ $r['talkmove-error-info'] = $parsed['info'];
}
}
@@ -129,6 +120,9 @@ class ApiMove extends ApiBase {
public function getAllowedParams() {
return array (
'from' => null,
+ 'fromid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
'to' => null,
'token' => null,
'reason' => null,
@@ -141,7 +135,8 @@ class ApiMove extends ApiBase {
public function getParamDescription() {
return array (
- 'from' => 'Title of the page you want to move.',
+ 'from' => 'Title of the page you want to move. Cannot be used together with fromid.',
+ 'fromid' => 'Page ID of the page you want to move. Cannot be used together with from.',
'to' => 'Title you want to rename the page to.',
'token' => 'A move token previously retrieved through prop=info',
'reason' => 'Reason for the move (optional).',
@@ -154,7 +149,7 @@ class ApiMove extends ApiBase {
public function getDescription() {
return array(
- 'Moves a page.'
+ 'Move a page.'
);
}
@@ -165,6 +160,6 @@ class ApiMove extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiMove.php 35619 2008-05-30 19:59:47Z btongminh $';
+ return __CLASS__ . ': $Id: ApiMove.php 47041 2009-02-09 14:39:41Z catrope $';
}
}
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index e09cb285..54482e4b 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -53,7 +53,7 @@ class ApiPageSet extends ApiQueryBase {
private $mRequestedPageFields;
public function __construct($query, $resolveRedirects = false) {
- parent :: __construct($query, __CLASS__);
+ parent :: __construct($query, 'query');
$this->mAllPages = array ();
$this->mTitles = array();
@@ -92,10 +92,11 @@ class ApiPageSet extends ApiQueryBase {
*/
public function getPageTableFields() {
// Ensure we get minimum required fields
+ // DON'T change this order
$pageFlds = array (
- 'page_id' => null,
'page_namespace' => null,
- 'page_title' => null
+ 'page_title' => null,
+ 'page_id' => null,
);
// only store non-default fields
@@ -227,19 +228,18 @@ class ApiPageSet extends ApiQueryBase {
*/
public function execute() {
$this->profileIn();
- $titles = $pageids = $revids = null;
- extract($this->extractRequestParams());
+ $params = $this->extractRequestParams();
// Only one of the titles/pageids/revids is allowed at the same time
$dataSource = null;
- if (isset ($titles))
+ if (isset ($params['titles']))
$dataSource = 'titles';
- if (isset ($pageids)) {
+ if (isset ($params['pageids'])) {
if (isset ($dataSource))
$this->dieUsage("Cannot use 'pageids' at the same time as '$dataSource'", 'multisource');
$dataSource = 'pageids';
}
- if (isset ($revids)) {
+ if (isset ($params['revids'])) {
if (isset ($dataSource))
$this->dieUsage("Cannot use 'revids' at the same time as '$dataSource'", 'multisource');
$dataSource = 'revids';
@@ -247,15 +247,17 @@ class ApiPageSet extends ApiQueryBase {
switch ($dataSource) {
case 'titles' :
- $this->initFromTitles($titles);
+ $this->initFromTitles($params['titles']);
break;
case 'pageids' :
- $this->initFromPageIds($pageids);
+ $this->initFromPageIds($params['pageids']);
break;
case 'revids' :
if($this->mResolveRedirects)
- $this->dieUsage('revids may not be used with redirect resolution', 'params');
- $this->initFromRevIDs($revids);
+ $this->setWarning('Redirect resolution cannot be used together with the revids= parameter. '.
+ 'Any redirects the revids= point to have not been resolved.');
+ $this->mResolveRedirects = false;
+ $this->initFromRevIDs($params['revids']);
break;
default :
// Do nothing - some queries do not need any of the data sources.
@@ -366,7 +368,7 @@ class ApiPageSet extends ApiQueryBase {
}
private function initFromPageIds($pageids) {
- if(empty($pageids))
+ if(!count($pageids))
return;
$pageids = array_map('intval', $pageids); // paranoia
@@ -424,7 +426,7 @@ class ApiPageSet extends ApiQueryBase {
if(isset($remaining)) {
// Any items left in the $remaining list are added as missing
if($processTitles) {
- // The remaining titles in $remaining are non-existant pages
+ // The remaining titles in $remaining are non-existent pages
foreach ($remaining as $ns => $dbkeys) {
foreach ( $dbkeys as $dbkey => $unused ) {
$title = Title :: makeTitle($ns, $dbkey);
@@ -438,7 +440,7 @@ class ApiPageSet extends ApiQueryBase {
else
{
// The remaining pageids do not exist
- if(empty($this->mMissingPageIDs))
+ if(!$this->mMissingPageIDs)
$this->mMissingPageIDs = array_keys($remaining);
else
$this->mMissingPageIDs = array_merge($this->mMissingPageIDs, array_keys($remaining));
@@ -448,16 +450,16 @@ class ApiPageSet extends ApiQueryBase {
private function initFromRevIDs($revids) {
- if(empty($revids))
+ if(!count($revids))
return;
$db = $this->getDB();
$pageids = array();
$remaining = array_flip($revids);
- $tables = array('revision');
+ $tables = array('revision','page');
$fields = array('rev_id','rev_page');
- $where = array('rev_deleted' => 0, 'rev_id' => $revids);
+ $where = array('rev_deleted' => 0, 'rev_id' => $revids,'rev_page = page_id');
// Get pageIDs data from the `page` table
$this->profileDBIn();
@@ -475,8 +477,6 @@ class ApiPageSet extends ApiQueryBase {
$this->mMissingRevIDs = array_keys($remaining);
// Populate all the page information
- if($this->mResolveRedirects)
- ApiBase :: dieDebug(__METHOD__, 'revids may not be used with redirect resolution');
$this->initFromPageIds(array_keys($pageids));
}
@@ -488,7 +488,7 @@ class ApiPageSet extends ApiQueryBase {
// 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 ($this->mPendingRedirectIDs) {
// Resolve redirects by querying the pagelinks table, and repeat the process
// Create a new linkBatch object for the next pass
@@ -537,7 +537,7 @@ class ApiPageSet extends ApiQueryBase {
$this->mRedirectTitles[$from] = $to;
}
$db->freeResult($res);
- if(!empty($this->mPendingRedirectIDs))
+ if($this->mPendingRedirectIDs)
{
# We found pages that aren't in the redirect table
# Add them
@@ -580,16 +580,16 @@ class ApiPageSet extends ApiQueryBase {
continue; // There's nothing else we can do
}
$iw = $titleObj->getInterwiki();
- if (!empty($iw)) {
+ if (strval($iw) !== '') {
// This title is an interwiki link.
$this->mInterwikiTitles[$titleObj->getPrefixedText()] = $iw;
} else {
// Validation
if ($titleObj->getNamespace() < 0)
- $this->dieUsage("No support for special pages has been implemented", 'unsupportednamespace');
-
- $linkBatch->addObj($titleObj);
+ $this->setWarning("No support for special pages has been implemented");
+ else
+ $linkBatch->addObj($titleObj);
}
// Make sure we remember the original title that was given to us
@@ -628,6 +628,6 @@ class ApiPageSet extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiPageSet.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiPageSet.php 45275 2009-01-01 02:02:03Z simetrical $';
}
}
diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php
index 77ce514f..2cf044cf 100644
--- a/includes/api/ApiParamInfo.php
+++ b/includes/api/ApiParamInfo.php
@@ -86,12 +86,12 @@ class ApiParamInfo extends ApiBase {
$retval['classname'] = get_class($obj);
$retval['description'] = (is_array($obj->getDescription()) ? implode("\n", $obj->getDescription()) : $obj->getDescription());
$retval['prefix'] = $obj->getModulePrefix();
- $allowedParams = $obj->getAllowedParams();
+ $allowedParams = $obj->getFinalParams();
if(!is_array($allowedParams))
return $retval;
$retval['parameters'] = array();
- $paramDesc = $obj->getParamDescription();
- foreach($obj->getAllowedParams() as $n => $p)
+ $paramDesc = $obj->getFinalParamDescription();
+ foreach($allowedParams as $n => $p)
{
$a = array('name' => $n);
if(!is_array($p))
@@ -111,7 +111,15 @@ class ApiParamInfo extends ApiBase {
$a['default'] = $p[ApiBase::PARAM_DFLT];
if(isset($p[ApiBase::PARAM_ISMULTI]))
if($p[ApiBase::PARAM_ISMULTI])
+ {
$a['multi'] = '';
+ $a['limit'] = $this->getMain()->canApiHighLimits() ?
+ ApiBase::LIMIT_SML2 :
+ ApiBase::LIMIT_SML1;
+ }
+ if(isset($p[ApiBase::PARAM_ALLOW_DUPLICATES]))
+ if($p[ApiBase::PARAM_ALLOW_DUPLICATES])
+ $a['allowsduplicates'] = '';
if(isset($p[ApiBase::PARAM_TYPE]))
{
$a['type'] = $p[ApiBase::PARAM_TYPE];
@@ -161,6 +169,6 @@ class ApiParamInfo extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiParamInfo.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiParamInfo.php 41653 2008-10-04 15:03:03Z catrope $';
}
}
diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php
index 4dcc94b6..e221fb1d 100644
--- a/includes/api/ApiParse.php
+++ b/includes/api/ApiParse.php
@@ -49,10 +49,13 @@ class ApiParse extends ApiBase {
$prop = array_flip($params['prop']);
$revid = false;
- global $wgParser, $wgUser;
+ // The parser needs $wgTitle to be set, apparently the
+ // $title parameter in Parser::parse isn't enough *sigh*
+ global $wgParser, $wgUser, $wgTitle;
$popts = new ParserOptions();
$popts->setTidy(true);
$popts->enableLimitReport();
+ $redirValues = null;
if(!is_null($oldid) || !is_null($page))
{
if(!is_null($oldid))
@@ -63,23 +66,42 @@ class ApiParse extends ApiBase {
$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();
+ $text = $rev->getText( Revision::FOR_THIS_USER );
$titleObj = $rev->getTitle();
+ $wgTitle = $titleObj;
$p_result = $wgParser->parse($text, $titleObj, $popts);
}
else
{
- $titleObj = Title::newFromText($page);
+ if($params['redirects'])
+ {
+ $req = new FauxRequest(array(
+ 'action' => 'query',
+ 'redirects' => '',
+ 'titles' => $page
+ ));
+ $main = new ApiMain($req);
+ $main->execute();
+ $data = $main->getResultData();
+ $redirValues = @$data['query']['redirects'];
+ $to = $page;
+ foreach((array)$redirValues as $r)
+ $to = $r['to'];
+ }
+ else
+ $to = $page;
+ $titleObj = Title::newFromText($to);
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();
+ // Try the parser cache first
$pcache = ParserCache::singleton();
$p_result = $pcache->get($articleObj, $wgUser);
- if(!$p_result) {
+ if(!$p_result)
+ {
$p_result = $wgParser->parse($articleObj->getContent(), $titleObj, $popts);
global $wgUseParserCache;
if($wgUseParserCache)
@@ -92,12 +114,25 @@ class ApiParse extends ApiBase {
$titleObj = Title::newFromText($title);
if(!$titleObj)
$titleObj = Title::newFromText("API");
+ $wgTitle = $titleObj;
+ if($params['pst'] || $params['onlypst'])
+ $text = $wgParser->preSaveTransform($text, $titleObj, $wgUser, $popts);
+ if($params['onlypst'])
+ {
+ // Build a result and bail out
+ $result_array['text'] = array();
+ $this->getResult()->setContent($result_array['text'], $text);
+ $this->getResult()->addValue(null, $this->getModuleName(), $result_array);
+ return;
+ }
$p_result = $wgParser->parse($text, $titleObj, $popts);
}
// Return result
$result = $this->getResult();
$result_array = array();
+ if($params['redirects'] && !is_null($redirValues))
+ $result_array['redirects'] = $redirValues;
if(isset($prop['text'])) {
$result_array['text'] = array();
$result->setContent($result_array['text'], $p_result->getText());
@@ -120,6 +155,7 @@ class ApiParse extends ApiBase {
$result_array['revid'] = $oldid;
$result_mapping = array(
+ 'redirects' => 'r',
'langlinks' => 'll',
'categories' => 'cl',
'links' => 'pl',
@@ -184,6 +220,7 @@ class ApiParse extends ApiBase {
),
'text' => null,
'page' => null,
+ 'redirects' => false,
'oldid' => null,
'prop' => array(
ApiBase :: PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid',
@@ -199,19 +236,28 @@ class ApiParse extends ApiBase {
'sections',
'revid'
)
- )
+ ),
+ 'pst' => false,
+ 'onlypst' => false,
);
}
public function getParamDescription() {
return array (
'text' => 'Wikitext to parse',
+ 'redirects' => 'If the page parameter is set to a redirect, resolve it',
'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'
),
+ 'pst' => array( 'Do a pre-save transform on the input before parsing it.',
+ 'Ignored if page or oldid is used.'
+ ),
+ 'onlypst' => array('Do a PST on the input, but don\'t parse it.',
+ 'Returns PSTed wikitext. Ignored if page or oldid is used.'
+ ),
);
}
@@ -226,6 +272,6 @@ class ApiParse extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiParse.php 36983 2008-07-03 15:01:50Z catrope $';
+ return __CLASS__ . ': $Id: ApiParse.php 44858 2008-12-20 20:00:07Z catrope $';
}
}
diff --git a/includes/api/ApiPatrol.php b/includes/api/ApiPatrol.php
new file mode 100644
index 00000000..08de87b0
--- /dev/null
+++ b/includes/api/ApiPatrol.php
@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * Created on Sep 2, 2008
+ *
+ * API for MediaWiki 1.14+
+ *
+ * Copyright (C) 2008 Soxred93 soxred93@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')) {
+ require_once ('ApiBase.php');
+}
+
+/**
+ * Allows user to patrol pages
+ * @ingroup API
+ */
+class ApiPatrol extends ApiBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ /**
+ * Patrols the article or provides the reason the patrol failed.
+ */
+ public function execute() {
+ global $wgUser, $wgUseRCPatrol, $wgUseNPPatrol;
+ $this->getMain()->requestWriteMode();
+ $params = $this->extractRequestParams();
+
+ if(!isset($params['token']))
+ $this->dieUsageMsg(array('missingparam', 'token'));
+ if(!isset($params['rcid']))
+ $this->dieUsageMsg(array('missingparam', 'rcid'));
+ if(!$wgUser->matchEditToken($params['token']))
+ $this->dieUsageMsg(array('sessionfailure'));
+
+ $rc = RecentChange::newFromID($params['rcid']);
+ if(!$rc instanceof RecentChange)
+ $this->dieUsageMsg(array('nosuchrcid', $params['rcid']));
+ $retval = RecentChange::markPatrolled($params['rcid']);
+
+ if($retval)
+ $this->dieUsageMsg(current($retval));
+
+ $result = array('rcid' => $rc->getAttribute('rc_id'));
+ ApiQueryBase::addTitleInfo($result, $rc->getTitle());
+ $this->getResult()->addValue(null, $this->getModuleName(), $result);
+ }
+
+ public function getAllowedParams() {
+ return array (
+ 'token' => null,
+ 'rcid' => array(
+ ApiBase :: PARAM_TYPE => 'integer'
+ ),
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'token' => 'Patrol token obtained from list=recentchanges',
+ 'rcid' => 'Recentchanges ID to patrol',
+ );
+ }
+
+ public function getDescription() {
+ return array (
+ 'Patrol a page or revision. '
+ );
+ }
+
+ protected function getExamples() {
+ return array(
+ 'api.php?action=patrol&token=123abc&rcid=230672766'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiPatrol.php 42548 2008-10-25 14:04:43Z tstarling $';
+ }
+}
diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php
index 30bcfdbc..522d02b2 100644
--- a/includes/api/ApiProtect.php
+++ b/includes/api/ApiProtect.php
@@ -37,7 +37,7 @@ class ApiProtect extends ApiBase {
}
public function execute() {
- global $wgUser;
+ global $wgUser, $wgRestrictionTypes, $wgRestrictionLevels;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
@@ -46,7 +46,7 @@ class ApiProtect extends ApiBase {
$this->dieUsageMsg(array('missingparam', 'title'));
if(!isset($params['token']))
$this->dieUsageMsg(array('missingparam', 'token'));
- if(!isset($params['protections']) || empty($params['protections']))
+ if(empty($params['protections']))
$this->dieUsageMsg(array('missingparam', 'protections'));
if(!$wgUser->matchEditToken($params['token']))
@@ -57,25 +57,23 @@ class ApiProtect extends ApiBase {
$this->dieUsageMsg(array('invalidtitle', $params['title']));
$errors = $titleObj->getUserPermissionsErrors('protect', $wgUser);
- if(!empty($errors))
+ if($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
+ $expiry = (array)$params['expiry'];
+ if(count($expiry) != count($params['protections']))
{
- $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'));
+ if(count($expiry) == 1)
+ $expiry = array_fill(0, count($params['protections']), $expiry[0]);
+ else
+ $this->dieUsageMsg(array('toofewexpiries', count($expiry), count($params['protections'])));
}
-
+
$protections = array();
- foreach($params['protections'] as $prot)
+ $expiryarray = array();
+ $resultProtections = array();
+ foreach($params['protections'] as $i => $prot)
{
$p = explode('=', $prot);
$protections[$p[0]] = ($p[1] == 'all' ? '' : $p[1]);
@@ -83,26 +81,45 @@ class ApiProtect extends ApiBase {
$this->dieUsageMsg(array('create-titleexists'));
if(!$titleObj->exists() && $p[0] != 'create')
$this->dieUsageMsg(array('missingtitles-createonly'));
+ if(!in_array($p[0], $wgRestrictionTypes) && $p[0] != 'create')
+ $this->dieUsageMsg(array('protect-invalidaction', $p[0]));
+ if(!in_array($p[1], $wgRestrictionLevels) && $p[1] != 'all')
+ $this->dieUsageMsg(array('protect-invalidlevel', $p[1]));
+
+ if(in_array($expiry[$i], array('infinite', 'indefinite', 'never')))
+ $expiryarray[$p[0]] = Block::infinity();
+ else
+ {
+ $exp = strtotime($expiry[$i]);
+ if($exp < 0 || $exp == false)
+ $this->dieUsageMsg(array('invalidexpiry', $expiry[$i]));
+
+ $exp = wfTimestamp(TS_MW, $exp);
+ if($exp < wfTimestampNow())
+ $this->dieUsageMsg(array('pastexpiry', $expiry[$i]));
+ $expiryarray[$p[0]] = $exp;
+ }
+ $resultProtections[] = array($p[0] => $protections[$p[0]],
+ 'expiry' => ($expiryarray[$p[0]] == Block::infinity() ?
+ 'infinite' :
+ wfTimestamp(TS_ISO_8601, $expiryarray[$p[0]])));
}
+ $cascade = $params['cascade'];
if($titleObj->exists()) {
$articleObj = new Article($titleObj);
- $ok = $articleObj->updateRestrictions($protections, $params['reason'], $params['cascade'], $expiry);
+ $ok = $articleObj->updateRestrictions($protections, $params['reason'], $cascade, $expiryarray);
} else
- $ok = $titleObj->updateTitleProtection($protections['create'], $params['reason'], $expiry);
+ $ok = $titleObj->updateTitleProtection($protections['create'], $params['reason'], $expiryarray['create']);
if(!$ok)
// 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());
$res = array('title' => $titleObj->getPrefixedText(), 'reason' => $params['reason']);
- if($expiry == Block::infinity())
- $res['expiry'] = 'infinity';
- else
- $res['expiry'] = wfTimestamp(TS_ISO_8601, $expiry);
-
- if($params['cascade'])
+ if($cascade)
$res['cascade'] = '';
- $res['protections'] = $protections;
+ $res['protections'] = $resultProtections;
+ $this->getResult()->setIndexedTagName($res['protections'], 'protection');
$this->getResult()->addValue(null, $this->getModuleName(), $res);
}
@@ -115,7 +132,11 @@ class ApiProtect extends ApiBase {
'protections' => array(
ApiBase :: PARAM_ISMULTI => true
),
- 'expiry' => 'infinite',
+ 'expiry' => array(
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_ALLOW_DUPLICATES => true,
+ ApiBase :: PARAM_DFLT => 'infinite',
+ ),
'reason' => '',
'cascade' => false
);
@@ -123,12 +144,14 @@ class ApiProtect extends ApiBase {
public function getParamDescription() {
return array (
- 'title' => 'Title of the page you want to restore.',
+ 'title' => 'Title of the page you want to (un)protect.',
'token' => 'A protect token previously retrieved through prop=info',
'protections' => 'Pipe-separated list of protection levels, formatted action=group (e.g. edit=sysop)',
- 'expiry' => 'Expiry timestamp. If set to \'infinite\', \'indefinite\' or \'never\', the protection will never expire.',
+ 'expiry' => array('Expiry timestamps. If only one timestamp is set, it\'ll be used for all protections.',
+ 'Use \'infinite\', \'indefinite\' or \'never\', for a neverexpiring protection.'),
'reason' => 'Reason for (un)protecting (optional)',
- 'cascade' => 'Enable cascading protection (i.e. protect pages included in this page)'
+ 'cascade' => array('Enable cascading protection (i.e. protect pages included in this page)',
+ 'Ignored if not all protection levels are \'sysop\' or \'protect\''),
);
}
@@ -140,12 +163,12 @@ class ApiProtect extends ApiBase {
protected function getExamples() {
return array (
- 'api.php?action=protect&title=Main%20Page&token=123ABC&protections=edit=sysop|move=sysop&cascade&expiry=20070901163000',
+ 'api.php?action=protect&title=Main%20Page&token=123ABC&protections=edit=sysop|move=sysop&cascade&expiry=20070901163000|never',
'api.php?action=protect&title=Main%20Page&token=123ABC&protections=edit=all|move=all&reason=Lifting%20restrictions'
);
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiProtect.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiProtect.php 44426 2008-12-10 22:39:41Z catrope $';
}
}
diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php
new file mode 100644
index 00000000..d7202a46
--- /dev/null
+++ b/includes/api/ApiPurge.php
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * Created on Sep 2, 2008
+ *
+ * API for MediaWiki 1.14+
+ *
+ * Copyright (C) 2008 Chad Horohoe
+ *
+ * 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')) {
+ require_once ('ApiBase.php');
+}
+
+/**
+ * API interface for page purging
+ * @ingroup API
+ */
+class ApiPurge extends ApiBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ /**
+ * Purges the cache of a page
+ */
+ public function execute() {
+ global $wgUser;
+ $params = $this->extractRequestParams();
+ if(!$wgUser->isAllowed('purge'))
+ $this->dieUsageMsg(array('cantpurge'));
+ if(!isset($params['titles']))
+ $this->dieUsageMsg(array('missingparam', 'titles'));
+ $result = array();
+ foreach($params['titles'] as $t) {
+ $r = array();
+ $title = Title::newFromText($t);
+ if(!$title instanceof Title)
+ {
+ $r['title'] = $t;
+ $r['invalid'] = '';
+ $result[] = $r;
+ continue;
+ }
+ ApiQueryBase::addTitleInfo($r, $title);
+ if(!$title->exists())
+ {
+ $r['missing'] = '';
+ $result[] = $r;
+ continue;
+ }
+ $article = new Article($title);
+ $article->doPurge(); // Directly purge and skip the UI part of purge().
+ $r['purged'] = '';
+ $result[] = $r;
+ }
+ $this->getResult()->setIndexedTagName($result, 'page');
+ $this->getResult()->addValue(null, $this->getModuleName(), $result);
+ }
+
+ public function getAllowedParams() {
+ return array (
+ 'titles' => array(
+ ApiBase :: PARAM_ISMULTI => true
+ )
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'titles' => 'A list of titles',
+ );
+ }
+
+ public function getDescription() {
+ return array (
+ 'Purge the cache for the given titles.'
+ );
+ }
+
+ protected function getExamples() {
+ return array(
+ 'api.php?action=purge&titles=Main_Page|API'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiPurge.php 41020 2008-09-19 00:21:03Z demon $';
+ }
+}
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index f4a2402f..45a5667a 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -56,6 +56,7 @@ class ApiQuery extends ApiBase {
'categories' => 'ApiQueryCategories',
'extlinks' => 'ApiQueryExternalLinks',
'categoryinfo' => 'ApiQueryCategoryInfo',
+ 'duplicatefiles' => 'ApiQueryDuplicateFiles',
);
private $mQueryListModules = array (
@@ -75,6 +76,7 @@ class ApiQuery extends ApiBase {
'search' => 'ApiQuerySearch',
'usercontribs' => 'ApiQueryContributions',
'watchlist' => 'ApiQueryWatchlist',
+ 'watchlistraw' => 'ApiQueryWatchlistRaw',
'exturlusage' => 'ApiQueryExtLinksUsage',
'users' => 'ApiQueryUsers',
'random' => 'ApiQueryRandom',
@@ -93,10 +95,10 @@ class ApiQuery extends ApiBase {
parent :: __construct($main, $action);
// Allow custom modules to be added in LocalSettings.php
- global $wgApiQueryPropModules, $wgApiQueryListModules, $wgApiQueryMetaModules;
- self :: appendUserModules($this->mQueryPropModules, $wgApiQueryPropModules);
- self :: appendUserModules($this->mQueryListModules, $wgApiQueryListModules);
- self :: appendUserModules($this->mQueryMetaModules, $wgApiQueryMetaModules);
+ global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules;
+ self :: appendUserModules($this->mQueryPropModules, $wgAPIPropModules);
+ self :: appendUserModules($this->mQueryListModules, $wgAPIListModules);
+ self :: appendUserModules($this->mQueryMetaModules, $wgAPIMetaModules);
$this->mPropModuleNames = array_keys($this->mQueryPropModules);
$this->mListModuleNames = array_keys($this->mQueryListModules);
@@ -209,6 +211,7 @@ class ApiQuery extends ApiBase {
foreach ($modules as $module) {
$module->profileIn();
$module->execute();
+ wfRunHooks('APIQueryAfterExecute', array(&$module));
$module->profileOut();
}
}
@@ -229,8 +232,8 @@ class ApiQuery extends ApiBase {
* Create instances of all modules requested by the client
*/
private function InstantiateModules(&$modules, $param, $moduleList) {
- $list = $this->params[$param];
- if (isset ($list))
+ $list = @$this->params[$param];
+ if (!is_null ($list))
foreach ($list as $moduleName)
$modules[] = new $moduleList[$moduleName] ($this, $moduleName);
}
@@ -253,7 +256,7 @@ class ApiQuery extends ApiBase {
);
}
- if (!empty ($normValues)) {
+ if (count($normValues)) {
$result->setIndexedTagName($normValues, 'n');
$result->addValue('query', 'normalized', $normValues);
}
@@ -267,7 +270,7 @@ class ApiQuery extends ApiBase {
);
}
- if (!empty ($intrwValues)) {
+ if (count($intrwValues)) {
$result->setIndexedTagName($intrwValues, 'i');
$result->addValue('query', 'interwiki', $intrwValues);
}
@@ -281,7 +284,7 @@ class ApiQuery extends ApiBase {
);
}
- if (!empty ($redirValues)) {
+ if (count($redirValues)) {
$result->setIndexedTagName($redirValues, 'r');
$result->addValue('query', 'redirects', $redirValues);
}
@@ -290,7 +293,7 @@ class ApiQuery extends ApiBase {
// Missing revision elements
//
$missingRevIDs = $pageSet->getMissingRevisionIDs();
- if (!empty ($missingRevIDs)) {
+ if (count($missingRevIDs)) {
$revids = array ();
foreach ($missingRevIDs as $revid) {
$revids[$revid] = array (
@@ -332,7 +335,7 @@ class ApiQuery extends ApiBase {
$pages[$pageid] = $vals;
}
- if (!empty ($pages)) {
+ if (count($pages)) {
if ($this->params['indexpageids']) {
$pageIDs = array_keys($pages);
@@ -381,6 +384,7 @@ class ApiQuery extends ApiBase {
// populate resultPageSet with the generator output
$generator->profileIn();
$generator->executeGenerator($resultPageSet);
+ wfRunHooks('APIQueryGeneratorAfterExecute', array(&$generator, &$resultPageSet));
$resultPageSet->finishPageSetGeneration();
$generator->profileOut();
@@ -476,7 +480,6 @@ class ApiQuery extends ApiBase {
return $psModule->makeHelpMsgParameters() . parent :: makeHelpMsgParameters();
}
- // @todo should work correctly
public function shouldCheckMaxlag() {
return true;
}
@@ -509,7 +512,7 @@ class ApiQuery extends ApiBase {
public function getVersion() {
$psModule = new ApiPageSet($this);
$vers = array ();
- $vers[] = __CLASS__ . ': $Id: ApiQuery.php 35098 2008-05-20 17:13:28Z ialex $';
+ $vers[] = __CLASS__ . ': $Id: ApiQuery.php 42548 2008-10-25 14:04:43Z tstarling $';
$vers[] = $psModule->getVersion();
return $vers;
}
diff --git a/includes/api/ApiQueryAllCategories.php b/includes/api/ApiQueryAllCategories.php
index 3ff42c88..e6287eea 100644
--- a/includes/api/ApiQueryAllCategories.php
+++ b/includes/api/ApiQueryAllCategories.php
@@ -56,17 +56,30 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
$this->addTables('category');
$this->addFields('cat_title');
- if (!is_null($params['from']))
- $this->addWhere('cat_title>=' . $db->addQuotes($this->titleToKey($params['from'])));
+ $dir = ($params['dir'] == 'descending' ? 'older' : 'newer');
+ $from = (is_null($params['from']) ? null : $this->titlePartToKey($params['from']));
+ $this->addWhereRange('cat_title', $dir, $from, null);
if (isset ($params['prefix']))
- $this->addWhere("cat_title LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("cat_title LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'");
$this->addOption('LIMIT', $params['limit']+1);
$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']) );
+ if(isset($prop['hidden']))
+ {
+ $this->addTables(array('page', 'page_props'));
+ $this->addJoinConds(array(
+ 'page' => array('LEFT JOIN', array(
+ 'page_namespace' => NS_CATEGORY,
+ 'page_title=cat_title')),
+ 'page_props' => array('LEFT JOIN', array(
+ 'pp_page=page_id',
+ 'pp_propname' => 'hiddencat')),
+ ));
+ $this->addFields('pp_propname AS cat_hidden');
+ }
$res = $this->select(__METHOD__);
@@ -158,6 +171,6 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllCategories.php 36790 2008-06-29 22:26:23Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllCategories.php 44590 2008-12-14 20:24:23Z catrope $';
}
}
diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php
index aefbb725..9ad34aa2 100644
--- a/includes/api/ApiQueryAllLinks.php
+++ b/includes/api/ApiQueryAllLinks.php
@@ -74,30 +74,30 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$arr = explode('|', $params['continue']);
if(count($arr) != 2)
$this->dieUsage("Invalid continue parameter", 'badcontinue');
- $params['from'] = $arr[0]; // Handled later
+ $from = $this->getDB()->strencode($this->titleToKey($arr[0]));
$id = intval($arr[1]);
- $this->addWhere("pl_from >= $id");
+ $this->addWhere("pl_title > '$from' OR " .
+ "(pl_title = '$from' AND " .
+ "pl_from > $id)");
}
if (!is_null($params['from']))
- $this->addWhere('pl_title>=' . $db->addQuotes($this->titleToKey($params['from'])));
+ $this->addWhere('pl_title>=' . $db->addQuotes($this->titlePartToKey($params['from'])));
if (isset ($params['prefix']))
- $this->addWhere("pl_title LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("pl_title LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'");
$this->addFields(array (
- 'pl_namespace',
'pl_title',
- 'pl_from'
));
+ $this->addFieldsIf('pl_from', !$params['unique']);
$this->addOption('USE INDEX', 'pl_namespace');
$limit = $params['limit'];
$this->addOption('LIMIT', $limit+1);
- # 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
+ if($params['unique'])
$this->addOption('ORDER BY', 'pl_title');
+ else
+ $this->addOption('ORDER BY', 'pl_title, pl_from');
$res = $this->select(__METHOD__);
@@ -107,7 +107,10 @@ 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('continue', $this->keyToTitle($row->pl_title) . "|" . $row->pl_from);
+ if($params['unique'])
+ $this->setContinueEnumParameter('from', $this->keyToTitle($row->pl_title));
+ else
+ $this->setContinueEnumParameter('continue', $this->keyToTitle($row->pl_title) . "|" . $row->pl_from);
break;
}
@@ -116,7 +119,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
if ($fld_ids)
$vals['fromid'] = intval($row->pl_from);
if ($fld_title) {
- $title = Title :: makeTitle($row->pl_namespace, $row->pl_title);
+ $title = Title :: makeTitle($params['namespace'], $row->pl_title);
$vals['ns'] = intval($title->getNamespace());
$vals['title'] = $title->getPrefixedText();
}
@@ -187,6 +190,6 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllLinks.php 37258 2008-07-07 14:48:40Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllLinks.php 45850 2009-01-17 20:03:25Z catrope $';
}
}
diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php
index dd0e98a8..8395808b 100644
--- a/includes/api/ApiQueryAllUsers.php
+++ b/includes/api/ApiQueryAllUsers.php
@@ -121,7 +121,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
$row = $db->fetchObject($res);
$count++;
- if (!$row || $lastUser != $row->user_name) {
+ if (!$row || $lastUser !== $row->user_name) {
// Save the last pass's user data
if (is_array($lastUserData))
$data[] = $lastUserData;
@@ -219,6 +219,6 @@ class ApiQueryAllUsers extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllUsers.php 36790 2008-06-29 22:26:23Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllUsers.php 44472 2008-12-11 21:51:01Z catrope $';
}
}
diff --git a/includes/api/ApiQueryAllimages.php b/includes/api/ApiQueryAllimages.php
index 26cbc368..ea83c667 100644
--- a/includes/api/ApiQueryAllimages.php
+++ b/includes/api/ApiQueryAllimages.php
@@ -61,10 +61,11 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
// Image filters
- if (!is_null($params['from']))
- $this->addWhere('img_name>=' . $db->addQuotes($this->titleToKey($params['from'])));
+ $dir = ($params['dir'] == 'descending' ? 'older' : 'newer');
+ $from = (is_null($params['from']) ? null : $this->titlePartToKey($params['from']));
+ $this->addWhereRange('img_name', $dir, $from, null);
if (isset ($params['prefix']))
- $this->addWhere("img_name LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("img_name LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'");
if (isset ($params['minsize'])) {
$this->addWhere('img_size>=' . intval($params['minsize']));
@@ -109,10 +110,10 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase {
if (is_null($resultPageSet)) {
$file = $repo->newFileFromRow( $row );
-
- $data[] = ApiQueryImageInfo::getInfo( $file, $prop, $result );
+ $data[] = array_merge(array('name' => $row->img_name),
+ ApiQueryImageInfo::getInfo($file, $prop, $result));
} else {
- $data[] = Title::makeTitle( NS_IMAGE, $row->img_name );
+ $data[] = Title::makeTitle(NS_FILE, $row->img_name);
}
}
$db->freeResult($res);
@@ -162,7 +163,8 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase {
'dimensions', // Obsolete
'mime',
'sha1',
- 'metadata'
+ 'metadata',
+ 'bitdepth',
),
ApiBase :: PARAM_DFLT => 'timestamp|url',
ApiBase :: PARAM_ISMULTI => true
@@ -200,6 +202,6 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllimages.php 37909 2008-07-22 13:26:15Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryAllimages.php 44121 2008-12-01 17:14:30Z vyznev $';
}
}
diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php
index 39490fe7..531fa02a 100644
--- a/includes/api/ApiQueryAllpages.php
+++ b/includes/api/ApiQueryAllpages.php
@@ -62,11 +62,21 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$this->addWhereIf('page_is_redirect = 0', $params['filterredir'] === 'nonredirects');
$this->addWhereFld('page_namespace', $params['namespace']);
$dir = ($params['dir'] == 'descending' ? 'older' : 'newer');
- $from = (is_null($params['from']) ? null : $this->titleToKey($params['from']));
+ $from = (is_null($params['from']) ? null : $this->titlePartToKey($params['from']));
$this->addWhereRange('page_title', $dir, $from, null);
if (isset ($params['prefix']))
- $this->addWhere("page_title LIKE '" . $db->escapeLike($this->titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("page_title LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'");
+ if (is_null($resultPageSet)) {
+ $selectFields = array (
+ 'page_namespace',
+ 'page_title',
+ 'page_id'
+ );
+ } else {
+ $selectFields = $resultPageSet->getPageTableFields();
+ }
+ $this->addFields($selectFields);
$forceNameTitleIndex = true;
if (isset ($params['minsize'])) {
$this->addWhere('page_len>=' . intval($params['minsize']));
@@ -79,15 +89,20 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
}
// Page protection filtering
- if (isset ($params['prtype'])) {
+ if (!empty ($params['prtype'])) {
$this->addTables('page_restrictions');
$this->addWhere('page_id=pr_page');
$this->addWhere('pr_expiry>' . $db->addQuotes($db->timestamp()));
$this->addWhereFld('pr_type', $params['prtype']);
- $prlevel = $params['prlevel'];
- if (!is_null($prlevel) && $prlevel != '' && $prlevel != '*')
+ // Remove the empty string and '*' from the prlevel array
+ $prlevel = array_diff($params['prlevel'], array('', '*'));
+ if (!empty($prlevel))
$this->addWhereFld('pr_level', $prlevel);
+ if ($params['prfiltercascade'] == 'cascading')
+ $this->addWhereFld('pr_cascade', 1);
+ if ($params['prfiltercascade'] == 'noncascading')
+ $this->addWhereFld('pr_cascade', 0);
$this->addOption('DISTINCT');
@@ -105,20 +120,16 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
} else if($params['filterlanglinks'] == 'withlanglinks') {
$this->addTables('langlinks');
$this->addWhere('page_id=ll_from');
+ $this->addOption('STRAIGHT_JOIN');
+ // We have to GROUP BY all selected fields to stop
+ // PostgreSQL from whining
+ $this->addOption('GROUP BY', implode(', ', $selectFields));
$forceNameTitleIndex = false;
}
if ($forceNameTitleIndex)
$this->addOption('USE INDEX', 'name_title');
- if (is_null($resultPageSet)) {
- $this->addFields(array (
- 'page_id',
- 'page_namespace',
- 'page_title'
- ));
- } else {
- $this->addFields($resultPageSet->getPageTableFields());
- }
+
$limit = $params['limit'];
$this->addOption('LIMIT', $limit+1);
@@ -185,6 +196,14 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
ApiBase :: PARAM_TYPE => $wgRestrictionLevels,
ApiBase :: PARAM_ISMULTI => true
),
+ 'prfiltercascade' => array (
+ ApiBase :: PARAM_DFLT => 'all',
+ ApiBase :: PARAM_TYPE => array (
+ 'cascading',
+ 'noncascading',
+ 'all'
+ ),
+ ),
'limit' => array (
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
@@ -221,6 +240,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
'maxsize' => 'Limit to pages with at most this many bytes',
'prtype' => 'Limit to protected pages only',
'prlevel' => 'The protection level (must be used with apprtype= parameter)',
+ 'prfiltercascade' => 'Filter protections based on cascadingness (ignored when apprtype isn\'t set)',
'filterlanglinks' => 'Filter based on whether a page has langlinks',
'limit' => 'How many total pages to return.'
);
@@ -244,6 +264,6 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryAllpages.php 37775 2008-07-17 09:26:01Z brion $';
+ return __CLASS__ . ': $Id: ApiQueryAllpages.php 44863 2008-12-20 23:54:04Z catrope $';
}
}
diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php
index fea058f3..f67e0044 100644
--- a/includes/api/ApiQueryBacklinks.php
+++ b/includes/api/ApiQueryBacklinks.php
@@ -60,7 +60,6 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
);
public function __construct($query, $moduleName) {
- $code = $prefix = $linktbl = null;
extract($this->backlinksSettings[$moduleName]);
parent :: __construct($query, $moduleName, $code);
@@ -100,7 +99,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
* AND pl_title='Foo' AND pl_namespace=0
* LIMIT 11 ORDER BY pl_from
*/
- $db = $this->getDb();
+ $db = $this->getDB();
$this->addTables(array('page', $this->bl_table));
$this->addWhere("{$this->bl_from}=page_id");
if(is_null($resultPageSet))
@@ -108,12 +107,12 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
else
$this->addFields($resultPageSet->getPageTableFields());
$this->addFields('page_is_redirect');
- $this->addWhereFld($this->bl_title, $this->rootTitle->getDbKey());
+ $this->addWhereFld($this->bl_title, $this->rootTitle->getDBKey());
if($this->hasNS)
$this->addWhereFld($this->bl_ns, $this->rootTitle->getNamespace());
$this->addWhereFld('page_namespace', $this->params['namespace']);
if(!is_null($this->contID))
- $this->addWhere("page_id>={$this->contID}");
+ $this->addWhere("{$this->bl_from}>={$this->contID}");
if($this->params['filterredir'] == 'redirects')
$this->addWhereFld('page_is_redirect', 1);
if($this->params['filterredir'] == 'nonredirects')
@@ -124,11 +123,11 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
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
+ FROM pagelinks, page WHERE pl_from=page_id
+ AND (pl_title='Foo' AND pl_namespace=0) OR (pl_title='Bar' AND pl_namespace=1)
+ ORDER BY pl_namespace, pl_title, pl_from LIMIT 11
*/
- $db = $this->getDb();
+ $db = $this->getDB();
$this->addTables(array('page', $this->bl_table));
$this->addWhere("{$this->bl_from}=page_id");
if(is_null($resultPageSet))
@@ -138,16 +137,31 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->addFields($this->bl_title);
if($this->hasNS)
$this->addFields($this->bl_ns);
- $titleWhere = '';
+ $titleWhere = array();
foreach($this->redirTitles as $t)
- $titleWhere .= ($titleWhere != '' ? " OR " : '') .
- "({$this->bl_title} = ".$db->addQuotes($t->getDBKey()).
+ $titleWhere[] = "({$this->bl_title} = ".$db->addQuotes($t->getDBKey()).
($this->hasNS ? " AND {$this->bl_ns} = '{$t->getNamespace()}'" : "") .
")";
- $this->addWhere($titleWhere);
+ $this->addWhere($db->makeList($titleWhere, LIST_OR));
$this->addWhereFld('page_namespace', $this->params['namespace']);
if(!is_null($this->redirID))
- $this->addWhere("page_id>={$this->redirID}");
+ {
+ $first = $this->redirTitles[0];
+ $title = $db->strencode($first->getDBKey());
+ $ns = $first->getNamespace();
+ $from = $this->redirID;
+ if($this->hasNS)
+ $this->addWhere("{$this->bl_ns} > $ns OR ".
+ "({$this->bl_ns} = $ns AND ".
+ "({$this->bl_title} > '$title' OR ".
+ "({$this->bl_title} = '$title' AND ".
+ "{$this->bl_from} >= $from)))");
+ else
+ $this->addWhere("{$this->bl_title} > '$title' OR ".
+ "({$this->bl_title} = '$title' AND ".
+ "{$this->bl_from} >= $from)");
+
+ }
if($this->params['filterredir'] == 'redirects')
$this->addWhereFld('page_is_redirect', 1);
if($this->params['filterredir'] == 'nonredirects')
@@ -170,7 +184,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->prepareFirstQuery($resultPageSet);
$db = $this->getDB();
- $res = $this->select(__METHOD__);
+ $res = $this->select(__METHOD__.'::firstQuery');
$count = 0;
$this->data = array ();
@@ -195,11 +209,11 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
$db->freeResult($res);
- if($this->redirect && !empty($this->redirTitles))
+ if($this->redirect && count($this->redirTitles))
{
$this->resetQueryParams();
$this->prepareSecondQuery($resultPageSet);
- $res = $this->select(__METHOD__);
+ $res = $this->select(__METHOD__.'::secondQuery');
$count = 0;
while($row = $db->fetchObject($res))
{
@@ -210,7 +224,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if($this->hasNS)
$contTitle = Title::makeTitle($row->{$this->bl_ns}, $row->{$this->bl_title});
else
- $contTitle = Title::makeTitle(NS_IMAGE, $row->{$this->bl_title});
+ $contTitle = Title::makeTitle(NS_FILE, $row->{$this->bl_title});
$this->continueStr = $this->getContinueRedirStr($contTitle->getArticleID(), $row->page_id);
break;
}
@@ -229,7 +243,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$resultData = array();
foreach($this->data as $ns => $a)
foreach($a as $title => $arr)
- $resultData[$arr['pageid']] = $arr;
+ $resultData[] = $arr;
$result = $this->getResult();
$result->setIndexedTagName($resultData, $this->bl_code);
$result->addValue('query', $this->getModuleName(), $resultData);
@@ -254,7 +268,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
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;
+ $ns = $this->hasNS ? $row->{$this->bl_ns} : NS_FILE;
$this->data[$ns][$row->{$this->bl_title}]['redirlinks'][] = $a;
$this->getResult()->setIndexedTagName($this->data[$ns][$row->{$this->bl_title}]['redirlinks'], $this->bl_code);
}
@@ -276,7 +290,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
// only image titles are allowed for the root in imageinfo mode
- if (!$this->hasNS && $this->rootTitle->getNamespace() !== NS_IMAGE)
+ if (!$this->hasNS && $this->rootTitle->getNamespace() !== NS_FILE)
$this->dieUsage("The title for {$this->getModuleName()} query must be an image", 'bad_image_title');
}
@@ -399,6 +413,6 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryBacklinks.php 37504 2008-07-10 14:28:09Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryBacklinks.php 46135 2009-01-24 13:03:40Z catrope $';
}
}
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php
index f392186b..896dd00c 100644
--- a/includes/api/ApiQueryBase.php
+++ b/includes/api/ApiQueryBase.php
@@ -126,13 +126,19 @@ abstract class ApiQueryBase extends ApiBase {
* 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)
*
+ * If $value is an empty array, this function does nothing.
+ *
* 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);
+ if (is_array($value)) {
+ // Sanity check: don't insert empty arrays,
+ // Database::makeList() chokes on them
+ if ( count( $value ) )
+ $this->where = array_merge($this->where, $value);
+ }
else
$this->where[] = $value;
}
@@ -154,10 +160,12 @@ abstract class ApiQueryBase extends ApiBase {
/**
* Equivalent to addWhere(array($field => $value))
* @param string $field Field name
- * @param string $value Value; ignored if nul;
+ * @param string $value Value; ignored if null or empty array;
*/
protected function addWhereFld($field, $value) {
- if (!is_null($value))
+ // Use count() to its full documented capabilities to simultaneously
+ // test for null, empty array or empty countable object
+ if ( count( $value ) )
$this->where[$field] = $value;
}
@@ -236,7 +244,7 @@ abstract class ApiQueryBase extends ApiBase {
/**
* Add information (title and namespace) about a Title object to a result array
- * @param array $arr Result array à la ApiResult
+ * @param array $arr Result array à la ApiResult
* @param Title $title Title object
* @param string $prefix Module prefix
*/
@@ -264,7 +272,7 @@ abstract class ApiQueryBase extends ApiBase {
/**
* 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
+ * @param array $data Data array à la ApiResult
*/
protected function addPageSubItems($pageId, $data) {
$result = $this->getResult();
@@ -324,10 +332,13 @@ abstract class ApiQueryBase extends ApiBase {
* @return string Page title with underscores
*/
public function titleToKey($title) {
+ # Don't throw an error if we got an empty string
+ if(trim($title) == '')
+ return '';
$t = Title::newFromText($title);
if(!$t)
$this->dieUsageMsg(array('invalidtitle', $title));
- return $t->getDbKey();
+ return $t->getPrefixedDbKey();
}
/**
@@ -336,19 +347,40 @@ abstract class ApiQueryBase extends ApiBase {
* @return string Page title with spaces
*/
public function keyToTitle($key) {
+ # Don't throw an error if we got an empty string
+ if(trim($key) == '')
+ return '';
$t = Title::newFromDbKey($key);
# This really shouldn't happen but we gotta check anyway
if(!$t)
$this->dieUsageMsg(array('invalidtitle', $key));
return $t->getPrefixedText();
}
+
+ /**
+ * An alternative to titleToKey() that doesn't trim trailing spaces
+ * @param string $titlePart Title part with spaces
+ * @return string Title part with underscores
+ */
+ public function titlePartToKey($titlePart) {
+ return substr($this->titleToKey($titlePart . 'x'), 0, -1);
+ }
+
+ /**
+ * An alternative to keyToTitle() that doesn't trim trailing spaces
+ * @param string $keyPart Key part with spaces
+ * @return string Key part with underscores
+ */
+ public function keyPartToTitle($keyPart) {
+ return substr($this->keyToTitle($keyPart . 'x'), 0, -1);
+ }
/**
* Get version string for use in the API help output
* @return string
*/
public static function getBaseVersion() {
- return __CLASS__ . ': $Id: ApiQueryBase.php 37083 2008-07-05 11:18:50Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryBase.php 44461 2008-12-11 19:11:11Z ialex $';
}
}
diff --git a/includes/api/ApiQueryBlocks.php b/includes/api/ApiQueryBlocks.php
index ebe87908..6f356cea 100644
--- a/includes/api/ApiQueryBlocks.php
+++ b/includes/api/ApiQueryBlocks.php
@@ -42,10 +42,6 @@ class ApiQueryBlocks extends ApiQueryBase {
}
public function execute() {
- $this->run();
- }
-
- private function run() {
global $wgUser;
$params = $this->extractRequestParams();
@@ -87,17 +83,17 @@ class ApiQueryBlocks extends ApiQueryBase {
if($fld_range)
$this->addFields(array('ipb_range_start', 'ipb_range_end'));
if($fld_flags)
- $this->addFields(array('ipb_auto', 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock', 'ipb_block_email', 'ipb_deleted'));
+ $this->addFields(array('ipb_auto', 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock', 'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk'));
$this->addOption('LIMIT', $params['limit'] + 1);
$this->addWhereRange('ipb_timestamp', $params['dir'], $params['start'], $params['end']);
if(isset($params['ids']))
- $this->addWhere(array('ipb_id' => $params['ids']));
+ $this->addWhereFld('ipb_id', $params['ids']);
if(isset($params['users']))
{
foreach((array)$params['users'] as $u)
$this->prepareUsername($u);
- $this->addWhere(array('ipb_address' => $this->usernames));
+ $this->addWhereFld('ipb_address', $this->usernames);
}
if(isset($params['ip']))
{
@@ -120,19 +116,18 @@ class ApiQueryBlocks extends ApiQueryBase {
));
}
if(!$wgUser->isAllowed('suppress'))
- $this->addWhere(array('ipb_deleted' => 0));
+ $this->addWhereFld('ipb_deleted', 0);
// Purge expired entries on one in every 10 queries
if(!mt_rand(0, 10))
Block::purgeExpired();
$res = $this->select(__METHOD__);
- $db = wfGetDB();
$count = 0;
- while($row = $db->fetchObject($res))
+ while($row = $res->fetchObject())
{
- if($count++ == $params['limit'])
+ if(++$count > $params['limit'])
{
// We've had enough
$this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ipb_timestamp));
@@ -142,13 +137,9 @@ class ApiQueryBlocks extends ApiQueryBase {
if($fld_id)
$block['id'] = $row->ipb_id;
if($fld_user && !$row->ipb_auto)
- {
$block['user'] = $row->ipb_address;
- }
if($fld_by)
- {
$block['by'] = $row->user_name;
- }
if($fld_timestamp)
$block['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ipb_timestamp);
if($fld_expiry)
@@ -157,8 +148,8 @@ class ApiQueryBlocks extends ApiQueryBase {
$block['reason'] = $row->ipb_reason;
if($fld_range)
{
- $block['rangestart'] = $this->convertHexIP($row->ipb_range_start);
- $block['rangeend'] = $this->convertHexIP($row->ipb_range_end);
+ $block['rangestart'] = IP::hexToQuad($row->ipb_range_start);
+ $block['rangeend'] = IP::hexToQuad($row->ipb_range_end);
}
if($fld_flags)
{
@@ -175,6 +166,8 @@ class ApiQueryBlocks extends ApiQueryBase {
$block['noemail'] = '';
if($row->ipb_deleted)
$block['hidden'] = '';
+ if($row->ipb_allow_usertalk)
+ $block['allowusertalk'] = '';
}
$data[] = $block;
}
@@ -194,19 +187,6 @@ class ApiQueryBlocks extends ApiQueryBase {
$this->usernames[] = $name;
}
- protected function convertHexIP($ip)
- {
- // Converts a hexadecimal IP to nnn.nnn.nnn.nnn format
- $dec = wfBaseConvert($ip, 16, 10);
- $parts[0] = (int)($dec / (256*256*256));
- $dec %= 256*256*256;
- $parts[1] = (int)($dec / (256*256));
- $dec %= 256*256;
- $parts[2] = (int)($dec / 256);
- $parts[3] = $dec % 256;
- return implode('.', $parts);
- }
-
public function getAllowedParams() {
return array (
'start' => array(
@@ -279,6 +259,6 @@ class ApiQueryBlocks extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryBlocks.php 37892 2008-07-21 21:37:11Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryBlocks.php 43676 2008-11-18 15:11:11Z catrope $';
}
}
diff --git a/includes/api/ApiQueryCategories.php b/includes/api/ApiQueryCategories.php
index 51492d63..9c4e9b41 100644
--- a/includes/api/ApiQueryCategories.php
+++ b/includes/api/ApiQueryCategories.php
@@ -54,6 +54,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
$prop = $params['prop'];
+ $show = array_flip((array)$params['show']);
$this->addFields(array (
'cl_from',
@@ -86,11 +87,31 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
$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]));
+ $clto = $this->getDB()->strencode($this->titleToKey($cont[1]));
$this->addWhere("cl_from > $clfrom OR ".
"(cl_from = $clfrom AND ".
"cl_to >= '$clto')");
}
+ if(isset($show['hidden']) && isset($show['!hidden']))
+ $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show');
+ if(isset($show['hidden']) || isset($show['!hidden']))
+ {
+ $this->addOption('STRAIGHT_JOIN');
+ $this->addTables(array('page', 'page_props'));
+ $this->addJoinConds(array(
+ 'page' => array('LEFT JOIN', array(
+ 'page_namespace' => NS_CATEGORY,
+ 'page_title = cl_to')),
+ 'page_props' => array('LEFT JOIN', array(
+ 'pp_page=page_id',
+ 'pp_propname' => 'hiddencat'))
+ ));
+ if(isset($show['hidden']))
+ $this->addWhere(array('pp_propname IS NOT NULL'));
+ else
+ $this->addWhere(array('pp_propname IS NULL'));
+ }
+
# 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');
@@ -128,7 +149,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
if ($fld_sortkey)
$vals['sortkey'] = $row->cl_sortkey;
if ($fld_timestamp)
- $vals['timestamp'] = $row->cl_timestamp;
+ $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp);
$data[] = $vals;
}
@@ -166,6 +187,13 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
'timestamp',
)
),
+ 'show' => array(
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array(
+ 'hidden',
+ '!hidden',
+ )
+ ),
'limit' => array(
ApiBase :: PARAM_DFLT => 10,
ApiBase :: PARAM_TYPE => 'limit',
@@ -181,6 +209,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
return array (
'prop' => 'Which additional properties to get for each category.',
'limit' => 'How many categories to return',
+ 'show' => 'Which kind of categories to show',
'continue' => 'When more results are available, use this to continue',
);
}
@@ -199,6 +228,6 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryCategories.php 37909 2008-07-22 13:26:15Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryCategories.php 44585 2008-12-14 17:39:50Z catrope $';
}
}
diff --git a/includes/api/ApiQueryCategoryInfo.php b/includes/api/ApiQueryCategoryInfo.php
index f809bb15..f83c4a5b 100644
--- a/includes/api/ApiQueryCategoryInfo.php
+++ b/includes/api/ApiQueryCategoryInfo.php
@@ -41,9 +41,10 @@ class ApiQueryCategoryInfo extends ApiQueryBase {
public function execute() {
$alltitles = $this->getPageSet()->getAllTitlesByNamespace();
- $categories = $alltitles[NS_CATEGORY];
- if(empty($categories))
+ if ( empty( $alltitles[NS_CATEGORY] ) ) {
return;
+ }
+ $categories = $alltitles[NS_CATEGORY];
$titles = $this->getPageSet()->getGoodTitles() +
$this->getPageSet()->getMissingTitles();
@@ -51,11 +52,19 @@ class ApiQueryCategoryInfo extends ApiQueryBase {
foreach($categories as $c)
{
$t = $titles[$c];
- $cattitles[$c] = $t->getDbKey();
+ $cattitles[$c] = $t->getDBKey();
}
- $this->addTables('category');
- $this->addFields(array('cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'cat_hidden'));
+ $this->addTables(array('category', 'page', 'page_props'));
+ $this->addJoinConds(array(
+ 'page' => array('LEFT JOIN', array(
+ 'page_namespace' => NS_CATEGORY,
+ 'page_title=cat_title')),
+ 'page_props' => array('LEFT JOIN', array(
+ 'pp_page=page_id',
+ 'pp_propname' => 'hiddencat')),
+ ));
+ $this->addFields(array('cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'pp_propname AS cat_hidden'));
$this->addWhere(array('cat_title' => $cattitles));
$db = $this->getDB();
@@ -86,6 +95,6 @@ class ApiQueryCategoryInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryCategoryInfo.php 37504 2008-07-10 14:28:09Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryCategoryInfo.php 44590 2008-12-14 20:24:23Z catrope $';
}
}
diff --git a/includes/api/ApiQueryCategoryMembers.php b/includes/api/ApiQueryCategoryMembers.php
index 3909b213..e2f577a2 100644
--- a/includes/api/ApiQueryCategoryMembers.php
+++ b/includes/api/ApiQueryCategoryMembers.php
@@ -76,17 +76,9 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
$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');
- // 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' . $dir . ', cl_from' . $dir);
- }
$this->addWhere('cl_from=page_id');
$this->setContinuation($params['continue'], $params['dir']);
@@ -94,6 +86,11 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
$this->addWhereFld('page_namespace', $params['namespace']);
if($params['sort'] == 'timestamp')
$this->addWhereRange('cl_timestamp', ($params['dir'] == 'asc' ? 'newer' : 'older'), $params['start'], $params['end']);
+ else
+ {
+ $this->addWhereRange('cl_sortkey', ($params['dir'] == 'asc' ? 'newer' : 'older'), $params['startsortkey'], $params['endsortkey']);
+ $this->addWhereRange('cl_from', ($params['dir'] == 'asc' ? 'newer' : 'older'), null, null);
+ }
$limit = $params['limit'];
$this->addOption('LIMIT', $limit +1);
@@ -157,18 +154,15 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
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);
- }
+ $pos = strrpos($continue, '|');
+ $sortkey = substr($continue, 0, $pos);
+ $fromstr = substr($continue, $pos + 1);
+ $from = intval($fromstr);
- if ($hasError)
+ if ($from == 0 && strlen($fromstr) > 0)
$this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "badcontinue");
- $encSortKey = $this->getDB()->addQuotes($continueList[0]);
+ $encSortKey = $this->getDB()->addQuotes($sortkey);
$encFrom = $this->getDB()->addQuotes($from);
$op = ($dir == 'desc' ? '<' : '>');
@@ -225,7 +219,9 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
),
'end' => array(
ApiBase :: PARAM_TYPE => 'timestamp'
- )
+ ),
+ 'startsortkey' => null,
+ 'endsortkey' => null,
);
}
@@ -238,6 +234,8 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
'dir' => 'In which direction to sort',
'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',
+ 'startsortkey' => 'Sortkey to start listing from. Can only be used with cmsort=sortkey',
+ 'endsortkey' => 'Sortkey to end listing at. Can only be used with cmsort=sortkey',
'continue' => 'For large categories, give the value retured from previous query',
'limit' => 'The maximum number of pages to return.',
);
@@ -257,6 +255,6 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 42197 2008-10-18 10:09:19Z ialex $';
}
}
diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php
index 8368896d..408421c4 100644
--- a/includes/api/ApiQueryDeletedrevs.php
+++ b/includes/api/ApiQueryDeletedrevs.php
@@ -107,6 +107,8 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$lb = new LinkBatch($titles);
$where = $lb->constructSet('ar', $db);
$this->addWhere($where);
+ } else {
+ $this->dieUsage('You have to specify a page title or titles');
}
$this->addOption('LIMIT', $limit + 1);
@@ -228,6 +230,6 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 37502 2008-07-10 14:13:11Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 40798 2008-09-13 20:41:58Z aaron $';
}
}
diff --git a/includes/api/ApiQueryDisabled.php b/includes/api/ApiQueryDisabled.php
new file mode 100644
index 00000000..50825464
--- /dev/null
+++ b/includes/api/ApiQueryDisabled.php
@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * Created on Sep 25, 2008
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2008 Roan Kattouw <Firstname>.<Lastname>@home.nl
+ *
+ * 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");
+}
+
+
+/**
+ * API module that does nothing
+ *
+ * Use this to disable core modules with e.g.
+ * $wgAPIPropModules['modulename'] = 'ApiQueryDisabled';
+ *
+ * To disable top-level modules, use ApiDisabled instead
+ *
+ * @ingroup API
+ */
+class ApiQueryDisabled extends ApiQueryBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ public function execute() {
+ $this->setWarning("The ``{$this->getModuleName()}'' module has been disabled.");
+ }
+
+ public function getAllowedParams() {
+ return array ();
+ }
+
+ public function getParamDescription() {
+ return array ();
+ }
+
+ public function getDescription() {
+ return array(
+ 'This module has been disabled.'
+ );
+ }
+
+ protected function getExamples() {
+ return array ();
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryDisabled.php 41268 2008-09-25 20:50:50Z catrope $';
+ }
+}
diff --git a/includes/api/ApiQueryDuplicateFiles.php b/includes/api/ApiQueryDuplicateFiles.php
new file mode 100644
index 00000000..5f7d7ee0
--- /dev/null
+++ b/includes/api/ApiQueryDuplicateFiles.php
@@ -0,0 +1,164 @@
+<?php
+
+/*
+ * Created on Sep 27, 2008
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2008 Roan Kattow <Firstname>,<Lastname>@home.nl
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+if (!defined('MEDIAWIKI')) {
+ // Eclipse helper - will be ignored in production
+ require_once ("ApiQueryBase.php");
+}
+
+/**
+ * A query module to list duplicates of the given file(s)
+ *
+ * @ingroup API
+ */
+class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'df');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+ $params = $this->extractRequestParams();
+ $namespaces = $this->getPageSet()->getAllTitlesByNamespace();
+ if ( empty( $namespaces[NS_FILE] ) ) {
+ return;
+ }
+ $images = $namespaces[NS_FILE];
+
+ $this->addTables('image', 'i1');
+ $this->addTables('image', 'i2');
+ $this->addFields(array(
+ 'i1.img_name AS orig_name',
+ 'i2.img_name AS dup_name',
+ 'i2.img_user_text AS dup_user_text',
+ 'i2.img_timestamp AS dup_timestamp'
+ ));
+ $this->addWhere(array(
+ 'i1.img_name' => array_keys($images),
+ 'i1.img_sha1 = i2.img_sha1',
+ 'i1.img_name != i2.img_name',
+ ));
+ if(isset($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");
+ $orig = $this->getDB()->strencode($this->titleTokey($cont[0]));
+ $dup = $this->getDB()->strencode($this->titleToKey($cont[1]));
+ $this->addWhere("i1.img_name > '$orig' OR ".
+ "(i1.img_name = '$orig' AND ".
+ "i2.img_name >= '$dup')");
+ }
+ $this->addOption('ORDER BY', 'i1.img_name');
+ $this->addOption('LIMIT', $params['limit'] + 1);
+
+ $res = $this->select(__METHOD__);
+ $db = $this->getDB();
+ $count = 0;
+ $data = array();
+ $titles = array();
+ $lastName = '';
+ 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',
+ $this->keyToTitle($row->orig_name) . '|' .
+ $this->keyToTitle($row->dup_name));
+ break;
+ }
+ if(!is_null($resultPageSet))
+ $titles[] = Title::makeTitle(NS_FILE, $row->dup_name);
+ else
+ {
+ if($row->orig_name != $lastName)
+ {
+ if($lastName != '')
+ {
+ $this->addPageSubItems($images[$lastName], $data);
+ $data = array();
+ }
+ $lastName = $row->orig_name;
+ }
+
+ $data[] = array(
+ 'name' => $row->dup_name,
+ 'user' => $row->dup_user_text,
+ 'timestamp' => wfTimestamp(TS_ISO_8601, $row->dup_timestamp)
+ );
+ }
+ }
+ if(!is_null($resultPageSet))
+ $resultPageSet->populateFromTitles($titles);
+ else if($lastName != '')
+ $this->addPageSubItems($images[$lastName], $data);
+ $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 files to return',
+ 'continue' => 'When more results are available, use this to continue',
+ );
+ }
+
+ public function getDescription() {
+ return 'List all files that are duplicates of the given file(s).';
+ }
+
+ protected function getExamples() {
+ return array ( 'api.php?action=query&titles=Image:Albert_Einstein_Head.jpg&prop=duplicatefiles',
+ 'api.php?action=query&generator=allimages&prop=duplicatefiles',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryDuplicateFiles.php 44121 2008-12-01 17:14:30Z vyznev $';
+ }
+}
diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php
index 8ffb7246..85e21f42 100644
--- a/includes/api/ApiQueryExtLinksUsage.php
+++ b/includes/api/ApiQueryExtLinksUsage.php
@@ -54,7 +54,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
// Find the right prefix
global $wgUrlProtocols;
- if(!is_null($protocol) && !empty($protocol) && !in_array($protocol, $wgUrlProtocols))
+ if($protocol && !in_array($protocol, $wgUrlProtocols))
{
foreach ($wgUrlProtocols as $p) {
if( substr( $p, 0, strlen( $protocol ) ) === $protocol ) {
@@ -66,7 +66,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
else
$protocol = null;
- $db = $this->getDb();
+ $db = $this->getDB();
$this->addTables(array('page','externallinks')); // must be in this order for 'USE INDEX'
$this->addOption('USE INDEX', 'el_index');
$this->addWhere('page_id=el_from');
@@ -206,6 +206,6 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 37909 2008-07-22 13:26:15Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 43271 2008-11-06 22:38:42Z siebrand $';
}
}
diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php
index 33ff1d3f..612d5cc9 100644
--- a/includes/api/ApiQueryImageInfo.php
+++ b/includes/api/ApiQueryImageInfo.php
@@ -56,10 +56,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
$pageIds = $this->getPageSet()->getAllTitlesByNamespace();
- if (!empty($pageIds[NS_IMAGE])) {
+ if (!empty($pageIds[NS_FILE])) {
$result = $this->getResult();
- $images = RepoGroup::singleton()->findFiles( array_keys( $pageIds[NS_IMAGE] ) );
+ $images = RepoGroup::singleton()->findFiles( array_keys( $pageIds[NS_FILE] ) );
foreach ( $images as $img ) {
$data = array();
@@ -78,14 +78,14 @@ class ApiQueryImageInfo extends ApiQueryBase {
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)
+ if(count($pageIds[NS_FILE]) == 1)
$this->setContinueEnumParameter('start', $oldie->getTimestamp());
break;
}
$data[] = self::getInfo( $oldie, $prop, $result );
}
- $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ];
+ $pageId = $pageIds[NS_FILE][ $img->getOriginalTitle()->getDBkey() ];
$result->addValue(
array( 'query', 'pages', intval( $pageId ) ),
'imagerepository', $img->getRepoName()
@@ -93,10 +93,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
$this->addPageSubItems($pageId, $data);
}
- $missing = array_diff( array_keys( $pageIds[NS_IMAGE] ), array_keys( $images ) );
+ $missing = array_diff( array_keys( $pageIds[NS_FILE] ), array_keys( $images ) );
foreach ( $missing as $title )
$result->addValue(
- array( 'query', 'pages', intval( $pageIds[NS_IMAGE][$title] ) ),
+ array( 'query', 'pages', intval( $pageIds[NS_FILE][$title] ) ),
'imagerepository', ''
);
}
@@ -123,12 +123,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
if( isset( $prop['url'] ) ) {
if( !is_null( $scale ) && !$file->isOld() ) {
- $thumb = $file->getThumbnail( $scale['width'], $scale['height'] );
- if( $thumb )
+ $mto = $file->transform( array( 'width' => $scale['width'], 'height' => $scale['height'] ) );
+ if( $mto && !$mto->isError() )
{
- $vals['thumburl'] = wfExpandUrl( $thumb->getURL() );
- $vals['thumbwidth'] = $thumb->getWidth();
- $vals['thumbheight'] = $thumb->getHeight();
+ $vals['thumburl'] = $mto->getUrl();
+ $vals['thumbwidth'] = $mto->getWidth();
+ $vals['thumbheight'] = $mto->getHeight();
}
}
$vals['url'] = $file->getFullURL();
@@ -148,6 +148,9 @@ class ApiQueryImageInfo extends ApiQueryBase {
if( isset( $prop['archivename'] ) && $file->isOld() )
$vals['archivename'] = $file->getArchiveName();
+
+ if( isset( $prop['bitdepth'] ) )
+ $vals['bitdepth'] = $file->getBitDepth();
return $vals;
}
@@ -166,7 +169,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
'sha1',
'mime',
'metadata',
- 'archivename'
+ 'archivename',
+ 'bitdepth',
)
),
'limit' => array(
@@ -219,6 +223,6 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryImageInfo.php 37504 2008-07-10 14:28:09Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryImageInfo.php 44121 2008-12-01 17:14:30Z vyznev $';
}
}
diff --git a/includes/api/ApiQueryImages.php b/includes/api/ApiQueryImages.php
index 32c4e1b0..02fe24f1 100644
--- a/includes/api/ApiQueryImages.php
+++ b/includes/api/ApiQueryImages.php
@@ -66,7 +66,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
$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]));
+ $ilto = $this->getDB()->strencode($this->titleToKey($cont[1]));
$this->addWhere("il_from > $ilfrom OR ".
"(il_from = $ilfrom AND ".
"il_to >= '$ilto')");
@@ -103,7 +103,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
}
$vals = array();
- ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle(NS_IMAGE, $row->il_to));
+ ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle(NS_FILE, $row->il_to));
$data[] = $vals;
}
@@ -123,7 +123,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
'|' . $this->keyToTitle($row->il_to));
break;
}
- $titles[] = Title :: makeTitle(NS_IMAGE, $row->il_to);
+ $titles[] = Title :: makeTitle(NS_FILE, $row->il_to);
}
$resultPageSet->populateFromTitles($titles);
}
@@ -165,6 +165,6 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryImages.php 37535 2008-07-10 21:20:43Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryImages.php 44121 2008-12-01 17:14:30Z vyznev $';
}
}
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index 9c6487b3..0c5c72fc 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -68,7 +68,8 @@ class ApiQueryInfo extends ApiQueryBase {
'protect' => array( 'ApiQueryInfo', 'getProtectToken' ),
'move' => array( 'ApiQueryInfo', 'getMoveToken' ),
'block' => array( 'ApiQueryInfo', 'getBlockToken' ),
- 'unblock' => array( 'ApiQueryInfo', 'getUnblockToken' )
+ 'unblock' => array( 'ApiQueryInfo', 'getUnblockToken' ),
+ 'email' => array( 'ApiQueryInfo', 'getEmailToken' ),
);
wfRunHooks('APIQueryInfoTokens', array(&$this->tokenFunctions));
return $this->tokenFunctions;
@@ -153,17 +154,33 @@ class ApiQueryInfo extends ApiQueryBase {
return self::getBlockToken($pageid, $title);
}
+ public static function getEmailToken($pageid, $title)
+ {
+ global $wgUser;
+ if(!$wgUser->canSendEmail() || $wgUser->isBlockedFromEmailUser())
+ return false;
+
+ static $cachedEmailToken = null;
+ if(!is_null($cachedEmailToken))
+ return $cachedEmailToken;
+
+ $cachedEmailToken = $wgUser->editToken();
+ return $cachedEmailToken;
+ }
+
public function execute() {
global $wgUser;
$params = $this->extractRequestParams();
- $fld_protection = $fld_talkid = $fld_subjectid = false;
+ $fld_protection = $fld_talkid = $fld_subjectid = $fld_url = $fld_readable = 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']);
+ $fld_url = isset($prop['url']);
+ $fld_readable = isset($prop['readable']);
}
$pageSet = $this->getPageSet();
@@ -180,7 +197,7 @@ class ApiQueryInfo extends ApiQueryBase {
$pageLength = $pageSet->getCustomField('page_len');
$db = $this->getDB();
- if ($fld_protection && !empty($titles)) {
+ if ($fld_protection && count($titles)) {
$this->addTables('page_restrictions');
$this->addFields(array('pr_page', 'pr_type', 'pr_level', 'pr_expiry', 'pr_cascade'));
$this->addWhereFld('pr_page', array_keys($titles));
@@ -195,12 +212,44 @@ class ApiQueryInfo extends ApiQueryBase {
if($row->pr_cascade)
$a['cascade'] = '';
$protections[$row->pr_page][] = $a;
+
+ # Also check old restrictions
+ if($pageRestrictions[$row->pr_page]) {
+ foreach(explode(':', trim($pageRestrictions[$pageid])) as $restrict) {
+ $temp = explode('=', trim($restrict));
+ if(count($temp) == 1) {
+ // old old format should be treated as edit/move restriction
+ $restriction = trim( $temp[0] );
+ if($restriction == '')
+ continue;
+ $protections[$row->pr_page][] = array(
+ 'type' => 'edit',
+ 'level' => $restriction,
+ 'expiry' => 'infinity',
+ );
+ $protections[$row->pr_page][] = array(
+ 'type' => 'move',
+ 'level' => $restriction,
+ 'expiry' => 'infinity',
+ );
+ } else {
+ $restriction = trim( $temp[1] );
+ if($restriction == '')
+ continue;
+ $protections[$row->pr_page][] = array(
+ 'type' => $temp[0],
+ 'level' => $restriction,
+ 'expiry' => 'infinity',
+ );
+ }
+ }
+ }
}
$db->freeResult($res);
$imageIds = array();
foreach ($titles as $id => $title)
- if ($title->getNamespace() == NS_IMAGE)
+ if ($title->getNamespace() == NS_FILE)
$imageIds[] = $id;
// To avoid code duplication
$cascadeTypes = array(
@@ -214,7 +263,7 @@ class ApiQueryInfo extends ApiQueryBase {
array(
'prefix' => 'il',
'table' => 'imagelinks',
- 'ns' => NS_IMAGE,
+ 'ns' => NS_FILE,
'title' => 'il_to',
'ids' => $imageIds
)
@@ -256,7 +305,7 @@ class ApiQueryInfo extends ApiQueryBase {
}
// We don't need to check for pt stuff if there are no nonexistent titles
- if($fld_protection && !empty($missing))
+ if($fld_protection && count($missing))
{
$this->resetQueryParams();
// Construct a custom WHERE clause that matches all titles in $missing
@@ -278,8 +327,8 @@ class ApiQueryInfo extends ApiQueryBase {
$images = array();
$others = array();
foreach ($missing as $title)
- if ($title->getNamespace() == NS_IMAGE)
- $images[] = $title->getDbKey();
+ if ($title->getNamespace() == NS_FILE)
+ $images[] = $title->getDBKey();
else
$others[] = $title;
@@ -328,7 +377,7 @@ class ApiQueryInfo extends ApiQueryBase {
'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ),
'source' => $source->getPrefixedText()
);
- $prottitles[NS_IMAGE][$row->il_to][] = $a;
+ $prottitles[NS_FILE][$row->il_to][] = $a;
}
$db->freeResult($res);
}
@@ -350,7 +399,7 @@ class ApiQueryInfo extends ApiQueryBase {
else if($fld_talkid)
$talktitles[] = $t->getTalkPage();
}
- if(!empty($talktitles) || !empty($subjecttitles))
+ if(count($talktitles) || count($subjecttitles))
{
// Construct a custom WHERE clause that matches
// all titles in $talktitles and $subjecttitles
@@ -386,6 +435,7 @@ class ApiQueryInfo extends ApiQueryBase {
if (!is_null($params['token'])) {
$tokenFunctions = $this->getTokenFunctions();
+ $pageInfo['starttimestamp'] = wfTimestamp(TS_ISO_8601, time());
foreach($params['token'] as $t)
{
$val = call_user_func($tokenFunctions[$t], $pageid, $title);
@@ -397,46 +447,23 @@ class ApiQueryInfo extends ApiQueryBase {
}
if($fld_protection) {
+ $pageInfo['protection'] = array();
if (isset($protections[$pageid])) {
$pageInfo['protection'] = $protections[$pageid];
$result->setIndexedTagName($pageInfo['protection'], 'pr');
- } else {
- # Also check old restrictions
- if( $pageRestrictions[$pageid] ) {
- foreach( explode( ':', trim( $pageRestrictions[$pageid] ) ) as $restrict ) {
- $temp = explode( '=', trim( $restrict ) );
- if(count($temp) == 1) {
- // old old format should be treated as edit/move restriction
- $restriction = trim( $temp[0] );
- $pageInfo['protection'][] = array(
- 'type' => 'edit',
- 'level' => $restriction,
- 'expiry' => 'infinity',
- );
- $pageInfo['protection'][] = array(
- 'type' => 'move',
- 'level' => $restriction,
- 'expiry' => 'infinity',
- );
- } else {
- $restriction = trim( $temp[1] );
- $pageInfo['protection'][] = array(
- 'type' => $temp[0],
- 'level' => $restriction,
- 'expiry' => 'infinity',
- );
- }
- }
- $result->setIndexedTagName($pageInfo['protection'], 'pr');
- } else {
- $pageInfo['protection'] = array();
- }
}
}
- 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()];
+ 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()];
+ if($fld_url) {
+ $pageInfo['fullurl'] = $title->getFullURL();
+ $pageInfo['editurl'] = $title->getFullURL('action=edit');
+ }
+ if($fld_readable)
+ if($title->userCanRead())
+ $pageInfo['readable'] = '';
$result->addValue(array (
'query',
@@ -444,19 +471,22 @@ class ApiQueryInfo extends ApiQueryBase {
), $pageid, $pageInfo);
}
- // Get edit/protect tokens and protection data for missing titles if requested
- // Delete and move tokens are N/A for missing titles anyway
- if(!is_null($params['token']) || $fld_protection || $fld_talkid || $fld_subjectid)
+ // Get properties for missing titles if requested
+ if(!is_null($params['token']) || $fld_protection || $fld_talkid || $fld_subjectid ||
+ $fld_url || $fld_readable)
{
$res = &$result->getData();
foreach($missing as $pageid => $title) {
if(!is_null($params['token']))
{
$tokenFunctions = $this->getTokenFunctions();
+ $res['query']['pages'][$pageid]['starttimestamp'] = wfTimestamp(TS_ISO_8601, time());
foreach($params['token'] as $t)
{
$val = call_user_func($tokenFunctions[$t], $pageid, $title);
- if($val !== false)
+ if($val === false)
+ $this->setWarning("Action '$t' is not allowed for the current user");
+ else
$res['query']['pages'][$pageid][$t . 'token'] = $val;
}
}
@@ -470,10 +500,17 @@ class ApiQueryInfo extends ApiQueryBase {
$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()];
+ 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()];
+ if($fld_url) {
+ $res['query']['pages'][$pageid]['fullurl'] = $title->getFullURL();
+ $res['query']['pages'][$pageid]['editurl'] = $title->getFullURL('action=edit');
+ }
+ if($fld_readable)
+ if($title->userCanRead())
+ $res['query']['pages'][$pageid]['readable'] = '';
}
}
}
@@ -486,7 +523,9 @@ class ApiQueryInfo extends ApiQueryBase {
ApiBase :: PARAM_TYPE => array (
'protection',
'talkid',
- 'subjectid'
+ 'subjectid',
+ 'url',
+ 'readable',
)),
'token' => array (
ApiBase :: PARAM_DFLT => NULL,
@@ -521,6 +560,6 @@ class ApiQueryInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryInfo.php 37191 2008-07-06 18:43:06Z brion $';
+ return __CLASS__ . ': $Id: ApiQueryInfo.php 45683 2009-01-12 19:10:42Z raymond $';
}
}
diff --git a/includes/api/ApiQueryLangLinks.php b/includes/api/ApiQueryLangLinks.php
index e7d84fc3..8eaf8d02 100644
--- a/includes/api/ApiQueryLangLinks.php
+++ b/includes/api/ApiQueryLangLinks.php
@@ -58,7 +58,7 @@ class ApiQueryLangLinks extends ApiQueryBase {
$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]);
+ $lllang = $this->getDB()->strencode($cont[1]);
$this->addWhere("ll_from > $llfrom OR ".
"(ll_from = $llfrom AND ".
"ll_lang >= '$lllang')");
@@ -134,6 +134,6 @@ class ApiQueryLangLinks extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLangLinks.php 37534 2008-07-10 21:08:37Z brion $';
+ return __CLASS__ . ': $Id: ApiQueryLangLinks.php 43271 2008-11-06 22:38:42Z siebrand $';
}
}
diff --git a/includes/api/ApiQueryLinks.php b/includes/api/ApiQueryLinks.php
index 546a599d..91b5b529 100644
--- a/includes/api/ApiQueryLinks.php
+++ b/includes/api/ApiQueryLinks.php
@@ -76,9 +76,9 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
$this->addFields(array (
- $this->prefix . '_from pl_from',
- $this->prefix . '_namespace pl_namespace',
- $this->prefix . '_title pl_title'
+ $this->prefix . '_from AS pl_from',
+ $this->prefix . '_namespace AS pl_namespace',
+ $this->prefix . '_title AS pl_title'
));
$this->addTables($this->table);
@@ -92,7 +92,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
"original value returned by the previous query", "_badcontinue");
$plfrom = intval($cont[0]);
$plns = intval($cont[1]);
- $pltitle = $this->getDb()->strencode($this->titleToKey($cont[2]));
+ $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 ".
@@ -213,6 +213,6 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLinks.php 37909 2008-07-22 13:26:15Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryLinks.php 43271 2008-11-06 22:38:42Z siebrand $';
}
}
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
index 47a526bb..83c73b83 100644
--- a/includes/api/ApiQueryLogEvents.php
+++ b/includes/api/ApiQueryLogEvents.php
@@ -93,16 +93,15 @@ class ApiQueryLogEvents extends ApiQueryBase {
$limit = $params['limit'];
$this->addOption('LIMIT', $limit +1);
-
+
+ $index = false;
$user = $params['user'];
if (!is_null($user)) {
- $userid = $db->selectField('user', 'user_id', array (
- 'user_name' => $user
- ));
+ $userid = User::idFromName($user);
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')));
+ $index = 'user_time';
}
$title = $params['title'];
@@ -112,8 +111,14 @@ 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')));
+
+ // Use the title index in preference to the user index if there is a conflict
+ $index = 'page_time';
}
+ if ( $index ) {
+ $this->addOption( 'USE INDEX', array( 'logging' => $index ) );
+ }
+
$data = array ();
$count = 0;
@@ -134,6 +139,48 @@ class ApiQueryLogEvents extends ApiQueryBase {
$this->getResult()->setIndexedTagName($data, 'item');
$this->getResult()->addValue('query', $this->getModuleName(), $data);
}
+
+ public static function addLogParams($result, &$vals, $params, $type, $ts) {
+ $params = explode("\n", $params);
+ switch ($type) {
+ case 'move':
+ if (isset ($params[0])) {
+ $title = Title :: newFromText($params[0]);
+ if ($title) {
+ $vals2 = array();
+ ApiQueryBase :: addTitleInfo($vals2, $title, "new_");
+ $vals[$type] = $vals2;
+ $params = null;
+ }
+ }
+ break;
+ case 'patrol':
+ $vals2 = array();
+ list( $vals2['cur'], $vals2['prev'], $vals2['auto'] ) = $params;
+ $vals[$type] = $vals2;
+ $params = null;
+ break;
+ case 'rights':
+ $vals2 = array();
+ list( $vals2['old'], $vals2['new'] ) = $params;
+ $vals[$type] = $vals2;
+ $params = null;
+ break;
+ case 'block':
+ $vals2 = array();
+ list( $vals2['duration'], $vals2['flags'] ) = $params;
+ $vals2['expiry'] = wfTimestamp(TS_ISO_8601,
+ strtotime($params[0], wfTimestamp(TS_UNIX, $ts)));
+ $vals[$type] = $vals2;
+ $params = null;
+ break;
+ }
+ if (!is_null($params)) {
+ $result->setIndexedTagName($params, 'param');
+ $vals = array_merge($vals, $params);
+ }
+ return $vals;
+ }
private function extractRowInfo($row) {
$vals = array();
@@ -154,43 +201,9 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
if ($this->fld_details && $row->log_params !== '') {
- $params = explode("\n", $row->log_params);
- switch ($row->log_type) {
- case 'move':
- if (isset ($params[0])) {
- $title = Title :: newFromText($params[0]);
- if ($title) {
- $vals2 = array();
- ApiQueryBase :: addTitleInfo($vals2, $title, "new_");
- $vals[$row->log_type] = $vals2;
- $params = null;
- }
- }
- break;
- case 'patrol':
- $vals2 = array();
- list( $vals2['cur'], $vals2['prev'], $vals2['auto'] ) = $params;
- $vals[$row->log_type] = $vals2;
- $params = null;
- break;
- case 'rights':
- $vals2 = array();
- list( $vals2['old'], $vals2['new'] ) = $params;
- $vals[$row->log_type] = $vals2;
- $params = null;
- break;
- case 'block':
- $vals2 = array();
- list( $vals2['duration'], $vals2['flags'] ) = $params;
- $vals[$row->log_type] = $vals2;
- $params = null;
- break;
- }
-
- if (isset($params)) {
- $this->getResult()->setIndexedTagName($params, 'param');
- $vals = array_merge($vals, $params);
- }
+ self::addLogParams($this->getResult(), $vals,
+ $row->log_params, $row->log_type,
+ $row->log_timestamp);
}
if ($this->fld_user) {
@@ -201,7 +214,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
if ($this->fld_timestamp) {
$vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->log_timestamp);
}
- if ($this->fld_comment && !empty ($row->log_comment)) {
+ if ($this->fld_comment && isset($row->log_comment)) {
$vals['comment'] = $row->log_comment;
}
@@ -277,6 +290,6 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryLogEvents.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiQueryLogEvents.php 44234 2008-12-04 15:59:26Z catrope $';
}
}
diff --git a/includes/api/ApiQueryRandom.php b/includes/api/ApiQueryRandom.php
index 046157a6..e7b8bf46 100644
--- a/includes/api/ApiQueryRandom.php
+++ b/includes/api/ApiQueryRandom.php
@@ -48,13 +48,13 @@ if (!defined('MEDIAWIKI')) {
$this->run($resultPageSet);
}
- protected function prepareQuery($randstr, $limit, $namespace, &$resultPageSet) {
+ protected function prepareQuery($randstr, $limit, $namespace, &$resultPageSet, $redirect) {
$this->resetQueryParams();
$this->addTables('page');
$this->addOption('LIMIT', $limit);
$this->addWhereFld('page_namespace', $namespace);
$this->addWhereRange('page_random', 'newer', $randstr, null);
- $this->addWhere(array('page_is_redirect' => 0));
+ $this->addWhereFld('page_is_redirect', $redirect);
$this->addOption('USE INDEX', 'page_random');
if(is_null($resultPageSet))
$this->addFields(array('page_id', 'page_title', 'page_namespace'));
@@ -89,7 +89,8 @@ if (!defined('MEDIAWIKI')) {
$result = $this->getResult();
$data = array();
$this->pageIDs = array();
- $this->prepareQuery(wfRandom(), $params['limit'], $params['namespace'], $resultPageSet);
+
+ $this->prepareQuery(wfRandom(), $params['limit'], $params['namespace'], $resultPageSet, $params['redirect']);
$count = $this->runQuery($data, $resultPageSet);
if($count < $params['limit'])
{
@@ -97,7 +98,7 @@ if (!defined('MEDIAWIKI')) {
* for page_random. We'll just take the lowest ones, see
* also the comment in Title::getRandomTitle()
*/
- $this->prepareQuery(0, $params['limit'] - $count, $params['namespace'], $resultPageSet);
+ $this->prepareQuery(0, $params['limit'] - $count, $params['namespace'], $resultPageSet, $params['redirect']);
$this->runQuery($data, $resultPageSet);
}
@@ -129,13 +130,15 @@ if (!defined('MEDIAWIKI')) {
ApiBase :: PARAM_MAX => 10,
ApiBase :: PARAM_MAX2 => 20
),
+ 'redirect' => false,
);
}
public function getParamDescription() {
return array (
'namespace' => 'Return pages in these namespaces only',
- 'limit' => 'Limit how many random pages will be returned'
+ 'limit' => 'Limit how many random pages will be returned',
+ 'redirect' => 'Load a random redirect instead of a random page'
);
}
diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php
index 2b8c6a92..04eb910f 100644
--- a/includes/api/ApiQueryRecentChanges.php
+++ b/includes/api/ApiQueryRecentChanges.php
@@ -43,16 +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;
+
+ 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(
+ 'patrol' => array( 'ApiQueryRecentChanges', 'getPatrolToken' )
+ );
+ wfRunHooks('APIQueryRecentChangesTokens', array(&$this->tokenFunctions));
+ return $this->tokenFunctions;
+ }
+
+ public static function getPatrolToken($pageid, $title, $rc)
+ {
+ global $wgUser;
+ if(!$wgUser->useRCPatrol() && !$wgUser->useNPPatrol())
+ return false;
+
+ // The patrol token is always the same, let's exploit that
+ static $cachedPatrolToken = null;
+ if(!is_null($cachedPatrolToken))
+ return $cachedPatrolToken;
+
+ $cachedPatrolToken = $wgUser->editToken();
+ return $cachedPatrolToken;
+ }
/**
* Generates and outputs the result of this query based upon the provided parameters.
*/
public function execute() {
- /* Initialize vars */
- $limit = $prop = $namespace = $titles = $show = $type = $dir = $start = $end = null;
-
/* Get the parameters of the request. */
- extract($this->extractRequestParams());
+ $params = $this->extractRequestParams();
/* Build our basic query. Namely, something along the lines of:
* SELECT * FROM recentchanges WHERE rc_timestamp > $start
@@ -62,13 +94,13 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$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->addWhereRange('rc_timestamp', $params['dir'], $params['start'], $params['end']);
+ $this->addWhereFld('rc_namespace', $params['namespace']);
$this->addWhereFld('rc_deleted', 0);
- if(!empty($titles))
+ if($params['titles'])
{
$lb = new LinkBatch;
- foreach($titles as $t)
+ foreach($params['titles'] as $t)
{
$obj = Title::newFromText($t);
$lb->addObj($obj);
@@ -77,19 +109,19 @@ class ApiQueryRecentChanges extends ApiQueryBase {
// 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;
+ $lb->data[$obj->getNamespace()][$obj->getDBKey()] = 1;
}
}
- $where = $lb->constructSet('rc', $this->getDb());
+ $where = $lb->constructSet('rc', $this->getDB());
if($where != '')
$this->addWhere($where);
}
- if(!is_null($type))
- $this->addWhereFld('rc_type', $this->parseRCType($type));
+ if(!is_null($params['type']))
+ $this->addWhereFld('rc_type', $this->parseRCType($params['type']));
- if (!is_null($show)) {
- $show = array_flip($show);
+ if (!is_null($params['show'])) {
+ $show = array_flip($params['show']);
/* Check for conflicting parameters. */
if ((isset ($show['minor']) && isset ($show['!minor']))
@@ -103,7 +135,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
// Check permissions
global $wgUser;
- if((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->isAllowed('patrol'))
+ if((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol())
$this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied');
/* Add additional conditions to query depending upon parameters. */
@@ -125,14 +157,15 @@ class ApiQueryRecentChanges extends ApiQueryBase {
'rc_timestamp',
'rc_namespace',
'rc_title',
+ 'rc_cur_id',
'rc_type',
'rc_moved_to_ns',
'rc_moved_to_title'
));
/* Determine what properties we need to display. */
- if (!is_null($prop)) {
- $prop = array_flip($prop);
+ if (!is_null($params['prop'])) {
+ $prop = array_flip($params['prop']);
/* Set up internal members based upon params. */
$this->fld_comment = isset ($prop['comment']);
@@ -144,14 +177,14 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$this->fld_sizes = isset ($prop['sizes']);
$this->fld_redirect = isset($prop['redirect']);
$this->fld_patrolled = isset($prop['patrolled']);
+ $this->fld_loginfo = isset($prop['loginfo']);
global $wgUser;
- if($this->fld_patrolled && !$wgUser->isAllowed('patrol'))
+ if($this->fld_patrolled && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol())
$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);
@@ -163,6 +196,10 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$this->addFieldsIf('rc_old_len', $this->fld_sizes);
$this->addFieldsIf('rc_new_len', $this->fld_sizes);
$this->addFieldsIf('rc_patrolled', $this->fld_patrolled);
+ $this->addFieldsIf('rc_logid', $this->fld_loginfo);
+ $this->addFieldsIf('rc_log_type', $this->fld_loginfo);
+ $this->addFieldsIf('rc_log_action', $this->fld_loginfo);
+ $this->addFieldsIf('rc_params', $this->fld_loginfo);
if($this->fld_redirect || isset($show['redirect']) || isset($show['!redirect']))
{
$this->addTables('page');
@@ -170,9 +207,8 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$this->addFields('page_is_redirect');
}
}
- /* 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);
+ $this->token = $params['token'];
+ $this->addOption('LIMIT', $params['limit'] +1);
$data = array ();
$count = 0;
@@ -183,7 +219,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
/* Iterate through the rows, adding data extracted from them to our query result. */
while ($row = $db->fetchObject($res)) {
- if (++ $count > $limit) {
+ if (++ $count > $params['limit']) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
$this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp));
break;
@@ -215,7 +251,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
private function extractRowInfo($row) {
/* If page was moved somewhere, get the title of the move target. */
$movedToTitle = false;
- if (!empty($row->rc_moved_to_title))
+ if (isset($row->rc_moved_to_title) && $row->rc_moved_to_title !== '')
$movedToTitle = Title :: makeTitle($row->rc_moved_to_ns, $row->rc_moved_to_title);
/* Determine the title of the page that has been changed. */
@@ -228,11 +264,11 @@ class ApiQueryRecentChanges extends ApiQueryBase {
/* Determine what kind of change this was. */
switch ( $type ) {
- case RC_EDIT: $vals['type'] = 'edit'; break;
- case RC_NEW: $vals['type'] = 'new'; break;
- case RC_MOVE: $vals['type'] = 'move'; break;
- case RC_LOG: $vals['type'] = 'log'; break;
- case RC_MOVE_OVER_REDIRECT: $vals['type'] = 'move over redirect'; break;
+ case RC_EDIT: $vals['type'] = 'edit'; break;
+ case RC_NEW: $vals['type'] = 'new'; break;
+ case RC_MOVE: $vals['type'] = 'move'; break;
+ case RC_LOG: $vals['type'] = 'log'; break;
+ case RC_MOVE_OVER_REDIRECT: $vals['type'] = 'move over redirect'; break;
default: $vals['type'] = $type;
}
@@ -279,7 +315,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
$vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp);
/* Add edit summary / log summary. */
- if ($this->fld_comment && !empty ($row->rc_comment)) {
+ if ($this->fld_comment && isset($row->rc_comment)) {
$vals['comment'] = $row->rc_comment;
}
@@ -290,6 +326,29 @@ class ApiQueryRecentChanges extends ApiQueryBase {
/* Add the patrolled flag */
if ($this->fld_patrolled && $row->rc_patrolled == 1)
$vals['patrolled'] = '';
+
+ if ($this->fld_loginfo && $row->rc_type == RC_LOG) {
+ $vals['logid'] = $row->rc_logid;
+ $vals['logtype'] = $row->rc_log_type;
+ $vals['logaction'] = $row->rc_log_action;
+ ApiQueryLogEvents::addLogParams($this->getResult(),
+ $vals, $row->rc_params,
+ $row->rc_log_type, $row->rc_timestamp);
+ }
+
+ if(!is_null($this->token))
+ {
+ $tokenFunctions = $this->getTokenFunctions();
+ foreach($this->token as $t)
+ {
+ $val = call_user_func($tokenFunctions[$t], $row->rc_cur_id,
+ $title, RecentChange::newFromRow($row));
+ if($val === false)
+ $this->setWarning("Action '$t' is not allowed for the current user");
+ else
+ $vals[$t . 'token'] = $val;
+ }
+ }
return $vals;
}
@@ -345,9 +404,14 @@ class ApiQueryRecentChanges extends ApiQueryBase {
'ids',
'sizes',
'redirect',
- 'patrolled'
+ 'patrolled',
+ 'loginfo',
)
),
+ 'token' => array(
+ ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()),
+ ApiBase :: PARAM_ISMULTI => true
+ ),
'show' => array (
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array (
@@ -389,6 +453,7 @@ class ApiQueryRecentChanges extends ApiQueryBase {
'namespace' => 'Filter log entries to only this namespace(s)',
'titles' => 'Filter log entries to only these page titles',
'prop' => 'Include additional pieces of information',
+ 'token' => 'Which tokens to obtain for each change',
'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'
@@ -409,6 +474,6 @@ class ApiQueryRecentChanges extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 37909 2008-07-22 13:26:15Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 44719 2008-12-17 16:34:01Z catrope $';
}
}
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php
index 1fd2d7c6..977e792b 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -58,7 +58,7 @@ class ApiQueryRevisions extends ApiQueryBase {
return array();
$this->tokenFunctions = array(
- 'rollback' => array( 'ApiQueryRevisions','getRollbackToken' )
+ 'rollback' => array( 'ApiQueryRevisions', 'getRollbackToken' )
);
wfRunHooks('APIQueryRevisionsTokens', array(&$this->tokenFunctions));
return $this->tokenFunctions;
@@ -74,14 +74,16 @@ class ApiQueryRevisions extends ApiQueryBase {
}
public function execute() {
- $limit = $startid = $endid = $start = $end = $dir = $prop = $user = $excludeuser = $expandtemplates = $section = $token = null;
- extract($this->extractRequestParams(false));
+ $params = $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
- $enumRevMode = (!is_null($user) || !is_null($excludeuser) || !is_null($limit) || !is_null($startid) || !is_null($endid) || $dir === 'newer' || !is_null($start) || !is_null($end));
+ $enumRevMode = (!is_null($params['user']) || !is_null($params['excludeuser']) ||
+ !is_null($params['limit']) || !is_null($params['startid']) ||
+ !is_null($params['endid']) || $params['dir'] === 'newer' ||
+ !is_null($params['start']) || !is_null($params['end']));
$pageSet = $this->getPageSet();
@@ -100,8 +102,10 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->addTables('revision');
$this->addFields( Revision::selectFields() );
+ $this->addTables( 'page' );
+ $this->addWhere('page_id = rev_page');
- $prop = array_flip($prop);
+ $prop = array_flip($params['prop']);
// Optional fields
$this->fld_ids = isset ($prop['ids']);
@@ -111,11 +115,9 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->fld_comment = isset ($prop['comment']);
$this->fld_size = isset ($prop['size']);
$this->fld_user = isset ($prop['user']);
- $this->token = $token;
+ $this->token = $params['token'];
- if ( !is_null($this->token) || ( $this->fld_content && $this->expandTemplates ) || $pageCount > 0) {
- $this->addTables( 'page' );
- $this->addWhere('page_id=rev_page');
+ if ( !is_null($this->token) || $pageCount > 0) {
$this->addFields( Revision::selectPageFields() );
}
@@ -136,15 +138,17 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->fld_content = true;
- $this->expandTemplates = $expandtemplates;
- if(isset($section))
- $this->section = $section;
+ $this->expandTemplates = $params['expandtemplates'];
+ $this->generateXML = $params['generatexml'];
+ if(isset($params['section']))
+ $this->section = $params['section'];
else
$this->section = false;
}
$userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
$botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
+ $limit = $params['limit'];
if( $limit == 'max' ) {
$limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
$this->getResult()->addValue( 'limits', $this->getModuleName(), $limit );
@@ -153,13 +157,13 @@ class ApiQueryRevisions extends ApiQueryBase {
if ($enumRevMode) {
// This is mostly to prevent parameter errors (and optimize SQL?)
- if (!is_null($startid) && !is_null($start))
+ if (!is_null($params['startid']) && !is_null($params['start']))
$this->dieUsage('start and startid cannot be used together', 'badparams');
- if (!is_null($endid) && !is_null($end))
+ if (!is_null($params['endid']) && !is_null($params['end']))
$this->dieUsage('end and endid cannot be used together', 'badparams');
- if(!is_null($user) && !is_null( $excludeuser))
+ if(!is_null($params['user']) && !is_null($params['excludeuser']))
$this->dieUsage('user and excludeuser cannot be used together', 'badparams');
// This code makes an assumption that sorting by rev_id and rev_timestamp produces
@@ -169,10 +173,12 @@ class ApiQueryRevisions extends ApiQueryBase {
// 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))
- $this->addWhereRange('rev_timestamp', $dir, $start, $end);
+ if (is_null($params['startid']) && is_null($params['endid']))
+ $this->addWhereRange('rev_timestamp', $params['dir'],
+ $params['start'], $params['end']);
else
- $this->addWhereRange('rev_id', $dir, $startid, $endid);
+ $this->addWhereRange('rev_id', $params['dir'],
+ $params['startid'], $params['endid']);
// must manually initialize unset limit
if (is_null($limit))
@@ -182,30 +188,38 @@ 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)) {
- $this->addWhere('rev_user_text != ' . $this->getDB()->addQuotes($excludeuser));
+ if(!is_null($params['user'])) {
+ $this->addWhereFld('rev_user_text', $params['user']);
+ } elseif (!is_null( $params['excludeuser'])) {
+ $this->addWhere('rev_user_text != ' .
+ $this->getDB()->addQuotes($params['excludeuser']));
}
}
elseif ($revCount > 0) {
- $this->validateLimit('rev_count', $revCount, 1, $userMax, $botMax);
+ $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
+ $revs = $pageSet->getRevisionIDs();
+ if(self::truncateArray($revs, $max))
+ $this->setWarning("Too many values supplied for parameter 'revids': the limit is $max");
// Get all revision IDs
- $this->addWhereFld('rev_id', array_keys($pageSet->getRevisionIDs()));
+ $this->addWhereFld('rev_id', array_keys($revs));
// assumption testing -- we should never get more then $revCount rows.
$limit = $revCount;
}
elseif ($pageCount > 0) {
+ $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
+ $titles = $pageSet->getGoodTitles();
+ if(self::truncateArray($titles, $max))
+ $this->setWarning("Too many values supplied for parameter 'titles': the limit is $max");
+
// When working in multi-page non-enumeration mode,
// limit to the latest revision only
$this->addWhere('page_id=rev_page');
$this->addWhere('page_latest=rev_id');
- $this->validateLimit('page_count', $pageCount, 1, $userMax, $botMax);
-
+
// Get all page IDs
- $this->addWhereFld('page_id', array_keys($pageSet->getGoodTitles()));
+ $this->addWhereFld('page_id', array_keys($titles));
// assumption testing -- we should never get more then $pageCount rows.
$limit = $pageCount;
@@ -281,7 +295,7 @@ class ApiQueryRevisions extends ApiQueryBase {
if ($this->fld_comment) {
$comment = $revision->getComment();
- if (!empty($comment))
+ if (strval($comment) !== '')
$vals['comment'] = $comment;
}
@@ -312,6 +326,17 @@ class ApiQueryRevisions extends ApiQueryBase {
if($text === false)
$this->dieUsage("There is no section {$this->section} in r".$revision->getId(), 'nosuchsection');
}
+ if ($this->generateXML) {
+ $wgParser->startExternalParse( $title, new ParserOptions(), OT_PREPROCESS );
+ $dom = $wgParser->preprocessToDom( $text );
+ if ( is_callable( array( $dom, 'saveXML' ) ) ) {
+ $xml = $dom->saveXML();
+ } else {
+ $xml = $dom->__toString();
+ }
+ $vals['parsetree'] = $xml;
+
+ }
if ($this->expandTemplates) {
$text = $wgParser->preprocess( $text, $title, new ParserOptions() );
}
@@ -366,11 +391,9 @@ class ApiQueryRevisions extends ApiQueryBase {
'excludeuser' => array(
ApiBase :: PARAM_TYPE => 'user'
),
-
'expandtemplates' => false,
- 'section' => array(
- ApiBase :: PARAM_TYPE => 'integer'
- ),
+ 'generatexml' => false,
+ 'section' => null,
'token' => array(
ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()),
ApiBase :: PARAM_ISMULTI => true
@@ -390,6 +413,7 @@ class ApiQueryRevisions extends ApiQueryBase {
'user' => 'only include revisions made by user',
'excludeuser' => 'exclude revisions made by user',
'expandtemplates' => 'expand templates in revision content',
+ 'generatexml' => 'generate XML parse tree for revision content',
'section' => 'only retrieve the content of this section',
'token' => 'Which tokens to obtain for each revision',
);
@@ -424,6 +448,6 @@ class ApiQueryRevisions extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryRevisions.php 37300 2008-07-08 08:42:27Z btongminh $';
+ return __CLASS__ . ': $Id: ApiQueryRevisions.php 44719 2008-12-17 16:34:01Z catrope $';
}
}
diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php
index 84a2ec63..cb020fff 100644
--- a/includes/api/ApiQuerySearch.php
+++ b/includes/api/ApiQuerySearch.php
@@ -53,7 +53,8 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
$limit = $params['limit'];
$query = $params['search'];
- if (is_null($query) || empty($query))
+ $what = $params['what'];
+ if (strval($query) === '')
$this->dieUsage("empty search string is not allowed", 'param-search');
$search = SearchEngine::create();
@@ -61,13 +62,30 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
$search->setNamespaces( $params['namespace'] );
$search->showRedirects = $params['redirects'];
- if ($params['what'] == 'text')
+ if ($what == 'text') {
$matches = $search->searchText( $query );
- else
+ } elseif( $what == 'title' ) {
$matches = $search->searchTitle( $query );
+ } else {
+ // We default to title searches; this is a terrible legacy
+ // of the way we initially set up the MySQL fulltext-based
+ // search engine with separate title and text fields.
+ // In the future, the default should be for a combined index.
+ $what = 'title';
+ $matches = $search->searchTitle( $query );
+
+ // Not all search engines support a separate title search,
+ // for instance the Lucene-based engine we use on Wikipedia.
+ // In this case, fall back to full-text search (which will
+ // include titles in it!)
+ if( is_null( $matches ) ) {
+ $what = 'text';
+ $matches = $search->searchText( $query );
+ }
+ }
if (is_null($matches))
- $this->dieUsage("{$params['what']} search is disabled",
- "search-{$params['what']}-disabled");
+ $this->dieUsage("{$what} search is disabled",
+ "search-{$what}-disabled");
$data = array ();
$count = 0;
@@ -78,8 +96,9 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
break;
}
- // Silently skip broken titles
- if ($result->isBrokenTitle()) continue;
+ // Silently skip broken and missing titles
+ if ($result->isBrokenTitle() || $result->isMissingRevision())
+ continue;
$title = $result->getTitle();
if (is_null($resultPageSet)) {
@@ -109,7 +128,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
ApiBase :: PARAM_ISMULTI => true,
),
'what' => array (
- ApiBase :: PARAM_DFLT => 'title',
+ ApiBase :: PARAM_DFLT => null,
ApiBase :: PARAM_TYPE => array (
'title',
'text',
@@ -151,6 +170,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQuerySearch.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiQuerySearch.php 44186 2008-12-03 19:33:57Z catrope $';
}
}
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index 1fd3b888..84757f7f 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -57,6 +57,9 @@ class ApiQuerySiteinfo extends ApiQueryBase {
case 'specialpagealiases':
$this->appendSpecialPageAliases( $p );
break;
+ case 'magicwords':
+ $this->appendMagicWords( $p );
+ break;
case 'interwikimap':
$filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
$this->appendInterwikiMap( $p, $filteriw );
@@ -70,6 +73,9 @@ class ApiQuerySiteinfo extends ApiQueryBase {
case 'usergroups':
$this->appendUserGroups( $p );
break;
+ case 'extensions':
+ $this->appendExtensions( $p );
+ break;
default :
ApiBase :: dieDebug( __METHOD__, "Unknown prop=$p" );
}
@@ -129,8 +135,13 @@ class ApiQuerySiteinfo extends ApiQueryBase {
'id' => $ns
);
ApiResult :: setContent( $data[$ns], $title );
- if( MWNamespace::hasSubpages($ns) )
+ $canonical = MWNamespace::getCanonicalName( $ns );
+
+ if( MWNamespace::hasSubpages( $ns ) )
$data[$ns]['subpages'] = '';
+
+ if( $canonical )
+ $data[$ns]['canonical'] = strtr($canonical, '_', ' ');
}
$this->getResult()->setIndexedTagName( $data, 'ns' );
@@ -138,9 +149,11 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
protected function appendNamespaceAliases( $property ) {
- global $wgNamespaceAliases;
+ global $wgNamespaceAliases, $wgContLang;
+ $wgContLang->load();
+ $aliases = array_merge($wgNamespaceAliases, $wgContLang->namespaceAliases);
$data = array();
- foreach( $wgNamespaceAliases as $title => $ns ) {
+ foreach( $aliases as $title => $ns ) {
$item = array(
'id' => $ns
);
@@ -164,6 +177,22 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$this->getResult()->setIndexedTagName( $data, 'specialpage' );
$this->getResult()->addValue( 'query', $property, $data );
}
+
+ protected function appendMagicWords( $property ) {
+ global $wgContLang;
+ $data = array();
+ foreach($wgContLang->getMagicWords() as $magicword => $aliases)
+ {
+ $caseSensitive = array_shift($aliases);
+ $arr = array('name' => $magicword, 'aliases' => $aliases);
+ if($caseSensitive)
+ $arr['case-sensitive'] = '';
+ $this->getResult()->setIndexedTagName($arr['aliases'], 'alias');
+ $data[] = $arr;
+ }
+ $this->getResult()->setIndexedTagName($data, 'magicword');
+ $this->getResult()->addValue('query', $property, $data);
+ }
protected function appendInterwikiMap( $property, $filter ) {
$this->resetQueryParams();
@@ -174,7 +203,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$this->addWhere( 'iw_local = 1' );
elseif( $filter === '!local' )
$this->addWhere( 'iw_local = 0' );
- elseif( $filter !== false )
+ elseif( $filter )
ApiBase :: dieDebug( __METHOD__, "Unknown filter=$filter" );
$this->addOption( 'ORDER BY', 'iw_prefix' );
@@ -239,7 +268,8 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data['edits'] = intval( SiteStats::edits() );
$data['images'] = intval( SiteStats::images() );
$data['users'] = intval( SiteStats::users() );
- $data['admins'] = intval( SiteStats::admins() );
+ $data['activeusers'] = intval( SiteStats::activeUsers() );
+ $data['admins'] = intval( SiteStats::numberingroup('sysop') );
$data['jobs'] = intval( SiteStats::jobs() );
$this->getResult()->addValue( 'query', $property, $data );
}
@@ -257,6 +287,40 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$this->getResult()->addValue( 'query', $property, $data );
}
+ protected function appendExtensions( $property ) {
+ global $wgExtensionCredits;
+ $data = array();
+ foreach ( $wgExtensionCredits as $type => $extensions ) {
+ foreach ( $extensions as $ext ) {
+ $ret = array();
+ $ret['type'] = $type;
+ if ( isset( $ext['name'] ) )
+ $ret['name'] = $ext['name'];
+ if ( isset( $ext['description'] ) )
+ $ret['description'] = $ext['description'];
+ if ( isset( $ext['descriptionmsg'] ) )
+ $ret['descriptionmsg'] = $ext['descriptionmsg'];
+ if ( isset( $ext['author'] ) ) {
+ $ret['author'] = is_array( $ext['author'] ) ?
+ implode( ', ', $ext['author' ] ) : $ext['author'];
+ }
+ if ( isset( $ext['version'] ) ) {
+ $ret['version'] = $ext['version'];
+ } elseif ( isset( $ext['svn-revision'] ) &&
+ preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
+ $ext['svn-revision'], $m ) )
+ {
+ $ret['version'] = 'r' . $m[1];
+ }
+ $data[] = $ret;
+ }
+ }
+
+ $this->getResult()->setIndexedTagName( $data, 'ext' );
+ $this->getResult()->addValue( 'query', $property, $data );
+ }
+
+
public function getAllowedParams() {
return array(
'prop' => array(
@@ -267,10 +331,12 @@ class ApiQuerySiteinfo extends ApiQueryBase {
'namespaces',
'namespacealiases',
'specialpagealiases',
+ 'magicwords',
'interwikimap',
'dbrepllag',
'statistics',
'usergroups',
+ 'extensions',
)
),
'filteriw' => array(
@@ -288,13 +354,15 @@ class ApiQuerySiteinfo extends ApiQueryBase {
'prop' => array(
'Which sysinfo properties to get:',
' "general" - Overall system information',
- ' "namespaces" - List of registered namespaces (localized)',
+ ' "namespaces" - List of registered namespaces and their canonical names',
' "namespacealiases" - List of registered namespace aliases',
' "specialpagealiases" - List of special page aliases',
+ ' "magicwords" - List of magic words and their 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',
+ ' "extensions" - Returns extensions installed on the wiki',
),
'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
'showalldb' => 'List all database servers, not just the one lagging the most',
@@ -314,6 +382,6 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 37034 2008-07-04 09:21:11Z vasilievvv $';
+ return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 44862 2008-12-20 23:49:16Z catrope $';
}
}
diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php
index c477acdb..be6c8bc4 100644
--- a/includes/api/ApiQueryUserContributions.php
+++ b/includes/api/ApiQueryUserContributions.php
@@ -62,6 +62,7 @@ class ApiQueryContributions extends ApiQueryBase {
if(isset($this->params['userprefix']))
{
$this->prefixMode = true;
+ $this->multiUserMode = true;
$this->userprefix = $this->params['userprefix'];
}
else
@@ -72,6 +73,7 @@ class ApiQueryContributions extends ApiQueryBase {
foreach($this->params['user'] as $u)
$this->prepareUsername($u);
$this->prefixMode = false;
+ $this->multiUserMode = (count($this->params['user']) > 1);
}
$this->prepareQuery();
@@ -87,7 +89,10 @@ class ApiQueryContributions extends ApiQueryBase {
while ( $row = $db->fetchObject( $res ) ) {
if (++ $count > $limit) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rev_timestamp));
+ if($this->multiUserMode)
+ $this->setContinueEnumParameter('continue', $this->continueStr($row));
+ else
+ $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rev_timestamp));
break;
}
@@ -132,13 +137,28 @@ class ApiQueryContributions extends ApiQueryBase {
//anything we retrieve.
$this->addTables(array('revision', 'page'));
$this->addWhere('page_id=rev_page');
+
+ // Handle continue parameter
+ if($this->multiUserMode && !is_null($this->params['continue']))
+ {
+ $continue = explode('|', $this->params['continue']);
+ if(count($continue) != 2)
+ $this->dieUsage("Invalid continue param. You should pass the original " .
+ "value returned by the previous query", "_badcontinue");
+ $encUser = $this->getDB()->strencode($continue[0]);
+ $encTS = wfTimestamp(TS_MW, $continue[1]);
+ $op = ($this->params['dir'] == 'older' ? '<' : '>');
+ $this->addWhere("rev_user_text $op '$encUser' OR " .
+ "(rev_user_text = '$encUser' AND " .
+ "rev_timestamp $op= '$encTS')");
+ }
$this->addWhereFld('rev_deleted', 0);
// We only want pages by the specified users.
if($this->prefixMode)
- $this->addWhere("rev_user_text LIKE '" . $this->getDb()->escapeLike($this->userprefix) . "%'");
+ $this->addWhere("rev_user_text LIKE '" . $this->getDB()->escapeLike($this->userprefix) . "%'");
else
- $this->addWhereFld( 'rev_user_text', $this->usernames );
+ $this->addWhereFld('rev_user_text', $this->usernames);
// ... and in the specified timeframe.
// Ensure the same sort order for rev_user_text and rev_timestamp
// so our query is indexed
@@ -157,6 +177,7 @@ class ApiQueryContributions extends ApiQueryBase {
$this->addWhereIf('rev_minor_edit != 0', isset ($show['minor']));
}
$this->addOption('LIMIT', $this->params['limit'] + 1);
+ $this->addOption( 'USE INDEX', array( 'revision' => 'usertext_timestamp' ) );
// Mandatory fields: timestamp allows request continuation
// ns+title checks if the user has access rights for this page
@@ -207,11 +228,17 @@ class ApiQueryContributions extends ApiQueryBase {
$vals['top'] = '';
}
- if ($this->fld_comment && !empty ($row->rev_comment))
+ if ($this->fld_comment && isset( $row->rev_comment ) )
$vals['comment'] = $row->rev_comment;
return $vals;
}
+
+ private function continueStr($row)
+ {
+ return $row->rev_user_text . '|' .
+ wfTimestamp(TS_ISO_8601, $row->rev_timestamp);
+ }
public function getAllowedParams() {
return array (
@@ -228,6 +255,7 @@ class ApiQueryContributions extends ApiQueryBase {
'end' => array (
ApiBase :: PARAM_TYPE => 'timestamp'
),
+ 'continue' => null,
'user' => array (
ApiBase :: PARAM_ISMULTI => true
),
@@ -269,6 +297,7 @@ class ApiQueryContributions extends ApiQueryBase {
'limit' => 'The maximum number of contributions to return.',
'start' => 'The start timestamp to return from.',
'end' => 'The end timestamp to return to.',
+ 'continue' => 'When more results are available, use this to continue.',
'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).',
@@ -290,6 +319,6 @@ class ApiQueryContributions extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUserContributions.php 37383 2008-07-09 11:44:49Z btongminh $';
+ return __CLASS__ . ': $Id: ApiQueryUserContributions.php 43271 2008-11-06 22:38:42Z siebrand $';
}
}
diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php
index 2d55a352..203b7e25 100644
--- a/includes/api/ApiQueryUserInfo.php
+++ b/includes/api/ApiQueryUserInfo.php
@@ -76,12 +76,16 @@ class ApiQueryUserInfo extends ApiQueryBase {
$result->setIndexedTagName($vals['groups'], 'g'); // even if empty
}
if (isset($this->prop['rights'])) {
- $vals['rights'] = $wgUser->getRights();
+ // User::getRights() may return duplicate values, strip them
+ $vals['rights'] = array_values(array_unique($wgUser->getRights()));
$result->setIndexedTagName($vals['rights'], 'r'); // even if empty
}
if (isset($this->prop['options'])) {
$vals['options'] = (is_null($wgUser->mOptions) ? User::getDefaultOptions() : $wgUser->mOptions);
}
+ if (isset($this->prop['preferencestoken']) && is_null($this->getMain()->getRequest()->getVal('callback'))) {
+ $vals['preferencestoken'] = $wgUser->editToken();
+ }
if (isset($this->prop['editcount'])) {
$vals['editcount'] = $wgUser->getEditCount();
}
@@ -110,6 +114,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
if(!$wgUser->isAnon())
$categories[] = 'newbie';
}
+ $categories = array_merge($categories, $wgUser->getGroups());
// Now get the actual limits
$retval = array();
@@ -134,6 +139,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
'groups',
'rights',
'options',
+ 'preferencestoken',
'editcount',
'ratelimits'
)
@@ -168,6 +174,6 @@ class ApiQueryUserInfo extends ApiQueryBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUserInfo.php 35186 2008-05-22 16:39:43Z brion $';
+ return __CLASS__ . ': $Id: ApiQueryUserInfo.php 43764 2008-11-20 15:15:00Z catrope $';
}
}
diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php
index a8147567..e50d8d82 100644
--- a/includes/api/ApiQueryUsers.php
+++ b/includes/api/ApiQueryUsers.php
@@ -68,15 +68,13 @@ if (!defined('MEDIAWIKI')) {
else
$goodNames[] = $n;
}
- if(empty($goodNames))
+ if(!count($goodNames))
return $retval;
- $db = $this->getDb();
+ $db = $this->getDB();
$this->addTables('user', 'u1');
- $this->addFields('u1.user_name');
+ $this->addFields('u1.*');
$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'])) {
$this->addTables('user_groups');
@@ -96,20 +94,26 @@ if (!defined('MEDIAWIKI')) {
$data = array();
$res = $this->select(__METHOD__);
while(($r = $db->fetchObject($res))) {
- $data[$r->user_name]['name'] = $r->user_name;
+ $user = User::newFromRow($r);
+ $name = $user->getName();
+ $data[$name]['name'] = $name;
if(isset($this->prop['editcount']))
- $data[$r->user_name]['editcount'] = $r->user_editcount;
+ // No proper member function in User class for this
+ $data[$name]['editcount'] = $r->user_editcount;
if(isset($this->prop['registration']))
- $data[$r->user_name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $r->user_registration);
+ // Nor for this one
+ $data[$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))
- $data[$r->user_name]['groups'][] = $r->ug_group;
+ $data[$name]['groups'][] = $r->ug_group;
if(isset($this->prop['blockinfo']))
if(!is_null($r->blocker_name)) {
- $data[$r->user_name]['blockedby'] = $r->blocker_name;
- $data[$r->user_name]['blockreason'] = $r->ipb_reason;
+ $data[$name]['blockedby'] = $r->blocker_name;
+ $data[$name]['blockreason'] = $r->ipb_reason;
}
+ if(isset($this->prop['emailable']) && $user->canReceiveEmail())
+ $data[$name]['emailable'] = '';
}
// Second pass: add result data to $retval
@@ -134,7 +138,8 @@ if (!defined('MEDIAWIKI')) {
'blockinfo',
'groups',
'editcount',
- 'registration'
+ 'registration',
+ 'emailable',
)
),
'users' => array(
@@ -147,9 +152,11 @@ if (!defined('MEDIAWIKI')) {
return array (
'prop' => array(
'What pieces of information to include',
- ' blockinfo - tags if the user is blocked, by whom, and for what reason',
- ' groups - lists all the groups the user belongs to',
- ' editcount - adds the user\'s edit count'
+ ' blockinfo - tags if the user is blocked, by whom, and for what reason',
+ ' groups - lists all the groups the user belongs to',
+ ' editcount - adds the user\'s edit count',
+ ' registration - adds the user\'s registration timestamp',
+ ' emailable - tags if the user can and wants to receive e-mail through [[Special:Emailuser]]',
),
'users' => 'A list of users to obtain the same information for'
);
@@ -164,6 +171,6 @@ if (!defined('MEDIAWIKI')) {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryUsers.php 38183 2008-07-29 12:58:04Z rotem $';
+ return __CLASS__ . ': $Id: ApiQueryUsers.php 44231 2008-12-04 14:42:30Z catrope $';
}
}
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index d17e83f6..ed3482fb 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -59,12 +59,11 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
if (!$wgUser->isLoggedIn())
$this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
- $allrev = $start = $end = $namespace = $dir = $limit = $prop = $show = null;
- extract($this->extractRequestParams());
+ $params = $this->extractRequestParams();
- if (!is_null($prop) && is_null($resultPageSet)) {
+ if (!is_null($params['prop']) && is_null($resultPageSet)) {
- $prop = array_flip($prop);
+ $prop = array_flip($params['prop']);
$this->fld_ids = isset($prop['ids']);
$this->fld_title = isset($prop['title']);
@@ -76,8 +75,8 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$this->fld_patrol = isset($prop['patrol']);
if ($this->fld_patrol) {
- global $wgUseRCPatrol, $wgUser;
- if (!$wgUseRCPatrol || !$wgUser->isAllowed('patrol'))
+ global $wgUser;
+ if (!$wgUser->useRCPatrol() && !$wgUser->useNPPatrol())
$this->dieUsage('patrol property is not available', 'patrol');
}
}
@@ -100,7 +99,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$this->addFieldsIf('rc_old_len', $this->fld_sizes);
$this->addFieldsIf('rc_new_len', $this->fld_sizes);
}
- elseif ($allrev) {
+ elseif ($params['allrev']) {
$this->addFields(array (
'rc_this_oldid',
'rc_namespace',
@@ -131,20 +130,26 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'rc_deleted' => 0,
));
- $this->addWhereRange('rc_timestamp', $dir, $start, $end);
- $this->addWhereFld('wl_namespace', $namespace);
- $this->addWhereIf('rc_this_oldid=page_latest', !$allrev);
+ $this->addWhereRange('rc_timestamp', $params['dir'], $params['start'], $params['end']);
+ $this->addWhereFld('wl_namespace', $params['namespace']);
+ $this->addWhereIf('rc_this_oldid=page_latest', !$params['allrev']);
- if (!is_null($show)) {
- $show = array_flip($show);
+ if (!is_null($params['show'])) {
+ $show = array_flip($params['show']);
/* Check for conflicting parameters. */
if ((isset ($show['minor']) && isset ($show['!minor']))
|| (isset ($show['bot']) && isset ($show['!bot']))
- || (isset ($show['anon']) && isset ($show['!anon']))) {
+ || (isset ($show['anon']) && isset ($show['!anon']))
+ || (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->useRCPatrol() && !$wgUser->useNPPatrol())
+ $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']));
@@ -153,13 +158,15 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$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 is an index optimization for mysql, as done in the Special:Watchlist page
- $this->addWhereIf("rc_timestamp > ''", !isset ($start) && !isset ($end) && $wgDBtype == 'mysql');
+ $this->addWhereIf("rc_timestamp > ''", !isset ($params['start']) && !isset ($params['end']) && $wgDBtype == 'mysql');
- $this->addOption('LIMIT', $limit +1);
+ $this->addOption('LIMIT', $params['limit'] +1);
$data = array ();
$count = 0;
@@ -167,7 +174,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$db = $this->getDB();
while ($row = $db->fetchObject($res)) {
- if (++ $count > $limit) {
+ if (++ $count > $params['limit']) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
$this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp));
break;
@@ -178,7 +185,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
if ($vals)
$data[] = $vals;
} else {
- if ($allrev) {
+ if ($params['allrev']) {
$data[] = intval($row->rc_this_oldid);
} else {
$data[] = intval($row->rc_cur_id);
@@ -192,7 +199,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$this->getResult()->setIndexedTagName($data, 'item');
$this->getResult()->addValue('query', $this->getModuleName(), $data);
}
- elseif ($allrev) {
+ elseif ($params['allrev']) {
$resultPageSet->populateFromRevisionIDs($data);
} else {
$resultPageSet->populateFromPageIDs($data);
@@ -237,7 +244,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$vals['newlen'] = intval($row->rc_new_len);
}
- if ($this->fld_comment && !empty ($row->rc_comment))
+ if ($this->fld_comment && isset( $row->rc_comment ))
$vals['comment'] = $row->rc_comment;
return $vals;
@@ -292,7 +299,9 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'bot',
'!bot',
'anon',
- '!anon'
+ '!anon',
+ 'patrolled',
+ '!patrolled',
)
)
);
@@ -329,6 +338,6 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiQueryWatchlist.php 37909 2008-07-22 13:26:15Z catrope $';
+ return __CLASS__ . ': $Id: ApiQueryWatchlist.php 44719 2008-12-17 16:34:01Z catrope $';
}
}
diff --git a/includes/api/ApiQueryWatchlistRaw.php b/includes/api/ApiQueryWatchlistRaw.php
new file mode 100644
index 00000000..e9951b42
--- /dev/null
+++ b/includes/api/ApiQueryWatchlistRaw.php
@@ -0,0 +1,179 @@
+<?php
+
+/*
+ * Created on Oct 4, 2008
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2008 Roan Kattouw <Firstname>.<Lastname>@home.nl
+ *
+ * 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 action allows clients to retrieve a list of pages
+ * on the logged-in user's watchlist.
+ *
+ * @ingroup API
+ */
+class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'wr');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+ global $wgUser;
+
+ $this->selectNamedDB('watchlist', DB_SLAVE, 'watchlist');
+
+ if (!$wgUser->isLoggedIn())
+ $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
+ $params = $this->extractRequestParams();
+ $prop = array_flip((array)$params['prop']);
+ $show = array_flip((array)$params['show']);
+ if(isset($show['changed']) && isset($show['!changed']))
+ $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show');
+
+ $this->addTables('watchlist');
+ $this->addFields(array('wl_namespace', 'wl_title'));
+ $this->addFieldsIf('wl_notificationtimestamp', isset($prop['changed']));
+ $this->addWhereFld('wl_user', $wgUser->getId());
+ $this->addWhereFld('wl_namespace', $params['namespace']);
+ $this->addWhereIf('wl_notificationtimestamp IS NOT NULL', isset($show['changed']));
+ $this->addWhereIf('wl_notificationtimestamp IS NULL', isset($show['!changed']));
+ if(isset($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");
+ $ns = intval($cont[0]);
+ $title = $this->getDB()->strencode($this->titleToKey($cont[1]));
+ $this->addWhere("wl_namespace > '$ns' OR ".
+ "(wl_namespace = '$ns' AND ".
+ "wl_title >= '$title')");
+ }
+ // Don't ORDER BY wl_namespace if it's constant in the WHERE clause
+ if(count($params['namespace']) == 1)
+ $this->addOption('ORDER BY', 'wl_title');
+ else
+ $this->addOption('ORDER BY', 'wl_namespace, wl_title');
+ $this->addOption('LIMIT', $params['limit'] + 1);
+ $res = $this->select(__METHOD__);
+
+ $db = $this->getDB();
+ $data = array();
+ $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->wl_namespace . '|' .
+ $this->keyToTitle($row->wl_title));
+ break;
+ }
+ $t = Title::makeTitle($row->wl_namespace, $row->wl_title);
+ if(is_null($resultPageSet))
+ {
+ $vals = array();
+ ApiQueryBase::addTitleInfo($vals, $t);
+ if(isset($prop['changed']) && !is_null($row->wl_notificationtimestamp))
+ $vals['changed'] = wfTimestamp(TS_ISO_8601, $row->wl_notificationtimestamp);
+ $data[] = $vals;
+ }
+ else
+ $titles[] = $t;
+ }
+ if(is_null($resultPageSet))
+ {
+ $this->getResult()->setIndexedTagName($data, 'wr');
+ $this->getResult()->addValue(null, $this->getModuleName(), $data);
+ }
+ else
+ $resultPageSet->populateFromTitles($titles);
+ }
+
+ public function getAllowedParams() {
+ return array (
+ 'continue' => null,
+ 'namespace' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => 'namespace'
+ ),
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ ),
+ 'prop' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'changed',
+ )
+ ),
+ 'show' => array (
+ ApiBase :: PARAM_ISMULTI => true,
+ ApiBase :: PARAM_TYPE => array (
+ 'changed',
+ '!changed',
+ )
+ )
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'continue' => 'When more results are available, use this to continue',
+ 'namespace' => 'Only list pages in the given namespace(s).',
+ 'limit' => 'How many total results to return per request.',
+ 'prop' => 'Which additional properties to get (non-generator mode only).',
+ 'show' => 'Only list items that meet these criteria.',
+ );
+ }
+
+ public function getDescription() {
+ return "Get all pages on the logged in user's watchlist";
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=watchlistraw',
+ 'api.php?action=query&generator=watchlistraw&gwrshow=changed&prop=revisions',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiQueryWatchlistRaw.php 41651 2008-10-04 14:30:33Z catrope $';
+ }
+}
diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php
index 9e798d35..900953e0 100644
--- a/includes/api/ApiResult.php
+++ b/includes/api/ApiResult.php
@@ -100,7 +100,7 @@ class ApiResult extends ApiBase {
}
elseif (is_array($arr[$name]) && is_array($value)) {
$merged = array_intersect_key($arr[$name], $value);
- if (empty ($merged))
+ if (!count($merged))
$arr[$name] += $value;
else
ApiBase :: dieDebug(__METHOD__, "Attempting to merge element $name");
@@ -180,18 +180,27 @@ class ApiResult extends ApiBase {
}
}
- if (empty($name))
+ if (!$name)
$data[] = $value; // Add list element
else
ApiResult :: setElement($data, $name, $value); // Add named element
}
+ /**
+ * Ensure all values in this result are valid UTF-8.
+ */
+ public function cleanUpUTF8()
+ {
+ $data = & $this->getData();
+ array_walk_recursive($data, array('UtfNormal', 'cleanUp'));
+ }
+
public function execute() {
ApiBase :: dieDebug(__METHOD__, 'execute() is not supported on Result object');
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiResult.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiResult.php 45752 2009-01-14 21:36:57Z catrope $';
}
}
@@ -201,7 +210,7 @@ if (!function_exists('array_intersect_key')) {
$argc = func_num_args();
if ($argc > 2) {
- for ($i = 1; !empty($isec) && $i < $argc; $i++) {
+ for ($i = 1; $isec && $i < $argc; $i++) {
$arr = func_get_arg($i);
foreach (array_keys($isec) as $key) {
diff --git a/includes/api/ApiRollback.php b/includes/api/ApiRollback.php
index 3739f694..653dca9e 100644
--- a/includes/api/ApiRollback.php
+++ b/includes/api/ApiRollback.php
@@ -37,7 +37,6 @@ class ApiRollback extends ApiBase {
}
public function execute() {
- global $wgUser;
$this->getMain()->requestWriteMode();
$params = $this->extractRequestParams();
@@ -55,7 +54,10 @@ class ApiRollback extends ApiBase {
if(!$titleObj->exists())
$this->dieUsageMsg(array('notanarticle'));
- $username = User::getCanonicalName($params['user']);
+ #We need to be able to revert IPs, but getCanonicalName rejects them
+ $username = User::isIP($params['user'])
+ ? $params['user']
+ : User::getCanonicalName($params['user']);
if(!$username)
$this->dieUsageMsg(array('invaliduser', $params['user']));
@@ -64,20 +66,17 @@ class ApiRollback extends ApiBase {
$details = null;
$retval = $articleObj->doRollback($username, $summary, $params['token'], $params['markbot'], $details);
- if(!empty($retval))
+ if($retval)
// We don't care about multiple errors, just report one of them
$this->dieUsageMsg(current($retval));
- $current = $target = $summary = NULL;
- extract($details);
-
$info = array(
'title' => $titleObj->getPrefixedText(),
- 'pageid' => $current->getPage(),
- 'summary' => $summary,
+ 'pageid' => $details['current']->getPage(),
+ 'summary' => $details['summary'],
'revid' => $titleObj->getLatestRevID(),
- 'old_revid' => $current->getID(),
- 'last_revid' => $target->getID()
+ 'old_revid' => $details['current']->getID(),
+ 'last_revid' => $details['target']->getID()
);
$this->getResult()->addValue(null, $this->getModuleName(), $info);
@@ -99,7 +98,7 @@ class ApiRollback extends ApiBase {
return array (
'title' => 'Title of the page you want to rollback.',
'user' => 'Name of the user whose edits are to be rolled back. If set incorrectly, you\'ll get a badtoken error.',
- 'token' => 'A rollback token previously retrieved through prop=info',
+ 'token' => 'A rollback token previously retrieved through prop=revisions',
'summary' => 'Custom edit summary. If not set, default summary will be used.',
'markbot' => 'Mark the reverted edits and the revert as bot edits'
);
@@ -107,8 +106,8 @@ class ApiRollback extends ApiBase {
public function getDescription() {
return array(
- 'Undoes the last edit to the page. If the last user who edited the page made multiple edits in a row,',
- 'they will all be rolled back. You need to be logged in as a sysop to use this function, see also action=login.'
+ 'Undo the last edit to the page. If the last user who edited the page made multiple edits in a row,',
+ 'they will all be rolled back.'
);
}
@@ -120,6 +119,6 @@ class ApiRollback extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiRollback.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiRollback.php 45043 2008-12-26 04:13:47Z mrzman $';
}
}
diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php
index d6a02a2a..cd52c518 100644
--- a/includes/api/ApiUnblock.php
+++ b/includes/api/ApiUnblock.php
@@ -64,14 +64,12 @@ class ApiUnblock extends ApiBase {
$this->dieUsageMsg(array('sessionfailure'));
if(!$wgUser->isAllowed('block'))
$this->dieUsageMsg(array('cantunblock'));
- if(wfReadOnly())
- $this->dieUsageMsg(array('readonlytext'));
$id = $params['id'];
$user = $params['user'];
$reason = (is_null($params['reason']) ? '' : $params['reason']);
$retval = IPUnblockForm::doUnblock($id, $user, $reason, $range);
- if(!empty($retval))
+ if($retval)
$this->dieUsageMsg($retval);
$res['id'] = $id;
@@ -96,7 +94,7 @@ class ApiUnblock extends ApiBase {
return array (
'id' => 'ID of the block you want to unblock (obtained through list=blocks). Cannot be used together with user',
'user' => 'Username, IP address or IP range you want to unblock. Cannot be used together with id',
- 'token' => 'An unblock token previously obtained through the gettoken parameter',
+ 'token' => 'An unblock token previously obtained through the gettoken parameter or prop=info',
'gettoken' => 'If set, an unblock token will be returned, and no other action will be taken',
'reason' => 'Reason for unblock (optional)',
);
@@ -116,6 +114,6 @@ class ApiUnblock extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiUnblock.php 35098 2008-05-20 17:13:28Z ialex $';
+ return __CLASS__ . ': $Id: ApiUnblock.php 42651 2008-10-27 12:06:49Z catrope $';
}
}
diff --git a/includes/api/ApiUndelete.php b/includes/api/ApiUndelete.php
index e054a70e..7ae9a3c0 100644
--- a/includes/api/ApiUndelete.php
+++ b/includes/api/ApiUndelete.php
@@ -51,8 +51,6 @@ class ApiUndelete extends ApiBase {
$this->dieUsageMsg(array('permdenied-undelete'));
if($wgUser->isBlocked())
$this->dieUsageMsg(array('blockedtext'));
- if(wfReadOnly())
- $this->dieUsageMsg(array('readonlytext'));
if(!$wgUser->matchEditToken($params['token']))
$this->dieUsageMsg(array('sessionfailure'));
@@ -69,7 +67,7 @@ class ApiUndelete extends ApiBase {
$params['timestamps'][$i] = wfTimestamp(TS_MW, $ts);
$pa = new PageArchive($titleObj);
- $dbw = wfGetDb(DB_MASTER);
+ $dbw = wfGetDB(DB_MASTER);
$dbw->begin();
$retval = $pa->undelete((isset($params['timestamps']) ? $params['timestamps'] : array()), $params['reason']);
if(!is_array($retval))
@@ -123,6 +121,6 @@ class ApiUndelete extends ApiBase {
}
public function getVersion() {
- return __CLASS__ . ': $Id: ApiUndelete.php 35348 2008-05-26 10:51:31Z catrope $';
+ return __CLASS__ . ': $Id: ApiUndelete.php 43270 2008-11-06 22:30:55Z siebrand $';
}
}
diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php
new file mode 100644
index 00000000..ab122fea
--- /dev/null
+++ b/includes/api/ApiWatch.php
@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * Created on Jan 4, 2008
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2008 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 ('ApiBase.php');
+}
+
+/**
+ * API module to allow users to log out of the wiki. API equivalent of
+ * Special:Userlogout.
+ *
+ * @ingroup API
+ */
+class ApiWatch extends ApiBase {
+
+ public function __construct($main, $action) {
+ parent :: __construct($main, $action);
+ }
+
+ public function execute() {
+ global $wgUser;
+ $this->getMain()->requestWriteMode();
+ if(!$wgUser->isLoggedIn())
+ $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
+ $params = $this->extractRequestParams();
+ $title = Title::newFromText($params['title']);
+ if(!$title)
+ $this->dieUsageMsg(array('invalidtitle', $params['title']));
+ $article = new Article($title);
+ $res = array('title' => $title->getPrefixedText());
+ if($params['unwatch'])
+ {
+ $res['unwatched'] = '';
+ $success = $article->doUnwatch();
+ }
+ else
+ {
+ $res['watched'] = '';
+ $success = $article->doWatch();
+ }
+ if(!$success)
+ $this->dieUsageMsg(array('hookaborted'));
+ $this->getResult()->addValue(null, $this->getModuleName(), $res);
+ }
+
+ public function getAllowedParams() {
+ return array (
+ 'title' => null,
+ 'unwatch' => false,
+ );
+ }
+
+ public function getParamDescription() {
+ return array (
+ 'title' => 'The page to (un)watch',
+ 'unwatch' => 'If set the page will be unwatched rather than watched',
+ );
+ }
+
+ public function getDescription() {
+ return array (
+ 'Add or remove a page from/to the current user\'s watchlist'
+ );
+ }
+
+ protected function getExamples() {
+ return array(
+ 'api.php?action=watch&title=Main_Page',
+ 'api.php?action=watch&title=Main_Page&unwatch',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id: ApiWatch.php 40460 2008-09-04 22:20:32Z ialex $';
+ }
+}