summaryrefslogtreecommitdiff
path: root/includes/api
diff options
context:
space:
mode:
Diffstat (limited to 'includes/api')
-rw-r--r--includes/api/ApiBase.php224
-rw-r--r--includes/api/ApiBlock.php61
-rw-r--r--includes/api/ApiComparePages.php51
-rw-r--r--includes/api/ApiDelete.php75
-rw-r--r--includes/api/ApiDisabled.php2
-rw-r--r--includes/api/ApiEditPage.php148
-rw-r--r--includes/api/ApiEmailUser.php24
-rw-r--r--includes/api/ApiExpandTemplates.php10
-rw-r--r--includes/api/ApiFeedContributions.php10
-rw-r--r--includes/api/ApiFeedWatchlist.php9
-rw-r--r--includes/api/ApiFileRevert.php38
-rw-r--r--includes/api/ApiFormatBase.php17
-rw-r--r--includes/api/ApiFormatDbg.php2
-rw-r--r--includes/api/ApiFormatJson.php2
-rw-r--r--includes/api/ApiFormatPhp.php2
-rw-r--r--includes/api/ApiFormatRaw.php2
-rw-r--r--includes/api/ApiFormatTxt.php2
-rw-r--r--includes/api/ApiFormatWddx.php2
-rw-r--r--includes/api/ApiFormatXml.php41
-rw-r--r--includes/api/ApiFormatYaml.php2
-rw-r--r--includes/api/ApiHelp.php2
-rw-r--r--includes/api/ApiImport.php42
-rw-r--r--includes/api/ApiLogin.php64
-rw-r--r--includes/api/ApiLogout.php6
-rw-r--r--includes/api/ApiMain.php161
-rw-r--r--includes/api/ApiMove.php51
-rw-r--r--includes/api/ApiOpenSearch.php4
-rw-r--r--includes/api/ApiOptions.php183
-rw-r--r--includes/api/ApiPageSet.php30
-rw-r--r--includes/api/ApiParamInfo.php58
-rw-r--r--includes/api/ApiParse.php115
-rw-r--r--includes/api/ApiPatrol.php15
-rw-r--r--includes/api/ApiProtect.php62
-rw-r--r--includes/api/ApiPurge.php38
-rw-r--r--includes/api/ApiQuery.php96
-rw-r--r--includes/api/ApiQueryAllCategories.php57
-rw-r--r--includes/api/ApiQueryAllImages.php409
-rw-r--r--includes/api/ApiQueryAllLinks.php68
-rw-r--r--includes/api/ApiQueryAllMessages.php (renamed from includes/api/ApiQueryAllmessages.php)25
-rw-r--r--includes/api/ApiQueryAllPages.php (renamed from includes/api/ApiQueryAllpages.php)45
-rw-r--r--includes/api/ApiQueryAllUsers.php103
-rw-r--r--includes/api/ApiQueryAllimages.php267
-rw-r--r--includes/api/ApiQueryBacklinks.php100
-rw-r--r--includes/api/ApiQueryBase.php13
-rw-r--r--includes/api/ApiQueryBlocks.php87
-rw-r--r--includes/api/ApiQueryCategories.php45
-rw-r--r--includes/api/ApiQueryCategoryInfo.php35
-rw-r--r--includes/api/ApiQueryCategoryMembers.php69
-rw-r--r--includes/api/ApiQueryDeletedrevs.php40
-rw-r--r--includes/api/ApiQueryDisabled.php2
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php143
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php17
-rw-r--r--includes/api/ApiQueryExternalLinks.php10
-rw-r--r--includes/api/ApiQueryFilearchive.php110
-rw-r--r--includes/api/ApiQueryIWBacklinks.php59
-rw-r--r--includes/api/ApiQueryIWLinks.php43
-rw-r--r--includes/api/ApiQueryImageInfo.php145
-rw-r--r--includes/api/ApiQueryImages.php38
-rw-r--r--includes/api/ApiQueryInfo.php259
-rw-r--r--includes/api/ApiQueryLangBacklinks.php59
-rw-r--r--includes/api/ApiQueryLangLinks.php38
-rw-r--r--includes/api/ApiQueryLinks.php43
-rw-r--r--includes/api/ApiQueryLogEvents.php66
-rw-r--r--includes/api/ApiQueryProtectedTitles.php38
-rw-r--r--includes/api/ApiQueryQueryPage.php37
-rw-r--r--includes/api/ApiQueryRandom.php10
-rw-r--r--includes/api/ApiQueryRecentChanges.php147
-rw-r--r--includes/api/ApiQueryRevisions.php98
-rw-r--r--includes/api/ApiQuerySearch.php59
-rw-r--r--includes/api/ApiQuerySiteinfo.php66
-rw-r--r--includes/api/ApiQueryStashImageInfo.php6
-rw-r--r--includes/api/ApiQueryTags.php21
-rw-r--r--includes/api/ApiQueryUserContributions.php100
-rw-r--r--includes/api/ApiQueryUserInfo.php74
-rw-r--r--includes/api/ApiQueryUsers.php96
-rw-r--r--includes/api/ApiQueryWatchlist.php85
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php50
-rw-r--r--includes/api/ApiResult.php4
-rw-r--r--includes/api/ApiRollback.php26
-rw-r--r--includes/api/ApiSetNotificationTimestamp.php285
-rw-r--r--includes/api/ApiTokens.php158
-rw-r--r--includes/api/ApiUnblock.php48
-rw-r--r--includes/api/ApiUndelete.php22
-rw-r--r--includes/api/ApiUpload.php130
-rw-r--r--includes/api/ApiUserrights.php8
-rw-r--r--includes/api/ApiWatch.php18
86 files changed, 4464 insertions, 1168 deletions
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index a586f688..875a3814 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -4,7 +4,7 @@
*
* Created on Sep 5, 2006
*
- * Copyright © 2006, 2010 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006, 2010 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
@@ -51,9 +51,16 @@ abstract class ApiBase extends ContextSource {
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 PARAM_DEPRECATED = 7; // Boolean, is the parameter deprecated (will show a warning)
+ /// @since 1.17
const PARAM_REQUIRED = 8; // Boolean, is the parameter required?
+ /// @since 1.17
const PARAM_RANGE_ENFORCE = 9; // Boolean, if MIN/MAX are set, enforce (die) these? Only applies if TYPE='integer' Use with extreme caution
+ const PROP_ROOT = 'ROOT'; // Name of property group that is on the root element of the result, i.e. not part of a list
+ const PROP_LIST = 'LIST'; // Boolean, is the result multiple items? Defaults to true for query modules, to false for other modules
+ const PROP_TYPE = 0; // Type of the property, uses same format as PARAM_TYPE
+ const PROP_NULLABLE = 1; // Boolean, can the property be not included in the result? Defaults to false
+
const LIMIT_BIG1 = 500; // Fast query, std user limit
const LIMIT_BIG2 = 5000; // Fast query, bot/sysop limit
const LIMIT_SML1 = 50; // Slow query, std user limit
@@ -127,7 +134,7 @@ abstract class ApiBase extends ContextSource {
/**
* Get the name of the module as shown in the profiler log
*
- * @param $db DatabaseBase
+ * @param $db DatabaseBase|bool
*
* @return string
*/
@@ -280,12 +287,12 @@ abstract class ApiBase extends ContextSource {
if ( is_numeric( $k ) ) {
$msg .= " $v\n";
} else {
- $v .= ":";
if ( is_array( $v ) ) {
$msgExample = implode( "\n", array_map( array( $this, 'indentExampleText' ), $v ) );
} else {
$msgExample = " $v";
}
+ $msgExample .= ":";
$msg .= wordwrap( $msgExample, 100, "\n" ) . "\n $k\n";
}
}
@@ -365,27 +372,38 @@ abstract class ApiBase extends ContextSource {
$desc = implode( $paramPrefix, $desc );
}
+ //handle shorthand
if ( !is_array( $paramSettings ) ) {
$paramSettings = array(
self::PARAM_DFLT => $paramSettings,
);
}
- $deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] ) ?
- $paramSettings[self::PARAM_DEPRECATED] : false;
- if ( $deprecated ) {
+ //handle missing type
+ if ( !isset( $paramSettings[ApiBase::PARAM_TYPE] ) ) {
+ $dflt = isset( $paramSettings[ApiBase::PARAM_DFLT] ) ? $paramSettings[ApiBase::PARAM_DFLT] : null;
+ if ( is_bool( $dflt ) ) {
+ $paramSettings[ApiBase::PARAM_TYPE] = 'boolean';
+ } elseif ( is_string( $dflt ) || is_null( $dflt ) ) {
+ $paramSettings[ApiBase::PARAM_TYPE] = 'string';
+ } elseif ( is_int( $dflt ) ) {
+ $paramSettings[ApiBase::PARAM_TYPE] = 'integer';
+ }
+ }
+
+ if ( isset( $paramSettings[self::PARAM_DEPRECATED] ) && $paramSettings[self::PARAM_DEPRECATED] ) {
$desc = "DEPRECATED! $desc";
}
- $required = isset( $paramSettings[self::PARAM_REQUIRED] ) ?
- $paramSettings[self::PARAM_REQUIRED] : false;
- if ( $required ) {
+ if ( isset( $paramSettings[self::PARAM_REQUIRED] ) && $paramSettings[self::PARAM_REQUIRED] ) {
$desc .= $paramPrefix . "This parameter is required";
}
$type = isset( $paramSettings[self::PARAM_TYPE] ) ? $paramSettings[self::PARAM_TYPE] : null;
if ( isset( $type ) ) {
- if ( isset( $paramSettings[self::PARAM_ISMULTI] ) && $paramSettings[self::PARAM_ISMULTI] ) {
+ $hintPipeSeparated = true;
+ $multi = isset( $paramSettings[self::PARAM_ISMULTI] ) ? $paramSettings[self::PARAM_ISMULTI] : false;
+ if ( $multi ) {
$prompt = 'Values (separate with \'|\'): ';
} else {
$prompt = 'One value: ';
@@ -393,7 +411,7 @@ abstract class ApiBase extends ContextSource {
if ( is_array( $type ) ) {
$choices = array();
- $nothingPrompt = false;
+ $nothingPrompt = '';
foreach ( $type as $t ) {
if ( $t === '' ) {
$nothingPrompt = 'Can be empty, or ';
@@ -404,6 +422,7 @@ abstract class ApiBase extends ContextSource {
$desc .= $paramPrefix . $nothingPrompt . $prompt;
$choicesstring = implode( ', ', $choices );
$desc .= wordwrap( $choicesstring, 100, $descWordwrap );
+ $hintPipeSeparated = false;
} else {
switch ( $type ) {
case 'namespace':
@@ -411,6 +430,7 @@ abstract class ApiBase extends ContextSource {
$desc .= $paramPrefix . $prompt;
$desc .= wordwrap( implode( ', ', MWNamespace::getValidNamespaces() ),
100, $descWordwrap );
+ $hintPipeSeparated = false;
break;
case 'limit':
$desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]}";
@@ -420,37 +440,39 @@ abstract class ApiBase extends ContextSource {
$desc .= ' allowed';
break;
case 'integer':
+ $s = $multi ? 's' : '';
$hasMin = isset( $paramSettings[self::PARAM_MIN] );
$hasMax = isset( $paramSettings[self::PARAM_MAX] );
if ( $hasMin || $hasMax ) {
if ( !$hasMax ) {
- $intRangeStr = "The value must be no less than {$paramSettings[self::PARAM_MIN]}";
+ $intRangeStr = "The value$s must be no less than {$paramSettings[self::PARAM_MIN]}";
} elseif ( !$hasMin ) {
- $intRangeStr = "The value must be no more than {$paramSettings[self::PARAM_MAX]}";
+ $intRangeStr = "The value$s must be no more than {$paramSettings[self::PARAM_MAX]}";
} else {
- $intRangeStr = "The value must be between {$paramSettings[self::PARAM_MIN]} and {$paramSettings[self::PARAM_MAX]}";
+ $intRangeStr = "The value$s must be between {$paramSettings[self::PARAM_MIN]} and {$paramSettings[self::PARAM_MAX]}";
}
$desc .= $paramPrefix . $intRangeStr;
}
break;
}
+ }
- if ( isset( $paramSettings[self::PARAM_ISMULTI] ) ) {
- $isArray = is_array( $paramSettings[self::PARAM_TYPE] );
+ if ( $multi ) {
+ if ( $hintPipeSeparated ) {
+ $desc .= $paramPrefix . "Separate values with '|'";
+ }
- if ( !$isArray
- || $isArray && count( $paramSettings[self::PARAM_TYPE] ) > self::LIMIT_SML1 ) {
- $desc .= $paramPrefix . "Maximum number of values " .
- self::LIMIT_SML1 . " (" . self::LIMIT_SML2 . " for bots)";
- }
+ $isArray = is_array( $type );
+ if ( !$isArray
+ || $isArray && count( $type ) > self::LIMIT_SML1 ) {
+ $desc .= $paramPrefix . "Maximum number of values " .
+ self::LIMIT_SML1 . " (" . self::LIMIT_SML2 . " for bots)";
}
}
}
- $default = is_array( $paramSettings )
- ? ( isset( $paramSettings[self::PARAM_DFLT] ) ? $paramSettings[self::PARAM_DFLT] : null )
- : $paramSettings;
+ $default = isset( $paramSettings[self::PARAM_DFLT] ) ? $paramSettings[self::PARAM_DFLT] : null;
if ( !is_null( $default ) && $default !== false ) {
$desc .= $paramPrefix . "Default: $default";
}
@@ -512,7 +534,7 @@ abstract class ApiBase extends ContextSource {
/**
* Returns usage examples for this module. Return false if no examples are available.
- * @return false|string|array
+ * @return bool|string|array
*/
protected function getExamples() {
return false;
@@ -523,7 +545,7 @@ abstract class ApiBase extends ContextSource {
* value) or (parameter name) => (array with PARAM_* constants as keys)
* Don't call this function directly: use getFinalParams() to allow
* hooks to modify parameters as needed.
- * @return array or false
+ * @return array|bool
*/
protected function getAllowedParams() {
return false;
@@ -533,7 +555,7 @@ abstract class ApiBase extends ContextSource {
* Returns an array of parameter descriptions.
* Don't call this functon directly: use getFinalParamDescription() to
* allow hooks to modify descriptions as needed.
- * @return array or false
+ * @return array|bool False on no parameter descriptions
*/
protected function getParamDescription() {
return false;
@@ -543,7 +565,7 @@ abstract class ApiBase extends ContextSource {
* Get final list of parameters, after hooks have had a chance to
* tweak it as needed.
*
- * @return array or false
+ * @return array|Bool False on no parameters
*/
public function getFinalParams() {
$params = $this->getAllowedParams();
@@ -555,7 +577,7 @@ abstract class ApiBase extends ContextSource {
* Get final parameter descriptions, after hooks have had a chance to tweak it as
* needed.
*
- * @return array
+ * @return array|bool False on no parameter descriptions
*/
public function getFinalParamDescription() {
$desc = $this->getParamDescription();
@@ -564,11 +586,56 @@ abstract class ApiBase extends ContextSource {
}
/**
- * Get final module description, after hooks have had a chance to tweak it as
+ * Returns possible properties in the result, grouped by the value of the prop parameter
+ * that shows them.
+ *
+ * Properties that are shown always are in a group with empty string as a key.
+ * Properties that can be shown by several values of prop are included multiple times.
+ * If some properties are part of a list and some are on the root object (see ApiQueryQueryPage),
+ * those on the root object are under the key PROP_ROOT.
+ * The array can also contain a boolean under the key PROP_LIST,
+ * indicating whether the result is a list.
+ *
+ * Don't call this functon directly: use getFinalResultProperties() to
+ * allow hooks to modify descriptions as needed.
+ *
+ * @return array|bool False on no properties
+ */
+ protected function getResultProperties() {
+ return false;
+ }
+
+ /**
+ * Get final possible result properties, after hooks have had a chance to tweak it as
* needed.
*
* @return array
*/
+ public function getFinalResultProperties() {
+ $properties = $this->getResultProperties();
+ wfRunHooks( 'APIGetResultProperties', array( $this, &$properties ) );
+ return $properties;
+ }
+
+ /**
+ * Add token properties to the array used by getResultProperties,
+ * based on a token functions mapping.
+ */
+ protected static function addTokenProperties( &$props, $tokenFunctions ) {
+ foreach ( array_keys( $tokenFunctions ) as $token ) {
+ $props[''][$token . 'token'] = array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ );
+ }
+ }
+
+ /**
+ * Get final module description, after hooks have had a chance to tweak it as
+ * needed.
+ *
+ * @return array|bool False on no parameters
+ */
public function getFinalDescription() {
$desc = $this->getDescription();
wfRunHooks( 'APIGetDescription', array( &$this, &$desc ) );
@@ -630,14 +697,15 @@ abstract class ApiBase extends ContextSource {
public function requireOnlyOneParameter( $params ) {
$required = func_get_args();
array_shift( $required );
+ $p = $this->getModulePrefix();
$intersection = array_intersect( array_keys( array_filter( $params,
array( $this, "parameterNotEmpty" ) ) ), $required );
if ( count( $intersection ) > 1 ) {
- $this->dieUsage( 'The parameters ' . implode( ', ', $intersection ) . ' can not be used together', 'invalidparammix' );
+ $this->dieUsage( "The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', "{$p}invalidparammix" );
} elseif ( count( $intersection ) == 0 ) {
- $this->dieUsage( 'One of the parameters ' . implode( ', ', $required ) . ' is required', 'missingparam' );
+ $this->dieUsage( "One of the parameters {$p}" . implode( ", {$p}", $required ) . ' is required', "{$p}missingparam" );
}
}
@@ -665,12 +733,13 @@ abstract class ApiBase extends ContextSource {
public function requireMaxOneParameter( $params ) {
$required = func_get_args();
array_shift( $required );
+ $p = $this->getModulePrefix();
$intersection = array_intersect( array_keys( array_filter( $params,
array( $this, "parameterNotEmpty" ) ) ), $required );
if ( count( $intersection ) > 1 ) {
- $this->dieUsage( 'The parameters ' . implode( ', ', $intersection ) . ' can not be used together', 'invalidparammix' );
+ $this->dieUsage( "The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', "{$p}invalidparammix" );
}
}
@@ -690,6 +759,53 @@ abstract class ApiBase extends ContextSource {
}
/**
+ * @param $params array
+ * @param $load bool|string Whether load the object's state from the database:
+ * - false: don't load (if the pageid is given, it will still be loaded)
+ * - 'fromdb': load from a slave database
+ * - 'fromdbmaster': load from the master database
+ * @return WikiPage
+ */
+ public function getTitleOrPageId( $params, $load = false ) {
+ $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
+
+ $pageObj = null;
+ if ( isset( $params['title'] ) ) {
+ $titleObj = Title::newFromText( $params['title'] );
+ if ( !$titleObj ) {
+ $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
+ }
+ $pageObj = WikiPage::factory( $titleObj );
+ if ( $load !== false ) {
+ $pageObj->loadPageData( $load );
+ }
+ } elseif ( isset( $params['pageid'] ) ) {
+ if ( $load === false ) {
+ $load = 'fromdb';
+ }
+ $pageObj = WikiPage::newFromID( $params['pageid'], $load );
+ if ( !$pageObj ) {
+ $this->dieUsageMsg( array( 'nosuchpageid', $params['pageid'] ) );
+ }
+ }
+
+ return $pageObj;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTitleOrPageIdErrorMessage() {
+ return array_merge(
+ $this->getRequireOnlyOneParameterErrorMessages( array( 'title', 'pageid' ) ),
+ array(
+ array( 'invalidtitle', 'title' ),
+ array( 'nosuchpageid', 'pageid' ),
+ )
+ );
+ }
+
+ /**
* Callback function used in requireOnlyOneParameter to check whether reequired parameters are set
*
* @param $x object Parameter to check is not null/false
@@ -719,7 +835,7 @@ abstract class ApiBase extends ContextSource {
*/
protected function getWatchlistValue ( $watchlist, $titleObj, $userOption = null ) {
- $userWatching = $titleObj->userIsWatching();
+ $userWatching = $this->getUser()->isWatched( $titleObj );
switch ( $watchlist ) {
case 'watch':
@@ -773,7 +889,7 @@ abstract class ApiBase extends ContextSource {
* Using the settings determine the value for the given parameter
*
* @param $paramName String: parameter name
- * @param $paramSettings Mixed: default value or an array of settings
+ * @param $paramSettings array|mixed default value or an array of settings
* using PARAM_* constants.
* @param $parseLimit Boolean: parse limit?
* @return mixed Parameter value
@@ -809,8 +925,8 @@ abstract class ApiBase extends ContextSource {
if ( $type == 'boolean' ) {
if ( isset( $default ) && $default !== false ) {
- // Having a default value of anything other than 'false' is pointless
- ApiBase::dieDebug( __METHOD__, "Boolean param $encParamName's default is set to '$default'" );
+ // Having a default value of anything other than 'false' is not allowed
+ ApiBase::dieDebug( __METHOD__, "Boolean param $encParamName's default is set to '$default'. Boolean parameters must default to false." );
}
$value = $this->getRequest()->getCheck( $encParamName );
@@ -1078,7 +1194,8 @@ abstract class ApiBase extends ContextSource {
* @param $errorCode string Brief, arbitrary, stable string to allow easy
* automated identification of the error, e.g., 'unknown_action'
* @param $httpRespCode int HTTP response code
- * @param $extradata array Data to add to the <error> element; array in ApiResult format
+ * @param $extradata array Data to add to the "<error>" element; array in ApiResult format
+ * @throws UsageException
*/
public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
Profiler::instance()->close();
@@ -1155,6 +1272,8 @@ abstract class ApiBase extends ContextSource {
'nouserspecified' => array( 'code' => 'invaliduser', 'info' => "Invalid username \"\$1\"" ),
'noname' => array( 'code' => 'invaliduser', 'info' => "Invalid username \"\$1\"" ),
'summaryrequired' => array( 'code' => 'summaryrequired', 'info' => 'Summary required' ),
+ 'import-rootpage-invalid' => array( 'code' => 'import-rootpage-invalid', 'info' => 'Root page is an invalid title' ),
+ 'import-rootpage-nosubpage' => array( 'code' => 'import-rootpage-nosubpage', 'info' => 'Namespace "$1" of the root page does not allow subpages' ),
// API-specific messages
'readrequired' => array( 'code' => 'readapidenied', 'info' => "You need read permission to use this module" ),
@@ -1186,7 +1305,6 @@ abstract class ApiBase extends ContextSource {
'toofewexpiries' => array( 'code' => 'toofewexpiries', 'info' => "\$1 expiry timestamps were provided where \$2 were needed" ),
'cantimport' => array( 'code' => 'cantimport', 'info' => "You don't have permission to import pages" ),
'cantimport-upload' => array( 'code' => 'cantimport-upload', 'info' => "You don't have permission to import uploaded pages" ),
- 'nouploadmodule' => array( 'code' => 'nomodule', 'info' => 'No upload module set' ),
'importnofile' => array( 'code' => 'nofile', 'info' => "You didn't upload a file" ),
'importuploaderrorsize' => array( 'code' => 'filetoobig', 'info' => 'The file you uploaded is bigger than the maximum upload size' ),
'importuploaderrorpartial' => array( 'code' => 'partialupload', 'info' => 'The file was only partially uploaded' ),
@@ -1202,12 +1320,14 @@ abstract class ApiBase extends ContextSource {
'specialpage-cantexecute' => array( 'code' => 'specialpage-cantexecute', 'info' => "You don't have permission to view the results of this special page" ),
'invalidoldimage' => array( 'code' => 'invalidoldimage', 'info' => 'The oldimage parameter has invalid format' ),
'nodeleteablefile' => array( 'code' => 'nodeleteablefile', 'info' => 'No such old version of the file' ),
+ 'fileexists-forbidden' => array( 'code' => 'fileexists-forbidden', 'info' => 'A file with name "$1" already exists, and cannot be overwritten.' ),
+ 'fileexists-shared-forbidden' => array( 'code' => 'fileexists-shared-forbidden', 'info' => 'A file with name "$1" already exists in the shared file repository, and cannot be overwritten.' ),
+ 'filerevert-badversion' => array( 'code' => 'filerevert-badversion', 'info' => 'There is no previous local version of this file with the provided timestamp.' ),
// ApiEditPage messages
'noimageredirect-anon' => array( 'code' => 'noimageredirect-anon', 'info' => "Anonymous users can't create image redirects" ),
'noimageredirect-logged' => array( 'code' => 'noimageredirect', 'info' => "You don't have permission to create image redirects" ),
'spamdetected' => array( 'code' => 'spamdetected', 'info' => "Your edit was refused because it contained a spam fragment: \"\$1\"" ),
- 'filtered' => array( 'code' => 'filtered', 'info' => "The filter callback function refused your edit" ),
'contenttoobig' => array( 'code' => 'contenttoobig', 'info' => "The content you supplied exceeds the article size limit of \$1 kilobytes" ),
'noedit-anon' => array( 'code' => 'noedit-anon', 'info' => "Anonymous users can't edit pages" ),
'noedit' => array( 'code' => 'noedit', 'info' => "You don't have permission to edit pages" ),
@@ -1227,10 +1347,11 @@ abstract class ApiBase extends ContextSource {
'edit-already-exists' => array( 'code' => 'edit-already-exists', 'info' => "It seems the page you tried to create already exist" ),
// uploadMsgs
- 'invalid-session-key' => array( 'code' => 'invalid-session-key', 'info' => 'Not a valid session key' ),
+ 'invalid-file-key' => array( 'code' => 'invalid-file-key', 'info' => 'Not a valid file key' ),
'nouploadmodule' => array( 'code' => 'nouploadmodule', 'info' => 'No upload module set' ),
'uploaddisabled' => array( 'code' => 'uploaddisabled', 'info' => 'Uploads are not enabled. Make sure $wgEnableUploads is set to true in LocalSettings.php and the PHP ini setting file_uploads is true' ),
'copyuploaddisabled' => array( 'code' => 'copyuploaddisabled', 'info' => 'Uploads by URL is not enabled. Make sure $wgAllowCopyUploads is set to true in LocalSettings.php.' ),
+ 'copyuploadbaddomain' => array( 'code' => 'copyuploadbaddomain', 'info' => 'Uploads by URL are not allowed from this domain.' ),
'filename-tooshort' => array( 'code' => 'filename-tooshort', 'info' => 'The filename is too short' ),
'filename-toolong' => array( 'code' => 'filename-toolong', 'info' => 'The filename is too long' ),
@@ -1280,10 +1401,9 @@ abstract class ApiBase extends ContextSource {
}
if ( isset( self::$messageMap[$key] ) ) {
- return array( 'code' =>
- wfMsgReplaceArgs( self::$messageMap[$key]['code'], $error ),
- 'info' =>
- wfMsgReplaceArgs( self::$messageMap[$key]['info'], $error )
+ return array(
+ 'code' => wfMsgReplaceArgs( self::$messageMap[$key]['code'], $error ),
+ 'info' => wfMsgReplaceArgs( self::$messageMap[$key]['info'], $error )
);
}
@@ -1332,7 +1452,9 @@ abstract class ApiBase extends ContextSource {
}
/**
- * Returns whether this module requires a Token to execute
+ * Returns whether this module requires a token to execute
+ * It is used to show possible errors in action=paraminfo
+ * see bug 25248
* @return bool
*/
public function needsToken() {
@@ -1340,8 +1462,12 @@ abstract class ApiBase extends ContextSource {
}
/**
- * Returns the token salt if there is one, '' if the module doesn't require a salt, else false if the module doesn't need a token
- * @return bool|string
+ * Returns the token salt if there is one,
+ * '' if the module doesn't require a salt,
+ * else false if the module doesn't need a token
+ * You have also to override needsToken()
+ * Value is passed to User::getEditToken
+ * @return bool|string|array
*/
public function getTokenSalt() {
return false;
@@ -1373,7 +1499,7 @@ abstract class ApiBase extends ContextSource {
}
/**
- * @return false|string|array Returns a false if the module has no help url, else returns a (array of) string
+ * @return bool|string|array Returns a false if the module has no help url, else returns a (array of) string
*/
public function getHelpUrls() {
return false;
diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php
index 351ac6b7..c879b35d 100644
--- a/includes/api/ApiBlock.php
+++ b/includes/api/ApiBlock.php
@@ -4,7 +4,7 @@
*
* Created on Sep 4, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -47,7 +47,7 @@ class ApiBlock extends ApiBase {
$params = $this->extractRequestParams();
if ( $params['gettoken'] ) {
- $res['blocktoken'] = $user->getEditToken( '', $this->getMain()->getRequest() );
+ $res['blocktoken'] = $user->getEditToken();
$this->getResult()->addValue( null, $this->getModuleName(), $res );
return;
}
@@ -72,9 +72,9 @@ class ApiBlock extends ApiBase {
$data = array(
'Target' => $params['user'],
'Reason' => array(
- is_null( $params['reason'] ) ? '' : $params['reason'],
+ $params['reason'],
'other',
- is_null( $params['reason'] ) ? '' : $params['reason']
+ $params['reason']
),
'Expiry' => $params['expiry'] == 'never' ? 'infinite' : $params['expiry'],
'HardBlock' => !$params['anononly'],
@@ -100,12 +100,14 @@ class ApiBlock extends ApiBase {
$block = Block::newFromTarget( $target );
if( $block instanceof Block ){
- $res['expiry'] = $block->mExpiry == wfGetDB( DB_SLAVE )->getInfinity()
+ $res['expiry'] = $block->mExpiry == $this->getDB()->getInfinity()
? 'infinite'
: wfTimestamp( TS_ISO_8601, $block->mExpiry );
+ $res['id'] = $block->getId();
} else {
# should be unreachable
$res['expiry'] = '';
+ $res['id'] = '';
}
$res['reason'] = $params['reason'];
@@ -149,9 +151,12 @@ class ApiBlock extends ApiBase {
ApiBase::PARAM_REQUIRED => true
),
'token' => null,
- 'gettoken' => false,
+ 'gettoken' => array(
+ ApiBase::PARAM_DFLT => false,
+ ApiBase::PARAM_DEPRECATED => true,
+ ),
'expiry' => 'never',
- 'reason' => null,
+ 'reason' => '',
'anononly' => false,
'nocreate' => false,
'autoblock' => false,
@@ -166,10 +171,10 @@ class ApiBlock extends ApiBase {
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 or prop=info',
+ 'token' => 'A block token previously obtained through 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)',
+ 'reason' => 'Reason for block',
'anononly' => 'Block anonymous users only (i.e. disable anonymous edits for this IP)',
'nocreate' => 'Prevent account creation',
'autoblock' => 'Automatically block the last used IP address, and any subsequent IP addresses they try to login from',
@@ -181,6 +186,44 @@ class ApiBlock extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'blocktoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'user' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'userID' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'expiry' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'id' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'reason' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'anononly' => 'boolean',
+ 'nocreate' => 'boolean',
+ 'autoblock' => 'boolean',
+ 'noemail' => 'boolean',
+ 'hidename' => 'boolean',
+ 'allowusertalk' => 'boolean',
+ 'watchuser' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
return 'Block a user';
}
diff --git a/includes/api/ApiComparePages.php b/includes/api/ApiComparePages.php
index 4bb94c4a..ed72b29b 100644
--- a/includes/api/ApiComparePages.php
+++ b/includes/api/ApiComparePages.php
@@ -32,8 +32,8 @@ class ApiComparePages extends ApiBase {
public function execute() {
$params = $this->extractRequestParams();
- $rev1 = $this->revisionOrTitle( $params['fromrev'], $params['fromtitle'] );
- $rev2 = $this->revisionOrTitle( $params['torev'], $params['totitle'] );
+ $rev1 = $this->revisionOrTitleOrId( $params['fromrev'], $params['fromtitle'], $params['fromid'] );
+ $rev2 = $this->revisionOrTitleOrId( $params['torev'], $params['totitle'], $params['toid'] );
$de = new DifferenceEngine( $this->getContext(),
$rev1,
@@ -46,10 +46,16 @@ class ApiComparePages extends ApiBase {
if ( isset( $params['fromtitle'] ) ) {
$vals['fromtitle'] = $params['fromtitle'];
}
+ if ( isset( $params['fromid'] ) ) {
+ $vals['fromid'] = $params['fromid'];
+ }
$vals['fromrevid'] = $rev1;
if ( isset( $params['totitle'] ) ) {
$vals['totitle'] = $params['totitle'];
}
+ if ( isset( $params['toid'] ) ) {
+ $vals['toid'] = $params['toid'];
+ }
$vals['torevid'] = $rev2;
$difftext = $de->getDiffBody();
@@ -67,9 +73,10 @@ class ApiComparePages extends ApiBase {
/**
* @param $revision int
* @param $titleText string
+ * @param $titleId int
* @return int
*/
- private function revisionOrTitle( $revision, $titleText ) {
+ private function revisionOrTitleOrId( $revision, $titleText, $titleId ) {
if( $revision ){
return $revision;
} elseif( $titleText ) {
@@ -78,17 +85,29 @@ class ApiComparePages extends ApiBase {
$this->dieUsageMsg( array( 'invalidtitle', $titleText ) );
}
return $title->getLatestRevID();
+ } elseif ( $titleId ) {
+ $title = Title::newFromID( $titleId );
+ if( !$title ) {
+ $this->dieUsageMsg( array( 'nosuchpageid', $titleId ) );
+ }
+ return $title->getLatestRevID();
}
- $this->dieUsage( 'inputneeded', 'A title or a revision number is needed for both the from and the to parameters' );
+ $this->dieUsage( 'inputneeded', 'A title, a page ID, or a revision number is needed for both the from and the to parameters' );
}
public function getAllowedParams() {
return array(
'fromtitle' => null,
+ 'fromid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
'fromrev' => array(
ApiBase::PARAM_TYPE => 'integer'
),
'totitle' => null,
+ 'toid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
'torev' => array(
ApiBase::PARAM_TYPE => 'integer'
),
@@ -98,15 +117,36 @@ class ApiComparePages extends ApiBase {
public function getParamDescription() {
return array(
'fromtitle' => 'First title to compare',
+ 'fromid' => 'First page ID to compare',
'fromrev' => 'First revision to compare',
'totitle' => 'Second title to compare',
+ 'toid' => 'Second page ID to compare',
'torev' => 'Second revision to compare',
);
}
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'fromtitle' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'fromrevid' => 'integer',
+ 'totitle' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'torevid' => 'integer',
+ '*' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return array(
'Get the difference between 2 pages',
- 'You must pass a revision number or a page title for each part (1 and 2)'
+ 'You must pass a revision number or a page title or a page ID id for each part (1 and 2)'
);
}
@@ -114,6 +154,7 @@ class ApiComparePages extends ApiBase {
return array_merge( parent::getPossibleErrors(), array(
array( 'code' => 'inputneeded', 'info' => 'A title or a revision is needed' ),
array( 'invalidtitle', 'title' ),
+ array( 'nosuchpageid', 'pageid' ),
array( 'code' => 'baddiff', 'info' => 'The diff cannot be retrieved. Maybe one or both revisions do not exist or you do not have permission to view them.' ),
) );
}
diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php
index cfaf6cc1..2d36f19a 100644
--- a/includes/api/ApiDelete.php
+++ b/includes/api/ApiDelete.php
@@ -4,7 +4,7 @@
*
* Created on Jun 30, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -46,35 +46,24 @@ class ApiDelete extends ApiBase {
public function execute() {
$params = $this->extractRequestParams();
- $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
-
- if ( isset( $params['title'] ) ) {
- $titleObj = Title::newFromText( $params['title'] );
- if ( !$titleObj ) {
- $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
- }
- } elseif ( isset( $params['pageid'] ) ) {
- $titleObj = Title::newFromID( $params['pageid'] );
- if ( !$titleObj ) {
- $this->dieUsageMsg( array( 'nosuchpageid', $params['pageid'] ) );
- }
- }
- if ( !$titleObj->exists() ) {
+ $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' );
+ if ( !$pageObj->exists() ) {
$this->dieUsageMsg( 'notanarticle' );
}
- $reason = ( isset( $params['reason'] ) ? $params['reason'] : null );
- $pageObj = WikiPage::factory( $titleObj );
+ $titleObj = $pageObj->getTitle();
+ $reason = $params['reason'];
$user = $this->getUser();
if ( $titleObj->getNamespace() == NS_FILE ) {
- $retval = self::deleteFile( $pageObj, $user, $params['token'], $params['oldimage'], $reason, false );
+ $status = self::deleteFile( $pageObj, $user, $params['token'], $params['oldimage'], $reason, false );
} else {
- $retval = self::delete( $pageObj, $user, $params['token'], $reason );
+ $status = self::delete( $pageObj, $user, $params['token'], $reason );
}
- if ( count( $retval ) ) {
- $this->dieUsageMsg( reset( $retval ) ); // We don't care about multiple errors, just report one of them
+ if ( !$status->isGood() ) {
+ $errors = $status->getErrorsArray();
+ $this->dieUsageMsg( $errors[0] ); // We don't care about multiple errors, just report one of them
}
// Deprecated parameters
@@ -87,7 +76,11 @@ class ApiDelete extends ApiBase {
}
$this->setWatch( $watch, $titleObj, 'watchdeletion' );
- $r = array( 'title' => $titleObj->getPrefixedText(), 'reason' => $reason );
+ $r = array(
+ 'title' => $titleObj->getPrefixedText(),
+ 'reason' => $reason,
+ 'logid' => $status->value
+ );
$this->getResult()->addValue( null, $this->getModuleName(), $r );
}
@@ -109,7 +102,7 @@ class ApiDelete extends ApiBase {
* @param $user User doing the action
* @param $token String: delete token (same as edit token)
* @param $reason String: reason for the deletion. Autogenerated if NULL
- * @return Title::getUserPermissionsErrors()-like array
+ * @return Status
*/
public static function delete( Page $page, User $user, $token, &$reason = null ) {
$title = $page->getTitle();
@@ -131,11 +124,7 @@ class ApiDelete extends ApiBase {
$error = '';
// Luckily, Article.php provides a reusable delete function that does the hard work for us
- if ( $page->doDeleteArticle( $reason, false, 0, true, $error ) ) {
- return array();
- } else {
- return array( array( 'cannotdelete', $title->getPrefixedText() ) );
- }
+ return $page->doDeleteArticleReal( $reason, false, 0, true, $error );
}
/**
@@ -145,7 +134,7 @@ class ApiDelete extends ApiBase {
* @param $oldimage
* @param $reason
* @param $suppress bool
- * @return \type|array|Title
+ * @return Status
*/
public static function deleteFile( Page $page, User $user, $token, $oldimage, &$reason = null, $suppress = false ) {
$title = $page->getTitle();
@@ -167,19 +156,12 @@ class ApiDelete extends ApiBase {
if ( !$oldfile->exists() || !$oldfile->isLocal() || $oldfile->getRedirected() ) {
return array( array( 'nodeleteablefile' ) );
}
- } else {
- $oldfile = false;
}
if ( is_null( $reason ) ) { // Log and RC don't like null reasons
$reason = '';
}
- $status = FileDeleteForm::doDelete( $title, $file, $oldimage, $reason, $suppress );
- if ( !$status->isGood() ) {
- return array( array( 'cannotdelete', $title->getPrefixedText() ) );
- }
-
- return array();
+ return FileDeleteForm::doDelete( $title, $file, $oldimage, $reason, $suppress );
}
public function mustBePosted() {
@@ -196,7 +178,10 @@ class ApiDelete extends ApiBase {
'pageid' => array(
ApiBase::PARAM_TYPE => 'integer'
),
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'reason' => null,
'watch' => array(
ApiBase::PARAM_DFLT => false,
@@ -233,16 +218,24 @@ class ApiDelete extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'title' => 'string',
+ 'reason' => 'string',
+ 'logid' => 'integer'
+ )
+ );
+ }
+
public function getDescription() {
return 'Delete a page';
}
public function getPossibleErrors() {
return array_merge( parent::getPossibleErrors(),
- $this->getRequireOnlyOneParameterErrorMessages( array( 'title', 'pageid' ) ),
+ $this->getTitleOrPageIdErrorMessage(),
array(
- array( 'invalidtitle', 'title' ),
- array( 'nosuchpageid', 'pageid' ),
array( 'notanarticle' ),
array( 'hookaborted', 'error' ),
array( 'delete-toobig', 'limit' ),
diff --git a/includes/api/ApiDisabled.php b/includes/api/ApiDisabled.php
index 55754896..13975aec 100644
--- a/includes/api/ApiDisabled.php
+++ b/includes/api/ApiDisabled.php
@@ -4,7 +4,7 @@
*
* Created on Sep 25, 2008
*
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<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
diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php
index 9ed6d08d..0963fe7c 100644
--- a/includes/api/ApiEditPage.php
+++ b/includes/api/ApiEditPage.php
@@ -4,7 +4,7 @@
*
* Created on August 16, 2007
*
- * Copyright © 2007 Iker Labarga <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Iker Labarga "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -48,8 +48,9 @@ class ApiEditPage extends ApiBase {
$this->dieUsageMsg( 'missingtext' );
}
- $titleObj = Title::newFromText( $params['title'] );
- if ( !$titleObj || $titleObj->isExternal() ) {
+ $pageObj = $this->getTitleOrPageId( $params );
+ $titleObj = $pageObj->getTitle();
+ if ( $titleObj->isExternal() ) {
$this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
}
@@ -59,7 +60,11 @@ class ApiEditPage extends ApiBase {
if ( $titleObj->isRedirect() ) {
$oldTitle = $titleObj;
- $titles = Title::newFromRedirectArray( Revision::newFromTitle( $oldTitle )->getText( Revision::FOR_THIS_USER ) );
+ $titles = Title::newFromRedirectArray(
+ Revision::newFromTitle(
+ $oldTitle, false, Revision::READ_LATEST
+ )->getText( Revision::FOR_THIS_USER )
+ );
// array_shift( $titles );
$redirValues = array();
@@ -161,7 +166,7 @@ class ApiEditPage extends ApiBase {
// If no summary was given and we only undid one rev,
// use an autosummary
if ( is_null( $params['summary'] ) && $titleObj->getNextRevisionID( $undoafterRev->getID() ) == $params['undo'] ) {
- $params['summary'] = wfMsgForContent( 'undo-summary', $params['undo'], $undoRev->getUserText() );
+ $params['summary'] = wfMessage( 'undo-summary', $params['undo'], $undoRev->getUserText() )->inContentLanguage()->text();
}
}
@@ -181,7 +186,7 @@ class ApiEditPage extends ApiBase {
if ( !is_null( $params['summary'] ) ) {
$requestArray['wpSummary'] = $params['summary'];
}
-
+
if ( !is_null( $params['sectiontitle'] ) ) {
$requestArray['wpSectionTitle'] = $params['sectiontitle'];
}
@@ -282,9 +287,6 @@ class ApiEditPage extends ApiBase {
case EditPage::AS_SPAM_ERROR:
$this->dieUsageMsg( array( 'spamdetected', $result['spam'] ) );
- case EditPage::AS_FILTERING:
- $this->dieUsageMsg( 'filtered' );
-
case EditPage::AS_BLOCKED_PAGE_FOR_USER:
$this->dieUsageMsg( 'blockedtext' );
@@ -342,16 +344,11 @@ class ApiEditPage extends ApiBase {
$this->dieUsageMsg( 'summaryrequired' );
case EditPage::AS_END:
+ default:
// $status came from WikiPage::doEdit()
$errors = $status->getErrorsArray();
$this->dieUsageMsg( $errors[0] ); // TODO: Add new errors to message map
break;
- default:
- if ( is_string( $status->value ) && strlen( $status->value ) ) {
- $this->dieUsage( "An unknown return value was returned by Editpage. The code returned was \"{$status->value}\"" , $status->value );
- } else {
- $this->dieUsageMsg( array( 'unknownerror', $status->value ) );
- }
}
$apiResult->addValue( null, $this->getModuleName(), $r );
}
@@ -371,45 +368,48 @@ class ApiEditPage extends ApiBase {
public function getPossibleErrors() {
global $wgMaxArticleSize;
- return array_merge( parent::getPossibleErrors(), array(
- array( 'missingtext' ),
- array( 'invalidtitle', 'title' ),
- array( 'createonly-exists' ),
- array( 'nocreate-missing' ),
- array( 'nosuchrevid', 'undo' ),
- array( 'nosuchrevid', 'undoafter' ),
- array( 'revwrongpage', 'id', 'text' ),
- array( 'undo-failure' ),
- array( 'hashcheckfailed' ),
- array( 'hookaborted' ),
- array( 'noimageredirect-anon' ),
- array( 'noimageredirect-logged' ),
- array( 'spamdetected', 'spam' ),
- array( 'summaryrequired' ),
- array( 'filtered' ),
- array( 'blockedtext' ),
- array( 'contenttoobig', $wgMaxArticleSize ),
- array( 'noedit-anon' ),
- array( 'noedit' ),
- array( 'actionthrottledtext' ),
- array( 'wasdeleted' ),
- array( 'nocreate-loggedin' ),
- array( 'blankpage' ),
- array( 'editconflict' ),
- array( 'emptynewsection' ),
- array( 'unknownerror', 'retval' ),
- array( 'code' => 'nosuchsection', 'info' => 'There is no section section.' ),
- array( 'code' => 'invalidsection', 'info' => 'The section parameter must be set to an integer or \'new\'' ),
- array( 'customcssprotected' ),
- array( 'customjsprotected' ),
- ) );
+ return array_merge( parent::getPossibleErrors(),
+ $this->getTitleOrPageIdErrorMessage(),
+ array(
+ array( 'missingtext' ),
+ array( 'createonly-exists' ),
+ array( 'nocreate-missing' ),
+ array( 'nosuchrevid', 'undo' ),
+ array( 'nosuchrevid', 'undoafter' ),
+ array( 'revwrongpage', 'id', 'text' ),
+ array( 'undo-failure' ),
+ array( 'hashcheckfailed' ),
+ array( 'hookaborted' ),
+ array( 'noimageredirect-anon' ),
+ array( 'noimageredirect-logged' ),
+ array( 'spamdetected', 'spam' ),
+ array( 'summaryrequired' ),
+ array( 'blockedtext' ),
+ array( 'contenttoobig', $wgMaxArticleSize ),
+ array( 'noedit-anon' ),
+ array( 'noedit' ),
+ array( 'actionthrottledtext' ),
+ array( 'wasdeleted' ),
+ array( 'nocreate-loggedin' ),
+ array( 'blankpage' ),
+ array( 'editconflict' ),
+ array( 'emptynewsection' ),
+ array( 'unknownerror', 'retval' ),
+ array( 'code' => 'nosuchsection', 'info' => 'There is no section section.' ),
+ array( 'code' => 'invalidsection', 'info' => 'The section parameter must be set to an integer or \'new\'' ),
+ array( 'customcssprotected' ),
+ array( 'customjsprotected' ),
+ )
+ );
}
public function getAllowedParams() {
return array(
'title' => array(
ApiBase::PARAM_TYPE => 'string',
- ApiBase::PARAM_REQUIRED => true
+ ),
+ 'pageid' => array(
+ ApiBase::PARAM_TYPE => 'integer',
),
'section' => null,
'sectiontitle' => array(
@@ -417,7 +417,10 @@ class ApiEditPage extends ApiBase {
ApiBase::PARAM_REQUIRED => false,
),
'text' => null,
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'summary' => null,
'minor' => false,
'notminor' => false,
@@ -463,19 +466,20 @@ class ApiEditPage extends ApiBase {
public function getParamDescription() {
$p = $this->getModulePrefix();
return array(
- 'title' => 'Page title',
+ 'title' => "Title of the page you want to edit. Cannot be used together with {$p}pageid",
+ 'pageid' => "Page ID of the page you want to edit. Cannot be used together with {$p}title",
'section' => 'Section number. 0 for the top section, \'new\' for a new section',
'sectiontitle' => 'The title for a new section',
'text' => 'Page content',
'token' => array( 'Edit token. You can get one of these through prop=info.',
- 'The token should always be sent as the last parameter, or at least, after the text parameter'
+ "The token should always be sent as the last parameter, or at least, after the {$p}text parameter"
),
- 'summary' => 'Edit summary. Also section title when section=new',
+ 'summary' => "Edit summary. Also section title when {$p}section=new and {$p}sectiontitle is not set",
'minor' => 'Minor edit',
'notminor' => 'Non-minor edit',
'bot' => 'Mark this edit as bot',
'basetimestamp' => array( 'Timestamp of the base revision (obtained through prop=revisions&rvprop=timestamp).',
- 'Used to detect edit conflicts; leave unset to ignore conflicts.'
+ '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'
@@ -489,13 +493,49 @@ class ApiEditPage extends ApiBase {
'md5' => array( "The MD5 hash of the {$p}text parameter, or the {$p}prependtext and {$p}appendtext parameters concatenated.",
'If set, the edit won\'t be done unless the hash is correct' ),
'prependtext' => "Add this text to the beginning of the page. Overrides {$p}text",
- 'appendtext' => "Add this text to the end of the page. Overrides {$p}text",
+ 'appendtext' => array( "Add this text to the end of the page. Overrides {$p}text.",
+ "Use {$p}section=new to append a new section" ),
'undo' => "Undo this revision. Overrides {$p}text, {$p}prependtext and {$p}appendtext",
'undoafter' => 'Undo all revisions from undo to this one. If not set, just undo one revision',
'redirect' => 'Automatically resolve redirects',
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'new' => 'boolean',
+ 'result' => array(
+ ApiBase::PROP_TYPE => array(
+ 'Success',
+ 'Failure'
+ ),
+ ),
+ 'pageid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'title' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'nochange' => 'boolean',
+ 'oldrevid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'newrevid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'newtimestamp' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function needsToken() {
return true;
}
diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php
index d9eed60c..4fa03434 100644
--- a/includes/api/ApiEmailUser.php
+++ b/includes/api/ApiEmailUser.php
@@ -55,7 +55,7 @@ class ApiEmailUser extends ApiBase {
'Subject' => $params['subject'],
'CCMe' => $params['ccme'],
);
- $retval = SpecialEmailUser::submit( $data );
+ $retval = SpecialEmailUser::submit( $data, $this->getContext() );
if ( $retval instanceof Status ) {
// SpecialEmailUser sometimes returns a status
@@ -98,7 +98,10 @@ class ApiEmailUser extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'ccme' => false,
);
}
@@ -113,6 +116,23 @@ class ApiEmailUser extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'result' => array(
+ ApiBase::PROP_TYPE => array(
+ 'Success',
+ 'Failure'
+ ),
+ ),
+ 'message' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'Email a user.';
}
diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php
index d570534d..160f5b91 100644
--- a/includes/api/ApiExpandTemplates.php
+++ b/includes/api/ApiExpandTemplates.php
@@ -4,7 +4,7 @@
*
* Created on Oct 05, 2007
*
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -103,6 +103,14 @@ class ApiExpandTemplates extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ '*' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Expands all templates in wikitext';
}
diff --git a/includes/api/ApiFeedContributions.php b/includes/api/ApiFeedContributions.php
index 4e70bde2..1cf760ae 100644
--- a/includes/api/ApiFeedContributions.php
+++ b/includes/api/ApiFeedContributions.php
@@ -60,7 +60,7 @@ class ApiFeedContributions extends ApiBase {
$this->dieUsage( 'Size difference is disabled in Miser Mode', 'sizediffdisabled' );
}
- $msg = wfMsgForContent( 'Contributions' );
+ $msg = wfMessage( 'Contributions' )->inContentLanguage()->text();
$feedTitle = $wgSitename . ' - ' . $msg . ' [' . $wgLanguageCode . ']';
$feedUrl = SpecialPage::getTitleFor( 'Contributions', $params['user'] )->getFullURL();
@@ -96,7 +96,7 @@ class ApiFeedContributions extends ApiBase {
}
protected function feedItem( $row ) {
- $title = Title::MakeTitle( intval( $row->page_namespace ), $row->page_title );
+ $title = Title::makeTitle( intval( $row->page_namespace ), $row->page_title );
if( $title ) {
$date = $row->rev_timestamp;
$comments = $title->getTalkPage()->getFullURL();
@@ -129,7 +129,8 @@ class ApiFeedContributions extends ApiBase {
*/
protected function feedItemDesc( $revision ) {
if( $revision ) {
- return '<p>' . htmlspecialchars( $revision->getUserText() ) . wfMsgForContent( 'colon-separator' ) .
+ $msg = wfMessage( 'colon-separator' )->inContentLanguage()->text();
+ return '<p>' . htmlspecialchars( $revision->getUserText() ) . $msg .
htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) .
"</p>\n<hr />\n<div>" .
nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>";
@@ -150,8 +151,7 @@ class ApiFeedContributions extends ApiBase {
ApiBase::PARAM_REQUIRED => true,
),
'namespace' => array(
- ApiBase::PARAM_TYPE => 'namespace',
- ApiBase::PARAM_ISMULTI => true
+ ApiBase::PARAM_TYPE => 'namespace'
),
'year' => array(
ApiBase::PARAM_TYPE => 'integer'
diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php
index eee8fa19..6ccb02fe 100644
--- a/includes/api/ApiFeedWatchlist.php
+++ b/includes/api/ApiFeedWatchlist.php
@@ -4,7 +4,7 @@
*
* Created on Oct 13, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -117,7 +117,7 @@ class ApiFeedWatchlist extends ApiBase {
$feedItems[] = $this->createFeedItem( $info );
}
- $msg = wfMsgForContent( 'watchlist' );
+ $msg = wfMessage( 'watchlist' )->inContentLanguage()->text();
$feedTitle = $wgSitename . ' - ' . $msg . ' [' . $wgLanguageCode . ']';
$feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL();
@@ -131,11 +131,12 @@ class ApiFeedWatchlist extends ApiBase {
// Error results should not be cached
$this->getMain()->setCacheMaxAge( 0 );
- $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent( 'watchlist' ) . ' [' . $wgLanguageCode . ']';
+ $feedTitle = $wgSitename . ' - Error - ' . wfMessage( 'watchlist' )->inContentLanguage()->text() . ' [' . $wgLanguageCode . ']';
$feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL();
$feedFormat = isset( $params['feedformat'] ) ? $params['feedformat'] : 'rss';
- $feed = new $wgFeedClasses[$feedFormat] ( $feedTitle, htmlspecialchars( wfMsgForContent( 'watchlist' ) ), $feedUrl );
+ $msg = wfMessage( 'watchlist' )->inContentLanguage()->escaped();
+ $feed = new $wgFeedClasses[$feedFormat] ( $feedTitle, $msg, $feedUrl );
if ( $e instanceof UsageException ) {
$errorCode = $e->getCodeString();
diff --git a/includes/api/ApiFileRevert.php b/includes/api/ApiFileRevert.php
index 7ef1da0a..83d078d2 100644
--- a/includes/api/ApiFileRevert.php
+++ b/includes/api/ApiFileRevert.php
@@ -71,9 +71,10 @@ class ApiFileRevert extends ApiBase {
* @param $user User The user to check.
*/
protected function checkPermissions( $user ) {
+ $title = $this->file->getTitle();
$permissionErrors = array_merge(
- $this->file->getTitle()->getUserPermissionsErrors( 'edit' , $user ),
- $this->file->getTitle()->getUserPermissionsErrors( 'upload' , $user )
+ $title->getUserPermissionsErrors( 'edit' , $user ),
+ $title->getUserPermissionsErrors( 'upload' , $user )
);
if ( $permissionErrors ) {
@@ -91,15 +92,17 @@ class ApiFileRevert extends ApiBase {
if ( is_null( $title ) ) {
$this->dieUsageMsg( array( 'invalidtitle', $this->params['filename'] ) );
}
+ $localRepo = RepoGroup::singleton()->getLocalRepo();
+
// Check if the file really exists
- $this->file = wfLocalFile( $title );
+ $this->file = $localRepo->newFile( $title );
if ( !$this->file->exists() ) {
$this->dieUsageMsg( 'notanarticle' );
}
// Check if the archivename is valid for this file
$this->archiveName = $this->params['archivename'];
- $oldFile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $this->archiveName );
+ $oldFile = $localRepo->newFromArchiveName( $title, $this->archiveName );
if ( !$oldFile->exists() ) {
$this->dieUsageMsg( 'filerevert-badversion' );
}
@@ -126,21 +129,38 @@ class ApiFileRevert extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true,
),
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
);
}
public function getParamDescription() {
- $params = array(
- 'filename' => 'Target filename',
+ return array(
+ 'filename' => 'Target filename without the File: prefix',
'token' => 'Edit token. You can get one of these through prop=info',
'comment' => 'Upload comment',
'archivename' => 'Archive name of the revision to revert to',
);
+ }
- return $params;
-
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'result' => array(
+ ApiBase::PROP_TYPE => array(
+ 'Success',
+ 'Failure'
+ )
+ ),
+ 'errors' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
}
public function getDescription() {
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index 1eee717a..8ad9b8ca 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -169,8 +169,10 @@ abstract class ApiFormatBase extends ApiBase {
<br />
<small>
You are looking at the HTML representation of the <?php echo( $this->mFormat ); ?> format.<br />
-HTML is good for debugging, but probably is not suitable for your application.<br />
-See <a href='https://www.mediawiki.org/wiki/API'>complete documentation</a>, or
+HTML is good for debugging, but is unsuitable for application use.<br />
+Specify the format parameter to change the output format.<br />
+To see the non HTML representation of the <?php echo( $this->mFormat ); ?> format, set format=<?php echo( strtolower( $this->mFormat ) ); ?>.<br />
+See the <a href='https://www.mediawiki.org/wiki/API'>complete documentation</a>, or
<a href='<?php echo( $script ); ?>'>API help</a> for more information.
</small>
<?php
@@ -264,11 +266,12 @@ See <a href='https://www.mediawiki.org/wiki/API'>complete documentation</a>, or
$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 );
+ $text = str_replace( '&lt;', '<span style="color:blue;">&lt;', $text );
+ $text = str_replace( '&gt;', '&gt;</span>', $text );
// identify URLs
$protos = wfUrlProtocolsWithoutProtRel();
// This regex hacks around bug 13218 (&quot; included in the URL)
- $text = preg_replace( "#(($protos).*?)(&quot;)?([ \\'\"<>\n]|&lt;|&gt;|&quot;)#", '<a href="\\1">\\1</a>\\3\\4', $text );
+ $text = preg_replace( "#(((?i)$protos).*?)(&quot;)?([ \\'\"<>\n]|&lt;|&gt;|&quot;)#", '<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 ) {
@@ -329,7 +332,7 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
*/
public static function setResult( $result, $feed, $feedItems ) {
// Store output in the Result data.
- // This way we can check during execution if any error has occured
+ // This way we can check during execution if any error has occurred
// Disable size checking for this because we can't continue
// cleanly; size checking would cause more problems than it'd
// solve
@@ -374,7 +377,7 @@ class ApiFormatFeedWrapper extends ApiFormatBase {
}
$feed->outFooter();
} else {
- // Error has occured, print something useful
+ // Error has occurred, print something useful
ApiBase::dieDebug( __METHOD__, 'Invalid feed class/item' );
}
}
diff --git a/includes/api/ApiFormatDbg.php b/includes/api/ApiFormatDbg.php
index 92619f76..3d2a39ca 100644
--- a/includes/api/ApiFormatDbg.php
+++ b/includes/api/ApiFormatDbg.php
@@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<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
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index e728d057..acbc7d3b 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php
index 60552c40..fac2ca58 100644
--- a/includes/api/ApiFormatPhp.php
+++ b/includes/api/ApiFormatPhp.php
@@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/includes/api/ApiFormatRaw.php b/includes/api/ApiFormatRaw.php
index db81aacd..184f0a34 100644
--- a/includes/api/ApiFormatRaw.php
+++ b/includes/api/ApiFormatRaw.php
@@ -4,7 +4,7 @@
*
* Created on Feb 2, 2009
*
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<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
diff --git a/includes/api/ApiFormatTxt.php b/includes/api/ApiFormatTxt.php
index e26b82b0..71414593 100644
--- a/includes/api/ApiFormatTxt.php
+++ b/includes/api/ApiFormatTxt.php
@@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<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
diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php
index 1bc9d025..65056e44 100644
--- a/includes/api/ApiFormatWddx.php
+++ b/includes/api/ApiFormatWddx.php
@@ -4,7 +4,7 @@
*
* Created on Oct 22, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php
index 8f4abc15..5ccf1859 100644
--- a/includes/api/ApiFormatXml.php
+++ b/includes/api/ApiFormatXml.php
@@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -83,16 +83,40 @@ class ApiFormatXml extends ApiFormatBase {
/**
* 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'].
- * Example: name='root', value = array( '_element'=>'page', 'x', 'y', 'z') creates <root> <page>x</page> <page>y</page> <page>z</page> </root>
+ * If array contains a key '_element', then the code assumes that ALL
+ * other keys are not important and replaces them with the
+ * value['_element'].
+ *
+ * @par Example:
+ * @verbatim
+ * name='root', value = array( '_element'=>'page', 'x', 'y', 'z')
+ * @endverbatim
+ * creates:
+ * @verbatim
+ * <root> <page>x</page> <page>y</page> <page>z</page> </root>
+ * @endverbatim
+ *
+ * If any of the array's element key is '*', then the code treats all
+ * other key->value pairs as attributes, and the value['*'] as the
+ * element's content.
+ *
+ * @par Example:
+ * @verbatim
+ * name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10)
+ * @endverbatim
+ * creates:
+ * @verbatim
+ * <root lang='en' id='10'>text</root>
+ * @endverbatim
*
- * If any of the array's element key is '*', then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content.
- * Example: name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10) creates <root lang='en' id='10'>text</root>
+ * Finally neither key is found, all keys become element names, and values
+ * become element content.
*
- * If neither key is found, all keys become element names, and values become element content.
- * The method is recursive, so the same rules apply to any sub-arrays.
+ * @note The method is recursive, so the same rules apply to any
+ * sub-arrays.
*
* @param $elemName
* @param $elemValue
@@ -215,7 +239,8 @@ class ApiFormatXml extends ApiFormatBase {
public function getParamDescription() {
return array(
'xmldoublequote' => 'If specified, double quotes all attributes and content',
- 'xslt' => 'If specified, adds <xslt> as stylesheet',
+ 'xslt' => 'If specified, adds <xslt> as stylesheet. This should be a wiki page '
+ . 'in the MediaWiki namespace whose page name ends with ".xsl"',
'includexmlnamespace' => 'If specified, adds an XML namespace'
);
}
diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php
index dbcdb21c..730ad8ea 100644
--- a/includes/api/ApiFormatYaml.php
+++ b/includes/api/ApiFormatYaml.php
@@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php
index 97da786b..2b5de21a 100644
--- a/includes/api/ApiHelp.php
+++ b/includes/api/ApiHelp.php
@@ -4,7 +4,7 @@
*
* Created on Sep 6, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/includes/api/ApiImport.php b/includes/api/ApiImport.php
index ade9f1f3..637c1fff 100644
--- a/includes/api/ApiImport.php
+++ b/includes/api/ApiImport.php
@@ -4,7 +4,7 @@
*
* Created on Feb 4, 2009
*
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<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
@@ -68,6 +68,12 @@ class ApiImport extends ApiBase {
if ( isset( $params['namespace'] ) ) {
$importer->setTargetNamespace( $params['namespace'] );
}
+ if ( isset( $params['rootpage'] ) ) {
+ $statusRootPage = $importer->setTargetRootPage( $params['rootpage'] );
+ if( !$statusRootPage->isGood() ) {
+ $this->dieUsageMsg( $statusRootPage->getErrorsArray() );
+ }
+ }
$reporter = new ApiImportReporter(
$importer,
$isUpload,
@@ -98,7 +104,10 @@ class ApiImport extends ApiBase {
public function getAllowedParams() {
global $wgImportSources;
return array(
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'summary' => null,
'xml' => null,
'interwikisource' => array(
@@ -109,7 +118,8 @@ class ApiImport extends ApiBase {
'templates' => false,
'namespace' => array(
ApiBase::PARAM_TYPE => 'namespace'
- )
+ ),
+ 'rootpage' => null,
);
}
@@ -123,6 +133,18 @@ class ApiImport extends ApiBase {
'fullhistory' => 'For interwiki imports: import the full history, not just the current version',
'templates' => 'For interwiki imports: import all included templates as well',
'namespace' => 'For interwiki imports: import to this namespace',
+ 'rootpage' => 'Import as subpage of this page',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ ApiBase::PROP_LIST => true,
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string',
+ 'revisions' => 'integer'
+ )
);
}
@@ -141,6 +163,8 @@ class ApiImport extends ApiBase {
array( 'cantimport-upload' ),
array( 'import-unknownerror', 'source' ),
array( 'import-unknownerror', 'result' ),
+ array( 'import-rootpage-nosubpage', 'namespace' ),
+ array( 'import-rootpage-invalid' ),
) );
}
@@ -186,8 +210,16 @@ class ApiImportReporter extends ImportReporter {
function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) {
// Add a result entry
$r = array();
- ApiQueryBase::addTitleInfo( $r, $title );
- $r['revisions'] = intval( $successCount );
+
+ if ( $title === null ) {
+ # Invalid or non-importable title
+ $r['title'] = $pageInfo['title'];
+ $r['invalid'] = '';
+ } else {
+ ApiQueryBase::addTitleInfo( $r, $title );
+ $r['revisions'] = intval( $successCount );
+ }
+
$this->mResultArr[] = $r;
// Piggyback on the parent to do the logging
diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php
index aa570cbc..1f91fe92 100644
--- a/includes/api/ApiLogin.php
+++ b/includes/api/ApiLogin.php
@@ -4,7 +4,7 @@
*
* Created on Sep 19, 2006
*
- * Copyright © 2006-2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
+ * Copyright © 2006-2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com",
* Daniel Cannon (cannon dot danielc at gmail dot com)
*
* This program is free software; you can redistribute it and/or modify
@@ -79,6 +79,8 @@ class ApiLogin extends ApiBase {
$user->setOption( 'rememberpassword', 1 );
$user->setCookies( $this->getRequest() );
+ ApiQueryInfo::resetTokenCache();
+
// Run hooks.
// @todo FIXME: Split back and frontend from this hook.
// @todo FIXME: This hook should be placed in the backend
@@ -181,6 +183,66 @@ class ApiLogin extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'result' => array(
+ ApiBase::PROP_TYPE => array(
+ 'Success',
+ 'NeedToken',
+ 'WrongToken',
+ 'NoName',
+ 'Illegal',
+ 'WrongPluginPass',
+ 'NotExists',
+ 'WrongPass',
+ 'EmptyPass',
+ 'CreateBlocked',
+ 'Throttled',
+ 'Blocked',
+ 'Aborted'
+ )
+ ),
+ 'lguserid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'lgusername' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'lgtoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'cookieprefix' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'sessionid' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'token' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'details' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'wait' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'reason' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return array(
'Log in and get the authentication tokens. ',
diff --git a/includes/api/ApiLogout.php b/includes/api/ApiLogout.php
index 81a054a6..b2f634d0 100644
--- a/includes/api/ApiLogout.php
+++ b/includes/api/ApiLogout.php
@@ -4,7 +4,7 @@
*
* Created on Jan 4, 2008
*
- * Copyright © 2008 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
+ * Copyright © 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
@@ -54,6 +54,10 @@ class ApiLogout extends ApiBase {
return array();
}
+ public function getResultProperties() {
+ return array();
+ }
+
public function getParamDescription() {
return array();
}
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index fa95cfca..35febd95 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -4,7 +4,7 @@
*
* Created on Sep 4, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -61,9 +61,11 @@ class ApiMain extends ApiBase {
'paraminfo' => 'ApiParamInfo',
'rsd' => 'ApiRsd',
'compare' => 'ApiComparePages',
+ 'tokens' => 'ApiTokens',
// Write modules
'purge' => 'ApiPurge',
+ 'setnotificationtimestamp' => 'ApiSetNotificationTimestamp',
'rollback' => 'ApiRollback',
'delete' => 'ApiDelete',
'undelete' => 'ApiUndelete',
@@ -79,6 +81,7 @@ class ApiMain extends ApiBase {
'patrol' => 'ApiPatrol',
'import' => 'ApiImport',
'userrights' => 'ApiUserrights',
+ 'options' => 'ApiOptions',
);
/**
@@ -352,6 +355,12 @@ class ApiMain extends ApiBase {
* have been accumulated, and replace it with an error message and a help screen.
*/
protected function executeActionWithErrorHandling() {
+ // Verify the CORS header before executing the action
+ if ( !$this->handleCORS() ) {
+ // handleCORS() has sent a 403, abort
+ return;
+ }
+
// In case an error occurs during data output,
// clear the output buffer and print just the error information
ob_start();
@@ -359,8 +368,11 @@ class ApiMain extends ApiBase {
try {
$this->executeAction();
} catch ( Exception $e ) {
+ // Allow extra cleanup and logging
+ wfRunHooks( 'ApiMain::onException', array( $this, $e ) );
+
// Log it
- if ( $e instanceof MWException ) {
+ if ( !( $e instanceof UsageException ) ) {
wfDebugLog( 'exception', $e->getLogMessage() );
}
@@ -384,7 +396,7 @@ class ApiMain extends ApiBase {
// Reset and print just the error message
ob_clean();
- // If the error occured during printing, do a printer->profileOut()
+ // If the error occurred during printing, do a printer->profileOut()
$this->mPrinter->safeProfileOut();
$this->printResult( true );
}
@@ -400,9 +412,101 @@ class ApiMain extends ApiBase {
ob_end_flush();
}
+ /**
+ * Check the &origin= query parameter against the Origin: HTTP header and respond appropriately.
+ *
+ * If no origin parameter is present, nothing happens.
+ * If an origin parameter is present but doesn't match the Origin header, a 403 status code
+ * is set and false is returned.
+ * If the parameter and the header do match, the header is checked against $wgCrossSiteAJAXdomains
+ * and $wgCrossSiteAJAXdomainExceptions, and if the origin qualifies, the appropriate CORS
+ * headers are set.
+ *
+ * @return bool False if the caller should abort (403 case), true otherwise (all other cases)
+ */
+ protected function handleCORS() {
+ global $wgCrossSiteAJAXdomains, $wgCrossSiteAJAXdomainExceptions;
+
+ $originParam = $this->getParameter( 'origin' ); // defaults to null
+ if ( $originParam === null ) {
+ // No origin parameter, nothing to do
+ return true;
+ }
+
+ $request = $this->getRequest();
+ $response = $request->response();
+ // Origin: header is a space-separated list of origins, check all of them
+ $originHeader = $request->getHeader( 'Origin' );
+ if ( $originHeader === false ) {
+ $origins = array();
+ } else {
+ $origins = explode( ' ', $originHeader );
+ }
+ if ( !in_array( $originParam, $origins ) ) {
+ // origin parameter set but incorrect
+ // Send a 403 response
+ $message = HttpStatus::getMessage( 403 );
+ $response->header( "HTTP/1.1 403 $message", true, 403 );
+ $response->header( 'Cache-Control: no-cache' );
+ echo "'origin' parameter does not match Origin header\n";
+ return false;
+ }
+ if ( self::matchOrigin( $originParam, $wgCrossSiteAJAXdomains, $wgCrossSiteAJAXdomainExceptions ) ) {
+ $response->header( "Access-Control-Allow-Origin: $originParam" );
+ $response->header( 'Access-Control-Allow-Credentials: true' );
+ $this->getOutput()->addVaryHeader( 'Origin' );
+ }
+ return true;
+ }
+
+ /**
+ * Attempt to match an Origin header against a set of rules and a set of exceptions
+ * @param $value string Origin header
+ * @param $rules array Set of wildcard rules
+ * @param $exceptions array Set of wildcard rules
+ * @return bool True if $value matches a rule in $rules and doesn't match any rules in $exceptions, false otherwise
+ */
+ protected static function matchOrigin( $value, $rules, $exceptions ) {
+ foreach ( $rules as $rule ) {
+ if ( preg_match( self::wildcardToRegex( $rule ), $value ) ) {
+ // Rule matches, check exceptions
+ foreach ( $exceptions as $exc ) {
+ if ( preg_match( self::wildcardToRegex( $exc ), $value ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Helper function to convert wildcard string into a regex
+ * '*' => '.*?'
+ * '?' => '.'
+ *
+ * @param $wildcard string String with wildcards
+ * @return string Regular expression
+ */
+ protected static function wildcardToRegex( $wildcard ) {
+ $wildcard = preg_quote( $wildcard, '/' );
+ $wildcard = str_replace(
+ array( '\*', '\?' ),
+ array( '.*?', '.' ),
+ $wildcard
+ );
+ return "/https?:\/\/$wildcard/";
+ }
+
protected function sendCacheHeaders() {
global $wgUseXVO, $wgVaryOnXFP;
$response = $this->getRequest()->response();
+ $out = $this->getOutput();
+
+ if ( $wgVaryOnXFP ) {
+ $out->addVaryHeader( 'X-Forwarded-Proto' );
+ }
if ( $this->mCacheMode == 'private' ) {
$response->header( 'Cache-Control: private' );
@@ -410,13 +514,9 @@ class ApiMain extends ApiBase {
}
if ( $this->mCacheMode == 'anon-public-user-private' ) {
- $xfp = $wgVaryOnXFP ? ', X-Forwarded-Proto' : '';
- $response->header( 'Vary: Accept-Encoding, Cookie' . $xfp );
+ $out->addVaryHeader( 'Cookie' );
+ $response->header( $out->getVaryHeader() );
if ( $wgUseXVO ) {
- $out = $this->getOutput();
- if ( $wgVaryOnXFP ) {
- $out->addVaryHeader( 'X-Forwarded-Proto' );
- }
$response->header( $out->getXVO() );
if ( $out->haveCacheVaryCookies() ) {
// Logged in, mark this request private
@@ -433,12 +533,9 @@ class ApiMain extends ApiBase {
}
// Send public headers
- if ( $wgVaryOnXFP ) {
- $response->header( 'Vary: Accept-Encoding, X-Forwarded-Proto' );
- if ( $wgUseXVO ) {
- // Bleeeeegh. Our header setting system sucks
- $response->header( 'X-Vary-Options: Accept-Encoding;list-contains=gzip, X-Forwarded-Proto' );
- }
+ $response->header( $out->getVaryHeader() );
+ if ( $wgUseXVO ) {
+ $response->header( $out->getXVO() );
}
// If nobody called setCacheMaxAge(), use the (s)maxage parameters
@@ -605,7 +702,7 @@ class ApiMain extends ApiBase {
if ( !isset( $moduleParams['token'] ) ) {
$this->dieUsageMsg( array( 'missingparam', 'token' ) );
} else {
- if ( !$this->getUser()->matchEditToken( $moduleParams['token'], $salt, $this->getRequest() ) ) {
+ if ( !$this->getUser()->matchEditToken( $moduleParams['token'], $salt, $this->getContext()->getRequest() ) ) {
$this->dieUsageMsg( 'sessionfailure' );
}
}
@@ -664,6 +761,12 @@ class ApiMain extends ApiBase {
$this->dieReadOnly();
}
}
+
+ // Allow extensions to stop execution for arbitrary reasons.
+ $message = false;
+ if( !wfRunHooks( 'ApiCheckCanExecute', array( $module, $user, &$message ) ) ) {
+ $this->dieUsageMsg( $message );
+ }
}
/**
@@ -713,6 +816,9 @@ class ApiMain extends ApiBase {
$module->profileOut();
if ( !$this->mInternalMode ) {
+ //append Debug information
+ MWDebug::appendDebugInfoToApiResult( $this->getContext(), $this->getResult() );
+
// Print result data
$this->printResult( false );
}
@@ -779,6 +885,7 @@ class ApiMain extends ApiBase {
),
'requestid' => null,
'servedby' => false,
+ 'origin' => null,
);
}
@@ -804,6 +911,12 @@ class ApiMain extends ApiBase {
'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',
'servedby' => 'Include the hostname that served the request in the results. Unconditionally shown on error',
+ 'origin' => array(
+ 'When accessing the API using a cross-domain AJAX request (CORS), set this to the originating domain.',
+ 'This must match one of the origins in the Origin: header exactly, so it has to be set to something like http://en.wikipedia.org or https://meta.wikimedia.org .',
+ 'If this parameter does not match the Origin: header, a 403 response will be returned.',
+ 'If this parameter matches the Origin: header and the origin is whitelisted, an Access-Control-Allow-Origin header will be set.',
+ ),
);
}
@@ -871,11 +984,11 @@ class ApiMain extends ApiBase {
protected function getCredits() {
return array(
'API developers:',
- ' Roan Kattouw <Firstname>.<Lastname>@gmail.com (lead developer Sep 2007-present)',
+ ' Roan Kattouw "<Firstname>.<Lastname>@gmail.com" (lead developer Sep 2007-present)',
' Victor Vasiliev - vasilvv at gee mail dot com',
' Bryan Tong Minh - bryan . tongminh @ gmail . com',
' Sam Reed - sam @ reedyboy . net',
- ' Yuri Astrakhan <Firstname><Lastname>@gmail.com (creator, lead developer Sep 2006-Sep 2007)',
+ ' Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007)',
'',
'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org',
'or file a bug report at https://bugzilla.wikimedia.org/'
@@ -1061,11 +1174,21 @@ class ApiMain extends ApiBase {
*
* @ingroup API
*/
-class UsageException extends Exception {
+class UsageException extends MWException {
private $mCodestr;
+
+ /**
+ * @var null|array
+ */
private $mExtraData;
+ /**
+ * @param $message string
+ * @param $codestr string
+ * @param $code int
+ * @param $extradata array|null
+ */
public function __construct( $message, $codestr, $code = 0, $extradata = null ) {
parent::__construct( $message, $code );
$this->mCodestr = $codestr;
diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php
index f0a25e4a..9d73562b 100644
--- a/includes/api/ApiMove.php
+++ b/includes/api/ApiMove.php
@@ -4,7 +4,7 @@
*
* Created on Oct 31, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -37,9 +37,6 @@ class ApiMove extends ApiBase {
public function execute() {
$user = $this->getUser();
$params = $this->extractRequestParams();
- if ( is_null( $params['reason'] ) ) {
- $params['reason'] = '';
- }
$this->requireOnlyOneParameter( $params, 'from', 'fromid' );
@@ -78,6 +75,7 @@ class ApiMove extends ApiBase {
}
// Move the page
+ $toTitleExists = $toTitle->exists();
$retval = $fromTitle->moveTo( $toTitle, true, $params['reason'], !$params['noredirect'] );
if ( $retval !== true ) {
$this->dieUsageMsg( reset( $retval ) );
@@ -87,13 +85,20 @@ class ApiMove extends ApiBase {
if ( !$params['noredirect'] || !$user->isAllowed( 'suppressredirect' ) ) {
$r['redirectcreated'] = '';
}
+ if( $toTitleExists ) {
+ $r['moveoverredirect'] = '';
+ }
// Move the talk page
if ( $params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage() ) {
+ $toTalkExists = $toTalk->exists();
$retval = $fromTalk->moveTo( $toTalk, true, $params['reason'], !$params['noredirect'] );
if ( $retval === true ) {
$r['talkfrom'] = $fromTalk->getPrefixedText();
$r['talkto'] = $toTalk->getPrefixedText();
+ if( $toTalkExists ) {
+ $r['talkmoveoverredirect'] = '';
+ }
} else {
// We're not gonna dieUsage() on failure, since we already changed something
$parsed = $this->parseMsg( reset( $retval ) );
@@ -180,8 +185,11 @@ class ApiMove extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
- 'token' => null,
- 'reason' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'reason' => '',
'movetalk' => false,
'movesubpages' => false,
'noredirect' => false,
@@ -213,7 +221,7 @@ class ApiMove extends ApiBase {
'fromid' => "Page ID of the page you want to move. Cannot be used together with {$p}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)',
+ 'reason' => 'Reason for the move',
'movetalk' => 'Move the talk page, if it exists',
'movesubpages' => 'Move subpages, if applicable',
'noredirect' => 'Don\'t create a redirect',
@@ -224,6 +232,35 @@ class ApiMove extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'from' => 'string',
+ 'to' => 'string',
+ 'reason' => 'string',
+ 'redirectcreated' => 'boolean',
+ 'moveoverredirect' => 'boolean',
+ 'talkfrom' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'talkto' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'talkmoveoverredirect' => 'boolean',
+ 'talkmove-error-code' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'talkmove-error-info' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'Move a page';
}
diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php
index 0727cffd..ef562741 100644
--- a/includes/api/ApiOpenSearch.php
+++ b/includes/api/ApiOpenSearch.php
@@ -4,7 +4,7 @@
*
* Created on Oct 13, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -45,7 +45,7 @@ class ApiOpenSearch extends ApiBase {
$namespaces = $params['namespace'];
$suggest = $params['suggest'];
- // MWSuggest or similar hit
+ // Some script that was loaded regardless of wgEnableOpenSearchSuggest, likely cached.
if ( $suggest && !$wgEnableOpenSearchSuggest ) {
$searches = array();
} else {
diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php
new file mode 100644
index 00000000..265c2ccb
--- /dev/null
+++ b/includes/api/ApiOptions.php
@@ -0,0 +1,183 @@
+<?php
+/**
+ *
+ *
+ * Created on Apr 15, 2012
+ *
+ * Copyright © 2012 Szymon Świerkosz beau@adres.pl
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+* API module that facilitates the changing of user's preferences.
+* Requires API write mode to be enabled.
+*
+ * @ingroup API
+ */
+class ApiOptions extends ApiBase {
+
+ public function __construct( $main, $action ) {
+ parent::__construct( $main, $action );
+ }
+
+ /**
+ * Changes preferences of the current user.
+ */
+ public function execute() {
+ $user = $this->getUser();
+
+ if ( $user->isAnon() ) {
+ $this->dieUsage( 'Anonymous users cannot change preferences', 'notloggedin' );
+ }
+
+ $params = $this->extractRequestParams();
+ $changed = false;
+
+ if ( isset( $params['optionvalue'] ) && !isset( $params['optionname'] ) ) {
+ $this->dieUsageMsg( array( 'missingparam', 'optionname' ) );
+ }
+
+ if ( $params['reset'] ) {
+ $user->resetOptions();
+ $changed = true;
+ }
+
+ $changes = array();
+ if ( count( $params['change'] ) ) {
+ foreach ( $params['change'] as $entry ) {
+ $array = explode( '=', $entry, 2 );
+ $changes[$array[0]] = isset( $array[1] ) ? $array[1] : null;
+ }
+ }
+ if ( isset( $params['optionname'] ) ) {
+ $newValue = isset( $params['optionvalue'] ) ? $params['optionvalue'] : null;
+ $changes[$params['optionname']] = $newValue;
+ }
+ if ( !$changed && !count( $changes ) ) {
+ $this->dieUsage( 'No changes were requested', 'nochanges' );
+ }
+
+ $prefs = Preferences::getPreferences( $user, $this->getContext() );
+ foreach ( $changes as $key => $value ) {
+ if ( !isset( $prefs[$key] ) ) {
+ $this->setWarning( "Not a valid preference: $key" );
+ continue;
+ }
+ $field = HTMLForm::loadInputFromParameters( $key, $prefs[$key] );
+ $validation = $field->validate( $value, $user->getOptions() );
+ if ( $validation === true ) {
+ $user->setOption( $key, $value );
+ $changed = true;
+ } else {
+ $this->setWarning( "Validation error for '$key': $validation" );
+ }
+ }
+
+ if ( $changed ) {
+ // Commit changes
+ $user->saveSettings();
+ }
+
+ $this->getResult()->addValue( null, $this->getModuleName(), 'success' );
+ }
+
+ public function mustBePosted() {
+ return true;
+ }
+
+ public function isWriteMode() {
+ return true;
+ }
+
+ public function getAllowedParams() {
+ return array(
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'reset' => false,
+ 'change' => array(
+ ApiBase::PARAM_ISMULTI => true,
+ ),
+ 'optionname' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ),
+ 'optionvalue' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ),
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ '*' => array(
+ ApiBase::PROP_TYPE => array(
+ 'success'
+ )
+ )
+ )
+ );
+ }
+
+ public function getParamDescription() {
+ return array(
+ 'token' => 'An options token previously obtained through the action=tokens',
+ 'reset' => 'Resets all preferences to the site defaults',
+ 'change' => 'List of changes, formatted name=value (e.g. skin=vector), value cannot contain pipe characters',
+ 'optionname' => 'A name of a option which should have an optionvalue set',
+ 'optionvalue' => 'A value of the option specified by the optionname, can contain pipe characters',
+ );
+ }
+
+ public function getDescription() {
+ return 'Change preferences of the current user';
+ }
+
+ public function getPossibleErrors() {
+ return array_merge( parent::getPossibleErrors(), array(
+ array( 'code' => 'notloggedin', 'info' => 'Anonymous users cannot change preferences' ),
+ array( 'code' => 'nochanges', 'info' => 'No changes were requested' ),
+ ) );
+ }
+
+ public function needsToken() {
+ return true;
+ }
+
+ public function getTokenSalt() {
+ return '';
+ }
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Options';
+ }
+
+ public function getExamples() {
+ return array(
+ 'api.php?action=options&reset=&token=123ABC',
+ 'api.php?action=options&change=skin=vector|hideminor=1&token=123ABC',
+ 'api.php?action=options&reset=&change=skin=monobook&optionname=nickname&optionvalue=[[User:Beau|Beau]]%20([[User_talk:Beau|talk]])&token=123ABC',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id$';
+ }
+}
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index 7b84c473..0f5be6b2 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -4,7 +4,7 @@
*
* Created on Sep 24, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -52,7 +52,7 @@ class ApiPageSet extends ApiQueryBase {
/**
* Constructor
- * @param $query ApiQueryBase
+ * @param $query ApiBase
* @param $resolveRedirects bool Whether redirects should be resolved
* @param $convertTitles bool
*/
@@ -266,8 +266,8 @@ class ApiPageSet extends ApiQueryBase {
}
/**
- * Returns the number of revisions (requested with revids= parameter)\
- * @return int
+ * Returns the number of revisions (requested with revids= parameter).
+ * @return int Number of revisions.
*/
public function getRevisionCount() {
return count( $this->getRevisionIDs() );
@@ -342,7 +342,7 @@ class ApiPageSet extends ApiQueryBase {
/**
* Populate this PageSet from a rowset returned from the database
- * @param $db Database object
+ * @param $db DatabaseBase object
* @param $queryResult ResultWrapper Query result object
*/
public function populateFromQueryResult( $db, $queryResult ) {
@@ -367,7 +367,7 @@ class ApiPageSet extends ApiQueryBase {
*/
public function processDbRow( $row ) {
// Store Title object in various data structures
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $title = Title::newFromRow( $row );
$pageId = intval( $row->page_id );
$this->mAllPages[$row->page_namespace][$row->page_title] = $pageId;
@@ -481,6 +481,7 @@ class ApiPageSet extends ApiQueryBase {
ApiBase::dieDebug( __METHOD__, 'Missing $processTitles parameter when $remaining is provided' );
}
+ $usernames = array();
if ( $res ) {
foreach ( $res as $row ) {
$pageId = intval( $row->page_id );
@@ -496,6 +497,11 @@ class ApiPageSet extends ApiQueryBase {
// Store any extra fields requested by modules
$this->processDbRow( $row );
+
+ // Need gender information
+ if( MWNamespace::hasGenderDistinction( $row->page_namespace ) ) {
+ $usernames[] = $row->page_title;
+ }
}
}
@@ -510,6 +516,11 @@ class ApiPageSet extends ApiQueryBase {
$this->mMissingTitles[$this->mFakePageId] = $title;
$this->mFakePageId--;
$this->mTitles[] = $title;
+
+ // need gender information
+ if( MWNamespace::hasGenderDistinction( $ns ) ) {
+ $usernames[] = $dbkey;
+ }
}
}
} else {
@@ -521,6 +532,10 @@ class ApiPageSet extends ApiQueryBase {
}
}
}
+
+ // Get gender information
+ $genderCache = GenderCache::singleton();
+ $genderCache->doQuery( $usernames, __METHOD__ );
}
/**
@@ -664,6 +679,9 @@ class ApiPageSet extends ApiQueryBase {
* @return LinkBatch
*/
private function processTitlesArray( $titles ) {
+ $genderCache = GenderCache::singleton();
+ $genderCache->doTitlesArray( $titles, __METHOD__ );
+
$linkBatch = new LinkBatch();
foreach ( $titles as $title ) {
diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php
index f2263476..343a2625 100644
--- a/includes/api/ApiParamInfo.php
+++ b/includes/api/ApiParamInfo.php
@@ -4,7 +4,7 @@
*
* Created on Dec 01, 2007
*
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<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
@@ -251,6 +251,62 @@ class ApiParamInfo extends ApiBase {
}
$result->setIndexedTagName( $retval['parameters'], 'param' );
+ $props = $obj->getFinalResultProperties();
+ $listResult = null;
+ if ( $props !== false ) {
+ $retval['props'] = array();
+
+ foreach ( $props as $prop => $properties ) {
+ $propResult = array();
+ if ( $prop == ApiBase::PROP_LIST ) {
+ $listResult = $properties;
+ continue;
+ }
+ if ( $prop != ApiBase::PROP_ROOT ) {
+ $propResult['name'] = $prop;
+ }
+ $propResult['properties'] = array();
+
+ foreach ( $properties as $name => $p ) {
+ $propertyResult = array();
+
+ $propertyResult['name'] = $name;
+
+ if ( !is_array( $p ) ) {
+ $p = array( ApiBase::PROP_TYPE => $p );
+ }
+
+ $propertyResult['type'] = $p[ApiBase::PROP_TYPE];
+
+ if ( is_array( $propertyResult['type'] ) ) {
+ $propertyResult['type'] = array_values( $propertyResult['type'] );
+ $result->setIndexedTagName( $propertyResult['type'], 't' );
+ }
+
+ $nullable = null;
+ if ( isset( $p[ApiBase::PROP_NULLABLE] ) ) {
+ $nullable = $p[ApiBase::PROP_NULLABLE];
+ }
+
+ if ( $nullable === true ) {
+ $propertyResult['nullable'] = '';
+ }
+
+ $propResult['properties'][] = $propertyResult;
+ }
+
+ $result->setIndexedTagName( $propResult['properties'], 'property' );
+ $retval['props'][] = $propResult;
+ }
+
+ // default is true for query modules, false for other modules, overriden by ApiBase::PROP_LIST
+ if ( $listResult === true || ( $listResult !== false && $obj instanceof ApiQueryBase ) ) {
+ $retval['listresult'] = '';
+ }
+
+ $result->setIndexedTagName( $retval['props'], 'prop' );
+ }
+
// Errors
$retval['errors'] = $this->parseErrors( $obj->getPossibleErrors() );
$result->setIndexedTagName( $retval['errors'], 'error' );
diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php
index 893491b9..db6e2bb8 100644
--- a/includes/api/ApiParse.php
+++ b/includes/api/ApiParse.php
@@ -2,7 +2,7 @@
/**
* Created on Dec 01, 2007
*
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -59,19 +59,15 @@ class ApiParse extends ApiBase {
// The parser needs $wgTitle to be set, apparently the
// $title parameter in Parser::parse isn't enough *sigh*
// TODO: Does this still need $wgTitle?
- global $wgParser, $wgTitle, $wgLang;
+ global $wgParser, $wgTitle;
// Currently unnecessary, code to act as a safeguard against any change in current behaviour of uselang breaks
$oldLang = null;
- if ( isset( $params['uselang'] ) && $params['uselang'] != $wgLang->getCode() ) {
- $oldLang = $wgLang; // Backup wgLang
- $wgLang = Language::factory( $params['uselang'] );
+ if ( isset( $params['uselang'] ) && $params['uselang'] != $this->getContext()->getLanguage()->getCode() ) {
+ $oldLang = $this->getContext()->getLanguage(); // Backup language
+ $this->getContext()->setLanguage( Language::factory( $params['uselang'] ) );
}
- $popts = ParserOptions::newFromContext( $this->getContext() );
- $popts->setTidy( true );
- $popts->enableLimitReport( !$params['disablepp'] );
-
$redirValues = null;
// Return result
@@ -89,13 +85,15 @@ class ApiParse extends ApiBase {
}
$titleObj = $rev->getTitle();
-
$wgTitle = $titleObj;
+ $pageObj = WikiPage::factory( $titleObj );
+ $popts = $pageObj->makeParserOptions( $this->getContext() );
+ $popts->enableLimitReport( !$params['disablepp'] );
// If for some reason the "oldid" is actually the current revision, it may be cached
if ( $titleObj->getLatestRevID() === intval( $oldid ) ) {
// May get from/save to parser cache
- $p_result = $this->getParsedSectionOrText( $titleObj, $popts, $pageid,
+ $p_result = $this->getParsedSectionOrText( $pageObj, $popts, $pageid,
isset( $prop['wikitext'] ) ) ;
} else { // This is an old revision, so get the text differently
$this->text = $rev->getText( Revision::FOR_THIS_USER, $this->getUser() );
@@ -129,32 +127,26 @@ class ApiParse extends ApiBase {
foreach ( (array)$redirValues as $r ) {
$to = $r['to'];
}
- $titleObj = Title::newFromText( $to );
- } else {
- if ( !is_null ( $pageid ) ) {
- $reqParams['pageids'] = $pageid;
- $titleObj = Title::newFromID( $pageid );
- } else { // $page
- $to = $page;
- $titleObj = Title::newFromText( $to );
- }
- }
- if ( !is_null ( $pageid ) ) {
- if ( !$titleObj ) {
- // Still throw nosuchpageid error if pageid was provided
- $this->dieUsageMsg( array( 'nosuchpageid', $pageid ) );
- }
- } elseif ( !$titleObj || !$titleObj->exists() ) {
- $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
+ $pageParams = array( 'title' => $to );
+ } elseif ( !is_null( $pageid ) ) {
+ $pageParams = array( 'pageid' => $pageid );
+ } else { // $page
+ $pageParams = array( 'title' => $page );
}
+
+ $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
+ $titleObj = $pageObj->getTitle();
$wgTitle = $titleObj;
if ( isset( $prop['revid'] ) ) {
- $oldid = $titleObj->getLatestRevID();
+ $oldid = $pageObj->getLatest();
}
+ $popts = $pageObj->makeParserOptions( $this->getContext() );
+ $popts->enableLimitReport( !$params['disablepp'] );
+
// Potentially cached
- $p_result = $this->getParsedSectionOrText( $titleObj, $popts, $pageid,
+ $p_result = $this->getParsedSectionOrText( $pageObj, $popts, $pageid,
isset( $prop['wikitext'] ) ) ;
}
} else { // Not $oldid, $pageid, $page. Hence based on $text
@@ -168,6 +160,10 @@ class ApiParse extends ApiBase {
$this->dieUsageMsg( array( 'invalidtitle', $title ) );
}
$wgTitle = $titleObj;
+ $pageObj = WikiPage::factory( $titleObj );
+
+ $popts = $pageObj->makeParserOptions( $this->getContext() );
+ $popts->enableLimitReport( !$params['disablepp'] );
if ( $this->section !== false ) {
$this->text = $this->getSectionText( $this->text, $titleObj->getText() );
@@ -285,6 +281,21 @@ class ApiParse extends ApiBase {
$result->setContent( $result_array['psttext'], $this->pstText );
}
}
+ if ( isset( $prop['properties'] ) ) {
+ $result_array['properties'] = $this->formatProperties( $p_result->getProperties() );
+ }
+
+ if ( $params['generatexml'] ) {
+ $wgParser->startExternalParse( $titleObj, $popts, OT_PREPROCESS );
+ $dom = $wgParser->preprocessToDom( $this->text );
+ if ( is_callable( array( $dom, 'saveXML' ) ) ) {
+ $xml = $dom->saveXML();
+ } else {
+ $xml = $dom->__toString();
+ }
+ $result_array['parsetree'] = array();
+ $result->setContent( $result_array['parsetree'], $xml );
+ }
$result_mapping = array(
'redirects' => 'r',
@@ -297,37 +308,39 @@ class ApiParse extends ApiBase {
'iwlinks' => 'iw',
'sections' => 's',
'headitems' => 'hi',
+ 'properties' => 'pp',
);
$this->setIndexedTagNames( $result_array, $result_mapping );
$result->addValue( null, $this->getModuleName(), $result_array );
if ( !is_null( $oldLang ) ) {
- $wgLang = $oldLang; // Reset $wgLang to $oldLang
+ $this->getContext()->setLanguage( $oldLang ); // Reset language to $oldLang
}
}
/**
- * @param $titleObj Title
+ * @param $page WikiPage
* @param $popts ParserOptions
* @param $pageId Int
* @param $getWikitext Bool
* @return ParserOutput
*/
- private function getParsedSectionOrText( $titleObj, $popts, $pageId = null, $getWikitext = false ) {
+ private function getParsedSectionOrText( $page, $popts, $pageId = null, $getWikitext = false ) {
global $wgParser;
- $page = WikiPage::factory( $titleObj );
-
if ( $this->section !== false ) {
$this->text = $this->getSectionText( $page->getRawText(), !is_null( $pageId )
- ? 'page id ' . $pageId : $titleObj->getText() );
+ ? 'page id ' . $pageId : $page->getTitle()->getPrefixedText() );
// Not cached (save or load)
- return $wgParser->parse( $this->text, $titleObj, $popts );
+ return $wgParser->parse( $this->text, $page->getTitle(), $popts );
} else {
// Try the parser cache first
// getParserOutput will save to Parser cache if able
$pout = $page->getParserOutput( $popts );
+ if ( !$pout ) {
+ $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
+ }
if ( $getWikitext ) {
$this->text = $page->getRawText();
}
@@ -394,19 +407,19 @@ class ApiParse extends ApiBase {
return '';
}
- $s = htmlspecialchars( wfMsg( 'otherlanguages' ) . wfMsg( 'colon-separator' ) );
+ $s = htmlspecialchars( wfMessage( 'otherlanguages' )->text() . wfMessage( 'colon-separator' )->text() );
$langs = array();
foreach ( $languages as $l ) {
$nt = Title::newFromText( $l );
- $text = $wgContLang->getLanguageName( $nt->getInterwiki() );
+ $text = Language::fetchLanguageName( $nt->getInterwiki() );
$langs[] = Html::element( 'a',
array( 'href' => $nt->getFullURL(), 'title' => $nt->getText(), 'class' => "external" ),
$text == '' ? $l : $text );
}
- $s .= implode( htmlspecialchars( wfMsgExt( 'pipe-separator', 'escapenoentities' ) ), $langs );
+ $s .= implode( wfMessage( 'pipe-separator' )->escaped(), $langs );
if ( $wgContLang->isRTL() ) {
$s = Html::rawElement( 'span', array( 'dir' => "LTR" ), $s );
@@ -461,6 +474,17 @@ class ApiParse extends ApiBase {
return $result;
}
+ private function formatProperties( $properties ) {
+ $result = array();
+ foreach ( $properties as $name => $value ) {
+ $entry = array();
+ $entry['name'] = $name;
+ $this->getResult()->setContent( $entry, $value );
+ $result[] = $entry;
+ }
+ return $result;
+ }
+
private function formatCss( $css ) {
$result = array();
foreach ( $css as $file => $link ) {
@@ -496,7 +520,7 @@ class ApiParse extends ApiBase {
ApiBase::PARAM_TYPE => 'integer',
),
'prop' => array(
- ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid|displaytitle',
+ ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid|displaytitle|iwlinks|properties',
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => array(
'text',
@@ -515,6 +539,7 @@ class ApiParse extends ApiBase {
'headhtml',
'iwlinks',
'wikitext',
+ 'properties',
)
),
'pst' => false,
@@ -522,6 +547,7 @@ class ApiParse extends ApiBase {
'uselang' => null,
'section' => null,
'disablepp' => false,
+ 'generatexml' => false,
);
}
@@ -553,6 +579,7 @@ class ApiParse extends ApiBase {
' headhtml - Gives parsed <head> of the page',
' iwlinks - Gives interwiki links in the parsed wikitext',
' wikitext - Gives the original wikitext that was parsed',
+ ' properties - Gives various properties defined in the parsed wikitext',
),
'pst' => array(
'Do a pre-save transform on the input before parsing it',
@@ -565,11 +592,15 @@ class ApiParse extends ApiBase {
'uselang' => 'Which language to parse the request in',
'section' => 'Only retrieve the content of this section number',
'disablepp' => 'Disable the PP Report from the parser output',
+ 'generatexml' => 'Generate XML parse tree',
);
}
public function getDescription() {
- return 'Parses wikitext and returns parser output';
+ return array(
+ 'Parses wikitext and returns parser output',
+ 'See the various prop-Modules of action=query to get information from the current version of a page',
+ );
}
public function getPossibleErrors() {
diff --git a/includes/api/ApiPatrol.php b/includes/api/ApiPatrol.php
index 1332f263..cb5e081a 100644
--- a/includes/api/ApiPatrol.php
+++ b/includes/api/ApiPatrol.php
@@ -65,7 +65,10 @@ class ApiPatrol extends ApiBase {
public function getAllowedParams() {
return array(
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'rcid' => array(
ApiBase::PARAM_TYPE => 'integer',
ApiBase::PARAM_REQUIRED => true
@@ -80,6 +83,16 @@ class ApiPatrol extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'rcid' => 'integer',
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Patrol a page or revision';
}
diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php
index fb225d86..b3ca67e6 100644
--- a/includes/api/ApiProtect.php
+++ b/includes/api/ApiProtect.php
@@ -4,7 +4,7 @@
*
* Created on Sep 1, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -37,10 +37,8 @@ class ApiProtect extends ApiBase {
global $wgRestrictionLevels;
$params = $this->extractRequestParams();
- $titleObj = Title::newFromText( $params['title'] );
- if ( !$titleObj ) {
- $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
- }
+ $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' );
+ $titleObj = $pageObj->getTitle();
$errors = $titleObj->getUserPermissionsErrors( 'protect', $this->getUser() );
if ( $errors ) {
@@ -58,7 +56,7 @@ class ApiProtect extends ApiBase {
}
$restrictionTypes = $titleObj->getRestrictionTypes();
- $dbr = wfGetDB( DB_SLAVE );
+ $db = $this->getDB();
$protections = array();
$expiryarray = array();
@@ -82,7 +80,7 @@ class ApiProtect extends ApiBase {
}
if ( in_array( $expiry[$i], array( 'infinite', 'indefinite', 'never' ) ) ) {
- $expiryarray[$p[0]] = $dbr->getInfinity();
+ $expiryarray[$p[0]] = $db->getInfinity();
} else {
$exp = strtotime( $expiry[$i] );
if ( $exp < 0 || !$exp ) {
@@ -96,7 +94,7 @@ class ApiProtect extends ApiBase {
$expiryarray[$p[0]] = $exp;
}
$resultProtections[] = array( $p[0] => $protections[$p[0]],
- 'expiry' => ( $expiryarray[$p[0]] == $dbr->getInfinity() ?
+ 'expiry' => ( $expiryarray[$p[0]] == $db->getInfinity() ?
'infinite' :
wfTimestamp( TS_ISO_8601, $expiryarray[$p[0]] ) ) );
}
@@ -106,7 +104,6 @@ class ApiProtect extends ApiBase {
$watch = $params['watch'] ? 'watch' : $params['watchlist'];
$this->setWatch( $watch, $titleObj );
- $pageObj = WikiPage::factory( $titleObj );
$status = $pageObj->doUpdateRestrictions( $protections, $expiryarray, $cascade, $params['reason'], $this->getUser() );
if ( !$status->isOK() ) {
@@ -138,9 +135,14 @@ class ApiProtect extends ApiBase {
return array(
'title' => array(
ApiBase::PARAM_TYPE => 'string',
+ ),
+ 'pageid' => array(
+ ApiBase::PARAM_TYPE => 'integer',
+ ),
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
- 'token' => null,
'protections' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_REQUIRED => true,
@@ -169,13 +171,15 @@ class ApiProtect extends ApiBase {
}
public function getParamDescription() {
+ $p = $this->getModulePrefix();
return array(
- 'title' => 'Title of the page you want to (un)protect',
+ 'title' => "Title of the page you want to (un)protect. Cannot be used together with {$p}pageid",
+ 'pageid' => "ID of the page you want to (un)protect. Cannot be used together with {$p}title",
'token' => 'A protect token previously retrieved through prop=info',
- 'protections' => 'Pipe-separated list of protection levels, formatted action=group (e.g. edit=sysop)',
+ 'protections' => 'List of protection levels, formatted action=group (e.g. edit=sysop)',
'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)',
+ 'reason' => 'Reason for (un)protecting',
'cascade' => array( 'Enable cascading protection (i.e. protect pages included in this page)',
'Ignored if not all protection levels are \'sysop\' or \'protect\'' ),
'watch' => 'If set, add the page being (un)protected to your watchlist',
@@ -183,21 +187,33 @@ class ApiProtect extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'title' => 'string',
+ 'reason' => 'string',
+ 'cascade' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
return 'Change the protection level of a page';
}
public function getPossibleErrors() {
- return array_merge( parent::getPossibleErrors(), array(
- array( 'invalidtitle', 'title' ),
- array( 'toofewexpiries', 'noofexpiries', 'noofprotections' ),
- array( 'create-titleexists' ),
- array( 'missingtitle-createonly' ),
- array( 'protect-invalidaction', 'action' ),
- array( 'protect-invalidlevel', 'level' ),
- array( 'invalidexpiry', 'expiry' ),
- array( 'pastexpiry', 'expiry' ),
- ) );
+ return array_merge( parent::getPossibleErrors(),
+ $this->getTitleOrPageIdErrorMessage(),
+ array(
+ array( 'toofewexpiries', 'noofexpiries', 'noofprotections' ),
+ array( 'create-titleexists' ),
+ array( 'missingtitle-createonly' ),
+ array( 'protect-invalidaction', 'action' ),
+ array( 'protect-invalidlevel', 'level' ),
+ array( 'invalidexpiry', 'expiry' ),
+ array( 'pastexpiry', 'expiry' ),
+ )
+ );
}
public function needsToken() {
diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php
index 9e9320fb..9fedaf1b 100644
--- a/includes/api/ApiPurge.php
+++ b/includes/api/ApiPurge.php
@@ -88,13 +88,13 @@ class ApiPurge extends ApiBase {
if ( !$user->pingLimiter() ) {
global $wgParser, $wgEnableParserCache;
- $popts = ParserOptions::newFromContext( $this->getContext() );
+ $popts = $page->makeParserOptions( 'canonical' );
$p_result = $wgParser->parse( $page->getRawText(), $title, $popts,
true, true, $page->getLatest() );
# Update the links tables
- $u = new LinksUpdate( $title, $p_result );
- $u->doUpdate();
+ $updates = $p_result->getSecondaryDataUpdates( $title );
+ DataUpdate::runUpdates( $updates );
$r['linkupdate'] = '';
@@ -103,7 +103,8 @@ class ApiPurge extends ApiBase {
$pcache->save( $p_result, $page, $popts );
}
} else {
- $this->setWarning( $this->parseMsg( array( 'actionthrottledtext' ) ) );
+ $error = $this->parseMsg( array( 'actionthrottledtext' ) );
+ $this->setWarning( $error['info'] );
$forceLinkUpdate = false;
}
}
@@ -133,6 +134,34 @@ class ApiPurge extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ ApiBase::PROP_LIST => true,
+ '' => array(
+ 'ns' => array(
+ ApiBase::PROP_TYPE => 'namespace',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'title' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'pageid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'revid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'invalid' => 'boolean',
+ 'missing' => 'boolean',
+ 'purged' => 'boolean',
+ 'linkupdate' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
return array( 'Purge the cache for the given titles.',
'Requires a POST request if the user is not logged in.'
@@ -143,7 +172,6 @@ class ApiPurge extends ApiBase {
$psModule = new ApiPageSet( $this );
return array_merge(
parent::getPossibleErrors(),
- array( array( 'cantpurge' ), ),
$psModule->getPossibleErrors()
);
}
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index cd54a7da..554aae5a 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -4,7 +4,7 @@
*
* Created on Sep 7, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,55 +47,55 @@ class ApiQuery extends ApiBase {
private $params, $redirects, $convertTitles, $iwUrl;
private $mQueryPropModules = array(
+ 'categories' => 'ApiQueryCategories',
+ 'categoryinfo' => 'ApiQueryCategoryInfo',
+ 'duplicatefiles' => 'ApiQueryDuplicateFiles',
+ 'extlinks' => 'ApiQueryExternalLinks',
+ 'images' => 'ApiQueryImages',
+ 'imageinfo' => 'ApiQueryImageInfo',
'info' => 'ApiQueryInfo',
- 'revisions' => 'ApiQueryRevisions',
'links' => 'ApiQueryLinks',
'iwlinks' => 'ApiQueryIWLinks',
'langlinks' => 'ApiQueryLangLinks',
- 'images' => 'ApiQueryImages',
- 'imageinfo' => 'ApiQueryImageInfo',
+ 'pageprops' => 'ApiQueryPageProps',
+ 'revisions' => 'ApiQueryRevisions',
'stashimageinfo' => 'ApiQueryStashImageInfo',
'templates' => 'ApiQueryLinks',
- 'categories' => 'ApiQueryCategories',
- 'extlinks' => 'ApiQueryExternalLinks',
- 'categoryinfo' => 'ApiQueryCategoryInfo',
- 'duplicatefiles' => 'ApiQueryDuplicateFiles',
- 'pageprops' => 'ApiQueryPageProps',
);
private $mQueryListModules = array(
- 'allimages' => 'ApiQueryAllimages',
- 'allpages' => 'ApiQueryAllpages',
- 'alllinks' => 'ApiQueryAllLinks',
'allcategories' => 'ApiQueryAllCategories',
+ 'allimages' => 'ApiQueryAllImages',
+ 'alllinks' => 'ApiQueryAllLinks',
+ 'allpages' => 'ApiQueryAllPages',
'allusers' => 'ApiQueryAllUsers',
'backlinks' => 'ApiQueryBacklinks',
'blocks' => 'ApiQueryBlocks',
'categorymembers' => 'ApiQueryCategoryMembers',
'deletedrevs' => 'ApiQueryDeletedrevs',
'embeddedin' => 'ApiQueryBacklinks',
+ 'exturlusage' => 'ApiQueryExtLinksUsage',
'filearchive' => 'ApiQueryFilearchive',
'imageusage' => 'ApiQueryBacklinks',
'iwbacklinks' => 'ApiQueryIWBacklinks',
'langbacklinks' => 'ApiQueryLangBacklinks',
'logevents' => 'ApiQueryLogEvents',
+ 'protectedtitles' => 'ApiQueryProtectedTitles',
+ 'querypage' => 'ApiQueryQueryPage',
+ 'random' => 'ApiQueryRandom',
'recentchanges' => 'ApiQueryRecentChanges',
'search' => 'ApiQuerySearch',
'tags' => 'ApiQueryTags',
'usercontribs' => 'ApiQueryContributions',
+ 'users' => 'ApiQueryUsers',
'watchlist' => 'ApiQueryWatchlist',
'watchlistraw' => 'ApiQueryWatchlistRaw',
- 'exturlusage' => 'ApiQueryExtLinksUsage',
- 'users' => 'ApiQueryUsers',
- 'random' => 'ApiQueryRandom',
- 'protectedtitles' => 'ApiQueryProtectedTitles',
- 'querypage' => 'ApiQueryQueryPage',
);
private $mQueryMetaModules = array(
+ 'allmessages' => 'ApiQueryAllMessages',
'siteinfo' => 'ApiQuerySiteinfo',
'userinfo' => 'ApiQueryUserInfo',
- 'allmessages' => 'ApiQueryAllmessages',
);
private $mSlaveDB = null;
@@ -103,11 +103,16 @@ class ApiQuery extends ApiBase {
protected $mAllowedGenerators = array();
+ /**
+ * @param $main ApiMain
+ * @param $action string
+ */
public function __construct( $main, $action ) {
parent::__construct( $main, $action );
// Allow custom modules to be added in LocalSettings.php
- global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules;
+ global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules,
+ $wgMemc, $wgAPICacheHelpTimeout;
self::appendUserModules( $this->mQueryPropModules, $wgAPIPropModules );
self::appendUserModules( $this->mQueryListModules, $wgAPIListModules );
self::appendUserModules( $this->mQueryMetaModules, $wgAPIMetaModules );
@@ -116,8 +121,22 @@ class ApiQuery extends ApiBase {
$this->mListModuleNames = array_keys( $this->mQueryListModules );
$this->mMetaModuleNames = array_keys( $this->mQueryMetaModules );
- $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' );
- $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' );
+ // Get array of query generators from cache if present
+ $key = wfMemcKey( 'apiquerygenerators', SpecialVersion::getVersion( 'nodb' ) );
+
+ if ( $wgAPICacheHelpTimeout > 0 ) {
+ $cached = $wgMemc->get( $key );
+ if ( $cached ) {
+ $this->mAllowedGenerators = $cached;
+ return;
+ }
+ }
+ $this->makeGeneratorList( $this->mQueryPropModules );
+ $this->makeGeneratorList( $this->mQueryListModules );
+
+ if ( $wgAPICacheHelpTimeout > 0 ) {
+ $wgMemc->set( $key, $this->mAllowedGenerators, $wgAPICacheHelpTimeout );
+ }
}
/**
@@ -135,7 +154,7 @@ class ApiQuery extends ApiBase {
/**
* Gets a default slave database connection object
- * @return Database
+ * @return DatabaseBase
*/
public function getDB() {
if ( !isset( $this->mSlaveDB ) ) {
@@ -154,7 +173,7 @@ class ApiQuery extends ApiBase {
* @param $name string Name to assign to the database connection
* @param $db int One of the DB_* constants
* @param $groups array Query groups
- * @return Database
+ * @return DatabaseBase
*/
public function getNamedDB( $name, $db, $groups ) {
if ( !array_key_exists( $name, $this->mNamedDB ) ) {
@@ -202,6 +221,9 @@ class ApiQuery extends ApiBase {
return null;
}
+ /**
+ * @return ApiFormatRaw|null
+ */
public function getCustomPrinter() {
// If &exportnowrap is set, use the raw formatter
if ( $this->getParameter( 'export' ) &&
@@ -258,6 +280,9 @@ class ApiQuery extends ApiBase {
$this->outputGeneralPageInfo();
// Execute all requested modules.
+ /**
+ * @var $module ApiQueryBase
+ */
foreach ( $modules as $module ) {
$params = $module->extractRequestParams();
$cacheMode = $this->mergeCacheMode(
@@ -303,6 +328,9 @@ class ApiQuery extends ApiBase {
*/
private function addCustomFldsToPageSet( $modules, $pageSet ) {
// Query all requested modules.
+ /**
+ * @var $module ApiQueryBase
+ */
foreach ( $modules as $module ) {
$module->requestExtraData( $pageSet );
}
@@ -384,6 +412,9 @@ class ApiQuery extends ApiBase {
// Show redirect information
$redirValues = array();
+ /**
+ * @var $titleTo Title
+ */
foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleTo ) {
$r = array(
'from' => strval( $titleStrFrom ),
@@ -602,7 +633,6 @@ class ApiQuery extends ApiBase {
// Make sure the internal object is empty
// (just in case a sub-module decides to optimize during instantiation)
$this->mPageSet = null;
- $this->mAllowedGenerators = array(); // Will be repopulated
$querySeparator = str_repeat( '--- ', 12 );
$moduleSeparator = str_repeat( '*** ', 14 );
@@ -614,8 +644,6 @@ class ApiQuery extends ApiBase {
$msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' );
$msg .= "\n\n$moduleSeparator Modules: continuation $moduleSeparator\n\n";
- // Perform the base call last because the $this->mAllowedGenerators
- // will be updated inside makeHelpMsgHelper()
// Use parent to make default message for the query module
$msg = parent::makeHelpMsg() . $msg;
@@ -643,7 +671,6 @@ class ApiQuery extends ApiBase {
$msg .= $msg2;
}
if ( $module instanceof ApiQueryGeneratorBase ) {
- $this->mAllowedGenerators[] = $moduleName;
$msg .= "Generator:\n This module may be used as a generator\n";
}
$moduleDescriptions[] = $msg;
@@ -653,6 +680,19 @@ class ApiQuery extends ApiBase {
}
/**
+ * Adds any classes that are a subclass of ApiQueryGeneratorBase
+ * to the allowed generator list
+ * @param $moduleList array()
+ */
+ private function makeGeneratorList( $moduleList ) {
+ foreach( $moduleList as $moduleName => $moduleClass ) {
+ if ( is_subclass_of( $moduleClass, 'ApiQueryGeneratorBase' ) ) {
+ $this->mAllowedGenerators[] = $moduleName;
+ }
+ }
+ }
+
+ /**
* Override to add extra parameters from PageSet
* @return string
*/
@@ -674,7 +714,7 @@ class ApiQuery extends ApiBase {
'NOTE: generator parameter names must be prefixed with a \'g\', see examples' ),
'redirects' => 'Automatically resolve redirects',
'converttitles' => array( "Convert titles to other variants if necessary. Only works if the wiki's content language supports variant conversion.",
- 'Languages that support variant conversion include gan, iu, kk, ku, shi, sr, tg, zh' ),
+ 'Languages that support variant conversion include ' . implode( ', ', LanguageConverter::$languagesWithVariants ) ),
'indexpageids' => 'Include an additional pageids section listing all returned page IDs',
'export' => 'Export the current revisions of all given or generated pages',
'exportnowrap' => 'Return the export XML without wrapping it in an XML result (same format as Special:Export). Can only be used with export',
diff --git a/includes/api/ApiQueryAllCategories.php b/includes/api/ApiQueryAllCategories.php
index 78367a45..4f4c77f0 100644
--- a/includes/api/ApiQueryAllCategories.php
+++ b/includes/api/ApiQueryAllCategories.php
@@ -4,7 +4,7 @@
*
* Created on December 12, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -58,6 +58,17 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
$this->addTables( 'category' );
$this->addFields( 'cat_title' );
+ if ( !is_null( $params['continue'] ) ) {
+ $cont = explode( '|', $params['continue'] );
+ if ( count( $cont ) != 1 ) {
+ $this->dieUsage( "Invalid continue param. You should pass the " .
+ "original value returned by the previous query", "_badcontinue" );
+ }
+ $op = $params['dir'] == 'descending' ? '<' : '>';
+ $cont_from = $db->addQuotes( $cont[0] );
+ $this->addWhere( "cat_title $op= $cont_from" );
+ }
+
$dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
$from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
$to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
@@ -65,14 +76,20 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
$min = $params['min'];
$max = $params['max'];
- $this->addWhereRange( 'cat_pages', $dir, $min, $max );
+ if ( $dir == 'newer' ) {
+ $this->addWhereRange( 'cat_pages', 'newer', $min, $max );
+ } else {
+ $this->addWhereRange( 'cat_pages', 'older', $max, $min);
+ }
+
if ( isset( $params['prefix'] ) ) {
$this->addWhere( 'cat_title' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) );
}
$this->addOption( 'LIMIT', $params['limit'] + 1 );
- $this->addOption( 'ORDER BY', 'cat_title' . ( $params['dir'] == 'descending' ? ' DESC' : '' ) );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ $this->addOption( 'ORDER BY', 'cat_title' . $sort );
$prop = array_flip( $params['prop'] );
$this->addFieldsIf( array( 'cat_pages', 'cat_subcats', 'cat_files' ), isset( $prop['size'] ) );
@@ -86,7 +103,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
'pp_page=page_id',
'pp_propname' => 'hiddencat' ) ),
) );
- $this->addFields( 'pp_propname AS cat_hidden' );
+ $this->addFields( array( 'cat_hidden' => 'pp_propname' ) );
}
$res = $this->select( __METHOD__ );
@@ -98,15 +115,14 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
foreach ( $res as $row ) {
if ( ++ $count > $params['limit'] ) {
// We've reached the one extra which shows that there are additional cats to be had. Stop here...
- // TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
+ $this->setContinueEnumParameter( 'continue', $row->cat_title );
break;
}
// Normalize titles
$titleObj = Title::makeTitle( NS_CATEGORY, $row->cat_title );
if ( !is_null( $resultPageSet ) ) {
- $pages[] = $titleObj->getPrefixedText();
+ $pages[] = $titleObj;
} else {
$item = array();
$result->setContent( $item, $titleObj->getText() );
@@ -121,7 +137,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
}
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $item );
if ( !$fit ) {
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
+ $this->setContinueEnumParameter( 'continue', $row->cat_title );
break;
}
}
@@ -137,6 +153,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
public function getAllowedParams() {
return array(
'from' => null,
+ 'continue' => null,
'to' => null,
'prefix' => null,
'dir' => array(
@@ -172,6 +189,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
public function getParamDescription() {
return array(
'from' => 'The category to start enumerating from',
+ 'continue' => 'When more results are available, use this to continue',
'to' => 'The category to stop enumerating at',
'prefix' => 'Search for all category titles that begin with this value',
'dir' => 'Direction to sort in',
@@ -186,10 +204,33 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ '*' => 'string'
+ ),
+ 'size' => array(
+ 'size' => 'integer',
+ 'pages' => 'integer',
+ 'files' => 'integer',
+ 'subcats' => 'integer'
+ ),
+ 'hidden' => array(
+ 'hidden' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
return 'Enumerate all categories';
}
+ public function getPossibleErrors() {
+ return array_merge( parent::getPossibleErrors(), array(
+ array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
+ ) );
+ }
+
public function getExamples() {
return array(
'api.php?action=query&list=allcategories&acprop=size',
diff --git a/includes/api/ApiQueryAllImages.php b/includes/api/ApiQueryAllImages.php
new file mode 100644
index 00000000..b562da8e
--- /dev/null
+++ b/includes/api/ApiQueryAllImages.php
@@ -0,0 +1,409 @@
+<?php
+
+/**
+ * API for MediaWiki 1.12+
+ *
+ * Created on Mar 16, 2008
+ *
+ * Copyright © 2008 Vasiliev Victor vasilvv@gmail.com,
+ * based on ApiQueryAllPages.php
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Query module to enumerate all available pages.
+ *
+ * @ingroup API
+ */
+class ApiQueryAllImages extends ApiQueryGeneratorBase {
+
+ protected $mRepo;
+
+ public function __construct( $query, $moduleName ) {
+ parent::__construct( $query, $moduleName, 'ai' );
+ $this->mRepo = RepoGroup::singleton()->getLocalRepo();
+ }
+
+ /**
+ * Override parent method to make sure to make sure the repo's DB is used
+ * which may not necesarilly be the same as the local DB.
+ *
+ * TODO: allow querying non-local repos.
+ * @return DatabaseBase
+ */
+ protected function getDB() {
+ return $this->mRepo->getSlaveDB();
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function getCacheMode( $params ) {
+ return 'public';
+ }
+
+ /**
+ * @param $resultPageSet ApiPageSet
+ * @return void
+ */
+ public function executeGenerator( $resultPageSet ) {
+ if ( $resultPageSet->isResolvingRedirects() ) {
+ $this->dieUsage( 'Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator', 'params' );
+ }
+
+ $this->run( $resultPageSet );
+ }
+
+ /**
+ * @param $resultPageSet ApiPageSet
+ * @return void
+ */
+ private function run( $resultPageSet = null ) {
+ $repo = $this->mRepo;
+ if ( !$repo instanceof LocalRepo ) {
+ $this->dieUsage( 'Local file repository does not support querying all images', 'unsupportedrepo' );
+ }
+
+ $prefix = $this->getModulePrefix();
+
+ $db = $this->getDB();
+
+ $params = $this->extractRequestParams();
+
+ // Table and return fields
+ $this->addTables( 'image' );
+
+ $prop = array_flip( $params['prop'] );
+ $this->addFields( LocalFile::selectFields() );
+
+ $dir = ( in_array( $params['dir'], array( 'descending', 'older' ) ) ? 'older' : 'newer' );
+
+ if ( $params['sort'] == 'name' ) {
+ // Check mutually exclusive params
+ $disallowed = array( 'start', 'end', 'user' );
+ foreach ( $disallowed as $pname ) {
+ if ( isset( $params[$pname] ) ) {
+ $this->dieUsage( "Parameter '{$prefix}{$pname}' can only be used with {$prefix}sort=timestamp", 'badparams' );
+ }
+ }
+ if ( $params['filterbots'] != 'all' ) {
+ $this->dieUsage( "Parameter '{$prefix}filterbots' can only be used with {$prefix}sort=timestamp", 'badparams' );
+ }
+
+ // Pagination
+ if ( !is_null( $params['continue'] ) ) {
+ $cont = explode( '|', $params['continue'] );
+ if ( count( $cont ) != 1 ) {
+ $this->dieUsage( 'Invalid continue param. You should pass the ' .
+ 'original value returned by the previous query', '_badcontinue' );
+ }
+ $op = ( $dir == 'older' ? '<' : '>' );
+ $cont_from = $db->addQuotes( $cont[0] );
+ $this->addWhere( "img_name $op= $cont_from" );
+ }
+
+ // Image filters
+ $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
+ $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
+ $this->addWhereRange( 'img_name', $dir, $from, $to );
+
+ if ( isset( $params['prefix'] ) ) {
+ $this->addWhere( 'img_name' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) );
+ }
+ } else {
+ // Check mutually exclusive params
+ $disallowed = array( 'from', 'to', 'prefix' );
+ foreach ( $disallowed as $pname ) {
+ if ( isset( $params[$pname] ) ) {
+ $this->dieUsage( "Parameter '{$prefix}{$pname}' can only be used with {$prefix}sort=name", 'badparams' );
+ }
+ }
+ if (!is_null( $params['user'] ) && $params['filterbots'] != 'all') {
+ // Since filterbots checks if each user has the bot right, it doesn't make sense to use it with user
+ $this->dieUsage( "Parameters 'user' and 'filterbots' cannot be used together", 'badparams' );
+ }
+
+ // Pagination
+ $this->addTimestampWhereRange( 'img_timestamp', $dir, $params['start'], $params['end'] );
+
+ // Image filters
+ if ( !is_null( $params['user'] ) ) {
+ $this->addWhereFld( 'img_user_text', $params['user'] );
+ }
+ if ( $params['filterbots'] != 'all' ) {
+ $this->addTables( 'user_groups' );
+ $groupCond = ( $params['filterbots'] == 'nobots' ? 'NULL': 'NOT NULL' );
+ $this->addWhere( "ug_group IS $groupCond" );
+ $this->addJoinConds( array( 'user_groups' => array(
+ 'LEFT JOIN',
+ array(
+ 'ug_group' => User::getGroupsWithPermission( 'bot' ),
+ 'ug_user = img_user'
+ )
+ ) ) );
+ }
+ }
+
+ // Filters not depending on sort
+ if ( isset( $params['minsize'] ) ) {
+ $this->addWhere( 'img_size>=' . intval( $params['minsize'] ) );
+ }
+
+ if ( isset( $params['maxsize'] ) ) {
+ $this->addWhere( 'img_size<=' . intval( $params['maxsize'] ) );
+ }
+
+ $sha1 = false;
+ if ( isset( $params['sha1'] ) ) {
+ if ( !$this->validateSha1Hash( $params['sha1'] ) ) {
+ $this->dieUsage( 'The SHA1 hash provided is not valid', 'invalidsha1hash' );
+ }
+ $sha1 = wfBaseConvert( $params['sha1'], 16, 36, 31 );
+ } elseif ( isset( $params['sha1base36'] ) ) {
+ $sha1 = $params['sha1base36'];
+ if ( !$this->validateSha1Base36Hash( $sha1 ) ) {
+ $this->dieUsage( 'The SHA1Base36 hash provided is not valid', 'invalidsha1base36hash' );
+ }
+ }
+ if ( $sha1 ) {
+ $this->addWhereFld( 'img_sha1', $sha1 );
+ }
+
+ if ( !is_null( $params['mime'] ) ) {
+ global $wgMiserMode;
+ if ( $wgMiserMode ) {
+ $this->dieUsage( 'MIME search disabled in Miser Mode', 'mimesearchdisabled' );
+ }
+
+ list( $major, $minor ) = File::splitMime( $params['mime'] );
+
+ $this->addWhereFld( 'img_major_mime', $major );
+ $this->addWhereFld( 'img_minor_mime', $minor );
+ }
+
+ $limit = $params['limit'];
+ $this->addOption( 'LIMIT', $limit + 1 );
+ $sort = ( $dir == 'older' ? ' DESC' : '' );
+ if ( $params['sort'] == 'timestamp' ) {
+ $this->addOption( 'ORDER BY', 'img_timestamp' . $sort );
+ if ( $params['filterbots'] == 'all' ) {
+ $this->addOption( 'USE INDEX', array( 'image' => 'img_timestamp' ) );
+ } else {
+ $this->addOption( 'USE INDEX', array( 'image' => 'img_usertext_timestamp' ) );
+ }
+ } else {
+ $this->addOption( 'ORDER BY', 'img_name' . $sort );
+ }
+
+ $res = $this->select( __METHOD__ );
+
+ $titles = array();
+ $count = 0;
+ $result = $this->getResult();
+ foreach ( $res as $row ) {
+ if ( ++ $count > $limit ) {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ if ( $params['sort'] == 'name' ) {
+ $this->setContinueEnumParameter( 'continue', $row->img_name );
+ } else {
+ $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->img_timestamp ) );
+ }
+ break;
+ }
+
+ if ( is_null( $resultPageSet ) ) {
+ $file = $repo->newFileFromRow( $row );
+ $info = array_merge( array( 'name' => $row->img_name ),
+ ApiQueryImageInfo::getInfo( $file, $prop, $result ) );
+ self::addTitleInfo( $info, $file->getTitle() );
+
+ $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $info );
+ if ( !$fit ) {
+ if ( $params['sort'] == 'name' ) {
+ $this->setContinueEnumParameter( 'continue', $row->img_name );
+ } else {
+ $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->img_timestamp ) );
+ }
+ break;
+ }
+ } else {
+ $titles[] = Title::makeTitle( NS_FILE, $row->img_name );
+ }
+ }
+
+ if ( is_null( $resultPageSet ) ) {
+ $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'img' );
+ } else {
+ $resultPageSet->populateFromTitles( $titles );
+ }
+ }
+
+ public function getAllowedParams() {
+ return array (
+ 'sort' => array(
+ ApiBase::PARAM_DFLT => 'name',
+ ApiBase::PARAM_TYPE => array(
+ 'name',
+ 'timestamp'
+ )
+ ),
+ 'dir' => array(
+ ApiBase::PARAM_DFLT => 'ascending',
+ ApiBase::PARAM_TYPE => array(
+ // sort=name
+ 'ascending',
+ 'descending',
+ // sort=timestamp
+ 'newer',
+ 'older',
+ )
+ ),
+ 'from' => null,
+ 'to' => null,
+ 'continue' => null,
+ 'start' => array(
+ ApiBase::PARAM_TYPE => 'timestamp'
+ ),
+ 'end' => array(
+ ApiBase::PARAM_TYPE => 'timestamp'
+ ),
+ 'prop' => array(
+ ApiBase::PARAM_TYPE => ApiQueryImageInfo::getPropertyNames( $this->propertyFilter ),
+ ApiBase::PARAM_DFLT => 'timestamp|url',
+ ApiBase::PARAM_ISMULTI => true
+ ),
+ 'prefix' => null,
+ 'minsize' => array(
+ ApiBase::PARAM_TYPE => 'integer',
+ ),
+ 'maxsize' => array(
+ ApiBase::PARAM_TYPE => 'integer',
+ ),
+ 'sha1' => null,
+ 'sha1base36' => null,
+ 'user' => array(
+ ApiBase::PARAM_TYPE => 'user'
+ ),
+ 'filterbots' => array(
+ ApiBase::PARAM_DFLT => 'all',
+ ApiBase::PARAM_TYPE => array(
+ 'all',
+ 'bots',
+ 'nobots'
+ )
+ ),
+ 'mime' => null,
+ '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
+ ),
+ );
+ }
+
+ public function getParamDescription() {
+ $p = $this->getModulePrefix();
+ return array(
+ 'sort' => 'Property to sort by',
+ 'dir' => 'The direction in which to list',
+ 'from' => "The image title to start enumerating from. Can only be used with {$p}sort=name",
+ 'to' => "The image title to stop enumerating at. Can only be used with {$p}sort=name",
+ 'continue' => 'When more results are available, use this to continue',
+ 'start' => "The timestamp to start enumerating from. Can only be used with {$p}sort=timestamp",
+ 'end' => "The timestamp to end enumerating. Can only be used with {$p}sort=timestamp",
+ 'prop' => ApiQueryImageInfo::getPropertyDescriptions( $this->propertyFilter ),
+ 'prefix' => "Search for all image titles that begin with this value. Can only be used with {$p}sort=name",
+ 'minsize' => 'Limit to images with at least this many bytes',
+ 'maxsize' => 'Limit to images with at most this many bytes',
+ 'sha1' => "SHA1 hash of image. Overrides {$p}sha1base36",
+ 'sha1base36' => 'SHA1 hash of image in base 36 (used in MediaWiki)',
+ 'user' => "Only return files uploaded by this user. Can only be used with {$p}sort=timestamp. Cannot be used together with {$p}filterbots",
+ 'filterbots' => "How to filter files uploaded by bots. Can only be used with {$p}sort=timestamp. Cannot be used together with {$p}user",
+ 'mime' => 'What MIME type to search for. e.g. image/jpeg. Disabled in Miser Mode',
+ 'limit' => 'How many images in total to return',
+ );
+ }
+
+ private $propertyFilter = array( 'archivename', 'thumbmime' );
+
+ public function getResultProperties() {
+ return array_merge(
+ array(
+ '' => array(
+ 'name' => 'string',
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
+ ),
+ ApiQueryImageInfo::getResultPropertiesFiltered( $this->propertyFilter )
+ );
+ }
+
+ public function getDescription() {
+ return 'Enumerate all images sequentially';
+ }
+
+ public function getPossibleErrors() {
+ $p = $this->getModulePrefix();
+ return array_merge( parent::getPossibleErrors(), array(
+ array( 'code' => 'params', 'info' => 'Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator' ),
+ array( 'code' => 'badparams', 'info' => "Parameter'{$p}start' can only be used with {$p}sort=timestamp" ),
+ array( 'code' => 'badparams', 'info' => "Parameter'{$p}end' can only be used with {$p}sort=timestamp" ),
+ array( 'code' => 'badparams', 'info' => "Parameter'{$p}user' can only be used with {$p}sort=timestamp" ),
+ array( 'code' => 'badparams', 'info' => "Parameter'{$p}filterbots' can only be used with {$p}sort=timestamp" ),
+ array( 'code' => 'badparams', 'info' => "Parameter'{$p}from' can only be used with {$p}sort=name" ),
+ array( 'code' => 'badparams', 'info' => "Parameter'{$p}to' can only be used with {$p}sort=name" ),
+ array( 'code' => 'badparams', 'info' => "Parameter'{$p}prefix' can only be used with {$p}sort=name" ),
+ array( 'code' => 'badparams', 'info' => "Parameters 'user' and 'filterbots' cannot be used together" ),
+ array( 'code' => 'unsupportedrepo', 'info' => 'Local file repository does not support querying all images' ),
+ array( 'code' => 'mimesearchdisabled', 'info' => 'MIME search disabled in Miser Mode' ),
+ array( 'code' => 'invalidsha1hash', 'info' => 'The SHA1 hash provided is not valid' ),
+ array( 'code' => 'invalidsha1base36hash', 'info' => 'The SHA1Base36 hash provided is not valid' ),
+ array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
+ ) );
+ }
+
+ public function getExamples() {
+ return array(
+ 'api.php?action=query&list=allimages&aifrom=B' => array(
+ 'Simple Use',
+ 'Show a list of files starting at the letter "B"',
+ ),
+ 'api.php?action=query&list=allimages&aiprop=user|timestamp|url&aisort=timestamp&aidir=older' => array(
+ 'Simple Use',
+ 'Show a list of recently uploaded files similar to Special:NewFiles',
+ ),
+ 'api.php?action=query&generator=allimages&gailimit=4&gaifrom=T&prop=imageinfo' => array(
+ 'Using as Generator',
+ 'Show info about 4 files starting at the letter "T"',
+ ),
+ );
+ }
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Allimages';
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id$';
+ }
+}
diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php
index 903f144f..da4840f0 100644
--- a/includes/api/ApiQueryAllLinks.php
+++ b/includes/api/ApiQueryAllLinks.php
@@ -4,7 +4,7 @@
*
* Created on July 7, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -76,17 +76,26 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$this->dieUsage( 'alcontinue and alfrom cannot be used together', 'params' );
}
if ( !is_null( $params['continue'] ) ) {
- $arr = explode( '|', $params['continue'] );
- if ( count( $arr ) != 2 ) {
- $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
+ $continueArr = explode( '|', $params['continue'] );
+ $op = $params['dir'] == 'descending' ? '<' : '>';
+ if ( $params['unique'] ) {
+ if ( count( $continueArr ) != 1 ) {
+ $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
+ }
+ $continueTitle = $db->addQuotes( $continueArr[0] );
+ $this->addWhere( "pl_title $op= $continueTitle" );
+ } else {
+ if ( count( $continueArr ) != 2 ) {
+ $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
+ }
+ $continueTitle = $db->addQuotes( $continueArr[0] );
+ $continueFrom = intval( $continueArr[1] );
+ $this->addWhere(
+ "pl_title $op $continueTitle OR " .
+ "(pl_title = $continueTitle AND " .
+ "pl_from $op= $continueFrom)"
+ );
}
- $from = $this->getDB()->strencode( $this->titleToKey( $arr[0] ) );
- $id = intval( $arr[1] );
- $this->addWhere(
- "pl_title > '$from' OR " .
- "(pl_title = '$from' AND " .
- "pl_from > $id)"
- );
}
$from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
@@ -104,9 +113,13 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$limit = $params['limit'];
$this->addOption( 'LIMIT', $limit + 1 );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ $orderBy = array();
+ $orderBy[] = 'pl_title' . $sort;
if ( !$params['unique'] ) {
- $this->addOption( 'ORDER BY', 'pl_title, pl_from' );
+ $orderBy[] = 'pl_from' . $sort;
}
+ $this->addOption( 'ORDER BY', $orderBy );
$res = $this->select( __METHOD__ );
@@ -116,11 +129,10 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
foreach ( $res as $row ) {
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
if ( $params['unique'] ) {
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
+ $this->setContinueEnumParameter( 'continue', $row->pl_title );
} else {
- $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
+ $this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
}
break;
}
@@ -137,9 +149,9 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
if ( !$fit ) {
if ( $params['unique'] ) {
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
+ $this->setContinueEnumParameter( 'continue', $row->pl_title );
} else {
- $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
+ $this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
}
break;
}
@@ -180,7 +192,14 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
ApiBase::PARAM_MIN => 1,
ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
- )
+ ),
+ 'dir' => array(
+ ApiBase::PARAM_DFLT => 'ascending',
+ ApiBase::PARAM_TYPE => array(
+ 'ascending',
+ 'descending'
+ )
+ ),
);
}
@@ -199,6 +218,19 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
'namespace' => 'The namespace to enumerate',
'limit' => 'How many total links to return',
'continue' => 'When more results are available, use this to continue',
+ 'dir' => 'The direction in which to list',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ 'ids' => array(
+ 'fromid' => 'integer'
+ ),
+ 'title' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
);
}
diff --git a/includes/api/ApiQueryAllmessages.php b/includes/api/ApiQueryAllMessages.php
index 44774927..f5e1146b 100644
--- a/includes/api/ApiQueryAllmessages.php
+++ b/includes/api/ApiQueryAllMessages.php
@@ -4,7 +4,7 @@
*
* Created on Dec 1, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@
*
* @ingroup API
*/
-class ApiQueryAllmessages extends ApiQueryBase {
+class ApiQueryAllMessages extends ApiQueryBase {
public function __construct( $query, $moduleName ) {
parent::__construct( $query, $moduleName, 'am' );
@@ -256,6 +256,27 @@ class ApiQueryAllmessages extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'name' => 'string',
+ 'customised' => 'boolean',
+ 'missing' => 'boolean',
+ '*' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'default' => array(
+ 'defaultmissing' => 'boolean',
+ 'default' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'Return messages from this site';
}
diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllPages.php
index e003ee91..16cc31d2 100644
--- a/includes/api/ApiQueryAllpages.php
+++ b/includes/api/ApiQueryAllPages.php
@@ -4,7 +4,7 @@
*
* Created on Sep 25, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@
*
* @ingroup API
*/
-class ApiQueryAllpages extends ApiQueryGeneratorBase {
+class ApiQueryAllPages extends ApiQueryGeneratorBase {
public function __construct( $query, $moduleName ) {
parent::__construct( $query, $moduleName, 'ap' );
@@ -67,6 +67,17 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
// Page filters
$this->addTables( 'page' );
+ if ( !is_null( $params['continue'] ) ) {
+ $cont = explode( '|', $params['continue'] );
+ if ( count( $cont ) != 1 ) {
+ $this->dieUsage( "Invalid continue param. You should pass the " .
+ "original value returned by the previous query", "_badcontinue" );
+ }
+ $op = $params['dir'] == 'descending' ? '<' : '>';
+ $cont_from = $db->addQuotes( $cont[0] );
+ $this->addWhere( "page_title $op= $cont_from" );
+ }
+
if ( $params['filterredir'] == 'redirects' ) {
$this->addWhereFld( 'page_is_redirect', 1 );
} elseif ( $params['filterredir'] == 'nonredirects' ) {
@@ -153,7 +164,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$this->addOption( 'STRAIGHT_JOIN' );
// We have to GROUP BY all selected fields to stop
// PostgreSQL from whining
- $this->addOption( 'GROUP BY', implode( ', ', $selectFields ) );
+ $this->addOption( 'GROUP BY', $selectFields );
$forceNameTitleIndex = false;
}
@@ -165,13 +176,22 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$this->addOption( 'LIMIT', $limit + 1 );
$res = $this->select( __METHOD__ );
+ //Get gender information
+ if( MWNamespace::hasGenderDistinction( $params['namespace'] ) ) {
+ $users = array();
+ foreach ( $res as $row ) {
+ $users[] = $row->page_title;
+ }
+ GenderCache::singleton()->doQuery( $users, __METHOD__ );
+ $res->rewind(); //reset
+ }
+
$count = 0;
$result = $this->getResult();
foreach ( $res as $row ) {
if ( ++ $count > $limit ) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- // TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
+ $this->setContinueEnumParameter( 'continue', $row->page_title );
break;
}
@@ -184,7 +204,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
);
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
if ( !$fit ) {
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
+ $this->setContinueEnumParameter( 'continue', $row->page_title );
break;
}
} else {
@@ -202,6 +222,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
return array(
'from' => null,
+ 'continue' => null,
'to' => null,
'prefix' => null,
'namespace' => array(
@@ -275,6 +296,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
$p = $this->getModulePrefix();
return array(
'from' => 'The page title to start enumerating from',
+ 'continue' => 'When more results are available, use this to continue',
'to' => 'The page title to stop enumerating at',
'prefix' => 'Search for all page titles that begin with this value',
'namespace' => 'The namespace to enumerate',
@@ -296,6 +318,16 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'pageid' => 'integer',
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Enumerate all pages sequentially in a given namespace';
}
@@ -304,6 +336,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase {
return array_merge( parent::getPossibleErrors(), array(
array( 'code' => 'params', 'info' => 'Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator' ),
array( 'code' => 'params', 'info' => 'prlevel may not be used without prtype' ),
+ array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
) );
}
diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php
index ac112ef9..7f50cbad 100644
--- a/includes/api/ApiQueryAllUsers.php
+++ b/includes/api/ApiQueryAllUsers.php
@@ -4,7 +4,7 @@
*
* Created on July 7, 2007
*
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,6 +34,16 @@ class ApiQueryAllUsers extends ApiQueryBase {
parent::__construct( $query, $moduleName, 'au' );
}
+ /**
+ * This function converts the user name to a canonical form
+ * which is stored in the database.
+ * @param String $name
+ * @return String
+ */
+ private function getCanonicalUserName( $name ) {
+ return str_replace( '_', ' ', $name );
+ }
+
public function execute() {
$db = $this->getDB();
$params = $this->extractRequestParams();
@@ -57,8 +67,8 @@ class ApiQueryAllUsers extends ApiQueryBase {
$useIndex = true;
$dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
- $from = is_null( $params['from'] ) ? null : $this->keyToTitle( $params['from'] );
- $to = is_null( $params['to'] ) ? null : $this->keyToTitle( $params['to'] );
+ $from = is_null( $params['from'] ) ? null : $this->getCanonicalUserName( $params['from'] );
+ $to = is_null( $params['to'] ) ? null : $this->getCanonicalUserName( $params['to'] );
# MySQL doesn't seem to use 'equality propagation' here, so like the
# ActiveUsers special page, we have to use rc_user_text for some cases.
@@ -68,7 +78,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
if ( !is_null( $params['prefix'] ) ) {
$this->addWhere( $userFieldToSort .
- $db->buildLike( $this->keyToTitle( $params['prefix'] ), $db->anyString() ) );
+ $db->buildLike( $this->getCanonicalUserName( $params['prefix'] ), $db->anyString() ) );
}
if ( !is_null( $params['rights'] ) ) {
@@ -142,11 +152,11 @@ class ApiQueryAllUsers extends ApiQueryBase {
'INNER JOIN', 'rc_user_text=user_name'
) ) );
- $this->addFields( 'COUNT(*) AS recentedits' );
+ $this->addFields( array( 'recentedits' => 'COUNT(*)' ) );
- $this->addWhere( "rc_log_type IS NULL OR rc_log_type != 'newusers'" );
+ $this->addWhere( 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ) );
$timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $wgActiveUserDays*24*3600 );
- $this->addWhere( "rc_timestamp >= {$db->addQuotes( $timestamp )}" );
+ $this->addWhere( 'rc_timestamp >= ' . $db->addQuotes( $timestamp ) );
$this->addOption( 'GROUP BY', $userFieldToSort );
}
@@ -190,15 +200,14 @@ class ApiQueryAllUsers extends ApiQueryBase {
$lastUserData = null;
if ( !$fit ) {
- $this->setContinueEnumParameter( 'from',
- $this->keyToTitle( $lastUserData['name'] ) );
+ $this->setContinueEnumParameter( 'from', $lastUserData['name'] );
break;
}
}
if ( $count > $limit ) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->user_name ) );
+ $this->setContinueEnumParameter( 'from', $row->user_name );
break;
}
@@ -209,7 +218,9 @@ class ApiQueryAllUsers extends ApiQueryBase {
'name' => $lastUser,
);
if ( $fld_blockinfo && !is_null( $row->ipb_by_text ) ) {
+ $lastUserData['blockid'] = $row->ipb_id;
$lastUserData['blockedby'] = $row->ipb_by_text;
+ $lastUserData['blockedbyid'] = $row->ipb_by;
$lastUserData['blockreason'] = $row->ipb_reason;
$lastUserData['blockexpiry'] = $row->ipb_expiry;
}
@@ -235,32 +246,45 @@ class ApiQueryAllUsers extends ApiQueryBase {
'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function' );
}
- $lastUserObj = User::newFromName( $lastUser );
+ $lastUserObj = User::newFromId( $row->user_id );
// Add user's group info
if ( $fld_groups ) {
- if ( !isset( $lastUserData['groups'] ) && $lastUserObj ) {
- $lastUserData['groups'] = ApiQueryUsers::getAutoGroups( $lastUserObj );
+ if ( !isset( $lastUserData['groups'] ) ) {
+ if ( $lastUserObj ) {
+ $lastUserData['groups'] = $lastUserObj->getAutomaticGroups();
+ } else {
+ // This should not normally happen
+ $lastUserData['groups'] = array();
+ }
}
if ( !is_null( $row->ug_group2 ) ) {
$lastUserData['groups'][] = $row->ug_group2;
}
+
$result->setIndexedTagName( $lastUserData['groups'], 'g' );
}
if ( $fld_implicitgroups && !isset( $lastUserData['implicitgroups'] ) && $lastUserObj ) {
- $lastUserData['implicitgroups'] = ApiQueryUsers::getAutoGroups( $lastUserObj );
+ $lastUserData['implicitgroups'] = $lastUserObj->getAutomaticGroups();
$result->setIndexedTagName( $lastUserData['implicitgroups'], 'g' );
}
if ( $fld_rights ) {
- if ( !isset( $lastUserData['rights'] ) && $lastUserObj ) {
- $lastUserData['rights'] = User::getGroupPermissions( $lastUserObj->getAutomaticGroups() );
+ if ( !isset( $lastUserData['rights'] ) ) {
+ if ( $lastUserObj ) {
+ $lastUserData['rights'] = User::getGroupPermissions( $lastUserObj->getAutomaticGroups() );
+ } else {
+ // This should not normally happen
+ $lastUserData['rights'] = array();
+ }
}
+
if ( !is_null( $row->ug_group2 ) ) {
$lastUserData['rights'] = array_unique( array_merge( $lastUserData['rights'],
User::getGroupPermissions( array( $row->ug_group2 ) ) ) );
}
+
$result->setIndexedTagName( $lastUserData['rights'], 'r' );
}
}
@@ -269,8 +293,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
$fit = $result->addValue( array( 'query', $this->getModuleName() ),
null, $lastUserData );
if ( !$fit ) {
- $this->setContinueEnumParameter( 'from',
- $this->keyToTitle( $lastUserData['name'] ) );
+ $this->setContinueEnumParameter( 'from', $lastUserData['name'] );
}
}
@@ -338,7 +361,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
'dir' => 'Direction to sort in',
'group' => 'Limit users to given group name(s)',
'excludegroup' => 'Exclude users in given group name(s)',
- 'rights' => 'Limit users to given right(s)',
+ 'rights' => 'Limit users to given right(s) (does not include rights granted by implicit or auto-promoted groups like *, user, or autoconfirmed)',
'prop' => array(
'What pieces of information to include.',
' blockinfo - Adds the information about a current block on the user',
@@ -354,6 +377,48 @@ class ApiQueryAllUsers extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'userid' => 'integer',
+ 'name' => 'string',
+ 'recenteditcount' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'blockinfo' => array(
+ 'blockid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedby' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedbyid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedreason' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedexpiry' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'hidden' => 'boolean'
+ ),
+ 'editcount' => array(
+ 'editcount' => 'integer'
+ ),
+ 'registration' => array(
+ 'registration' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Enumerate all registered users';
}
diff --git a/includes/api/ApiQueryAllimages.php b/includes/api/ApiQueryAllimages.php
deleted file mode 100644
index ca344f73..00000000
--- a/includes/api/ApiQueryAllimages.php
+++ /dev/null
@@ -1,267 +0,0 @@
-<?php
-
-/**
- * API for MediaWiki 1.12+
- *
- * Created on Mar 16, 2008
- *
- * Copyright © 2008 Vasiliev Victor vasilvv@gmail.com,
- * based on ApiQueryAllpages.php
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * Query module to enumerate all available pages.
- *
- * @ingroup API
- */
-class ApiQueryAllimages extends ApiQueryGeneratorBase {
-
- protected $mRepo;
-
- public function __construct( $query, $moduleName ) {
- parent::__construct( $query, $moduleName, 'ai' );
- $this->mRepo = RepoGroup::singleton()->getLocalRepo();
- }
-
- /**
- * Override parent method to make sure to make sure the repo's DB is used
- * which may not necesarilly be the same as the local DB.
- *
- * TODO: allow querying non-local repos.
- * @return DatabaseBase
- */
- protected function getDB() {
- return $this->mRepo->getSlaveDB();
- }
-
- public function execute() {
- $this->run();
- }
-
- public function getCacheMode( $params ) {
- return 'public';
- }
-
- /**
- * @param $resultPageSet ApiPageSet
- * @return void
- */
- public function executeGenerator( $resultPageSet ) {
- if ( $resultPageSet->isResolvingRedirects() ) {
- $this->dieUsage( 'Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator', 'params' );
- }
-
- $this->run( $resultPageSet );
- }
-
- /**
- * @param $resultPageSet ApiPageSet
- * @return void
- */
- private function run( $resultPageSet = null ) {
- $repo = $this->mRepo;
- if ( !$repo instanceof LocalRepo ) {
- $this->dieUsage( 'Local file repository does not support querying all images', 'unsupportedrepo' );
- }
-
- $db = $this->getDB();
-
- $params = $this->extractRequestParams();
-
- // Image filters
- $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
- $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
- $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
- $this->addWhereRange( 'img_name', $dir, $from, $to );
-
- if ( isset( $params['prefix'] ) )
- $this->addWhere( 'img_name' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) );
-
- if ( isset( $params['minsize'] ) ) {
- $this->addWhere( 'img_size>=' . intval( $params['minsize'] ) );
- }
-
- if ( isset( $params['maxsize'] ) ) {
- $this->addWhere( 'img_size<=' . intval( $params['maxsize'] ) );
- }
-
- $sha1 = false;
- if ( isset( $params['sha1'] ) ) {
- if ( !$this->validateSha1Hash( $params['sha1'] ) ) {
- $this->dieUsage( 'The SHA1 hash provided is not valid', 'invalidsha1hash' );
- }
- $sha1 = wfBaseConvert( $params['sha1'], 16, 36, 31 );
- } elseif ( isset( $params['sha1base36'] ) ) {
- $sha1 = $params['sha1base36'];
- if ( !$this->validateSha1Base36Hash( $sha1 ) ) {
- $this->dieUsage( 'The SHA1Base36 hash provided is not valid', 'invalidsha1base36hash' );
- }
- }
- if ( $sha1 ) {
- $this->addWhereFld( 'img_sha1', $sha1 );
- }
-
- if ( !is_null( $params['mime'] ) ) {
- global $wgMiserMode;
- if ( $wgMiserMode ) {
- $this->dieUsage( 'MIME search disabled in Miser Mode', 'mimesearchdisabled' );
- }
-
- list( $major, $minor ) = File::splitMime( $params['mime'] );
-
- $this->addWhereFld( 'img_major_mime', $major );
- $this->addWhereFld( 'img_minor_mime', $minor );
- }
-
- $this->addTables( 'image' );
-
- $prop = array_flip( $params['prop'] );
- $this->addFields( LocalFile::selectFields() );
-
- $limit = $params['limit'];
- $this->addOption( 'LIMIT', $limit + 1 );
- $this->addOption( 'ORDER BY', 'img_name' .
- ( $params['dir'] == 'descending' ? ' DESC' : '' ) );
-
- $res = $this->select( __METHOD__ );
-
- $titles = array();
- $count = 0;
- $result = $this->getResult();
- foreach ( $res as $row ) {
- if ( ++ $count > $limit ) {
- // We've reached the one extra which shows that there are additional pages to be had. Stop here...
- // TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->img_name ) );
- break;
- }
-
- if ( is_null( $resultPageSet ) ) {
- $file = $repo->newFileFromRow( $row );
- $info = array_merge( array( 'name' => $row->img_name ),
- ApiQueryImageInfo::getInfo( $file, $prop, $result ) );
- self::addTitleInfo( $info, $file->getTitle() );
-
- $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $info );
- if ( !$fit ) {
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->img_name ) );
- break;
- }
- } else {
- $titles[] = Title::makeTitle( NS_IMAGE, $row->img_name );
- }
- }
-
- if ( is_null( $resultPageSet ) ) {
- $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'img' );
- } else {
- $resultPageSet->populateFromTitles( $titles );
- }
- }
-
- public function getAllowedParams() {
- return array (
- 'from' => null,
- 'to' => null,
- 'prefix' => null,
- 'minsize' => array(
- ApiBase::PARAM_TYPE => 'integer',
- ),
- 'maxsize' => array(
- ApiBase::PARAM_TYPE => 'integer',
- ),
- 'limit' => array(
- ApiBase::PARAM_DFLT => 10,
- ApiBase::PARAM_TYPE => 'limit',
- ApiBase::PARAM_MIN => 1,
- ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
- ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
- ),
- 'dir' => array(
- ApiBase::PARAM_DFLT => 'ascending',
- ApiBase::PARAM_TYPE => array(
- 'ascending',
- 'descending'
- )
- ),
- 'sha1' => null,
- 'sha1base36' => null,
- 'prop' => array(
- ApiBase::PARAM_TYPE => ApiQueryImageInfo::getPropertyNames( $this->propertyFilter ),
- ApiBase::PARAM_DFLT => 'timestamp|url',
- ApiBase::PARAM_ISMULTI => true
- ),
- 'mime' => null,
- );
- }
-
- public function getParamDescription() {
- return array(
- 'from' => 'The image title to start enumerating from',
- 'to' => 'The image title to stop enumerating at',
- 'prefix' => 'Search for all image titles that begin with this value',
- 'dir' => 'The direction in which to list',
- 'minsize' => 'Limit to images with at least this many bytes',
- 'maxsize' => 'Limit to images with at most this many bytes',
- 'limit' => 'How many images in total to return',
- 'sha1' => "SHA1 hash of image. Overrides {$this->getModulePrefix()}sha1base36",
- 'sha1base36' => 'SHA1 hash of image in base 36 (used in MediaWiki)',
- 'prop' => ApiQueryImageInfo::getPropertyDescriptions( $this->propertyFilter ),
- 'mime' => 'What MIME type to search for. e.g. image/jpeg. Disabled in Miser Mode',
- );
- }
-
- private $propertyFilter = array( 'archivename' );
-
- public function getDescription() {
- return 'Enumerate all images sequentially';
- }
-
- public function getPossibleErrors() {
- return array_merge( parent::getPossibleErrors(), array(
- array( 'code' => 'params', 'info' => 'Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator' ),
- array( 'code' => 'unsupportedrepo', 'info' => 'Local file repository does not support querying all images' ),
- array( 'code' => 'mimesearchdisabled', 'info' => 'MIME search disabled in Miser Mode' ),
- array( 'code' => 'invalidsha1hash', 'info' => 'The SHA1 hash provided is not valid' ),
- array( 'code' => 'invalidsha1base36hash', 'info' => 'The SHA1Base36 hash provided is not valid' ),
- ) );
- }
-
- public function getExamples() {
- return array(
- 'api.php?action=query&list=allimages&aifrom=B' => array(
- 'Simple Use',
- 'Show a list of images starting at the letter "B"',
- ),
- 'api.php?action=query&generator=allimages&gailimit=4&gaifrom=T&prop=imageinfo' => array(
- 'Using as Generator',
- 'Show info about 4 images starting at the letter "T"',
- ),
- );
- }
-
- public function getHelpUrls() {
- return 'https://www.mediawiki.org/wiki/API:Allimages';
- }
-
- public function getVersion() {
- return __CLASS__ . ': $Id$';
- }
-}
diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php
index 381ef550..06db87bf 100644
--- a/includes/api/ApiQueryBacklinks.php
+++ b/includes/api/ApiQueryBacklinks.php
@@ -4,7 +4,7 @@
*
* Created on Oct 16, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,7 +40,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
private $rootTitle;
private $params, $contID, $redirID, $redirect;
- private $bl_ns, $bl_from, $bl_table, $bl_code, $bl_title, $bl_sort, $bl_fields, $hasNS;
+ private $bl_ns, $bl_from, $bl_table, $bl_code, $bl_title, $bl_fields, $hasNS;
/**
* Maps ns and title to pageid
@@ -91,14 +91,12 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->hasNS = $moduleName !== 'imageusage';
if ( $this->hasNS ) {
$this->bl_title = $prefix . '_title';
- $this->bl_sort = "{$this->bl_ns}, {$this->bl_title}, {$this->bl_from}";
$this->bl_fields = array(
$this->bl_ns,
$this->bl_title
);
} else {
$this->bl_title = $prefix . '_to';
- $this->bl_sort = "{$this->bl_title}, {$this->bl_from}";
$this->bl_fields = array(
$this->bl_title
);
@@ -144,7 +142,8 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$this->addWhereFld( 'page_namespace', $this->params['namespace'] );
if ( !is_null( $this->contID ) ) {
- $this->addWhere( "{$this->bl_from}>={$this->contID}" );
+ $op = $this->params['dir'] == 'descending' ? '<' : '>';
+ $this->addWhere( "{$this->bl_from}$op={$this->contID}" );
}
if ( $this->params['filterredir'] == 'redirects' ) {
@@ -155,7 +154,8 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
$this->addOption( 'LIMIT', $this->params['limit'] + 1 );
- $this->addOption( 'ORDER BY', $this->bl_from );
+ $sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
+ $this->addOption( 'ORDER BY', $this->bl_from . $sort );
$this->addOption( 'STRAIGHT_JOIN' );
}
@@ -186,28 +186,35 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
// We can't use LinkBatch here because $this->hasNS may be false
$titleWhere = array();
+ $allRedirNs = array();
+ $allRedirDBkey = array();
foreach ( $this->redirTitles as $t ) {
- $titleWhere[] = "{$this->bl_title} = " . $db->addQuotes( $t->getDBkey() ) .
- ( $this->hasNS ? " AND {$this->bl_ns} = '{$t->getNamespace()}'" : '' );
+ $redirNs = $t->getNamespace();
+ $redirDBkey = $t->getDBkey();
+ $titleWhere[] = "{$this->bl_title} = " . $db->addQuotes( $redirDBkey ) .
+ ( $this->hasNS ? " AND {$this->bl_ns} = {$redirNs}" : '' );
+ $allRedirNs[] = $redirNs;
+ $allRedirDBkey[] = $redirDBkey;
}
$this->addWhere( $db->makeList( $titleWhere, LIST_OR ) );
$this->addWhereFld( 'page_namespace', $this->params['namespace'] );
if ( !is_null( $this->redirID ) ) {
+ $op = $this->params['dir'] == 'descending' ? '<' : '>';
$first = $this->redirTitles[0];
- $title = $db->strencode( $first->getDBkey() );
+ $title = $db->addQuotes( $first->getDBkey() );
$ns = $first->getNamespace();
$from = $this->redirID;
if ( $this->hasNS ) {
- $this->addWhere( "{$this->bl_ns} > $ns OR " .
+ $this->addWhere( "{$this->bl_ns} $op $ns OR " .
"({$this->bl_ns} = $ns AND " .
- "({$this->bl_title} > '$title' OR " .
- "({$this->bl_title} = '$title' AND " .
- "{$this->bl_from} >= $from)))" );
+ "({$this->bl_title} $op $title OR " .
+ "({$this->bl_title} = $title AND " .
+ "{$this->bl_from} $op= $from)))" );
} else {
- $this->addWhere( "{$this->bl_title} > '$title' OR " .
- "({$this->bl_title} = '$title' AND " .
- "{$this->bl_from} >= $from)" );
+ $this->addWhere( "{$this->bl_title} $op $title OR " .
+ "({$this->bl_title} = $title AND " .
+ "{$this->bl_from} $op= $from)" );
}
}
if ( $this->params['filterredir'] == 'redirects' ) {
@@ -217,7 +224,17 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
$this->addOption( 'LIMIT', $this->params['limit'] + 1 );
- $this->addOption( 'ORDER BY', $this->bl_sort );
+ $orderBy = array();
+ $sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
+ // Don't order by namespace/title if it's constant in the WHERE clause
+ if( $this->hasNS && count( array_unique( $allRedirNs ) ) != 1 ) {
+ $orderBy[] = $this->bl_ns . $sort;
+ }
+ if( count( array_unique( $allRedirDBkey ) ) != 1 ) {
+ $orderBy[] = $this->bl_title . $sort;
+ }
+ $orderBy[] = $this->bl_from . $sort;
+ $this->addOption( 'ORDER BY', $orderBy );
$this->addOption( 'USE INDEX', array( 'page' => 'PRIMARY' ) );
}
@@ -277,7 +294,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if ( $this->hasNS ) {
$parentID = $this->pageMap[$row-> { $this->bl_ns } ][$row-> { $this->bl_title } ];
} else {
- $parentID = $this->pageMap[NS_IMAGE][$row-> { $this->bl_title } ];
+ $parentID = $this->pageMap[NS_FILE][$row-> { $this->bl_title } ];
}
$this->continueStr = $this->getContinueRedirStr( $parentID, $row->page_id );
break;
@@ -369,14 +386,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if ( !is_null( $this->params['continue'] ) ) {
$this->parseContinueParam();
} else {
- if ( $this->params['title'] !== '' ) {
- $title = Title::newFromText( $this->params['title'] );
- if ( !$title ) {
- $this->dieUsageMsg( array( 'invalidtitle', $this->params['title'] ) );
- } else {
- $this->rootTitle = $title;
- }
- }
+ $this->rootTitle = $this->getTitleOrPageId( $this->params )->getTitle();
}
// only image titles are allowed for the root in imageinfo mode
@@ -436,13 +446,22 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$retval = array(
'title' => array(
ApiBase::PARAM_TYPE => 'string',
- ApiBase::PARAM_REQUIRED => true
+ ),
+ 'pageid' => array(
+ ApiBase::PARAM_TYPE => 'integer',
),
'continue' => null,
'namespace' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => 'namespace'
),
+ 'dir' => array(
+ ApiBase::PARAM_DFLT => 'ascending',
+ ApiBase::PARAM_TYPE => array(
+ 'ascending',
+ 'descending'
+ )
+ ),
'filterredir' => array(
ApiBase::PARAM_DFLT => 'all',
ApiBase::PARAM_TYPE => array(
@@ -468,9 +487,11 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
public function getParamDescription() {
$retval = array(
- 'title' => 'Title to search',
+ 'title' => "Title to search. Cannot be used together with {$this->bl_code}pageid",
+ 'pageid' => "Pageid to search. Cannot be used together with {$this->bl_code}title",
'continue' => 'When more results are available, use this to continue',
'namespace' => 'The namespace to enumerate',
+ 'dir' => 'The direction in which to list',
);
if ( $this->getModuleName() != 'embeddedin' ) {
return array_merge( $retval, array(
@@ -485,6 +506,17 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
) );
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'pageid' => 'integer',
+ 'ns' => 'namespace',
+ 'title' => 'string',
+ 'redirect' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
switch ( $this->getModuleName() ) {
case 'backlinks':
@@ -499,11 +531,13 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
}
public function getPossibleErrors() {
- return array_merge( parent::getPossibleErrors(), array(
- array( 'invalidtitle', 'title' ),
- array( 'code' => 'bad_image_title', 'info' => "The title for {$this->getModuleName()} query must be an image" ),
- array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
- ) );
+ return array_merge( parent::getPossibleErrors(),
+ $this->getTitleOrPageIdErrorMessage(),
+ array(
+ array( 'code' => 'bad_image_title', 'info' => "The title for {$this->getModuleName()} query must be an image" ),
+ array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
+ )
+ );
}
public function getExamples() {
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php
index 4fe82de0..2c48aca0 100644
--- a/includes/api/ApiQueryBase.php
+++ b/includes/api/ApiQueryBase.php
@@ -4,7 +4,7 @@
*
* Created on Sep 7, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -233,7 +233,7 @@ abstract class ApiQueryBase extends ApiBase {
*/
protected function addTimestampWhereRange( $field, $dir, $start, $end, $sort = true ) {
$db = $this->getDb();
- return $this->addWhereRange( $field, $dir,
+ $this->addWhereRange( $field, $dir,
$db->timestampOrNull( $start ), $db->timestampOrNull( $end ), $sort );
}
@@ -392,7 +392,7 @@ abstract class ApiQueryBase extends ApiBase {
* @param $name string Name to assign to the database connection
* @param $db int One of the DB_* constants
* @param $groups array Query groups
- * @return Database
+ * @return DatabaseBase
*/
public function selectNamedDB( $name, $db, $groups ) {
$this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups );
@@ -519,7 +519,7 @@ abstract class ApiQueryBase extends ApiBase {
$this->addFields( 'ipb_deleted' );
if ( $showBlockInfo ) {
- $this->addFields( array( 'ipb_reason', 'ipb_by_text', 'ipb_expiry' ) );
+ $this->addFields( array( 'ipb_id', 'ipb_by', 'ipb_by_text', 'ipb_reason', 'ipb_expiry' ) );
}
// Don't show hidden names
@@ -571,6 +571,11 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase {
private $mIsGenerator;
+ /**
+ * @param $query ApiBase
+ * @param $moduleName string
+ * @param $paramPrefix string
+ */
public function __construct( $query, $moduleName, $paramPrefix = '' ) {
parent::__construct( $query, $moduleName, $paramPrefix );
$this->mIsGenerator = false;
diff --git a/includes/api/ApiQueryBlocks.php b/includes/api/ApiQueryBlocks.php
index bebb5a7d..96b86962 100644
--- a/includes/api/ApiQueryBlocks.php
+++ b/includes/api/ApiQueryBlocks.php
@@ -4,7 +4,7 @@
*
* Created on Sep 10, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -77,6 +77,9 @@ class ApiQueryBlocks extends ApiQueryBase {
$this->addOption( 'LIMIT', $params['limit'] + 1 );
$this->addTimestampWhereRange( 'ipb_timestamp', $params['dir'], $params['start'], $params['end'] );
+
+ $db = $this->getDB();
+
if ( isset( $params['ids'] ) ) {
$this->addWhereFld( 'ipb_id', $params['ids'] );
}
@@ -87,7 +90,6 @@ class ApiQueryBlocks extends ApiQueryBase {
$this->addWhereFld( 'ipb_address', $this->usernames );
$this->addWhereFld( 'ipb_auto', 0 );
}
- $db = $this->getDB();
if ( isset( $params['ip'] ) ) {
list( $ip, $range ) = IP::parseCIDR( $params['ip'] );
if ( $ip && $range ) {
@@ -101,10 +103,15 @@ class ApiQueryBlocks extends ApiQueryBase {
}
$prefix = substr( $lower, 0, 4 );
+ # Fairly hard to make a malicious SQL statement out of hex characters,
+ # but it is good practice to add quotes
+ $lower = $db->addQuotes( $lower );
+ $upper = $db->addQuotes( $upper );
+
$this->addWhere( array(
'ipb_range_start' . $db->buildLike( $prefix, $db->anyString() ),
- "ipb_range_start <= '$lower'",
- "ipb_range_end >= '$upper'",
+ 'ipb_range_start <= ' . $lower,
+ 'ipb_range_end >= ' . $upper,
'ipb_auto' => 0
) );
}
@@ -292,8 +299,8 @@ class ApiQueryBlocks extends ApiQueryBase {
'start' => 'The timestamp to start enumerating from',
'end' => 'The timestamp to stop enumerating at',
'dir' => $this->getDirectionDescription( $p ),
- 'ids' => 'Pipe-separated list of block IDs to list (optional)',
- 'users' => 'Pipe-separated list of users to search for (optional)',
+ 'ids' => 'List of block IDs to list (optional)',
+ 'users' => 'List of users to search for (optional)',
'ip' => array( 'Get all blocks applying to this IP or CIDR range, including range blocks.',
'Cannot be used together with bkusers. CIDR ranges broader than /16 are not accepted' ),
'limit' => 'The maximum amount of blocks to list',
@@ -317,18 +324,74 @@ class ApiQueryBlocks extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ 'id' => array(
+ 'id' => 'integer'
+ ),
+ 'user' => array(
+ 'user' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'userid' => array(
+ 'userid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'by' => array(
+ 'by' => 'string'
+ ),
+ 'byid' => array(
+ 'byid' => 'integer'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'expiry' => array(
+ 'expiry' => 'timestamp'
+ ),
+ 'reason' => array(
+ 'reason' => 'string'
+ ),
+ 'range' => array(
+ 'rangestart' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'rangeend' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'flags' => array(
+ 'automatic' => 'boolean',
+ 'anononly' => 'boolean',
+ 'nocreate' => 'boolean',
+ 'autoblock' => 'boolean',
+ 'noemail' => 'boolean',
+ 'hidden' => 'boolean',
+ 'allowusertalk' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
return 'List all blocked users and IP addresses';
}
public function getPossibleErrors() {
- return array_merge( parent::getPossibleErrors(), array(
+ return array_merge( parent::getPossibleErrors(),
$this->getRequireOnlyOneParameterErrorMessages( array( 'users', 'ip' ) ),
- array( 'code' => 'cidrtoobroad', 'info' => 'CIDR ranges broader than /16 are not accepted' ),
- array( 'code' => 'param_user', 'info' => 'User parameter may not be empty' ),
- array( 'code' => 'param_user', 'info' => 'User name user is not valid' ),
- array( 'show' ),
- ) );
+ array(
+ array( 'code' => 'cidrtoobroad', 'info' => 'CIDR ranges broader than /16 are not accepted' ),
+ array( 'code' => 'param_user', 'info' => 'User parameter may not be empty' ),
+ array( 'code' => 'param_user', 'info' => 'User name user is not valid' ),
+ array( 'show' ),
+ )
+ );
}
public function getExamples() {
diff --git a/includes/api/ApiQueryCategories.php b/includes/api/ApiQueryCategories.php
index 1c1f1550..309c2ce9 100644
--- a/includes/api/ApiQueryCategories.php
+++ b/includes/api/ApiQueryCategories.php
@@ -4,7 +4,7 @@
*
* Created on May 13, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -89,12 +89,13 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
$this->dieUsage( "Invalid continue param. You should pass the " .
"original value returned by the previous query", "_badcontinue" );
}
+ $op = $params['dir'] == 'descending' ? '<' : '>';
$clfrom = intval( $cont[0] );
- $clto = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) );
+ $clto = $this->getDB()->addQuotes( $cont[1] );
$this->addWhere(
- "cl_from > $clfrom OR " .
+ "cl_from $op $clfrom OR " .
"(cl_from = $clfrom AND " .
- "cl_to >= '$clto')"
+ "cl_to $op= $clto)"
);
}
@@ -123,14 +124,14 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
$this->addOption( 'USE INDEX', array( 'categorylinks' => 'cl_from' ) );
- $dir = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
// 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' . $dir );
+ $this->addOption( 'ORDER BY', 'cl_to' . $sort );
} else {
$this->addOption( 'ORDER BY', array(
- 'cl_from' . $dir,
- 'cl_to' . $dir
+ 'cl_from' . $sort,
+ 'cl_to' . $sort
));
}
@@ -142,8 +143,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
if ( ++$count > $params['limit'] ) {
// We've reached the one extra which shows that
// there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter( 'continue', $row->cl_from .
- '|' . $this->keyToTitle( $row->cl_to ) );
+ $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
break;
}
@@ -163,8 +163,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
$fit = $this->addPageSubItem( $row->cl_from, $vals );
if ( !$fit ) {
- $this->setContinueEnumParameter( 'continue', $row->cl_from .
- '|' . $this->keyToTitle( $row->cl_to ) );
+ $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
break;
}
}
@@ -174,8 +173,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
if ( ++$count > $params['limit'] ) {
// We've reached the one extra which shows that
// there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter( 'continue', $row->cl_from .
- '|' . $this->keyToTitle( $row->cl_to ) );
+ $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
break;
}
@@ -239,6 +237,25 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'sortkey' => array(
+ 'sortkey' => 'string',
+ 'sortkeyprefix' => 'string'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'hidden' => array(
+ 'hidden' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
return 'List all categories the page(s) belong to';
}
diff --git a/includes/api/ApiQueryCategoryInfo.php b/includes/api/ApiQueryCategoryInfo.php
index c5070e87..31517fab 100644
--- a/includes/api/ApiQueryCategoryInfo.php
+++ b/includes/api/ApiQueryCategoryInfo.php
@@ -4,7 +4,7 @@
*
* Created on May 13, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,7 +25,8 @@
*/
/**
- * This query adds the <categories> subelement to all pages with the list of categories the page is in
+ * This query adds the "<categories>" subelement to all pages with the list of
+ * categories the page is in.
*
* @ingroup API
*/
@@ -61,7 +62,7 @@ class ApiQueryCategoryInfo extends ApiQueryBase {
'pp_propname' => 'hiddencat' ) ),
) );
- $this->addFields( array( 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'pp_propname AS cat_hidden' ) );
+ $this->addFields( array( 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'cat_hidden' => 'pp_propname' ) );
$this->addWhere( array( 'cat_title' => $cattitles ) );
if ( !is_null( $params['continue'] ) ) {
@@ -106,6 +107,34 @@ class ApiQueryCategoryInfo extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ ApiBase::PROP_LIST => false,
+ '' => array(
+ 'size' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => false
+ ),
+ 'pages' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => false
+ ),
+ 'files' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => false
+ ),
+ 'subcats' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => false
+ ),
+ 'hidden' => array(
+ ApiBase::PROP_TYPE => 'boolean',
+ ApiBase::PROP_NULLABLE => false
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'Returns information about the given categories';
}
diff --git a/includes/api/ApiQueryCategoryMembers.php b/includes/api/ApiQueryCategoryMembers.php
index 4b19b7e8..55ce0234 100644
--- a/includes/api/ApiQueryCategoryMembers.php
+++ b/includes/api/ApiQueryCategoryMembers.php
@@ -4,7 +4,7 @@
*
* Created on June 14, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -54,22 +54,9 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
private function run( $resultPageSet = null ) {
$params = $this->extractRequestParams();
- $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
-
- if ( isset( $params['title'] ) ) {
- $categoryTitle = Title::newFromText( $params['title'] );
-
- if ( is_null( $categoryTitle ) || $categoryTitle->getNamespace() != NS_CATEGORY ) {
- $this->dieUsage( 'The category name you entered is not valid', 'invalidcategory' );
- }
- } elseif( isset( $params['pageid'] ) ) {
- $categoryTitle = Title::newFromID( $params['pageid'] );
-
- if ( !$categoryTitle ) {
- $this->dieUsageMsg( array( 'nosuchpageid', $params['pageid'] ) );
- } elseif ( $categoryTitle->getNamespace() != NS_CATEGORY ) {
- $this->dieUsage( 'The category name you entered is not valid', 'invalidcategory' );
- }
+ $categoryTitle = $this->getTitleOrPageId( $params )->getTitle();
+ if ( $categoryTitle->getNamespace() != NS_CATEGORY ) {
+ $this->dieUsage( 'The category name you entered is not valid', 'invalidcategory' );
}
$prop = array_flip( $params['prop'] );
@@ -107,10 +94,10 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
$this->addWhereFld( 'page_namespace', $params['namespace'] );
}
- $dir = $params['dir'] == 'asc' ? 'newer' : 'older';
+ $dir = in_array( $params['dir'], array( 'asc', 'ascending', 'newer' ) ) ? 'newer' : 'older';
if ( $params['sort'] == 'timestamp' ) {
- $this->addWhereRange( 'cl_timestamp',
+ $this->addTimestampWhereRange( 'cl_timestamp',
$dir,
$params['start'],
$params['end'] );
@@ -313,10 +300,15 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
)
),
'dir' => array(
- ApiBase::PARAM_DFLT => 'asc',
+ ApiBase::PARAM_DFLT => 'ascending',
ApiBase::PARAM_TYPE => array(
'asc',
- 'desc'
+ 'desc',
+ // Normalising with other modules
+ 'ascending',
+ 'descending',
+ 'newer',
+ 'older',
)
),
'start' => array(
@@ -357,7 +349,7 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
'endsortkey' => "Sortkey to end listing at. Must be given in binary format. Can only be used with {$p}sort=sortkey",
'startsortkeyprefix' => "Sortkey prefix to start listing from. Can only be used with {$p}sort=sortkey. Overrides {$p}startsortkey",
'endsortkeyprefix' => "Sortkey prefix to end listing BEFORE (not at, if this value occurs it will not be included!). Can only be used with {$p}sort=sortkey. Overrides {$p}endsortkey",
- 'continue' => 'For large categories, give the value retured from previous query',
+ 'continue' => 'For large categories, give the value returned from previous query',
'limit' => 'The maximum number of pages to return.',
);
@@ -372,17 +364,46 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
return $desc;
}
+ public function getResultProperties() {
+ return array(
+ 'ids' => array(
+ 'pageid' => 'integer'
+ ),
+ 'title' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'sortkey' => array(
+ 'sortkey' => 'string'
+ ),
+ 'sortkeyprefix' => array(
+ 'sortkeyprefix' => 'string'
+ ),
+ 'type' => array(
+ 'type' => array(
+ ApiBase::PROP_TYPE => array(
+ 'page',
+ 'subcat',
+ 'file'
+ )
+ )
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ )
+ );
+ }
+
public function getDescription() {
return 'List all pages in a given category';
}
public function getPossibleErrors() {
return array_merge( parent::getPossibleErrors(),
- $this->getRequireOnlyOneParameterErrorMessages( array( 'title', 'pageid' ) ),
+ $this->getTitleOrPageIdErrorMessage(),
array(
array( 'code' => 'invalidcategory', 'info' => 'The category name you entered is not valid' ),
array( 'code' => 'badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
- array( 'nosuchpageid', 'pageid' ),
)
);
}
diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php
index 0a0cc93d..e69ccbd6 100644
--- a/includes/api/ApiQueryDeletedrevs.php
+++ b/includes/api/ApiQueryDeletedrevs.php
@@ -4,7 +4,7 @@
*
* Created on Jul 2, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -155,7 +155,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$this->addWhereFld( 'ar_user_text', $params['user'] );
} elseif ( !is_null( $params['excludeuser'] ) ) {
$this->addWhere( 'ar_user_text != ' .
- $this->getDB()->addQuotes( $params['excludeuser'] ) );
+ $db->addQuotes( $params['excludeuser'] ) );
}
if ( !is_null( $params['continue'] ) && ( $mode == 'all' || $mode == 'revs' ) ) {
@@ -164,14 +164,14 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$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] ) );
- $ts = $this->getDB()->strencode( $cont[2] );
+ $title = $db->addQuotes( $cont[1] );
+ $ts = $db->addQuotes( $db->timestamp( $cont[2] ) );
$op = ( $dir == 'newer' ? '>' : '<' );
$this->addWhere( "ar_namespace $op $ns OR " .
"(ar_namespace = $ns AND " .
- "(ar_title $op '$title' OR " .
- "(ar_title = '$title' AND " .
- "ar_timestamp $op= '$ts')))" );
+ "(ar_title $op $title OR " .
+ "(ar_title = $title AND " .
+ "ar_timestamp $op= $ts)))" );
}
$this->addOption( 'LIMIT', $limit + 1 );
@@ -180,7 +180,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
if ( $params['unique'] ) {
$this->addOption( 'GROUP BY', 'ar_title' );
} else {
- $this->addOption( 'ORDER BY', 'ar_title, ar_timestamp' );
+ $sort = ( $dir == 'newer' ? '' : ' DESC' );
+ $this->addOption( 'ORDER BY', array(
+ 'ar_title' . $sort,
+ 'ar_timestamp' . $sort
+ ));
}
} else {
if ( $mode == 'revs' ) {
@@ -199,7 +203,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
// We've had enough
if ( $mode == 'all' || $mode == 'revs' ) {
$this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' .
- $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp );
+ $row->ar_title . '|' . $row->ar_timestamp );
} else {
$this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) );
}
@@ -265,7 +269,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
if ( !$fit ) {
if ( $mode == 'all' || $mode == 'revs' ) {
$this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' .
- $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp );
+ $row->ar_title . '|' . $row->ar_timestamp );
} else {
$this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) );
}
@@ -334,8 +338,8 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
public function getParamDescription() {
return array(
- 'start' => 'The timestamp to start enumerating from (1,2)',
- 'end' => 'The timestamp to stop enumerating at (1,2)',
+ 'start' => 'The timestamp to start enumerating from (1, 2)',
+ 'end' => 'The timestamp to stop enumerating at (1, 2)',
'dir' => $this->getDirectionDescription( $this->getModulePrefix(), ' (1, 3)' ),
'from' => 'Start listing at this title (3)',
'to' => 'Stop listing at this title (3)',
@@ -363,6 +367,18 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'token' => array(
+ 'token' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
$p = $this->getModulePrefix();
return array(
diff --git a/includes/api/ApiQueryDisabled.php b/includes/api/ApiQueryDisabled.php
index d68480c3..6715969a 100644
--- a/includes/api/ApiQueryDisabled.php
+++ b/includes/api/ApiQueryDisabled.php
@@ -4,7 +4,7 @@
*
* Created on Sep 25, 2008
*
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<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
diff --git a/includes/api/ApiQueryDuplicateFiles.php b/includes/api/ApiQueryDuplicateFiles.php
index beca5879..8f0fd3be 100644
--- a/includes/api/ApiQueryDuplicateFiles.php
+++ b/includes/api/ApiQueryDuplicateFiles.php
@@ -4,7 +4,7 @@
*
* Created on Sep 27, 2008
*
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<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
@@ -59,67 +59,99 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
}
$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( $params['dir'] == 'descending' ) {
+ $images = array_reverse( $images );
+ }
+ $skipUntilThisDup = false;
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')"
- );
+ $fromImage = $cont[0];
+ $skipUntilThisDup = $cont[1];
+ // Filter out any images before $fromImage
+ foreach ( $images as $image => $pageId ) {
+ if ( $image < $fromImage ) {
+ unset( $images[$image] );
+ } else {
+ break;
+ }
+ }
}
- $dir = ( $params['dir'] == 'descending' ? ' DESC' : '' );
- $this->addOption( 'ORDER BY', 'i1.img_name' . $dir );
- $this->addOption( 'LIMIT', $params['limit'] + 1 );
+ $filesToFind = array_keys( $images );
+ if( $params['localonly'] ) {
+ $files = RepoGroup::singleton()->getLocalRepo()->findFiles( $filesToFind );
+ } else {
+ $files = RepoGroup::singleton()->findFiles( $filesToFind );
+ }
- $res = $this->select( __METHOD__ );
+ $fit = true;
$count = 0;
$titles = array();
- foreach ( $res as $row ) {
- 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;
+
+ $sha1s = array();
+ foreach ( $files as $file ) {
+ $sha1s[$file->getName()] = $file->getSha1();
+ }
+
+ // find all files with the hashes, result format is: array( hash => array( dup1, dup2 ), hash1 => ... )
+ $filesToFindBySha1s = array_unique( array_values( $sha1s ) );
+ if( $params['localonly'] ) {
+ $filesBySha1s = RepoGroup::singleton()->getLocalRepo()->findBySha1s( $filesToFindBySha1s );
+ } else {
+ $filesBySha1s = RepoGroup::singleton()->findBySha1s( $filesToFindBySha1s );
+ }
+
+ // iterate over $images to handle continue param correct
+ foreach( $images as $image => $pageId ) {
+ if( !isset( $sha1s[$image] ) ) {
+ continue; //file does not exist
+ }
+ $sha1 = $sha1s[$image];
+ $dupFiles = $filesBySha1s[$sha1];
+ if( $params['dir'] == 'descending' ) {
+ $dupFiles = array_reverse( $dupFiles );
}
- if ( !is_null( $resultPageSet ) ) {
- $titles[] = Title::makeTitle( NS_FILE, $row->dup_name );
- } else {
- $r = array(
- 'name' => $row->dup_name,
- 'user' => $row->dup_user_text,
- 'timestamp' => wfTimestamp( TS_ISO_8601, $row->dup_timestamp )
- );
- $fit = $this->addPageSubItem( $images[$row->orig_name], $r );
- if ( !$fit ) {
- $this->setContinueEnumParameter( 'continue',
- $this->keyToTitle( $row->orig_name ) . '|' .
- $this->keyToTitle( $row->dup_name ) );
+ foreach ( $dupFiles as $dupFile ) {
+ $dupName = $dupFile->getName();
+ if( $image == $dupName && $dupFile->isLocal() ) {
+ continue; //ignore the local file itself
+ }
+ if( $skipUntilThisDup !== false && $dupName < $skipUntilThisDup ) {
+ continue; //skip to pos after the image from continue param
+ }
+ $skipUntilThisDup = false;
+ if ( ++$count > $params['limit'] ) {
+ $fit = false; //break outer loop
+ // We're one over limit which shows that
+ // there are additional images to be had. Stop here...
+ $this->setContinueEnumParameter( 'continue', $image . '|' . $dupName );
break;
}
+ if ( !is_null( $resultPageSet ) ) {
+ $titles[] = $file->getTitle();
+ } else {
+ $r = array(
+ 'name' => $dupName,
+ 'user' => $dupFile->getUser( 'text' ),
+ 'timestamp' => wfTimestamp( TS_ISO_8601, $dupFile->getTimestamp() )
+ );
+ if( !$dupFile->isLocal() ) {
+ $r['shared'] = '';
+ }
+ $fit = $this->addPageSubItem( $pageId, $r );
+ if ( !$fit ) {
+ $this->setContinueEnumParameter( 'continue', $image . '|' . $dupName );
+ break;
+ }
+ }
+ }
+ if( !$fit ) {
+ break;
}
}
if ( !is_null( $resultPageSet ) ) {
@@ -144,19 +176,32 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
'descending'
)
),
+ 'localonly' => false,
);
}
public function getParamDescription() {
return array(
- 'limit' => 'How many files to return',
+ 'limit' => 'How many duplicate files to return',
'continue' => 'When more results are available, use this to continue',
'dir' => 'The direction in which to list',
+ 'localonly' => 'Look only for files in the local repository',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'name' => 'string',
+ 'user' => 'string',
+ 'timestamp' => 'timestamp',
+ 'shared' => 'boolean',
+ )
);
}
public function getDescription() {
- return 'List all files that are duplicates of the given file(s)';
+ return 'List all files that are duplicates of the given file(s) based on hash values';
}
public function getPossibleErrors() {
diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php
index 93c71e2f..42b398ba 100644
--- a/includes/api/ApiQueryExtLinksUsage.php
+++ b/includes/api/ApiQueryExtLinksUsage.php
@@ -4,7 +4,7 @@
*
* Created on July 7, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -232,6 +232,21 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
return $desc;
}
+ public function getResultProperties() {
+ return array(
+ 'ids' => array(
+ 'pageid' => 'integer'
+ ),
+ 'title' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'url' => array(
+ 'url' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Enumerate pages that contain a given URL';
}
diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php
index a9fbc839..9365a9b8 100644
--- a/includes/api/ApiQueryExternalLinks.php
+++ b/includes/api/ApiQueryExternalLinks.php
@@ -4,7 +4,7 @@
*
* Created on May 13, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -133,6 +133,14 @@ class ApiQueryExternalLinks extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ '*' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Returns all external urls (not interwikies) from the given page(s)';
}
diff --git a/includes/api/ApiQueryFilearchive.php b/includes/api/ApiQueryFilearchive.php
index be995f30..a5486ef4 100644
--- a/includes/api/ApiQueryFilearchive.php
+++ b/includes/api/ApiQueryFilearchive.php
@@ -6,7 +6,7 @@
*
* Copyright © 2010 Sam Reed
* Copyright © 2008 Vasiliev Victor vasilvv@gmail.com,
- * based on ApiQueryAllpages.php
+ * based on ApiQueryAllPages.php
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -56,8 +56,10 @@ class ApiQueryFilearchive extends ApiQueryBase {
$fld_dimensions = isset( $prop['dimensions'] );
$fld_description = isset( $prop['description'] ) || isset( $prop['parseddescription'] );
$fld_mime = isset( $prop['mime'] );
+ $fld_mediatype = isset( $prop['mediatype'] );
$fld_metadata = isset( $prop['metadata'] );
$fld_bitdepth = isset( $prop['bitdepth'] );
+ $fld_archivename = isset( $prop['archivename'] );
$this->addTables( 'filearchive' );
@@ -68,12 +70,28 @@ class ApiQueryFilearchive extends ApiQueryBase {
$this->addFieldsIf( array( 'fa_height', 'fa_width', 'fa_size' ), $fld_dimensions || $fld_size );
$this->addFieldsIf( 'fa_description', $fld_description );
$this->addFieldsIf( array( 'fa_major_mime', 'fa_minor_mime' ), $fld_mime );
+ $this->addFieldsIf( 'fa_media_type', $fld_mediatype );
$this->addFieldsIf( 'fa_metadata', $fld_metadata );
$this->addFieldsIf( 'fa_bits', $fld_bitdepth );
+ $this->addFieldsIf( 'fa_archive_name', $fld_archivename );
+
+ if ( !is_null( $params['continue'] ) ) {
+ $cont = explode( '|', $params['continue'] );
+ if ( count( $cont ) != 1 ) {
+ $this->dieUsage( "Invalid continue param. You should pass the " .
+ "original value returned by the previous query", "_badcontinue" );
+ }
+ $op = $params['dir'] == 'descending' ? '<' : '>';
+ $cont_from = $db->addQuotes( $cont[0] );
+ $this->addWhere( "fa_name $op= $cont_from" );
+ }
// Image filters
$dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
$from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
+ if ( !is_null( $params['continue'] ) ) {
+ $from = $params['continue'];
+ }
$to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
$this->addWhereRange( 'fa_name', $dir, $from, $to );
if ( isset( $params['prefix'] ) ) {
@@ -117,8 +135,8 @@ class ApiQueryFilearchive extends ApiQueryBase {
$limit = $params['limit'];
$this->addOption( 'LIMIT', $limit + 1 );
- $this->addOption( 'ORDER BY', 'fa_name' .
- ( $params['dir'] == 'descending' ? ' DESC' : '' ) );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ $this->addOption( 'ORDER BY', 'fa_name' . $sort );
$res = $this->select( __METHOD__ );
@@ -127,8 +145,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
foreach ( $res as $row ) {
if ( ++$count > $limit ) {
// We've reached the one extra which shows that there are additional pages to be had. Stop here...
- // TODO: Security issue - if the user has no right to view next title, it will still be shown
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->fa_name ) );
+ $this->setContinueEnumParameter( 'continue', $row->fa_name );
break;
}
@@ -165,6 +182,9 @@ class ApiQueryFilearchive extends ApiQueryBase {
$row->fa_description, $title );
}
}
+ if ( $fld_mediatype ) {
+ $file['mediatype'] = $row->fa_media_type;
+ }
if ( $fld_metadata ) {
$file['metadata'] = $row->fa_metadata
? ApiQueryImageInfo::processMetaData( unserialize( $row->fa_metadata ), $result )
@@ -176,6 +196,9 @@ class ApiQueryFilearchive extends ApiQueryBase {
if ( $fld_mime ) {
$file['mime'] = "$row->fa_major_mime/$row->fa_minor_mime";
}
+ if ( $fld_archivename && !is_null( $row->fa_archive_name ) ) {
+ $file['archivename'] = $row->fa_archive_name;
+ }
if ( $row->fa_deleted & File::DELETED_FILE ) {
$file['filehidden'] = '';
@@ -194,7 +217,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $file );
if ( !$fit ) {
- $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->fa_name ) );
+ $this->setContinueEnumParameter( 'continue', $row->fa_name );
break;
}
}
@@ -205,6 +228,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
public function getAllowedParams() {
return array (
'from' => null,
+ 'continue' => null,
'to' => null,
'prefix' => null,
'limit' => array(
@@ -235,8 +259,10 @@ class ApiQueryFilearchive extends ApiQueryBase {
'description',
'parseddescription',
'mime',
+ 'mediatype',
'metadata',
- 'bitdepth'
+ 'bitdepth',
+ 'archivename',
),
),
);
@@ -245,6 +271,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
public function getParamDescription() {
return array(
'from' => 'The image title to start enumerating from',
+ 'continue' => 'When more results are available, use this to continue',
'to' => 'The image title to stop enumerating at',
'prefix' => 'Search for all image titles that begin with this value',
'dir' => 'The direction in which to list',
@@ -261,9 +288,75 @@ class ApiQueryFilearchive extends ApiQueryBase {
' description - Adds description the image version',
' parseddescription - Parse the description on the version',
' mime - Adds MIME of the image',
+ ' mediatype - Adds the media type of the image',
' metadata - Lists EXIF metadata for the version of the image',
' bitdepth - Adds the bit depth of the version',
- ),
+ ' archivename - Adds the file name of the archive version for non-latest versions'
+ ),
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'name' => 'string',
+ 'ns' => 'namespace',
+ 'title' => 'string',
+ 'filehidden' => 'boolean',
+ 'commenthidden' => 'boolean',
+ 'userhidden' => 'boolean',
+ 'suppressed' => 'boolean'
+ ),
+ 'sha1' => array(
+ 'sha1' => 'string'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'user' => array(
+ 'userid' => 'integer',
+ 'user' => 'string'
+ ),
+ 'size' => array(
+ 'size' => 'integer',
+ 'pagecount' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'height' => 'integer',
+ 'width' => 'integer'
+ ),
+ 'dimensions' => array(
+ 'size' => 'integer',
+ 'pagecount' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'height' => 'integer',
+ 'width' => 'integer'
+ ),
+ 'description' => array(
+ 'description' => 'string'
+ ),
+ 'parseddescription' => array(
+ 'description' => 'string',
+ 'parseddescription' => 'string'
+ ),
+ 'metadata' => array(
+ 'metadata' => 'string'
+ ),
+ 'bitdepth' => array(
+ 'bitdepth' => 'integer'
+ ),
+ 'mime' => array(
+ 'mime' => 'string'
+ ),
+ 'mediatype' => array(
+ 'mediatype' => 'string'
+ ),
+ 'archivename' => array(
+ 'archivename' => 'string'
+ ),
);
}
@@ -277,6 +370,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
array( 'code' => 'hashsearchdisabled', 'info' => 'Search by hash disabled in Miser Mode' ),
array( 'code' => 'invalidsha1hash', 'info' => 'The SHA1 hash provided is not valid' ),
array( 'code' => 'invalidsha1base36hash', 'info' => 'The SHA1Base36 hash provided is not valid' ),
+ array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
) );
}
diff --git a/includes/api/ApiQueryIWBacklinks.php b/includes/api/ApiQueryIWBacklinks.php
index feda1779..c5012f08 100644
--- a/includes/api/ApiQueryIWBacklinks.php
+++ b/includes/api/ApiQueryIWBacklinks.php
@@ -5,7 +5,7 @@
* Created on May 14, 2010
*
* Copyright © 2010 Sam Reed
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -61,15 +61,17 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
'original value returned by the previous query', '_badcontinue' );
}
- $prefix = $this->getDB()->strencode( $cont[0] );
- $title = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) );
+ $db = $this->getDB();
+ $op = $params['dir'] == 'descending' ? '<' : '>';
+ $prefix = $db->addQuotes( $cont[0] );
+ $title = $db->addQuotes( $cont[1] );
$from = intval( $cont[2] );
$this->addWhere(
- "iwl_prefix > '$prefix' OR " .
- "(iwl_prefix = '$prefix' AND " .
- "(iwl_title > '$title' OR " .
- "(iwl_title = '$title' AND " .
- "iwl_from >= $from)))"
+ "iwl_prefix $op $prefix OR " .
+ "(iwl_prefix = $prefix AND " .
+ "(iwl_title $op $title OR " .
+ "(iwl_title = $title AND " .
+ "iwl_from $op= $from)))"
);
}
@@ -83,16 +85,24 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
$this->addFields( array( 'page_id', 'page_title', 'page_namespace', 'page_is_redirect',
'iwl_from', 'iwl_prefix', 'iwl_title' ) );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
if ( isset( $params['prefix'] ) ) {
$this->addWhereFld( 'iwl_prefix', $params['prefix'] );
if ( isset( $params['title'] ) ) {
$this->addWhereFld( 'iwl_title', $params['title'] );
- $this->addOption( 'ORDER BY', 'iwl_from' );
+ $this->addOption( 'ORDER BY', 'iwl_from' . $sort );
} else {
- $this->addOption( 'ORDER BY', 'iwl_title, iwl_from' );
+ $this->addOption( 'ORDER BY', array(
+ 'iwl_title' . $sort,
+ 'iwl_from' . $sort
+ ));
}
} else {
- $this->addOption( 'ORDER BY', 'iwl_prefix, iwl_title, iwl_from' );
+ $this->addOption( 'ORDER BY', array(
+ 'iwl_prefix' . $sort,
+ 'iwl_title' . $sort,
+ 'iwl_from' . $sort
+ ));
}
$this->addOption( 'LIMIT', $params['limit'] + 1 );
@@ -170,6 +180,13 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
'iwtitle',
),
),
+ 'dir' => array(
+ ApiBase::PARAM_DFLT => 'ascending',
+ ApiBase::PARAM_TYPE => array(
+ 'ascending',
+ 'descending'
+ )
+ ),
);
}
@@ -184,6 +201,24 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
' iwtitle - Adds the title of the interwiki',
),
'limit' => 'How many total pages to return',
+ 'dir' => 'The direction in which to list',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'pageid' => 'integer',
+ 'ns' => 'namespace',
+ 'title' => 'string',
+ 'redirect' => 'boolean'
+ ),
+ 'iwprefix' => array(
+ 'iwprefix' => 'string'
+ ),
+ 'iwtitle' => array(
+ 'iwtitle' => 'string'
+ )
);
}
@@ -205,7 +240,7 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
public function getExamples() {
return array(
'api.php?action=query&list=iwbacklinks&iwbltitle=Test&iwblprefix=wikibooks',
- 'api.php?action=query&generator=iwbacklinks&giwbltitle=Test&iwblprefix=wikibooks&prop=info'
+ 'api.php?action=query&generator=iwbacklinks&giwbltitle=Test&giwblprefix=wikibooks&prop=info'
);
}
diff --git a/includes/api/ApiQueryIWLinks.php b/includes/api/ApiQueryIWLinks.php
index 13256ad8..30c7f5a8 100644
--- a/includes/api/ApiQueryIWLinks.php
+++ b/includes/api/ApiQueryIWLinks.php
@@ -5,7 +5,7 @@
* Created on May 14, 2010
*
* Copyright © 2010 Sam Reed
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -62,38 +62,40 @@ class ApiQueryIWLinks extends ApiQueryBase {
$this->dieUsage( 'Invalid continue param. You should pass the ' .
'original value returned by the previous query', '_badcontinue' );
}
+ $op = $params['dir'] == 'descending' ? '<' : '>';
+ $db = $this->getDB();
$iwlfrom = intval( $cont[0] );
- $iwlprefix = $this->getDB()->strencode( $cont[1] );
- $iwltitle = $this->getDB()->strencode( $this->titleToKey( $cont[2] ) );
+ $iwlprefix = $db->addQuotes( $cont[1] );
+ $iwltitle = $db->addQuotes( $cont[2] );
$this->addWhere(
- "iwl_from > $iwlfrom OR " .
+ "iwl_from $op $iwlfrom OR " .
"(iwl_from = $iwlfrom AND " .
- "(iwl_prefix > '$iwlprefix' OR " .
- "(iwl_prefix = '$iwlprefix' AND " .
- "iwl_title >= '$iwltitle')))"
+ "(iwl_prefix $op $iwlprefix OR " .
+ "(iwl_prefix = $iwlprefix AND " .
+ "iwl_title $op= $iwltitle)))"
);
}
- $dir = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
if ( isset( $params['prefix'] ) ) {
$this->addWhereFld( 'iwl_prefix', $params['prefix'] );
if ( isset( $params['title'] ) ) {
$this->addWhereFld( 'iwl_title', $params['title'] );
- $this->addOption( 'ORDER BY', 'iwl_from' . $dir );
+ $this->addOption( 'ORDER BY', 'iwl_from' . $sort );
} else {
$this->addOption( 'ORDER BY', array(
- 'iwl_title' . $dir,
- 'iwl_from' . $dir
+ 'iwl_title' . $sort,
+ 'iwl_from' . $sort
));
}
} else {
// Don't order by iwl_from if it's constant in the WHERE clause
if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) {
- $this->addOption( 'ORDER BY', 'iwl_prefix' . $dir );
+ $this->addOption( 'ORDER BY', 'iwl_prefix' . $sort );
} else {
$this->addOption( 'ORDER BY', array (
- 'iwl_from' . $dir,
- 'iwl_prefix' . $dir
+ 'iwl_from' . $sort,
+ 'iwl_prefix' . $sort
));
}
}
@@ -165,6 +167,19 @@ class ApiQueryIWLinks extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'prefix' => 'string',
+ 'url' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ '*' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Returns all interwiki links from the given page(s)';
}
diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php
index 03a24821..d822eed5 100644
--- a/includes/api/ApiQueryImageInfo.php
+++ b/includes/api/ApiQueryImageInfo.php
@@ -4,7 +4,7 @@
*
* Created on July 6, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -73,7 +73,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
$result = $this->getResult();
- $images = RepoGroup::singleton()->findFiles( $titles );
+ //search only inside the local repo
+ if( $params['localonly'] ) {
+ $images = RepoGroup::singleton()->getLocalRepo()->findFiles( $titles );
+ } else {
+ $images = RepoGroup::singleton()->findFiles( $titles );
+ }
foreach ( $images as $img ) {
// Skip redirects
if ( $img->getOriginalTitle()->isRedirect() ) {
@@ -81,14 +86,14 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
$start = $skip ? $fromTimestamp : $params['start'];
- $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ];
+ $pageId = $pageIds[NS_FILE][ $img->getOriginalTitle()->getDBkey() ];
$fit = $result->addValue(
array( 'query', 'pages', intval( $pageId ) ),
'imagerepository', $img->getRepoName()
);
if ( !$fit ) {
- if ( count( $pageIds[NS_IMAGE] ) == 1 ) {
+ if ( count( $pageIds[NS_FILE] ) == 1 ) {
// The user is screwed. imageinfo can't be solely
// responsible for exceeding the limit in this case,
// so set a query-continue that just returns the same
@@ -119,7 +124,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
self::getInfo( $img, $prop, $result,
$finalThumbParams, $params['metadataversion'] ) );
if ( !$fit ) {
- if ( count( $pageIds[NS_IMAGE] ) == 1 ) {
+ if ( count( $pageIds[NS_FILE] ) == 1 ) {
// See the 'the user is screwed' comment above
$this->setContinueEnumParameter( 'start',
wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) );
@@ -149,7 +154,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
self::getInfo( $oldie, $prop, $result,
$finalThumbParams, $params['metadataversion'] ) );
if ( !$fit ) {
- if ( count( $pageIds[NS_IMAGE] ) == 1 ) {
+ if ( count( $pageIds[NS_FILE] ) == 1 ) {
$this->setContinueEnumParameter( 'start',
wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) );
} else {
@@ -356,8 +361,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) {
list( $ext, $mime ) = $file->getHandler()->getThumbType(
- substr( $mto->getPath(), strrpos( $mto->getPath(), '.' ) + 1 ),
- $file->getMimeType(), $thumbParams );
+ $mto->getExtension(), $file->getMimeType(), $thumbParams );
$vals['thumbmime'] = $mime;
}
} elseif ( $mto && $mto->isError() ) {
@@ -430,7 +434,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
* @param $img File
* @return string
*/
- private function getContinueStr( $img ) {
+ protected function getContinueStr( $img ) {
return $img->getOriginalTitle()->getText() .
'|' . $img->getTimestamp();
}
@@ -472,6 +476,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
ApiBase::PARAM_TYPE => 'string',
),
'continue' => null,
+ 'localonly' => false,
);
}
@@ -491,7 +496,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
*
* @return array
*/
- private static function getProperties() {
+ private static function getProperties( $modulePrefix = '' ) {
return array(
'timestamp' => ' timestamp - Adds timestamp for the uploaded version',
'user' => ' user - Adds the user who uploaded the image version',
@@ -503,7 +508,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
'dimensions' => ' dimensions - Alias for size', // For backwards compatibility with Allimages
'sha1' => ' sha1 - Adds SHA-1 hash for the image',
'mime' => ' mime - Adds MIME type of the image',
- 'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail (requires url)',
+ 'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail' .
+ ' (requires url and param ' . $modulePrefix . 'urlwidth)',
'mediatype' => ' mediatype - Adds the media type of the image',
'metadata' => ' metadata - Lists EXIF metadata for the version of the image',
'archivename' => ' archivename - Adds the file name of the archive version for non-latest versions',
@@ -518,10 +524,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
*
* @return array
*/
- public static function getPropertyDescriptions( $filter = array() ) {
+ public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) {
return array_merge(
array( 'What image information to get:' ),
- array_values( array_diff_key( self::getProperties(), array_flip( $filter ) ) )
+ array_values( array_diff_key( self::getProperties( $modulePrefix ), array_flip( $filter ) ) )
);
}
@@ -532,7 +538,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
public function getParamDescription() {
$p = $this->getModulePrefix();
return array(
- 'prop' => self::getPropertyDescriptions(),
+ 'prop' => self::getPropertyDescriptions( array(), $p ),
'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
'Only the current version of the image can be scaled' ),
'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth",
@@ -543,10 +549,119 @@ class ApiQueryImageInfo extends ApiQueryBase {
'end' => 'Timestamp to stop listing at',
'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
"Defaults to '1' for backwards compatibility" ),
- 'continue' => 'If the query response includes a continue value, use it here to get another page of results'
+ 'continue' => 'If the query response includes a continue value, use it here to get another page of results',
+ 'localonly' => 'Look only for files in the local repository',
);
}
+ public static function getResultPropertiesFiltered( $filter = array() ) {
+ $props = array(
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'user' => array(
+ 'userhidden' => 'boolean',
+ 'user' => 'string',
+ 'anon' => 'boolean'
+ ),
+ 'userid' => array(
+ 'userhidden' => 'boolean',
+ 'userid' => 'integer',
+ 'anon' => 'boolean'
+ ),
+ 'size' => array(
+ 'size' => 'integer',
+ 'width' => 'integer',
+ 'height' => 'integer',
+ 'pagecount' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'comment' => array(
+ 'commenthidden' => 'boolean',
+ 'comment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'parsedcomment' => array(
+ 'commenthidden' => 'boolean',
+ 'parsedcomment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'url' => array(
+ 'filehidden' => 'boolean',
+ 'thumburl' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'thumbwidth' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'thumbheight' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'thumberror' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'url' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'descriptionurl' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'sha1' => array(
+ 'filehidden' => 'boolean',
+ 'sha1' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'mime' => array(
+ 'filehidden' => 'boolean',
+ 'mime' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'mediatype' => array(
+ 'filehidden' => 'boolean',
+ 'mediatype' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'archivename' => array(
+ 'filehidden' => 'boolean',
+ 'archivename' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'bitdepth' => array(
+ 'filehidden' => 'boolean',
+ 'bitdepth' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ );
+ return array_diff_key( $props, array_flip( $filter ) );
+ }
+
+ public function getResultProperties() {
+ return self::getResultPropertiesFiltered();
+ }
+
public function getDescription() {
return 'Returns image information and upload history';
}
diff --git a/includes/api/ApiQueryImages.php b/includes/api/ApiQueryImages.php
index f03b2874..6052a75f 100644
--- a/includes/api/ApiQueryImages.php
+++ b/includes/api/ApiQueryImages.php
@@ -4,7 +4,7 @@
*
* Created on May 13, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,7 +25,8 @@
*/
/**
- * This query adds an <images> subelement to all pages with the list of images embedded into those pages.
+ * This query adds an "<images>" subelement to all pages with the list of
+ * images embedded into those pages.
*
* @ingroup API
*/
@@ -65,23 +66,24 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
$this->dieUsage( 'Invalid continue param. You should pass the ' .
'original value returned by the previous query', '_badcontinue' );
}
+ $op = $params['dir'] == 'descending' ? '<' : '>';
$ilfrom = intval( $cont[0] );
- $ilto = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) );
+ $ilto = $this->getDB()->addQuotes( $cont[1] );
$this->addWhere(
- "il_from > $ilfrom OR " .
+ "il_from $op $ilfrom OR " .
"(il_from = $ilfrom AND " .
- "il_to >= '$ilto')"
+ "il_to $op= $ilto)"
);
}
- $dir = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
// Don't order by il_from if it's constant in the WHERE clause
if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) {
- $this->addOption( 'ORDER BY', 'il_to' . $dir );
+ $this->addOption( 'ORDER BY', 'il_to' . $sort );
} else {
$this->addOption( 'ORDER BY', array(
- 'il_from' . $dir,
- 'il_to' . $dir
+ 'il_from' . $sort,
+ 'il_to' . $sort
));
}
$this->addOption( 'LIMIT', $params['limit'] + 1 );
@@ -107,16 +109,14 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
if ( ++$count > $params['limit'] ) {
// We've reached the one extra which shows that
// there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter( 'continue', $row->il_from .
- '|' . $this->keyToTitle( $row->il_to ) );
+ $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
break;
}
$vals = array();
ApiQueryBase::addTitleInfo( $vals, Title::makeTitle( NS_FILE, $row->il_to ) );
$fit = $this->addPageSubItem( $row->il_from, $vals );
if ( !$fit ) {
- $this->setContinueEnumParameter( 'continue', $row->il_from .
- '|' . $this->keyToTitle( $row->il_to ) );
+ $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
break;
}
}
@@ -127,8 +127,7 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
if ( ++$count > $params['limit'] ) {
// We've reached the one extra which shows that
// there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter( 'continue', $row->il_from .
- '|' . $this->keyToTitle( $row->il_to ) );
+ $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
break;
}
$titles[] = Title::makeTitle( NS_FILE, $row->il_to );
@@ -173,6 +172,15 @@ class ApiQueryImages extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Returns all images contained on the given page(s)';
}
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index f0d0faa3..5d4f0346 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -4,7 +4,7 @@
*
* Created on Sep 25, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ class ApiQueryInfo extends ApiQueryBase {
private $fld_protection = false, $fld_talkid = false,
$fld_subjectid = false, $fld_url = false,
- $fld_readable = false, $fld_watched = false,
+ $fld_readable = false, $fld_watched = false, $fld_notificationtimestamp = false,
$fld_preload = false, $fld_displaytitle = false;
private $params, $titles, $missing, $everything, $pageCounter;
@@ -41,7 +41,7 @@ class ApiQueryInfo extends ApiQueryBase {
private $pageRestrictions, $pageIsRedir, $pageIsNew, $pageTouched,
$pageLatest, $pageLength;
- private $protections, $watched, $talkids, $subjectids, $displaytitles;
+ private $protections, $watched, $notificationtimestamps, $talkids, $subjectids, $displaytitles;
private $tokenFunctions;
@@ -57,7 +57,10 @@ class ApiQueryInfo extends ApiQueryBase {
global $wgDisableCounters;
$pageSet->requestField( 'page_restrictions' );
- $pageSet->requestField( 'page_is_redirect' );
+ // when resolving redirects, no page will have this field
+ if( !$pageSet->isResolvingRedirects() ) {
+ $pageSet->requestField( 'page_is_redirect' );
+ }
$pageSet->requestField( 'page_is_new' );
if ( !$wgDisableCounters ) {
$pageSet->requestField( 'page_counter' );
@@ -99,6 +102,12 @@ class ApiQueryInfo extends ApiQueryBase {
return $this->tokenFunctions;
}
+ static $cachedTokens = array();
+
+ public static function resetTokenCache() {
+ ApiQueryInfo::$cachedTokens = array();
+ }
+
public static function getEditToken( $pageid, $title ) {
// We could check for $title->userCan('edit') here,
// but that's too expensive for this purpose
@@ -108,14 +117,12 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- // The edit token is always the same, let's exploit that
- static $cachedEditToken = null;
- if ( !is_null( $cachedEditToken ) ) {
- return $cachedEditToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'edit' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'edit' ] = $wgUser->getEditToken();
}
- $cachedEditToken = $wgUser->getEditToken();
- return $cachedEditToken;
+ return ApiQueryInfo::$cachedTokens[ 'edit' ];
}
public static function getDeleteToken( $pageid, $title ) {
@@ -124,13 +131,12 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- static $cachedDeleteToken = null;
- if ( !is_null( $cachedDeleteToken ) ) {
- return $cachedDeleteToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'delete' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'delete' ] = $wgUser->getEditToken();
}
- $cachedDeleteToken = $wgUser->getEditToken();
- return $cachedDeleteToken;
+ return ApiQueryInfo::$cachedTokens[ 'delete' ];
}
public static function getProtectToken( $pageid, $title ) {
@@ -139,13 +145,12 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- static $cachedProtectToken = null;
- if ( !is_null( $cachedProtectToken ) ) {
- return $cachedProtectToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'protect' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'protect' ] = $wgUser->getEditToken();
}
- $cachedProtectToken = $wgUser->getEditToken();
- return $cachedProtectToken;
+ return ApiQueryInfo::$cachedTokens[ 'protect' ];
}
public static function getMoveToken( $pageid, $title ) {
@@ -154,13 +159,12 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- static $cachedMoveToken = null;
- if ( !is_null( $cachedMoveToken ) ) {
- return $cachedMoveToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'move' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'move' ] = $wgUser->getEditToken();
}
- $cachedMoveToken = $wgUser->getEditToken();
- return $cachedMoveToken;
+ return ApiQueryInfo::$cachedTokens[ 'move' ];
}
public static function getBlockToken( $pageid, $title ) {
@@ -169,13 +173,12 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- static $cachedBlockToken = null;
- if ( !is_null( $cachedBlockToken ) ) {
- return $cachedBlockToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'block' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'block' ] = $wgUser->getEditToken();
}
- $cachedBlockToken = $wgUser->getEditToken();
- return $cachedBlockToken;
+ return ApiQueryInfo::$cachedTokens[ 'block' ];
}
public static function getUnblockToken( $pageid, $title ) {
@@ -189,13 +192,12 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- static $cachedEmailToken = null;
- if ( !is_null( $cachedEmailToken ) ) {
- return $cachedEmailToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'email' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'email' ] = $wgUser->getEditToken();
}
- $cachedEmailToken = $wgUser->getEditToken();
- return $cachedEmailToken;
+ return ApiQueryInfo::$cachedTokens[ 'email' ];
}
public static function getImportToken( $pageid, $title ) {
@@ -204,13 +206,12 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- static $cachedImportToken = null;
- if ( !is_null( $cachedImportToken ) ) {
- return $cachedImportToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'import' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'import' ] = $wgUser->getEditToken();
}
- $cachedImportToken = $wgUser->getEditToken();
- return $cachedImportToken;
+ return ApiQueryInfo::$cachedTokens[ 'import' ];
}
public static function getWatchToken( $pageid, $title ) {
@@ -219,13 +220,26 @@ class ApiQueryInfo extends ApiQueryBase {
return false;
}
- static $cachedWatchToken = null;
- if ( !is_null( $cachedWatchToken ) ) {
- return $cachedWatchToken;
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'watch' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'watch' ] = $wgUser->getEditToken( 'watch' );
}
- $cachedWatchToken = $wgUser->getEditToken( 'watch' );
- return $cachedWatchToken;
+ return ApiQueryInfo::$cachedTokens[ 'watch' ];
+ }
+
+ public static function getOptionsToken( $pageid, $title ) {
+ global $wgUser;
+ if ( !$wgUser->isLoggedIn() ) {
+ return false;
+ }
+
+ // The token is always the same, let's exploit that
+ if ( !isset( ApiQueryInfo::$cachedTokens[ 'options' ] ) ) {
+ ApiQueryInfo::$cachedTokens[ 'options' ] = $wgUser->getEditToken();
+ }
+
+ return ApiQueryInfo::$cachedTokens[ 'options' ];
}
public function execute() {
@@ -234,6 +248,7 @@ class ApiQueryInfo extends ApiQueryBase {
$prop = array_flip( $this->params['prop'] );
$this->fld_protection = isset( $prop['protection'] );
$this->fld_watched = isset( $prop['watched'] );
+ $this->fld_notificationtimestamp = isset( $prop['notificationtimestamp'] );
$this->fld_talkid = isset( $prop['talkid'] );
$this->fld_subjectid = isset( $prop['subjectid'] );
$this->fld_url = isset( $prop['url'] );
@@ -269,7 +284,10 @@ class ApiQueryInfo extends ApiQueryBase {
}
$this->pageRestrictions = $pageSet->getCustomField( 'page_restrictions' );
- $this->pageIsRedir = $pageSet->getCustomField( 'page_is_redirect' );
+ // when resolving redirects, no page will have this field
+ $this->pageIsRedir = !$pageSet->isResolvingRedirects()
+ ? $pageSet->getCustomField( 'page_is_redirect' )
+ : array();
$this->pageIsNew = $pageSet->getCustomField( 'page_is_new' );
global $wgDisableCounters;
@@ -286,7 +304,7 @@ class ApiQueryInfo extends ApiQueryBase {
$this->getProtectionInfo();
}
- if ( $this->fld_watched ) {
+ if ( $this->fld_watched || $this->fld_notificationtimestamp ) {
$this->getWatchedInfo();
}
@@ -322,7 +340,10 @@ class ApiQueryInfo extends ApiQueryBase {
*/
private function extractPageInfo( $pageid, $title ) {
$pageInfo = array();
- if ( $title->exists() ) {
+ $titleExists = $pageid > 0; //$title->exists() needs pageid, which is not set for all title objects
+ $ns = $title->getNamespace();
+ $dbkey = $title->getDBkey();
+ if ( $titleExists ) {
global $wgDisableCounters;
$pageInfo['touched'] = wfTimestamp( TS_ISO_8601, $this->pageTouched[$pageid] );
@@ -332,7 +353,7 @@ class ApiQueryInfo extends ApiQueryBase {
: intval( $this->pageCounter[$pageid] );
$pageInfo['length'] = intval( $this->pageLength[$pageid] );
- if ( $this->pageIsRedir[$pageid] ) {
+ if ( isset( $this->pageIsRedir[$pageid] ) && $this->pageIsRedir[$pageid] ) {
$pageInfo['redirect'] = '';
}
if ( $this->pageIsNew[$pageid] ) {
@@ -355,23 +376,30 @@ class ApiQueryInfo extends ApiQueryBase {
if ( $this->fld_protection ) {
$pageInfo['protection'] = array();
- if ( isset( $this->protections[$title->getNamespace()][$title->getDBkey()] ) ) {
+ if ( isset( $this->protections[$ns][$dbkey] ) ) {
$pageInfo['protection'] =
- $this->protections[$title->getNamespace()][$title->getDBkey()];
+ $this->protections[$ns][$dbkey];
}
$this->getResult()->setIndexedTagName( $pageInfo['protection'], 'pr' );
}
- if ( $this->fld_watched && isset( $this->watched[$title->getNamespace()][$title->getDBkey()] ) ) {
+ if ( $this->fld_watched && isset( $this->watched[$ns][$dbkey] ) ) {
$pageInfo['watched'] = '';
}
- if ( $this->fld_talkid && isset( $this->talkids[$title->getNamespace()][$title->getDBkey()] ) ) {
- $pageInfo['talkid'] = $this->talkids[$title->getNamespace()][$title->getDBkey()];
+ if ( $this->fld_notificationtimestamp ) {
+ $pageInfo['notificationtimestamp'] = '';
+ if ( isset( $this->notificationtimestamps[$ns][$dbkey] ) ) {
+ $pageInfo['notificationtimestamp'] = wfTimestamp( TS_ISO_8601, $this->notificationtimestamps[$ns][$dbkey] );
+ }
+ }
+
+ if ( $this->fld_talkid && isset( $this->talkids[$ns][$dbkey] ) ) {
+ $pageInfo['talkid'] = $this->talkids[$ns][$dbkey];
}
- if ( $this->fld_subjectid && isset( $this->subjectids[$title->getNamespace()][$title->getDBkey()] ) ) {
- $pageInfo['subjectid'] = $this->subjectids[$title->getNamespace()][$title->getDBkey()];
+ if ( $this->fld_subjectid && isset( $this->subjectids[$ns][$dbkey] ) ) {
+ $pageInfo['subjectid'] = $this->subjectids[$ns][$dbkey];
}
if ( $this->fld_url ) {
@@ -383,7 +411,7 @@ class ApiQueryInfo extends ApiQueryBase {
}
if ( $this->fld_preload ) {
- if ( $title->exists() ) {
+ if ( $titleExists ) {
$pageInfo['preload'] = '';
} else {
$text = null;
@@ -394,8 +422,8 @@ class ApiQueryInfo extends ApiQueryBase {
}
if ( $this->fld_displaytitle ) {
- if ( isset( $this->displaytitles[$title->getArticleId()] ) ) {
- $pageInfo['displaytitle'] = $this->displaytitles[$title->getArticleId()];
+ if ( isset( $this->displaytitles[$pageid] ) ) {
+ $pageInfo['displaytitle'] = $this->displaytitles[$pageid];
} else {
$pageInfo['displaytitle'] = $title->getPrefixedText();
}
@@ -415,15 +443,14 @@ class ApiQueryInfo extends ApiQueryBase {
// Get normal protections for existing titles
if ( count( $this->titles ) ) {
$this->resetQueryParams();
- $this->addTables( array( 'page_restrictions', 'page' ) );
- $this->addWhere( 'page_id=pr_page' );
+ $this->addTables( 'page_restrictions' );
$this->addFields( array( 'pr_page', 'pr_type', 'pr_level',
- 'pr_expiry', 'pr_cascade', 'page_namespace',
- 'page_title' ) );
+ 'pr_expiry', 'pr_cascade' ) );
$this->addWhereFld( 'pr_page', array_keys( $this->titles ) );
$res = $this->select( __METHOD__ );
foreach ( $res as $row ) {
+ $title = $this->titles[$row->pr_page];
$a = array(
'type' => $row->pr_type,
'level' => $row->pr_level,
@@ -432,11 +459,14 @@ class ApiQueryInfo extends ApiQueryBase {
if ( $row->pr_cascade ) {
$a['cascade'] = '';
}
- $this->protections[$row->page_namespace][$row->page_title][] = $a;
-
- // Also check old restrictions
- if ( $this->pageRestrictions[$row->pr_page] ) {
- $restrictions = explode( ':', trim( $this->pageRestrictions[$row->pr_page] ) );
+ $this->protections[$title->getNamespace()][$title->getDBkey()][] = $a;
+ }
+ // Also check old restrictions
+ foreach( $this->titles as $pageId => $title ) {
+ if ( $this->pageRestrictions[$pageId] ) {
+ $namespace = $title->getNamespace();
+ $dbKey = $title->getDBkey();
+ $restrictions = explode( ':', trim( $this->pageRestrictions[$pageId] ) );
foreach ( $restrictions as $restrict ) {
$temp = explode( '=', trim( $restrict ) );
if ( count( $temp ) == 1 ) {
@@ -446,12 +476,12 @@ class ApiQueryInfo extends ApiQueryBase {
if ( $restriction == '' ) {
continue;
}
- $this->protections[$row->page_namespace][$row->page_title][] = array(
+ $this->protections[$namespace][$dbKey][] = array(
'type' => 'edit',
'level' => $restriction,
'expiry' => 'infinity',
);
- $this->protections[$row->page_namespace][$row->page_title][] = array(
+ $this->protections[$namespace][$dbKey][] = array(
'type' => 'move',
'level' => $restriction,
'expiry' => 'infinity',
@@ -461,7 +491,7 @@ class ApiQueryInfo extends ApiQueryBase {
if ( $restriction == '' ) {
continue;
}
- $this->protections[$row->page_namespace][$row->page_title][] = array(
+ $this->protections[$namespace][$dbKey][] = array(
'type' => $temp[0],
'level' => $restriction,
'expiry' => 'infinity',
@@ -612,6 +642,7 @@ class ApiQueryInfo extends ApiQueryBase {
/**
* Get information about watched status and put it in $this->watched
+ * and $this->notificationtimestamps
*/
private function getWatchedInfo() {
$user = $this->getUser();
@@ -621,6 +652,7 @@ class ApiQueryInfo extends ApiQueryBase {
}
$this->watched = array();
+ $this->notificationtimestamps = array();
$db = $this->getDB();
$lb = new LinkBatch( $this->everything );
@@ -628,6 +660,7 @@ class ApiQueryInfo extends ApiQueryBase {
$this->resetQueryParams();
$this->addTables( array( 'watchlist' ) );
$this->addFields( array( 'wl_title', 'wl_namespace' ) );
+ $this->addFieldsIf( 'wl_notificationtimestamp', $this->fld_notificationtimestamp );
$this->addWhere( array(
$lb->constructSet( 'wl', $db ),
'wl_user' => $user->getID()
@@ -636,7 +669,12 @@ class ApiQueryInfo extends ApiQueryBase {
$res = $this->select( __METHOD__ );
foreach ( $res as $row ) {
- $this->watched[$row->wl_namespace][$row->wl_title] = true;
+ if ( $this->fld_watched ) {
+ $this->watched[$row->wl_namespace][$row->wl_title] = true;
+ }
+ if ( $this->fld_notificationtimestamp ) {
+ $this->notificationtimestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
+ }
}
}
@@ -671,6 +709,7 @@ class ApiQueryInfo extends ApiQueryBase {
'protection',
'talkid',
'watched', # private
+ 'notificationtimestamp', # private
'subjectid',
'url',
'readable', # private
@@ -692,20 +731,80 @@ class ApiQueryInfo extends ApiQueryBase {
return array(
'prop' => array(
'Which additional properties to get:',
- ' protection - List the protection level of each page',
- ' talkid - The page ID of the talk page for each non-talk page',
- ' watched - List the watched status of each page',
- ' subjectid - The page ID of the parent page for each talk page',
- ' url - Gives a full URL to the page, and also an edit URL',
- ' readable - Whether the user can read this page',
- ' preload - Gives the text returned by EditFormPreloadText',
- ' displaytitle - Gives the way the page title is actually displayed',
+ ' protection - List the protection level of each page',
+ ' talkid - The page ID of the talk page for each non-talk page',
+ ' watched - List the watched status of each page',
+ ' notificationtimestamp - The watchlist notification timestamp of each page',
+ ' subjectid - The page ID of the parent page for each talk page',
+ ' url - Gives a full URL to the page, and also an edit URL',
+ ' readable - Whether the user can read this page',
+ ' preload - Gives the text returned by EditFormPreloadText',
+ ' displaytitle - Gives the way the page title is actually displayed',
),
'token' => 'Request a token to perform a data-modifying action on a page',
'continue' => 'When more results are available, use this to continue',
);
}
+ public function getResultProperties() {
+ $props = array(
+ ApiBase::PROP_LIST => false,
+ '' => array(
+ 'touched' => 'timestamp',
+ 'lastrevid' => 'integer',
+ 'counter' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'length' => 'integer',
+ 'redirect' => 'boolean',
+ 'new' => 'boolean',
+ 'starttimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'watched' => array(
+ 'watched' => 'boolean'
+ ),
+ 'notificationtimestamp' => array(
+ 'notificationtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'talkid' => array(
+ 'talkid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'subjectid' => array(
+ 'subjectid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'url' => array(
+ 'fullurl' => 'string',
+ 'editurl' => 'string'
+ ),
+ 'readable' => array(
+ 'readable' => 'boolean'
+ ),
+ 'preload' => array(
+ 'preload' => 'string'
+ ),
+ 'displaytitle' => array(
+ 'displaytitle' => 'string'
+ )
+ );
+
+ self::addTokenProperties( $props, $this->getTokenFunctions() );
+
+ return $props;
+ }
+
public function getDescription() {
return 'Get basic page information such as namespace, title, last touched date, ...';
}
diff --git a/includes/api/ApiQueryLangBacklinks.php b/includes/api/ApiQueryLangBacklinks.php
index 15734944..3920407b 100644
--- a/includes/api/ApiQueryLangBacklinks.php
+++ b/includes/api/ApiQueryLangBacklinks.php
@@ -5,7 +5,7 @@
* Created on May 14, 2011
*
* Copyright © 2011 Sam Reed
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -61,15 +61,17 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
'original value returned by the previous query', '_badcontinue' );
}
- $prefix = $this->getDB()->strencode( $cont[0] );
- $title = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) );
+ $db = $this->getDB();
+ $op = $params['dir'] == 'descending' ? '<' : '>';
+ $prefix = $db->addQuotes( $cont[0] );
+ $title = $db->addQuotes( $cont[1] );
$from = intval( $cont[2] );
$this->addWhere(
- "ll_lang > '$prefix' OR " .
- "(ll_lang = '$prefix' AND " .
- "(ll_title > '$title' OR " .
- "(ll_title = '$title' AND " .
- "ll_from >= $from)))"
+ "ll_lang $op $prefix OR " .
+ "(ll_lang = $prefix AND " .
+ "(ll_title $op $title OR " .
+ "(ll_title = $title AND " .
+ "ll_from $op= $from)))"
);
}
@@ -83,16 +85,24 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
$this->addFields( array( 'page_id', 'page_title', 'page_namespace', 'page_is_redirect',
'll_from', 'll_lang', 'll_title' ) );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
if ( isset( $params['lang'] ) ) {
$this->addWhereFld( 'll_lang', $params['lang'] );
if ( isset( $params['title'] ) ) {
$this->addWhereFld( 'll_title', $params['title'] );
- $this->addOption( 'ORDER BY', 'll_from' );
+ $this->addOption( 'ORDER BY', 'll_from' . $sort );
} else {
- $this->addOption( 'ORDER BY', 'll_title, ll_from' );
+ $this->addOption( 'ORDER BY', array(
+ 'll_title' . $sort,
+ 'll_from' . $sort
+ ));
}
} else {
- $this->addOption( 'ORDER BY', 'll_lang, ll_title, ll_from' );
+ $this->addOption( 'ORDER BY', array(
+ 'll_lang' . $sort,
+ 'll_title' . $sort,
+ 'll_from' . $sort
+ ));
}
$this->addOption( 'LIMIT', $params['limit'] + 1 );
@@ -170,6 +180,13 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
'lltitle',
),
),
+ 'dir' => array(
+ ApiBase::PARAM_DFLT => 'ascending',
+ ApiBase::PARAM_TYPE => array(
+ 'ascending',
+ 'descending'
+ )
+ ),
);
}
@@ -184,6 +201,24 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
' lltitle - Adds the title of the language ink',
),
'limit' => 'How many total pages to return',
+ 'dir' => 'The direction in which to list',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'pageid' => 'integer',
+ 'ns' => 'namespace',
+ 'title' => 'string',
+ 'redirect' => 'boolean'
+ ),
+ 'lllang' => array(
+ 'lllang' => 'string'
+ ),
+ 'lltitle' => array(
+ 'lltitle' => 'string'
+ )
);
}
@@ -205,7 +240,7 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
public function getExamples() {
return array(
'api.php?action=query&list=langbacklinks&lbltitle=Test&lbllang=fr',
- 'api.php?action=query&generator=langbacklinks&glbltitle=Test&lbllang=fr&prop=info'
+ 'api.php?action=query&generator=langbacklinks&glbltitle=Test&glbllang=fr&prop=info'
);
}
diff --git a/includes/api/ApiQueryLangLinks.php b/includes/api/ApiQueryLangLinks.php
index fdba8465..3109a090 100644
--- a/includes/api/ApiQueryLangLinks.php
+++ b/includes/api/ApiQueryLangLinks.php
@@ -4,7 +4,7 @@
*
* Created on May 13, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -60,35 +60,36 @@ class ApiQueryLangLinks extends ApiQueryBase {
$this->dieUsage( 'Invalid continue param. You should pass the ' .
'original value returned by the previous query', '_badcontinue' );
}
+ $op = $params['dir'] == 'descending' ? '<' : '>';
$llfrom = intval( $cont[0] );
- $lllang = $this->getDB()->strencode( $cont[1] );
+ $lllang = $this->getDB()->addQuotes( $cont[1] );
$this->addWhere(
- "ll_from > $llfrom OR " .
+ "ll_from $op $llfrom OR " .
"(ll_from = $llfrom AND " .
- "ll_lang >= '$lllang')"
+ "ll_lang $op= $lllang)"
);
}
- $dir = ( $params['dir'] == 'descending' ? ' DESC' : '' );
- if ( isset( $params['lang'] ) ) {
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ if ( isset( $params['lang'] ) ) {
$this->addWhereFld( 'll_lang', $params['lang'] );
if ( isset( $params['title'] ) ) {
$this->addWhereFld( 'll_title', $params['title'] );
- $this->addOption( 'ORDER BY', 'll_from' . $dir );
+ $this->addOption( 'ORDER BY', 'll_from' . $sort );
} else {
$this->addOption( 'ORDER BY', array(
- 'll_title' . $dir,
- 'll_from' . $dir
+ 'll_title' . $sort,
+ 'll_from' . $sort
));
}
} else {
// Don't order by ll_from if it's constant in the WHERE clause
if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) {
- $this->addOption( 'ORDER BY', 'll_lang' . $dir );
+ $this->addOption( 'ORDER BY', 'll_lang' . $sort );
} else {
$this->addOption( 'ORDER BY', array(
- 'll_from' . $dir,
- 'll_lang' . $dir
+ 'll_from' . $sort,
+ 'll_lang' . $sort
));
}
}
@@ -158,6 +159,19 @@ class ApiQueryLangLinks extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'lang' => 'string',
+ 'url' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ '*' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Returns all interlanguage links from the given page(s)';
}
diff --git a/includes/api/ApiQueryLinks.php b/includes/api/ApiQueryLinks.php
index 0377eddb..9e4b7ebb 100644
--- a/includes/api/ApiQueryLinks.php
+++ b/includes/api/ApiQueryLinks.php
@@ -4,7 +4,7 @@
*
* Created on May 12, 2007
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -85,9 +85,9 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
$this->addFields( array(
- $this->prefix . '_from AS pl_from',
- $this->prefix . '_namespace AS pl_namespace',
- $this->prefix . '_title AS pl_title'
+ 'pl_from' => $this->prefix . '_from',
+ 'pl_namespace' => $this->prefix . '_namespace',
+ 'pl_title' => $this->prefix . '_title'
) );
$this->addTables( $this->table );
@@ -116,19 +116,20 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
$this->dieUsage( 'Invalid continue param. You should pass the ' .
'original value returned by the previous query', '_badcontinue' );
}
+ $op = $params['dir'] == 'descending' ? '<' : '>';
$plfrom = intval( $cont[0] );
$plns = intval( $cont[1] );
- $pltitle = $this->getDB()->strencode( $this->titleToKey( $cont[2] ) );
+ $pltitle = $this->getDB()->addQuotes( $cont[2] );
$this->addWhere(
- "{$this->prefix}_from > $plfrom OR " .
+ "{$this->prefix}_from $op $plfrom OR " .
"({$this->prefix}_from = $plfrom AND " .
- "({$this->prefix}_namespace > $plns OR " .
+ "({$this->prefix}_namespace $op $plns OR " .
"({$this->prefix}_namespace = $plns AND " .
- "{$this->prefix}_title >= '$pltitle')))"
+ "{$this->prefix}_title $op= $pltitle)))"
);
}
- $dir = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
// Here's some MySQL craziness going on: if you use WHERE foo='bar'
// and later ORDER BY foo MySQL doesn't notice the ORDER BY is pointless
// but instead goes and filesorts, because the index for foo was used
@@ -136,13 +137,13 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
// clause from the ORDER BY clause
$order = array();
if ( count( $this->getPageSet()->getGoodTitles() ) != 1 ) {
- $order[] = $this->prefix . '_from' . $dir;
+ $order[] = $this->prefix . '_from' . $sort;
}
if ( count( $params['namespace'] ) != 1 ) {
- $order[] = $this->prefix . '_namespace' . $dir;
+ $order[] = $this->prefix . '_namespace' . $sort;
}
- $order[] = $this->prefix . "_title" . $dir;
+ $order[] = $this->prefix . '_title' . $sort;
$this->addOption( 'ORDER BY', $order );
$this->addOption( 'USE INDEX', $this->prefix . '_from' );
$this->addOption( 'LIMIT', $params['limit'] + 1 );
@@ -156,8 +157,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
// We've reached the one extra which shows that
// there are additional pages to be had. Stop here...
$this->setContinueEnumParameter( 'continue',
- "{$row->pl_from}|{$row->pl_namespace}|" .
- $this->keyToTitle( $row->pl_title ) );
+ "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
break;
}
$vals = array();
@@ -165,8 +165,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
$fit = $this->addPageSubItem( $row->pl_from, $vals );
if ( !$fit ) {
$this->setContinueEnumParameter( 'continue',
- "{$row->pl_from}|{$row->pl_namespace}|" .
- $this->keyToTitle( $row->pl_title ) );
+ "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
break;
}
}
@@ -178,8 +177,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
// We've reached the one extra which shows that
// there are additional pages to be had. Stop here...
$this->setContinueEnumParameter( 'continue',
- "{$row->pl_from}|{$row->pl_namespace}|" .
- $this->keyToTitle( $row->pl_title ) );
+ "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
break;
}
$titles[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
@@ -226,6 +224,15 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return "Returns all {$this->description}s from the given page(s)";
}
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
index 0d07a254..5d85c221 100644
--- a/includes/api/ApiQueryLogEvents.php
+++ b/includes/api/ApiQueryLogEvents.php
@@ -4,7 +4,7 @@
*
* Created on Oct 16, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -195,6 +195,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
* @param $type string
* @param $action string
* @param $ts
+ * @param $legacy bool
* @return array
*/
public static function addLogParams( $result, &$vals, $params, $type, $action, $ts, $legacy = false ) {
@@ -262,6 +263,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
if ( !is_null( $params ) ) {
$result->setIndexedTagName( $params, 'param' );
+ $result->setIndexedTagName_recursive( $params, 'param' );
$vals = array_merge( $vals, $params );
}
return $vals;
@@ -358,7 +360,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
public function getCacheMode( $params ) {
if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
- // formatComment() calls wfMsg() among other things
+ // formatComment() calls wfMessage() among other things
return 'anon-public-user-private';
} else {
return 'public';
@@ -366,7 +368,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
public function getAllowedParams() {
- global $wgLogTypes, $wgLogActions;
+ global $wgLogTypes, $wgLogActions, $wgLogActionsHandlers;
return array(
'prop' => array(
ApiBase::PARAM_ISMULTI => true,
@@ -388,7 +390,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
ApiBase::PARAM_TYPE => $wgLogTypes
),
'action' => array(
- ApiBase::PARAM_TYPE => array_keys( $wgLogActions )
+ ApiBase::PARAM_TYPE => array_keys( array_merge( $wgLogActions, $wgLogActionsHandlers ) )
),
'start' => array(
ApiBase::PARAM_TYPE => 'timestamp'
@@ -446,6 +448,62 @@ class ApiQueryLogEvents extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ global $wgLogTypes;
+ return array(
+ 'ids' => array(
+ 'logid' => 'integer',
+ 'pageid' => 'integer'
+ ),
+ 'title' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'type' => array(
+ 'type' => array(
+ ApiBase::PROP_TYPE => $wgLogTypes
+ ),
+ 'action' => 'string'
+ ),
+ 'details' => array(
+ 'actionhidden' => 'boolean'
+ ),
+ 'user' => array(
+ 'userhidden' => 'boolean',
+ 'user' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'anon' => 'boolean'
+ ),
+ 'userid' => array(
+ 'userhidden' => 'boolean',
+ 'userid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'anon' => 'boolean'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'comment' => array(
+ 'commenthidden' => 'boolean',
+ 'comment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'parsedcomment' => array(
+ 'commenthidden' => 'boolean',
+ 'parsedcomment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'Get events from logs';
}
diff --git a/includes/api/ApiQueryProtectedTitles.php b/includes/api/ApiQueryProtectedTitles.php
index 44cc1d32..14aed28d 100644
--- a/includes/api/ApiQueryProtectedTitles.php
+++ b/includes/api/ApiQueryProtectedTitles.php
@@ -4,7 +4,7 @@
*
* Created on Feb 13, 2009
*
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<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
@@ -139,7 +139,7 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
public function getCacheMode( $params ) {
if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
- // formatComment() calls wfMsg() among other things
+ // formatComment() calls wfMessage() among other things
return 'anon-public-user-private';
} else {
return 'public';
@@ -214,6 +214,40 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ global $wgRestrictionLevels;
+ return array(
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'user' => array(
+ 'user' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'userid' => 'integer'
+ ),
+ 'comment' => array(
+ 'comment' => 'string'
+ ),
+ 'parsedcomment' => array(
+ 'parsedcomment' => 'string'
+ ),
+ 'expiry' => array(
+ 'expiry' => 'timestamp'
+ ),
+ 'level' => array(
+ 'level' => array(
+ ApiBase::PROP_TYPE => array_diff( $wgRestrictionLevels, array( '' ) )
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'List all titles protected from creation';
}
diff --git a/includes/api/ApiQueryQueryPage.php b/includes/api/ApiQueryQueryPage.php
index 5eba0de6..a8be26d3 100644
--- a/includes/api/ApiQueryQueryPage.php
+++ b/includes/api/ApiQueryQueryPage.php
@@ -4,7 +4,7 @@
*
* Created on Dec 22, 2010
*
- * Copyright © 2010 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2010 Roan Kattouw "<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
@@ -70,6 +70,8 @@ class ApiQueryQueryPage extends ApiQueryGeneratorBase {
* @param $resultPageSet ApiPageSet
*/
public function run( $resultPageSet = null ) {
+ global $wgQueryCacheLimit;
+
$params = $this->extractRequestParams();
$result = $this->getResult();
@@ -88,6 +90,7 @@ class ApiQueryQueryPage extends ApiQueryGeneratorBase {
if ( $ts ) {
$r['cachedtimestamp'] = wfTimestamp( TS_ISO_8601, $ts );
}
+ $r['maxresults'] = $wgQueryCacheLimit;
}
}
$result->addValue( array( 'query' ), $this->getModuleName(), $r );
@@ -170,6 +173,38 @@ class ApiQueryQueryPage extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ ApiBase::PROP_ROOT => array(
+ 'name' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => false
+ ),
+ 'disabled' => array(
+ ApiBase::PROP_TYPE => 'boolean',
+ ApiBase::PROP_NULLABLE => false
+ ),
+ 'cached' => array(
+ ApiBase::PROP_TYPE => 'boolean',
+ ApiBase::PROP_NULLABLE => false
+ ),
+ 'cachedtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ '' => array(
+ 'value' => 'string',
+ 'timestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Get a list provided by a QueryPage-based special page';
}
diff --git a/includes/api/ApiQueryRandom.php b/includes/api/ApiQueryRandom.php
index 2e9e2dd5..ddf5841b 100644
--- a/includes/api/ApiQueryRandom.php
+++ b/includes/api/ApiQueryRandom.php
@@ -161,6 +161,16 @@ class ApiQueryRandom extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'id' => 'integer',
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return array(
'Get a set of random pages',
diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php
index bf5bbd9b..7ae4f371 100644
--- a/includes/api/ApiQueryRecentChanges.php
+++ b/includes/api/ApiQueryRecentChanges.php
@@ -4,7 +4,7 @@
*
* Created on Oct 19, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -70,24 +70,37 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
/**
* @param $pageid
* @param $title
- * @param $rc RecentChange
+ * @param $rc RecentChange (optional)
* @return bool|String
*/
- public static function getPatrolToken( $pageid, $title, $rc ) {
+ public static function getPatrolToken( $pageid, $title, $rc = null ) {
global $wgUser;
- if ( !$wgUser->useRCPatrol() && ( !$wgUser->useNPPatrol() ||
- $rc->getAttribute( 'rc_type' ) != RC_NEW ) )
- {
- return false;
+
+ $validTokenUser = false;
+
+ if ( $rc ) {
+ if ( ( $wgUser->useRCPatrol() && $rc->getAttribute( 'rc_type' ) == RC_EDIT ) ||
+ ( $wgUser->useNPPatrol() && $rc->getAttribute( 'rc_type' ) == RC_NEW ) )
+ {
+ $validTokenUser = true;
+ }
+ } else {
+ if ( $wgUser->useRCPatrol() || $wgUser->useNPPatrol() ) {
+ $validTokenUser = true;
+ }
}
- // The patrol token is always the same, let's exploit that
- static $cachedPatrolToken = null;
- if ( is_null( $cachedPatrolToken ) ) {
- $cachedPatrolToken = $wgUser->getEditToken( 'patrol' );
+ if ( $validTokenUser ) {
+ // The patrol token is always the same, let's exploit that
+ static $cachedPatrolToken = null;
+ if ( is_null( $cachedPatrolToken ) ) {
+ $cachedPatrolToken = $wgUser->getEditToken( 'patrol' );
+ }
+ return $cachedPatrolToken;
+ } else {
+ return false;
}
- return $cachedPatrolToken;
}
/**
@@ -131,7 +144,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
/* Build our basic query. Namely, something along the lines of:
* SELECT * FROM recentchanges WHERE rc_timestamp > $start
* AND rc_timestamp < $end AND rc_namespace = $namespace
- * AND rc_deleted = '0'
+ * AND rc_deleted = 0
*/
$this->addTables( 'recentchanges' );
$index = array( 'recentchanges' => 'rc_timestamp' ); // May change
@@ -223,7 +236,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
$this->addFieldsIf( 'rc_user', $this->fld_user );
$this->addFieldsIf( 'rc_user_text', $this->fld_user || $this->fld_userid );
- $this->addFieldsIf( array( 'rc_minor', 'rc_new', 'rc_bot' ) , $this->fld_flags );
+ $this->addFieldsIf( array( 'rc_minor', 'rc_type', 'rc_bot' ) , $this->fld_flags );
$this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes );
$this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
$this->addFieldsIf( array( 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ), $this->fld_loginfo );
@@ -304,7 +317,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
* Extracts from a single sql row the data needed to describe one recent change.
*
* @param $row The row from which to extract the data.
- * @return An array mapping strings (descriptors) to their respective string values.
+ * @return array An array mapping strings (descriptors) to their respective string values.
* @access public
*/
public function extractRowInfo( $row ) {
@@ -380,7 +393,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
if ( $row->rc_bot ) {
$vals['bot'] = '';
}
- if ( $row->rc_new ) {
+ if ( $row->rc_type == RC_NEW ) {
$vals['new'] = '';
}
if ( $row->rc_minor ) {
@@ -423,13 +436,14 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$vals['logid'] = intval( $row->rc_logid );
$vals['logtype'] = $row->rc_log_type;
$vals['logaction'] = $row->rc_log_action;
+ $logEntry = DatabaseLogEntry::newFromRow( (array)$row );
ApiQueryLogEvents::addLogParams(
$this->getResult(),
$vals,
- $row->rc_params,
- $row->rc_log_action,
- $row->rc_log_type,
- $row->rc_timestamp
+ $logEntry->getParameters(),
+ $logEntry->getType(),
+ $logEntry->getSubtype(),
+ $logEntry->getTimestamp()
);
}
@@ -489,7 +503,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
return 'private';
}
if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
- // formatComment() calls wfMsg() among other things
+ // formatComment() calls wfMessage() among other things
return 'anon-public-user-private';
}
return 'public';
@@ -615,6 +629,97 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ global $wgLogTypes;
+ $props = array(
+ '' => array(
+ 'type' => array(
+ ApiBase::PROP_TYPE => array(
+ 'edit',
+ 'new',
+ 'move',
+ 'log',
+ 'move over redirect'
+ )
+ )
+ ),
+ 'title' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string',
+ 'new_ns' => array(
+ ApiBase::PROP_TYPE => 'namespace',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'new_title' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'ids' => array(
+ 'rcid' => 'integer',
+ 'pageid' => 'integer',
+ 'revid' => 'integer',
+ 'old_revid' => 'integer'
+ ),
+ 'user' => array(
+ 'user' => 'string',
+ 'anon' => 'boolean'
+ ),
+ 'userid' => array(
+ 'userid' => 'integer',
+ 'anon' => 'boolean'
+ ),
+ 'flags' => array(
+ 'bot' => 'boolean',
+ 'new' => 'boolean',
+ 'minor' => 'boolean'
+ ),
+ 'sizes' => array(
+ 'oldlen' => 'integer',
+ 'newlen' => 'integer'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'comment' => array(
+ 'comment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'parsedcomment' => array(
+ 'parsedcomment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'redirect' => array(
+ 'redirect' => 'boolean'
+ ),
+ 'patrolled' => array(
+ 'patrolled' => 'boolean'
+ ),
+ 'loginfo' => array(
+ 'logid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'logtype' => array(
+ ApiBase::PROP_TYPE => $wgLogTypes,
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'logaction' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+
+ self::addTokenProperties( $props, $this->getTokenFunctions() );
+
+ return $props;
+ }
+
public function getDescription() {
return 'Enumerate recent changes';
}
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php
index fa58bdf0..b89a8ea9 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -4,7 +4,7 @@
*
* Created on Sep 7, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -224,6 +224,13 @@ class ApiQueryRevisions extends ApiQueryBase {
}
}
+ // add user name, if needed
+ if ( $this->fld_user ) {
+ $this->addTables( 'user' );
+ $this->addJoinConds( array( 'user' => Revision::userJoinCond() ) );
+ $this->addFields( Revision::selectUserFields() );
+ }
+
// Bug 24166 - API error when using rvprop=tags
$this->addTables( 'revision' );
@@ -241,6 +248,16 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' );
}
+ // Continuing effectively uses startid. But we can't use rvstartid
+ // directly, because there is no way to tell the client to ''not''
+ // send rvstart if it sent it in the original query. So instead we
+ // send the continuation startid as rvcontinue, and ignore both
+ // rvstart and rvstartid when that is supplied.
+ if ( !is_null( $params['continue'] ) ) {
+ $params['startid'] = $params['continue'];
+ unset( $params['start'] );
+ }
+
// This code makes an assumption that sorting by rev_id and rev_timestamp produces
// the same result. This way users may request revisions starting at a given time,
// but to page through results use the rev_id returned after each page.
@@ -290,7 +307,7 @@ class ApiQueryRevisions extends ApiQueryBase {
$this->addWhereFld( 'rev_id', array_keys( $revs ) );
if ( !is_null( $params['continue'] ) ) {
- $this->addWhere( "rev_id >= '" . intval( $params['continue'] ) . "'" );
+ $this->addWhere( 'rev_id >= ' . intval( $params['continue'] ) );
}
$this->addOption( 'ORDER BY', 'rev_id' );
@@ -322,12 +339,15 @@ class ApiQueryRevisions extends ApiQueryBase {
$pageid = intval( $cont[0] );
$revid = intval( $cont[1] );
$this->addWhere(
- "rev_page > '$pageid' OR " .
- "(rev_page = '$pageid' AND " .
- "rev_id >= '$revid')"
+ "rev_page > $pageid OR " .
+ "(rev_page = $pageid AND " .
+ "rev_id >= $revid)"
);
}
- $this->addOption( 'ORDER BY', 'rev_page, rev_id' );
+ $this->addOption( 'ORDER BY', array(
+ 'rev_page',
+ 'rev_id'
+ ));
// assumption testing -- we should never get more then $pageCount rows.
$limit = $pageCount;
@@ -347,14 +367,14 @@ class ApiQueryRevisions extends ApiQueryBase {
if ( !$enumRevMode ) {
ApiBase::dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report
}
- $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) );
+ $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
break;
}
$fit = $this->addPageSubItem( $row->rev_page, $this->extractRowInfo( $row ), 'rev' );
if ( !$fit ) {
if ( $enumRevMode ) {
- $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) );
+ $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
} elseif ( $revCount > 0 ) {
$this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
} else {
@@ -528,7 +548,7 @@ class ApiQueryRevisions extends ApiQueryBase {
return 'private';
}
if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
- // formatComment() calls wfMsg() among other things
+ // formatComment() calls wfMessage() among other things
return 'anon-public-user-private';
}
return 'public';
@@ -638,6 +658,66 @@ class ApiQueryRevisions extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ $props = array(
+ '' => array(),
+ 'ids' => array(
+ 'revid' => 'integer',
+ 'parentid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'flags' => array(
+ 'minor' => 'boolean'
+ ),
+ 'user' => array(
+ 'userhidden' => 'boolean',
+ 'user' => 'string',
+ 'anon' => 'boolean'
+ ),
+ 'userid' => array(
+ 'userhidden' => 'boolean',
+ 'userid' => 'integer',
+ 'anon' => 'boolean'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'size' => array(
+ 'size' => 'integer'
+ ),
+ 'sha1' => array(
+ 'sha1' => 'string'
+ ),
+ 'comment' => array(
+ 'commenthidden' => 'boolean',
+ 'comment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'parsedcomment' => array(
+ 'commenthidden' => 'boolean',
+ 'parsedcomment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'content' => array(
+ '*' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'texthidden' => 'boolean'
+ )
+ );
+
+ self::addTokenProperties( $props, $this->getTokenFunctions() );
+
+ return $props;
+ }
+
public function getDescription() {
return array(
'Get revision information',
diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php
index 40aac050..364433d5 100644
--- a/includes/api/ApiQuerySearch.php
+++ b/includes/api/ApiQuerySearch.php
@@ -4,7 +4,7 @@
*
* Created on July 30, 2007
*
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -280,6 +280,63 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'snippet' => array(
+ 'snippet' => 'string'
+ ),
+ 'size' => array(
+ 'size' => 'integer'
+ ),
+ 'wordcount' => array(
+ 'wordcount' => 'integer'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'score' => array(
+ 'score' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'titlesnippet' => array(
+ 'titlesnippet' => 'string'
+ ),
+ 'redirecttitle' => array(
+ 'redirecttitle' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'redirectsnippet' => array(
+ 'redirectsnippet' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'sectiontitle' => array(
+ 'sectiontitle' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'sectionsnippet' => array(
+ 'sectionsnippet' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'hasrelated' => array(
+ 'hasrelated' => 'boolean'
+ )
+ );
+ }
+
public function getDescription() {
return 'Perform a full text search';
}
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index e2580ac6..ec503d64 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -4,7 +4,7 @@
*
* Created on Sep 25, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -93,6 +93,9 @@ class ApiQuerySiteinfo extends ApiQueryBase {
case 'showhooks':
$fit = $this->appendSubscribedHooks( $p );
break;
+ case 'variables':
+ $fit = $this->appendVariables( $p );
+ break;
default:
ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
}
@@ -121,9 +124,14 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data['dbtype'] = $GLOBALS['wgDBtype'];
$data['dbversion'] = $this->getDB()->getServerVersion();
- $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
- if ( $svn ) {
- $data['rev'] = $svn;
+ $git = SpecialVersion::getGitHeadSha1( $GLOBALS['IP'] );
+ if ( $git ) {
+ $data['git-hash'] = $git;
+ } else {
+ $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
+ if ( $svn ) {
+ $data['rev'] = $svn;
+ }
}
// 'case-insensitive' option is reserved for future
@@ -142,6 +150,15 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data['fallback'] = $fallbacks;
$this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
+ if( $wgContLang->hasVariants() ) {
+ $variants = array();
+ foreach( $wgContLang->getVariants() as $code ) {
+ $variants[] = array( 'code' => $code );
+ }
+ $data['variants'] = $variants;
+ $this->getResult()->setIndexedTagName( $data['variants'], 'lang' );
+ }
+
if ( $wgContLang->isRTL() ) {
$data['rtl'] = '';
}
@@ -177,6 +194,8 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data['misermode'] = '';
}
+ $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
+
wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
return $this->getResult()->addValue( 'query', $property, $data );
@@ -204,6 +223,10 @@ class ApiQuerySiteinfo extends ApiQueryBase {
if ( MWNamespace::isContent( $ns ) ) {
$data[$ns]['content'] = '';
}
+
+ if ( MWNamespace::isNonincludable( $ns ) ) {
+ $data[$ns]['nonincludable'] = '';
+ }
}
$this->getResult()->setIndexedTagName( $data, 'ns' );
@@ -234,10 +257,13 @@ class ApiQuerySiteinfo extends ApiQueryBase {
protected function appendSpecialPageAliases( $property ) {
global $wgContLang;
$data = array();
- foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) {
- $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
- $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
- $data[] = $arr;
+ $aliases = $wgContLang->getSpecialPageAliases();
+ foreach ( SpecialPageFactory::getList() as $specialpage => $stuff ) {
+ if ( isset( $aliases[$specialpage] ) ) {
+ $arr = array( 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] );
+ $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
+ $data[] = $arr;
+ }
}
$this->getResult()->setIndexedTagName( $data, 'specialpage' );
return $this->getResult()->addValue( 'query', $property, $data );
@@ -271,12 +297,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$params = $this->extractRequestParams();
$langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
-
- if( $langCode ) {
- $langNames = Language::getTranslatedLanguageNames( $langCode );
- } else {
- $langNames = Language::getLanguageNames();
- }
+ $langNames = Language::fetchLanguageNames( $langCode );
$getPrefixes = Interwiki::getAllPrefixes( $local );
$data = array();
@@ -477,12 +498,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
public function appendLanguages( $property ) {
$params = $this->extractRequestParams();
$langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
-
- if( $langCode ) {
- $langNames = Language::getTranslatedLanguageNames( $langCode );
- } else {
- $langNames = Language::getLanguageNames();
- }
+ $langNames = Language::fetchLanguageNames( $langCode );
$data = array();
@@ -522,6 +538,12 @@ class ApiQuerySiteinfo extends ApiQueryBase {
return $this->getResult()->addValue( 'query', $property, $hooks );
}
+ public function appendVariables( $property ) {
+ $variables = MagicWord::getVariableIDs();
+ $this->getResult()->setIndexedTagName( $variables, 'v' );
+ return $this->getResult()->addValue( 'query', $property, $variables );
+ }
+
private function formatParserTags( $item ) {
return "<{$item}>";
}
@@ -573,6 +595,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
'extensiontags',
'functionhooks',
'showhooks',
+ 'variables',
)
),
'filteriw' => array(
@@ -608,7 +631,8 @@ class ApiQuerySiteinfo extends ApiQueryBase {
' skins - Returns a list of all enabled skins',
' extensiontags - Returns a list of parser extension tags',
' functionhooks - Returns a list of parser function hooks',
- ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)'
+ ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)',
+ ' variables - Returns a list of variable IDs',
),
'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
'showalldb' => 'List all database servers, not just the one lagging the most',
diff --git a/includes/api/ApiQueryStashImageInfo.php b/includes/api/ApiQueryStashImageInfo.php
index 4501ec58..a310d109 100644
--- a/includes/api/ApiQueryStashImageInfo.php
+++ b/includes/api/ApiQueryStashImageInfo.php
@@ -113,7 +113,7 @@ class ApiQueryStashImageInfo extends ApiQueryImageInfo {
public function getParamDescription() {
$p = $this->getModulePrefix();
return array(
- 'prop' => self::getPropertyDescriptions( $this->propertyFilter ),
+ 'prop' => self::getPropertyDescriptions( $this->propertyFilter, $p ),
'filekey' => 'Key that identifies a previous upload that was stashed temporarily.',
'sessionkey' => 'Alias for filekey, for backward compatibility.',
'urlwidth' => "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
@@ -123,6 +123,10 @@ class ApiQueryStashImageInfo extends ApiQueryImageInfo {
);
}
+ public function getResultProperties() {
+ return ApiQueryImageInfo::getResultPropertiesFiltered( $this->propertyFilter );
+ }
+
public function getDescription() {
return 'Returns image information for stashed images';
}
diff --git a/includes/api/ApiQueryTags.php b/includes/api/ApiQueryTags.php
index 12cea1d7..f97c1b2a 100644
--- a/includes/api/ApiQueryTags.php
+++ b/includes/api/ApiQueryTags.php
@@ -59,7 +59,7 @@ class ApiQueryTags extends ApiQueryBase {
$this->addTables( 'change_tag' );
$this->addFields( 'ct_tag' );
- $this->addFieldsIf( 'count(*) AS hitcount', $this->fld_hitcount );
+ $this->addFieldsIf( array( 'hitcount' => 'COUNT(*)' ), $this->fld_hitcount );
$this->addOption( 'LIMIT', $this->limit + 1 );
$this->addOption( 'GROUP BY', 'ct_tag' );
@@ -73,7 +73,7 @@ class ApiQueryTags extends ApiQueryBase {
if ( !$ok ) {
break;
}
- $ok = $this->doTag( $row->ct_tag, $row->hitcount );
+ $ok = $this->doTag( $row->ct_tag, $this->fld_hitcount ? $row->hitcount : 0 );
}
// include tags with no hits yet
@@ -169,6 +169,23 @@ class ApiQueryTags extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'name' => 'string'
+ ),
+ 'displayname' => array(
+ 'displayname' => 'string'
+ ),
+ 'description' => array(
+ 'description' => 'string'
+ ),
+ 'hitcount' => array(
+ 'hitcount' => 'integer'
+ )
+ );
+ }
+
public function getDescription() {
return 'List change tags';
}
diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php
index 8e2f20db..f30b1325 100644
--- a/includes/api/ApiQueryUserContributions.php
+++ b/includes/api/ApiQueryUserContributions.php
@@ -4,7 +4,7 @@
*
* Created on Oct 16, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,10 +35,10 @@ class ApiQueryContributions extends ApiQueryBase {
parent::__construct( $query, $moduleName, 'uc' );
}
- private $params, $prefixMode, $userprefix, $multiUserMode, $usernames;
+ private $params, $prefixMode, $userprefix, $multiUserMode, $usernames, $parentLens;
private $fld_ids = false, $fld_title = false, $fld_timestamp = false,
$fld_comment = false, $fld_parsedcomment = false, $fld_flags = false,
- $fld_patrolled = false, $fld_tags = false, $fld_size = false;
+ $fld_patrolled = false, $fld_tags = false, $fld_size = false, $fld_sizediff = false;
public function execute() {
// Parse some parameters
@@ -50,6 +50,7 @@ class ApiQueryContributions extends ApiQueryBase {
$this->fld_comment = isset( $prop['comment'] );
$this->fld_parsedcomment = isset ( $prop['parsedcomment'] );
$this->fld_size = isset( $prop['size'] );
+ $this->fld_sizediff = isset( $prop['sizediff'] );
$this->fld_flags = isset( $prop['flags'] );
$this->fld_timestamp = isset( $prop['timestamp'] );
$this->fld_patrolled = isset( $prop['patrolled'] );
@@ -82,6 +83,17 @@ class ApiQueryContributions extends ApiQueryBase {
// Do the actual query.
$res = $this->select( __METHOD__ );
+ if( $this->fld_sizediff ) {
+ $revIds = array();
+ foreach ( $res as $row ) {
+ if( $row->rev_parent_id ) {
+ $revIds[] = $row->rev_parent_id;
+ }
+ }
+ $this->parentLens = Revision::getParentLengths( $this->getDB(), $revIds );
+ $res->rewind(); // reset
+ }
+
// Initialise some variables
$count = 0;
$limit = $this->params['limit'];
@@ -152,13 +164,14 @@ class ApiQueryContributions extends ApiQueryBase {
$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] );
+ $db = $this->getDB();
+ $encUser = $db->addQuotes( $continue[0] );
+ $encTS = $db->addQuotes( $db->timestamp( $continue[1] ) );
$op = ( $this->params['dir'] == 'older' ? '<' : '>' );
$this->addWhere(
- "rev_user_text $op '$encUser' OR " .
- "(rev_user_text = '$encUser' AND " .
- "rev_timestamp $op= '$encTS')"
+ "rev_user_text $op $encUser OR " .
+ "(rev_user_text = $encUser AND " .
+ "rev_timestamp $op= $encTS)"
);
}
@@ -185,7 +198,7 @@ class ApiQueryContributions extends ApiQueryBase {
if ( !is_null( $show ) ) {
$show = array_flip( $show );
if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) )
- || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) ) {
+ || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) ) {
$this->dieUsageMsg( 'show' );
}
@@ -243,8 +256,9 @@ class ApiQueryContributions extends ApiQueryBase {
$this->addFieldsIf( 'page_latest', $this->fld_flags );
// $this->addFieldsIf( 'rev_text_id', $this->fld_ids ); // Should this field be exposed?
$this->addFieldsIf( 'rev_comment', $this->fld_comment || $this->fld_parsedcomment );
- $this->addFieldsIf( 'rev_len', $this->fld_size );
- $this->addFieldsIf( array( 'rev_minor_edit', 'rev_parent_id' ), $this->fld_flags );
+ $this->addFieldsIf( 'rev_len', $this->fld_size || $this->fld_sizediff );
+ $this->addFieldsIf( 'rev_minor_edit', $this->fld_flags );
+ $this->addFieldsIf( 'rev_parent_id', $this->fld_flags || $this->fld_sizediff );
$this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
if ( $this->fld_tags ) {
@@ -332,6 +346,11 @@ class ApiQueryContributions extends ApiQueryBase {
$vals['size'] = intval( $row->rev_len );
}
+ if ( $this->fld_sizediff && !is_null( $row->rev_len ) && !is_null( $row->rev_parent_id ) ) {
+ $parentLen = isset( $this->parentLens[$row->rev_parent_id] ) ? $this->parentLens[$row->rev_parent_id] : 0;
+ $vals['sizediff'] = intval( $row->rev_len - $parentLen );
+ }
+
if ( $this->fld_tags ) {
if ( $row->ts_tags ) {
$tags = explode( ',', $row->ts_tags );
@@ -397,6 +416,7 @@ class ApiQueryContributions extends ApiQueryBase {
'comment',
'parsedcomment',
'size',
+ 'sizediff',
'flags',
'patrolled',
'tags'
@@ -435,7 +455,8 @@ class ApiQueryContributions extends ApiQueryBase {
' timestamp - Adds the timestamp of the edit',
' comment - Adds the comment of the edit',
' parsedcomment - Adds the parsed comment of the edit',
- ' size - Adds the size of the page',
+ ' size - Adds the new size of the edit',
+ ' sizediff - Adds the size delta of the edit against its parent',
' flags - Adds flags of the edit',
' patrolled - Tags patrolled edits',
' tags - Lists tags for the edit',
@@ -447,6 +468,61 @@ class ApiQueryContributions extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'userid' => 'integer',
+ 'user' => 'string',
+ 'userhidden' => 'boolean'
+ ),
+ 'ids' => array(
+ 'pageid' => 'integer',
+ 'revid' => 'integer'
+ ),
+ 'title' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'flags' => array(
+ 'new' => 'boolean',
+ 'minor' => 'boolean',
+ 'top' => 'boolean'
+ ),
+ 'comment' => array(
+ 'commenthidden' => 'boolean',
+ 'comment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'parsedcomment' => array(
+ 'commenthidden' => 'boolean',
+ 'parsedcomment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'patrolled' => array(
+ 'patrolled' => 'boolean'
+ ),
+ 'size' => array(
+ 'size' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'sizediff' => array(
+ 'sizediff' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'Get all edits by a user';
}
diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php
index a0ee227f..66906659 100644
--- a/includes/api/ApiQueryUserInfo.php
+++ b/includes/api/ApiQueryUserInfo.php
@@ -4,7 +4,7 @@
*
* Created on July 30, 2007
*
- * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
}
protected function getCurrentUserInfo() {
- global $wgRequest, $wgHiddenPrefs;
+ global $wgHiddenPrefs;
$user = $this->getUser();
$result = $this->getResult();
$vals = array();
@@ -63,7 +63,10 @@ class ApiQueryUserInfo extends ApiQueryBase {
if ( isset( $this->prop['blockinfo'] ) ) {
if ( $user->isBlocked() ) {
- $vals['blockedby'] = User::whoIs( $user->blockedBy() );
+ $block = $user->getBlock();
+ $vals['blockid'] = $block->getId();
+ $vals['blockedby'] = $block->getByName();
+ $vals['blockedbyid'] = $block->getBy();
$vals['blockreason'] = $user->blockedFor();
}
}
@@ -73,14 +76,12 @@ class ApiQueryUserInfo extends ApiQueryBase {
}
if ( isset( $this->prop['groups'] ) ) {
- $autolist = ApiQueryUsers::getAutoGroups( $user );
-
- $vals['groups'] = array_merge( $autolist, $user->getGroups() );
+ $vals['groups'] = $user->getEffectiveGroups();
$result->setIndexedTagName( $vals['groups'], 'g' ); // even if empty
}
if ( isset( $this->prop['implicitgroups'] ) ) {
- $vals['implicitgroups'] = ApiQueryUsers::getAutoGroups( $user );
+ $vals['implicitgroups'] = $user->getAutomaticGroups();
$result->setIndexedTagName( $vals['implicitgroups'], 'g' ); // even if empty
}
@@ -136,7 +137,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
}
if ( isset( $this->prop['acceptlang'] ) ) {
- $langs = $wgRequest->getAcceptLang();
+ $langs = $this->getRequest()->getAcceptLang();
$acceptLang = array();
foreach ( $langs as $lang => $val ) {
$r = array( 'q' => $val );
@@ -231,6 +232,63 @@ class ApiQueryUserInfo extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ return array(
+ ApiBase::PROP_LIST => false,
+ '' => array(
+ 'id' => 'integer',
+ 'name' => 'string',
+ 'anon' => 'boolean'
+ ),
+ 'blockinfo' => array(
+ 'blockid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedby' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedbyid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedreason' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'hasmsg' => array(
+ 'messages' => 'boolean'
+ ),
+ 'preferencestoken' => array(
+ 'preferencestoken' => 'string'
+ ),
+ 'editcount' => array(
+ 'editcount' => 'integer'
+ ),
+ 'realname' => array(
+ 'realname' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'email' => array(
+ 'email' => 'string',
+ 'emailauthenticated' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'registrationdate' => array(
+ 'registrationdate' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return 'Get information about the current user';
}
diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php
index 31624bdf..bf438d1d 100644
--- a/includes/api/ApiQueryUsers.php
+++ b/includes/api/ApiQueryUsers.php
@@ -4,7 +4,7 @@
*
* Created on July 30, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -61,10 +61,10 @@ class ApiQueryUsers extends ApiQueryBase {
return $this->tokenFunctions;
}
- /**
- * @param $user User
- * @return String
- */
+ /**
+ * @param $user User
+ * @return String
+ */
public static function getUserrightsToken( $user ) {
global $wgUser;
// Since the permissions check for userrights is non-trivial,
@@ -107,7 +107,7 @@ class ApiQueryUsers extends ApiQueryBase {
if ( count( $goodNames ) ) {
$this->addTables( 'user' );
- $this->addFields( '*' );
+ $this->addFields( User::selectFields() );
$this->addWhereFld( 'user_name', $goodNames );
if ( isset( $this->prop['groups'] ) || isset( $this->prop['rights'] ) ) {
@@ -138,7 +138,7 @@ class ApiQueryUsers extends ApiQueryBase {
if ( isset( $this->prop['groups'] ) ) {
if ( !isset( $data[$name]['groups'] ) ) {
- $data[$name]['groups'] = self::getAutoGroups( $user );
+ $data[$name]['groups'] = $user->getAutomaticGroups();
}
if ( !is_null( $row->ug_group ) ) {
@@ -148,7 +148,7 @@ class ApiQueryUsers extends ApiQueryBase {
}
if ( isset( $this->prop['implicitgroups'] ) && !isset( $data[$name]['implicitgroups'] ) ) {
- $data[$name]['implicitgroups'] = self::getAutoGroups( $user );
+ $data[$name]['implicitgroups'] = $user->getAutomaticGroups();
}
if ( isset( $this->prop['rights'] ) ) {
@@ -165,7 +165,9 @@ class ApiQueryUsers extends ApiQueryBase {
$data[$name]['hidden'] = '';
}
if ( isset( $this->prop['blockinfo'] ) && !is_null( $row->ipb_by_text ) ) {
+ $data[$name]['blockid'] = $row->ipb_id;
$data[$name]['blockedby'] = $row->ipb_by_text;
+ $data[$name]['blockedbyid'] = $row->ipb_by;
$data[$name]['blockreason'] = $row->ipb_reason;
$data[$name]['blockexpiry'] = $row->ipb_expiry;
}
@@ -247,18 +249,15 @@ class ApiQueryUsers extends ApiQueryBase {
/**
* Gets all the groups that a user is automatically a member of (implicit groups)
+ *
+ * @deprecated since 1.20; call User::getAutomaticGroups() directly.
* @param $user User
* @return array
*/
public static function getAutoGroups( $user ) {
- $groups = array();
- $groups[] = '*';
+ wfDeprecated( __METHOD__, '1.20' );
- if ( !$user->isAnon() ) {
- $groups[] = 'user';
- }
-
- return array_merge( $groups, Autopromote::getAutopromoteGroups( $user ) );
+ return $user->getAutomaticGroups();
}
public function getCacheMode( $params ) {
@@ -313,6 +312,73 @@ class ApiQueryUsers extends ApiQueryBase {
);
}
+ public function getResultProperties() {
+ $props = array(
+ '' => array(
+ 'userid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'name' => 'string',
+ 'invalid' => 'boolean',
+ 'hidden' => 'boolean',
+ 'interwiki' => 'boolean',
+ 'missing' => 'boolean'
+ ),
+ 'editcount' => array(
+ 'editcount' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'registration' => array(
+ 'registration' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'blockinfo' => array(
+ 'blockid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedby' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedbyid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedreason' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blockedexpiry' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'emailable' => array(
+ 'emailable' => 'boolean'
+ ),
+ 'gender' => array(
+ 'gender' => array(
+ ApiBase::PROP_TYPE => array(
+ 'male',
+ 'female',
+ 'unknown'
+ ),
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+
+ self::addTokenProperties( $props, $this->getTokenFunctions() );
+
+ return $props;
+ }
+
public function getDescription() {
return 'Get information about a list of users';
}
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index ea56fcd9..a1a33728 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -4,7 +4,7 @@
*
* Created on Sep 25, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -96,7 +96,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'rc_last_oldid',
) );
- $this->addFieldsIf( array( 'rc_new', 'rc_minor', 'rc_bot' ), $this->fld_flags );
+ $this->addFieldsIf( array( 'rc_type', 'rc_minor', 'rc_bot' ), $this->fld_flags );
$this->addFieldsIf( 'rc_user', $this->fld_user || $this->fld_userid );
$this->addFieldsIf( 'rc_user_text', $this->fld_user );
$this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
@@ -254,7 +254,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
if ( $this->fld_flags ) {
- if ( $row->rc_new ) {
+ if ( $row->rc_type == RC_NEW ) {
$vals['new'] = '';
}
if ( $row->rc_minor ) {
@@ -296,13 +296,14 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$vals['logid'] = intval( $row->rc_logid );
$vals['logtype'] = $row->rc_log_type;
$vals['logaction'] = $row->rc_log_action;
+ $logEntry = DatabaseLogEntry::newFromRow( (array)$row );
ApiQueryLogEvents::addLogParams(
$this->getResult(),
$vals,
- $row->rc_params,
- $row->rc_log_type,
- $row->rc_log_action,
- $row->rc_timestamp
+ $logEntry->getParameters(),
+ $logEntry->getType(),
+ $logEntry->getSubtype(),
+ $logEntry->getTimestamp()
);
}
@@ -417,6 +418,76 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
);
}
+ public function getResultProperties() {
+ global $wgLogTypes;
+ return array(
+ 'ids' => array(
+ 'pageid' => 'integer',
+ 'revid' => 'integer',
+ 'old_revid' => 'integer'
+ ),
+ 'title' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'user' => array(
+ 'user' => 'string',
+ 'anon' => 'boolean'
+ ),
+ 'userid' => array(
+ 'userid' => 'integer',
+ 'anon' => 'boolean'
+ ),
+ 'flags' => array(
+ 'new' => 'boolean',
+ 'minor' => 'boolean',
+ 'bot' => 'boolean'
+ ),
+ 'patrol' => array(
+ 'patrolled' => 'boolean'
+ ),
+ 'timestamp' => array(
+ 'timestamp' => 'timestamp'
+ ),
+ 'sizes' => array(
+ 'oldlen' => 'integer',
+ 'newlen' => 'integer'
+ ),
+ 'notificationtimestamp' => array(
+ 'notificationtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'comment' => array(
+ 'comment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'parsedcomment' => array(
+ 'parsedcomment' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ 'loginfo' => array(
+ 'logid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'logtype' => array(
+ ApiBase::PROP_TYPE => $wgLogTypes,
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'logaction' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return "Get all recent changes to pages in the logged in user's watchlist";
}
diff --git a/includes/api/ApiQueryWatchlistRaw.php b/includes/api/ApiQueryWatchlistRaw.php
index 506944f0..6b24aef3 100644
--- a/includes/api/ApiQueryWatchlistRaw.php
+++ b/includes/api/ApiQueryWatchlistRaw.php
@@ -4,7 +4,7 @@
*
* Created on Oct 4, 2008
*
- * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2008 Roan Kattouw "<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
@@ -76,19 +76,24 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
"original value returned by the previous query", "_badcontinue" );
}
$ns = intval( $cont[0] );
- $title = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) );
+ $title = $this->getDB()->addQuotes( $cont[1] );
+ $op = $params['dir'] == 'ascending' ? '>' : '<';
$this->addWhere(
- "wl_namespace > '$ns' OR " .
- "(wl_namespace = '$ns' AND " .
- "wl_title >= '$title')"
+ "wl_namespace $op $ns OR " .
+ "(wl_namespace = $ns AND " .
+ "wl_title $op= $title)"
);
}
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
// 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' );
+ $this->addOption( 'ORDER BY', 'wl_title' . $sort );
} else {
- $this->addOption( 'ORDER BY', 'wl_namespace, wl_title' );
+ $this->addOption( 'ORDER BY', array(
+ 'wl_namespace' . $sort,
+ 'wl_title' . $sort
+ ));
}
$this->addOption( 'LIMIT', $params['limit'] + 1 );
$res = $this->select( __METHOD__ );
@@ -98,8 +103,7 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
foreach ( $res as $row ) {
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 ) );
+ $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
break;
}
$t = Title::makeTitle( $row->wl_namespace, $row->wl_title );
@@ -113,8 +117,7 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
}
$fit = $this->getResult()->addValue( $this->getModuleName(), null, $vals );
if ( !$fit ) {
- $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' .
- $this->keyToTitle( $row->wl_title ) );
+ $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
break;
}
} else {
@@ -160,7 +163,14 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
),
'token' => array(
ApiBase::PARAM_TYPE => 'string'
- )
+ ),
+ 'dir' => array(
+ ApiBase::PARAM_DFLT => 'ascending',
+ ApiBase::PARAM_TYPE => array(
+ 'ascending',
+ 'descending'
+ ),
+ ),
);
}
@@ -176,6 +186,22 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
'show' => 'Only list items that meet these criteria',
'owner' => 'The name of the user whose watchlist you\'d like to access',
'token' => 'Give a security token (settable in preferences) to allow access to another user\'s watchlist',
+ 'dir' => 'Direction to sort the titles and namespaces in',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'ns' => 'namespace',
+ 'title' => 'string'
+ ),
+ 'changed' => array(
+ 'changed' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
);
}
diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php
index 798b2275..91e20812 100644
--- a/includes/api/ApiResult.php
+++ b/includes/api/ApiResult.php
@@ -4,7 +4,7 @@
*
* Created on Sep 4, 2006
*
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -165,7 +165,7 @@ class ApiResult extends ApiBase {
* @param $value Mixed
* @param $subElemName string when present, content element is created
* as a sub item of $arr. Use this parameter to create elements in
- * format <elem>text</elem> without attributes
+ * format "<elem>text</elem>" without attributes.
*/
public static function setContent( &$arr, $value, $subElemName = null ) {
if ( is_array( $value ) ) {
diff --git a/includes/api/ApiRollback.php b/includes/api/ApiRollback.php
index 436c392b..677df16a 100644
--- a/includes/api/ApiRollback.php
+++ b/includes/api/ApiRollback.php
@@ -4,7 +4,7 @@
*
* Created on Jun 20, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -49,7 +49,7 @@ class ApiRollback extends ApiBase {
// User and title already validated in call to getTokenSalt from Main
$titleObj = $this->getRbTitle();
$pageObj = WikiPage::factory( $titleObj );
- $summary = ( isset( $params['summary'] ) ? $params['summary'] : '' );
+ $summary = $params['summary'];
$details = array();
$retval = $pageObj->doRollback( $this->getRbUser(), $summary, $params['token'], $params['markbot'], $details, $this->getUser() );
@@ -90,8 +90,11 @@ class ApiRollback extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
- 'token' => null,
- 'summary' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
+ 'summary' => '',
'markbot' => false,
'watchlist' => array(
ApiBase::PARAM_DFLT => 'preferences',
@@ -110,12 +113,25 @@ class ApiRollback extends ApiBase {
'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 {$this->getModulePrefix()}prop=revisions",
- 'summary' => 'Custom edit summary. If not set, default summary will be used',
+ 'summary' => 'Custom edit summary. If empty, default summary will be used',
'markbot' => 'Mark the reverted edits and the revert as bot edits',
'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'title' => 'string',
+ 'pageid' => 'integer',
+ 'summary' => 'string',
+ 'revid' => 'integer',
+ 'old_revid' => 'integer',
+ 'last_revid' => 'integer'
+ )
+ );
+ }
+
public function getDescription() {
return array(
'Undo the last edit to the page. If the last user who edited the page made multiple edits in a row,',
diff --git a/includes/api/ApiSetNotificationTimestamp.php b/includes/api/ApiSetNotificationTimestamp.php
new file mode 100644
index 00000000..098b1a66
--- /dev/null
+++ b/includes/api/ApiSetNotificationTimestamp.php
@@ -0,0 +1,285 @@
+<?php
+
+/**
+ * API for MediaWiki 1.14+
+ *
+ * Created on Jun 18, 2012
+ *
+ * Copyright © 2012 Brad Jorsch
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * API interface for setting the wl_notificationtimestamp field
+ * @ingroup API
+ */
+class ApiSetNotificationTimestamp extends ApiBase {
+
+ public function __construct( $main, $action ) {
+ parent::__construct( $main, $action );
+ }
+
+ public function execute() {
+ $user = $this->getUser();
+
+ if ( $user->isAnon() ) {
+ $this->dieUsage( 'Anonymous users cannot use watchlist change notifications', 'notloggedin' );
+ }
+
+ $params = $this->extractRequestParams();
+ $this->requireMaxOneParameter( $params, 'timestamp', 'torevid', 'newerthanrevid' );
+
+ $pageSet = new ApiPageSet( $this );
+ $args = array_merge( array( $params, 'entirewatchlist' ), array_keys( $pageSet->getAllowedParams() ) );
+ call_user_func_array( array( $this, 'requireOnlyOneParameter' ), $args );
+
+ $dbw = $this->getDB( DB_MASTER );
+
+ $timestamp = null;
+ if ( isset( $params['timestamp'] ) ) {
+ $timestamp = $dbw->timestamp( $params['timestamp'] );
+ }
+
+ if ( !$params['entirewatchlist'] ) {
+ $pageSet->execute();
+ }
+
+ if ( isset( $params['torevid'] ) ) {
+ if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
+ $this->dieUsage( 'torevid may only be used with a single page', 'multpages' );
+ }
+ $title = reset( $pageSet->getGoodTitles() );
+ $timestamp = Revision::getTimestampFromId( $title, $params['torevid'] );
+ if ( $timestamp ) {
+ $timestamp = $dbw->timestamp( $timestamp );
+ } else {
+ $timestamp = null;
+ }
+ } elseif ( isset( $params['newerthanrevid'] ) ) {
+ if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
+ $this->dieUsage( 'newerthanrevid may only be used with a single page', 'multpages' );
+ }
+ $title = reset( $pageSet->getGoodTitles() );
+ $revid = $title->getNextRevisionID( $params['newerthanrevid'] );
+ if ( $revid ) {
+ $timestamp = $dbw->timestamp( Revision::getTimestampFromId( $title, $revid ) );
+ } else {
+ $timestamp = null;
+ }
+ }
+
+ $apiResult = $this->getResult();
+ $result = array();
+ if ( $params['entirewatchlist'] ) {
+ // Entire watchlist mode: Just update the thing and return a success indicator
+ $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $timestamp ),
+ array( 'wl_user' => $user->getID() ),
+ __METHOD__
+ );
+
+ $result['notificationtimestamp'] = ( is_null( $timestamp ) ? '' : wfTimestamp( TS_ISO_8601, $timestamp ) );
+ } else {
+ // First, log the invalid titles
+ foreach( $pageSet->getInvalidTitles() as $title ) {
+ $r = array();
+ $r['title'] = $title;
+ $r['invalid'] = '';
+ $result[] = $r;
+ }
+ foreach( $pageSet->getMissingPageIDs() as $p ) {
+ $page = array();
+ $page['pageid'] = $p;
+ $page['missing'] = '';
+ $page['notwatched'] = '';
+ $result[] = $page;
+ }
+ foreach( $pageSet->getMissingRevisionIDs() as $r ) {
+ $rev = array();
+ $rev['revid'] = $r;
+ $rev['missing'] = '';
+ $rev['notwatched'] = '';
+ $result[] = $rev;
+ }
+
+ // Now process the valid titles
+ $lb = new LinkBatch( $pageSet->getTitles() );
+ $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $timestamp ),
+ array( 'wl_user' => $user->getID(), $lb->constructSet( 'wl', $dbw ) ),
+ __METHOD__
+ );
+
+ // Query the results of our update
+ $timestamps = array();
+ $res = $dbw->select( 'watchlist', array( 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ),
+ array( 'wl_user' => $user->getID(), $lb->constructSet( 'wl', $dbw ) ),
+ __METHOD__
+ );
+ foreach ( $res as $row ) {
+ $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
+ }
+
+ // Now, put the valid titles into the result
+ foreach ( $pageSet->getTitles() as $title ) {
+ $ns = $title->getNamespace();
+ $dbkey = $title->getDBkey();
+ $r = array(
+ 'ns' => intval( $ns ),
+ 'title' => $title->getPrefixedText(),
+ );
+ if ( !$title->exists() ) {
+ $r['missing'] = '';
+ }
+ if ( isset( $timestamps[$ns] ) && array_key_exists( $dbkey, $timestamps[$ns] ) ) {
+ $r['notificationtimestamp'] = '';
+ if ( $timestamps[$ns][$dbkey] !== null ) {
+ $r['notificationtimestamp'] = wfTimestamp( TS_ISO_8601, $timestamps[$ns][$dbkey] );
+ }
+ } else {
+ $r['notwatched'] = '';
+ }
+ $result[] = $r;
+ }
+
+ $apiResult->setIndexedTagName( $result, 'page' );
+ }
+ $apiResult->addValue( null, $this->getModuleName(), $result );
+ }
+
+ public function mustBePosted() {
+ return true;
+ }
+
+ public function isWriteMode() {
+ return true;
+ }
+
+ public function needsToken() {
+ return true;
+ }
+
+ public function getTokenSalt() {
+ return '';
+ }
+
+ public function getAllowedParams() {
+ $psModule = new ApiPageSet( $this );
+ return $psModule->getAllowedParams() + array(
+ 'entirewatchlist' => array(
+ ApiBase::PARAM_TYPE => 'boolean'
+ ),
+ 'token' => null,
+ 'timestamp' => array(
+ ApiBase::PARAM_TYPE => 'timestamp'
+ ),
+ 'torevid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
+ 'newerthanrevid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
+ );
+ }
+
+ public function getParamDescription() {
+ $psModule = new ApiPageSet( $this );
+ return $psModule->getParamDescription() + array(
+ 'entirewatchlist' => 'Work on all watched pages',
+ 'timestamp' => 'Timestamp to which to set the notification timestamp',
+ 'torevid' => 'Revision to set the notification timestamp to (one page only)',
+ 'newerthanrevid' => 'Revision to set the notification timestamp newer than (one page only)',
+ 'token' => 'A token previously acquired via prop=info',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ ApiBase::PROP_LIST => true,
+ ApiBase::PROP_ROOT => array(
+ 'notificationtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ ),
+ '' => array(
+ 'ns' => array(
+ ApiBase::PROP_TYPE => 'namespace',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'title' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'pageid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'revid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'invalid' => 'boolean',
+ 'missing' => 'boolean',
+ 'notwatched' => 'boolean',
+ 'notificationtimestamp' => array(
+ ApiBase::PROP_TYPE => 'timestamp',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
+ public function getDescription() {
+ return array( 'Update the notification timestamp for watched pages.',
+ 'This affects the highlighting of changed pages in the watchlist and history,',
+ 'and the sending of email when the "E-mail me when a page on my watchlist is',
+ 'changed" preference is enabled.'
+ );
+ }
+
+ public function getPossibleErrors() {
+ $psModule = new ApiPageSet( $this );
+ return array_merge(
+ parent::getPossibleErrors(),
+ $psModule->getPossibleErrors(),
+ $this->getRequireMaxOneParameterErrorMessages( array( 'timestamp', 'torevid', 'newerthanrevid' ) ),
+ $this->getRequireOnlyOneParameterErrorMessages( array_merge( array( 'entirewatchlist' ), array_keys( $psModule->getAllowedParams() ) ) ),
+ array(
+ array( 'code' => 'notloggedin', 'info' => 'Anonymous users cannot use watchlist change notifications' ),
+ array( 'code' => 'multpages', 'info' => 'torevid may only be used with a single page' ),
+ array( 'code' => 'multpages', 'info' => 'newerthanrevid may only be used with a single page' ),
+ )
+ );
+ }
+
+ public function getExamples() {
+ return array(
+ 'api.php?action=setnotificationtimestamp&entirewatchlist=&token=ABC123' => 'Reset the notification status for the entire watchlist',
+ 'api.php?action=setnotificationtimestamp&titles=Main_page&token=ABC123' => 'Reset the notification status for "Main page"',
+ 'api.php?action=setnotificationtimestamp&titles=Main_page&timestamp=2012-01-01T00:00:00Z&token=ABC123' => 'Set the notification timestamp for "Main page" so all edits since 1 January 2012 are unviewed',
+ );
+ }
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:SetNotificationTimestamp';
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id$';
+ }
+}
diff --git a/includes/api/ApiTokens.php b/includes/api/ApiTokens.php
new file mode 100644
index 00000000..2c9b482c
--- /dev/null
+++ b/includes/api/ApiTokens.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ *
+ *
+ * Created on Jul 29, 2011
+ *
+ * Copyright © 2011 John Du Hart john@johnduhart.me
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+
+/**
+ * @ingroup API
+ */
+class ApiTokens extends ApiBase {
+
+ public function __construct( $main, $action ) {
+ parent::__construct( $main, $action );
+ }
+
+ public function execute() {
+ wfProfileIn( __METHOD__ );
+ $params = $this->extractRequestParams();
+ $res = array();
+
+ $types = $this->getTokenTypes();
+ foreach ( $params['type'] as $type ) {
+ $type = strtolower( $type );
+
+ $val = call_user_func( $types[$type], null, null );
+
+ if ( $val === false ) {
+ $this->setWarning( "Action '$type' is not allowed for the current user" );
+ } else {
+ $res[$type . 'token'] = $val;
+ }
+ }
+
+ $this->getResult()->addValue( null, $this->getModuleName(), $res );
+ wfProfileOut( __METHOD__ );
+ }
+
+ private function getTokenTypes() {
+ static $types = null;
+ if ( $types ) {
+ return $types;
+ }
+ wfProfileIn( __METHOD__ );
+ $types = array( 'patrol' => 'ApiQueryRecentChanges::getPatrolToken' );
+ $names = array( 'edit', 'delete', 'protect', 'move', 'block', 'unblock',
+ 'email', 'import', 'watch', 'options' );
+ foreach ( $names as $name ) {
+ $types[$name] = 'ApiQueryInfo::get' . ucfirst( $name ) . 'Token';
+ }
+ wfRunHooks( 'ApiTokensGetTokenTypes', array( &$types ) );
+ ksort( $types );
+ wfProfileOut( __METHOD__ );
+ return $types;
+ }
+
+ public function getAllowedParams() {
+ return array(
+ 'type' => array(
+ ApiBase::PARAM_DFLT => 'edit',
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_TYPE => array_keys( $this->getTokenTypes() ),
+ ),
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'patroltoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'edittoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'deletetoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'protecttoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'movetoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'blocktoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'unblocktoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'emailtoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'importtoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'watchtoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'optionstoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
+ public function getParamDescription() {
+ return array(
+ 'type' => 'Type of token(s) to request'
+ );
+ }
+
+ public function getDescription() {
+ return 'Gets tokens for data-modifying actions';
+ }
+
+ protected function getExamples() {
+ return array(
+ 'api.php?action=tokens' => 'Retrieve an edit token (the default)',
+ 'api.php?action=tokens&type=email|move' => 'Retrieve an email token and a move token'
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id$';
+ }
+}
diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php
index db94fd5b..e34771fc 100644
--- a/includes/api/ApiUnblock.php
+++ b/includes/api/ApiUnblock.php
@@ -4,7 +4,7 @@
*
* Created on Sep 7, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -44,7 +44,7 @@ class ApiUnblock extends ApiBase {
$params = $this->extractRequestParams();
if ( $params['gettoken'] ) {
- $res['unblocktoken'] = $user->getEditToken( '', $this->getMain()->getRequest() );
+ $res['unblocktoken'] = $user->getEditToken();
$this->getResult()->addValue( null, $this->getModuleName(), $res );
return;
}
@@ -69,7 +69,7 @@ class ApiUnblock extends ApiBase {
$data = array(
'Target' => is_null( $params['id'] ) ? $params['user'] : "#{$params['id']}",
- 'Reason' => is_null( $params['reason'] ) ? '' : $params['reason']
+ 'Reason' => $params['reason']
);
$block = Block::newFromTarget( $data['Target'] );
$retval = SpecialUnblock::processUnblock( $data, $this->getContext() );
@@ -78,7 +78,9 @@ class ApiUnblock extends ApiBase {
}
$res['id'] = $block->getId();
- $res['user'] = $block->getType() == Block::TYPE_AUTO ? '' : $block->getTarget();
+ $target = $block->getType() == Block::TYPE_AUTO ? '' : $block->getTarget();
+ $res['user'] = $target;
+ $res['userid'] = $target instanceof User ? $target->getId() : 0;
$res['reason'] = $params['reason'];
$this->getResult()->addValue( null, $this->getModuleName(), $res );
}
@@ -98,8 +100,11 @@ class ApiUnblock extends ApiBase {
),
'user' => null,
'token' => null,
- 'gettoken' => false,
- 'reason' => null,
+ 'gettoken' => array(
+ ApiBase::PARAM_DFLT => false,
+ ApiBase::PARAM_DEPRECATED => true,
+ ),
+ 'reason' => '',
);
}
@@ -108,9 +113,36 @@ class ApiUnblock extends ApiBase {
return array(
'id' => "ID of the block you want to unblock (obtained through list=blocks). Cannot be used together with {$p}user",
'user' => "Username, IP address or IP range you want to unblock. Cannot be used together with {$p}id",
- 'token' => "An unblock token previously obtained through the gettoken parameter or {$p}prop=info",
+ 'token' => "An unblock token previously obtained through prop=info",
'gettoken' => 'If set, an unblock token will be returned, and no other action will be taken',
- 'reason' => 'Reason for unblock (optional)',
+ 'reason' => 'Reason for unblock',
+ );
+ }
+
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'unblocktoken' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'id' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'user' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'userid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'reason' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
);
}
diff --git a/includes/api/ApiUndelete.php b/includes/api/ApiUndelete.php
index d3429972..c9962517 100644
--- a/includes/api/ApiUndelete.php
+++ b/includes/api/ApiUndelete.php
@@ -4,7 +4,7 @@
*
* Created on Jul 3, 2007
*
- * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2007 Roan Kattouw "<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
@@ -76,7 +76,7 @@ class ApiUndelete extends ApiBase {
$info['title'] = $titleObj->getPrefixedText();
$info['revisions'] = intval( $retval[0] );
$info['fileversions'] = intval( $retval[1] );
- $info['reason'] = intval( $retval[2] );
+ $info['reason'] = $retval[2];
$this->getResult()->addValue( null, $this->getModuleName(), $info );
}
@@ -94,7 +94,10 @@ class ApiUndelete extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
),
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'reason' => '',
'timestamps' => array(
ApiBase::PARAM_TYPE => 'timestamp',
@@ -116,12 +119,23 @@ class ApiUndelete extends ApiBase {
return array(
'title' => 'Title of the page you want to restore',
'token' => 'An undelete token previously retrieved through list=deletedrevs',
- 'reason' => 'Reason for restoring (optional)',
+ 'reason' => 'Reason for restoring',
'timestamps' => 'Timestamps of the revisions to restore. If not set, all revisions will be restored.',
'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'title' => 'string',
+ 'revisions' => 'integer',
+ 'filerevisions' => 'integer',
+ 'reason' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return array(
'Restore certain revisions of a deleted page. A list of deleted revisions (including timestamps) can be',
diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php
index fdc1eff0..3a9b5c56 100644
--- a/includes/api/ApiUpload.php
+++ b/includes/api/ApiUpload.php
@@ -113,27 +113,30 @@ class ApiUpload extends ApiBase {
}
/**
* Get an uplaod result based on upload context
+ * @return array
*/
private function getContextResult(){
$warnings = $this->getApiWarnings();
- if ( $warnings ) {
+ if ( $warnings && !$this->mParams['ignorewarnings'] ) {
// Get warnings formated in result array format
return $this->getWarningsResult( $warnings );
} elseif ( $this->mParams['chunk'] ) {
// Add chunk, and get result
- return $this->getChunkResult();
+ return $this->getChunkResult( $warnings );
} elseif ( $this->mParams['stash'] ) {
// Stash the file and get stash result
- return $this->getStashResult();
+ return $this->getStashResult( $warnings );
}
// This is the most common case -- a normal upload with no warnings
// performUpload will return a formatted properly for the API with status
- return $this->performUpload();
+ return $this->performUpload( $warnings );
}
/**
- * Get Stash Result, throws an expetion if the file could not be stashed.
+ * Get Stash Result, throws an expetion if the file could not be stashed.
+ * @param $warnings array Array of Api upload warnings
+ * @return array
*/
- private function getStashResult(){
+ private function getStashResult( $warnings ){
$result = array ();
// Some uploads can request they be stashed, so as not to publish them immediately.
// In this case, a failure to stash ought to be fatal
@@ -141,6 +144,9 @@ class ApiUpload extends ApiBase {
$result['result'] = 'Success';
$result['filekey'] = $this->performStash();
$result['sessionkey'] = $result['filekey']; // backwards compatibility
+ if ( $warnings && count( $warnings ) > 0 ) {
+ $result['warnings'] = $warnings;
+ }
} catch ( MWException $e ) {
$this->dieUsage( $e->getMessage(), 'stashfailed' );
}
@@ -148,7 +154,8 @@ class ApiUpload extends ApiBase {
}
/**
* Get Warnings Result
- * @param $warnings Array of Api upload warnings
+ * @param $warnings array Array of Api upload warnings
+ * @return array
*/
private function getWarningsResult( $warnings ){
$result = array();
@@ -165,12 +172,17 @@ class ApiUpload extends ApiBase {
return $result;
}
/**
- * Get the result of a chunk upload.
+ * Get the result of a chunk upload.
+ * @param $warnings array Array of Api upload warnings
+ * @return array
*/
- private function getChunkResult(){
+ private function getChunkResult( $warnings ){
$result = array();
-
+
$result['result'] = 'Continue';
+ if ( $warnings && count( $warnings ) > 0 ) {
+ $result['warnings'] = $warnings;
+ }
$request = $this->getMain()->getRequest();
$chunkPath = $request->getFileTempname( 'chunk' );
$chunkSize = $request->getUpload( 'chunk' )->getSize();
@@ -181,17 +193,30 @@ class ApiUpload extends ApiBase {
$this->mParams['offset']);
if ( !$status->isGood() ) {
$this->dieUsage( $status->getWikiText(), 'stashfailed' );
- return ;
+ return array();
}
- $result['filekey'] = $this->mParams['filekey'];
+
// Check we added the last chunk:
if( $this->mParams['offset'] + $chunkSize == $this->mParams['filesize'] ) {
$status = $this->mUpload->concatenateChunks();
+
if ( !$status->isGood() ) {
$this->dieUsage( $status->getWikiText(), 'stashfailed' );
- return ;
+ return array();
}
+
+ // We have a new filekey for the fully concatenated file.
+ $result['filekey'] = $this->mUpload->getLocalFile()->getFileKey();
+
+ // Remove chunk from stash. (Checks against user ownership of chunks.)
+ $this->mUpload->stash->removeFile( $this->mParams['filekey'] );
+
$result['result'] = 'Success';
+
+ } else {
+
+ // Continue passing through the filekey for adding further chunks.
+ $result['filekey'] = $this->mParams['filekey'];
}
}
$result['offset'] = $this->mParams['offset'] + $chunkSize;
@@ -318,6 +343,10 @@ class ApiUpload extends ApiBase {
$this->dieUsageMsg( 'copyuploaddisabled' );
}
+ if ( !UploadFromUrl::isAllowedHost( $this->mParams['url'] ) ) {
+ $this->dieUsageMsg( 'copyuploadbaddomain' );
+ }
+
$async = false;
if ( $this->mParams['asyncdownload'] ) {
$this->checkAsyncDownloadEnabled();
@@ -399,11 +428,21 @@ class ApiUpload extends ApiBase {
break;
case UploadBase::FILETYPE_BADTYPE:
- $this->dieUsage( 'This type of file is banned', 'filetype-banned',
- 0, array(
- 'filetype' => $verification['finalExt'],
- 'allowed' => $wgFileExtensions
- ) );
+ $extradata = array(
+ 'filetype' => $verification['finalExt'],
+ 'allowed' => $wgFileExtensions
+ );
+ $this->getResult()->setIndexedTagName( $extradata['allowed'], 'ext' );
+
+ $msg = "Filetype not permitted: ";
+ if ( isset( $verification['blacklistedExt'] ) ) {
+ $msg .= join( ', ', $verification['blacklistedExt'] );
+ $extradata['blacklisted'] = array_values( $verification['blacklistedExt'] );
+ $this->getResult()->setIndexedTagName( $extradata['blacklisted'], 'ext' );
+ } else {
+ $msg .= $verification['finalExt'];
+ }
+ $this->dieUsage( $msg, 'filetype-banned', 0, $extradata );
break;
case UploadBase::VERIFICATION_ERROR:
$this->getResult()->setIndexedTagName( $verification['details'], 'detail' );
@@ -423,18 +462,15 @@ class ApiUpload extends ApiBase {
/**
- * Check warnings if ignorewarnings is not set.
+ * Check warnings.
* Returns a suitable array for inclusion into API results if there were warnings
* Returns the empty array if there were no warnings
*
* @return array
*/
protected function getApiWarnings() {
- $warnings = array();
+ $warnings = $this->mUpload->checkWarnings();
- if ( !$this->mParams['ignorewarnings'] ) {
- $warnings = $this->mUpload->checkWarnings();
- }
return $this->transformWarnings( $warnings );
}
@@ -467,9 +503,10 @@ class ApiUpload extends ApiBase {
* Perform the actual upload. Returns a suitable result array on success;
* dies on failure.
*
+ * @param $warnings array Array of Api upload warnings
* @return array
*/
- protected function performUpload() {
+ protected function performUpload( $warnings ) {
// Use comment as initial page text by default
if ( is_null( $this->mParams['text'] ) ) {
$this->mParams['text'] = $this->mParams['comment'];
@@ -508,6 +545,9 @@ class ApiUpload extends ApiBase {
$result['result'] = 'Success';
$result['filename'] = $file->getName();
+ if ( $warnings && count( $warnings ) > 0 ) {
+ $result['warnings'] = $warnings;
+ }
return $result;
}
@@ -539,7 +579,10 @@ class ApiUpload extends ApiBase {
ApiBase::PARAM_DFLT => ''
),
'text' => null,
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'watch' => array(
ApiBase::PARAM_DFLT => false,
ApiBase::PARAM_DEPRECATED => true,
@@ -602,6 +645,41 @@ class ApiUpload extends ApiBase {
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'result' => array(
+ ApiBase::PROP_TYPE => array(
+ 'Success',
+ 'Warning',
+ 'Continue',
+ 'Queued'
+ ),
+ ),
+ 'filekey' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'sessionkey' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'offset' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'statuskey' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'filename' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ )
+ )
+ );
+ }
+
public function getDescription() {
return array(
'Upload a file, or get the status of pending uploads. Several methods are available:',
@@ -631,6 +709,8 @@ class ApiUpload extends ApiBase {
array( 'code' => 'stashfailed', 'info' => 'Stashing temporary file failed' ),
array( 'code' => 'internal-error', 'info' => 'An internal error occurred' ),
array( 'code' => 'asynccopyuploaddisabled', 'info' => 'Asynchronous copy uploads disabled' ),
+ array( 'fileexists-forbidden' ),
+ array( 'fileexists-shared-forbidden' ),
)
);
}
diff --git a/includes/api/ApiUserrights.php b/includes/api/ApiUserrights.php
index 191dd3ec..cbb66a41 100644
--- a/includes/api/ApiUserrights.php
+++ b/includes/api/ApiUserrights.php
@@ -5,7 +5,7 @@
*
* Created on Mar 24, 2009
*
- * Copyright © 2009 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+ * Copyright © 2009 Roan Kattouw "<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
@@ -43,6 +43,7 @@ class ApiUserrights extends ApiBase {
$form = new UserrightsPage;
$r['user'] = $user->getName();
+ $r['userid'] = $user->getId();
list( $r['added'], $r['removed'] ) =
$form->doSaveUserGroups(
$user, (array)$params['add'],
@@ -99,7 +100,10 @@ class ApiUserrights extends ApiBase {
ApiBase::PARAM_TYPE => User::getAllGroups(),
ApiBase::PARAM_ISMULTI => true
),
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
'reason' => array(
ApiBase::PARAM_DFLT => ''
)
diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php
index fa382b3b..0509f1f8 100644
--- a/includes/api/ApiWatch.php
+++ b/includes/api/ApiWatch.php
@@ -4,7 +4,7 @@
*
* Created on Jan 4, 2008
*
- * Copyright © 2008 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
+ * Copyright © 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
@@ -88,7 +88,10 @@ class ApiWatch extends ApiBase {
ApiBase::PARAM_REQUIRED => true
),
'unwatch' => false,
- 'token' => null,
+ 'token' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true
+ ),
);
}
@@ -100,6 +103,17 @@ class ApiWatch extends ApiBase {
);
}
+ public function getResultProperties() {
+ return array(
+ '' => array(
+ 'title' => 'string',
+ 'unwatched' => 'boolean',
+ 'watched' => 'boolean',
+ 'message' => 'string'
+ )
+ );
+ }
+
public function getDescription() {
return 'Add or remove a page from/to the current user\'s watchlist';
}