From 222b01f5169f1c7e69762e0e8904c24f78f71882 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Wed, 28 Jul 2010 11:52:48 +0200 Subject: update to MediaWiki 1.16.0 --- includes/api/ApiBase.php | 955 +++++++++++++++++------------ includes/api/ApiBlock.php | 105 ++-- includes/api/ApiDelete.php | 203 +++--- includes/api/ApiDisabled.php | 23 +- includes/api/ApiEditPage.php | 385 +++++++----- includes/api/ApiEmailUser.php | 34 +- includes/api/ApiExpandTemplates.php | 17 +- includes/api/ApiFeedWatchlist.php | 80 ++- includes/api/ApiFormatBase.php | 115 ++-- includes/api/ApiFormatDbg.php | 18 +- includes/api/ApiFormatJson.php | 44 +- includes/api/ApiFormatJson_json.php | 861 -------------------------- includes/api/ApiFormatPhp.php | 12 +- includes/api/ApiFormatRaw.php | 28 +- includes/api/ApiFormatTxt.php | 18 +- includes/api/ApiFormatWddx.php | 77 ++- includes/api/ApiFormatXml.php | 117 ++-- includes/api/ApiFormatYaml.php | 12 +- includes/api/ApiFormatYaml_spyc.php | 98 +-- includes/api/ApiHelp.php | 12 +- includes/api/ApiImport.php | 98 +-- includes/api/ApiLogin.php | 99 +-- includes/api/ApiLogout.php | 12 +- includes/api/ApiMain.php | 347 ++++++----- includes/api/ApiMove.php | 159 +++-- includes/api/ApiOpenSearch.php | 38 +- includes/api/ApiPageSet.php | 257 ++++---- includes/api/ApiParamInfo.php | 134 ++-- includes/api/ApiParse.php | 194 +++--- includes/api/ApiPatrol.php | 50 +- includes/api/ApiProtect.php | 146 +++-- includes/api/ApiPurge.php | 41 +- includes/api/ApiQuery.php | 243 ++++---- includes/api/ApiQueryAllCategories.php | 92 +-- includes/api/ApiQueryAllLinks.php | 135 ++-- includes/api/ApiQueryAllUsers.php | 148 ++--- includes/api/ApiQueryAllimages.php | 121 ++-- includes/api/ApiQueryAllmessages.php | 118 ++-- includes/api/ApiQueryAllpages.php | 162 ++--- includes/api/ApiQueryBacklinks.php | 338 +++++----- includes/api/ApiQueryBase.php | 196 +++--- includes/api/ApiQueryBlocks.php | 205 ++++--- includes/api/ApiQueryCategories.php | 179 +++--- includes/api/ApiQueryCategoryInfo.php | 66 +- includes/api/ApiQueryCategoryMembers.php | 186 +++--- includes/api/ApiQueryDeletedrevs.php | 257 ++++---- includes/api/ApiQueryDisabled.php | 12 +- includes/api/ApiQueryDuplicateFiles.php | 97 +-- includes/api/ApiQueryExtLinksUsage.php | 125 ++-- includes/api/ApiQueryExternalLinks.php | 50 +- includes/api/ApiQueryImageInfo.php | 229 +++---- includes/api/ApiQueryImages.php | 101 +-- includes/api/ApiQueryInfo.php | 447 ++++++++------ includes/api/ApiQueryLangLinks.php | 75 ++- includes/api/ApiQueryLinks.php | 120 ++-- includes/api/ApiQueryLogEvents.php | 274 +++++---- includes/api/ApiQueryProtectedTitles.php | 112 ++-- includes/api/ApiQueryRandom.php | 82 +-- includes/api/ApiQueryRecentChanges.php | 383 +++++++----- includes/api/ApiQueryRevisions.php | 435 +++++++------ includes/api/ApiQuerySearch.php | 129 +++- includes/api/ApiQuerySiteinfo.php | 198 +++--- includes/api/ApiQueryTags.php | 181 ++++++ includes/api/ApiQueryUserContributions.php | 305 +++++---- includes/api/ApiQueryUserInfo.php | 99 +-- includes/api/ApiQueryUsers.php | 202 ++++-- includes/api/ApiQueryWatchlist.php | 316 ++++++---- includes/api/ApiQueryWatchlistRaw.php | 118 ++-- includes/api/ApiResult.php | 168 +++-- includes/api/ApiRollback.php | 76 ++- includes/api/ApiUnblock.php | 59 +- includes/api/ApiUndelete.php | 87 +-- includes/api/ApiUpload.php | 325 ++++++++++ includes/api/ApiUserrights.php | 128 ++++ includes/api/ApiWatch.php | 46 +- 75 files changed, 6760 insertions(+), 5454 deletions(-) delete mode 100644 includes/api/ApiFormatJson_json.php create mode 100644 includes/api/ApiQueryTags.php create mode 100644 includes/api/ApiUpload.php create mode 100644 includes/api/ApiUserrights.php (limited to 'includes/api') diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 8cf8c096..b703ab4f 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -1,11 +1,11 @@ @gmail.com + * Copyright © 2006, 2010 Yuri Astrakhan @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,6 +49,7 @@ abstract class ApiBase { const PARAM_MAX2 = 4; // Max value allowed for a parameter for bots and sysops. Only applies if TYPE='integer' const PARAM_MIN = 5; // Lowest value allowed for a parameter. Only applies if TYPE='integer' const PARAM_ALLOW_DUPLICATES = 6; // Boolean, do we allow the same value to be set more than once when ISMULTI=true + const PARAM_DEPRECATED = 7; // Boolean, is the parameter deprecated (will show a warning) const LIMIT_BIG1 = 500; // Fast query, std user limit const LIMIT_BIG2 = 5000; // Fast query, bot/sysop limit @@ -56,6 +57,7 @@ abstract class ApiBase { const LIMIT_SML2 = 500; // Slow query, bot/sysop limit private $mMainModule, $mModuleName, $mModulePrefix; + private $mParamCache = array(); /** * Constructor @@ -63,7 +65,7 @@ abstract class ApiBase { * @param $moduleName string Name of this module * @param $modulePrefix string Prefix to use for parameter names */ - public function __construct($mainModule, $moduleName, $modulePrefix = '') { + public function __construct( $mainModule, $moduleName, $modulePrefix = '' ) { $this->mMainModule = $mainModule; $this->mModuleName = $moduleName; $this->mModulePrefix = $modulePrefix; @@ -119,11 +121,12 @@ abstract class ApiBase { * Get the name of the module as shown in the profiler log * @return string */ - public function getModuleProfileName($db = false) { - if ($db) + public function getModuleProfileName( $db = false ) { + if ( $db ) { return 'API:' . $this->mModuleName . '-DB'; - else + } else { return 'API:' . $this->mModuleName; + } } /** @@ -150,8 +153,9 @@ abstract class ApiBase { public function getResult() { // Main module has getResult() method overriden // Safety - avoid infinite loop: - if ($this->isMain()) - ApiBase :: dieDebug(__METHOD__, 'base method was called on main module. '); + if ( $this->isMain() ) { + ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' ); + } return $this->getMain()->getResult(); } @@ -170,23 +174,24 @@ abstract class ApiBase { * newlines * @param $warning string Warning message */ - public function setWarning($warning) { + public function setWarning( $warning ) { $data = $this->getResult()->getData(); - if(isset($data['warnings'][$this->getModuleName()])) - { - # Don't add duplicate warnings - $warn_regex = preg_quote($warning, '/'); - if(preg_match("/{$warn_regex}(\\n|$)/", $data['warnings'][$this->getModuleName()]['*'])) + if ( isset( $data['warnings'][$this->getModuleName()] ) ) { + // Don't add duplicate warnings + $warn_regex = preg_quote( $warning, '/' ); + if ( preg_match( "/{$warn_regex}(\\n|$)/", $data['warnings'][$this->getModuleName()]['*'] ) ) + { return; + } $oldwarning = $data['warnings'][$this->getModuleName()]['*']; - # If there is a warning already, append it to the existing one + // If there is a warning already, append it to the existing one $warning = "$oldwarning\n$warning"; - $this->getResult()->unsetValue('warnings', $this->getModuleName()); + $this->getResult()->unsetValue( 'warnings', $this->getModuleName() ); } $msg = array(); - ApiResult :: setContent($msg, $warning); + ApiResult::setContent( $msg, $warning ); $this->getResult()->disableSizeCheck(); - $this->getResult()->addValue('warnings', $this->getModuleName(), $msg); + $this->getResult()->addValue( 'warnings', $this->getModuleName(), $msg ); $this->getResult()->enableSizeCheck(); } @@ -205,58 +210,65 @@ abstract class ApiBase { * @return mixed string or false */ public function makeHelpMsg() { - static $lnPrfx = "\n "; $msg = $this->getDescription(); - if ($msg !== false) { + if ( $msg !== false ) { - if (!is_array($msg)) - $msg = array ( + if ( !is_array( $msg ) ) { + $msg = array( $msg ); - $msg = $lnPrfx . implode($lnPrfx, $msg) . "\n"; + } + $msg = $lnPrfx . implode( $lnPrfx, $msg ) . "\n"; - if ($this->isReadMode()) + if ( $this->isReadMode() ) { $msg .= "\nThis module requires read rights."; - if ($this->isWriteMode()) + } + if ( $this->isWriteMode() ) { $msg .= "\nThis module requires write rights."; - if ($this->mustBePosted()) + } + if ( $this->mustBePosted() ) { $msg .= "\nThis module only accepts POST requests."; - if ($this->isReadMode() || $this->isWriteMode() || - $this->mustBePosted()) + } + if ( $this->isReadMode() || $this->isWriteMode() || + $this->mustBePosted() ) + { $msg .= "\n"; + } // Parameters $paramsMsg = $this->makeHelpMsgParameters(); - if ($paramsMsg !== false) { + if ( $paramsMsg !== false ) { $msg .= "Parameters:\n$paramsMsg"; } // Examples $examples = $this->getExamples(); - if ($examples !== false) { - if (!is_array($examples)) - $examples = array ( + if ( $examples !== false ) { + if ( !is_array( $examples ) ) { + $examples = array( $examples ); - $msg .= 'Example' . (count($examples) > 1 ? 's' : '') . ":\n "; - $msg .= implode($lnPrfx, $examples) . "\n"; + } + $msg .= 'Example' . ( count( $examples ) > 1 ? 's' : '' ) . ":\n "; + $msg .= implode( $lnPrfx, $examples ) . "\n"; } - if ($this->getMain()->getShowVersions()) { + if ( $this->getMain()->getShowVersions() ) { $versions = $this->getVersion(); $pattern = '/(\$.*) ([0-9a-z_]+\.php) (.*\$)/i'; - $replacement = '\\0' . "\n " . 'http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/api/\\2'; + $callback = array( $this, 'makeHelpMsg_callback' ); - if (is_array($versions)) { - foreach ($versions as &$v) - $v = preg_replace($pattern, $replacement, $v); - $versions = implode("\n ", $versions); + if ( is_array( $versions ) ) { + foreach ( $versions as &$v ) { + $v = preg_replace_callback( $pattern, $callback, $v ); + } + $versions = implode( "\n ", $versions ); + } else { + $versions = preg_replace_callback( $pattern, $callback, $versions ); } - else - $versions = preg_replace($pattern, $replacement, $versions); $msg .= "Version:\n $versions\n"; } @@ -272,51 +284,61 @@ abstract class ApiBase { */ public function makeHelpMsgParameters() { $params = $this->getFinalParams(); - if ($params !== false) { + if ( $params ) { $paramsDescription = $this->getFinalParamDescription(); $msg = ''; - $paramPrefix = "\n" . str_repeat(' ', 19); - foreach ($params as $paramName => $paramSettings) { - $desc = isset ($paramsDescription[$paramName]) ? $paramsDescription[$paramName] : ''; - if (is_array($desc)) - $desc = implode($paramPrefix, $desc); - - $type = isset($paramSettings[self :: PARAM_TYPE])? $paramSettings[self :: PARAM_TYPE] : null; - if (isset ($type)) { - if (isset ($paramSettings[self :: PARAM_ISMULTI])) + $paramPrefix = "\n" . str_repeat( ' ', 19 ); + foreach ( $params as $paramName => $paramSettings ) { + $desc = isset( $paramsDescription[$paramName] ) ? $paramsDescription[$paramName] : ''; + if ( is_array( $desc ) ) { + $desc = implode( $paramPrefix, $desc ); + } + + $deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] ) ? + $paramSettings[self::PARAM_DEPRECATED] : false; + if ( $deprecated ) { + $desc = "DEPRECATED! $desc"; + } + + $type = isset( $paramSettings[self::PARAM_TYPE] ) ? $paramSettings[self::PARAM_TYPE] : null; + if ( isset( $type ) ) { + if ( isset( $paramSettings[self::PARAM_ISMULTI] ) ) { $prompt = 'Values (separate with \'|\'): '; - else + } else { $prompt = 'One value: '; + } - if (is_array($type)) { + if ( is_array( $type ) ) { $choices = array(); $nothingPrompt = false; - foreach ($type as $t) - if ($t === '') + foreach ( $type as $t ) + if ( $t === '' ) { $nothingPrompt = 'Can be empty, or '; - else + } else { $choices[] = $t; - $desc .= $paramPrefix . $nothingPrompt . $prompt . implode(', ', $choices); + } + $desc .= $paramPrefix . $nothingPrompt . $prompt . implode( ', ', $choices ); } else { - switch ($type) { + switch ( $type ) { case 'namespace': // Special handling because namespaces are type-limited, yet they are not given - $desc .= $paramPrefix . $prompt . implode(', ', ApiBase :: getValidNamespaces()); + $desc .= $paramPrefix . $prompt . implode( ', ', ApiBase::getValidNamespaces() ); break; case 'limit': - $desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]} ({$paramSettings[self :: PARAM_MAX2]} for bots) allowed."; + $desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]} ({$paramSettings[self::PARAM_MAX2]} for bots) allowed."; break; case 'integer': - $hasMin = isset($paramSettings[self :: PARAM_MIN]); - $hasMax = isset($paramSettings[self :: PARAM_MAX]); - if ($hasMin || $hasMax) { - if (!$hasMax) - $intRangeStr = "The value must be no less than {$paramSettings[self :: PARAM_MIN]}"; - elseif (!$hasMin) - $intRangeStr = "The value must be no more than {$paramSettings[self :: PARAM_MAX]}"; - else - $intRangeStr = "The value must be between {$paramSettings[self :: PARAM_MIN]} and {$paramSettings[self :: PARAM_MAX]}"; + $hasMin = isset( $paramSettings[self::PARAM_MIN] ); + $hasMax = isset( $paramSettings[self::PARAM_MAX] ); + if ( $hasMin || $hasMax ) { + if ( !$hasMax ) { + $intRangeStr = "The value must be no less than {$paramSettings[self::PARAM_MIN]}"; + } elseif ( !$hasMin ) { + $intRangeStr = "The value must be no more than {$paramSettings[self::PARAM_MAX]}"; + } else { + $intRangeStr = "The value must be between {$paramSettings[self::PARAM_MIN]} and {$paramSettings[self::PARAM_MAX]}"; + } $desc .= $paramPrefix . $intRangeStr; } @@ -325,16 +347,51 @@ abstract class ApiBase { } } - $default = is_array($paramSettings) ? (isset ($paramSettings[self :: PARAM_DFLT]) ? $paramSettings[self :: PARAM_DFLT] : null) : $paramSettings; - if (!is_null($default) && $default !== false) + $default = is_array( $paramSettings ) ? ( isset( $paramSettings[self::PARAM_DFLT] ) ? $paramSettings[self::PARAM_DFLT] : null ) : $paramSettings; + if ( !is_null( $default ) && $default !== false ) { $desc .= $paramPrefix . "Default: $default"; + } - $msg .= sprintf(" %-14s - %s\n", $this->encodeParamName($paramName), $desc); + $msg .= sprintf( " %-14s - %s\n", $this->encodeParamName( $paramName ), $desc ); } return $msg; - } else + } else { return false; + } + } + + /** + * Callback for preg_replace_callback() call in makeHelpMsg(). + * Replaces a source file name with a link to ViewVC + */ + public function makeHelpMsg_callback( $matches ) { + global $wgAutoloadClasses, $wgAutoloadLocalClasses; + if ( isset( $wgAutoloadLocalClasses[get_class( $this )] ) ) { + $file = $wgAutoloadLocalClasses[get_class( $this )]; + } elseif ( isset( $wgAutoloadClasses[get_class( $this )] ) ) { + $file = $wgAutoloadClasses[get_class( $this )]; + } + + // Do some guesswork here + $path = strstr( $file, 'includes/api/' ); + if ( $path === false ) { + $path = strstr( $file, 'extensions/' ); + } else { + $path = 'phase3/' . $path; + } + + // Get the filename from $matches[2] instead of $file + // If they're not the same file, they're assumed to be in the + // same directory + // This is necessary to make stuff like ApiMain::getVersion() + // returning the version string for ApiBase work + if ( $path ) { + return "{$matches[0]}\n http://svn.wikimedia.org/" . + "viewvc/mediawiki/trunk/" . dirname( $path ) . + "/{$matches[2]}"; + } + return $matches[0]; } /** @@ -373,7 +430,7 @@ abstract class ApiBase { protected function getParamDescription() { return false; } - + /** * Get final list of parameters, after hooks have had a chance to * tweak it as needed. @@ -381,7 +438,7 @@ abstract class ApiBase { */ public function getFinalParams() { $params = $this->getAllowedParams(); - wfRunHooks('APIGetAllowedParams', array(&$this, &$params)); + wfRunHooks( 'APIGetAllowedParams', array( &$this, &$params ) ); return $params; } @@ -392,7 +449,7 @@ abstract class ApiBase { */ public function getFinalParamDescription() { $desc = $this->getParamDescription(); - wfRunHooks('APIGetParamDescription', array(&$this, &$desc)); + wfRunHooks( 'APIGetParamDescription', array( &$this, &$desc ) ); return $desc; } @@ -402,56 +459,63 @@ abstract class ApiBase { * @param $paramName string Parameter name * @return string Prefixed parameter name */ - public function encodeParamName($paramName) { + public function encodeParamName( $paramName ) { return $this->mModulePrefix . $paramName; } /** - * Using getAllowedParams(), this function makes an array of the values - * provided by the user, with key being the name of the variable, and - * value - validated value from user or default. limit=max will not be - * parsed if $parseMaxLimit is set to false; use this when the max - * limit is not definitive yet, e.g. when getting revisions. - * @param $parseMaxLimit bool - * @return array - */ - public function extractRequestParams($parseMaxLimit = true) { - $params = $this->getFinalParams(); - $results = array (); - - foreach ($params as $paramName => $paramSettings) - $results[$paramName] = $this->getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit); - - return $results; + * Using getAllowedParams(), this function makes an array of the values + * provided by the user, with key being the name of the variable, and + * value - validated value from user or default. limits will not be + * parsed if $parseLimit is set to false; use this when the max + * limit is not definitive yet, e.g. when getting revisions. + * @param $parseLimit Boolean: true by default + * @return array + */ + public function extractRequestParams( $parseLimit = true ) { + // Cache parameters, for performance and to avoid bug 24564. + if ( !isset( $this->mParamCache[$parseLimit] ) ) { + $params = $this->getFinalParams(); + $results = array(); + + if ( $params ) { // getFinalParams() can return false + foreach ( $params as $paramName => $paramSettings ) { + $results[$paramName] = $this->getParameterFromSettings( + $paramName, $paramSettings, $parseLimit ); + } + } + $this->mParamCache[$parseLimit] = $results; + } + return $this->mParamCache[$parseLimit]; } /** * Get a value for the given parameter * @param $paramName string Parameter name - * @param $parseMaxLimit bool see extractRequestParams() + * @param $parseLimit bool see extractRequestParams() * @return mixed Parameter value */ - protected function getParameter($paramName, $parseMaxLimit = true) { + protected function getParameter( $paramName, $parseLimit = true ) { $params = $this->getFinalParams(); $paramSettings = $params[$paramName]; - return $this->getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit); + return $this->getParameterFromSettings( $paramName, $paramSettings, $parseLimit ); } - + /** - * Die if none or more than one of a certain set of parameters is set + * Die if none or more than one of a certain set of parameters is set and not false. * @param $params array of parameter names */ - public function requireOnlyOneParameter($params) { + public function requireOnlyOneParameter( $params ) { $required = func_get_args(); - array_shift($required); - - $intersection = array_intersect(array_keys(array_filter($params, - create_function('$x', 'return !is_null($x);') - )), $required); - if (count($intersection) > 1) { - $this->dieUsage('The parameters '.implode(', ', $intersection).' can not be used together', 'invalidparammix'); - } elseif (count($intersection) == 0) { - $this->dieUsage('One of the parameters '.implode(', ', $required).' is required', 'missingparam'); + array_shift( $required ); + + $intersection = array_intersect( array_keys( array_filter( $params, + create_function( '$x', 'return !is_null($x) && $x !== false;' ) + ) ), $required ); + if ( count( $intersection ) > 1 ) { + $this->dieUsage( 'The parameters ' . implode( ', ', $intersection ) . ' can not be used together', 'invalidparammix' ); + } elseif ( count( $intersection ) == 0 ) { + $this->dieUsage( 'One of the parameters ' . implode( ', ', $required ) . ' is required', 'missingparam' ); } } @@ -462,15 +526,17 @@ abstract class ApiBase { */ public static function getValidNamespaces() { static $mValidNamespaces = null; - if (is_null($mValidNamespaces)) { + if ( is_null( $mValidNamespaces ) ) { global $wgContLang; - $mValidNamespaces = array (); - foreach (array_keys($wgContLang->getNamespaces()) as $ns) { - if ($ns >= 0) + $mValidNamespaces = array(); + foreach ( array_keys( $wgContLang->getNamespaces() ) as $ns ) { + if ( $ns >= 0 ) { $mValidNamespaces[] = $ns; + } } } + return $mValidNamespaces; } @@ -480,163 +546,183 @@ abstract class ApiBase { * @param $paramName String: parameter name * @param $paramSettings Mixed: default value or an array of settings * using PARAM_* constants. - * @param $parseMaxLimit Boolean: parse limit when max is given? + * @param $parseLimit Boolean: parse limit? * @return mixed Parameter value */ - protected function getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit) { - + protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) { // Some classes may decide to change parameter names - $encParamName = $this->encodeParamName($paramName); + $encParamName = $this->encodeParamName( $paramName ); - if (!is_array($paramSettings)) { + if ( !is_array( $paramSettings ) ) { $default = $paramSettings; $multi = false; - $type = gettype($paramSettings); + $type = gettype( $paramSettings ); $dupes = false; + $deprecated = false; } else { - $default = isset ($paramSettings[self :: PARAM_DFLT]) ? $paramSettings[self :: PARAM_DFLT] : null; - $multi = isset ($paramSettings[self :: PARAM_ISMULTI]) ? $paramSettings[self :: PARAM_ISMULTI] : false; - $type = isset ($paramSettings[self :: PARAM_TYPE]) ? $paramSettings[self :: PARAM_TYPE] : null; - $dupes = isset ($paramSettings[self:: PARAM_ALLOW_DUPLICATES]) ? $paramSettings[self :: PARAM_ALLOW_DUPLICATES] : false; + $default = isset( $paramSettings[self::PARAM_DFLT] ) ? $paramSettings[self::PARAM_DFLT] : null; + $multi = isset( $paramSettings[self::PARAM_ISMULTI] ) ? $paramSettings[self::PARAM_ISMULTI] : false; + $type = isset( $paramSettings[self::PARAM_TYPE] ) ? $paramSettings[self::PARAM_TYPE] : null; + $dupes = isset( $paramSettings[self::PARAM_ALLOW_DUPLICATES] ) ? $paramSettings[self::PARAM_ALLOW_DUPLICATES] : false; + $deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] ) ? $paramSettings[self::PARAM_DEPRECATED] : false; // When type is not given, and no choices, the type is the same as $default - if (!isset ($type)) { - if (isset ($default)) - $type = gettype($default); - else + if ( !isset( $type ) ) { + if ( isset( $default ) ) { + $type = gettype( $default ); + } else { $type = 'NULL'; // allow everything + } } } - if ($type == 'boolean') { - if (isset ($default) && $default !== false) { + 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'"); + ApiBase::dieDebug( __METHOD__, "Boolean param $encParamName's default is set to '$default'" ); } - $value = $this->getMain()->getRequest()->getCheck($encParamName); + $value = $this->getMain()->getRequest()->getCheck( $encParamName ); } else { - $value = $this->getMain()->getRequest()->getVal($encParamName, $default); + $value = $this->getMain()->getRequest()->getVal( $encParamName, $default ); - if (isset ($value) && $type == 'namespace') - $type = ApiBase :: getValidNamespaces(); + if ( isset( $value ) && $type == 'namespace' ) { + $type = ApiBase::getValidNamespaces(); + } } - if (isset ($value) && ($multi || is_array($type))) - $value = $this->parseMultiValue($encParamName, $value, $multi, is_array($type) ? $type : null); + if ( isset( $value ) && ( $multi || is_array( $type ) ) ) { + $value = $this->parseMultiValue( $encParamName, $value, $multi, is_array( $type ) ? $type : null ); + } // More validation only when choices were not given // choices were validated in parseMultiValue() - if (isset ($value)) { - if (!is_array($type)) { - switch ($type) { - case 'NULL' : // nothing to do + if ( isset( $value ) ) { + if ( !is_array( $type ) ) { + switch ( $type ) { + case 'NULL': // nothing to do break; - case 'string' : // nothing to do + case 'string': // nothing to do break; - case 'integer' : // Force everything using intval() and optionally validate limits + case 'integer': // Force everything using intval() and optionally validate limits - $value = is_array($value) ? array_map('intval', $value) : intval($value); - $min = isset ($paramSettings[self :: PARAM_MIN]) ? $paramSettings[self :: PARAM_MIN] : null; - $max = isset ($paramSettings[self :: PARAM_MAX]) ? $paramSettings[self :: PARAM_MAX] : null; + $value = is_array( $value ) ? array_map( 'intval', $value ) : intval( $value ); + $min = isset ( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : null; + $max = isset ( $paramSettings[self::PARAM_MAX] ) ? $paramSettings[self::PARAM_MAX] : null; - if (!is_null($min) || !is_null($max)) { - $values = is_array($value) ? $value : array($value); - foreach ($values as $v) { - $this->validateLimit($paramName, $v, $min, $max); + if ( !is_null( $min ) || !is_null( $max ) ) { + $values = is_array( $value ) ? $value : array( $value ); + foreach ( $values as &$v ) { + $this->validateLimit( $paramName, $v, $min, $max ); } } break; - case 'limit' : - if (!isset ($paramSettings[self :: PARAM_MAX]) || !isset ($paramSettings[self :: PARAM_MAX2])) - ApiBase :: dieDebug(__METHOD__, "MAX1 or MAX2 are not defined for the limit $encParamName"); - if ($multi) - ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName"); - $min = isset ($paramSettings[self :: PARAM_MIN]) ? $paramSettings[self :: PARAM_MIN] : 0; - if( $value == 'max' ) { - if( $parseMaxLimit ) { - $value = $this->getMain()->canApiHighLimits() ? $paramSettings[self :: PARAM_MAX2] : $paramSettings[self :: PARAM_MAX]; - $this->getResult()->addValue( 'limits', $this->getModuleName(), $value ); - $this->validateLimit($paramName, $value, $min, $paramSettings[self :: PARAM_MAX], $paramSettings[self :: PARAM_MAX2]); - } + case 'limit': + if ( !$parseLimit ) { + // Don't do any validation whatsoever + break; + } + if ( !isset( $paramSettings[self::PARAM_MAX] ) || !isset( $paramSettings[self::PARAM_MAX2] ) ) { + ApiBase::dieDebug( __METHOD__, "MAX1 or MAX2 are not defined for the limit $encParamName" ); } - else { - $value = intval($value); - $this->validateLimit($paramName, $value, $min, $paramSettings[self :: PARAM_MAX], $paramSettings[self :: PARAM_MAX2]); + if ( $multi ) { + ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); + } + $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : 0; + if ( $value == 'max' ) { + $value = $this->getMain()->canApiHighLimits() ? $paramSettings[self::PARAM_MAX2] : $paramSettings[self::PARAM_MAX]; + $this->getResult()->addValue( 'limits', $this->getModuleName(), $value ); + } else { + $value = intval( $value ); + $this->validateLimit( $paramName, $value, $min, $paramSettings[self::PARAM_MAX], $paramSettings[self::PARAM_MAX2] ); } break; - case 'boolean' : - if ($multi) - ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName"); + case 'boolean': + if ( $multi ) + ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); break; - case 'timestamp' : - if ($multi) - ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName"); - $value = wfTimestamp(TS_UNIX, $value); - if ($value === 0) - $this->dieUsage("Invalid value '$value' for timestamp parameter $encParamName", "badtimestamp_{$encParamName}"); - $value = wfTimestamp(TS_MW, $value); + case 'timestamp': + if ( $multi ) { + ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); + } + $value = wfTimestamp( TS_UNIX, $value ); + if ( $value === 0 ) { + $this->dieUsage( "Invalid value '$value' for timestamp parameter $encParamName", "badtimestamp_{$encParamName}" ); + } + $value = wfTimestamp( TS_MW, $value ); break; - case 'user' : + case 'user': $title = Title::makeTitleSafe( NS_USER, $value ); - if ( is_null( $title ) ) - $this->dieUsage("Invalid value for user parameter $encParamName", "baduser_{$encParamName}"); + if ( is_null( $title ) ) { + $this->dieUsage( "Invalid value for user parameter $encParamName", "baduser_{$encParamName}" ); + } $value = $title->getText(); break; - default : - ApiBase :: dieDebug(__METHOD__, "Param $encParamName's type is unknown - $type"); + default: + ApiBase::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" ); } } // Throw out duplicates if requested - if (is_array($value) && !$dupes) - $value = array_unique($value); + if ( is_array( $value ) && !$dupes ) { + $value = array_unique( $value ); + } + + // Set a warning if a deprecated parameter has been passed + if ( $deprecated && $value !== false ) { + $this->setWarning( "The $encParamName parameter has been deprecated." ); + } } return $value; } /** - * Return an array of values that were given in a 'a|b|c' notation, - * after it optionally validates them against the list allowed values. - * - * @param $valueName string The name of the parameter (for error - * reporting) - * @param $value mixed The value being parsed - * @param $allowMultiple bool Can $value contain more than one value - * separated by '|'? - * @param $allowedValues mixed An array of values to check against. If - * null, all values are accepted. - * @return mixed (allowMultiple ? an_array_of_values : a_single_value) - */ - protected function parseMultiValue($valueName, $value, $allowMultiple, $allowedValues) { - if( trim($value) === "" && $allowMultiple) + * Return an array of values that were given in a 'a|b|c' notation, + * after it optionally validates them against the list allowed values. + * + * @param $valueName string The name of the parameter (for error + * reporting) + * @param $value mixed The value being parsed + * @param $allowMultiple bool Can $value contain more than one value + * separated by '|'? + * @param $allowedValues mixed An array of values to check against. If + * null, all values are accepted. + * @return mixed (allowMultiple ? an_array_of_values : a_single_value) + */ + protected function parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues ) { + if ( trim( $value ) === '' && $allowMultiple ) { return array(); - $sizeLimit = $this->mMainModule->canApiHighLimits() ? self::LIMIT_SML2 : self::LIMIT_SML1; - $valuesList = explode('|', $value, $sizeLimit + 1); - if( self::truncateArray($valuesList, $sizeLimit) ) { - $this->setWarning("Too many values supplied for parameter '$valueName': the limit is $sizeLimit"); - } - if (!$allowMultiple && count($valuesList) != 1) { - $possibleValues = is_array($allowedValues) ? "of '" . implode("', '", $allowedValues) . "'" : ''; - $this->dieUsage("Only one $possibleValues is allowed for parameter '$valueName'", "multival_$valueName"); - } - if (is_array($allowedValues)) { - # Check for unknown values - $unknown = array_diff($valuesList, $allowedValues); - if(count($unknown)) - { - if($allowMultiple) - { - $s = count($unknown) > 1 ? "s" : ""; - $vals = implode(", ", $unknown); - $this->setWarning("Unrecognized value$s for parameter '$valueName': $vals"); + } + + // This is a bit awkward, but we want to avoid calling canApiHighLimits() because it unstubs $wgUser + $valuesList = explode( '|', $value, self::LIMIT_SML2 + 1 ); + $sizeLimit = count( $valuesList ) > self::LIMIT_SML1 && $this->mMainModule->canApiHighLimits() ? + self::LIMIT_SML2 : self::LIMIT_SML1; + + if ( self::truncateArray( $valuesList, $sizeLimit ) ) { + $this->setWarning( "Too many values supplied for parameter '$valueName': the limit is $sizeLimit" ); + } + + if ( !$allowMultiple && count( $valuesList ) != 1 ) { + $possibleValues = is_array( $allowedValues ) ? "of '" . implode( "', '", $allowedValues ) . "'" : ''; + $this->dieUsage( "Only one $possibleValues is allowed for parameter '$valueName'", "multival_$valueName" ); + } + + if ( is_array( $allowedValues ) ) { + // Check for unknown values + $unknown = array_diff( $valuesList, $allowedValues ); + if ( count( $unknown ) ) { + if ( $allowMultiple ) { + $s = count( $unknown ) > 1 ? 's' : ''; + $vals = implode( ", ", $unknown ); + $this->setWarning( "Unrecognized value$s for parameter '$valueName': $vals" ); + } else { + $this->dieUsage( "Unrecognized value for parameter '$valueName': {$valuesList[0]}", "unknown_$valueName" ); } - else - $this->dieUsage("Unrecognized value for parameter '$valueName': {$valuesList[0]}", "unknown_$valueName"); } - # Now throw them out - $valuesList = array_intersect($valuesList, $allowedValues); + // Now throw them out + $valuesList = array_intersect( $valuesList, $allowedValues ); } return $allowMultiple ? $valuesList : $valuesList[0]; @@ -651,54 +737,61 @@ abstract class ApiBase { * @param $max int Maximum value for users * @param $botMax int Maximum value for sysops/bots */ - function validateLimit($paramName, $value, $min, $max, $botMax = null) { - if (!is_null($min) && $value < $min) { - $this->dieUsage($this->encodeParamName($paramName) . " may not be less than $min (set to $value)", $paramName); + function validateLimit( $paramName, &$value, $min, $max, $botMax = null ) { + if ( !is_null( $min ) && $value < $min ) { + $this->setWarning( $this->encodeParamName( $paramName ) . " may not be less than $min (set to $value)" ); + $value = $min; } // Minimum is always validated, whereas maximum is checked only if not running in internal call mode - if ($this->getMain()->isInternalMode()) + if ( $this->getMain()->isInternalMode() ) { return; + } // Optimization: do not check user's bot status unless really needed -- skips db query // assumes $botMax >= $max - if (!is_null($max) && $value > $max) { - if (!is_null($botMax) && $this->getMain()->canApiHighLimits()) { - if ($value > $botMax) { - $this->dieUsage($this->encodeParamName($paramName) . " may not be over $botMax (set to $value) for bots or sysops", $paramName); + if ( !is_null( $max ) && $value > $max ) { + if ( !is_null( $botMax ) && $this->getMain()->canApiHighLimits() ) { + if ( $value > $botMax ) { + $this->setWarning( $this->encodeParamName( $paramName ) . " may not be over $botMax (set to $value) for bots or sysops" ); + $value = $botMax; } } else { - $this->dieUsage($this->encodeParamName($paramName) . " may not be over $max (set to $value) for users", $paramName); + $this->setWarning( $this->encodeParamName( $paramName ) . " may not be over $max (set to $value) for users" ); + $value = $max; } } } - + /** * Truncate an array to a certain length. * @param $arr array Array to truncate * @param $limit int Maximum length * @return bool True if the array was truncated, false otherwise */ - public static function truncateArray(&$arr, $limit) - { + public static function truncateArray( &$arr, $limit ) { $modified = false; - while(count($arr) > $limit) - { - $junk = array_pop($arr); + while ( count( $arr ) > $limit ) { + $junk = array_pop( $arr ); $modified = true; } return $modified; } /** - * Call the main module's error handler - * @param $description string Error text - * @param $errorCode string Error code + * Throw a UsageException, which will (if uncaught) call the main module's + * error handler and die with an error message. + * + * @param $description string One-line human-readable description of the + * error condition, e.g., "The API requires a valid action parameter" + * @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 element; array in ApiResult format */ - public function dieUsage($description, $errorCode, $httpRespCode = 0) { + public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) { wfProfileClose(); - throw new UsageException($description, $this->encodeParamName($errorCode), $httpRespCode); + throw new UsageException( $description, $this->encodeParamName( $errorCode ), $httpRespCode, $extradata ); } /** @@ -706,145 +799,170 @@ abstract class ApiBase { */ public static $messageMap = array( // This one MUST be present, or dieUsageMsg() will recurse infinitely - 'unknownerror' => array('code' => 'unknownerror', 'info' => "Unknown error: ``\$1''"), - 'unknownerror-nocode' => array('code' => 'unknownerror', 'info' => 'Unknown error'), + 'unknownerror' => array( 'code' => 'unknownerror', 'info' => "Unknown error: ``\$1''" ), + 'unknownerror-nocode' => array( 'code' => 'unknownerror', 'info' => 'Unknown error' ), // Messages from Title::getUserPermissionsErrors() - 'ns-specialprotected' => array('code' => 'unsupportednamespace', 'info' => "Pages in the Special namespace can't be edited"), - 'protectedinterface' => array('code' => 'protectednamespace-interface', 'info' => "You're not allowed to edit interface messages"), - 'namespaceprotected' => array('code' => 'protectednamespace', 'info' => "You're not allowed to edit pages in the ``\$1'' namespace"), - 'customcssjsprotected' => array('code' => 'customcssjsprotected', 'info' => "You're not allowed to edit custom CSS and JavaScript pages"), - 'cascadeprotected' => array('code' => 'cascadeprotected', 'info' =>"The page you're trying to edit is protected because it's included in a cascade-protected page"), - 'protectedpagetext' => array('code' => 'protectedpage', 'info' => "The ``\$1'' right is required to edit this page"), - 'protect-cantedit' => array('code' => 'cantedit', 'info' => "You can't protect this page because you can't edit it"), - 'badaccess-group0' => array('code' => 'permissiondenied', 'info' => "Permission denied"), // Generic permission denied message - 'badaccess-groups' => array('code' => 'permissiondenied', 'info' => "Permission denied"), - 'titleprotected' => array('code' => 'protectedtitle', 'info' => "This title has been protected from creation"), - 'nocreate-loggedin' => array('code' => 'cantcreate', 'info' => "You don't have permission to create new pages"), - 'nocreatetext' => array('code' => 'cantcreate-anon', 'info' => "Anonymous users can't create new pages"), - 'movenologintext' => array('code' => 'cantmove-anon', 'info' => "Anonymous users can't move pages"), - 'movenotallowed' => array('code' => 'cantmove', 'info' => "You don't have permission to move pages"), - 'confirmedittext' => array('code' => 'confirmemail', 'info' => "You must confirm your e-mail address before you can edit"), - 'blockedtext' => array('code' => 'blocked', 'info' => "You have been blocked from editing"), - 'autoblockedtext' => array('code' => 'autoblocked', 'info' => "Your IP address has been blocked automatically, because it was used by a blocked user"), + 'ns-specialprotected' => array( 'code' => 'unsupportednamespace', 'info' => "Pages in the Special namespace can't be edited" ), + 'protectedinterface' => array( 'code' => 'protectednamespace-interface', 'info' => "You're not allowed to edit interface messages" ), + 'namespaceprotected' => array( 'code' => 'protectednamespace', 'info' => "You're not allowed to edit pages in the ``\$1'' namespace" ), + 'customcssjsprotected' => array( 'code' => 'customcssjsprotected', 'info' => "You're not allowed to edit custom CSS and JavaScript pages" ), + 'cascadeprotected' => array( 'code' => 'cascadeprotected', 'info' => "The page you're trying to edit is protected because it's included in a cascade-protected page" ), + 'protectedpagetext' => array( 'code' => 'protectedpage', 'info' => "The ``\$1'' right is required to edit this page" ), + 'protect-cantedit' => array( 'code' => 'cantedit', 'info' => "You can't protect this page because you can't edit it" ), + 'badaccess-group0' => array( 'code' => 'permissiondenied', 'info' => "Permission denied" ), // Generic permission denied message + 'badaccess-groups' => array( 'code' => 'permissiondenied', 'info' => "Permission denied" ), + 'titleprotected' => array( 'code' => 'protectedtitle', 'info' => "This title has been protected from creation" ), + 'nocreate-loggedin' => array( 'code' => 'cantcreate', 'info' => "You don't have permission to create new pages" ), + 'nocreatetext' => array( 'code' => 'cantcreate-anon', 'info' => "Anonymous users can't create new pages" ), + 'movenologintext' => array( 'code' => 'cantmove-anon', 'info' => "Anonymous users can't move pages" ), + 'movenotallowed' => array( 'code' => 'cantmove', 'info' => "You don't have permission to move pages" ), + 'confirmedittext' => array( 'code' => 'confirmemail', 'info' => "You must confirm your e-mail address before you can edit" ), + 'blockedtext' => array( 'code' => 'blocked', 'info' => "You have been blocked from editing" ), + 'autoblockedtext' => array( 'code' => 'autoblocked', 'info' => "Your IP address has been blocked automatically, because it was used by a blocked user" ), // Miscellaneous interface messages - 'actionthrottledtext' => array('code' => 'ratelimited', 'info' => "You've exceeded your rate limit. Please wait some time and try again"), - 'alreadyrolled' => array('code' => 'alreadyrolled', 'info' => "The page you tried to rollback was already rolled back"), - 'cantrollback' => array('code' => 'onlyauthor', 'info' => "The page you tried to rollback only has one author"), - 'readonlytext' => array('code' => 'readonly', 'info' => "The wiki is currently in read-only mode"), - 'sessionfailure' => array('code' => 'badtoken', 'info' => "Invalid token"), - 'cannotdelete' => array('code' => 'cantdelete', 'info' => "Couldn't delete ``\$1''. Maybe it was deleted already by someone else"), - 'notanarticle' => array('code' => 'missingtitle', 'info' => "The page you requested doesn't exist"), - 'selfmove' => array('code' => 'selfmove', 'info' => "Can't move a page to itself"), - 'immobile_namespace' => array('code' => 'immobilenamespace', 'info' => "You tried to move pages from or to a namespace that is protected from moving"), - 'articleexists' => array('code' => 'articleexists', 'info' => "The destination article already exists and is not a redirect to the source article"), - 'protectedpage' => array('code' => 'protectedpage', 'info' => "You don't have permission to perform this move"), - 'hookaborted' => array('code' => 'hookaborted', 'info' => "The modification you tried to make was aborted by an extension hook"), - 'cantmove-titleprotected' => array('code' => 'protectedtitle', 'info' => "The destination article has been protected from creation"), - 'imagenocrossnamespace' => array('code' => 'nonfilenamespace', 'info' => "Can't move a file to a non-file namespace"), - 'imagetypemismatch' => array('code' => 'filetypemismatch', 'info' => "The new file extension doesn't match its type"), + 'actionthrottledtext' => array( 'code' => 'ratelimited', 'info' => "You've exceeded your rate limit. Please wait some time and try again" ), + 'alreadyrolled' => array( 'code' => 'alreadyrolled', 'info' => "The page you tried to rollback was already rolled back" ), + 'cantrollback' => array( 'code' => 'onlyauthor', 'info' => "The page you tried to rollback only has one author" ), + 'readonlytext' => array( 'code' => 'readonly', 'info' => "The wiki is currently in read-only mode" ), + 'sessionfailure' => array( 'code' => 'badtoken', 'info' => "Invalid token" ), + 'cannotdelete' => array( 'code' => 'cantdelete', 'info' => "Couldn't delete ``\$1''. Maybe it was deleted already by someone else" ), + 'notanarticle' => array( 'code' => 'missingtitle', 'info' => "The page you requested doesn't exist" ), + 'selfmove' => array( 'code' => 'selfmove', 'info' => "Can't move a page to itself" ), + 'immobile_namespace' => array( 'code' => 'immobilenamespace', 'info' => "You tried to move pages from or to a namespace that is protected from moving" ), + 'articleexists' => array( 'code' => 'articleexists', 'info' => "The destination article already exists and is not a redirect to the source article" ), + 'protectedpage' => array( 'code' => 'protectedpage', 'info' => "You don't have permission to perform this move" ), + 'hookaborted' => array( 'code' => 'hookaborted', 'info' => "The modification you tried to make was aborted by an extension hook" ), + 'cantmove-titleprotected' => array( 'code' => 'protectedtitle', 'info' => "The destination article has been protected from creation" ), + 'imagenocrossnamespace' => array( 'code' => 'nonfilenamespace', 'info' => "Can't move a file to a non-file namespace" ), + 'imagetypemismatch' => array( 'code' => 'filetypemismatch', 'info' => "The new file extension doesn't match its type" ), // 'badarticleerror' => shouldn't happen // 'badtitletext' => shouldn't happen - 'ip_range_invalid' => array('code' => 'invalidrange', 'info' => "Invalid IP range"), - 'range_block_disabled' => array('code' => 'rangedisabled', 'info' => "Blocking IP ranges has been disabled"), - 'nosuchusershort' => array('code' => 'nosuchuser', 'info' => "The user you specified doesn't exist"), - 'badipaddress' => array('code' => 'invalidip', 'info' => "Invalid IP address specified"), - 'ipb_expiry_invalid' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time"), - 'ipb_already_blocked' => array('code' => 'alreadyblocked', 'info' => "The user you tried to block was already blocked"), - 'ipb_blocked_as_range' => array('code' => 'blockedasrange', 'info' => "IP address ``\$1'' was blocked as part of range ``\$2''. You can't unblock the IP invidually, but you can unblock the range as a whole."), - 'ipb_cant_unblock' => array('code' => 'cantunblock', 'info' => "The block you specified was not found. It may have been unblocked already"), - 'mailnologin' => array('code' => 'cantsend', 'info' => "You're not logged in or you don't have a confirmed e-mail address, so you can't send e-mail"), - 'usermaildisabled' => array('code' => 'usermaildisabled', 'info' => "User email has been disabled"), - 'blockedemailuser' => array('code' => 'blockedfrommail', 'info' => "You have been blocked from sending e-mail"), - 'notarget' => array('code' => 'notarget', 'info' => "You have not specified a valid target for this action"), - 'noemail' => array('code' => 'noemail', 'info' => "The user has not specified a valid e-mail address, or has chosen not to receive e-mail from other users"), - 'rcpatroldisabled' => array('code' => 'patroldisabled', 'info' => "Patrolling is disabled on this wiki"), - 'markedaspatrollederror-noautopatrol' => array('code' => 'noautopatrol', 'info' => "You don't have permission to patrol your own changes"), - 'delete-toobig' => array('code' => 'bigdelete', 'info' => "You can't delete this page because it has more than \$1 revisions"), - 'movenotallowedfile' => array('code' => 'cantmovefile', 'info' => "You don't have permission to move files"), + 'ip_range_invalid' => array( 'code' => 'invalidrange', 'info' => "Invalid IP range" ), + 'range_block_disabled' => array( 'code' => 'rangedisabled', 'info' => "Blocking IP ranges has been disabled" ), + 'nosuchusershort' => array( 'code' => 'nosuchuser', 'info' => "The user you specified doesn't exist" ), + 'badipaddress' => array( 'code' => 'invalidip', 'info' => "Invalid IP address specified" ), + 'ipb_expiry_invalid' => array( 'code' => 'invalidexpiry', 'info' => "Invalid expiry time" ), + 'ipb_already_blocked' => array( 'code' => 'alreadyblocked', 'info' => "The user you tried to block was already blocked" ), + 'ipb_blocked_as_range' => array( 'code' => 'blockedasrange', 'info' => "IP address ``\$1'' was blocked as part of range ``\$2''. You can't unblock the IP invidually, but you can unblock the range as a whole." ), + 'ipb_cant_unblock' => array( 'code' => 'cantunblock', 'info' => "The block you specified was not found. It may have been unblocked already" ), + 'mailnologin' => array( 'code' => 'cantsend', 'info' => "You are not logged in, you do not have a confirmed e-mail address, or you are not allowed to send e-mail to other users, so you cannot send e-mail" ), + 'usermaildisabled' => array( 'code' => 'usermaildisabled', 'info' => "User email has been disabled" ), + 'blockedemailuser' => array( 'code' => 'blockedfrommail', 'info' => "You have been blocked from sending e-mail" ), + 'notarget' => array( 'code' => 'notarget', 'info' => "You have not specified a valid target for this action" ), + 'noemail' => array( 'code' => 'noemail', 'info' => "The user has not specified a valid e-mail address, or has chosen not to receive e-mail from other users" ), + 'rcpatroldisabled' => array( 'code' => 'patroldisabled', 'info' => "Patrolling is disabled on this wiki" ), + 'markedaspatrollederror-noautopatrol' => array( 'code' => 'noautopatrol', 'info' => "You don't have permission to patrol your own changes" ), + 'delete-toobig' => array( 'code' => 'bigdelete', 'info' => "You can't delete this page because it has more than \$1 revisions" ), + 'movenotallowedfile' => array( 'code' => 'cantmovefile', 'info' => "You don't have permission to move files" ), + 'userrights-no-interwiki' => array( 'code' => 'nointerwikiuserrights', 'info' => "You don't have permission to change user rights on other wikis" ), + 'userrights-nodatabase' => array( 'code' => 'nosuchdatabase', 'info' => "Database ``\$1'' does not exist or is not local" ), + 'nouserspecified' => array( 'code' => 'invaliduser', 'info' => "Invalid username ``\$1''" ), + 'noname' => array( 'code' => 'invaliduser', 'info' => "Invalid username ``\$1''" ), // API-specific messages - 'readrequired' => array('code' => 'readapidenied', 'info' => "You need read permission to use this module"), - 'writedisabled' => array('code' => 'noapiwrite', 'info' => "Editing of this wiki through the API is disabled. Make sure the \$wgEnableWriteAPI=true; statement is included in the wiki's LocalSettings.php file"), - 'writerequired' => array('code' => 'writeapidenied', 'info' => "You're not allowed to edit this wiki through the API"), - 'missingparam' => array('code' => 'no$1', 'info' => "The \$1 parameter must be set"), - 'invalidtitle' => array('code' => 'invalidtitle', 'info' => "Bad title ``\$1''"), - 'nosuchpageid' => array('code' => 'nosuchpageid', 'info' => "There is no page with ID \$1"), - 'nosuchrevid' => array('code' => 'nosuchrevid', 'info' => "There is no revision with ID \$1"), - 'invaliduser' => array('code' => 'invaliduser', 'info' => "Invalid username ``\$1''"), - 'invalidexpiry' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time ``\$1''"), - 'pastexpiry' => array('code' => 'pastexpiry', 'info' => "Expiry time ``\$1'' is in the past"), - 'create-titleexists' => array('code' => 'create-titleexists', 'info' => "Existing titles can't be protected with 'create'"), - 'missingtitle-createonly' => array('code' => 'missingtitle-createonly', 'info' => "Missing titles can only be protected with 'create'"), - 'cantblock' => array('code' => 'cantblock', 'info' => "You don't have permission to block users"), - 'canthide' => array('code' => 'canthide', 'info' => "You don't have permission to hide user names from the block log"), - 'cantblock-email' => array('code' => 'cantblock-email', 'info' => "You don't have permission to block users from sending e-mail through the wiki"), - 'unblock-notarget' => array('code' => 'notarget', 'info' => "Either the id or the user parameter must be set"), - 'unblock-idanduser' => array('code' => 'idanduser', 'info' => "The id and user parameters can't be used together"), - 'cantunblock' => array('code' => 'permissiondenied', 'info' => "You don't have permission to unblock users"), - 'cannotundelete' => array('code' => 'cantundelete', 'info' => "Couldn't undelete: the requested revisions may not exist, or may have been undeleted already"), - 'permdenied-undelete' => array('code' => 'permissiondenied', 'info' => "You don't have permission to restore deleted revisions"), - 'createonly-exists' => array('code' => 'articleexists', 'info' => "The article you tried to create has been created already"), - 'nocreate-missing' => array('code' => 'missingtitle', 'info' => "The article you tried to edit doesn't exist"), - 'nosuchrcid' => array('code' => 'nosuchrcid', 'info' => "There is no change with rcid ``\$1''"), - 'cantpurge' => array('code' => 'cantpurge', 'info' => "Only users with the 'purge' right can purge pages via the API"), - 'protect-invalidaction' => array('code' => 'protect-invalidaction', 'info' => "Invalid protection type ``\$1''"), - 'protect-invalidlevel' => array('code' => 'protect-invalidlevel', 'info' => "Invalid protection level ``\$1''"), - 'toofewexpiries' => array('code' => 'toofewexpiries', 'info' => "\$1 expiry timestamps were provided where \$2 were needed"), - '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"), - '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'), - 'importuploaderrortemp' => array('code' => 'notempdir', 'info' => 'The temporary upload directory is missing'), - 'importcantopen' => array('code' => 'cantopenfile', 'info' => "Couldn't open the uploaded file"), - 'import-noarticle' => array('code' => 'badinterwiki', 'info' => 'Invalid interwiki title specified'), - 'importbadinterwiki' => array('code' => 'badinterwiki', 'info' => 'Invalid interwiki title specified'), - 'import-unknownerror' => array('code' => 'import-unknownerror', 'info' => "Unknown error on import: ``\$1''"), + 'readrequired' => array( 'code' => 'readapidenied', 'info' => "You need read permission to use this module" ), + 'writedisabled' => array( 'code' => 'noapiwrite', 'info' => "Editing of this wiki through the API is disabled. Make sure the \$wgEnableWriteAPI=true; statement is included in the wiki's LocalSettings.php file" ), + 'writerequired' => array( 'code' => 'writeapidenied', 'info' => "You're not allowed to edit this wiki through the API" ), + 'missingparam' => array( 'code' => 'no$1', 'info' => "The \$1 parameter must be set" ), + 'invalidtitle' => array( 'code' => 'invalidtitle', 'info' => "Bad title ``\$1''" ), + 'nosuchpageid' => array( 'code' => 'nosuchpageid', 'info' => "There is no page with ID \$1" ), + 'nosuchrevid' => array( 'code' => 'nosuchrevid', 'info' => "There is no revision with ID \$1" ), + 'nosuchuser' => array( 'code' => 'nosuchuser', 'info' => "User ``\$1'' doesn't exist" ), + 'invaliduser' => array( 'code' => 'invaliduser', 'info' => "Invalid username ``\$1''" ), + 'invalidexpiry' => array( 'code' => 'invalidexpiry', 'info' => "Invalid expiry time ``\$1''" ), + 'pastexpiry' => array( 'code' => 'pastexpiry', 'info' => "Expiry time ``\$1'' is in the past" ), + 'create-titleexists' => array( 'code' => 'create-titleexists', 'info' => "Existing titles can't be protected with 'create'" ), + 'missingtitle-createonly' => array( 'code' => 'missingtitle-createonly', 'info' => "Missing titles can only be protected with 'create'" ), + 'cantblock' => array( 'code' => 'cantblock', 'info' => "You don't have permission to block users" ), + 'canthide' => array( 'code' => 'canthide', 'info' => "You don't have permission to hide user names from the block log" ), + 'cantblock-email' => array( 'code' => 'cantblock-email', 'info' => "You don't have permission to block users from sending e-mail through the wiki" ), + 'unblock-notarget' => array( 'code' => 'notarget', 'info' => "Either the id or the user parameter must be set" ), + 'unblock-idanduser' => array( 'code' => 'idanduser', 'info' => "The id and user parameters can't be used together" ), + 'cantunblock' => array( 'code' => 'permissiondenied', 'info' => "You don't have permission to unblock users" ), + 'cannotundelete' => array( 'code' => 'cantundelete', 'info' => "Couldn't undelete: the requested revisions may not exist, or may have been undeleted already" ), + 'permdenied-undelete' => array( 'code' => 'permissiondenied', 'info' => "You don't have permission to restore deleted revisions" ), + 'createonly-exists' => array( 'code' => 'articleexists', 'info' => "The article you tried to create has been created already" ), + 'nocreate-missing' => array( 'code' => 'missingtitle', 'info' => "The article you tried to edit doesn't exist" ), + 'nosuchrcid' => array( 'code' => 'nosuchrcid', 'info' => "There is no change with rcid ``\$1''" ), + 'cantpurge' => array( 'code' => 'cantpurge', 'info' => "Only users with the 'purge' right can purge pages via the API" ), + 'protect-invalidaction' => array( 'code' => 'protect-invalidaction', 'info' => "Invalid protection type ``\$1''" ), + 'protect-invalidlevel' => array( 'code' => 'protect-invalidlevel', 'info' => "Invalid protection level ``\$1''" ), + 'toofewexpiries' => array( 'code' => 'toofewexpiries', 'info' => "\$1 expiry timestamps were provided where \$2 were needed" ), + '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' ), + 'importuploaderrortemp' => array( 'code' => 'notempdir', 'info' => 'The temporary upload directory is missing' ), + 'importcantopen' => array( 'code' => 'cantopenfile', 'info' => "Couldn't open the uploaded file" ), + 'import-noarticle' => array( 'code' => 'badinterwiki', 'info' => 'Invalid interwiki title specified' ), + 'importbadinterwiki' => array( 'code' => 'badinterwiki', 'info' => 'Invalid interwiki title specified' ), + 'import-unknownerror' => array( 'code' => 'import-unknownerror', 'info' => "Unknown error on import: ``\$1''" ), + 'cantoverwrite-sharedfile' => array( 'code' => 'cantoverwrite-sharedfile', 'info' => 'The target file exists on a shared repository and you do not have permission to override it' ), + 'sharedfile-exists' => array( 'code' => 'fileexists-sharedrepo-perm', 'info' => 'The target file exists on a shared repository. Use the ignorewarnings parameter to override it.' ), + 'mustbeposted' => array( 'code' => 'mustbeposted', 'info' => "The \$1 module requires a POST request" ), + 'show' => array( 'code' => 'show', 'info' => 'Incorrect parameter - mutually exclusive values may not be supplied' ), // 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"), - 'wasdeleted' => array('code' => 'pagedeleted', 'info' => "The page has been deleted since you fetched its timestamp"), - 'blankpage' => array('code' => 'emptypage', 'info' => "Creating new, empty pages is not allowed"), - 'editconflict' => array('code' => 'editconflict', 'info' => "Edit conflict detected"), - 'hashcheckfailed' => array('code' => 'badmd5', 'info' => "The supplied MD5 hash was incorrect"), - 'missingtext' => array('code' => 'notext', 'info' => "One of the text, appendtext, prependtext and undo parameters must be set"), - 'emptynewsection' => array('code' => 'emptynewsection', 'info' => 'Creating empty new sections is not possible.'), - 'revwrongpage' => array('code' => 'revwrongpage', 'info' => "r\$1 is not a revision of ``\$2''"), - 'undo-failure' => array('code' => 'undofailure', 'info' => 'Undo failed due to conflicting intermediate edits'), + '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" ), + 'wasdeleted' => array( 'code' => 'pagedeleted', 'info' => "The page has been deleted since you fetched its timestamp" ), + 'blankpage' => array( 'code' => 'emptypage', 'info' => "Creating new, empty pages is not allowed" ), + 'editconflict' => array( 'code' => 'editconflict', 'info' => "Edit conflict detected" ), + 'hashcheckfailed' => array( 'code' => 'badmd5', 'info' => "The supplied MD5 hash was incorrect" ), + 'missingtext' => array( 'code' => 'notext', 'info' => "One of the text, appendtext, prependtext and undo parameters must be set" ), + 'emptynewsection' => array( 'code' => 'emptynewsection', 'info' => 'Creating empty new sections is not possible.' ), + 'revwrongpage' => array( 'code' => 'revwrongpage', 'info' => "r\$1 is not a revision of ``\$2''" ), + 'undo-failure' => array( 'code' => 'undofailure', 'info' => 'Undo failed due to conflicting intermediate edits' ), + + // uploadMsgs + 'invalid-session-key' => array( 'code' => 'invalid-session-key', 'info' => 'Not a valid session 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' ), ); + /** + * Helper function for readonly errors + */ + public function dieReadOnly() { + $parsed = $this->parseMsg( array( 'readonlytext' ) ); + $this->dieUsage( $parsed['info'], $parsed['code'], /* http error */ 0, + array( 'readonlyreason' => wfReadOnlyReason() ) ); + } + /** * Output the error message related to a certain array * @param $error array Element of a getUserPermissionsErrors()-style array */ - public function dieUsageMsg($error) { - $parsed = $this->parseMsg($error); - $this->dieUsage($parsed['info'], $parsed['code']); + public function dieUsageMsg( $error ) { + $parsed = $this->parseMsg( $error ); + $this->dieUsage( $parsed['info'], $parsed['code'] ); } - + /** * Return the error message related to a certain array * @param $error array Element of a getUserPermissionsErrors()-style array * @return array('code' => code, 'info' => info) */ - public function parseMsg($error) { - $key = array_shift($error); - if(isset(self::$messageMap[$key])) - return array( 'code' => - wfMsgReplaceArgs(self::$messageMap[$key]['code'], $error), + public function parseMsg( $error ) { + $key = array_shift( $error ); + if ( isset( self::$messageMap[$key] ) ) { + return array( 'code' => + wfMsgReplaceArgs( self::$messageMap[$key]['code'], $error ), 'info' => - wfMsgReplaceArgs(self::$messageMap[$key]['info'], $error) + wfMsgReplaceArgs( self::$messageMap[$key]['info'], $error ) ); + } // If the key isn't present, throw an "unknown error" - return $this->parseMsg(array('unknownerror', $key)); + return $this->parseMsg( array( 'unknownerror', $key ) ); } /** @@ -852,8 +970,8 @@ abstract class ApiBase { * @param $method string Method or function name * @param $message string Error message */ - protected static function dieDebug($method, $message) { - wfDebugDieBacktrace("Internal error in $method: $message"); + protected static function dieDebug( $method, $message ) { + wfDebugDieBacktrace( "Internal error in $method: $message" ); } /** @@ -887,6 +1005,59 @@ abstract class ApiBase { return false; } + /** + * 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 + * @returns bool + */ + public function getTokenSalt() { + return false; + } + + /** + * Returns a list of all possible errors returned by the module + * @return array in the format of array( key, param1, param2, ... ) or array( 'code' => ..., 'info' => ... ) + */ + public function getPossibleErrors() { + $ret = array(); + + if ( $this->mustBePosted() ) { + $ret[] = array( 'mustbeposted', $this->getModuleName() ); + } + + if ( $this->isReadMode() ) { + $ret[] = array( 'readrequired' ); + } + + if ( $this->isWriteMode() ) { + $ret[] = array( 'writerequired' ); + $ret[] = array( 'writedisabled' ); + } + + if ( $this->getTokenSalt() !== false ) { + $ret[] = array( 'missingparam', 'token' ); + $ret[] = array( 'sessionfailure' ); + } + + return $ret; + } + + /** + * Parses a list of errors into a standardised format + * @param $errors array List of errors. Items can be in the for array( key, param1, param2, ... ) or array( 'code' => ..., 'info' => ... ) + * @return array Parsed list of errors with items in the form array( 'code' => ..., 'info' => ... ) + */ + public function parseErrors( $errors ) { + $ret = array(); + + foreach ( $errors as $row ) { + if ( isset( $row['code'] ) && isset( $row['info'] ) ) { + $ret[] = $row; + } else { + $ret[] = $this->parseMsg( $row ); + } + } + return $ret; + } /** * Profiling: total module execution time @@ -897,24 +1068,27 @@ abstract class ApiBase { * Start module profiling */ public function profileIn() { - if ($this->mTimeIn !== 0) - ApiBase :: dieDebug(__METHOD__, 'called twice without calling profileOut()'); - $this->mTimeIn = microtime(true); - wfProfileIn($this->getModuleProfileName()); + if ( $this->mTimeIn !== 0 ) { + ApiBase::dieDebug( __METHOD__, 'called twice without calling profileOut()' ); + } + $this->mTimeIn = microtime( true ); + wfProfileIn( $this->getModuleProfileName() ); } /** * End module profiling */ public function profileOut() { - if ($this->mTimeIn === 0) - ApiBase :: dieDebug(__METHOD__, 'called without calling profileIn() first'); - if ($this->mDBTimeIn !== 0) - ApiBase :: dieDebug(__METHOD__, 'must be called after database profiling is done with profileDBOut()'); + if ( $this->mTimeIn === 0 ) { + ApiBase::dieDebug( __METHOD__, 'called without calling profileIn() first' ); + } + if ( $this->mDBTimeIn !== 0 ) { + ApiBase::dieDebug( __METHOD__, 'must be called after database profiling is done with profileDBOut()' ); + } - $this->mModuleTime += microtime(true) - $this->mTimeIn; + $this->mModuleTime += microtime( true ) - $this->mTimeIn; $this->mTimeIn = 0; - wfProfileOut($this->getModuleProfileName()); + wfProfileOut( $this->getModuleProfileName() ); } /** @@ -922,9 +1096,10 @@ abstract class ApiBase { * of the profiling state the module was in. This method does such cleanup. */ public function safeProfileOut() { - if ($this->mTimeIn !== 0) { - if ($this->mDBTimeIn !== 0) + if ( $this->mTimeIn !== 0 ) { + if ( $this->mDBTimeIn !== 0 ) { $this->profileDBOut(); + } $this->profileOut(); } } @@ -934,8 +1109,9 @@ abstract class ApiBase { * @return float */ public function getProfileTime() { - if ($this->mTimeIn !== 0) - ApiBase :: dieDebug(__METHOD__, 'called without calling profileOut() first'); + if ( $this->mTimeIn !== 0 ) { + ApiBase::dieDebug( __METHOD__, 'called without calling profileOut() first' ); + } return $this->mModuleTime; } @@ -948,29 +1124,33 @@ abstract class ApiBase { * Start module profiling */ public function profileDBIn() { - if ($this->mTimeIn === 0) - ApiBase :: dieDebug(__METHOD__, 'must be called while profiling the entire module with profileIn()'); - if ($this->mDBTimeIn !== 0) - ApiBase :: dieDebug(__METHOD__, 'called twice without calling profileDBOut()'); - $this->mDBTimeIn = microtime(true); - wfProfileIn($this->getModuleProfileName(true)); + if ( $this->mTimeIn === 0 ) { + ApiBase::dieDebug( __METHOD__, 'must be called while profiling the entire module with profileIn()' ); + } + if ( $this->mDBTimeIn !== 0 ) { + ApiBase::dieDebug( __METHOD__, 'called twice without calling profileDBOut()' ); + } + $this->mDBTimeIn = microtime( true ); + wfProfileIn( $this->getModuleProfileName( true ) ); } /** * End database profiling */ public function profileDBOut() { - if ($this->mTimeIn === 0) - ApiBase :: dieDebug(__METHOD__, 'must be called while profiling the entire module with profileIn()'); - if ($this->mDBTimeIn === 0) - ApiBase :: dieDebug(__METHOD__, 'called without calling profileDBIn() first'); + if ( $this->mTimeIn === 0 ) { + ApiBase::dieDebug( __METHOD__, 'must be called while profiling the entire module with profileIn()' ); + } + if ( $this->mDBTimeIn === 0 ) { + ApiBase::dieDebug( __METHOD__, 'called without calling profileDBIn() first' ); + } - $time = microtime(true) - $this->mDBTimeIn; + $time = microtime( true ) - $this->mDBTimeIn; $this->mDBTimeIn = 0; $this->mDBTime += $time; $this->getMain()->mDBTime += $time; - wfProfileOut($this->getModuleProfileName(true)); + wfProfileOut( $this->getModuleProfileName( true ) ); } /** @@ -978,8 +1158,9 @@ abstract class ApiBase { * @return float */ public function getProfileDBTime() { - if ($this->mDBTimeIn !== 0) - ApiBase :: dieDebug(__METHOD__, 'called without calling profileDBOut() first'); + if ( $this->mDBTimeIn !== 0 ) { + ApiBase::dieDebug( __METHOD__, 'called without calling profileDBOut() first' ); + } return $this->mDBTime; } @@ -989,20 +1170,20 @@ abstract class ApiBase { * @param $name string Description of the printed value * @param $backtrace bool If true, print a backtrace */ - public static function debugPrint($value, $name = 'unknown', $backtrace = false) { + public static function debugPrint( $value, $name = 'unknown', $backtrace = false ) { print "\n\n
Debugging value '$name':\n\n";
-		var_export($value);
-		if ($backtrace)
+		var_export( $value );
+		if ( $backtrace ) {
 			print "\n" . wfBacktrace();
+		}
 		print "\n
\n"; } - /** * Returns a string that identifies the version of this class. * @return string */ public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiBase.php 50217 2009-05-05 13:12:16Z tstarling $'; + return __CLASS__ . ': $Id: ApiBase.php 70066 2010-07-28 05:52:32Z tstarling $'; } } diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php index 1c0bd5ac..91bbaf6d 100644 --- a/includes/api/ApiBlock.php +++ b/includes/api/ApiBlock.php @@ -1,10 +1,10 @@ .@home.nl + * Copyright © 2007 Roan Kattouw .@home.nl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,9 +22,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once( "ApiBase.php" ); } /** @@ -38,8 +38,8 @@ class ApiBlock extends ApiBase { /** * Std ctor. */ - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent::__construct( $main, $action ); } /** @@ -52,31 +52,30 @@ class ApiBlock extends ApiBase { global $wgUser, $wgBlockAllowsUTEdit; $params = $this->extractRequestParams(); - if($params['gettoken']) - { + if ( $params['gettoken'] ) { $res['blocktoken'] = $wgUser->editToken(); - $this->getResult()->addValue(null, $this->getModuleName(), $res); + $this->getResult()->addValue( null, $this->getModuleName(), $res ); return; } - if(is_null($params['user'])) - $this->dieUsageMsg(array('missingparam', 'user')); - if(is_null($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); - if(!$wgUser->isAllowed('block')) - $this->dieUsageMsg(array('cantblock')); - if($params['hidename'] && !$wgUser->isAllowed('hideuser')) - $this->dieUsageMsg(array('canthide')); - if($params['noemail'] && !$wgUser->isAllowed('blockemail')) - $this->dieUsageMsg(array('cantblock-email')); - - $form = new IPBlockForm(''); + if ( is_null( $params['user'] ) ) { + $this->dieUsageMsg( array( 'missingparam', 'user' ) ); + } + if ( !$wgUser->isAllowed( 'block' ) ) { + $this->dieUsageMsg( array( 'cantblock' ) ); + } + if ( $params['hidename'] && !$wgUser->isAllowed( 'hideuser' ) ) { + $this->dieUsageMsg( array( 'canthide' ) ); + } + if ( $params['noemail'] && !IPBlockForm::canBlockEmail( $wgUser ) ) { + $this->dieUsageMsg( array( 'cantblock-email' ) ); + } + + $form = new IPBlockForm( '' ); $form->BlockAddress = $params['user']; - $form->BlockReason = (is_null($params['reason']) ? '' : $params['reason']); + $form->BlockReason = ( is_null( $params['reason'] ) ? '' : $params['reason'] ); $form->BlockReasonList = 'other'; - $form->BlockExpiry = ($params['expiry'] == 'never' ? 'infinite' : $params['expiry']); + $form->BlockExpiry = ( $params['expiry'] == 'never' ? 'infinite' : $params['expiry'] ); $form->BlockOther = ''; $form->BlockAnonOnly = $params['anononly']; $form->BlockCreateAccount = $params['nocreate']; @@ -87,39 +86,48 @@ class ApiBlock extends ApiBase { $form->BlockReblock = $params['reblock']; $userID = $expiry = null; - $retval = $form->doBlock($userID, $expiry); - if(count($retval)) + $retval = $form->doBlock( $userID, $expiry ); + if ( count( $retval ) ) { // We don't care about multiple errors, just report one of them - $this->dieUsageMsg($retval); + $this->dieUsageMsg( $retval ); + } $res['user'] = $params['user']; - $res['userID'] = intval($userID); - $res['expiry'] = ($expiry == Block::infinity() ? 'infinite' : wfTimestamp(TS_ISO_8601, $expiry)); + $res['userID'] = intval( $userID ); + $res['expiry'] = ( $expiry == Block::infinity() ? 'infinite' : wfTimestamp( TS_ISO_8601, $expiry ) ); $res['reason'] = $params['reason']; - if($params['anononly']) + if ( $params['anononly'] ) { $res['anononly'] = ''; - if($params['nocreate']) + } + if ( $params['nocreate'] ) { $res['nocreate'] = ''; - if($params['autoblock']) + } + if ( $params['autoblock'] ) { $res['autoblock'] = ''; - if($params['noemail']) + } + if ( $params['noemail'] ) { $res['noemail'] = ''; - if($params['hidename']) + } + if ( $params['hidename'] ) { $res['hidename'] = ''; - if($params['allowusertalk']) + } + if ( $params['allowusertalk'] ) { $res['allowusertalk'] = ''; + } - $this->getResult()->addValue(null, $this->getModuleName(), $res); + $this->getResult()->addValue( null, $this->getModuleName(), $res ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; } public function getAllowedParams() { - return array ( + return array( 'user' => null, 'token' => null, 'gettoken' => false, @@ -136,7 +144,7 @@ class ApiBlock extends ApiBase { } public function getParamDescription() { - return array ( + 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', 'gettoken' => 'If set, a block token will be returned, and no other action will be taken', @@ -157,15 +165,28 @@ class ApiBlock extends ApiBase { 'Block a user.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'user' ), + array( 'cantblock' ), + array( 'canthide' ), + array( 'cantblock-email' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { - return array ( + return array( 'api.php?action=block&user=123.5.5.12&expiry=3%20days&reason=First%20strike', 'api.php?action=block&user=Vandal&expiry=never&reason=Vandalism&nocreate&autoblock&noemail' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiBlock.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiBlock.php 62766 2010-02-21 12:32:46Z ashley $'; } } diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php index 9431ad78..2b349bd7 100644 --- a/includes/api/ApiDelete.php +++ b/includes/api/ApiDelete.php @@ -1,10 +1,10 @@ .@home.nl + * Copyright © 2007 Roan Kattouw .@home.nl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,12 +22,11 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once( "ApiBase.php" ); } - /** * API module that facilitates deleting pages. The API eqivalent of action=delete. * Requires API write mode to be enabled. @@ -36,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiDelete extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent::__construct( $main, $action ); } /** @@ -49,65 +48,60 @@ class ApiDelete extends ApiBase { */ public function execute() { global $wgUser; + $params = $this->extractRequestParams(); - $this->requireOnlyOneParameter($params, 'title', 'pageid'); - if(!isset($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); + $this->requireOnlyOneParameter( $params, 'title', 'pageid' ); - if(isset($params['title'])) - { - $titleObj = Title::newFromText($params['title']); - if(!$titleObj) - $this->dieUsageMsg(array('invalidtitle', $params['title'])); + if ( isset( $params['title'] ) ) { + $titleObj = Title::newFromText( $params['title'] ); + if ( !$titleObj ) { + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + } + } elseif ( isset( $params['pageid'] ) ) { + $titleObj = Title::newFromID( $params['pageid'] ); + if ( !$titleObj ) { + $this->dieUsageMsg( array( 'nosuchpageid', $params['pageid'] ) ); + } } - else if(isset($params['pageid'])) - { - $titleObj = Title::newFromID($params['pageid']); - if(!$titleObj) - $this->dieUsageMsg(array('nosuchpageid', $params['pageid'])); + if ( !$titleObj->exists() ) { + $this->dieUsageMsg( array( 'notanarticle' ) ); } - if(!$titleObj->exists()) - $this->dieUsageMsg(array('notanarticle')); - - $reason = (isset($params['reason']) ? $params['reason'] : NULL); - if ($titleObj->getNamespace() == NS_FILE) { - $retval = self::deleteFile($params['token'], $titleObj, $params['oldimage'], $reason, false); - if(count($retval)) - // We don't care about multiple errors, just report one of them - $this->dieUsageMsg(reset($retval)); + + $reason = ( isset( $params['reason'] ) ? $params['reason'] : null ); + if ( $titleObj->getNamespace() == NS_FILE ) { + $retval = self::deleteFile( $params['token'], $titleObj, $params['oldimage'], $reason, false ); + if ( count( $retval ) ) { + $this->dieUsageMsg( reset( $retval ) ); // We don't care about multiple errors, just report one of them + } } else { - $articleObj = new Article($titleObj); - if($articleObj->isBigDeletion() && !$wgUser->isAllowed('bigdelete')) { - global $wgDeleteRevisionsLimit; - $this->dieUsageMsg(array('delete-toobig', $wgDeleteRevisionsLimit)); + $articleObj = new Article( $titleObj ); + $retval = self::delete( $articleObj, $params['token'], $reason ); + + if ( count( $retval ) ) { + $this->dieUsageMsg( reset( $retval ) ); // We don't care about multiple errors, just report one of them } - $retval = self::delete($articleObj, $params['token'], $reason); - - if(count($retval)) - // We don't care about multiple errors, just report one of them - $this->dieUsageMsg(reset($retval)); - - if($params['watch'] || $wgUser->getOption('watchdeletion')) + + if ( $params['watch'] || $wgUser->getOption( 'watchdeletion' ) ) { $articleObj->doWatch(); - else if($params['unwatch']) + } elseif ( $params['unwatch'] ) { $articleObj->doUnwatch(); + } } - $r = array('title' => $titleObj->getPrefixedText(), 'reason' => $reason); - $this->getResult()->addValue(null, $this->getModuleName(), $r); + $r = array( 'title' => $titleObj->getPrefixedText(), 'reason' => $reason ); + $this->getResult()->addValue( null, $this->getModuleName(), $r ); } - private static function getPermissionsError(&$title, $token) { + private static function getPermissionsError( &$title, $token ) { global $wgUser; - + // Check permissions - $errors = $title->getUserPermissionsErrors('delete', $wgUser); - if (count($errors) > 0) return $errors; - - // Check token - if(!$wgUser->matchEditToken($token)) - return array(array('sessionfailure')); + $errors = $title->getUserPermissionsErrors( 'delete', $wgUser ); + if ( count( $errors ) > 0 ) { + return $errors; + } + return array(); } @@ -119,70 +113,84 @@ class ApiDelete extends ApiBase { * @param string $reason - Reason for the deletion. Autogenerated if NULL * @return Title::getUserPermissionsErrors()-like array */ - public static function delete(&$article, $token, &$reason = NULL) - { + public static function delete( &$article, $token, &$reason = null ) { global $wgUser; + if ( $article->isBigDeletion() && !$wgUser->isAllowed( 'bigdelete' ) ) { + global $wgDeleteRevisionsLimit; + return array( array( 'delete-toobig', $wgDeleteRevisionsLimit ) ); + } $title = $article->getTitle(); - $errors = self::getPermissionsError($title, $token); - if (count($errors)) return $errors; + $errors = self::getPermissionsError( $title, $token ); + if ( count( $errors ) ) { + return $errors; + } // Auto-generate a summary, if necessary - if(is_null($reason)) - { - # Need to pass a throwaway variable because generateReason expects - # a reference + if ( is_null( $reason ) ) { + // Need to pass a throwaway variable because generateReason expects + // a reference $hasHistory = false; - $reason = $article->generateReason($hasHistory); - if($reason === false) - return array(array('cannotdelete')); + $reason = $article->generateReason( $hasHistory ); + if ( $reason === false ) { + return array( array( 'cannotdelete' ) ); + } } $error = ''; - if (!wfRunHooks('ArticleDelete', array(&$article, &$wgUser, &$reason, $error))) - $this->dieUsageMsg(array('hookaborted', $error)); + if ( !wfRunHooks( 'ArticleDelete', array( &$article, &$wgUser, &$reason, $error ) ) ) { + $this->dieUsageMsg( array( 'hookaborted', $error ) ); + } // Luckily, Article.php provides a reusable delete function that does the hard work for us - if($article->doDeleteArticle($reason)) { - wfRunHooks('ArticleDeleteComplete', array(&$article, &$wgUser, $reason, $article->getId())); + if ( $article->doDeleteArticle( $reason ) ) { + wfRunHooks( 'ArticleDeleteComplete', array( &$article, &$wgUser, $reason, $article->getId() ) ); return array(); } - return array(array('cannotdelete', $article->mTitle->getPrefixedText())); + return array( array( 'cannotdelete', $article->mTitle->getPrefixedText() ) ); } - public static function deleteFile($token, &$title, $oldimage, &$reason = NULL, $suppress = false) - { - $errors = self::getPermissionsError($title, $token); - if (count($errors)) return $errors; + public static function deleteFile( $token, &$title, $oldimage, &$reason = null, $suppress = false ) { + $errors = self::getPermissionsError( $title, $token ); + if ( count( $errors ) ) { + return $errors; + } - if( $oldimage && !FileDeleteForm::isValidOldSpec($oldimage) ) - return array(array('invalidoldimage')); + if ( $oldimage && !FileDeleteForm::isValidOldSpec( $oldimage ) ) { + return array( array( 'invalidoldimage' ) ); + } - $file = wfFindFile($title, false, FileRepo::FIND_IGNORE_REDIRECT); + $file = wfFindFile( $title, array( 'ignoreRedirect' => true ) ); $oldfile = false; - - if( $oldimage ) + + if ( $oldimage ) { $oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $oldimage ); - - if( !FileDeleteForm::haveDeletableFile($file, $oldfile, $oldimage) ) - return array(array('nofile')); - if (is_null($reason)) # Log and RC don't like null reasons + } + + if ( !FileDeleteForm::haveDeletableFile( $file, $oldfile, $oldimage ) ) { + return self::delete( new Article( $title ), $token, $reason ); + } + 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())); - + + if ( !$status->isGood() ) { + return array( array( 'cannotdelete', $title->getPrefixedText() ) ); + } + return array(); } - - public function mustBePosted() { return true; } + + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; } public function getAllowedParams() { - return array ( + return array( 'title' => null, 'pageid' => array( ApiBase::PARAM_TYPE => 'integer' @@ -196,7 +204,7 @@ class ApiDelete extends ApiBase { } public function getParamDescription() { - return array ( + return array( 'title' => 'Title of the page you want to delete. Cannot be used together with pageid', 'pageid' => 'Page ID of the page you want to delete. Cannot be used together with title', 'token' => 'A delete token previously retrieved through prop=info', @@ -213,14 +221,27 @@ class ApiDelete extends ApiBase { ); } + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'invalidtitle', 'title' ), + array( 'nosuchpageid', 'pageid' ), + array( 'notanarticle' ), + array( 'hookaborted', 'error' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } + protected function getExamples() { - return array ( + return array( 'api.php?action=delete&title=Main%20Page&token=123ABC', 'api.php?action=delete&title=Main%20Page&token=123ABC&reason=Preparing%20for%20move' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiDelete.php 48122 2009-03-07 12:58:41Z catrope $'; + return __CLASS__ . ': $Id: ApiDelete.php 62703 2010-02-19 12:54:09Z ashley $'; } -} +} \ No newline at end of file diff --git a/includes/api/ApiDisabled.php b/includes/api/ApiDisabled.php index 9e0bf56e..60e0e7ee 100644 --- a/includes/api/ApiDisabled.php +++ b/includes/api/ApiDisabled.php @@ -1,10 +1,10 @@ .@home.nl + * Copyright © 2008 Roan Kattouw .@home.nl * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,12 +22,11 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once( "ApiBase.php" ); } - /** * API module that dies with an error immediately. * @@ -40,12 +39,12 @@ if (!defined('MEDIAWIKI')) { */ class ApiDisabled extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent::__construct( $main, $action ); } public function execute() { - $this->dieUsage("The ``{$this->getModuleName()}'' module has been disabled.", 'moduledisabled'); + $this->dieUsage( "The ``{$this->getModuleName()}'' module has been disabled.", 'moduledisabled' ); } public function isReadMode() { @@ -53,11 +52,11 @@ class ApiDisabled extends ApiBase { } public function getAllowedParams() { - return array (); + return array(); } public function getParamDescription() { - return array (); + return array(); } public function getDescription() { @@ -67,10 +66,10 @@ class ApiDisabled extends ApiBase { } protected function getExamples() { - return array (); + return array(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiDisabled.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiDisabled.php 62783 2010-02-21 18:09:00Z ashley $'; } } diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index d4a57b83..50a9836a 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -37,242 +37,295 @@ if (!defined('MEDIAWIKI')) { */ class ApiEditPage extends ApiBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName ); } public function execute() { global $wgUser; $params = $this->extractRequestParams(); - if(is_null($params['title'])) - $this->dieUsageMsg(array('missingparam', 'title')); - if(is_null($params['text']) && is_null($params['appendtext']) && - is_null($params['prependtext']) && - $params['undo'] == 0) - $this->dieUsageMsg(array('missingtext')); - if(is_null($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); - - $titleObj = Title::newFromText($params['title']); - if(!$titleObj) - $this->dieUsageMsg(array('invalidtitle', $params['title'])); + + if ( is_null( $params['title'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'title' ) ); + + if ( is_null( $params['text'] ) && is_null( $params['appendtext'] ) && + is_null( $params['prependtext'] ) && + $params['undo'] == 0 ) + $this->dieUsageMsg( array( 'missingtext' ) ); + + $titleObj = Title::newFromText( $params['title'] ); + if ( !$titleObj || $titleObj->isExternal() ) + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + // Some functions depend on $wgTitle == $ep->mTitle global $wgTitle; $wgTitle = $titleObj; - if($params['createonly'] && $titleObj->exists()) - $this->dieUsageMsg(array('createonly-exists')); - if($params['nocreate'] && !$titleObj->exists()) - $this->dieUsageMsg(array('nocreate-missing')); + if ( $params['createonly'] && $titleObj->exists() ) + $this->dieUsageMsg( array( 'createonly-exists' ) ); + if ( $params['nocreate'] && !$titleObj->exists() ) + $this->dieUsageMsg( array( 'nocreate-missing' ) ); // Now let's check whether we're even allowed to do this - $errors = $titleObj->getUserPermissionsErrors('edit', $wgUser); - if(!$titleObj->exists()) - $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $wgUser)); - if(count($errors)) - $this->dieUsageMsg($errors[0]); + $errors = $titleObj->getUserPermissionsErrors( 'edit', $wgUser ); + if ( !$titleObj->exists() ) + $errors = array_merge( $errors, $titleObj->getUserPermissionsErrors( 'create', $wgUser ) ); + if ( count( $errors ) ) + $this->dieUsageMsg( $errors[0] ); - $articleObj = new Article($titleObj); + $articleObj = new Article( $titleObj ); $toMD5 = $params['text']; - if(!is_null($params['appendtext']) || !is_null($params['prependtext'])) + if ( !is_null( $params['appendtext'] ) || !is_null( $params['prependtext'] ) ) { // For non-existent pages, Article::getContent() // returns an interface message rather than '' // We do want getContent()'s behavior for non-existent // MediaWiki: pages, though - if($articleObj->getID() == 0 && $titleObj->getNamespace() != NS_MEDIAWIKI) + if ( $articleObj->getID() == 0 && $titleObj->getNamespace() != NS_MEDIAWIKI ) $content = ''; else $content = $articleObj->getContent(); + + if ( !is_null( $params['section'] ) ) + { + // Process the content for section edits + global $wgParser; + $section = intval( $params['section'] ); + $content = $wgParser->getSection( $content, $section, false ); + if ( $content === false ) + $this->dieUsage( "There is no section {$section}.", 'nosuchsection' ); + } $params['text'] = $params['prependtext'] . $content . $params['appendtext']; $toMD5 = $params['prependtext'] . $params['appendtext']; } - if($params['undo'] > 0) + if ( $params['undo'] > 0 ) { - if($params['undoafter'] > 0) + if ( $params['undoafter'] > 0 ) { - if($params['undo'] < $params['undoafter']) - list($params['undo'], $params['undoafter']) = - array($params['undoafter'], $params['undo']); - $undoafterRev = Revision::newFromID($params['undoafter']); + if ( $params['undo'] < $params['undoafter'] ) + list( $params['undo'], $params['undoafter'] ) = + array( $params['undoafter'], $params['undo'] ); + $undoafterRev = Revision::newFromID( $params['undoafter'] ); } - $undoRev = Revision::newFromID($params['undo']); - if(is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) - $this->dieUsageMsg(array('nosuchrevid', $params['undo'])); - if($params['undoafter'] == 0) + $undoRev = Revision::newFromID( $params['undo'] ); + if ( is_null( $undoRev ) || $undoRev->isDeleted( Revision::DELETED_TEXT ) ) + $this->dieUsageMsg( array( 'nosuchrevid', $params['undo'] ) ); + + if ( $params['undoafter'] == 0 ) $undoafterRev = $undoRev->getPrevious(); - if(is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) - $this->dieUsageMsg(array('nosuchrevid', $params['undoafter'])); - if($undoRev->getPage() != $articleObj->getID()) - $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); - if($undoafterRev->getPage() != $articleObj->getID()) - $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); - $newtext = $articleObj->getUndoText($undoRev, $undoafterRev); - if($newtext === false) - $this->dieUsageMsg(array('undo-failure')); + if ( is_null( $undoafterRev ) || $undoafterRev->isDeleted( Revision::DELETED_TEXT ) ) + $this->dieUsageMsg( array( 'nosuchrevid', $params['undoafter'] ) ); + + if ( $undoRev->getPage() != $articleObj->getID() ) + $this->dieUsageMsg( array( 'revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText() ) ); + if ( $undoafterRev->getPage() != $articleObj->getID() ) + $this->dieUsageMsg( array( 'revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText() ) ); + + $newtext = $articleObj->getUndoText( $undoRev, $undoafterRev ); + if ( $newtext === false ) + $this->dieUsageMsg( array( 'undo-failure' ) ); $params['text'] = $newtext; // 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()); + if ( is_null( $params['summary'] ) && $titleObj->getNextRevisionID( $undoafterRev->getID() ) == $params['undo'] ) + $params['summary'] = wfMsgForContent( 'undo-summary', $params['undo'], $undoRev->getUserText() ); } - # See if the MD5 hash checks out - if(!is_null($params['md5'])) - if(md5($toMD5) !== $params['md5']) - $this->dieUsageMsg(array('hashcheckfailed')); + // See if the MD5 hash checks out + if ( !is_null( $params['md5'] ) && md5( $toMD5 ) !== $params['md5'] ) + $this->dieUsageMsg( array( 'hashcheckfailed' ) ); - $ep = new EditPage($articleObj); + $ep = new EditPage( $articleObj ); // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable - $reqArr = array('wpTextbox1' => $params['text'], - 'wpEdittoken' => $params['token'], + $reqArr = array( 'wpTextbox1' => $params['text'], + 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => '' ); - if(!is_null($params['summary'])) + + if ( !is_null( $params['summary'] ) ) $reqArr['wpSummary'] = $params['summary']; - # Watch out for basetimestamp == '' - # wfTimestamp() treats it as NOW, almost certainly causing an edit conflict - if(!is_null($params['basetimestamp']) && $params['basetimestamp'] != '') - $reqArr['wpEdittime'] = wfTimestamp(TS_MW, $params['basetimestamp']); + + // Watch out for basetimestamp == '' + // wfTimestamp() treats it as NOW, almost certainly causing an edit conflict + if ( !is_null( $params['basetimestamp'] ) && $params['basetimestamp'] != '' ) + $reqArr['wpEdittime'] = wfTimestamp( TS_MW, $params['basetimestamp'] ); else $reqArr['wpEdittime'] = $articleObj->getTimestamp(); - if(!is_null($params['starttimestamp']) && $params['starttimestamp'] != '') - $reqArr['wpStarttime'] = wfTimestamp(TS_MW, $params['starttimestamp']); + + if ( !is_null( $params['starttimestamp'] ) && $params['starttimestamp'] != '' ) + $reqArr['wpStarttime'] = wfTimestamp( TS_MW, $params['starttimestamp'] ); else - # Fake wpStartime - $reqArr['wpStarttime'] = $reqArr['wpEdittime']; - if($params['minor'] || (!$params['notminor'] && $wgUser->getOption('minordefault'))) + $reqArr['wpStarttime'] = $reqArr['wpEdittime']; // Fake wpStartime + + if ( $params['minor'] || ( !$params['notminor'] && $wgUser->getOption( 'minordefault' ) ) ) $reqArr['wpMinoredit'] = ''; - if($params['recreate']) + + if ( $params['recreate'] ) $reqArr['wpRecreate'] = ''; - if(!is_null($params['section'])) + + if ( !is_null( $params['section'] ) ) { - $section = intval($params['section']); - if($section == 0 && $params['section'] != '0' && $params['section'] != 'new') - $this->dieUsage("The section parameter must be set to an integer or 'new'", "invalidsection"); + $section = intval( $params['section'] ); + if ( $section == 0 && $params['section'] != '0' && $params['section'] != 'new' ) + $this->dieUsage( "The section parameter must be set to an integer or 'new'", "invalidsection" ); $reqArr['wpSection'] = $params['section']; } else $reqArr['wpSection'] = ''; - if($params['watch']) - $watch = true; - else if($params['unwatch']) - $watch = false; - else if($titleObj->userIsWatching()) - $watch = true; - else if($wgUser->getOption('watchdefault')) - $watch = true; - else if($wgUser->getOption('watchcreations') && !$titleObj->exists()) + // Handle watchlist settings + switch ( $params['watchlist'] ) + { + case 'watch': + $watch = true; + break; + case 'unwatch': + $watch = false; + break; + case 'preferences': + if ( $titleObj->exists() ) + $watch = $wgUser->getOption( 'watchdefault' ) || $titleObj->userIsWatching(); + else + $watch = $wgUser->getOption( 'watchcreations' ); + break; + case 'nochange': + default: + $watch = $titleObj->userIsWatching(); + } + // Deprecated parameters + if ( $params['watch'] ) $watch = true; - else + elseif ( $params['unwatch'] ) $watch = false; - if($watch) + + if ( $watch ) $reqArr['wpWatchthis'] = ''; - $req = new FauxRequest($reqArr, true); - $ep->importFormData($req); + $req = new FauxRequest( $reqArr, true ); + $ep->importFormData( $req ); - # Run hooks - # Handle CAPTCHA parameters + // Run hooks + // Handle CAPTCHA parameters global $wgRequest; - if(!is_null($params['captchaid'])) + if ( !is_null( $params['captchaid'] ) ) $wgRequest->setVal( 'wpCaptchaId', $params['captchaid'] ); - if(!is_null($params['captchaword'])) + if ( !is_null( $params['captchaword'] ) ) $wgRequest->setVal( 'wpCaptchaWord', $params['captchaword'] ); + $r = array(); - if(!wfRunHooks('APIEditBeforeSave', array(&$ep, $ep->textbox1, &$r))) + if ( !wfRunHooks( 'APIEditBeforeSave', array( $ep, $ep->textbox1, &$r ) ) ) { - if(count($r)) + if ( count( $r ) ) { $r['result'] = "Failure"; - $this->getResult()->addValue(null, $this->getModuleName(), $r); + $this->getResult()->addValue( null, $this->getModuleName(), $r ); return; } else - $this->dieUsageMsg(array('hookaborted')); + $this->dieUsageMsg( array( 'hookaborted' ) ); } - # Do the actual save + // Do the actual save $oldRevId = $articleObj->getRevIdFetched(); $result = null; - # Fake $wgRequest for some hooks inside EditPage - # FIXME: This interface SUCKS + // Fake $wgRequest for some hooks inside EditPage + // FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; - $retval = $ep->internalAttemptSave($result, $wgUser->isAllowed('bot') && $params['bot']); + $retval = $ep->internalAttemptSave( $result, $wgUser->isAllowed( 'bot' ) && $params['bot'] ); $wgRequest = $oldRequest; - switch($retval) + switch( $retval ) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: - $this->dieUsageMsg(array('hookaborted')); + $this->dieUsageMsg( array( 'hookaborted' ) ); + case EditPage::AS_IMAGE_REDIRECT_ANON: - $this->dieUsageMsg(array('noimageredirect-anon')); + $this->dieUsageMsg( array( 'noimageredirect-anon' ) ); + case EditPage::AS_IMAGE_REDIRECT_LOGGED: - $this->dieUsageMsg(array('noimageredirect-logged')); + $this->dieUsageMsg( array( 'noimageredirect-logged' ) ); + case EditPage::AS_SPAM_ERROR: - $this->dieUsageMsg(array('spamdetected', $result['spam'])); + $this->dieUsageMsg( array( 'spamdetected', $result['spam'] ) ); + case EditPage::AS_FILTERING: - $this->dieUsageMsg(array('filtered')); + $this->dieUsageMsg( array( 'filtered' ) ); + case EditPage::AS_BLOCKED_PAGE_FOR_USER: - $this->dieUsageMsg(array('blockedtext')); + $this->dieUsageMsg( array( 'blockedtext' ) ); + case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: case EditPage::AS_CONTENT_TOO_BIG: global $wgMaxArticleSize; - $this->dieUsageMsg(array('contenttoobig', $wgMaxArticleSize)); + $this->dieUsageMsg( array( 'contenttoobig', $wgMaxArticleSize ) ); + case EditPage::AS_READ_ONLY_PAGE_ANON: - $this->dieUsageMsg(array('noedit-anon')); + $this->dieUsageMsg( array( 'noedit-anon' ) ); + case EditPage::AS_READ_ONLY_PAGE_LOGGED: - $this->dieUsageMsg(array('noedit')); + $this->dieUsageMsg( array( 'noedit' ) ); + case EditPage::AS_READ_ONLY_PAGE: - $this->dieUsageMsg(array('readonlytext')); + $this->dieReadOnly(); + case EditPage::AS_RATE_LIMITED: - $this->dieUsageMsg(array('actionthrottledtext')); + $this->dieUsageMsg( array( 'actionthrottledtext' ) ); + case EditPage::AS_ARTICLE_WAS_DELETED: - $this->dieUsageMsg(array('wasdeleted')); + $this->dieUsageMsg( array( 'wasdeleted' ) ); + case EditPage::AS_NO_CREATE_PERMISSION: - $this->dieUsageMsg(array('nocreate-loggedin')); + $this->dieUsageMsg( array( 'nocreate-loggedin' ) ); + case EditPage::AS_BLANK_ARTICLE: - $this->dieUsageMsg(array('blankpage')); + $this->dieUsageMsg( array( 'blankpage' ) ); + case EditPage::AS_CONFLICT_DETECTED: - $this->dieUsageMsg(array('editconflict')); - #case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary + $this->dieUsageMsg( array( 'editconflict' ) ); + + // case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary case EditPage::AS_TEXTBOX_EMPTY: - $this->dieUsageMsg(array('emptynewsection')); - case EditPage::AS_END: - # This usually means some kind of race condition - # or DB weirdness occurred. Throw an unknown error here. - $this->dieUsageMsg(array('unknownerror')); + $this->dieUsageMsg( array( 'emptynewsection' ) ); + case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = ''; case EditPage::AS_SUCCESS_UPDATE: $r['result'] = "Success"; - $r['pageid'] = intval($titleObj->getArticleID()); + $r['pageid'] = intval( $titleObj->getArticleID() ); $r['title'] = $titleObj->getPrefixedText(); - # HACK: We create a new Article object here because getRevIdFetched() - # refuses to be run twice, and because Title::getLatestRevId() - # won't fetch from the master unless we select for update, which we - # don't want to do. - $newArticle = new Article($titleObj); + // HACK: We create a new Article object here because getRevIdFetched() + // refuses to be run twice, and because Title::getLatestRevId() + // won't fetch from the master unless we select for update, which we + // don't want to do. + $newArticle = new Article( $titleObj ); $newRevId = $newArticle->getRevIdFetched(); - if($newRevId == $oldRevId) + if ( $newRevId == $oldRevId ) $r['nochange'] = ''; else { - $r['oldrevid'] = intval($oldRevId); - $r['newrevid'] = intval($newRevId); + $r['oldrevid'] = intval( $oldRevId ); + $r['newrevid'] = intval( $newRevId ); + $r['newtimestamp'] = wfTimestamp( TS_ISO_8601, + $newArticle->getTimestamp() ); } break; + + case EditPage::AS_END: + // This usually means some kind of race condition + // or DB weirdness occurred. Fall through to throw an unknown + // error. + + // This needs fixing higher up, as Article::doEdit should be + // used rather than Article::updateArticle, so that specific + // error conditions can be returned default: - $this->dieUsageMsg(array('unknownerror', $retval)); + $this->dieUsageMsg( array( 'unknownerror', $retval ) ); } - $this->getResult()->addValue(null, $this->getModuleName(), $r); + $this->getResult()->addValue( null, $this->getModuleName(), $r ); } public function mustBePosted() { @@ -286,6 +339,41 @@ class ApiEditPage extends ApiBase { protected function getDescription() { return 'Create and edit pages.'; } + + public function getPossibleErrors() { + global $wgMaxArticleSize; + + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'title' ), + 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( '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\'' ), + ) ); + } protected function getAllowedParams() { return array ( @@ -304,8 +392,23 @@ class ApiEditPage extends ApiBase { 'nocreate' => false, 'captchaword' => null, 'captchaid' => null, - 'watch' => false, - 'unwatch' => false, + 'watch' => array( + ApiBase :: PARAM_DFLT => false, + ApiBase :: PARAM_DEPRECATED => true, + ), + 'unwatch' => array( + ApiBase :: PARAM_DFLT => false, + ApiBase :: PARAM_DEPRECATED => true, + ), + 'watchlist' => array( + ApiBase :: PARAM_DFLT => 'preferences', + ApiBase :: PARAM_TYPE => array( + 'watch', + 'unwatch', + 'preferences', + 'nochange' + ), + ), 'md5' => null, 'prependtext' => null, 'appendtext' => null, @@ -328,10 +431,10 @@ class ApiEditPage extends ApiBase { 'minor' => 'Minor edit', 'notminor' => 'Non-minor edit', 'bot' => 'Mark this edit as bot', - 'basetimestamp' => array('Timestamp of the base revision (gotten through prop=revisions&rvprop=timestamp).', + 'basetimestamp' => array( 'Timestamp of the base revision (gotten through prop=revisions&rvprop=timestamp).', 'Used to detect edit conflicts; leave unset to ignore conflicts.' ), - 'starttimestamp' => array('Timestamp when you obtained the edit token.', + 'starttimestamp' => array( 'Timestamp when you obtained the edit token.', 'Used to detect edit conflicts; leave unset to ignore conflicts.' ), 'recreate' => 'Override any errors about the article having been deleted in the meantime', @@ -339,17 +442,21 @@ class ApiEditPage extends ApiBase { 'nocreate' => 'Throw an error if the page doesn\'t exist', 'watch' => 'Add the page to your watchlist', 'unwatch' => 'Remove the page from your watchlist', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', 'captchaid' => 'CAPTCHA ID from previous request', 'captchaword' => 'Answer to the CAPTCHA', 'md5' => array( 'The MD5 hash of the text parameter, or the prependtext and appendtext parameters concatenated.', - 'If set, the edit won\'t be done unless the hash is correct'), - 'prependtext' => array( 'Add this text to the beginning of the page. Overrides text.', - 'Don\'t use together with section: that won\'t do what you expect.'), + 'If set, the edit won\'t be done unless the hash is correct' ), + 'prependtext' => 'Add this text to the beginning of the page. Overrides text.', 'appendtext' => 'Add this text to the end of the page. Overrides text', 'undo' => 'Undo this revision. Overrides text, prependtext and appendtext', 'undoafter' => 'Undo all revisions from undo to this one. If not set, just undo one revision', ); } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { return array ( @@ -363,6 +470,6 @@ class ApiEditPage extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiEditPage.php 50220 2009-05-05 14:07:59Z tstarling $'; + return __CLASS__ . ': $Id: ApiEditPage.php 62600 2010-02-16 22:01:38Z reedy $'; } } diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php index 9bb504fb..912480ef 100644 --- a/includes/api/ApiEmailUser.php +++ b/includes/api/ApiEmailUser.php @@ -22,19 +22,18 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } - /** * @ingroup API */ class ApiEmailUser extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { @@ -49,8 +48,6 @@ class ApiEmailUser extends ApiBase { $this->dieUsageMsg( array( 'missingparam', 'target' ) ); if ( !isset( $params['text'] ) ) $this->dieUsageMsg( array( 'missingparam', 'text' ) ); - if ( !isset( $params['token'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'token' ) ); // Validate target $targetUser = EmailUserForm::validateEmailTarget( $params['target'] ); @@ -61,8 +58,7 @@ class ApiEmailUser extends ApiBase { $error = EmailUserForm::getPermissionsError( $wgUser, $params['token'] ); if ( $error ) $this->dieUsageMsg( array( $error ) ); - - + $form = new EmailUserForm( $targetUser, $params['text'], $params['subject'], $params['ccme'] ); $retval = $form->doSubmit(); if ( is_null( $retval ) ) @@ -74,7 +70,9 @@ class ApiEmailUser extends ApiBase { $this->getResult()->addValue( null, $this->getModuleName(), $result ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; @@ -105,6 +103,18 @@ class ApiEmailUser extends ApiBase { 'Email a user.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'usermaildisabled' ), + array( 'missingparam', 'target' ), + array( 'missingparam', 'text' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { return array ( @@ -113,7 +123,7 @@ class ApiEmailUser extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiEmailUser.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiEmailUser.php 62599 2010-02-16 21:59:16Z reedy $'; } -} +} \ No newline at end of file diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php index afb11402..d0c00db7 100644 --- a/includes/api/ApiExpandTemplates.php +++ b/includes/api/ApiExpandTemplates.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -37,8 +37,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiExpandTemplates extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { @@ -48,9 +48,9 @@ class ApiExpandTemplates extends ApiBase { // Get parameters $params = $this->extractRequestParams(); - //Create title for parser + // Create title for parser $title_obj = Title :: newFromText( $params['title'] ); - if(!$title_obj) + if ( !$title_obj ) $title_obj = Title :: newFromText( "API" ); // default $result = $this->getResult(); @@ -58,6 +58,7 @@ class ApiExpandTemplates extends ApiBase { // Parse text global $wgParser; $options = new ParserOptions(); + if ( $params['generatexml'] ) { $wgParser->startExternalParse( $title_obj, $options, OT_PREPROCESS ); @@ -69,7 +70,7 @@ class ApiExpandTemplates extends ApiBase { } $xml_result = array(); $result->setContent( $xml_result, $xml ); - $result->addValue( null, 'parsetree', $xml_result); + $result->addValue( null, 'parsetree', $xml_result ); } $retval = $wgParser->preprocess( $params['text'], $title_obj, $options ); @@ -108,6 +109,6 @@ class ApiExpandTemplates extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiExpandTemplates.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiExpandTemplates.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php index 0859232e..03d12800 100644 --- a/includes/api/ApiFeedWatchlist.php +++ b/includes/api/ApiFeedWatchlist.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -37,15 +37,15 @@ if (!defined('MEDIAWIKI')) { */ class ApiFeedWatchlist extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } /** * This module uses a custom feed wrapper printer. */ public function getCustomPrinter() { - return new ApiFormatFeedWrapper($this->getMain()); + return new ApiFormatFeedWrapper( $this->getMain() ); } /** @@ -60,7 +60,7 @@ class ApiFeedWatchlist extends ApiBase { $params = $this->extractRequestParams(); // limit to the number of hours going from now back - $endTime = wfTimestamp(TS_MW, time() - intval($params['hours'] * 60 * 60)); + $endTime = wfTimestamp( TS_MW, time() - intval( $params['hours'] * 60 * 60 ) ); $dbr = wfGetDB( DB_SLAVE ); // Prepare parameters for nested request @@ -71,48 +71,56 @@ class ApiFeedWatchlist extends ApiBase { 'list' => 'watchlist', 'wlprop' => 'title|user|comment|timestamp', 'wldir' => 'older', // reverse order - from newest to oldest - 'wlend' => $dbr->timestamp($endTime), // stop at this time - 'wllimit' => (50 > $wgFeedLimit) ? $wgFeedLimit : 50 + 'wlend' => $dbr->timestamp( $endTime ), // stop at this time + 'wllimit' => ( 50 > $wgFeedLimit ) ? $wgFeedLimit : 50 ); + if ( !is_null( $params['wlowner'] ) ) { + $fauxReqArr['wlowner'] = $params['wlowner']; + } + if ( !is_null( $params['wltoken'] ) ) { + $fauxReqArr['wltoken'] = $params['wltoken']; + } + // Check for 'allrev' parameter, and if found, show all revisions to each page on wl. - if ( ! is_null ( $params['allrev'] ) ) $fauxReqArr['wlallrev'] = ''; + if ( !is_null ( $params['allrev'] ) ) { + $fauxReqArr['wlallrev'] = ''; + } // Create the request $fauxReq = new FauxRequest ( $fauxReqArr ); // Execute - $module = new ApiMain($fauxReq); + $module = new ApiMain( $fauxReq ); $module->execute(); // Get data array $data = $module->getResultData(); - $feedItems = array (); - foreach ((array)$data['query']['watchlist'] as $info) { - $feedItems[] = $this->createFeedItem($info); + $feedItems = array(); + foreach ( (array)$data['query']['watchlist'] as $info ) { + $feedItems[] = $this->createFeedItem( $info ); } - $feedTitle = $wgSitename . ' - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']'; + $feedTitle = $wgSitename . ' - ' . wfMsgForContent( 'watchlist' ) . ' [' . $wgContLanguageCode . ']'; $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl(); - $feed = new $wgFeedClasses[$params['feedformat']] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl); + $feed = new $wgFeedClasses[$params['feedformat']] ( $feedTitle, htmlspecialchars( wfMsgForContent( 'watchlist' ) ), $feedUrl ); - ApiFormatFeedWrapper :: setResult($this->getResult(), $feed, $feedItems); + ApiFormatFeedWrapper :: setResult( $this->getResult(), $feed, $feedItems ); - } catch (Exception $e) { + } catch ( Exception $e ) { // Error results should not be cached - $this->getMain()->setCacheMaxAge(0); + $this->getMain()->setCacheMaxAge( 0 ); - $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']'; + $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent( 'watchlist' ) . ' [' . $wgContLanguageCode . ']'; $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl(); - $feedFormat = isset($params['feedformat']) ? $params['feedformat'] : 'rss'; - $feed = new $wgFeedClasses[$feedFormat] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl); - + $feedFormat = isset( $params['feedformat'] ) ? $params['feedformat'] : 'rss'; + $feed = new $wgFeedClasses[$feedFormat] ( $feedTitle, htmlspecialchars( wfMsgForContent( 'watchlist' ) ), $feedUrl ); - if ($e instanceof UsageException) { + if ( $e instanceof UsageException ) { $errorCode = $e->getCodeString(); } else { // Something is seriously wrong @@ -120,14 +128,14 @@ class ApiFeedWatchlist extends ApiBase { } $errorText = $e->getMessage(); - $feedItems[] = new FeedItem("Error ($errorCode)", $errorText, "", "", ""); - ApiFormatFeedWrapper :: setResult($this->getResult(), $feed, $feedItems); + $feedItems[] = new FeedItem( "Error ($errorCode)", $errorText, "", "", "" ); + ApiFormatFeedWrapper :: setResult( $this->getResult(), $feed, $feedItems ); } } - private function createFeedItem($info) { + private function createFeedItem( $info ) { $titleStr = $info['title']; - $title = Title :: newFromText($titleStr); + $title = Title :: newFromText( $titleStr ); $titleUrl = $title->getFullUrl(); $comment = isset( $info['comment'] ) ? $info['comment'] : null; $timestamp = $info['timestamp']; @@ -135,12 +143,12 @@ class ApiFeedWatchlist extends ApiBase { $completeText = "$comment ($user)"; - return new FeedItem($titleStr, $completeText, $titleUrl, $timestamp, $user); + return new FeedItem( $titleStr, $completeText, $titleUrl, $timestamp, $user ); } public function getAllowedParams() { global $wgFeedClasses; - $feedFormatNames = array_keys($wgFeedClasses); + $feedFormatNames = array_keys( $wgFeedClasses ); return array ( 'feedformat' => array ( ApiBase :: PARAM_DFLT => 'rss', @@ -152,7 +160,13 @@ class ApiFeedWatchlist extends ApiBase { ApiBase :: PARAM_MIN => 1, ApiBase :: PARAM_MAX => 72, ), - 'allrev' => null + 'allrev' => null, + 'wlowner' => array ( + ApiBase :: PARAM_TYPE => 'user' + ), + 'wltoken' => array ( + ApiBase :: PARAM_TYPE => 'string' + ) ); } @@ -160,7 +174,9 @@ class ApiFeedWatchlist extends ApiBase { return array ( 'feedformat' => 'The format of the feed', 'hours' => 'List pages modified within this many hours from now', - 'allrev' => 'Include multiple revisions of the same page within given timeframe.' + 'allrev' => 'Include multiple revisions of the same page within given timeframe.', + 'wlowner' => "The user whose watchlist you want (must be accompanied by wltoken if it's not you)", + 'wltoken' => 'Security token that requested user set in their preferences' ); } @@ -175,6 +191,6 @@ class ApiFeedWatchlist extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFeedWatchlist.php 46848 2009-02-05 15:31:06Z catrope $'; + return __CLASS__ . ': $Id: ApiFeedWatchlist.php 69357 2010-07-14 22:39:23Z mah $'; } } diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php index cc7434c6..de211fe9 100644 --- a/includes/api/ApiFormatBase.php +++ b/includes/api/ApiFormatBase.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -36,6 +36,7 @@ if (!defined('MEDIAWIKI')) { abstract class ApiFormatBase extends ApiBase { private $mIsHtml, $mFormat, $mUnescapeAmps, $mHelp, $mCleared; + private $mBufferResult = false, $mBuffer; /** * Constructor @@ -43,15 +44,15 @@ abstract class ApiFormatBase extends ApiBase { * @param $main ApiMain * @param $format string Format name */ - public function __construct($main, $format) { - parent :: __construct($main, $format); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); - $this->mIsHtml = (substr($format, -2, 2) === 'fm'); // ends with 'fm' - if ($this->mIsHtml) - $this->mFormat = substr($format, 0, -2); // remove ending 'fm' + $this->mIsHtml = ( substr( $format, - 2, 2 ) === 'fm' ); // ends with 'fm' + if ( $this->mIsHtml ) + $this->mFormat = substr( $format, 0, - 2 ); // remove ending 'fm' else $this->mFormat = $format; - $this->mFormat = strtoupper($this->mFormat); + $this->mFormat = strtoupper( $this->mFormat ); $this->mCleared = false; } @@ -101,30 +102,40 @@ abstract class ApiFormatBase extends ApiBase { return $this->mIsHtml; } + /** + * Whether this formatter can format the help message in a nice way. + * By default, this returns the same as getIsHtml(). + * When action=help is set explicitly, the help will always be shown + * @return bool + */ + public function getWantsHelp() { + return $this->getIsHtml(); + } + /** * Initialize the printer function and prepare the output headers, etc. * This method must be the first outputing method during execution. * A help screen's header is printed for the HTML-based output * @param $isError bool Whether an error message is printed */ - function initPrinter($isError) { + function initPrinter( $isError ) { $isHtml = $this->getIsHtml(); $mime = $isHtml ? 'text/html' : $this->getMimeType(); $script = wfScript( 'api' ); // Some printers (ex. Feed) do their own header settings, // in which case $mime will be set to null - if (is_null($mime)) + if ( is_null( $mime ) ) return; // skip any initialization - header("Content-Type: $mime; charset=utf-8"); + header( "Content-Type: $mime; charset=utf-8" ); - if ($isHtml) { + if ( $isHtml ) { ?> -mUnescapeAmps) { +mUnescapeAmps ) { ?> MediaWiki API MediaWiki API Result @@ -134,12 +145,12 @@ abstract class ApiFormatBase extends ApiBase { -
+
-You are looking at the HTML representation of the mFormat ); ?> format.
-HTML is good for debugging, but probably is not suitable for your application.
+You are looking at the HTML representation of the mFormat ); ?> format.
+HTML is good for debugging, but probably is not suitable for your application.
See complete documentation, or API help for more information.
@@ -159,7 +170,7 @@ See complete documentation, or * Finish printing. Closes HTML tags. */ public function closePrinter() { - if ($this->getIsHtml()) { + if ( $this->getIsHtml() ) { ?> @@ -177,15 +188,16 @@ See complete documentation, or * when format name ends in 'fm'. * @param $text string */ - public function printText($text) { - if ($this->getIsHtml()) - echo $this->formatHTML($text); - else - { + public function printText( $text ) { + if ( $this->mBufferResult ) { + $this->mBuffer = $text; + } elseif ( $this->getIsHtml() ) { + echo $this->formatHTML( $text ); + } else { // For non-HTML output, clear all errors that might have been // displayed if display_errors=On // Do this only once, of course - if(!$this->mCleared) + if ( !$this->mCleared ) { ob_clean(); $this->mCleared = true; @@ -194,6 +206,19 @@ See complete documentation, or } } + /** + * Get the contents of the buffer. + */ + public function getBuffer() { + return $this->mBuffer; + } + /** + * Set the flag to buffer the result instead of printing it. + */ + public function setBufferResult( $value ) { + $this->mBufferResult = $value; + } + /** * Sets whether the pretty-printer should format *bold* and $italics$ * @param $help bool @@ -208,25 +233,25 @@ See complete documentation, or * @param $text string * @return string */ - protected function formatHTML($text) { + protected function formatHTML( $text ) { global $wgUrlProtocols; - + // Escape everything first for full coverage - $text = htmlspecialchars($text); + $text = htmlspecialchars( $text ); // encode all comments or tags as safe blue strings - $text = preg_replace('/\<(!--.*?--|.*?)\>/', '<\1>', $text); + $text = preg_replace( '/\<(!--.*?--|.*?)\>/', '<\1>', $text ); // identify URLs - $protos = implode("|", $wgUrlProtocols); - # This regex hacks around bug 13218 (" included in the URL) - $text = preg_replace("#(($protos).*?)(")?([ \\'\"<>\n]|<|>|")#", '\\1\\3\\4', $text); + $protos = implode( "|", $wgUrlProtocols ); + // This regex hacks around bug 13218 (" included in the URL) + $text = preg_replace( "#(($protos).*?)(")?([ \\'\"<>\n]|<|>|")#", '\\1\\3\\4', $text ); // identify requests to api.php - $text = preg_replace("#api\\.php\\?[^ \\()<\n\t]+#", '\\0', $text); - if( $this->mHelp ) { + $text = preg_replace( "#api\\.php\\?[^ \\()<\n\t]+#", '\\0', $text ); + if ( $this->mHelp ) { // make strings inside * bold - $text = preg_replace("#\\*[^<>\n]+\\*#", '\\0', $text); + $text = preg_replace( "#\\*[^<>\n]+\\*#", '\\0', $text ); // make strings inside $ italic - $text = preg_replace("#\\$[^<>\n]+\\$#", '\\0', $text); + $text = preg_replace( "#\\$[^<>\n]+\\$#", '\\0', $text ); } /* Temporary fix for bad links in help messages. As a special case, @@ -248,7 +273,7 @@ See complete documentation, or } public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiFormatBase.php 48521 2009-03-18 19:25:29Z ialex $'; + return __CLASS__ . ': $Id: ApiFormatBase.php 62367 2010-02-12 14:09:42Z siebrand $'; } } @@ -258,8 +283,8 @@ See complete documentation, or */ class ApiFormatFeedWrapper extends ApiFormatBase { - public function __construct($main) { - parent :: __construct($main, 'feed'); + public function __construct( $main ) { + parent :: __construct( $main, 'feed' ); } /** @@ -268,15 +293,15 @@ class ApiFormatFeedWrapper extends ApiFormatBase { * @param $feed object an instance of one of the $wgFeedClasses classes * @param $feedItems array of FeedItem objects */ - public static function setResult($result, $feed, $feedItems) { + 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 // Disable size checking for this because we can't continue // cleanly; size checking would cause more problems than it'd // solve $result->disableSizeCheck(); - $result->addValue(null, '_feed', $feed); - $result->addValue(null, '_feeditems', $feedItems); + $result->addValue( null, '_feed', $feed ); + $result->addValue( null, '_feeditems', $feedItems ); $result->enableSizeCheck(); } @@ -301,13 +326,13 @@ class ApiFormatFeedWrapper extends ApiFormatBase { */ public function execute() { $data = $this->getResultData(); - if (isset ($data['_feed']) && isset ($data['_feeditems'])) { + if ( isset ( $data['_feed'] ) && isset ( $data['_feeditems'] ) ) { $feed = $data['_feed']; $items = $data['_feeditems']; $feed->outHeader(); - foreach ($items as & $item) - $feed->outItem($item); + foreach ( $items as & $item ) + $feed->outItem( $item ); $feed->outFooter(); } else { // Error has occured, print something useful @@ -316,6 +341,6 @@ class ApiFormatFeedWrapper extends ApiFormatBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatBase.php 48521 2009-03-18 19:25:29Z ialex $'; + return __CLASS__ . ': $Id: ApiFormatBase.php 62367 2010-02-12 14:09:42Z siebrand $'; } } \ No newline at end of file diff --git a/includes/api/ApiFormatDbg.php b/includes/api/ApiFormatDbg.php index 254c140b..26afd329 100644 --- a/includes/api/ApiFormatDbg.php +++ b/includes/api/ApiFormatDbg.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -33,19 +33,19 @@ if (!defined('MEDIAWIKI')) { */ class ApiFormatDbg extends ApiFormatBase { - public function __construct($main, $format) { - parent :: __construct($main, $format); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); } public function getMimeType() { - # This looks like it should be text/plain, but IE7 is so - # brain-damaged it tries to parse text/plain as HTML if it - # contains HTML tags. Using MIME text/text works around this bug + // This looks like it should be text/plain, but IE7 is so + // brain-damaged it tries to parse text/plain as HTML if it + // contains HTML tags. Using MIME text/text works around this bug return 'text/text'; } public function execute() { - $this->printText(var_export($this->getResultData(), true)); + $this->printText( var_export( $this->getResultData(), true ) ); } public function getDescription() { @@ -53,6 +53,6 @@ class ApiFormatDbg extends ApiFormatBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatDbg.php 35098 2008-05-20 17:13:28Z ialex $'; + return __CLASS__ . ': $Id: ApiFormatDbg.php 61444 2010-01-23 22:52:40Z reedy $'; } } diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php index 7b5a02a4..69686bfb 100644 --- a/includes/api/ApiFormatJson.php +++ b/includes/api/ApiFormatJson.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -35,12 +35,17 @@ class ApiFormatJson extends ApiFormatBase { private $mIsRaw; - public function __construct($main, $format) { - parent :: __construct($main, $format); - $this->mIsRaw = ($format === 'rawfm'); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); + $this->mIsRaw = ( $format === 'rawfm' ); } public function getMimeType() { + $params = $this->extractRequestParams(); + // callback: + if ( $params['callback'] ) { + return 'text/javascript'; + } return 'application/json'; } @@ -48,30 +53,29 @@ class ApiFormatJson extends ApiFormatBase { return $this->mIsRaw; } + public function getWantsHelp() { + // Help is always ugly in JSON + return false; + } + public function execute() { $prefix = $suffix = ""; $params = $this->extractRequestParams(); $callback = $params['callback']; - if(!is_null($callback)) { - $prefix = preg_replace("/[^][.\\'\\\"_A-Za-z0-9]/", "", $callback ) . "("; + if ( !is_null( $callback ) ) { + $prefix = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", "", $callback ) . "("; $suffix = ")"; } - - // Some versions of PHP have a broken json_encode, see PHP bug - // 46944. Test encoding an affected character (U+20000) to - // avoid this. - if (!function_exists('json_encode') || $this->getIsHtml() || strtolower(json_encode("\xf0\xa0\x80\x80")) != '"\ud840\udc00"') { - $json = new Services_JSON(); - $this->printText($prefix . $json->encode($this->getResultData(), $this->getIsHtml()) . $suffix); - } else { - $this->printText($prefix . json_encode($this->getResultData()) . $suffix); - } + $this->printText( + $prefix . + FormatJson::encode( $this->getResultData(), $this->getIsHtml() ) . + $suffix ); } public function getAllowedParams() { return array ( - 'callback' => null + 'callback' => null, ); } @@ -82,13 +86,13 @@ class ApiFormatJson extends ApiFormatBase { } public function getDescription() { - if ($this->mIsRaw) + if ( $this->mIsRaw ) return 'Output data with the debuging elements in JSON format' . parent :: getDescription(); else return 'Output data in JSON format' . parent :: getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatJson.php 48713 2009-03-23 19:58:07Z catrope $'; + return __CLASS__ . ': $Id: ApiFormatJson.php 62354 2010-02-12 06:44:16Z mah $'; } } diff --git a/includes/api/ApiFormatJson_json.php b/includes/api/ApiFormatJson_json.php deleted file mode 100644 index 8cb3606d..00000000 --- a/includes/api/ApiFormatJson_json.php +++ /dev/null @@ -1,861 +0,0 @@ - -* @author Matt Knapp -* @author Brett Stimmerman -* @copyright 2005 Michal Migurski -* @version CVS: $Id: ApiFormatJson_json.php 45765 2009-01-15 10:18:44Z catrope $ -* @license http://www.opensource.org/licenses/bsd-license.php -* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198 -*/ - -/** -* Marker constant for Services_JSON::decode(), used to flag stack state -*/ -define('SERVICES_JSON_SLICE', 1); - -/** -* Marker constant for Services_JSON::decode(), used to flag stack state -*/ -define('SERVICES_JSON_IN_STR', 2); - -/** -* Marker constant for Services_JSON::decode(), used to flag stack state -*/ -define('SERVICES_JSON_IN_ARR', 3); - -/** -* Marker constant for Services_JSON::decode(), used to flag stack state -*/ -define('SERVICES_JSON_IN_OBJ', 4); - -/** -* Marker constant for Services_JSON::decode(), used to flag stack state -*/ -define('SERVICES_JSON_IN_CMT', 5); - -/** -* Behavior switch for Services_JSON::decode() -*/ -define('SERVICES_JSON_LOOSE_TYPE', 16); - -/** -* Behavior switch for Services_JSON::decode() -*/ -define('SERVICES_JSON_SUPPRESS_ERRORS', 32); - -/** - * Converts to and from JSON format. - * - * Brief example of use: - * - * - * // create a new instance of Services_JSON - * $json = new Services_JSON(); - * - * // convert a complexe value to JSON notation, and send it to the browser - * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); - * $output = $json->encode($value); - * - * print($output); - * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] - * - * // accept incoming POST data, assumed to be in JSON notation - * $input = file_get_contents('php://input', 1000000); - * $value = $json->decode($input); - * - * - * @ingroup API - */ -class Services_JSON -{ - /** - * constructs a new JSON instance - * - * @param int $use object behavior flags; combine with boolean-OR - * - * possible values: - * - SERVICES_JSON_LOOSE_TYPE: loose typing. - * "{...}" syntax creates associative arrays - * instead of objects in decode(). - * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. - * Values which can't be encoded (e.g. resources) - * appear as NULL instead of throwing errors. - * By default, a deeply-nested resource will - * bubble up with an error, so all return values - * from encode() should be checked with isError() - */ - function Services_JSON($use = 0) - { - $this->use = $use; - } - - /** - * convert a string from one UTF-16 char to one UTF-8 char - * - * Normally should be handled by mb_convert_encoding, but - * provides a slower PHP-only method for installations - * that lack the multibye string extension. - * - * @param string $utf16 UTF-16 character - * @return string UTF-8 character - * @access private - */ - function utf162utf8($utf16) - { - // oh please oh please oh please oh please oh please - if(function_exists('mb_convert_encoding')) { - return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); - } - - $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); - - switch(true) { - case ((0x7F & $bytes) == $bytes): - // this case should never be reached, because we are in ASCII range - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0x7F & $bytes); - - case (0x07FF & $bytes) == $bytes: - // return a 2-byte UTF-8 character - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0xC0 | (($bytes >> 6) & 0x1F)) - . chr(0x80 | ($bytes & 0x3F)); - - case (0xFC00 & $bytes) == 0xD800 && strlen($utf16) >= 4 && (0xFC & ord($utf16{2})) == 0xDC: - // return a 4-byte UTF-8 character - $char = ((($bytes & 0x03FF) << 10) - | ((ord($utf16{2}) & 0x03) << 8) - | ord($utf16{3})); - $char += 0x10000; - return chr(0xF0 | (($char >> 18) & 0x07)) - . chr(0x80 | (($char >> 12) & 0x3F)) - . chr(0x80 | (($char >> 6) & 0x3F)) - . chr(0x80 | ($char & 0x3F)); - - case (0xFFFF & $bytes) == $bytes: - // return a 3-byte UTF-8 character - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0xE0 | (($bytes >> 12) & 0x0F)) - . chr(0x80 | (($bytes >> 6) & 0x3F)) - . chr(0x80 | ($bytes & 0x3F)); - } - - // ignoring UTF-32 for now, sorry - return ''; - } - - /** - * convert a string from one UTF-8 char to one UTF-16 char - * - * Normally should be handled by mb_convert_encoding, but - * provides a slower PHP-only method for installations - * that lack the multibye string extension. - * - * @param string $utf8 UTF-8 character - * @return string UTF-16 character - * @access private - */ - function utf82utf16($utf8) - { - // oh please oh please oh please oh please oh please - if(function_exists('mb_convert_encoding')) { - return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); - } - - switch(strlen($utf8)) { - case 1: - // this case should never be reached, because we are in ASCII range - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return $utf8; - - case 2: - // return a UTF-16 character from a 2-byte UTF-8 char - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0x07 & (ord($utf8{0}) >> 2)) - . chr((0xC0 & (ord($utf8{0}) << 6)) - | (0x3F & ord($utf8{1}))); - - case 3: - // return a UTF-16 character from a 3-byte UTF-8 char - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr((0xF0 & (ord($utf8{0}) << 4)) - | (0x0F & (ord($utf8{1}) >> 2))) - . chr((0xC0 & (ord($utf8{1}) << 6)) - | (0x7F & ord($utf8{2}))); - - case 4: - // return a UTF-16 surrogate pair from a 4-byte UTF-8 char - if(ord($utf8{0}) > 0xF4) return ''; # invalid - $char = ((0x1C0000 & (ord($utf8{0}) << 18)) - | (0x03F000 & (ord($utf8{1}) << 12)) - | (0x000FC0 & (ord($utf8{2}) << 6)) - | (0x00003F & ord($utf8{3}))); - if($char > 0x10FFFF) return ''; # invalid - $char -= 0x10000; - return chr(0xD8 | (($char >> 18) & 0x03)) - . chr(($char >> 10) & 0xFF) - . chr(0xDC | (($char >> 8) & 0x03)) - . chr($char & 0xFF); - } - - // ignoring UTF-32 for now, sorry - return ''; - } - - /** - * encodes an arbitrary variable into JSON format - * - * @param mixed $var any number, boolean, string, array, or object to be encoded. - * see argument 1 to Services_JSON() above for array-parsing behavior. - * if var is a strng, note that encode() always expects it - * to be in ASCII or UTF-8 format! - * @param bool $pretty pretty-print output with indents and newlines - * - * @return mixed JSON string representation of input var or an error if a problem occurs - * @access public - */ - function encode($var, $pretty=false) - { - $this->indent = 0; - $this->pretty = $pretty; - $this->nameValSeparator = $pretty ? ': ' : ':'; - return $this->encode2($var); - } - - /** - * encodes an arbitrary variable into JSON format - * - * @param mixed $var any number, boolean, string, array, or object to be encoded. - * see argument 1 to Services_JSON() above for array-parsing behavior. - * if var is a strng, note that encode() always expects it - * to be in ASCII or UTF-8 format! - * - * @return mixed JSON string representation of input var or an error if a problem occurs - * @access private - */ - function encode2($var) - { - if ($this->pretty) { - $close = "\n" . str_repeat("\t", $this->indent); - $open = $close . "\t"; - $mid = ',' . $open; - } - else { - $open = $close = ''; - $mid = ','; - } - - switch (gettype($var)) { - case 'boolean': - return $var ? 'true' : 'false'; - - case 'NULL': - return 'null'; - - case 'integer': - return (int) $var; - - case 'double': - case 'float': - return (float) $var; - - case 'string': - // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT - $ascii = ''; - $strlen_var = strlen($var); - - /* - * Iterate over every character in the string, - * escaping with a slash or encoding to UTF-8 where necessary - */ - for ($c = 0; $c < $strlen_var; ++$c) { - - $ord_var_c = ord($var{$c}); - - switch (true) { - case $ord_var_c == 0x08: - $ascii .= '\b'; - break; - case $ord_var_c == 0x09: - $ascii .= '\t'; - break; - case $ord_var_c == 0x0A: - $ascii .= '\n'; - break; - case $ord_var_c == 0x0C: - $ascii .= '\f'; - break; - case $ord_var_c == 0x0D: - $ascii .= '\r'; - break; - - case $ord_var_c == 0x22: - case $ord_var_c == 0x2F: - case $ord_var_c == 0x5C: - // double quote, slash, slosh - $ascii .= '\\'.$var{$c}; - break; - - case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): - // characters U-00000000 - U-0000007F (same as ASCII) - $ascii .= $var{$c}; - break; - - case (($ord_var_c & 0xE0) == 0xC0): - // characters U-00000080 - U-000007FF, mask 110XXXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, ord($var{$c + 1})); - $c += 1; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xF0) == 0xE0): - // characters U-00000800 - U-0000FFFF, mask 1110XXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, - ord($var{$c + 1}), - ord($var{$c + 2})); - $c += 2; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xF8) == 0xF0): - // characters U-00010000 - U-001FFFFF, mask 11110XXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - // These will always return a surrogate pair - $char = pack('C*', $ord_var_c, - ord($var{$c + 1}), - ord($var{$c + 2}), - ord($var{$c + 3})); - $c += 3; - $utf16 = $this->utf82utf16($char); - if($utf16 == '') { - $ascii .= '\ufffd'; - } else { - $utf16 = str_split($utf16, 2); - $ascii .= sprintf('\u%04s\u%04s', bin2hex($utf16[0]), bin2hex($utf16[1])); - } - break; - } - } - - return '"'.$ascii.'"'; - - case 'array': - /* - * As per JSON spec if any array key is not an integer - * we must treat the the whole array as an object. We - * also try to catch a sparsely populated associative - * array with numeric keys here because some JS engines - * will create an array with empty indexes up to - * max_index which can cause memory issues and because - * the keys, which may be relevant, will be remapped - * otherwise. - * - * As per the ECMA and JSON specification an object may - * have any string as a property. Unfortunately due to - * a hole in the ECMA specification if the key is a - * ECMA reserved word or starts with a digit the - * parameter is only accessible using ECMAScript's - * bracket notation. - */ - - // treat as a JSON object - if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { - $this->indent++; - $properties = array_map(array($this, 'name_value'), - array_keys($var), - array_values($var)); - $this->indent--; - - foreach($properties as $property) { - if(Services_JSON::isError($property)) { - return $property; - } - } - - return '{' . $open . join($mid, $properties) . $close . '}'; - } - - // treat it like a regular array - $this->indent++; - $elements = array_map(array($this, 'encode2'), $var); - $this->indent--; - - foreach($elements as $element) { - if(Services_JSON::isError($element)) { - return $element; - } - } - - return '[' . $open . join($mid, $elements) . $close . ']'; - - case 'object': - $vars = get_object_vars($var); - - $this->indent++; - $properties = array_map(array($this, 'name_value'), - array_keys($vars), - array_values($vars)); - $this->indent--; - - foreach($properties as $property) { - if(Services_JSON::isError($property)) { - return $property; - } - } - - return '{' . $open . join($mid, $properties) . $close . '}'; - - default: - return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) - ? 'null' - : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); - } - } - - /** - * array-walking function for use in generating JSON-formatted name-value pairs - * - * @param string $name name of key to use - * @param mixed $value reference to an array element to be encoded - * - * @return string JSON-formatted name-value pair, like '"name":value' - * @access private - */ - function name_value($name, $value) - { - $encoded_value = $this->encode2($value); - - if(Services_JSON::isError($encoded_value)) { - return $encoded_value; - } - - return $this->encode2(strval($name)) . $this->nameValSeparator . $encoded_value; - } - - /** - * reduce a string by removing leading and trailing comments and whitespace - * - * @param $str string string value to strip of comments and whitespace - * - * @return string string value stripped of comments and whitespace - * @access private - */ - function reduce_string($str) - { - $str = preg_replace(array( - - // eliminate single line comments in '// ...' form - '#^\s*//(.+)$#m', - - // eliminate multi-line comments in '/* ... */' form, at start of string - '#^\s*/\*(.+)\*/#Us', - - // eliminate multi-line comments in '/* ... */' form, at end of string - '#/\*(.+)\*/\s*$#Us' - - ), '', $str); - - // eliminate extraneous space - return trim($str); - } - - /** - * decodes a JSON string into appropriate variable - * - * @param string $str JSON-formatted string - * - * @return mixed number, boolean, string, array, or object - * corresponding to given JSON input string. - * See argument 1 to Services_JSON() above for object-output behavior. - * Note that decode() always returns strings - * in ASCII or UTF-8 format! - * @access public - */ - function decode($str) - { - $str = $this->reduce_string($str); - - switch (strtolower($str)) { - case 'true': - return true; - - case 'false': - return false; - - case 'null': - return null; - - default: - $m = array(); - - if (is_numeric($str)) { - // Lookie-loo, it's a number - - // This would work on its own, but I'm trying to be - // good about returning integers where appropriate: - // return (float)$str; - - // Return float or int, as appropriate - return ((float)$str == (integer)$str) - ? (integer)$str - : (float)$str; - - } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { - // STRINGS RETURNED IN UTF-8 FORMAT - $delim = substr($str, 0, 1); - $chrs = substr($str, 1, -1); - $utf8 = ''; - $strlen_chrs = strlen($chrs); - - for ($c = 0; $c < $strlen_chrs; ++$c) { - - $substr_chrs_c_2 = substr($chrs, $c, 2); - $ord_chrs_c = ord($chrs{$c}); - - switch (true) { - case $substr_chrs_c_2 == '\b': - $utf8 .= chr(0x08); - ++$c; - break; - case $substr_chrs_c_2 == '\t': - $utf8 .= chr(0x09); - ++$c; - break; - case $substr_chrs_c_2 == '\n': - $utf8 .= chr(0x0A); - ++$c; - break; - case $substr_chrs_c_2 == '\f': - $utf8 .= chr(0x0C); - ++$c; - break; - case $substr_chrs_c_2 == '\r': - $utf8 .= chr(0x0D); - ++$c; - break; - - case $substr_chrs_c_2 == '\\"': - case $substr_chrs_c_2 == '\\\'': - case $substr_chrs_c_2 == '\\\\': - case $substr_chrs_c_2 == '\\/': - if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || - ($delim == "'" && $substr_chrs_c_2 != '\\"')) { - $utf8 .= $chrs{++$c}; - } - break; - - case preg_match('/\\\uD[89AB][0-9A-F]{2}\\\uD[C-F][0-9A-F]{2}/i', substr($chrs, $c, 12)): - // escaped unicode surrogate pair - $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) - . chr(hexdec(substr($chrs, ($c + 4), 2))) - . chr(hexdec(substr($chrs, ($c + 8), 2))) - . chr(hexdec(substr($chrs, ($c + 10), 2))); - $utf8 .= $this->utf162utf8($utf16); - $c += 11; - break; - - case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): - // single, escaped unicode character - $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) - . chr(hexdec(substr($chrs, ($c + 4), 2))); - $utf8 .= $this->utf162utf8($utf16); - $c += 5; - break; - - case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): - $utf8 .= $chrs{$c}; - break; - - case ($ord_chrs_c & 0xE0) == 0xC0: - // characters U-00000080 - U-000007FF, mask 110XXXXX - //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 2); - ++$c; - break; - - case ($ord_chrs_c & 0xF0) == 0xE0: - // characters U-00000800 - U-0000FFFF, mask 1110XXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 3); - $c += 2; - break; - - case ($ord_chrs_c & 0xF8) == 0xF0: - // characters U-00010000 - U-001FFFFF, mask 11110XXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 4); - $c += 3; - break; - - case ($ord_chrs_c & 0xFC) == 0xF8: - // characters U-00200000 - U-03FFFFFF, mask 111110XX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 5); - $c += 4; - break; - - case ($ord_chrs_c & 0xFE) == 0xFC: - // characters U-04000000 - U-7FFFFFFF, mask 1111110X - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 6); - $c += 5; - break; - - } - - } - - return $utf8; - - } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { - // array, or object notation - - if ($str{0} == '[') { - $stk = array(SERVICES_JSON_IN_ARR); - $arr = array(); - } else { - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $stk = array(SERVICES_JSON_IN_OBJ); - $obj = array(); - } else { - $stk = array(SERVICES_JSON_IN_OBJ); - $obj = new stdClass(); - } - } - - array_push($stk, array( 'what' => SERVICES_JSON_SLICE, - 'where' => 0, - 'delim' => false)); - - $chrs = substr($str, 1, -1); - $chrs = $this->reduce_string($chrs); - - if ($chrs == '') { - if (reset($stk) == SERVICES_JSON_IN_ARR) { - return $arr; - - } else { - return $obj; - - } - } - - //print("\nparsing {$chrs}\n"); - - $strlen_chrs = strlen($chrs); - - for ($c = 0; $c <= $strlen_chrs; ++$c) { - - $top = end($stk); - $substr_chrs_c_2 = substr($chrs, $c, 2); - - if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { - // found a comma that is not inside a string, array, etc., - // OR we've reached the end of the character list - $slice = substr($chrs, $top['where'], ($c - $top['where'])); - array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); - //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - if (reset($stk) == SERVICES_JSON_IN_ARR) { - // we are in an array, so just push an element onto the stack - array_push($arr, $this->decode($slice)); - - } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { - // we are in an object, so figure - // out the property name and set an - // element in an associative array, - // for now - $parts = array(); - - if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { - // "name":value pair - $key = $this->decode($parts[1]); - $val = $this->decode($parts[2]); - - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $obj[$key] = $val; - } else { - $obj->$key = $val; - } - } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { - // name:value pair, where name is unquoted - $key = $parts[1]; - $val = $this->decode($parts[2]); - - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $obj[$key] = $val; - } else { - $obj->$key = $val; - } - } - - } - - } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { - // found a quote, and we are not inside a string - array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); - //print("Found start of string at {$c}\n"); - - } elseif (($chrs{$c} == $top['delim']) && - ($top['what'] == SERVICES_JSON_IN_STR) && - (($chrs{$c - 1} != '\\') || - ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) { - // found a quote, we're in a string, and it's not escaped - array_pop($stk); - //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); - - } elseif (($chrs{$c} == '[') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a left-bracket, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); - //print("Found start of array at {$c}\n"); - - } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { - // found a right-bracket, and we're in an array - array_pop($stk); - //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } elseif (($chrs{$c} == '{') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a left-brace, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); - //print("Found start of object at {$c}\n"); - - } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { - // found a right-brace, and we're in an object - array_pop($stk); - //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } elseif (($substr_chrs_c_2 == '/*') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a comment start, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); - $c++; - //print("Found start of comment at {$c}\n"); - - } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { - // found a comment end, and we're in one now - array_pop($stk); - $c++; - - for ($i = $top['where']; $i <= $c; ++$i) - $chrs = substr_replace($chrs, ' ', $i, 1); - - //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } - - } - - if (reset($stk) == SERVICES_JSON_IN_ARR) { - return $arr; - - } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { - return $obj; - - } - - } - } - } - - /** - * @todo Ultimately, this should just call PEAR::isError() - */ - function isError($data, $code = null) - { - if (class_exists('pear')) { - return PEAR::isError($data, $code); - } elseif (is_object($data) && (get_class($data) == 'services_json_error' || - is_subclass_of($data, 'services_json_error'))) { - return true; - } - - return false; - } -} - - -// Hide the PEAR_Error variant from Doxygen -/// @cond -if (class_exists('PEAR_Error')) { - - /** - * @ingroup API - */ - class Services_JSON_Error extends PEAR_Error - { - function Services_JSON_Error($message = 'unknown error', $code = null, - $mode = null, $options = null, $userinfo = null) - { - parent::PEAR_Error($message, $code, $mode, $options, $userinfo); - } - } - -} else { -/// @endcond - - /** - * @todo Ultimately, this class shall be descended from PEAR_Error - * @ingroup API - */ - class Services_JSON_Error - { - function Services_JSON_Error($message = 'unknown error', $code = null, - $mode = null, $options = null, $userinfo = null) - { - - } - } -} diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php index 163d3028..dd03c300 100644 --- a/includes/api/ApiFormatPhp.php +++ b/includes/api/ApiFormatPhp.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -33,8 +33,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiFormatPhp extends ApiFormatBase { - public function __construct($main, $format) { - parent :: __construct($main, $format); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); } public function getMimeType() { @@ -42,7 +42,7 @@ class ApiFormatPhp extends ApiFormatBase { } public function execute() { - $this->printText(serialize($this->getResultData())); + $this->printText( serialize( $this->getResultData() ) ); } public function getDescription() { @@ -50,6 +50,6 @@ class ApiFormatPhp extends ApiFormatBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatPhp.php 35098 2008-05-20 17:13:28Z ialex $'; + return __CLASS__ . ': $Id: ApiFormatPhp.php 60930 2010-01-11 15:55:52Z simetrical $'; } } diff --git a/includes/api/ApiFormatRaw.php b/includes/api/ApiFormatRaw.php index 51025448..8bb66aea 100644 --- a/includes/api/ApiFormatRaw.php +++ b/includes/api/ApiFormatRaw.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -39,33 +39,37 @@ class ApiFormatRaw extends ApiFormatBase { * @param $main ApiMain object * @param $errorFallback Formatter object to fall back on for errors */ - public function __construct($main, $errorFallback) { - parent :: __construct($main, 'raw'); + public function __construct( $main, $errorFallback ) { + parent :: __construct( $main, 'raw' ); $this->mErrorFallback = $errorFallback; } public function getMimeType() { $data = $this->getResultData(); - if(isset($data['error'])) + + if ( isset( $data['error'] ) ) return $this->mErrorFallback->getMimeType(); - if(!isset($data['mime'])) - ApiBase::dieDebug(__METHOD__, "No MIME type set for raw formatter"); + + if ( !isset( $data['mime'] ) ) + ApiBase::dieDebug( __METHOD__, "No MIME type set for raw formatter" ); + return $data['mime']; } public function execute() { $data = $this->getResultData(); - if(isset($data['error'])) + if ( isset( $data['error'] ) ) { $this->mErrorFallback->execute(); return; } - if(!isset($data['text'])) - ApiBase::dieDebug(__METHOD__, "No text given for raw formatter"); - $this->printText($data['text']); + + if ( !isset( $data['text'] ) ) + ApiBase::dieDebug( __METHOD__, "No text given for raw formatter" ); + $this->printText( $data['text'] ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatRaw.php 48629 2009-03-20 11:40:54Z catrope $'; + return __CLASS__ . ': $Id: ApiFormatRaw.php 61437 2010-01-23 22:26:40Z reedy $'; } } diff --git a/includes/api/ApiFormatTxt.php b/includes/api/ApiFormatTxt.php index 5f608d5c..1627dde6 100644 --- a/includes/api/ApiFormatTxt.php +++ b/includes/api/ApiFormatTxt.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -33,19 +33,19 @@ if (!defined('MEDIAWIKI')) { */ class ApiFormatTxt extends ApiFormatBase { - public function __construct($main, $format) { - parent :: __construct($main, $format); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); } public function getMimeType() { - # This looks like it should be text/plain, but IE7 is so - # brain-damaged it tries to parse text/plain as HTML if it - # contains HTML tags. Using MIME text/text works around this bug + // This looks like it should be text/plain, but IE7 is so + // brain-damaged it tries to parse text/plain as HTML if it + // contains HTML tags. Using MIME text/text works around this bug return 'text/text'; } public function execute() { - $this->printText(print_r($this->getResultData(), true)); + $this->printText( print_r( $this->getResultData(), true ) ); } public function getDescription() { @@ -53,6 +53,6 @@ class ApiFormatTxt extends ApiFormatBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatTxt.php 35098 2008-05-20 17:13:28Z ialex $'; + return __CLASS__ . ': $Id: ApiFormatTxt.php 61444 2010-01-23 22:52:40Z reedy $'; } } diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php index a716373d..e95e540b 100644 --- a/includes/api/ApiFormatWddx.php +++ b/includes/api/ApiFormatWddx.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -33,8 +33,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiFormatWddx extends ApiFormatBase { - public function __construct($main, $format) { - parent :: __construct($main, $format); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); } public function getMimeType() { @@ -46,67 +46,66 @@ class ApiFormatWddx extends ApiFormatBase { // PHP bug 45314. Test encoding an affected character (U+00A0) // to avoid this. $expected = "
\xc2\xa0"; - if (function_exists('wddx_serialize_value') + if ( function_exists( 'wddx_serialize_value' ) && !$this->getIsHtml() - && wddx_serialize_value("\xc2\xa0") == $expected) { - $this->printText(wddx_serialize_value($this->getResultData())); + && wddx_serialize_value( "\xc2\xa0" ) == $expected ) { + $this->printText( wddx_serialize_value( $this->getResultData() ) ); } else { // Don't do newlines and indentation if we weren't asked // for pretty output - $nl = ($this->getIsHtml() ? "" : "\n"); + $nl = ( $this->getIsHtml() ? "" : "\n" ); $indstr = " "; - $this->printText("$nl"); - $this->printText("$nl"); - $this->printText("$indstr
$nl"); - $this->printText("$indstr$nl"); - $this->slowWddxPrinter($this->getResultData(), 4); - $this->printText("$indstr$nl"); - $this->printText("$nl"); + $this->printText( "$nl" ); + $this->printText( "$nl" ); + $this->printText( "$indstr
$nl" ); + $this->printText( "$indstr$nl" ); + $this->slowWddxPrinter( $this->getResultData(), 4 ); + $this->printText( "$indstr$nl" ); + $this->printText( "$nl" ); } } /** * Recursively go through the object and output its data in WDDX format. */ - function slowWddxPrinter($elemValue, $indent = 0) { - $indstr = ($this->getIsHtml() ? "" : str_repeat(' ', $indent)); - $indstr2 = ($this->getIsHtml() ? "" : str_repeat(' ', $indent + 2)); - $nl = ($this->getIsHtml() ? "" : "\n"); - switch (gettype($elemValue)) { + function slowWddxPrinter( $elemValue, $indent = 0 ) { + $indstr = ( $this->getIsHtml() ? "" : str_repeat( ' ', $indent ) ); + $indstr2 = ( $this->getIsHtml() ? "" : str_repeat( ' ', $indent + 2 ) ); + $nl = ( $this->getIsHtml() ? "" : "\n" ); + switch ( gettype( $elemValue ) ) { case 'array' : // Check whether we've got an associative array () // or a regular array () - $cnt = count($elemValue); - if($cnt == 0 || array_keys($elemValue) === range(0, $cnt - 1)) { + $cnt = count( $elemValue ); + if ( $cnt == 0 || array_keys( $elemValue ) === range( 0, $cnt - 1 ) ) { // Regular array - $this->printText($indstr . Xml::element('array', array( - 'length' => $cnt - ), null) . $nl); - foreach($elemValue as $subElemValue) - $this->slowWddxPrinter($subElemValue, $indent + 2); - $this->printText("$indstr$nl"); + $this->printText( $indstr . Xml::element( 'array', array( + 'length' => $cnt ), null ) . $nl ); + foreach ( $elemValue as $subElemValue ) + $this->slowWddxPrinter( $subElemValue, $indent + 2 ); + $this->printText( "$indstr$nl" ); } else { // Associative array () - $this->printText("$indstr$nl"); - foreach($elemValue as $subElemName => $subElemValue) { - $this->printText($indstr2 . Xml::element('var', array( + $this->printText( "$indstr$nl" ); + foreach ( $elemValue as $subElemName => $subElemValue ) { + $this->printText( $indstr2 . Xml::element( 'var', array( 'name' => $subElemName - ), null) . $nl); - $this->slowWddxPrinter($subElemValue, $indent + 4); - $this->printText("$indstr2$nl"); + ), null ) . $nl ); + $this->slowWddxPrinter( $subElemValue, $indent + 4 ); + $this->printText( "$indstr2$nl" ); } - $this->printText("$indstr$nl"); + $this->printText( "$indstr$nl" ); } break; case 'integer' : case 'double' : - $this->printText($indstr . Xml::element('number', null, $elemValue) . $nl); + $this->printText( $indstr . Xml::element( 'number', null, $elemValue ) . $nl ); break; case 'string' : - $this->printText($indstr . Xml::element('string', null, $elemValue) . $nl); + $this->printText( $indstr . Xml::element( 'string', null, $elemValue ) . $nl ); break; default : - ApiBase :: dieDebug(__METHOD__, 'Unknown type ' . gettype($elemValue)); + ApiBase :: dieDebug( __METHOD__, 'Unknown type ' . gettype( $elemValue ) ); } } @@ -115,6 +114,6 @@ class ApiFormatWddx extends ApiFormatBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatWddx.php 48716 2009-03-23 20:06:16Z catrope $'; + return __CLASS__ . ': $Id: ApiFormatWddx.php 61437 2010-01-23 22:26:40Z reedy $'; } } diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php index 35b412c9..a3758a49 100644 --- a/includes/api/ApiFormatXml.php +++ b/includes/api/ApiFormatXml.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -35,9 +35,10 @@ class ApiFormatXml extends ApiFormatBase { private $mRootElemName = 'api'; private $mDoubleQuote = false; + private $mXslt = null; - public function __construct($main, $format) { - parent :: __construct($main, $format); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); } public function getMimeType() { @@ -48,16 +49,22 @@ class ApiFormatXml extends ApiFormatBase { return true; } - public function setRootElement($rootElemName) { + public function setRootElement( $rootElemName ) { $this->mRootElemName = $rootElemName; } public function execute() { $params = $this->extractRequestParams(); $this->mDoubleQuote = $params['xmldoublequote']; - - $this->printText(''); - $this->recXmlPrint($this->mRootElemName, $this->getResultData(), $this->getIsHtml() ? -2 : null); + $this->mXslt = $params['xslt']; + + $this->printText( '' ); + if ( !is_null( $this->mXslt ) ) + $this->addXslt(); + $this->printText( self::recXmlPrint( $this->mRootElemName, + $this->getResultData(), + $this->getIsHtml() ? - 2 : null, + $this->mDoubleQuote ) ); } /** @@ -73,22 +80,23 @@ class ApiFormatXml extends ApiFormatBase { * 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. */ - function recXmlPrint($elemName, $elemValue, $indent) { - if (!is_null($indent)) { + public static function recXmlPrint( $elemName, $elemValue, $indent, $doublequote = false ) { + $retval = ''; + if ( !is_null( $indent ) ) { $indent += 2; - $indstr = "\n" . str_repeat(" ", $indent); + $indstr = "\n" . str_repeat( " ", $indent ); } else { $indstr = ''; } - $elemName = str_replace(' ', '_', $elemName); + $elemName = str_replace( ' ', '_', $elemName ); - switch (gettype($elemValue)) { + switch ( gettype( $elemValue ) ) { case 'array' : - if (isset ($elemValue['*'])) { + if ( isset ( $elemValue['*'] ) ) { $subElemContent = $elemValue['*']; - if ($this->mDoubleQuote) - $subElemContent = $this->doubleQuote($subElemContent); - unset ($elemValue['*']); + if ( $doublequote ) + $subElemContent = Sanitizer::encodeAttribute( $subElemContent ); + unset ( $elemValue['*'] ); // Add xml:space="preserve" to the // element so XML parsers will leave @@ -98,80 +106,95 @@ class ApiFormatXml extends ApiFormatBase { $subElemContent = null; } - if (isset ($elemValue['_element'])) { + if ( isset ( $elemValue['_element'] ) ) { $subElemIndName = $elemValue['_element']; - unset ($elemValue['_element']); + unset ( $elemValue['_element'] ); } else { $subElemIndName = null; } $indElements = array (); $subElements = array (); - foreach ($elemValue as $subElemId => & $subElemValue) { - if (is_string($subElemValue) && $this->mDoubleQuote) - $subElemValue = $this->doubleQuote($subElemValue); + foreach ( $elemValue as $subElemId => & $subElemValue ) { + if ( is_string( $subElemValue ) && $doublequote ) + $subElemValue = Sanitizer::encodeAttribute( $subElemValue ); - if (gettype($subElemId) === 'integer') { + if ( gettype( $subElemId ) === 'integer' ) { $indElements[] = $subElemValue; - unset ($elemValue[$subElemId]); - } elseif (is_array($subElemValue)) { + unset ( $elemValue[$subElemId] ); + } elseif ( is_array( $subElemValue ) ) { $subElements[$subElemId] = $subElemValue; - unset ($elemValue[$subElemId]); + unset ( $elemValue[$subElemId] ); } } - if (is_null($subElemIndName) && count($indElements)) - ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has integer keys without _element value. Use ApiResult::setIndexedTagName()."); + if ( is_null( $subElemIndName ) && count( $indElements ) ) + ApiBase :: dieDebug( __METHOD__, "($elemName, ...) has integer keys without _element value. Use ApiResult::setIndexedTagName()." ); - if (count($subElements) && count($indElements) && !is_null($subElemContent)) - ApiBase :: dieDebug(__METHOD__, "($elemName, ...) has content and subelements"); + if ( count( $subElements ) && count( $indElements ) && !is_null( $subElemContent ) ) + ApiBase :: dieDebug( __METHOD__, "($elemName, ...) has content and subelements" ); - if (!is_null($subElemContent)) { - $this->printText($indstr . Xml::element($elemName, $elemValue, $subElemContent)); - } elseif (!count($indElements) && !count($subElements)) { - $this->printText($indstr . Xml::element($elemName, $elemValue)); + if ( !is_null( $subElemContent ) ) { + $retval .= $indstr . Xml::element( $elemName, $elemValue, $subElemContent ); + } elseif ( !count( $indElements ) && !count( $subElements ) ) { + $retval .= $indstr . Xml::element( $elemName, $elemValue ); } else { - $this->printText($indstr . Xml::element($elemName, $elemValue, null)); + $retval .= $indstr . Xml::element( $elemName, $elemValue, null ); - foreach ($subElements as $subElemId => & $subElemValue) - $this->recXmlPrint($subElemId, $subElemValue, $indent); + foreach ( $subElements as $subElemId => & $subElemValue ) + $retval .= self::recXmlPrint( $subElemId, $subElemValue, $indent ); - foreach ($indElements as $subElemId => & $subElemValue) - $this->recXmlPrint($subElemIndName, $subElemValue, $indent); + foreach ( $indElements as $subElemId => & $subElemValue ) + $retval .= self::recXmlPrint( $subElemIndName, $subElemValue, $indent ); - $this->printText($indstr . Xml::closeElement($elemName)); + $retval .= $indstr . Xml::closeElement( $elemName ); } break; case 'object' : // ignore break; default : - $this->printText($indstr . Xml::element($elemName, null, $elemValue)); + $retval .= $indstr . Xml::element( $elemName, null, $elemValue ); break; } + return $retval; } - private function doubleQuote( $text ) { - return Sanitizer::encodeAttribute( $text ); + function addXslt() { + $nt = Title::newFromText( $this->mXslt ); + if ( is_null( $nt ) || !$nt->exists() ) { + $this->setWarning( 'Invalid or non-existent stylesheet specified' ); + return; + } + if ( $nt->getNamespace() != NS_MEDIAWIKI ) { + $this->setWarning( 'Stylesheet should be in the MediaWiki namespace.' ); + return; + } + if ( substr( $nt->getText(), - 4 ) !== '.xsl' ) { + $this->setWarning( 'Stylesheet should have .xsl extension.' ); + return; + } + $this->printText( 'escapeLocalURL( 'action=raw' ) . '" type="text/xsl" ?>' ); } - + public function getAllowedParams() { return array ( - 'xmldoublequote' => false + 'xmldoublequote' => false, + 'xslt' => null, ); } public function getParamDescription() { return array ( 'xmldoublequote' => 'If specified, double quotes all attributes and content.', + 'xslt' => 'If specified, adds as stylesheet', ); } - public function getDescription() { return 'Output data in XML format' . parent :: getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatXml.php 50217 2009-05-05 13:12:16Z tstarling $'; + return __CLASS__ . ': $Id: ApiFormatXml.php 62402 2010-02-13 00:09:05Z reedy $'; } } diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php index cc255c63..39381b0f 100644 --- a/includes/api/ApiFormatYaml.php +++ b/includes/api/ApiFormatYaml.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiFormatBase.php'); + require_once ( 'ApiFormatBase.php' ); } /** @@ -33,8 +33,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiFormatYaml extends ApiFormatBase { - public function __construct($main, $format) { - parent :: __construct($main, $format); + public function __construct( $main, $format ) { + parent :: __construct( $main, $format ); } public function getMimeType() { @@ -42,7 +42,7 @@ class ApiFormatYaml extends ApiFormatBase { } public function execute() { - $this->printText(Spyc :: YAMLDump($this->getResultData())); + $this->printText( Spyc :: YAMLDump( $this->getResultData() ) ); } public function getDescription() { @@ -50,6 +50,6 @@ class ApiFormatYaml extends ApiFormatBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatYaml.php 35098 2008-05-20 17:13:28Z ialex $'; + return __CLASS__ . ': $Id: ApiFormatYaml.php 60930 2010-01-11 15:55:52Z simetrical $'; } } diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php index f16b2c8a..30f860dd 100644 --- a/includes/api/ApiFormatYaml_spyc.php +++ b/includes/api/ApiFormatYaml_spyc.php @@ -38,9 +38,9 @@ class Spyc { * @param $indent Integer: Pass in false to use the default, which is 2 * @param $wordwrap Integer: Pass in 0 for no wordwrap, false for default (40) */ - public static function YAMLDump($array,$indent = false,$wordwrap = false) { + public static function YAMLDump( $array, $indent = false, $wordwrap = false ) { $spyc = new Spyc; - return $spyc->dump($array,$indent,$wordwrap); + return $spyc->dump( $array, $indent, $wordwrap ); } /** @@ -63,18 +63,18 @@ class Spyc { * @param $indent Integer: Pass in false to use the default, which is 2 * @param $wordwrap Integer: Pass in 0 for no wordwrap, false for default (40) */ - function dump($array,$indent = false,$wordwrap = false) { + function dump( $array, $indent = false, $wordwrap = false ) { // Dumps to some very clean YAML. We'll have to add some more features // and options soon. And better support for folding. // New features and options. - if ($indent === false or !is_numeric($indent)) { + if ( $indent === false or !is_numeric( $indent ) ) { $this->_dumpIndent = 2; } else { $this->_dumpIndent = $indent; } - if ($wordwrap === false or !is_numeric($wordwrap)) { + if ( $wordwrap === false or !is_numeric( $wordwrap ) ) { $this->_dumpWordWrap = 40; } else { $this->_dumpWordWrap = $wordwrap; @@ -84,8 +84,8 @@ class Spyc { $string = "---\n"; // Start at the base of the array and move through it. - foreach ($array as $key => $value) { - $string .= $this->_yamlize($key,$value,0); + foreach ( $array as $key => $value ) { + $string .= $this->_yamlize( $key, $value, 0 ); } return $string; } @@ -110,18 +110,18 @@ class Spyc { * @param $value The value of the item * @param $indent The indent of the current node */ - private function _yamlize($key,$value,$indent) { - if (is_array($value)) { + private function _yamlize( $key, $value, $indent ) { + if ( is_array( $value ) ) { // It has children. What to do? // Make it the right kind of item - $string = $this->_dumpNode($key,NULL,$indent); + $string = $this->_dumpNode( $key, null, $indent ); // Add the indent $indent += $this->_dumpIndent; // Yamlize the array - $string .= $this->_yamlizeArray($value,$indent); - } elseif (!is_array($value)) { + $string .= $this->_yamlizeArray( $value, $indent ); + } elseif ( !is_array( $value ) ) { // It doesn't have children. Yip. - $string = $this->_dumpNode($key,$value,$indent); + $string = $this->_dumpNode( $key, $value, $indent ); } return $string; } @@ -132,11 +132,11 @@ class Spyc { * @param $array The array you want to convert * @param $indent The indent of the current level */ - private function _yamlizeArray($array,$indent) { - if (is_array($array)) { + private function _yamlizeArray( $array, $indent ) { + if ( is_array( $array ) ) { $string = ''; - foreach ($array as $key => $value) { - $string .= $this->_yamlize($key,$value,$indent); + foreach ( $array as $key => $value ) { + $string .= $this->_yamlize( $key, $value, $indent ); } return $string; } else { @@ -150,16 +150,15 @@ class Spyc { * @param $value The string to check * @return bool */ - function _needLiteral($value) { - # Check whether the string contains # or : or begins with any of: - # [ - ? , [ ] { } ! * & | > ' " % @ ` ] - # or is a number or contains newlines - return (bool)(gettype($value) == "string" && - (is_numeric($value) || - strpos($value, "\n") || - preg_match("/[#:]/", $value) || - preg_match("/^[-?,[\]{}!*&|>'\"%@`]/", $value))); - + function _needLiteral( $value ) { + // Check whether the string contains # or : or begins with any of: + // [ - ? , [ ] { } ! * & | > ' " % @ ` ] + // or is a number or contains newlines + return (bool)( gettype( $value ) == "string" && + ( is_numeric( $value ) || + strpos( $value, "\n" ) || + preg_match( "/[#:]/", $value ) || + preg_match( "/^[-?,[\]{}!*&|>'\"%@`]/", $value ) ) ); } /** @@ -169,25 +168,28 @@ class Spyc { * @param $value The value of the item * @param $indent The indent of the current node */ - private function _dumpNode($key,$value,$indent) { + private function _dumpNode( $key, $value, $indent ) { // do some folding here, for blocks - if ($this->_needLiteral($value)) { - $value = $this->_doLiteralBlock($value,$indent); + if ( $this->_needLiteral( $value ) ) { + $value = $this->_doLiteralBlock( $value, $indent ); } else { - $value = $this->_doFolding($value,$indent); + $value = $this->_doFolding( $value, $indent ); } - $spaces = str_repeat(' ',$indent); + $spaces = str_repeat( ' ', $indent ); - if (is_int($key)) { + if ( is_int( $key ) ) { // It's a sequence - if ($value !== '' && !is_null($value)) - $string = $spaces.'- '.$value."\n"; + if ( $value !== '' && !is_null( $value ) ) + $string = $spaces . '- ' . $value . "\n"; else $string = $spaces . "-\n"; } else { + if ($key == '*') //bug 21922 - Quote asterix used as keys + $key = "'*'"; + // It's mapped - if ($value !== '' && !is_null($value)) + if ( $value !== '' && !is_null( $value ) ) $string = $spaces . $key . ': ' . $value . "\n"; else $string = $spaces . $key . ":\n"; @@ -201,13 +203,13 @@ class Spyc { * @param $value * @param $indent int The value of the indent */ - private function _doLiteralBlock($value,$indent) { - $exploded = explode("\n",$value); - $newValue = '|'; + private function _doLiteralBlock( $value, $indent ) { + $exploded = explode( "\n", $value ); + $newValue = '|-'; $indent += $this->_dumpIndent; - $spaces = str_repeat(' ',$indent); - foreach ($exploded as $line) { - $newValue .= "\n" . $spaces . trim($line); + $spaces = str_repeat( ' ', $indent ); + foreach ( $exploded as $line ) { + $newValue .= "\n" . $spaces . trim( $line ); } return $newValue; } @@ -217,17 +219,17 @@ class Spyc { * @return string * @param $value The string you wish to fold */ - private function _doFolding($value,$indent) { + private function _doFolding( $value, $indent ) { // Don't do anything if wordwrap is set to 0 - if ($this->_dumpWordWrap === 0) { + if ( $this->_dumpWordWrap === 0 ) { return $value; } - if (strlen($value) > $this->_dumpWordWrap) { + if ( strlen( $value ) > $this->_dumpWordWrap ) { $indent += $this->_dumpIndent; - $indent = str_repeat(' ',$indent); - $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); - $value = ">\n".$indent.$wrapped; + $indent = str_repeat( ' ', $indent ); + $wrapped = wordwrap( $value, $this->_dumpWordWrap, "\n$indent" ); + $value = ">-\n" . $indent . $wrapped; } return $value; } diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index c001a7dc..1f32e019 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -35,15 +35,15 @@ if (!defined('MEDIAWIKI')) { */ class ApiHelp extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } /** * Stub module for displaying help when no parameters are given */ public function execute() { - $this->dieUsage('', 'help'); + $this->dieUsage( '', 'help' ); } public function shouldCheckMaxlag() { @@ -61,6 +61,6 @@ class ApiHelp extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiHelp.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiHelp.php 60930 2010-01-11 15:55:52Z simetrical $'; } } diff --git a/includes/api/ApiImport.php b/includes/api/ApiImport.php index 4b1518bb..032b684c 100644 --- a/includes/api/ApiImport.php +++ b/includes/api/ApiImport.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -35,70 +35,68 @@ if (!defined('MEDIAWIKI')) { */ class ApiImport extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { global $wgUser; - if(!$wgUser->isAllowed('import')) - $this->dieUsageMsg(array('cantimport')); + if ( !$wgUser->isAllowed( 'import' ) ) + $this->dieUsageMsg( array( 'cantimport' ) ); $params = $this->extractRequestParams(); - if(!isset($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); $source = null; $isUpload = false; - if(isset($params['interwikisource'])) + if ( isset( $params['interwikisource'] ) ) { - if(!isset($params['interwikipage'])) - $this->dieUsageMsg(array('missingparam', 'interwikipage')); + if ( !isset( $params['interwikipage'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'interwikipage' ) ); $source = ImportStreamSource::newFromInterwiki( $params['interwikisource'], $params['interwikipage'], $params['fullhistory'], - $params['templates']); + $params['templates'] ); } else { $isUpload = true; - if(!$wgUser->isAllowed('importupload')) - $this->dieUsageMsg(array('cantimport-upload')); - $source = ImportStreamSource::newFromUpload('xml'); + if ( !$wgUser->isAllowed( 'importupload' ) ) + $this->dieUsageMsg( array( 'cantimport-upload' ) ); + $source = ImportStreamSource::newFromUpload( 'xml' ); } - if($source instanceof WikiErrorMsg) - $this->dieUsageMsg(array_merge( - array($source->getMessageKey()), - $source->getMessageArgs())); - else if(WikiError::isError($source)) + if ( $source instanceof WikiErrorMsg ) + $this->dieUsageMsg( array_merge( + array( $source->getMessageKey() ), + $source->getMessageArgs() ) ); + else if ( WikiError::isError( $source ) ) // This shouldn't happen - $this->dieUsageMsg(array('import-unknownerror', $source->getMessage())); + $this->dieUsageMsg( array( 'import-unknownerror', $source->getMessage() ) ); - $importer = new WikiImporter($source); - if(isset($params['namespace'])) - $importer->setTargetNamespace($params['namespace']); - $reporter = new ApiImportReporter($importer, $isUpload, + $importer = new WikiImporter( $source ); + if ( isset( $params['namespace'] ) ) + $importer->setTargetNamespace( $params['namespace'] ); + $reporter = new ApiImportReporter( $importer, $isUpload, $params['interwikisource'], - $params['summary']); + $params['summary'] ); $result = $importer->doImport(); - if($result instanceof WikiXmlError) - $this->dieUsageMsg(array('import-xml-error', + if ( $result instanceof WikiXmlError ) + $this->dieUsageMsg( array( 'import-xml-error', $result->mLine, $result->mColumn, $result->mByte . $result->mContext, - xml_error_string($result->mXmlError))); - else if(WikiError::isError($result)) - // This shouldn't happen - $this->dieUsageMsg(array('import-unknownerror', $result->getMessage())); + xml_error_string( $result->mXmlError ) ) ); + else if ( WikiError::isError( $result ) ) + $this->dieUsageMsg( array( 'import-unknownerror', $result->getMessage() ) ); // This shouldn't happen + $resultData = $reporter->getData(); - $this->getResult()->setIndexedTagName($resultData, 'page'); - $this->getResult()->addValue(null, $this->getModuleName(), $resultData); + $this->getResult()->setIndexedTagName( $resultData, 'page' ); + $this->getResult()->addValue( null, $this->getModuleName(), $resultData ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; @@ -140,6 +138,20 @@ class ApiImport extends ApiBase { 'Import a page from another wiki, or an XML file' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'cantimport' ), + array( 'missingparam', 'interwikipage' ), + array( 'cantimport-upload' ), + array( 'import-unknownerror', 'source' ), + array( 'import-unknownerror', 'result' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { return array( @@ -149,7 +161,7 @@ class ApiImport extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiImport.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiImport.php 62599 2010-02-16 21:59:16Z reedy $'; } } @@ -160,20 +172,20 @@ class ApiImport extends ApiBase { class ApiImportReporter extends ImportReporter { private $mResultArr = array(); - function reportPage($title, $origTitle, $revisionCount, $successCount) + function reportPage( $title, $origTitle, $revisionCount, $successCount ) { // Add a result entry $r = array(); - ApiQueryBase::addTitleInfo($r, $title); - $r['revisions'] = intval($successCount); + ApiQueryBase::addTitleInfo( $r, $title ); + $r['revisions'] = intval( $successCount ); $this->mResultArr[] = $r; // Piggyback on the parent to do the logging - parent::reportPage($title, $origTitle, $revisionCount, $successCount); + parent::reportPage( $title, $origTitle, $revisionCount, $successCount ); } function getData() { return $this->mResultArr; } -} +} \ No newline at end of file diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php index 8f1fb834..442bc44c 100644 --- a/includes/api/ApiLogin.php +++ b/includes/api/ApiLogin.php @@ -1,11 +1,11 @@ @gmail.com, + * Copyright © 2006-2007 Yuri Astrakhan @gmail.com, * Daniel Cannon (cannon dot danielc at gmail dot com) * * This program is free software; you can redistribute it and/or modify @@ -24,9 +24,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once( 'ApiBase.php' ); } /** @@ -36,8 +36,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiLogin extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action, 'lg'); + public function __construct( $main, $action ) { + parent::__construct( $main, $action, 'lg' ); } /** @@ -48,42 +48,40 @@ class ApiLogin extends ApiBase { * user, or any other reason, the host is cached with an expiry * and no log-in attempts will be accepted until that expiry * is reached. The expiry is $this->mLoginThrottle. - * - * @access public */ public function execute() { $params = $this->extractRequestParams(); - $result = array (); + $result = array(); - $req = new FauxRequest(array ( + $req = new FauxRequest( array( 'wpName' => $params['name'], 'wpPassword' => $params['password'], 'wpDomain' => $params['domain'], 'wpLoginToken' => $params['token'], 'wpRemember' => '' - )); + ) ); // Init session if necessary - if( session_id() == '' ) { + if ( session_id() == '' ) { wfSetupSession(); } - $loginForm = new LoginForm($req); - switch ($authRes = $loginForm->authenticateUserData()) { - case LoginForm :: SUCCESS : + $loginForm = new LoginForm( $req ); + switch ( $authRes = $loginForm->authenticateUserData() ) { + case LoginForm::SUCCESS: global $wgUser, $wgCookiePrefix; - $wgUser->setOption('rememberpassword', 1); + $wgUser->setOption( 'rememberpassword', 1 ); $wgUser->setCookies(); // Run hooks. FIXME: split back and frontend from this hook. // FIXME: This hook should be placed in the backend $injected_html = ''; - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html)); + wfRunHooks( 'UserLoginComplete', array( &$wgUser, &$injected_html ) ); $result['result'] = 'Success'; - $result['lguserid'] = intval($wgUser->getId()); + $result['lguserid'] = intval( $wgUser->getId() ); $result['lgusername'] = $wgUser->getName(); $result['lgtoken'] = $wgUser->getToken(); $result['cookieprefix'] = $wgCookiePrefix; @@ -102,48 +100,63 @@ class ApiLogin extends ApiBase { $result['result'] = 'WrongToken'; break; - case LoginForm :: NO_NAME : + case LoginForm::NO_NAME: $result['result'] = 'NoName'; break; - case LoginForm :: ILLEGAL : + + case LoginForm::ILLEGAL: $result['result'] = 'Illegal'; break; - case LoginForm :: WRONG_PLUGIN_PASS : + + case LoginForm::WRONG_PLUGIN_PASS: $result['result'] = 'WrongPluginPass'; break; - case LoginForm :: NOT_EXISTS : + + case LoginForm::NOT_EXISTS: $result['result'] = 'NotExists'; break; - case LoginForm :: WRONG_PASS : + + case LoginForm::RESET_PASS: // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin - "The e-mailed temporary password should not be used for actual logins;" + case LoginForm::WRONG_PASS: $result['result'] = 'WrongPass'; break; - case LoginForm :: EMPTY_PASS : + + case LoginForm::EMPTY_PASS: $result['result'] = 'EmptyPass'; break; - case LoginForm :: CREATE_BLOCKED : + + case LoginForm::CREATE_BLOCKED: $result['result'] = 'CreateBlocked'; $result['details'] = 'Your IP address is blocked from account creation'; break; - case LoginForm :: THROTTLED : + + case LoginForm::THROTTLED: global $wgPasswordAttemptThrottle; $result['result'] = 'Throttled'; - $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']); + $result['wait'] = intval( $wgPasswordAttemptThrottle['seconds'] ); break; - default : - ApiBase :: dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); + + case LoginForm::USER_BLOCKED: + $result['result'] = 'Blocked'; + break; + + default: + ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" ); } - $this->getResult()->addValue(null, 'login', $result); + $this->getResult()->addValue( null, 'login', $result ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isReadMode() { return false; } public function getAllowedParams() { - return array ( + return array( 'name' => null, 'password' => null, 'domain' => null, @@ -152,7 +165,7 @@ class ApiLogin extends ApiBase { } public function getParamDescription() { - return array ( + return array( 'name' => 'User Name', 'password' => 'Password', 'domain' => 'Domain (optional)', @@ -161,7 +174,7 @@ class ApiLogin extends ApiBase { } public function getDescription() { - return array ( + return array( 'This module is used to login and get the authentication tokens. ', 'In the event of a successful log-in, a cookie will be attached', 'to your session. In the event of a failed log-in, you will not ', @@ -170,6 +183,22 @@ class ApiLogin extends ApiBase { ); } + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'NeedToken', 'info' => 'You need to resubmit your login with the specified token. See https://bugzilla.wikimedia.org/show_bug.cgi?id=23076' ), + array( 'code' => 'WrongToken', 'info' => 'You specified an invalid token' ), + array( 'code' => 'NoName', 'info' => 'You didn\'t set the lgname parameter' ), + array( 'code' => 'Illegal', 'info' => ' You provided an illegal username' ), + array( 'code' => 'NotExists', 'info' => ' The username you provided doesn\'t exist' ), + array( 'code' => 'EmptyPass', 'info' => ' You didn\'t set the lgpassword parameter or you left it empty' ), + array( 'code' => 'WrongPass', 'info' => ' The password you provided is incorrect' ), + array( 'code' => 'WrongPluginPass', 'info' => 'Same as `WrongPass", returned when an authentication plugin rather than MediaWiki itself rejected the password' ), + array( 'code' => 'CreateBlocked', 'info' => 'The wiki tried to automatically create a new account for you, but your IP address has been blocked from account creation' ), + array( 'code' => 'Throttled', 'info' => 'You\'ve logged in too many times in a short time' ), + array( 'code' => 'Blocked', 'info' => 'User is blocked' ), + ) ); + } + protected function getExamples() { return array( 'api.php?action=login&lgname=user&lgpassword=password' @@ -177,6 +206,6 @@ class ApiLogin extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiLogin.php 69990 2010-07-27 08:44:08Z tstarling $'; + return __CLASS__ . ': $Id: ApiLogin.php 64697 2010-04-07 09:05:05Z catrope $'; } } diff --git a/includes/api/ApiLogout.php b/includes/api/ApiLogout.php index aa9f2829..6637ee09 100644 --- a/includes/api/ApiLogout.php +++ b/includes/api/ApiLogout.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -36,8 +36,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiLogout extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { @@ -47,7 +47,7 @@ class ApiLogout extends ApiBase { // Give extensions to do something after user logout $injected_html = ''; - wfRunHooks( 'UserLogoutComplete', array(&$wgUser, &$injected_html, $oldName) ); + wfRunHooks( 'UserLogoutComplete', array( &$wgUser, &$injected_html, $oldName ) ); } public function isReadMode() { @@ -75,6 +75,6 @@ class ApiLogout extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiLogout.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiLogout.php 69578 2010-07-20 02:46:20Z tstarling $'; } } diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 063e3574..fa6957b6 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -76,10 +76,12 @@ class ApiMain extends ApiBase { 'unblock' => 'ApiUnblock', 'move' => 'ApiMove', 'edit' => 'ApiEditPage', + 'upload' => 'ApiUpload', 'emailuser' => 'ApiEmailUser', 'watch' => 'ApiWatch', 'patrol' => 'ApiPatrol', 'import' => 'ApiImport', + 'userrights' => 'ApiUserrights', ); /** @@ -102,26 +104,28 @@ class ApiMain extends ApiBase { 'dbg' => 'ApiFormatDbg', 'dbgfm' => 'ApiFormatDbg' ); - + /** * List of user roles that are specifically relevant to the API. * array( 'right' => array ( 'msg' => 'Some message with a $1', * 'params' => array ( $someVarToSubst ) ), * ); */ - private static $mRights = array('writeapi' => array( + private static $mRights = array( 'writeapi' => array( 'msg' => 'Use of the write API', 'params' => array() ), 'apihighlimits' => array( 'msg' => 'Use higher limits in API queries (Slow queries: $1 results; Fast queries: $2 results). The limits for slow queries also apply to multivalue parameters.', - 'params' => array (ApiMain::LIMIT_SML2, ApiMain::LIMIT_BIG2) + 'params' => array ( ApiMain::LIMIT_SML2, ApiMain::LIMIT_BIG2 ) ) ); private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames; - private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode; + private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest; + private $mInternalMode, $mSquidMaxage, $mModule; + private $mCacheMode = 'private'; private $mCacheControl = array(); @@ -131,21 +135,21 @@ class ApiMain extends ApiBase { * @param $request object - if this is an instance of FauxRequest, errors are thrown and no printing occurs * @param $enableWrite bool should be set to true if the api may modify data */ - public function __construct($request, $enableWrite = false) { + public function __construct( $request, $enableWrite = false ) { - $this->mInternalMode = ($request instanceof FauxRequest); + $this->mInternalMode = ( $request instanceof FauxRequest ); // Special handling for the main module: $parent === $this - parent :: __construct($this, $this->mInternalMode ? 'main_int' : 'main'); + parent :: __construct( $this, $this->mInternalMode ? 'main_int' : 'main' ); - if (!$this->mInternalMode) { + if ( !$this->mInternalMode ) { // Impose module restrictions. // If the current user cannot read, // Remove all modules other than login global $wgUser; - if( $request->getVal( 'callback' ) !== null ) { + if ( $request->getVal( 'callback' ) !== null ) { // JSON callback allows cross-site reads. // For safety, strip user credentials. wfDebug( "API: stripping user credentials for JSON callback\n" ); @@ -156,16 +160,17 @@ class ApiMain extends ApiBase { global $wgAPIModules; // extension modules $this->mModules = $wgAPIModules + self :: $Modules; - $this->mModuleNames = array_keys($this->mModules); + $this->mModuleNames = array_keys( $this->mModules ); $this->mFormats = self :: $Formats; - $this->mFormatNames = array_keys($this->mFormats); + $this->mFormatNames = array_keys( $this->mFormats ); - $this->mResult = new ApiResult($this); + $this->mResult = new ApiResult( $this ); $this->mShowVersions = false; $this->mEnableWrite = $enableWrite; $this->mRequest = & $request; + $this->mSquidMaxage = - 1; // flag for executeActionWithErrorHandling() $this->mCommit = false; } @@ -184,27 +189,34 @@ class ApiMain extends ApiBase { } /** - * Get the ApiResult object asscosiated with current request + * Get the ApiResult object associated with current request */ public function getResult() { return $this->mResult; } + /** + * Get the API module object. Only works after executeAction() + */ + public function getModule() { + return $this->mModule; + } + /** * Only kept for backwards compatibility * @deprecated Use isWriteMode() instead */ public function requestWriteMode() { - if (!$this->mEnableWrite) - $this->dieUsageMsg(array('writedisabled')); - if (wfReadOnly()) - $this->dieUsageMsg(array('readonlytext')); + if ( !$this->mEnableWrite ) + $this->dieUsageMsg( array( 'writedisabled' ) ); + if ( wfReadOnly() ) + $this->dieUsageMsg( array( 'readonlytext' ) ); } /** * Set how long the response should be cached. */ - public function setCacheMaxAge($maxage) { + public function setCacheMaxAge( $maxage ) { $this->setCacheControl( array( 'max-age' => $maxage, 's-maxage' => $maxage @@ -293,10 +305,10 @@ class ApiMain extends ApiBase { /** * Create an instance of an output formatter by its name */ - public function createPrinterByName($format) { - if( !isset( $this->mFormats[$format] ) ) + public function createPrinterByName( $format ) { + if ( !isset( $this->mFormats[$format] ) ) $this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' ); - return new $this->mFormats[$format] ($this, $format); + return new $this->mFormats[$format] ( $this, $format ); } /** @@ -304,11 +316,11 @@ class ApiMain extends ApiBase { */ public function execute() { $this->profileIn(); - if ($this->mInternalMode) + if ( $this->mInternalMode ) $this->executeAction(); else $this->executeActionWithErrorHandling(); - + $this->profileOut(); } @@ -324,7 +336,7 @@ class ApiMain extends ApiBase { try { $this->executeAction(); - } catch (Exception $e) { + } catch ( Exception $e ) { // Log it if ( $e instanceof MWException ) { wfDebugLog( 'exception', $e->getLogMessage() ); @@ -336,31 +348,32 @@ class ApiMain extends ApiBase { // handler will process and log it. // - $errCode = $this->substituteResultWithError($e); + $errCode = $this->substituteResultWithError( $e ); // Error results should not be cached $this->setCacheMode( 'private' ); $headerStr = 'MediaWiki-API-Error: ' . $errCode; - if ($e->getCode() === 0) - header($headerStr); + if ( $e->getCode() === 0 ) + header( $headerStr ); else - header($headerStr, true, $e->getCode()); + header( $headerStr, true, $e->getCode() ); // Reset and print just the error message ob_clean(); // If the error occured during printing, do a printer->profileOut() $this->mPrinter->safeProfileOut(); - $this->printResult(true); + $this->printResult( true ); } // Send cache headers after any code which might generate an error, to // avoid sending public cache headers for errors. $this->sendCacheHeaders(); - if($this->mPrinter->getIsHtml()) + if ( $this->mPrinter->getIsHtml() ) { echo wfReportTime(); + } ob_end_flush(); } @@ -372,15 +385,22 @@ class ApiMain extends ApiBase { } if ( $this->mCacheMode == 'anon-public-user-private' ) { - global $wgOut; + global $wgUseXVO, $wgOut; header( 'Vary: Accept-Encoding, Cookie' ); - header( $wgOut->getXVO() ); - if ( session_id() != '' || $wgOut->haveCacheVaryCookies() ) { - // Logged in, mark this request private + if ( $wgUseXVO ) { + header( $wgOut->getXVO() ); + if ( $wgOut->haveCacheVaryCookies() ) { + // Logged in, mark this request private + header( 'Cache-Control: private' ); + return; + } + // Logged out, send normal public headers below + } elseif ( session_id() != '' ) { + // Logged in or otherwise has session (e.g. anonymous users who have edited) + // Mark request private header( 'Cache-Control: private' ); return; - } - // Logged out, send normal public headers below + } // else no XVO and anonymous, send public headers below } else /* if public */ { // Give a debugging message if the user object is unstubbed on a public request global $wgUser; @@ -396,7 +416,7 @@ class ApiMain extends ApiBase { if ( !isset( $this->mCacheControl['max-age'] ) ) { $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' ); } - + if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) { // Public cache not requested // Sending a Vary header in this case is harmless, and protects us @@ -426,7 +446,7 @@ class ApiMain extends ApiBase { $separator = ', '; } } - + header( "Cache-Control: $ccHeader" ); } @@ -434,57 +454,55 @@ class ApiMain extends ApiBase { * Replace the result data with the information about an exception. * Returns the error code */ - protected function substituteResultWithError($e) { + protected function substituteResultWithError( $e ) { - // Printer may not be initialized if the extractRequestParams() fails for the main module - if (!isset ($this->mPrinter)) { - // The printer has not been created yet. Try to manually get formatter value. - $value = $this->getRequest()->getVal('format', self::API_DEFAULT_FORMAT); - if (!in_array($value, $this->mFormatNames)) - $value = self::API_DEFAULT_FORMAT; + // Printer may not be initialized if the extractRequestParams() fails for the main module + if ( !isset ( $this->mPrinter ) ) { + // The printer has not been created yet. Try to manually get formatter value. + $value = $this->getRequest()->getVal( 'format', self::API_DEFAULT_FORMAT ); + if ( !in_array( $value, $this->mFormatNames ) ) + $value = self::API_DEFAULT_FORMAT; - $this->mPrinter = $this->createPrinterByName($value); - if ($this->mPrinter->getNeedsRawData()) - $this->getResult()->setRawMode(); - } + $this->mPrinter = $this->createPrinterByName( $value ); + if ( $this->mPrinter->getNeedsRawData() ) + $this->getResult()->setRawMode(); + } - if ($e instanceof UsageException) { - // - // User entered incorrect parameters - print usage screen - // - $errMessage = array ( - 'code' => $e->getCodeString(), - 'info' => $e->getMessage()); + if ( $e instanceof UsageException ) { + // + // User entered incorrect parameters - print usage screen + // + $errMessage = $e->getMessageArray(); - // Only print the help message when this is for the developer, not runtime - if ($this->mPrinter->getIsHtml() || $this->mAction == 'help') - ApiResult :: setContent($errMessage, $this->makeHelpMsg()); + // Only print the help message when this is for the developer, not runtime + if ( $this->mPrinter->getWantsHelp() || $this->mAction == 'help' ) + ApiResult :: setContent( $errMessage, $this->makeHelpMsg() ); + } else { + global $wgShowSQLErrors, $wgShowExceptionDetails; + // + // Something is seriously wrong + // + if ( ( $e instanceof DBQueryError ) && !$wgShowSQLErrors ) { + $info = "Database query error"; } else { - global $wgShowSQLErrors, $wgShowExceptionDetails; - // - // Something is seriously wrong - // - if ( ( $e instanceof DBQueryError ) && !$wgShowSQLErrors ) { - $info = "Database query error"; - } else { - $info = "Exception Caught: {$e->getMessage()}"; - } - - $errMessage = array ( - 'code' => 'internal_api_error_'. get_class($e), - 'info' => $info, - ); - ApiResult :: setContent($errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : "" ); + $info = "Exception Caught: {$e->getMessage()}"; } - $this->getResult()->reset(); - $this->getResult()->disableSizeCheck(); - // Re-add the id - $requestid = $this->getParameter('requestid'); - if(!is_null($requestid)) - $this->getResult()->addValue(null, 'requestid', $requestid); - $this->getResult()->addValue(null, 'error', $errMessage); + $errMessage = array ( + 'code' => 'internal_api_error_' . get_class( $e ), + 'info' => $info, + ); + ApiResult :: setContent( $errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : "" ); + } + + $this->getResult()->reset(); + $this->getResult()->disableSizeCheck(); + // Re-add the id + $requestid = $this->getParameter( 'requestid' ); + if ( !is_null( $requestid ) ) + $this->getResult()->addValue( null, 'requestid', $requestid ); + $this->getResult()->addValue( null, 'error', $errMessage ); return $errMessage['code']; } @@ -494,23 +512,40 @@ class ApiMain extends ApiBase { */ protected function executeAction() { // First add the id to the top element - $requestid = $this->getParameter('requestid'); - if(!is_null($requestid)) - $this->getResult()->addValue(null, 'requestid', $requestid); + $requestid = $this->getParameter( 'requestid' ); + if ( !is_null( $requestid ) ) + $this->getResult()->addValue( null, 'requestid', $requestid ); $params = $this->extractRequestParams(); $this->mShowVersions = $params['version']; $this->mAction = $params['action']; - if( !is_string( $this->mAction ) ) { + if ( !is_string( $this->mAction ) ) { $this->dieUsage( "The API requires a valid action parameter", 'unknown_action' ); } - + // Instantiate the module requested by the user - $module = new $this->mModules[$this->mAction] ($this, $this->mAction); + $module = new $this->mModules[$this->mAction] ( $this, $this->mAction ); + $this->mModule = $module; + + $moduleParams = $module->extractRequestParams(); + + // Die if token required, but not provided (unless there is a gettoken parameter) + $salt = $module->getTokenSalt(); + if ( $salt !== false && !isset( $moduleParams['gettoken'] ) ) + { + if ( !isset( $moduleParams['token'] ) ) { + $this->dieUsageMsg( array( 'missingparam', 'token' ) ); + } else { + global $wgUser; + if ( !$wgUser->matchEditToken( $moduleParams['token'], $salt ) ) { + $this->dieUsageMsg( array( 'sessionfailure' ) ); + } + } + } - if( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) { + if ( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) { // Check for maxlag global $wgShowHostnames; $maxLag = $params['maxlag']; @@ -518,8 +553,7 @@ class ApiMain extends ApiBase { if ( $lag > $maxLag ) { header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) ); header( 'X-Database-Lag: ' . intval( $lag ) ); - // XXX: should we return a 503 HTTP error code like wfMaxlagError() does? - if( $wgShowHostnames ) { + if ( $wgShowHostnames ) { $this->dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' ); } else { $this->dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' ); @@ -528,50 +562,50 @@ class ApiMain extends ApiBase { } } - global $wgUser; - if ($module->isReadMode() && !$wgUser->isAllowed('read')) - $this->dieUsageMsg(array('readrequired')); - if ($module->isWriteMode()) { - if (!$this->mEnableWrite) - $this->dieUsageMsg(array('writedisabled')); - if (!$wgUser->isAllowed('writeapi')) - $this->dieUsageMsg(array('writerequired')); - if (wfReadOnly()) - $this->dieUsageMsg(array('readonlytext')); + global $wgUser, $wgGroupPermissions; + if ( $module->isReadMode() && !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) && !$wgUser->isAllowed( 'read' ) ) + $this->dieUsageMsg( array( 'readrequired' ) ); + if ( $module->isWriteMode() ) { + if ( !$this->mEnableWrite ) + $this->dieUsageMsg( array( 'writedisabled' ) ); + if ( !$wgUser->isAllowed( 'writeapi' ) ) + $this->dieUsageMsg( array( 'writerequired' ) ); + if ( wfReadOnly() ) + $this->dieReadOnly(); } - if (!$this->mInternalMode) { + if ( !$this->mInternalMode ) { // Ignore mustBePosted() for internal calls - if($module->mustBePosted() && !$this->mRequest->wasPosted()) - $this->dieUsage("The {$this->mAction} module requires a POST request", 'mustbeposted'); + if ( $module->mustBePosted() && !$this->mRequest->wasPosted() ) + $this->dieUsageMsg( array ( 'mustbeposted', $this->mAction ) ); // See if custom printer is used $this->mPrinter = $module->getCustomPrinter(); - if (is_null($this->mPrinter)) { + if ( is_null( $this->mPrinter ) ) { // Create an appropriate printer - $this->mPrinter = $this->createPrinterByName($params['format']); + $this->mPrinter = $this->createPrinterByName( $params['format'] ); } - if ($this->mPrinter->getNeedsRawData()) + if ( $this->mPrinter->getNeedsRawData() ) $this->getResult()->setRawMode(); } // Execute $module->profileIn(); $module->execute(); - wfRunHooks('APIAfterExecute', array(&$module)); + wfRunHooks( 'APIAfterExecute', array( &$module ) ); $module->profileOut(); - if (!$this->mInternalMode) { + if ( !$this->mInternalMode ) { // Print result data - $this->printResult(false); + $this->printResult( false ); } } /** * Print results using the current printer */ - protected function printResult($isError) { + protected function printResult( $isError ) { $this->getResult()->cleanUpUTF8(); $printer = $this->mPrinter; $printer->profileIn(); @@ -582,13 +616,13 @@ class ApiMain extends ApiBase { $printer->setUnescapeAmps ( ( $this->mAction == 'help' || $isError ) && $printer->getFormat() == 'XML' && $printer->getIsHtml() ); - $printer->initPrinter($isError); + $printer->initPrinter( $isError ); $printer->execute(); $printer->closePrinter(); $printer->profileOut(); } - + public function isReadMode() { return false; } @@ -668,6 +702,16 @@ class ApiMain extends ApiBase { ); } + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'readonlytext' ), + array( 'code' => 'unknown_format', 'info' => 'Unrecognized format: format' ), + array( 'code' => 'unknown_action', 'info' => 'The API requires a valid action parameter' ), + array( 'code' => 'maxlag', 'info' => 'Waiting for host: x seconds lagged' ), + array( 'code' => 'maxlag', 'info' => 'Waiting for a database server: x seconds lagged' ), + ) ); + } + /** * Returns an array of strings with credits for the API */ @@ -677,6 +721,7 @@ class ApiMain extends ApiBase { ' Roan Kattouw .@home.nl (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 @gmail.com (creator, lead developer Sep 2006-Sep 2007)', '', 'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org', @@ -688,19 +733,37 @@ class ApiMain extends ApiBase { * Override the parent to generate help messages for all available modules. */ public function makeHelpMsg() { + global $wgMemc, $wgAPICacheHelp, $wgAPICacheHelpTimeout; + $this->mPrinter->setHelp(); + // Get help text from cache if present + $key = wfMemcKey( 'apihelp', $this->getModuleName(), + SpecialVersion::getVersion( 'nodb' ) . + $this->getMain()->getShowVersions() ); + if ( $wgAPICacheHelp ) { + $cached = $wgMemc->get( $key ); + if ( $cached ) + return $cached; + } + $retval = $this->reallyMakeHelpMsg(); + if ( $wgAPICacheHelp ) + $wgMemc->set( $key, $retval, $wgAPICacheHelpTimeout ); + return $retval; + } + + public function reallyMakeHelpMsg() { $this->mPrinter->setHelp(); // Use parent to make default message for the main module $msg = parent :: makeHelpMsg(); - $astriks = str_repeat('*** ', 10); + $astriks = str_repeat( '*** ', 10 ); $msg .= "\n\n$astriks Modules $astriks\n\n"; - foreach( $this->mModules as $moduleName => $unused ) { - $module = new $this->mModules[$moduleName] ($this, $moduleName); - $msg .= self::makeHelpMsgHeader($module, 'action'); + foreach ( $this->mModules as $moduleName => $unused ) { + $module = new $this->mModules[$moduleName] ( $this, $moduleName ); + $msg .= self::makeHelpMsgHeader( $module, 'action' ); $msg2 = $module->makeHelpMsg(); - if ($msg2 !== false) + if ( $msg2 !== false ) $msg .= $msg2; $msg .= "\n"; } @@ -708,30 +771,30 @@ class ApiMain extends ApiBase { $msg .= "\n$astriks Permissions $astriks\n\n"; foreach ( self :: $mRights as $right => $rightMsg ) { $groups = User::getGroupsWithPermission( $right ); - $msg .= "* " . $right . " *\n " . wfMsgReplaceArgs( $rightMsg[ 'msg' ], $rightMsg[ 'params' ] ) . + $msg .= "* " . $right . " *\n " . wfMsgReplaceArgs( $rightMsg[ 'msg' ], $rightMsg[ 'params' ] ) . "\nGranted to:\n " . str_replace( "*", "all", implode( ", ", $groups ) ) . "\n"; } $msg .= "\n$astriks Formats $astriks\n\n"; - foreach( $this->mFormats as $formatName => $unused ) { - $module = $this->createPrinterByName($formatName); - $msg .= self::makeHelpMsgHeader($module, 'format'); + foreach ( $this->mFormats as $formatName => $unused ) { + $module = $this->createPrinterByName( $formatName ); + $msg .= self::makeHelpMsgHeader( $module, 'format' ); $msg2 = $module->makeHelpMsg(); - if ($msg2 !== false) + if ( $msg2 !== false ) $msg .= $msg2; $msg .= "\n"; } - $msg .= "\n*** Credits: ***\n " . implode("\n ", $this->getCredits()) . "\n"; + $msg .= "\n*** Credits: ***\n " . implode( "\n ", $this->getCredits() ) . "\n"; return $msg; } - public static function makeHelpMsgHeader($module, $paramName) { + public static function makeHelpMsgHeader( $module, $paramName ) { $modulePrefix = $module->getModulePrefix(); - if (strval($modulePrefix) !== '') + if ( strval( $modulePrefix ) !== '' ) $modulePrefix = "($modulePrefix) "; return "* $paramName={$module->getModuleName()} $modulePrefix*"; @@ -746,9 +809,9 @@ class ApiMain extends ApiBase { * OBSOLETE, use canApiHighLimits() instead */ public function isBot() { - if (!isset ($this->mIsBot)) { + if ( !isset ( $this->mIsBot ) ) { global $wgUser; - $this->mIsBot = $wgUser->isAllowed('bot'); + $this->mIsBot = $wgUser->isAllowed( 'bot' ); } return $this->mIsBot; } @@ -759,9 +822,9 @@ class ApiMain extends ApiBase { * OBSOLETE, use canApiHighLimits() instead */ public function isSysop() { - if (!isset ($this->mIsSysop)) { + if ( !isset ( $this->mIsSysop ) ) { global $wgUser; - $this->mIsSysop = in_array( 'sysop', $wgUser->getGroups()); + $this->mIsSysop = in_array( 'sysop', $wgUser->getGroups() ); } return $this->mIsSysop; @@ -772,9 +835,9 @@ class ApiMain extends ApiBase { * @return bool */ public function canApiHighLimits() { - if (!isset($this->mCanApiHighLimits)) { + if ( !isset( $this->mCanApiHighLimits ) ) { global $wgUser; - $this->mCanApiHighLimits = $wgUser->isAllowed('apihighlimits'); + $this->mCanApiHighLimits = $wgUser->isAllowed( 'apihighlimits' ); } return $this->mCanApiHighLimits; @@ -795,11 +858,10 @@ class ApiMain extends ApiBase { public function getVersion() { $vers = array (); $vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/"; - $vers[] = __CLASS__ . ': $Id: ApiMain.php 69990 2010-07-27 08:44:08Z tstarling $'; + $vers[] = __CLASS__ . ': $Id: ApiMain.php 70066 2010-07-28 05:52:32Z tstarling $'; $vers[] = ApiBase :: getBaseVersion(); $vers[] = ApiFormatBase :: getBaseVersion(); $vers[] = ApiQueryBase :: getBaseVersion(); - $vers[] = ApiFormatFeedWrapper :: getVersion(); // not accessible with format=xxx return $vers; } @@ -845,14 +907,25 @@ class ApiMain extends ApiBase { class UsageException extends Exception { private $mCodestr; + private $mExtraData; - public function __construct($message, $codestr, $code = 0) { - parent :: __construct($message, $code); + public function __construct( $message, $codestr, $code = 0, $extradata = null ) { + parent :: __construct( $message, $code ); $this->mCodestr = $codestr; + $this->mExtraData = $extradata; } public function getCodeString() { return $this->mCodestr; } + public function getMessageArray() { + $result = array ( + 'code' => $this->mCodestr, + 'info' => $this->getMessage() + ); + if ( is_array( $this->mExtraData ) ) + $result = array_merge( $result, $this->mExtraData ); + return $result; + } public function __toString() { return "{$this->getCodeString()}: {$this->getMessage()}"; } diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php index e22d0294..71010de7 100644 --- a/includes/api/ApiMove.php +++ b/includes/api/ApiMove.php @@ -22,9 +22,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } @@ -33,60 +33,68 @@ if (!defined('MEDIAWIKI')) { */ class ApiMove extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { global $wgUser; $params = $this->extractRequestParams(); - if(is_null($params['reason'])) + if ( is_null( $params['reason'] ) ) $params['reason'] = ''; - $this->requireOnlyOneParameter($params, 'from', 'fromid'); - if(!isset($params['to'])) - $this->dieUsageMsg(array('missingparam', 'to')); - if(!isset($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); + $this->requireOnlyOneParameter( $params, 'from', 'fromid' ); + if ( !isset( $params['to'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'to' ) ); - if(isset($params['from'])) + if ( isset( $params['from'] ) ) { - $fromTitle = Title::newFromText($params['from']); - if(!$fromTitle) - $this->dieUsageMsg(array('invalidtitle', $params['from'])); + $fromTitle = Title::newFromText( $params['from'] ); + if ( !$fromTitle ) + $this->dieUsageMsg( array( 'invalidtitle', $params['from'] ) ); } - else if(isset($params['fromid'])) + else if ( isset( $params['fromid'] ) ) { - $fromTitle = Title::newFromID($params['fromid']); - if(!$fromTitle) - $this->dieUsageMsg(array('nosuchpageid', $params['fromid'])); + $fromTitle = Title::newFromID( $params['fromid'] ); + if ( !$fromTitle ) + $this->dieUsageMsg( array( 'nosuchpageid', $params['fromid'] ) ); } - if(!$fromTitle->exists()) - $this->dieUsageMsg(array('notanarticle')); + + if ( !$fromTitle->exists() ) + $this->dieUsageMsg( array( 'notanarticle' ) ); $fromTalk = $fromTitle->getTalkPage(); - $toTitle = Title::newFromText($params['to']); - if(!$toTitle) - $this->dieUsageMsg(array('invalidtitle', $params['to'])); + $toTitle = Title::newFromText( $params['to'] ); + if ( !$toTitle ) + $this->dieUsageMsg( array( 'invalidtitle', $params['to'] ) ); $toTalk = $toTitle->getTalkPage(); - # Move the page + if ( $toTitle->getNamespace() == NS_FILE + && !RepoGroup::singleton()->getLocalRepo()->findFile( $toTitle ) + && wfFindFile( $toTitle ) ) + { + if ( !$params['ignorewarnings'] && $wgUser->isAllowed( 'reupload-shared' ) ) { + $this->dieUsageMsg( array( 'sharedfile-exists' ) ); + } elseif ( !$wgUser->isAllowed( 'reupload-shared' ) ) { + $this->dieUsageMsg( array( 'cantoverwrite-sharedfile' ) ); + } + } + + // Move the page $hookErr = null; - $retval = $fromTitle->moveTo($toTitle, true, $params['reason'], !$params['noredirect']); - if($retval !== true) - $this->dieUsageMsg(reset($retval)); + $retval = $fromTitle->moveTo( $toTitle, true, $params['reason'], !$params['noredirect'] ); + if ( $retval !== true ) + $this->dieUsageMsg( reset( $retval ) ); - $r = array('from' => $fromTitle->getPrefixedText(), 'to' => $toTitle->getPrefixedText(), 'reason' => $params['reason']); - if(!$params['noredirect'] || !$wgUser->isAllowed('suppressredirect')) + $r = array( 'from' => $fromTitle->getPrefixedText(), 'to' => $toTitle->getPrefixedText(), 'reason' => $params['reason'] ); + if ( !$params['noredirect'] || !$wgUser->isAllowed( 'suppressredirect' ) ) $r['redirectcreated'] = ''; - # Move the talk page - if($params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage()) + // Move the talk page + if ( $params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage() ) { - $retval = $fromTalk->moveTo($toTalk, true, $params['reason'], !$params['noredirect']); - if($retval === true) + $retval = $fromTalk->moveTo( $toTalk, true, $params['reason'], !$params['noredirect'] ); + if ( $retval === true ) { $r['talkfrom'] = $fromTalk->getPrefixedText(); $r['talkto'] = $toTalk->getPrefixedText(); @@ -94,55 +102,55 @@ class ApiMove extends ApiBase { // We're not gonna dieUsage() on failure, since we already changed something else { - $parsed = $this->parseMsg(reset($retval)); + $parsed = $this->parseMsg( reset( $retval ) ); $r['talkmove-error-code'] = $parsed['code']; $r['talkmove-error-info'] = $parsed['info']; } } - # Move subpages - if($params['movesubpages']) + // Move subpages + if ( $params['movesubpages'] ) { - $r['subpages'] = $this->moveSubpages($fromTitle, $toTitle, - $params['reason'], $params['noredirect']); - $this->getResult()->setIndexedTagName($r['subpages'], 'subpage'); - if($params['movetalk']) + $r['subpages'] = $this->moveSubpages( $fromTitle, $toTitle, + $params['reason'], $params['noredirect'] ); + $this->getResult()->setIndexedTagName( $r['subpages'], 'subpage' ); + if ( $params['movetalk'] ) { - $r['subpages-talk'] = $this->moveSubpages($fromTalk, $toTalk, - $params['reason'], $params['noredirect']); - $this->getResult()->setIndexedTagName($r['subpages-talk'], 'subpage'); + $r['subpages-talk'] = $this->moveSubpages( $fromTalk, $toTalk, + $params['reason'], $params['noredirect'] ); + $this->getResult()->setIndexedTagName( $r['subpages-talk'], 'subpage' ); } } - # Watch pages - if($params['watch'] || $wgUser->getOption('watchmoves')) + // Watch pages + if ( $params['watch'] || $wgUser->getOption( 'watchmoves' ) ) { - $wgUser->addWatch($fromTitle); - $wgUser->addWatch($toTitle); + $wgUser->addWatch( $fromTitle ); + $wgUser->addWatch( $toTitle ); } - else if($params['unwatch']) + else if ( $params['unwatch'] ) { - $wgUser->removeWatch($fromTitle); - $wgUser->removeWatch($toTitle); + $wgUser->removeWatch( $fromTitle ); + $wgUser->removeWatch( $toTitle ); } - $this->getResult()->addValue(null, $this->getModuleName(), $r); + $this->getResult()->addValue( null, $this->getModuleName(), $r ); } - - public function moveSubpages($fromTitle, $toTitle, $reason, $noredirect) + + public function moveSubpages( $fromTitle, $toTitle, $reason, $noredirect ) { $retval = array(); - $success = $fromTitle->moveSubpages($toTitle, true, $reason, !$noredirect); - if(isset($success[0])) - return array('error' => $this->parseMsg($success)); + $success = $fromTitle->moveSubpages( $toTitle, true, $reason, !$noredirect ); + if ( isset( $success[0] ) ) + return array( 'error' => $this->parseMsg( $success ) ); else { // At least some pages could be moved // Report each of them separately - foreach($success as $oldTitle => $newTitle) + foreach ( $success as $oldTitle => $newTitle ) { - $r = array('from' => $oldTitle); - if(is_array($newTitle)) - $r['error'] = $this->parseMsg(reset($newTitle)); + $r = array( 'from' => $oldTitle ); + if ( is_array( $newTitle ) ) + $r['error'] = $this->parseMsg( reset( $newTitle ) ); else // Success $r['to'] = $newTitle; @@ -152,7 +160,9 @@ class ApiMove extends ApiBase { return $retval; } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; @@ -171,7 +181,8 @@ class ApiMove extends ApiBase { 'movesubpages' => false, 'noredirect' => false, 'watch' => false, - 'unwatch' => false + 'unwatch' => false, + 'ignorewarnings' => false ); } @@ -186,7 +197,8 @@ class ApiMove extends ApiBase { 'movesubpages' => 'Move subpages, if applicable', 'noredirect' => 'Don\'t create a redirect', 'watch' => 'Add the page and the redirect to your watchlist', - 'unwatch' => 'Remove the page and the redirect from your watchlist' + 'unwatch' => 'Remove the page and the redirect from your watchlist', + 'ignorewarnings' => 'Ignore any warnings' ); } @@ -195,6 +207,21 @@ class ApiMove extends ApiBase { 'Move a page.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'to' ), + array( 'invalidtitle', 'from' ), + array( 'nosuchpageid', 'fromid' ), + array( 'notanarticle' ), + array( 'invalidtitle', 'to' ), + array( 'sharedfile-exists' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { return array ( @@ -203,6 +230,6 @@ class ApiMove extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiMove.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiMove.php 62810 2010-02-22 03:34:56Z mah $'; } } diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php index d2e6ea21..e145d80c 100644 --- a/includes/api/ApiOpenSearch.php +++ b/includes/api/ApiOpenSearch.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -33,34 +33,38 @@ if (!defined('MEDIAWIKI')) { */ class ApiOpenSearch extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function getCustomPrinter() { - return $this->getMain()->createPrinterByName('json'); + return $this->getMain()->createPrinterByName( 'json' ); } public function execute() { - global $wgEnableMWSuggest; + global $wgEnableOpenSearchSuggest, $wgSearchSuggestCacheExpiry; $params = $this->extractRequestParams(); $search = $params['search']; $limit = $params['limit']; $namespaces = $params['namespace']; $suggest = $params['suggest']; - # $wgEnableMWSuggest hit incoming when $wgEnableMWSuggest is disabled - if( $suggest && !$wgEnableMWSuggest ) return; - - // Open search results may be stored for a very long time - $this->getMain()->setCacheMaxAge(1200); - $this->getMain()->setCacheMode( 'public' ); - $srchres = PrefixSearch::titleSearch( $search, $limit, $namespaces ); + // MWSuggest or similar hit + if ( $suggest && !$wgEnableOpenSearchSuggest ) + $srchres = array(); + else { + // Open search results may be stored for a very long + // time + $this->getMain()->setCacheMaxAge( $wgSearchSuggestCacheExpiry ); + $this->getMain()->setCacheMode( 'public' ); + $srchres = PrefixSearch::titleSearch( $search, $limit, + $namespaces ); + } // Set top level elements $result = $this->getResult(); - $result->addValue(null, 0, $search); - $result->addValue(null, 1, $srchres); + $result->addValue( null, 0, $search ); + $result->addValue( null, 1, $srchres ); } public function getAllowedParams() { @@ -87,7 +91,7 @@ class ApiOpenSearch extends ApiBase { 'search' => 'Search string', 'limit' => 'Maximum amount of results to return', 'namespace' => 'Namespaces to search', - 'suggest' => 'Do nothing if $wgEnableMWSuggest is false', + 'suggest' => 'Do nothing if $wgEnableOpenSearchSuggest is false', ); } @@ -102,6 +106,6 @@ class ApiOpenSearch extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiOpenSearch.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiOpenSearch.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php index 6b9e90b8..361f1d8b 100644 --- a/includes/api/ApiPageSet.php +++ b/includes/api/ApiPageSet.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -58,8 +58,8 @@ class ApiPageSet extends ApiQueryBase { * @param $query ApiQuery * @param $resolveRedirects bool Whether redirects should be resolved */ - public function __construct($query, $resolveRedirects = false) { - parent :: __construct($query, 'query'); + public function __construct( $query, $resolveRedirects = false ) { + parent :: __construct( $query, 'query' ); $this->mAllPages = array (); $this->mTitles = array(); @@ -75,10 +75,10 @@ class ApiPageSet extends ApiQueryBase { $this->mRequestedPageFields = array (); $this->mResolveRedirects = $resolveRedirects; - if($resolveRedirects) + if ( $resolveRedirects ) $this->mPendingRedirectIDs = array(); - $this->mFakePageId = -1; + $this->mFakePageId = - 1; } /** @@ -94,7 +94,7 @@ class ApiPageSet extends ApiQueryBase { * before execute() * @param $fieldName string Field name */ - public function requestField($fieldName) { + public function requestField( $fieldName ) { $this->mRequestedPageFields[$fieldName] = null; } @@ -104,7 +104,7 @@ class ApiPageSet extends ApiQueryBase { * @param $fieldName string Field name * @return mixed Field value */ - public function getCustomField($fieldName) { + public function getCustomField( $fieldName ) { return $this->mRequestedPageFields[$fieldName]; } @@ -123,14 +123,14 @@ class ApiPageSet extends ApiQueryBase { 'page_id' => null, ); - if ($this->mResolveRedirects) + if ( $this->mResolveRedirects ) $pageFlds['page_is_redirect'] = null; // only store non-default fields - $this->mRequestedPageFields = array_diff_key($this->mRequestedPageFields, $pageFlds); + $this->mRequestedPageFields = array_diff_key( $this->mRequestedPageFields, $pageFlds ); - $pageFlds = array_merge($pageFlds, $this->mRequestedPageFields); - return array_keys($pageFlds); + $pageFlds = array_merge( $pageFlds, $this->mRequestedPageFields ); + return array_keys( $pageFlds ); } /** @@ -156,7 +156,7 @@ class ApiPageSet extends ApiQueryBase { * @return int */ public function getTitleCount() { - return count($this->mTitles); + return count( $this->mTitles ); } /** @@ -172,7 +172,7 @@ class ApiPageSet extends ApiQueryBase { * @return int */ public function getGoodTitleCount() { - return count($this->mGoodTitles); + return count( $this->mGoodTitles ); } /** @@ -249,7 +249,7 @@ class ApiPageSet extends ApiQueryBase { * @return int */ public function getRevisionCount() { - return count($this->getRevisionIDs()); + return count( $this->getRevisionIDs() ); } /** @@ -261,32 +261,32 @@ class ApiPageSet extends ApiQueryBase { // Only one of the titles/pageids/revids is allowed at the same time $dataSource = null; - if (isset ($params['titles'])) + if ( isset ( $params['titles'] ) ) $dataSource = 'titles'; - if (isset ($params['pageids'])) { - if (isset ($dataSource)) - $this->dieUsage("Cannot use 'pageids' at the same time as '$dataSource'", 'multisource'); + if ( isset ( $params['pageids'] ) ) { + if ( isset ( $dataSource ) ) + $this->dieUsage( "Cannot use 'pageids' at the same time as '$dataSource'", 'multisource' ); $dataSource = 'pageids'; } - if (isset ($params['revids'])) { - if (isset ($dataSource)) - $this->dieUsage("Cannot use 'revids' at the same time as '$dataSource'", 'multisource'); + if ( isset ( $params['revids'] ) ) { + if ( isset ( $dataSource ) ) + $this->dieUsage( "Cannot use 'revids' at the same time as '$dataSource'", 'multisource' ); $dataSource = 'revids'; } - switch ($dataSource) { + switch ( $dataSource ) { case 'titles' : - $this->initFromTitles($params['titles']); + $this->initFromTitles( $params['titles'] ); break; case 'pageids' : - $this->initFromPageIds($params['pageids']); + $this->initFromPageIds( $params['pageids'] ); break; case 'revids' : - if($this->mResolveRedirects) - $this->setWarning('Redirect resolution cannot be used together with the revids= parameter. '. - 'Any redirects the revids= point to have not been resolved.'); + if ( $this->mResolveRedirects ) + $this->setWarning( 'Redirect resolution cannot be used together with the revids= parameter. ' . + 'Any redirects the revids= point to have not been resolved.' ); $this->mResolveRedirects = false; - $this->initFromRevIDs($params['revids']); + $this->initFromRevIDs( $params['revids'] ); break; default : // Do nothing - some queries do not need any of the data sources. @@ -299,9 +299,9 @@ class ApiPageSet extends ApiQueryBase { * Populate this PageSet from a list of Titles * @param $titles array of Title objects */ - public function populateFromTitles($titles) { + public function populateFromTitles( $titles ) { $this->profileIn(); - $this->initFromTitles($titles); + $this->initFromTitles( $titles ); $this->profileOut(); } @@ -309,9 +309,9 @@ class ApiPageSet extends ApiQueryBase { * Populate this PageSet from a list of page IDs * @param $pageIDs array of page IDs */ - public function populateFromPageIDs($pageIDs) { + public function populateFromPageIDs( $pageIDs ) { $this->profileIn(); - $this->initFromPageIds($pageIDs); + $this->initFromPageIds( $pageIDs ); $this->profileOut(); } @@ -320,9 +320,9 @@ class ApiPageSet extends ApiQueryBase { * @param $db Database object * @param $queryResult Query result object */ - public function populateFromQueryResult($db, $queryResult) { + public function populateFromQueryResult( $db, $queryResult ) { $this->profileIn(); - $this->initFromQueryResult($db, $queryResult); + $this->initFromQueryResult( $db, $queryResult ); $this->profileOut(); } @@ -330,9 +330,9 @@ class ApiPageSet extends ApiQueryBase { * Populate this PageSet from a list of revision IDs * @param $revIDs array of revision IDs */ - public function populateFromRevisionIDs($revIDs) { + public function populateFromRevisionIDs( $revIDs ) { $this->profileIn(); - $this->initFromRevIDs($revIDs); + $this->initFromRevIDs( $revIDs ); $this->profileOut(); } @@ -340,22 +340,22 @@ class ApiPageSet extends ApiQueryBase { * Extract all requested fields from the row received from the database * @param $row Result row */ - public function processDbRow($row) { + public function processDbRow( $row ) { // Store Title object in various data structures - $title = Title :: makeTitle($row->page_namespace, $row->page_title); + $title = Title :: makeTitle( $row->page_namespace, $row->page_title ); - $pageId = intval($row->page_id); + $pageId = intval( $row->page_id ); $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId; $this->mTitles[] = $title; - if ($this->mResolveRedirects && $row->page_is_redirect == '1') { + if ( $this->mResolveRedirects && $row->page_is_redirect == '1' ) { $this->mPendingRedirectIDs[$pageId] = $title; } else { $this->mGoodTitles[$pageId] = $title; } - foreach ($this->mRequestedPageFields as $fieldName => & $fieldValues) + foreach ( $this->mRequestedPageFields as $fieldName => & $fieldValues ) $fieldValues[$pageId] = $row-> $fieldName; } @@ -384,24 +384,24 @@ class ApiPageSet extends ApiQueryBase { * * @param $titles array of Title objects or strings */ - private function initFromTitles($titles) { + private function initFromTitles( $titles ) { // Get validated and normalized title objects - $linkBatch = $this->processTitlesArray($titles); - if($linkBatch->isEmpty()) + $linkBatch = $this->processTitlesArray( $titles ); + if ( $linkBatch->isEmpty() ) return; $db = $this->getDB(); - $set = $linkBatch->constructSet('page', $db); + $set = $linkBatch->constructSet( 'page', $db ); // Get pageIDs data from the `page` table $this->profileDBIn(); - $res = $db->select('page', $this->getPageTableFields(), $set, - __METHOD__); + $res = $db->select( 'page', $this->getPageTableFields(), $set, + __METHOD__ ); $this->profileDBOut(); // Hack: get the ns:titles stored in array(ns => array(titles)) format - $this->initFromQueryResult($db, $res, $linkBatch->data, true); // process Titles + $this->initFromQueryResult( $db, $res, $linkBatch->data, true ); // process Titles // Resolve any found redirects $this->resolvePendingRedirects(); @@ -411,11 +411,11 @@ class ApiPageSet extends ApiQueryBase { * Does the same as initFromTitles(), but is based on page IDs instead * @param $pageids array of page IDs */ - private function initFromPageIds($pageids) { - if(!count($pageids)) + private function initFromPageIds( $pageids ) { + if ( !count( $pageids ) ) return; - $pageids = array_map('intval', $pageids); // paranoia + $pageids = array_map( 'intval', $pageids ); // paranoia $set = array ( 'page_id' => $pageids ); @@ -423,12 +423,12 @@ class ApiPageSet extends ApiQueryBase { // Get pageIDs data from the `page` table $this->profileDBIn(); - $res = $db->select('page', $this->getPageTableFields(), $set, - __METHOD__); + $res = $db->select( 'page', $this->getPageTableFields(), $set, + __METHOD__ ); $this->profileDBOut(); - $remaining = array_flip($pageids); - $this->initFromQueryResult($db, $res, $remaining, false); // process PageIDs + $remaining = array_flip( $pageids ); + $this->initFromQueryResult( $db, $res, $remaining, false ); // process PageIDs // Resolve any found redirects $this->resolvePendingRedirects(); @@ -445,34 +445,34 @@ class ApiPageSet extends ApiQueryBase { * If true, treat $remaining as an array of [ns][title] * If false, treat it as an array of [pageIDs] */ - private function initFromQueryResult($db, $res, &$remaining = null, $processTitles = null) { - if (!is_null($remaining) && is_null($processTitles)) - ApiBase :: dieDebug(__METHOD__, 'Missing $processTitles parameter when $remaining is provided'); + private function initFromQueryResult( $db, $res, &$remaining = null, $processTitles = null ) { + if ( !is_null( $remaining ) && is_null( $processTitles ) ) + ApiBase :: dieDebug( __METHOD__, 'Missing $processTitles parameter when $remaining is provided' ); - while ($row = $db->fetchObject($res)) { + while ( $row = $db->fetchObject( $res ) ) { - $pageId = intval($row->page_id); + $pageId = intval( $row->page_id ); // Remove found page from the list of remaining items - if (isset($remaining)) { - if ($processTitles) - unset ($remaining[$row->page_namespace][$row->page_title]); + if ( isset( $remaining ) ) { + if ( $processTitles ) + unset ( $remaining[$row->page_namespace][$row->page_title] ); else - unset ($remaining[$pageId]); + unset ( $remaining[$pageId] ); } // Store any extra fields requested by modules - $this->processDbRow($row); + $this->processDbRow( $row ); } - $db->freeResult($res); + $db->freeResult( $res ); - if(isset($remaining)) { + if ( isset( $remaining ) ) { // Any items left in the $remaining list are added as missing - if($processTitles) { + if ( $processTitles ) { // The remaining titles in $remaining are non-existent pages - foreach ($remaining as $ns => $dbkeys) { + foreach ( $remaining as $ns => $dbkeys ) { foreach ( $dbkeys as $dbkey => $unused ) { - $title = Title :: makeTitle($ns, $dbkey); + $title = Title :: makeTitle( $ns, $dbkey ); $this->mAllPages[$ns][$dbkey] = $this->mFakePageId; $this->mMissingTitles[$this->mFakePageId] = $title; $this->mFakePageId--; @@ -483,10 +483,10 @@ class ApiPageSet extends ApiQueryBase { else { // The remaining pageids do not exist - if(!$this->mMissingPageIDs) - $this->mMissingPageIDs = array_keys($remaining); + if ( !$this->mMissingPageIDs ) + $this->mMissingPageIDs = array_keys( $remaining ); else - $this->mMissingPageIDs = array_merge($this->mMissingPageIDs, array_keys($remaining)); + $this->mMissingPageIDs = array_merge( $this->mMissingPageIDs, array_keys( $remaining ) ); } } } @@ -496,37 +496,37 @@ class ApiPageSet extends ApiQueryBase { * instead * @param $revids array of revision IDs */ - private function initFromRevIDs($revids) { + private function initFromRevIDs( $revids ) { - if(!count($revids)) + if ( !count( $revids ) ) return; - $revids = array_map('intval', $revids); // paranoia + $revids = array_map( 'intval', $revids ); // paranoia $db = $this->getDB(); $pageids = array(); - $remaining = array_flip($revids); + $remaining = array_flip( $revids ); - $tables = array('revision', 'page'); - $fields = array('rev_id', 'rev_page'); - $where = array('rev_id' => $revids, 'rev_page = page_id'); + $tables = array( 'revision', 'page' ); + $fields = array( 'rev_id', 'rev_page' ); + $where = array( 'rev_id' => $revids, 'rev_page = page_id' ); // Get pageIDs data from the `page` table $this->profileDBIn(); - $res = $db->select($tables, $fields, $where, __METHOD__); - while ($row = $db->fetchObject($res)) { - $revid = intval($row->rev_id); - $pageid = intval($row->rev_page); + $res = $db->select( $tables, $fields, $where, __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { + $revid = intval( $row->rev_id ); + $pageid = intval( $row->rev_page ); $this->mGoodRevIDs[$revid] = $pageid; $pageids[$pageid] = ''; - unset($remaining[$revid]); + unset( $remaining[$revid] ); } - $db->freeResult($res); + $db->freeResult( $res ); $this->profileDBOut(); - $this->mMissingRevIDs = array_keys($remaining); + $this->mMissingRevIDs = array_keys( $remaining ); // Populate all the page information - $this->initFromPageIds(array_keys($pageids)); + $this->initFromPageIds( array_keys( $pageids ) ); } /** @@ -536,32 +536,32 @@ class ApiPageSet extends ApiQueryBase { */ private function resolvePendingRedirects() { - if($this->mResolveRedirects) { + if ( $this->mResolveRedirects ) { $db = $this->getDB(); $pageFlds = $this->getPageTableFields(); // Repeat until all redirects have been resolved // The infinite loop is prevented by keeping all known pages in $this->mAllPages - while ($this->mPendingRedirectIDs) { + while ( $this->mPendingRedirectIDs ) { // Resolve redirects by querying the pagelinks table, and repeat the process // Create a new linkBatch object for the next pass $linkBatch = $this->getRedirectTargets(); - if ($linkBatch->isEmpty()) + if ( $linkBatch->isEmpty() ) break; - $set = $linkBatch->constructSet('page', $db); - if($set === false) + $set = $linkBatch->constructSet( 'page', $db ); + if ( $set === false ) break; // Get pageIDs data from the `page` table $this->profileDBIn(); - $res = $db->select('page', $pageFlds, $set, __METHOD__); + $res = $db->select( 'page', $pageFlds, $set, __METHOD__ ); $this->profileDBOut(); // Hack: get the ns:titles stored in array(ns => array(titles)) format - $this->initFromQueryResult($db, $res, $linkBatch->data, true); + $this->initFromQueryResult( $db, $res, $linkBatch->data, true ); } } } @@ -578,40 +578,40 @@ class ApiPageSet extends ApiQueryBase { $db = $this->getDB(); $this->profileDBIn(); - $res = $db->select('redirect', array( + $res = $db->select( 'redirect', array( 'rd_from', 'rd_namespace', 'rd_title' - ), array('rd_from' => array_keys($this->mPendingRedirectIDs)), + ), array( 'rd_from' => array_keys( $this->mPendingRedirectIDs ) ), __METHOD__ ); $this->profileDBOut(); - while($row = $db->fetchObject($res)) + while ( $row = $db->fetchObject( $res ) ) { - $rdfrom = intval($row->rd_from); + $rdfrom = intval( $row->rd_from ); $from = $this->mPendingRedirectIDs[$rdfrom]->getPrefixedText(); - $to = Title::makeTitle($row->rd_namespace, $row->rd_title)->getPrefixedText(); - unset($this->mPendingRedirectIDs[$rdfrom]); - if(!isset($this->mAllPages[$row->rd_namespace][$row->rd_title])) - $lb->add($row->rd_namespace, $row->rd_title); + $to = Title::makeTitle( $row->rd_namespace, $row->rd_title )->getPrefixedText(); + unset( $this->mPendingRedirectIDs[$rdfrom] ); + if ( !isset( $this->mAllPages[$row->rd_namespace][$row->rd_title] ) ) + $lb->add( $row->rd_namespace, $row->rd_title ); $this->mRedirectTitles[$from] = $to; } - $db->freeResult($res); - if($this->mPendingRedirectIDs) + $db->freeResult( $res ); + if ( $this->mPendingRedirectIDs ) { - # We found pages that aren't in the redirect table - # Add them - foreach($this->mPendingRedirectIDs as $id => $title) + // We found pages that aren't in the redirect table + // Add them + foreach ( $this->mPendingRedirectIDs as $id => $title ) { - $article = new Article($title); + $article = new Article( $title ); $rt = $article->insertRedirect(); - if(!$rt) - # What the hell. Let's just ignore this + if ( !$rt ) + // What the hell. Let's just ignore this continue; - $lb->addObj($rt); + $lb->addObj( $rt ); $this->mRedirectTitles[$title->getPrefixedText()] = $rt->getPrefixedText(); - unset($this->mPendingRedirectIDs[$id]); + unset( $this->mPendingRedirectIDs[$id] ); } } return $lb; @@ -626,32 +626,32 @@ class ApiPageSet extends ApiQueryBase { * @param $titles array of Title objects or strings * @return LinkBatch */ - private function processTitlesArray($titles) { + private function processTitlesArray( $titles ) { $linkBatch = new LinkBatch(); - foreach ($titles as $title) { + foreach ( $titles as $title ) { - $titleObj = is_string($title) ? Title :: newFromText($title) : $title; - if (!$titleObj) + $titleObj = is_string( $title ) ? Title :: newFromText( $title ) : $title; + if ( !$titleObj ) { - # Handle invalid titles gracefully + // Handle invalid titles gracefully $this->mAllpages[0][$title] = $this->mFakePageId; $this->mInvalidTitles[$this->mFakePageId] = $title; $this->mFakePageId--; continue; // There's nothing else we can do } $iw = $titleObj->getInterwiki(); - if (strval($iw) !== '') { + if ( strval( $iw ) !== '' ) { // This title is an interwiki link. $this->mInterwikiTitles[$titleObj->getPrefixedText()] = $iw; } else { // Validation - if ($titleObj->getNamespace() < 0) - $this->setWarning("No support for special pages has been implemented"); + if ( $titleObj->getNamespace() < 0 ) + $this->setWarning( "No support for special pages has been implemented" ); else - $linkBatch->addObj($titleObj); + $linkBatch->addObj( $titleObj ); } // Make sure we remember the original title that was @@ -659,7 +659,7 @@ class ApiPageSet extends ApiQueryBase { // titles with the originally requested when e.g. the // namespace is localized or the capitalization is // different - if (is_string($title) && $title !== $titleObj->getPrefixedText()) { + if ( is_string( $title ) && $title !== $titleObj->getPrefixedText() ) { $this->mNormalizedTitles[$title] = $titleObj->getPrefixedText(); } } @@ -691,7 +691,14 @@ class ApiPageSet extends ApiQueryBase { ); } + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'multisource', 'info' => "Cannot use 'pageids' at the same time as 'dataSource'" ), + array( 'code' => 'multisource', 'info' => "Cannot use 'revids' at the same time as 'dataSource'" ), + ) ); + } + public function getVersion() { - return __CLASS__ . ': $Id: ApiPageSet.php 47424 2009-02-18 05:29:11Z werdna $'; + return __CLASS__ . ': $Id: ApiPageSet.php 62410 2010-02-13 01:21:52Z reedy $'; } } diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php index d710c206..8fe2cad2 100644 --- a/includes/api/ApiParamInfo.php +++ b/includes/api/ApiParamInfo.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -33,123 +33,149 @@ if (!defined('MEDIAWIKI')) { */ class ApiParamInfo extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { // Get parameters $params = $this->extractRequestParams(); $result = $this->getResult(); - $queryObj = new ApiQuery($this->getMain(), 'query'); + $queryObj = new ApiQuery( $this->getMain(), 'query' ); $r = array(); - if(is_array($params['modules'])) + if ( is_array( $params['modules'] ) ) { $modArr = $this->getMain()->getModules(); - foreach($params['modules'] as $m) + $r['modules'] = array(); + foreach ( $params['modules'] as $m ) { - if(!isset($modArr[$m])) + if ( !isset( $modArr[$m] ) ) { - $r['modules'][] = array('name' => $m, 'missing' => ''); + $r['modules'][] = array( 'name' => $m, 'missing' => '' ); continue; } - $obj = new $modArr[$m]($this->getMain(), $m); - $a = $this->getClassInfo($obj); + $obj = new $modArr[$m]( $this->getMain(), $m ); + $a = $this->getClassInfo( $obj ); $a['name'] = $m; $r['modules'][] = $a; } - $result->setIndexedTagName($r['modules'], 'module'); + $result->setIndexedTagName( $r['modules'], 'module' ); } - if(is_array($params['querymodules'])) + if ( is_array( $params['querymodules'] ) ) { $qmodArr = $queryObj->getModules(); - foreach($params['querymodules'] as $qm) + $r['querymodules'] = array(); + foreach ( $params['querymodules'] as $qm ) { - if(!isset($qmodArr[$qm])) + if ( !isset( $qmodArr[$qm] ) ) { - $r['querymodules'][] = array('name' => $qm, 'missing' => ''); + $r['querymodules'][] = array( 'name' => $qm, 'missing' => '' ); continue; } - $obj = new $qmodArr[$qm]($this, $qm); - $a = $this->getClassInfo($obj); + $obj = new $qmodArr[$qm]( $this, $qm ); + $a = $this->getClassInfo( $obj ); $a['name'] = $qm; $r['querymodules'][] = $a; } - $result->setIndexedTagName($r['querymodules'], 'module'); + $result->setIndexedTagName( $r['querymodules'], 'module' ); } - if($params['mainmodule']) - $r['mainmodule'] = $this->getClassInfo($this->getMain()); - if($params['pagesetmodule']) + if ( $params['mainmodule'] ) + $r['mainmodule'] = $this->getClassInfo( $this->getMain() ); + if ( $params['pagesetmodule'] ) { - $pageSet = new ApiPageSet($queryObj); - $r['pagesetmodule'] = $this->getClassInfo($pageSet); + $pageSet = new ApiPageSet( $queryObj ); + $r['pagesetmodule'] = $this->getClassInfo( $pageSet ); } - $result->addValue(null, $this->getModuleName(), $r); + $result->addValue( null, $this->getModuleName(), $r ); } - function getClassInfo($obj) + function getClassInfo( $obj ) { $result = $this->getResult(); - $retval['classname'] = get_class($obj); - $retval['description'] = (is_array($obj->getDescription()) ? implode("\n", $obj->getDescription()) : $obj->getDescription()); + $retval['classname'] = get_class( $obj ); + $retval['description'] = implode( "\n", (array)$obj->getDescription() ); + $retval['version'] = implode( "\n", (array)$obj->getVersion() ); $retval['prefix'] = $obj->getModulePrefix(); - if($obj->isReadMode()) + + if ( $obj->isReadMode() ) $retval['readrights'] = ''; - if($obj->isWriteMode()) + if ( $obj->isWriteMode() ) $retval['writerights'] = ''; - if($obj->mustBePosted()) + if ( $obj->mustBePosted() ) $retval['mustbeposted'] = ''; + if ( $obj instanceof ApiQueryGeneratorBase ) + $retval['generator'] = ''; + $allowedParams = $obj->getFinalParams(); - if(!is_array($allowedParams)) + if ( !is_array( $allowedParams ) ) return $retval; + $retval['parameters'] = array(); $paramDesc = $obj->getFinalParamDescription(); - foreach($allowedParams as $n => $p) + foreach ( $allowedParams as $n => $p ) { - $a = array('name' => $n); - if(!is_array($p)) + $a = array( 'name' => $n ); + if ( isset( $paramDesc[$n] ) ) + $a['description'] = implode( "\n", (array)$paramDesc[$n] ); + if ( isset( $p[ApiBase::PARAM_DEPRECATED] ) && $p[ApiBase::PARAM_DEPRECATED] ) + $a['deprecated'] = ''; + if ( !is_array( $p ) ) { - if(is_bool($p)) + if ( is_bool( $p ) ) { $a['type'] = 'bool'; - $a['default'] = ($p ? 'true' : 'false'); + $a['default'] = ( $p ? 'true' : 'false' ); + } + else if ( is_string( $p ) || is_null( $p ) ) + { + $a['type'] = 'string'; + $a['default'] = strval( $p ); + } + else if ( is_int( $p ) ) + { + $a['type'] = 'integer'; + $a['default'] = intval( $p ); } - if(is_string($p)) - $a['default'] = $p; $retval['parameters'][] = $a; continue; } - if(isset($p[ApiBase::PARAM_DFLT])) + if ( isset( $p[ApiBase::PARAM_DFLT] ) ) $a['default'] = $p[ApiBase::PARAM_DFLT]; - if(isset($p[ApiBase::PARAM_ISMULTI])) - if($p[ApiBase::PARAM_ISMULTI]) + if ( isset( $p[ApiBase::PARAM_ISMULTI] ) ) + if ( $p[ApiBase::PARAM_ISMULTI] ) { $a['multi'] = ''; $a['limit'] = $this->getMain()->canApiHighLimits() ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_SML1; } - if(isset($p[ApiBase::PARAM_ALLOW_DUPLICATES])) - if($p[ApiBase::PARAM_ALLOW_DUPLICATES]) + + if ( isset( $p[ApiBase::PARAM_ALLOW_DUPLICATES] ) ) + if ( $p[ApiBase::PARAM_ALLOW_DUPLICATES] ) $a['allowsduplicates'] = ''; - if(isset($p[ApiBase::PARAM_TYPE])) + + if ( isset( $p[ApiBase::PARAM_TYPE] ) ) { $a['type'] = $p[ApiBase::PARAM_TYPE]; - if(is_array($a['type'])) - $result->setIndexedTagName($a['type'], 't'); + if ( is_array( $a['type'] ) ) + $result->setIndexedTagName( $a['type'], 't' ); } - if(isset($p[ApiBase::PARAM_MAX])) + if ( isset( $p[ApiBase::PARAM_MAX] ) ) $a['max'] = $p[ApiBase::PARAM_MAX]; - if(isset($p[ApiBase::PARAM_MAX2])) + if ( isset( $p[ApiBase::PARAM_MAX2] ) ) $a['highmax'] = $p[ApiBase::PARAM_MAX2]; - if(isset($p[ApiBase::PARAM_MIN])) + if ( isset( $p[ApiBase::PARAM_MIN] ) ) $a['min'] = $p[ApiBase::PARAM_MIN]; - if(isset($paramDesc[$n])) - $a['description'] = (is_array($paramDesc[$n]) ? implode("\n", $paramDesc[$n]) : $paramDesc[$n]); $retval['parameters'][] = $a; } - $result->setIndexedTagName($retval['parameters'], 'param'); + $result->setIndexedTagName( $retval['parameters'], 'param' ); + + // Errors + $retval['errors'] = $this->parseErrors( $obj->getPossibleErrors() ); + + $result->setIndexedTagName( $retval['errors'], 'error' ); + return $retval; } @@ -190,6 +216,6 @@ class ApiParamInfo extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiParamInfo.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiParamInfo.php 62336 2010-02-11 22:22:20Z reedy $'; } } diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php index c8cd07fe..db389bdb 100644 --- a/includes/api/ApiParse.php +++ b/includes/api/ApiParse.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -33,8 +33,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiParse extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { @@ -47,119 +47,140 @@ class ApiParse extends ApiBase { $title = $params['title']; $page = $params['page']; $oldid = $params['oldid']; - if(!is_null($page) && (!is_null($text) || $title != "API")) - $this->dieUsage("The page parameter cannot be used together with the text and title parameters", 'params'); - $prop = array_flip($params['prop']); + if ( !is_null( $page ) && ( !is_null( $text ) || $title != "API" ) ) + $this->dieUsage( "The page parameter cannot be used together with the text and title parameters", 'params' ); + $prop = array_flip( $params['prop'] ); $revid = false; // The parser needs $wgTitle to be set, apparently the // $title parameter in Parser::parse isn't enough *sigh* - global $wgParser, $wgUser, $wgTitle; + global $wgParser, $wgUser, $wgTitle, $wgEnableParserCache; $popts = new ParserOptions(); - $popts->setTidy(true); + $popts->setTidy( true ); $popts->enableLimitReport(); $redirValues = null; - if(!is_null($oldid) || !is_null($page)) + if ( !is_null( $oldid ) || !is_null( $page ) ) { - if(!is_null($oldid)) + if ( !is_null( $oldid ) ) { - # Don't use the parser cache - $rev = Revision::newFromID($oldid); - if(!$rev) - $this->dieUsage("There is no revision ID $oldid", 'missingrev'); - if(!$rev->userCan(Revision::DELETED_TEXT)) - $this->dieUsage("You don't have permission to view deleted revisions", 'permissiondenied'); + // Don't use the parser cache + $rev = Revision::newFromID( $oldid ); + if ( !$rev ) + $this->dieUsage( "There is no revision ID $oldid", 'missingrev' ); + if ( !$rev->userCan( Revision::DELETED_TEXT ) ) + $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' ); + $text = $rev->getText( Revision::FOR_THIS_USER ); $titleObj = $rev->getTitle(); $wgTitle = $titleObj; - $p_result = $wgParser->parse($text, $titleObj, $popts); + $p_result = $wgParser->parse( $text, $titleObj, $popts ); } else { - if($params['redirects']) + if ( $params['redirects'] ) { - $req = new FauxRequest(array( + $req = new FauxRequest( array( 'action' => 'query', 'redirects' => '', 'titles' => $page - )); - $main = new ApiMain($req); + ) ); + $main = new ApiMain( $req ); $main->execute(); $data = $main->getResultData(); $redirValues = @$data['query']['redirects']; $to = $page; - foreach((array)$redirValues as $r) + foreach ( (array)$redirValues as $r ) $to = $r['to']; } else - $to = $page; - $titleObj = Title::newFromText($to); - if(!$titleObj) - $this->dieUsage("The page you specified doesn't exist", 'missingtitle'); + $to = $page; + $titleObj = Title::newFromText( $to ); + if ( !$titleObj ) + $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' ); - $articleObj = new Article($titleObj); - if(isset($prop['revid'])) + $articleObj = new Article( $titleObj ); + if ( isset( $prop['revid'] ) ) $oldid = $articleObj->getRevIdFetched(); // Try the parser cache first + $p_result = false; $pcache = ParserCache::singleton(); - $p_result = $pcache->get($articleObj, $wgUser); - if(!$p_result) + if ( $wgEnableParserCache ) + $p_result = $pcache->get( $articleObj, $wgUser ); + if ( !$p_result ) { - $p_result = $wgParser->parse($articleObj->getContent(), $titleObj, $popts); - global $wgUseParserCache; - if($wgUseParserCache) - $pcache->save($p_result, $articleObj, $popts); + $p_result = $wgParser->parse( $articleObj->getContent(), $titleObj, $popts ); + + if ( $wgEnableParserCache ) + $pcache->save( $p_result, $articleObj, $popts ); } } } else { - $titleObj = Title::newFromText($title); - if(!$titleObj) - $titleObj = Title::newFromText("API"); + $titleObj = Title::newFromText( $title ); + if ( !$titleObj ) + $titleObj = Title::newFromText( "API" ); $wgTitle = $titleObj; - if($params['pst'] || $params['onlypst']) - $text = $wgParser->preSaveTransform($text, $titleObj, $wgUser, $popts); - if($params['onlypst']) + if ( $params['pst'] || $params['onlypst'] ) + $text = $wgParser->preSaveTransform( $text, $titleObj, $wgUser, $popts ); + if ( $params['onlypst'] ) { // Build a result and bail out $result_array['text'] = array(); - $this->getResult()->setContent($result_array['text'], $text); - $this->getResult()->addValue(null, $this->getModuleName(), $result_array); + $this->getResult()->setContent( $result_array['text'], $text ); + $this->getResult()->addValue( null, $this->getModuleName(), $result_array ); return; } - $p_result = $wgParser->parse($text, $titleObj, $popts); + $p_result = $wgParser->parse( $text, $titleObj, $popts ); } // Return result $result = $this->getResult(); $result_array = array(); - if($params['redirects'] && !is_null($redirValues)) + if ( $params['redirects'] && !is_null( $redirValues ) ) $result_array['redirects'] = $redirValues; - if(isset($prop['text'])) { + + if ( isset( $prop['text'] ) ) { $result_array['text'] = array(); - $result->setContent($result_array['text'], $p_result->getText()); + $result->setContent( $result_array['text'], $p_result->getText() ); + } + + if ( !is_null( $params['summary'] ) ) { + $result_array['parsedsummary'] = array(); + $result->setContent( $result_array['parsedsummary'], $wgUser->getSkin()->formatComment( $params['summary'], $titleObj ) ); } - if(isset($prop['langlinks'])) - $result_array['langlinks'] = $this->formatLangLinks($p_result->getLanguageLinks()); - if(isset($prop['categories'])) - $result_array['categories'] = $this->formatCategoryLinks($p_result->getCategories()); - if(isset($prop['links'])) - $result_array['links'] = $this->formatLinks($p_result->getLinks()); - if(isset($prop['templates'])) - $result_array['templates'] = $this->formatLinks($p_result->getTemplates()); - if(isset($prop['images'])) - $result_array['images'] = array_keys($p_result->getImages()); - if(isset($prop['externallinks'])) - $result_array['externallinks'] = array_keys($p_result->getExternalLinks()); - if(isset($prop['sections'])) + + if ( isset( $prop['langlinks'] ) ) + $result_array['langlinks'] = $this->formatLangLinks( $p_result->getLanguageLinks() ); + if ( isset( $prop['categories'] ) ) + $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() ); + if ( isset( $prop['links'] ) ) + $result_array['links'] = $this->formatLinks( $p_result->getLinks() ); + if ( isset( $prop['templates'] ) ) + $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() ); + if ( isset( $prop['images'] ) ) + $result_array['images'] = array_keys( $p_result->getImages() ); + if ( isset( $prop['externallinks'] ) ) + $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() ); + if ( isset( $prop['sections'] ) ) $result_array['sections'] = $p_result->getSections(); - if(isset($prop['displaytitle'])) + if ( isset( $prop['displaytitle'] ) ) $result_array['displaytitle'] = $p_result->getDisplayTitle() ? $p_result->getDisplayTitle() : $titleObj->getPrefixedText(); - if(!is_null($oldid)) - $result_array['revid'] = intval($oldid); + + if ( isset( $prop['headitems'] ) ) + $result_array['headitems'] = $this->formatHeadItems( $p_result->getHeadItems() ); + + if ( isset( $prop['headhtml'] ) ) { + $out = new OutputPage; + $out->addParserOutputNoText( $p_result ); + $result_array['headhtml'] = array(); + $result->setContent( $result_array['headhtml'], $out->headElement( $wgUser->getSkin() ) ); + } + + if ( !is_null( $oldid ) ) + $result_array['revid'] = intval( $oldid ); $result_mapping = array( 'redirects' => 'r', @@ -170,6 +191,7 @@ class ApiParse extends ApiBase { 'images' => 'img', 'externallinks' => 'el', 'sections' => 's', + 'headitems' => 'hi' ); $this->setIndexedTagNames( $result_array, $result_mapping ); $result->addValue( null, $this->getModuleName(), $result_array ); @@ -177,9 +199,9 @@ class ApiParse extends ApiBase { private function formatLangLinks( $links ) { $result = array(); - foreach( $links as $link ) { + foreach ( $links as $link ) { $entry = array(); - $bits = split( ':', $link, 2 ); + $bits = explode( ':', $link, 2 ); $entry['lang'] = $bits[0]; $this->getResult()->setContent( $entry, $bits[1] ); $result[] = $entry; @@ -189,7 +211,7 @@ class ApiParse extends ApiBase { private function formatCategoryLinks( $links ) { $result = array(); - foreach( $links as $link => $sortkey ) { + foreach ( $links as $link => $sortkey ) { $entry = array(); $entry['sortkey'] = $sortkey; $this->getResult()->setContent( $entry, $link ); @@ -200,12 +222,12 @@ class ApiParse extends ApiBase { private function formatLinks( $links ) { $result = array(); - foreach( $links as $ns => $nslinks ) { - foreach( $nslinks as $title => $id ) { + foreach ( $links as $ns => $nslinks ) { + foreach ( $nslinks as $title => $id ) { $entry = array(); $entry['ns'] = $ns; $this->getResult()->setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() ); - if( $id != 0 ) + if ( $id != 0 ) $entry['exists'] = ''; $result[] = $entry; } @@ -213,9 +235,20 @@ class ApiParse extends ApiBase { return $result; } + private function formatHeadItems( $headItems ) { + $result = array(); + foreach ( $headItems as $tag => $content ) { + $entry = array(); + $entry['tag'] = $tag; + $this->getResult()->setContent( $entry, $content ); + $result[] = $entry; + } + return $result; + } + private function setIndexedTagNames( &$array, $mapping ) { - foreach( $mapping as $key => $name ) { - if( isset( $array[$key] ) ) + foreach ( $mapping as $key => $name ) { + if ( isset( $array[$key] ) ) $this->getResult()->setIndexedTagName( $array[$key], $name ); } } @@ -226,6 +259,7 @@ class ApiParse extends ApiBase { ApiBase :: PARAM_DFLT => 'API', ), 'text' => null, + 'summary' => null, 'page' => null, 'redirects' => false, 'oldid' => null, @@ -243,6 +277,8 @@ class ApiParse extends ApiBase { 'sections', 'revid', 'displaytitle', + 'headitems', + 'headhtml' ) ), 'pst' => false, @@ -253,17 +289,18 @@ class ApiParse extends ApiBase { public function getParamDescription() { return array ( 'text' => 'Wikitext to parse', + 'summary' => 'Summary to parse', 'redirects' => 'If the page parameter is set to a redirect, resolve it', 'title' => 'Title of page the text belongs to', 'page' => 'Parse the content of this page. Cannot be used together with text and title', 'oldid' => 'Parse the content of this revision. Overrides page', - 'prop' => array('Which pieces of information to get.', + 'prop' => array( 'Which pieces of information to get.', 'NOTE: Section tree is only generated if there are more than 4 sections, or if the __TOC__ keyword is present' ), 'pst' => array( 'Do a pre-save transform on the input before parsing it.', 'Ignored if page or oldid is used.' ), - 'onlypst' => array('Do a PST on the input, but don\'t parse it.', + 'onlypst' => array( 'Do a PST on the input, but don\'t parse it.', 'Returns PSTed wikitext. Ignored if page or oldid is used.' ), ); @@ -272,6 +309,15 @@ class ApiParse extends ApiBase { public function getDescription() { return 'This module parses wikitext and returns parser output'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'params', 'info' => 'The page parameter cannot be used together with the text and title parameters' ), + array( 'code' => 'missingrev', 'info' => 'There is no revision ID oldid' ), + array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revisions' ), + array( 'code' => 'missingtitle', 'info' => 'The page you specified doesn\'t exist' ), + ) ); + } protected function getExamples() { return array ( @@ -280,6 +326,6 @@ class ApiParse extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiParse.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiParse.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiPatrol.php b/includes/api/ApiPatrol.php index a6f25af2..3b2b2046 100644 --- a/includes/api/ApiPatrol.php +++ b/includes/api/ApiPatrol.php @@ -23,8 +23,8 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { - require_once ('ApiBase.php'); +if ( !defined( 'MEDIAWIKI' ) ) { + require_once ( 'ApiBase.php' ); } /** @@ -33,35 +33,30 @@ if (!defined('MEDIAWIKI')) { */ class ApiPatrol extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } /** * Patrols the article or provides the reason the patrol failed. */ public function execute() { - global $wgUser, $wgUseRCPatrol, $wgUseNPPatrol; $params = $this->extractRequestParams(); - if(!isset($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - if(!isset($params['rcid'])) - $this->dieUsageMsg(array('missingparam', 'rcid')); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); + if ( !isset( $params['rcid'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'rcid' ) ); - $rc = RecentChange::newFromID($params['rcid']); - if(!$rc instanceof RecentChange) - $this->dieUsageMsg(array('nosuchrcid', $params['rcid'])); - $retval = RecentChange::markPatrolled($params['rcid']); + $rc = RecentChange::newFromID( $params['rcid'] ); + if ( !$rc instanceof RecentChange ) + $this->dieUsageMsg( array( 'nosuchrcid', $params['rcid'] ) ); + $retval = RecentChange::markPatrolled( $params['rcid'] ); - if($retval) - $this->dieUsageMsg(reset($retval)); + if ( $retval ) + $this->dieUsageMsg( reset( $retval ) ); - $result = array('rcid' => intval($rc->getAttribute('rc_id'))); - ApiQueryBase::addTitleInfo($result, $rc->getTitle()); - $this->getResult()->addValue(null, $this->getModuleName(), $result); + $result = array( 'rcid' => intval( $rc->getAttribute( 'rc_id' ) ) ); + ApiQueryBase::addTitleInfo( $result, $rc->getTitle() ); + $this->getResult()->addValue( null, $this->getModuleName(), $result ); } public function isWriteMode() { @@ -89,6 +84,17 @@ class ApiPatrol extends ApiBase { 'Patrol a page or revision. ' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'rcid' ), + array( 'nosuchrcid', 'rcid' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { return array( @@ -97,6 +103,6 @@ class ApiPatrol extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiPatrol.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiPatrol.php 69578 2010-07-20 02:46:20Z tstarling $'; } -} +} \ No newline at end of file diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php index aad37066..ca47c1b8 100644 --- a/includes/api/ApiProtect.php +++ b/includes/api/ApiProtect.php @@ -22,9 +22,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -32,99 +32,100 @@ if (!defined('MEDIAWIKI')) { */ class ApiProtect extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { global $wgUser, $wgRestrictionTypes, $wgRestrictionLevels; $params = $this->extractRequestParams(); - $titleObj = NULL; - if(!isset($params['title'])) - $this->dieUsageMsg(array('missingparam', 'title')); - if(!isset($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - if(empty($params['protections'])) - $this->dieUsageMsg(array('missingparam', 'protections')); + $titleObj = null; + if ( !isset( $params['title'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'title' ) ); + if ( empty( $params['protections'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'protections' ) ); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); + $titleObj = Title::newFromText( $params['title'] ); + if ( !$titleObj ) + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); - $titleObj = Title::newFromText($params['title']); - if(!$titleObj) - $this->dieUsageMsg(array('invalidtitle', $params['title'])); - - $errors = $titleObj->getUserPermissionsErrors('protect', $wgUser); - if($errors) + $errors = $titleObj->getUserPermissionsErrors( 'protect', $wgUser ); + if ( $errors ) // We don't care about multiple errors, just report one of them - $this->dieUsageMsg(reset($errors)); + $this->dieUsageMsg( reset( $errors ) ); $expiry = (array)$params['expiry']; - if(count($expiry) != count($params['protections'])) + if ( count( $expiry ) != count( $params['protections'] ) ) { - if(count($expiry) == 1) - $expiry = array_fill(0, count($params['protections']), $expiry[0]); + if ( count( $expiry ) == 1 ) + $expiry = array_fill( 0, count( $params['protections'] ), $expiry[0] ); else - $this->dieUsageMsg(array('toofewexpiries', count($expiry), count($params['protections']))); + $this->dieUsageMsg( array( 'toofewexpiries', count( $expiry ), count( $params['protections'] ) ) ); } + + $restrictionTypes = $titleObj->getRestrictionTypes(); $protections = array(); $expiryarray = array(); $resultProtections = array(); - foreach($params['protections'] as $i => $prot) + foreach ( $params['protections'] as $i => $prot ) { - $p = explode('=', $prot); - $protections[$p[0]] = ($p[1] == 'all' ? '' : $p[1]); - if($titleObj->exists() && $p[0] == 'create') - $this->dieUsageMsg(array('create-titleexists')); - if(!$titleObj->exists() && $p[0] != 'create') - $this->dieUsageMsg(array('missingtitles-createonly')); - if(!in_array($p[0], $wgRestrictionTypes) && $p[0] != 'create') - $this->dieUsageMsg(array('protect-invalidaction', $p[0])); - if(!in_array($p[1], $wgRestrictionLevels) && $p[1] != 'all') - $this->dieUsageMsg(array('protect-invalidlevel', $p[1])); - - if(in_array($expiry[$i], array('infinite', 'indefinite', 'never'))) + $p = explode( '=', $prot ); + $protections[$p[0]] = ( $p[1] == 'all' ? '' : $p[1] ); + + if ( $titleObj->exists() && $p[0] == 'create' ) + $this->dieUsageMsg( array( 'create-titleexists' ) ); + if ( !$titleObj->exists() && $p[0] != 'create' ) + $this->dieUsageMsg( array( 'missingtitle-createonly' ) ); + + if ( !in_array( $p[0], $restrictionTypes ) && $p[0] != 'create' ) + $this->dieUsageMsg( array( 'protect-invalidaction', $p[0] ) ); + if ( !in_array( $p[1], $wgRestrictionLevels ) && $p[1] != 'all' ) + $this->dieUsageMsg( array( 'protect-invalidlevel', $p[1] ) ); + + if ( in_array( $expiry[$i], array( 'infinite', 'indefinite', 'never' ) ) ) $expiryarray[$p[0]] = Block::infinity(); else { - $exp = strtotime($expiry[$i]); - if($exp < 0 || $exp == false) - $this->dieUsageMsg(array('invalidexpiry', $expiry[$i])); + $exp = strtotime( $expiry[$i] ); + if ( $exp < 0 || $exp == false ) + $this->dieUsageMsg( array( 'invalidexpiry', $expiry[$i] ) ); - $exp = wfTimestamp(TS_MW, $exp); - if($exp < wfTimestampNow()) - $this->dieUsageMsg(array('pastexpiry', $expiry[$i])); + $exp = wfTimestamp( TS_MW, $exp ); + if ( $exp < wfTimestampNow() ) + $this->dieUsageMsg( array( 'pastexpiry', $expiry[$i] ) ); $expiryarray[$p[0]] = $exp; } - $resultProtections[] = array($p[0] => $protections[$p[0]], - 'expiry' => ($expiryarray[$p[0]] == Block::infinity() ? + $resultProtections[] = array( $p[0] => $protections[$p[0]], + 'expiry' => ( $expiryarray[$p[0]] == Block::infinity() ? 'infinite' : - wfTimestamp(TS_ISO_8601, $expiryarray[$p[0]]))); + wfTimestamp( TS_ISO_8601, $expiryarray[$p[0]] ) ) ); } $cascade = $params['cascade']; - $articleObj = new Article($titleObj); - if($params['watch']) + $articleObj = new Article( $titleObj ); + if ( $params['watch'] ) $articleObj->doWatch(); - if($titleObj->exists()) - $ok = $articleObj->updateRestrictions($protections, $params['reason'], $cascade, $expiryarray); + if ( $titleObj->exists() ) + $ok = $articleObj->updateRestrictions( $protections, $params['reason'], $cascade, $expiryarray ); else - $ok = $titleObj->updateTitleProtection($protections['create'], $params['reason'], $expiryarray['create']); - if(!$ok) + $ok = $titleObj->updateTitleProtection( $protections['create'], $params['reason'], $expiryarray['create'] ); + if ( !$ok ) // This is very weird. Maybe the article was deleted or the user was blocked/desysopped in the meantime? // Just throw an unknown error in this case, as it's very likely to be a race condition - $this->dieUsageMsg(array()); - $res = array('title' => $titleObj->getPrefixedText(), 'reason' => $params['reason']); - if($cascade) + $this->dieUsageMsg( array() ); + $res = array( 'title' => $titleObj->getPrefixedText(), 'reason' => $params['reason'] ); + if ( $cascade ) $res['cascade'] = ''; $res['protections'] = $resultProtections; - $this->getResult()->setIndexedTagName($res['protections'], 'protection'); - $this->getResult()->addValue(null, $this->getModuleName(), $res); + $this->getResult()->setIndexedTagName( $res['protections'], 'protection' ); + $this->getResult()->addValue( null, $this->getModuleName(), $res ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; @@ -153,11 +154,11 @@ class ApiProtect extends ApiBase { 'title' => 'Title of the page you want to (un)protect.', 'token' => 'A protect token previously retrieved through prop=info', 'protections' => 'Pipe-separated list of protection levels, formatted action=group (e.g. edit=sysop)', - 'expiry' => 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.'), + 'expiry' => array( 'Expiry timestamps. If only one timestamp is set, it\'ll be used for all protections.', + 'Use \'infinite\', \'indefinite\' or \'never\', for a neverexpiring protection.' ), 'reason' => 'Reason for (un)protecting (optional)', - 'cascade' => array('Enable cascading protection (i.e. protect pages included in this page)', - 'Ignored if not all protection levels are \'sysop\' or \'protect\''), + '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', ); } @@ -167,6 +168,25 @@ class ApiProtect extends ApiBase { 'Change the protection level of a page.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'title' ), + array( 'missingparam', 'protections' ), + 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' ), + ) ); + } + + public function getTokenSalt() { + return null; + } protected function getExamples() { return array ( @@ -176,6 +196,6 @@ class ApiProtect extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiProtect.php 48122 2009-03-07 12:58:41Z catrope $'; + return __CLASS__ . ': $Id: ApiProtect.php 62557 2010-02-15 23:53:43Z reedy $'; } } diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php index d1e6824d..76d45404 100644 --- a/includes/api/ApiPurge.php +++ b/includes/api/ApiPurge.php @@ -23,8 +23,8 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { - require_once ('ApiBase.php'); +if ( !defined( 'MEDIAWIKI' ) ) { + require_once ( 'ApiBase.php' ); } /** @@ -33,8 +33,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiPurge extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } /** @@ -43,35 +43,35 @@ class ApiPurge extends ApiBase { public function execute() { global $wgUser; $params = $this->extractRequestParams(); - if(!$wgUser->isAllowed('purge')) - $this->dieUsageMsg(array('cantpurge')); - if(!isset($params['titles'])) - $this->dieUsageMsg(array('missingparam', 'titles')); + if ( !$wgUser->isAllowed( 'purge' ) ) + $this->dieUsageMsg( array( 'cantpurge' ) ); + if ( !isset( $params['titles'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'titles' ) ); $result = array(); - foreach($params['titles'] as $t) { + foreach ( $params['titles'] as $t ) { $r = array(); - $title = Title::newFromText($t); - if(!$title instanceof Title) + $title = Title::newFromText( $t ); + if ( !$title instanceof Title ) { $r['title'] = $t; $r['invalid'] = ''; $result[] = $r; continue; } - ApiQueryBase::addTitleInfo($r, $title); - if(!$title->exists()) + ApiQueryBase::addTitleInfo( $r, $title ); + if ( !$title->exists() ) { $r['missing'] = ''; $result[] = $r; continue; } - $article = new Article($title); + $article = Mediawiki::articleFromTitle( $title ); $article->doPurge(); // Directly purge and skip the UI part of purge(). $r['purged'] = ''; $result[] = $r; } - $this->getResult()->setIndexedTagName($result, 'page'); - $this->getResult()->addValue(null, $this->getModuleName(), $result); + $this->getResult()->setIndexedTagName( $result, 'page' ); + $this->getResult()->addValue( null, $this->getModuleName(), $result ); } public function mustBePosted() { @@ -102,6 +102,13 @@ class ApiPurge extends ApiBase { 'Purge the cache for the given titles.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'cantpurge' ), + array( 'missingparam', 'titles' ), + ) ); + } protected function getExamples() { return array( @@ -110,6 +117,6 @@ class ApiPurge extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiPurge.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiPurge.php 69578 2010-07-20 02:46:20Z tstarling $'; } } diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php index 49ddcdd3..8d3ef616 100644 --- a/includes/api/ApiQuery.php +++ b/includes/api/ApiQuery.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -74,6 +74,7 @@ class ApiQuery extends ApiBase { 'logevents' => 'ApiQueryLogEvents', 'recentchanges' => 'ApiQueryRecentChanges', 'search' => 'ApiQuerySearch', + 'tags' => 'ApiQueryTags', 'usercontribs' => 'ApiQueryContributions', 'watchlist' => 'ApiQueryWatchlist', 'watchlistraw' => 'ApiQueryWatchlistRaw', @@ -92,22 +93,22 @@ class ApiQuery extends ApiBase { private $mSlaveDB = null; private $mNamedDB = array(); - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); // Allow custom modules to be added in LocalSettings.php global $wgAPIPropModules, $wgAPIListModules, $wgAPIMetaModules; - self :: appendUserModules($this->mQueryPropModules, $wgAPIPropModules); - self :: appendUserModules($this->mQueryListModules, $wgAPIListModules); - self :: appendUserModules($this->mQueryMetaModules, $wgAPIMetaModules); + self :: appendUserModules( $this->mQueryPropModules, $wgAPIPropModules ); + self :: appendUserModules( $this->mQueryListModules, $wgAPIListModules ); + self :: appendUserModules( $this->mQueryMetaModules, $wgAPIMetaModules ); - $this->mPropModuleNames = array_keys($this->mQueryPropModules); - $this->mListModuleNames = array_keys($this->mQueryListModules); - $this->mMetaModuleNames = array_keys($this->mQueryMetaModules); + $this->mPropModuleNames = array_keys( $this->mQueryPropModules ); + $this->mListModuleNames = array_keys( $this->mQueryListModules ); + $this->mMetaModuleNames = array_keys( $this->mQueryMetaModules ); // Allow the entire list of modules at first, // but during module instantiation check if it can be used as a generator. - $this->mAllowedGenerators = array_merge($this->mListModuleNames, $this->mPropModuleNames); + $this->mAllowedGenerators = array_merge( $this->mListModuleNames, $this->mPropModuleNames ); } /** @@ -115,9 +116,9 @@ class ApiQuery extends ApiBase { * @param $modules array Module array * @param $newModules array Module array to add to $modules */ - private static function appendUserModules(&$modules, $newModules) { - if (is_array( $newModules )) { - foreach ( $newModules as $moduleName => $moduleClass) { + private static function appendUserModules( &$modules, $newModules ) { + if ( is_array( $newModules ) ) { + foreach ( $newModules as $moduleName => $moduleClass ) { $modules[$moduleName] = $moduleClass; } } @@ -128,9 +129,9 @@ class ApiQuery extends ApiBase { * @return Database */ public function getDB() { - if (!isset ($this->mSlaveDB)) { + if ( !isset ( $this->mSlaveDB ) ) { $this->profileDBIn(); - $this->mSlaveDB = wfGetDB(DB_SLAVE,'api'); + $this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' ); $this->profileDBOut(); } return $this->mSlaveDB; @@ -146,10 +147,10 @@ class ApiQuery extends ApiBase { * @param $groups array Query groups * @return Database */ - public function getNamedDB($name, $db, $groups) { - if (!array_key_exists($name, $this->mNamedDB)) { + public function getNamedDB( $name, $db, $groups ) { + if ( !array_key_exists( $name, $this->mNamedDB ) ) { $this->profileDBIn(); - $this->mNamedDB[$name] = wfGetDB($db, $groups); + $this->mNamedDB[$name] = wfGetDB( $db, $groups ); $this->profileDBOut(); } return $this->mNamedDB[$name]; @@ -168,15 +169,15 @@ class ApiQuery extends ApiBase { * @return array(modulename => classname) */ function getModules() { - return array_merge($this->mQueryPropModules, $this->mQueryListModules, $this->mQueryMetaModules); + return array_merge( $this->mQueryPropModules, $this->mQueryListModules, $this->mQueryMetaModules ); } - + public function getCustomPrinter() { // If &exportnowrap is set, use the raw formatter - if ($this->getParameter('export') && - $this->getParameter('exportnowrap')) - return new ApiFormatRaw($this->getMain(), - $this->getMain()->createPrinterByName('xml')); + if ( $this->getParameter( 'export' ) && + $this->getParameter( 'exportnowrap' ) ) + return new ApiFormatRaw( $this->getMain(), + $this->getMain()->createPrinterByName( 'xml' ) ); else return null; } @@ -196,24 +197,18 @@ class ApiQuery extends ApiBase { $this->params = $this->extractRequestParams(); $this->redirects = $this->params['redirects']; - // // Create PageSet - // - $this->mPageSet = new ApiPageSet($this, $this->redirects); + $this->mPageSet = new ApiPageSet( $this, $this->redirects ); - // // Instantiate requested modules - // $modules = array (); - $this->InstantiateModules($modules, 'prop', $this->mQueryPropModules); - $this->InstantiateModules($modules, 'list', $this->mQueryListModules); - $this->InstantiateModules($modules, 'meta', $this->mQueryMetaModules); + $this->InstantiateModules( $modules, 'prop', $this->mQueryPropModules ); + $this->InstantiateModules( $modules, 'list', $this->mQueryListModules ); + $this->InstantiateModules( $modules, 'meta', $this->mQueryMetaModules ); $cacheMode = 'public'; - // // If given, execute generator to substitute user supplied data with generated data. - // if ( isset ( $this->params['generator'] ) ) { $generator = $this->newGenerator( $this->params['generator'] ); $params = $generator->extractRequestParams(); @@ -222,25 +217,21 @@ class ApiQuery extends ApiBase { $this->executeGeneratorModule( $generator, $modules ); } else { // Append custom fields and populate page/revision information - $this->addCustomFldsToPageSet($modules, $this->mPageSet); + $this->addCustomFldsToPageSet( $modules, $this->mPageSet ); $this->mPageSet->execute(); } - // // Record page information (title, namespace, if exists, etc) - // $this->outputGeneralPageInfo(); - // // Execute all requested modules. - // - foreach ($modules as $module) { + foreach ( $modules as $module ) { $params = $module->extractRequestParams(); $cacheMode = $this->mergeCacheMode( $cacheMode, $module->getCacheMode( $params ) ); $module->profileIn(); $module->execute(); - wfRunHooks('APIQueryAfterExecute', array(&$module)); + wfRunHooks( 'APIQueryAfterExecute', array( &$module ) ); $module->profileOut(); } @@ -273,10 +264,10 @@ class ApiQuery extends ApiBase { * @param $modules array of module objects * @param $pageSet ApiPageSet */ - private function addCustomFldsToPageSet($modules, $pageSet) { + private function addCustomFldsToPageSet( $modules, $pageSet ) { // Query all requested modules. - foreach ($modules as $module) { - $module->requestExtraData($pageSet); + foreach ( $modules as $module ) { + $module->requestExtraData( $pageSet ); } } @@ -286,11 +277,11 @@ class ApiQuery extends ApiBase { * @param $param string Parameter name to read modules from * @param $moduleList array(modulename => classname) */ - private function InstantiateModules(&$modules, $param, $moduleList) { + private function InstantiateModules( &$modules, $param, $moduleList ) { $list = @$this->params[$param]; - if (!is_null ($list)) - foreach ($list as $moduleName) - $modules[] = new $moduleList[$moduleName] ($this, $moduleName); + if ( !is_null ( $list ) ) + foreach ( $list as $moduleName ) + $modules[] = new $moduleList[$moduleName] ( $this, $moduleName ); } /** @@ -303,65 +294,65 @@ class ApiQuery extends ApiBase { $pageSet = $this->getPageSet(); $result = $this->getResult(); - # We don't check for a full result set here because we can't be adding - # more than 380K. The maximum revision size is in the megabyte range, - # and the maximum result size must be even higher than that. + // We don't check for a full result set here because we can't be adding + // more than 380K. The maximum revision size is in the megabyte range, + // and the maximum result size must be even higher than that. // Title normalizations $normValues = array (); - foreach ($pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr) { + foreach ( $pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr ) { $normValues[] = array ( 'from' => $rawTitleStr, 'to' => $titleStr ); } - if (count($normValues)) { - $result->setIndexedTagName($normValues, 'n'); - $result->addValue('query', 'normalized', $normValues); + if ( count( $normValues ) ) { + $result->setIndexedTagName( $normValues, 'n' ); + $result->addValue( 'query', 'normalized', $normValues ); } // Interwiki titles $intrwValues = array (); - foreach ($pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr) { + foreach ( $pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr ) { $intrwValues[] = array ( 'title' => $rawTitleStr, 'iw' => $interwikiStr ); } - if (count($intrwValues)) { - $result->setIndexedTagName($intrwValues, 'i'); - $result->addValue('query', 'interwiki', $intrwValues); + if ( count( $intrwValues ) ) { + $result->setIndexedTagName( $intrwValues, 'i' ); + $result->addValue( 'query', 'interwiki', $intrwValues ); } // Show redirect information $redirValues = array (); - foreach ($pageSet->getRedirectTitles() as $titleStrFrom => $titleStrTo) { + foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleStrTo ) { $redirValues[] = array ( - 'from' => strval($titleStrFrom), + 'from' => strval( $titleStrFrom ), 'to' => $titleStrTo ); } - if (count($redirValues)) { - $result->setIndexedTagName($redirValues, 'r'); - $result->addValue('query', 'redirects', $redirValues); + if ( count( $redirValues ) ) { + $result->setIndexedTagName( $redirValues, 'r' ); + $result->addValue( 'query', 'redirects', $redirValues ); } // // Missing revision elements // $missingRevIDs = $pageSet->getMissingRevisionIDs(); - if (count($missingRevIDs)) { + if ( count( $missingRevIDs ) ) { $revids = array (); - foreach ($missingRevIDs as $revid) { + foreach ( $missingRevIDs as $revid ) { $revids[$revid] = array ( 'revid' => $revid ); } - $result->setIndexedTagName($revids, 'rev'); - $result->addValue('query', 'badrevids', $revids); + $result->setIndexedTagName( $revids, 'rev' ); + $result->addValue( 'query', 'badrevids', $revids ); } // @@ -370,17 +361,17 @@ class ApiQuery extends ApiBase { $pages = array (); // Report any missing titles - foreach ($pageSet->getMissingTitles() as $fakeId => $title) { + foreach ( $pageSet->getMissingTitles() as $fakeId => $title ) { $vals = array(); - ApiQueryBase :: addTitleInfo($vals, $title); + ApiQueryBase :: addTitleInfo( $vals, $title ); $vals['missing'] = ''; $pages[$fakeId] = $vals; } // Report any invalid titles - foreach ($pageSet->getInvalidTitles() as $fakeId => $title) - $pages[$fakeId] = array('title' => $title, 'invalid' => ''); + foreach ( $pageSet->getInvalidTitles() as $fakeId => $title ) + $pages[$fakeId] = array( 'title' => $title, 'invalid' => '' ); // Report any missing page ids - foreach ($pageSet->getMissingPageIDs() as $pageid) { + foreach ( $pageSet->getMissingPageIDs() as $pageid ) { $pages[$pageid] = array ( 'pageid' => $pageid, 'missing' => '' @@ -388,51 +379,52 @@ class ApiQuery extends ApiBase { } // Output general page information for found titles - foreach ($pageSet->getGoodTitles() as $pageid => $title) { + foreach ( $pageSet->getGoodTitles() as $pageid => $title ) { $vals = array(); $vals['pageid'] = $pageid; - ApiQueryBase :: addTitleInfo($vals, $title); + ApiQueryBase :: addTitleInfo( $vals, $title ); $pages[$pageid] = $vals; } - if (count($pages)) { + if ( count( $pages ) ) { - if ($this->params['indexpageids']) { - $pageIDs = array_keys($pages); + if ( $this->params['indexpageids'] ) { + $pageIDs = array_keys( $pages ); // json treats all map keys as strings - converting to match - $pageIDs = array_map('strval', $pageIDs); - $result->setIndexedTagName($pageIDs, 'id'); - $result->addValue('query', 'pageids', $pageIDs); + $pageIDs = array_map( 'strval', $pageIDs ); + $result->setIndexedTagName( $pageIDs, 'id' ); + $result->addValue( 'query', 'pageids', $pageIDs ); } - $result->setIndexedTagName($pages, 'page'); - $result->addValue('query', 'pages', $pages); + $result->setIndexedTagName( $pages, 'page' ); + $result->addValue( 'query', 'pages', $pages ); } - if ($this->params['export']) { - $exporter = new WikiExporter($this->getDB()); + if ( $this->params['export'] ) { + $exporter = new WikiExporter( $this->getDB() ); // WikiExporter writes to stdout, so catch its // output with an ob ob_start(); $exporter->openStream(); - foreach (@$pageSet->getGoodTitles() as $title) - if ($title->userCanRead()) - $exporter->pageByTitle($title); + foreach ( @$pageSet->getGoodTitles() as $title ) + if ( $title->userCanRead() ) + $exporter->pageByTitle( $title ); $exporter->closeStream(); $exportxml = ob_get_contents(); ob_end_clean(); + // Don't check the size of exported stuff // It's not continuable, so it would cause more // problems than it'd solve $result->disableSizeCheck(); - if ($this->params['exportnowrap']) { + if ( $this->params['exportnowrap'] ) { $result->reset(); // Raw formatter will handle this - $result->addValue(null, 'text', $exportxml); - $result->addValue(null, 'mime', 'text/xml'); + $result->addValue( null, 'text', $exportxml ); + $result->addValue( null, 'mime', 'text/xml' ); } else { $r = array(); - ApiResult::setContent($r, $exportxml); - $result->addValue('query', 'export', $r); + ApiResult::setContent( $r, $exportxml ); + $result->addValue( 'query', 'export', $r ); } $result->enableSizeCheck(); } @@ -442,22 +434,23 @@ class ApiQuery extends ApiBase { * Create a generator object of the given type and return it */ public function newGenerator( $generatorName ) { + // Find class that implements requested generator - if (isset ($this->mQueryListModules[$generatorName])) { + if ( isset ( $this->mQueryListModules[$generatorName] ) ) { $className = $this->mQueryListModules[$generatorName]; - } elseif (isset ($this->mQueryPropModules[$generatorName])) { + } elseif ( isset ( $this->mQueryPropModules[$generatorName] ) ) { $className = $this->mQueryPropModules[$generatorName]; } else { - ApiBase :: dieDebug(__METHOD__, "Unknown generator=$generatorName"); + ApiBase :: dieDebug( __METHOD__, "Unknown generator=$generatorName" ); } // Generator results - $resultPageSet = new ApiPageSet($this, $this->redirects); + $resultPageSet = new ApiPageSet( $this, $this->redirects ); // Create and execute the generator - $generator = new $className ($this, $generatorName); - if (!$generator instanceof ApiQueryGeneratorBase) - $this->dieUsage("Module $generatorName cannot be used as a generator", "badgenerator"); + $generator = new $className ( $this, $generatorName ); + if ( !$generator instanceof ApiQueryGeneratorBase ) + $this->dieUsage( "Module $generatorName cannot be used as a generator", "badgenerator" ); $generator->setGeneratorMode(); return $generator; } @@ -473,16 +466,16 @@ class ApiQuery extends ApiBase { $resultPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles ); // Add any additional fields modules may need - $generator->requestExtraData($this->mPageSet); - $this->addCustomFldsToPageSet($modules, $resultPageSet); + $generator->requestExtraData( $this->mPageSet ); + $this->addCustomFldsToPageSet( $modules, $resultPageSet ); // Populate page information with the original user input $this->mPageSet->execute(); // populate resultPageSet with the generator output $generator->profileIn(); - $generator->executeGenerator($resultPageSet); - wfRunHooks('APIQueryGeneratorAfterExecute', array(&$generator, &$resultPageSet)); + $generator->executeGenerator( $resultPageSet ); + wfRunHooks( 'APIQueryGeneratorAfterExecute', array( &$generator, &$resultPageSet ) ); $resultPageSet->finishPageSetGeneration(); $generator->profileOut(); @@ -527,14 +520,14 @@ class ApiQuery extends ApiBase { $this->mPageSet = null; $this->mAllowedGenerators = array(); // Will be repopulated - $astriks = str_repeat('--- ', 8); - $astriks2 = str_repeat('*** ', 10); + $astriks = str_repeat( '--- ', 8 ); + $astriks2 = str_repeat( '*** ', 10 ); $msg .= "\n$astriks Query: Prop $astriks\n\n"; - $msg .= $this->makeHelpMsgHelper($this->mQueryPropModules, 'prop'); + $msg .= $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' ); $msg .= "\n$astriks Query: List $astriks\n\n"; - $msg .= $this->makeHelpMsgHelper($this->mQueryListModules, 'list'); + $msg .= $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' ); $msg .= "\n$astriks Query: Meta $astriks\n\n"; - $msg .= $this->makeHelpMsgHelper($this->mQueryMetaModules, 'meta'); + $msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' ); $msg .= "\n\n$astriks2 Modules: continuation $astriks2\n\n"; // Perform the base call last because the $this->mAllowedGenerators @@ -551,25 +544,25 @@ class ApiQuery extends ApiBase { * @param $paramName string Parameter name * @return string */ - private function makeHelpMsgHelper($moduleList, $paramName) { + private function makeHelpMsgHelper( $moduleList, $paramName ) { $moduleDescriptions = array (); - foreach ($moduleList as $moduleName => $moduleClass) { - $module = new $moduleClass ($this, $moduleName, null); + foreach ( $moduleList as $moduleName => $moduleClass ) { + $module = new $moduleClass ( $this, $moduleName, null ); - $msg = ApiMain::makeHelpMsgHeader($module, $paramName); + $msg = ApiMain::makeHelpMsgHeader( $module, $paramName ); $msg2 = $module->makeHelpMsg(); - if ($msg2 !== false) + if ( $msg2 !== false ) $msg .= $msg2; - if ($module instanceof ApiQueryGeneratorBase) { + if ( $module instanceof ApiQueryGeneratorBase ) { $this->mAllowedGenerators[] = $moduleName; $msg .= "Generator:\n This module may be used as a generator\n"; } $moduleDescriptions[] = $msg; } - return implode("\n", $moduleDescriptions); + return implode( "\n", $moduleDescriptions ); } /** @@ -577,7 +570,7 @@ class ApiQuery extends ApiBase { * @return string */ public function makeHelpMsgParameters() { - $psModule = new ApiPageSet($this); + $psModule = new ApiPageSet( $this ); return $psModule->makeHelpMsgParameters() . parent :: makeHelpMsgParameters(); } @@ -590,8 +583,8 @@ class ApiQuery extends ApiBase { 'prop' => 'Which properties to get for the titles/revisions/pageids', 'list' => 'Which lists to get', 'meta' => 'Which meta data to get about the site', - 'generator' => array('Use the output of a list as the input for other prop/list/meta items', - 'NOTE: generator parameter names must be prefixed with a \'g\', see examples.'), + 'generator' => array( 'Use the output of a list as the input for other prop/list/meta items', + 'NOTE: generator parameter names must be prefixed with a \'g\', see examples.' ), 'redirects' => 'Automatically resolve redirects', 'indexpageids' => 'Include an additional pageids section listing all returned page IDs.', 'export' => 'Export the current revisions of all given or generated pages', @@ -606,6 +599,12 @@ class ApiQuery extends ApiBase { 'All data modifications will first have to use query to acquire a token to prevent abuse from malicious sites.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'badgenerator', 'info' => 'Module $generatorName cannot be used as a generator' ), + ) ); + } protected function getExamples() { return array ( @@ -615,9 +614,9 @@ class ApiQuery extends ApiBase { } public function getVersion() { - $psModule = new ApiPageSet($this); + $psModule = new ApiPageSet( $this ); $vers = array (); - $vers[] = __CLASS__ . ': $Id: ApiQuery.php 69986 2010-07-27 03:57:39Z tstarling $'; + $vers[] = __CLASS__ . ': $Id: ApiQuery.php 69932 2010-07-26 08:03:21Z tstarling $'; $vers[] = $psModule->getVersion(); return $vers; } diff --git a/includes/api/ApiQueryAllCategories.php b/includes/api/ApiQueryAllCategories.php index fca92c4b..8f24fc7c 100644 --- a/includes/api/ApiQueryAllCategories.php +++ b/includes/api/ApiQueryAllCategories.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -36,8 +36,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryAllCategories extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'ac'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'ac' ); } public function execute() { @@ -48,86 +48,86 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { $db = $this->getDB(); $params = $this->extractRequestParams(); - $this->addTables('category'); - $this->addFields('cat_title'); + $this->addTables( 'category' ); + $this->addFields( 'cat_title' ); - $dir = ($params['dir'] == 'descending' ? 'older' : 'newer'); - $from = (is_null($params['from']) ? null : $this->titlePartToKey($params['from'])); - $this->addWhereRange('cat_title', $dir, $from, null); - if (isset ($params['prefix'])) - $this->addWhere("cat_title LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'"); + $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' ); + $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) ); + $this->addWhereRange( 'cat_title', $dir, $from, null ); + if ( isset ( $params['prefix'] ) ) + $this->addWhere( 'cat_title' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) ); - $this->addOption('LIMIT', $params['limit']+1); - $this->addOption('ORDER BY', 'cat_title' . ($params['dir'] == 'descending' ? ' DESC' : '')); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + $this->addOption( 'ORDER BY', 'cat_title' . ( $params['dir'] == 'descending' ? ' DESC' : '' ) ); - $prop = array_flip($params['prop']); - $this->addFieldsIf( array( 'cat_pages', 'cat_subcats', 'cat_files' ), isset($prop['size']) ); - if(isset($prop['hidden'])) + $prop = array_flip( $params['prop'] ); + $this->addFieldsIf( array( 'cat_pages', 'cat_subcats', 'cat_files' ), isset( $prop['size'] ) ); + if ( isset( $prop['hidden'] ) ) { - $this->addTables(array('page', 'page_props')); - $this->addJoinConds(array( - 'page' => array('LEFT JOIN', array( + $this->addTables( array( 'page', 'page_props' ) ); + $this->addJoinConds( array( + 'page' => array( 'LEFT JOIN', array( 'page_namespace' => NS_CATEGORY, - 'page_title=cat_title')), - 'page_props' => array('LEFT JOIN', array( + 'page_title=cat_title' ) ), + 'page_props' => array( 'LEFT JOIN', array( 'pp_page=page_id', - 'pp_propname' => 'hiddencat')), - )); - $this->addFields('pp_propname AS cat_hidden'); + 'pp_propname' => 'hiddencat' ) ), + ) ); + $this->addFields( 'pp_propname AS cat_hidden' ); } - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $pages = array(); $categories = array(); $result = $this->getResult(); $count = 0; - while ($row = $db->fetchObject($res)) { - if (++ $count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional cats to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown - $this->setContinueEnumParameter('from', $this->keyToTitle($row->cat_title)); + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) ); break; } // Normalize titles - $titleObj = Title::makeTitle(NS_CATEGORY, $row->cat_title); - if(!is_null($resultPageSet)) + $titleObj = Title::makeTitle( NS_CATEGORY, $row->cat_title ); + if ( !is_null( $resultPageSet ) ) $pages[] = $titleObj->getPrefixedText(); else { $item = array(); $result->setContent( $item, $titleObj->getText() ); - if( isset( $prop['size'] ) ) { - $item['size'] = intval($row->cat_pages); + if ( isset( $prop['size'] ) ) { + $item['size'] = intval( $row->cat_pages ); $item['pages'] = $row->cat_pages - $row->cat_subcats - $row->cat_files; - $item['files'] = intval($row->cat_files); - $item['subcats'] = intval($row->cat_subcats); + $item['files'] = intval( $row->cat_files ); + $item['subcats'] = intval( $row->cat_subcats ); } - if( isset( $prop['hidden'] ) && $row->cat_hidden ) + if ( isset( $prop['hidden'] ) && $row->cat_hidden ) $item['hidden'] = ''; - $fit = $result->addValue(array('query', $this->getModuleName()), null, $item); - if(!$fit) + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $item ); + if ( !$fit ) { - $this->setContinueEnumParameter('from', $this->keyToTitle($row->cat_title)); + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) ); break; } } } - $db->freeResult($res); + $db->freeResult( $res ); - if (is_null($resultPageSet)) { - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'c'); + if ( is_null( $resultPageSet ) ) { + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'c' ); } else { - $resultPageSet->populateFromTitles($pages); + $resultPageSet->populateFromTitles( $pages ); } } @@ -179,6 +179,6 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllCategories.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllCategories.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php index 73788aa6..6b6fc2c0 100644 --- a/includes/api/ApiQueryAllLinks.php +++ b/includes/api/ApiQueryAllLinks.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryAllLinks extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'al'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'al' ); } public function execute() { @@ -47,105 +47,105 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { $db = $this->getDB(); $params = $this->extractRequestParams(); - $prop = array_flip($params['prop']); - $fld_ids = isset($prop['ids']); - $fld_title = isset($prop['title']); + $prop = array_flip( $params['prop'] ); + $fld_ids = isset( $prop['ids'] ); + $fld_title = isset( $prop['title'] ); - if ($params['unique']) { - if (!is_null($resultPageSet)) - $this->dieUsage($this->getModuleName() . ' cannot be used as a generator in unique links mode', 'params'); - if ($fld_ids) - $this->dieUsage($this->getModuleName() . ' cannot return corresponding page ids in unique links mode', 'params'); - $this->addOption('DISTINCT'); + if ( $params['unique'] ) { + if ( !is_null( $resultPageSet ) ) + $this->dieUsage( $this->getModuleName() . ' cannot be used as a generator in unique links mode', 'params' ); + if ( $fld_ids ) + $this->dieUsage( $this->getModuleName() . ' cannot return corresponding page ids in unique links mode', 'params' ); + $this->addOption( 'DISTINCT' ); } - $this->addTables('pagelinks'); - $this->addWhereFld('pl_namespace', $params['namespace']); + $this->addTables( 'pagelinks' ); + $this->addWhereFld( 'pl_namespace', $params['namespace'] ); - if (!is_null($params['from']) && !is_null($params['continue'])) - $this->dieUsage('alcontinue and alfrom cannot be used together', 'params'); - if (!is_null($params['continue'])) + if ( !is_null( $params['from'] ) && !is_null( $params['continue'] ) ) + $this->dieUsage( 'alcontinue and alfrom cannot be used together', 'params' ); + if ( !is_null( $params['continue'] ) ) { - $arr = explode('|', $params['continue']); - if(count($arr) != 2) - $this->dieUsage("Invalid continue parameter", 'badcontinue'); - $from = $this->getDB()->strencode($this->titleToKey($arr[0])); - $id = intval($arr[1]); - $this->addWhere("pl_title > '$from' OR " . + $arr = explode( '|', $params['continue'] ); + if ( count( $arr ) != 2 ) + $this->dieUsage( "Invalid continue parameter", 'badcontinue' ); + $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)"); - } + "pl_from > $id)" ); + } - if (!is_null($params['from'])) - $this->addWhere('pl_title>=' . $db->addQuotes($this->titlePartToKey($params['from']))); - if (isset ($params['prefix'])) - $this->addWhere("pl_title LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'"); + if ( !is_null( $params['from'] ) ) + $this->addWhere( 'pl_title>=' . $db->addQuotes( $this->titlePartToKey( $params['from'] ) ) ); + if ( isset ( $params['prefix'] ) ) + $this->addWhere( 'pl_title' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) ); - $this->addFields(array ( + $this->addFields( array ( 'pl_title', - )); - $this->addFieldsIf('pl_from', !$params['unique']); + ) ); + $this->addFieldsIf( 'pl_from', !$params['unique'] ); - $this->addOption('USE INDEX', 'pl_namespace'); + $this->addOption( 'USE INDEX', 'pl_namespace' ); $limit = $params['limit']; - $this->addOption('LIMIT', $limit+1); - if($params['unique']) - $this->addOption('ORDER BY', 'pl_title'); + $this->addOption( 'LIMIT', $limit + 1 ); + if ( $params['unique'] ) + $this->addOption( 'ORDER BY', 'pl_title' ); else - $this->addOption('ORDER BY', 'pl_title, pl_from'); + $this->addOption( 'ORDER BY', 'pl_title, pl_from' ); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $pageids = array (); $count = 0; $result = $this->getResult(); - while ($row = $db->fetchObject($res)) { - if (++ $count > $limit) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown - if($params['unique']) - $this->setContinueEnumParameter('from', $this->keyToTitle($row->pl_title)); + if ( $params['unique'] ) + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) ); else - $this->setContinueEnumParameter('continue', $this->keyToTitle($row->pl_title) . "|" . $row->pl_from); + $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from ); break; } - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $vals = array(); - if ($fld_ids) - $vals['fromid'] = intval($row->pl_from); - if ($fld_title) { - $title = Title :: makeTitle($params['namespace'], $row->pl_title); - ApiQueryBase::addTitleInfo($vals, $title); + if ( $fld_ids ) + $vals['fromid'] = intval( $row->pl_from ); + if ( $fld_title ) { + $title = Title :: makeTitle( $params['namespace'], $row->pl_title ); + ApiQueryBase::addTitleInfo( $vals, $title ); } - $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { - if($params['unique']) - $this->setContinueEnumParameter('from', $this->keyToTitle($row->pl_title)); + if ( $params['unique'] ) + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) ); else - $this->setContinueEnumParameter('continue', $this->keyToTitle($row->pl_title) . "|" . $row->pl_from); + $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from ); break; } } else { $pageids[] = $row->pl_from; } } - $db->freeResult($res); + $db->freeResult( $res ); - if (is_null($resultPageSet)) { - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'l'); + if ( is_null( $resultPageSet ) ) { + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'l' ); } else { - $resultPageSet->populateFromPageIDs($pageids); + $resultPageSet->populateFromPageIDs( $pageids ); } } @@ -192,6 +192,15 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { public function getDescription() { return 'Enumerate all links that point to a given namespace'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'params', 'info' => $this->getModuleName() . ' cannot be used as a generator in unique links mode' ), + array( 'code' => 'params', 'info' => $this->getModuleName() . ' cannot return corresponding page ids in unique links mode' ), + array( 'code' => 'params', 'info' => 'alcontinue and alfrom cannot be used together' ), + array( 'code' => 'badcontinue', 'info' => 'Invalid continue parameter' ), + ) ); + } protected function getExamples() { return array ( @@ -200,6 +209,6 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllLinks.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php index c18f6dd1..f8d475cc 100644 --- a/includes/api/ApiQueryAllUsers.php +++ b/includes/api/ApiQueryAllUsers.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryAllUsers extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'au'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'au' ); } public function execute() { @@ -44,67 +44,74 @@ class ApiQueryAllUsers extends ApiQueryBase { $params = $this->extractRequestParams(); $prop = $params['prop']; - if (!is_null($prop)) { - $prop = array_flip($prop); - $fld_blockinfo = isset($prop['blockinfo']); - $fld_editcount = isset($prop['editcount']); - $fld_groups = isset($prop['groups']); - $fld_registration = isset($prop['registration']); - } else { + if ( !is_null( $prop ) ) { + $prop = array_flip( $prop ); + $fld_blockinfo = isset( $prop['blockinfo'] ); + $fld_editcount = isset( $prop['editcount'] ); + $fld_groups = isset( $prop['groups'] ); + $fld_registration = isset( $prop['registration'] ); + } else { $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = false; } $limit = $params['limit']; - $this->addTables('user', 'u1'); + $this->addTables( 'user', 'u1' ); + $useIndex = true; - if (!is_null($params['from'])) - $this->addWhere('u1.user_name >= ' . $db->addQuotes($this->keyToTitle($params['from']))); + if ( !is_null( $params['from'] ) ) + $this->addWhere( 'u1.user_name >= ' . $db->addQuotes( $this->keyToTitle( $params['from'] ) ) ); - if (!is_null($params['prefix'])) - $this->addWhere('u1.user_name LIKE "' . $db->escapeLike($this->keyToTitle( $params['prefix'])) . '%"'); + if ( !is_null( $params['prefix'] ) ) + $this->addWhere( 'u1.user_name' . $db->buildLike( $this->keyToTitle( $params['prefix'] ), $db->anyString() ) ); - if (!is_null($params['group'])) { + if ( !is_null( $params['group'] ) ) { + $useIndex = false; // Filter only users that belong to a given group - $this->addTables('user_groups', 'ug1'); - $this->addWhere('ug1.ug_user=u1.user_id'); - $this->addWhereFld('ug1.ug_group', $params['group']); + $this->addTables( 'user_groups', 'ug1' ); + $ug1 = $this->getAliasedName( 'user_groups', 'ug1' ); + $this->addJoinConds( array( $ug1 => array( 'INNER JOIN', array( 'ug1.ug_user=u1.user_id', + 'ug1.ug_group' => $params['group'] ) ) ) ); } - if ($params['witheditsonly']) - $this->addWhere('u1.user_editcount > 0'); + if ( $params['witheditsonly'] ) + $this->addWhere( 'u1.user_editcount > 0' ); - if ($fld_groups) { + if ( $fld_groups ) { // Show the groups the given users belong to // request more than needed to avoid not getting all rows that belong to one user - $groupCount = count(User::getAllGroups()); - $sqlLimit = $limit+$groupCount+1; + $groupCount = count( User::getAllGroups() ); + $sqlLimit = $limit + $groupCount + 1; - $this->addTables('user_groups', 'ug2'); - $tname = $this->getAliasedName('user_groups', 'ug2'); - $this->addJoinConds(array($tname => array('LEFT JOIN', 'ug2.ug_user=u1.user_id'))); - $this->addFields('ug2.ug_group ug_group2'); + $this->addTables( 'user_groups', 'ug2' ); + $tname = $this->getAliasedName( 'user_groups', 'ug2' ); + $this->addJoinConds( array( $tname => array( 'LEFT JOIN', 'ug2.ug_user=u1.user_id' ) ) ); + $this->addFields( 'ug2.ug_group ug_group2' ); } else { - $sqlLimit = $limit+1; + $sqlLimit = $limit + 1; } - if ($fld_blockinfo) { - $this->addTables('ipblocks'); - $this->addTables('user', 'u2'); - $u2 = $this->getAliasedName('user', 'u2'); - $this->addJoinConds(array( - 'ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'), - $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id'))); - $this->addFields(array('ipb_reason', 'u2.user_name blocker_name')); + if ( $fld_blockinfo ) { + $this->addTables( 'ipblocks' ); + $this->addTables( 'user', 'u2' ); + $u2 = $this->getAliasedName( 'user', 'u2' ); + $this->addJoinConds( array( + 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=u1.user_id' ), + $u2 => array( 'LEFT JOIN', 'ipb_by=u2.user_id' ) ) ); + $this->addFields( array( 'ipb_reason', 'u2.user_name AS blocker_name' ) ); } - $this->addOption('LIMIT', $sqlLimit); + $this->addOption( 'LIMIT', $sqlLimit ); - $this->addFields('u1.user_name'); - $this->addFieldsIf('u1.user_editcount', $fld_editcount); - $this->addFieldsIf('u1.user_registration', $fld_registration); + $this->addFields( 'u1.user_name' ); + $this->addFieldsIf( 'u1.user_editcount', $fld_editcount ); + $this->addFieldsIf( 'u1.user_registration', $fld_registration ); - $this->addOption('ORDER BY', 'u1.user_name'); + $this->addOption( 'ORDER BY', 'u1.user_name' ); + if ( $useIndex ) { + $u1 = $this->getAliasedName( 'user', 'u1' ); + $this->addOption( 'USE INDEX', array( $u1 => 'user_name' ) ); + } - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $data = array (); $count = 0; @@ -119,66 +126,67 @@ class ApiQueryAllUsers extends ApiQueryBase { // The setContinue... is more complex because of this, and takes into account the higher sql limit // to make sure all rows that belong to the same user are received. // - while (true) { + while ( true ) { - $row = $db->fetchObject($res); + $row = $db->fetchObject( $res ); $count++; - if (!$row || $lastUser !== $row->user_name) { + if ( !$row || $lastUser !== $row->user_name ) { // Save the last pass's user data - if (is_array($lastUserData)) + if ( is_array( $lastUserData ) ) { - $fit = $result->addValue(array('query', $this->getModuleName()), - null, $lastUserData); - if(!$fit) + $fit = $result->addValue( array( 'query', $this->getModuleName() ), + null, $lastUserData ); + if ( !$fit ) { - $this->setContinueEnumParameter('from', - $this->keyToTitle($lastUserData['name'])); + $this->setContinueEnumParameter( 'from', + $this->keyToTitle( $lastUserData['name'] ) ); break; } } // No more rows left - if (!$row) + if ( !$row ) break; - if ($count > $limit) { + 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', $this->keyToTitle( $row->user_name ) ); break; } // Record new user's data $lastUser = $row->user_name; $lastUserData = array( 'name' => $lastUser ); - if ($fld_blockinfo) { + if ( $fld_blockinfo ) { $lastUserData['blockedby'] = $row->blocker_name; $lastUserData['blockreason'] = $row->ipb_reason; } - if ($fld_editcount) - $lastUserData['editcount'] = intval($row->user_editcount); - if ($fld_registration) - $lastUserData['registration'] = wfTimestamp(TS_ISO_8601, $row->user_registration); + if ( $fld_editcount ) + $lastUserData['editcount'] = intval( $row->user_editcount ); + if ( $fld_registration ) + $lastUserData['registration'] = $row->user_registration ? + wfTimestamp( TS_ISO_8601, $row->user_registration ) : ''; } - if ($sqlLimit == $count) { + if ( $sqlLimit == $count ) { // BUG! database contains group name that User::getAllGroups() does not return // TODO: should handle this more gracefully - ApiBase :: dieDebug(__METHOD__, - 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function'); + ApiBase :: dieDebug( __METHOD__, + 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function' ); } // Add user's group info - if ($fld_groups && !is_null($row->ug_group2)) { + if ( $fld_groups && !is_null( $row->ug_group2 ) ) { $lastUserData['groups'][] = $row->ug_group2; - $result->setIndexedTagName($lastUserData['groups'], 'g'); + $result->setIndexedTagName( $lastUserData['groups'], 'g' ); } } - $db->freeResult($res); + $db->freeResult( $res ); - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'u'); + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'u' ); } public function getCacheMode( $params ) { @@ -219,7 +227,7 @@ class ApiQueryAllUsers extends ApiQueryBase { 'group' => 'Limit users to a given group name', 'prop' => array( 'What pieces of information to include.', - '`groups` property uses more server resources and may return fewer results than the limit.'), + '`groups` property uses more server resources and may return fewer results than the limit.' ), 'limit' => 'How many total user names to return.', 'witheditsonly' => 'Only list users who have made edits', ); @@ -236,6 +244,6 @@ class ApiQueryAllUsers extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllUsers.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllUsers.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryAllimages.php b/includes/api/ApiQueryAllimages.php index 983c6469..0a745516 100644 --- a/includes/api/ApiQueryAllimages.php +++ b/includes/api/ApiQueryAllimages.php @@ -24,9 +24,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -36,8 +36,19 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryAllimages extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'ai'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'ai' ); + $this->mRepo = RepoGroup::singleton()->getLocalRepo(); + } + + /** + * Overide 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. + */ + protected function getDB() { + return $this->mRepo->getSlaveDB(); } public function execute() { @@ -48,89 +59,89 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - if ($resultPageSet->isResolvingRedirects()) - $this->dieUsage('Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator', 'params'); + 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); + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { - $repo = RepoGroup::singleton()->getLocalRepo(); + private function run( $resultPageSet = null ) { + $repo = $this->mRepo; if ( !$repo instanceof LocalRepo ) - $this->dieUsage('Local file repository does not support querying all images', 'unsupportedrepo'); + $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'])); - $this->addWhereRange('img_name', $dir, $from, null); - if (isset ($params['prefix'])) - $this->addWhere("img_name LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'"); - - if (isset ($params['minsize'])) { - $this->addWhere('img_size>=' . intval($params['minsize'])); + $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' ); + $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) ); + $this->addWhereRange( 'img_name', $dir, $from, null ); + if ( isset ( $params['prefix'] ) ) + $this->addWhere( 'img_name' . $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'])); + if ( isset ( $params['maxsize'] ) ) { + $this->addWhere( 'img_size<=' . intval( $params['maxsize'] ) ); } $sha1 = false; - if( isset( $params['sha1'] ) ) { + if ( isset( $params['sha1'] ) ) { $sha1 = wfBaseConvert( $params['sha1'], 16, 36, 31 ); - } elseif( isset( $params['sha1base36'] ) ) { + } elseif ( isset( $params['sha1base36'] ) ) { $sha1 = $params['sha1base36']; } - if( $sha1 ) { + if ( $sha1 ) { $this->addWhere( 'img_sha1=' . $db->addQuotes( $sha1 ) ); } - $this->addTables('image'); + $this->addTables( 'image' ); - $prop = array_flip($params['prop']); + $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' : '')); + $this->addOption( 'LIMIT', $limit + 1 ); + $this->addOption( 'ORDER BY', 'img_name' . + ( $params['dir'] == 'descending' ? ' DESC' : '' ) ); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $titles = array(); $count = 0; $result = $this->getResult(); - while ($row = $db->fetchObject($res)) { - if (++ $count > $limit) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown - $this->setContinueEnumParameter('from', $this->keyToTitle($row->img_name)); + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->img_name ) ); break; } - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $file = $repo->newFileFromRow( $row ); - $info = array_merge(array('name' => $row->img_name), - ApiQueryImageInfo::getInfo($file, $prop, $result)); - $fit = $result->addValue(array('query', $this->getModuleName()), null, $info); - if( !$fit ) { - $this->setContinueEnumParameter('from', $this->keyToTitle($row->img_name)); + $info = array_merge( array( 'name' => $row->img_name ), + ApiQueryImageInfo::getInfo( $file, $prop, $result ) ); + $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); + $titles[] = Title::makeTitle( NS_IMAGE, $row->img_name ); } } - $db->freeResult($res); + $db->freeResult( $res ); - if (is_null($resultPageSet)) { - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'img'); + if ( is_null( $resultPageSet ) ) { + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'img' ); } else { - $resultPageSet->populateFromTitles($titles); + $resultPageSet->populateFromTitles( $titles ); } } @@ -161,18 +172,7 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { 'sha1' => null, 'sha1base36' => null, 'prop' => array ( - ApiBase :: PARAM_TYPE => array( - 'timestamp', - 'user', - 'comment', - 'url', - 'size', - 'dimensions', // Obsolete - 'mime', - 'sha1', - 'metadata', - 'bitdepth', - ), + ApiBase :: PARAM_TYPE => ApiQueryImageInfo::getPropertyNames(), ApiBase :: PARAM_DFLT => 'timestamp|url', ApiBase :: PARAM_ISMULTI => true ) @@ -196,6 +196,13 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { 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' ), + ) ); + } protected function getExamples() { return array ( @@ -209,6 +216,6 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllimages.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllimages.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryAllmessages.php b/includes/api/ApiQueryAllmessages.php index c615daf4..7dd9d874 100644 --- a/includes/api/ApiQueryAllmessages.php +++ b/includes/api/ApiQueryAllmessages.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,79 +35,98 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryAllmessages extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'am'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'am' ); } public function execute() { - global $wgMessageCache; $params = $this->extractRequestParams(); - if(!is_null($params['lang'])) + if ( !is_null( $params['lang'] ) ) { global $wgLang; - $wgLang = Language::factory($params['lang']); + $wgLang = Language::factory( $params['lang'] ); } + + $prop = array_flip( (array)$params['prop'] ); - - //Determine which messages should we print + // Determine which messages should we print $messages_target = array(); - if( $params['messages'] == '*' ) { - $wgMessageCache->loadAllMessages(); - $message_names = array_keys( array_merge( Language::getMessagesFor( 'en' ), $wgMessageCache->getExtensionMessagesFor( 'en' ) ) ); + if ( in_array( '*', $params['messages'] ) ) { + $message_names = array_keys( Language::getMessagesFor( 'en' ) ); sort( $message_names ); $messages_target = $message_names; } else { - $messages_target = explode( '|', $params['messages'] ); + $messages_target = $params['messages']; } - //Filter messages - if( isset( $params['filter'] ) ) { + // Filter messages + if ( isset( $params['filter'] ) ) { $messages_filtered = array(); - foreach( $messages_target as $message ) { - if( strpos( $message, $params['filter'] ) !== false ) { //!== is used because filter can be at the beginnig of the string + foreach ( $messages_target as $message ) { + if ( strpos( $message, $params['filter'] ) !== false ) { // !== is used because filter can be at the beginnig of the string $messages_filtered[] = $message; } } $messages_target = $messages_filtered; } - //Get all requested messages + // Get all requested messages and print the result $messages = array(); - $skip = !is_null($params['from']); - foreach( $messages_target as $message ) { + $skip = !is_null( $params['from'] ); + $result = $this->getResult(); + foreach ( $messages_target as $message ) { // Skip all messages up to $params['from'] - if($skip && $message === $params['from']) + if ( $skip && $message === $params['from'] ) $skip = false; - if(!$skip) - $messages[$message] = wfMsg( $message ); - } - //Print the result - $result = $this->getResult(); - $messages_out = array(); - foreach( $messages as $name => $value ) { - $message = array(); - $message['name'] = $name; - if( wfEmptyMsg( $name, $value ) ) { - $message['missing'] = ''; - } else { - $result->setContent( $message, $value ); - } - $fit = $result->addValue(array('query', $this->getModuleName()), null, $message); - if(!$fit) - { - $this->setContinueEnumParameter('from', $name); - break; + if ( !$skip ) { + $a = array( 'name' => $message ); + $args = null; + if ( isset( $params['args'] ) && count( $params['args'] ) != 0 ) { + $args = $params['args']; + } + // Check if the parser is enabled: + if ( $params['enableparser'] ) { + $msg = wfMsgExt( $message, array( 'parsemag' ), $args ); + } else if ( $args ) { + $msgString = wfMsgGetKey( $message, true, false, false ); + $msg = wfMsgReplaceArgs( $msgString, $args ); + } else { + $msg = wfMsgGetKey( $message, true, false, false ); + } + + if ( wfEmptyMsg( $message, $msg ) ) + $a['missing'] = ''; + else { + ApiResult::setContent( $a, $msg ); + if ( isset( $prop['default'] ) ) { + $default = wfMsgGetKey( $message, false, false, false ); + if ( $default !== $msg ) { + if ( wfEmptyMsg( $message, $default ) ) + $a['defaultmissing'] = ''; + else + $a['default'] = $default; + } + } + } + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $a ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'from', $name ); + break; + } } } - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'message'); + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'message' ); } public function getCacheMode( $params ) { if ( is_null( $params['lang'] ) ) { // Language not specified, will be fetched from preferences return 'anon-public-user-private'; + } elseif ( $params['enableparser'] ) { + // User-specific parser options will be used + return 'anon-public-user-private'; } else { // OK to cache return 'public'; @@ -118,6 +137,17 @@ class ApiQueryAllmessages extends ApiQueryBase { return array ( 'messages' => array ( ApiBase :: PARAM_DFLT => '*', + ApiBase :: PARAM_ISMULTI => true, + ), + 'prop' => array( + ApiBase :: PARAM_ISMULTI => true, + ApiBase :: PARAM_TYPE => array( + 'default' + ) + ), + 'enableparser' => false, + 'args' => array( + ApiBase :: PARAM_ISMULTI => true ), 'filter' => array(), 'lang' => null, @@ -128,6 +158,10 @@ class ApiQueryAllmessages extends ApiQueryBase { public function getParamDescription() { return array ( 'messages' => 'Which messages to output. "*" means all messages', + 'prop' => 'Which properties to get', + 'enableparser' => array( 'Set to enable parser, will preprocess the wikitext of message', + 'Will substitute magic words, handle templates etc.' ), + 'args' => 'Arguments to be substituted into message', 'filter' => 'Return only messages that contain this string', 'lang' => 'Return messages in this language', 'from' => 'Return messages starting at this message', @@ -146,6 +180,6 @@ class ApiQueryAllmessages extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllmessages.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllmessages.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php index e123e8fe..37f22ee2 100644 --- a/includes/api/ApiQueryAllpages.php +++ b/includes/api/ApiQueryAllpages.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryAllpages extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'ap'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'ap' ); } public function execute() { @@ -47,31 +47,35 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - if ($resultPageSet->isResolvingRedirects()) - $this->dieUsage('Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator', 'params'); + public function executeGenerator( $resultPageSet ) { + if ( $resultPageSet->isResolvingRedirects() ) + $this->dieUsage( 'Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator', 'params' ); - $this->run($resultPageSet); + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { - + private function run( $resultPageSet = null ) { $db = $this->getDB(); $params = $this->extractRequestParams(); // Page filters - $this->addTables('page'); - if (!$this->addWhereIf('page_is_redirect = 1', $params['filterredir'] === 'redirects')) - $this->addWhereIf('page_is_redirect = 0', $params['filterredir'] === 'nonredirects'); - $this->addWhereFld('page_namespace', $params['namespace']); - $dir = ($params['dir'] == 'descending' ? 'older' : 'newer'); - $from = (is_null($params['from']) ? null : $this->titlePartToKey($params['from'])); - $this->addWhereRange('page_title', $dir, $from, null); - if (isset ($params['prefix'])) - $this->addWhere("page_title LIKE '" . $db->escapeLike($this->titlePartToKey($params['prefix'])) . "%'"); - - if (is_null($resultPageSet)) { + $this->addTables( 'page' ); + + if ( $params['filterredir'] == 'redirects' ) + $this->addWhereFld( 'page_is_redirect', 1 ); + else if ( $params['filterredir'] == 'nonredirects' ) + $this->addWhereFld( 'page_is_redirect', 0 ); + + $this->addWhereFld( 'page_namespace', $params['namespace'] ); + $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' ); + $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) ); + $this->addWhereRange( 'page_title', $dir, $from, null ); + + if ( isset ( $params['prefix'] ) ) + $this->addWhere( 'page_title' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) ); + + if ( is_null( $resultPageSet ) ) { $selectFields = array ( 'page_namespace', 'page_title', @@ -80,95 +84,98 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { } else { $selectFields = $resultPageSet->getPageTableFields(); } - $this->addFields($selectFields); + + $this->addFields( $selectFields ); $forceNameTitleIndex = true; - if (isset ($params['minsize'])) { - $this->addWhere('page_len>=' . intval($params['minsize'])); + if ( isset ( $params['minsize'] ) ) { + $this->addWhere( 'page_len>=' . intval( $params['minsize'] ) ); $forceNameTitleIndex = false; } - if (isset ($params['maxsize'])) { - $this->addWhere('page_len<=' . intval($params['maxsize'])); + if ( isset ( $params['maxsize'] ) ) { + $this->addWhere( 'page_len<=' . intval( $params['maxsize'] ) ); $forceNameTitleIndex = false; } // Page protection filtering - if (!empty ($params['prtype'])) { - $this->addTables('page_restrictions'); - $this->addWhere('page_id=pr_page'); - $this->addWhere('pr_expiry>' . $db->addQuotes($db->timestamp())); - $this->addWhereFld('pr_type', $params['prtype']); - - // Remove the empty string and '*' from the prlevel array - $prlevel = array_diff($params['prlevel'], array('', '*')); - if (!empty($prlevel)) - $this->addWhereFld('pr_level', $prlevel); - if ($params['prfiltercascade'] == 'cascading') - $this->addWhereFld('pr_cascade', 1); - if ($params['prfiltercascade'] == 'noncascading') - $this->addWhereFld('pr_cascade', 0); - - $this->addOption('DISTINCT'); + if ( !empty ( $params['prtype'] ) ) { + $this->addTables( 'page_restrictions' ); + $this->addWhere( 'page_id=pr_page' ); + $this->addWhere( 'pr_expiry>' . $db->addQuotes( $db->timestamp() ) ); + $this->addWhereFld( 'pr_type', $params['prtype'] ); + + if ( isset ( $params['prlevel'] ) ) { + // Remove the empty string and '*' from the prlevel array + $prlevel = array_diff( $params['prlevel'], array( '', '*' ) ); + + if ( !empty( $prlevel ) ) + $this->addWhereFld( 'pr_level', $prlevel ); + } + if ( $params['prfiltercascade'] == 'cascading' ) + $this->addWhereFld( 'pr_cascade', 1 ); + else if ( $params['prfiltercascade'] == 'noncascading' ) + $this->addWhereFld( 'pr_cascade', 0 ); + + $this->addOption( 'DISTINCT' ); $forceNameTitleIndex = false; - } else if (isset ($params['prlevel'])) { - $this->dieUsage('prlevel may not be used without prtype', 'params'); + } else if ( isset ( $params['prlevel'] ) ) { + $this->dieUsage( 'prlevel may not be used without prtype', 'params' ); } - if($params['filterlanglinks'] == 'withoutlanglinks') { - $this->addTables('langlinks'); - $this->addJoinConds(array('langlinks' => array('LEFT JOIN', 'page_id=ll_from'))); - $this->addWhere('ll_from IS NULL'); + if ( $params['filterlanglinks'] == 'withoutlanglinks' ) { + $this->addTables( 'langlinks' ); + $this->addJoinConds( array( 'langlinks' => array( 'LEFT JOIN', 'page_id=ll_from' ) ) ); + $this->addWhere( 'll_from IS NULL' ); $forceNameTitleIndex = false; - } else if($params['filterlanglinks'] == 'withlanglinks') { - $this->addTables('langlinks'); - $this->addWhere('page_id=ll_from'); - $this->addOption('STRAIGHT_JOIN'); + } else if ( $params['filterlanglinks'] == 'withlanglinks' ) { + $this->addTables( 'langlinks' ); + $this->addWhere( 'page_id=ll_from' ); + $this->addOption( 'STRAIGHT_JOIN' ); // We have to GROUP BY all selected fields to stop // PostgreSQL from whining - $this->addOption('GROUP BY', implode(', ', $selectFields)); + $this->addOption( 'GROUP BY', implode( ', ', $selectFields ) ); $forceNameTitleIndex = false; } - if ($forceNameTitleIndex) - $this->addOption('USE INDEX', 'name_title'); - + if ( $forceNameTitleIndex ) + $this->addOption( 'USE INDEX', 'name_title' ); $limit = $params['limit']; - $this->addOption('LIMIT', $limit+1); - $res = $this->select(__METHOD__); + $this->addOption( 'LIMIT', $limit + 1 ); + $res = $this->select( __METHOD__ ); $count = 0; $result = $this->getResult(); - while ($row = $db->fetchObject($res)) { - if (++ $count > $limit) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown - $this->setContinueEnumParameter('from', $this->keyToTitle($row->page_title)); + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) ); break; } - if (is_null($resultPageSet)) { - $title = Title :: makeTitle($row->page_namespace, $row->page_title); + if ( is_null( $resultPageSet ) ) { + $title = Title :: makeTitle( $row->page_namespace, $row->page_title ); $vals = array( - 'pageid' => intval($row->page_id), - 'ns' => intval($title->getNamespace()), - 'title' => $title->getPrefixedText()); - $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) + 'pageid' => intval( $row->page_id ), + 'ns' => intval( $title->getNamespace() ), + 'title' => $title->getPrefixedText() ); + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('from', $this->keyToTitle($row->page_title)); + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) ); break; } } else { - $resultPageSet->processDbRow($row); + $resultPageSet->processDbRow( $row ); } } - $db->freeResult($res); + $db->freeResult( $res ); - if (is_null($resultPageSet)) { - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'p'); + if ( is_null( $resultPageSet ) ) { + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'p' ); } } @@ -257,6 +264,13 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { public function getDescription() { return 'Enumerate all pages sequentially in a given namespace'; } + + public function getPossibleErrors() { + 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' ), + ) ); + } protected function getExamples() { return array ( @@ -272,6 +286,6 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllpages.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllpages.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php index 1b1fe639..648da069 100644 --- a/includes/api/ApiQueryBacklinks.php +++ b/includes/api/ApiQueryBacklinks.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -61,18 +61,18 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { ) ); - public function __construct($query, $moduleName) { - extract($this->backlinksSettings[$moduleName]); + public function __construct( $query, $moduleName ) { + extract( $this->backlinksSettings[$moduleName] ); $this->resultArr = array(); - parent :: __construct($query, $moduleName, $code); + parent :: __construct( $query, $moduleName, $code ); $this->bl_ns = $prefix . '_namespace'; $this->bl_from = $prefix . '_from'; $this->bl_table = $linktbl; $this->bl_code = $code; $this->hasNS = $moduleName !== 'imageusage'; - if ($this->hasNS) { + if ( $this->hasNS ) { $this->bl_title = $prefix . '_title'; $this->bl_sort = "{$this->bl_ns}, {$this->bl_title}, {$this->bl_from}"; $this->bl_fields = array ( @@ -96,210 +96,223 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function prepareFirstQuery($resultPageSet = null) { + private function prepareFirstQuery( $resultPageSet = null ) { /* SELECT page_id, page_title, page_namespace, page_is_redirect * FROM pagelinks, page WHERE pl_from=page_id * AND pl_title='Foo' AND pl_namespace=0 * LIMIT 11 ORDER BY pl_from */ $db = $this->getDB(); - $this->addTables(array('page', $this->bl_table)); - $this->addWhere("{$this->bl_from}=page_id"); - if(is_null($resultPageSet)) - $this->addFields(array('page_id', 'page_title', 'page_namespace')); + $this->addTables( array( $this->bl_table, 'page' ) ); + $this->addWhere( "{$this->bl_from}=page_id" ); + if ( is_null( $resultPageSet ) ) + $this->addFields( array( 'page_id', 'page_title', 'page_namespace' ) ); else - $this->addFields($resultPageSet->getPageTableFields()); - $this->addFields('page_is_redirect'); - $this->addWhereFld($this->bl_title, $this->rootTitle->getDBKey()); - if($this->hasNS) - $this->addWhereFld($this->bl_ns, $this->rootTitle->getNamespace()); - $this->addWhereFld('page_namespace', $this->params['namespace']); - if(!is_null($this->contID)) - $this->addWhere("{$this->bl_from}>={$this->contID}"); - if($this->params['filterredir'] == 'redirects') - $this->addWhereFld('page_is_redirect', 1); - if($this->params['filterredir'] == 'nonredirects') - $this->addWhereFld('page_is_redirect', 0); - $this->addOption('LIMIT', $this->params['limit'] + 1); - $this->addOption('ORDER BY', $this->bl_from); + $this->addFields( $resultPageSet->getPageTableFields() ); + + $this->addFields( 'page_is_redirect' ); + $this->addWhereFld( $this->bl_title, $this->rootTitle->getDBkey() ); + + if ( $this->hasNS ) + $this->addWhereFld( $this->bl_ns, $this->rootTitle->getNamespace() ); + $this->addWhereFld( 'page_namespace', $this->params['namespace'] ); + + if ( !is_null( $this->contID ) ) + $this->addWhere( "{$this->bl_from}>={$this->contID}" ); + + if ( $this->params['filterredir'] == 'redirects' ) + $this->addWhereFld( 'page_is_redirect', 1 ); + else if ( $this->params['filterredir'] == 'nonredirects' && !$this->redirect ) + // bug 22245 - Check for !redirect, as filtering nonredirects, when getting what links to them is contradictory + $this->addWhereFld( 'page_is_redirect', 0 ); + + $this->addOption( 'LIMIT', $this->params['limit'] + 1 ); + $this->addOption( 'ORDER BY', $this->bl_from ); + $this->addOption( 'STRAIGHT_JOIN' ); } - private function prepareSecondQuery($resultPageSet = null) { + private function prepareSecondQuery( $resultPageSet = null ) { /* SELECT page_id, page_title, page_namespace, page_is_redirect, pl_title, pl_namespace FROM pagelinks, page WHERE pl_from=page_id AND (pl_title='Foo' AND pl_namespace=0) OR (pl_title='Bar' AND pl_namespace=1) ORDER BY pl_namespace, pl_title, pl_from LIMIT 11 */ $db = $this->getDB(); - $this->addTables(array('page', $this->bl_table)); - $this->addWhere("{$this->bl_from}=page_id"); - if(is_null($resultPageSet)) - $this->addFields(array('page_id', 'page_title', 'page_namespace', 'page_is_redirect')); + $this->addTables( array( 'page', $this->bl_table ) ); + $this->addWhere( "{$this->bl_from}=page_id" ); + + if ( is_null( $resultPageSet ) ) + $this->addFields( array( 'page_id', 'page_title', 'page_namespace', 'page_is_redirect' ) ); else - $this->addFields($resultPageSet->getPageTableFields()); - $this->addFields($this->bl_title); - if($this->hasNS) - $this->addFields($this->bl_ns); + $this->addFields( $resultPageSet->getPageTableFields() ); + + $this->addFields( $this->bl_title ); + if ( $this->hasNS ) + $this->addFields( $this->bl_ns ); + // We can't use LinkBatch here because $this->hasNS may be false $titleWhere = array(); - foreach($this->redirTitles as $t) - $titleWhere[] = "{$this->bl_title} = ".$db->addQuotes($t->getDBKey()). - ($this->hasNS ? " AND {$this->bl_ns} = '{$t->getNamespace()}'" : ""); - $this->addWhere($db->makeList($titleWhere, LIST_OR)); - $this->addWhereFld('page_namespace', $this->params['namespace']); - if(!is_null($this->redirID)) + foreach ( $this->redirTitles as $t ) + $titleWhere[] = "{$this->bl_title} = " . $db->addQuotes( $t->getDBkey() ) . + ( $this->hasNS ? " AND {$this->bl_ns} = '{$t->getNamespace()}'" : "" ); + $this->addWhere( $db->makeList( $titleWhere, LIST_OR ) ); + $this->addWhereFld( 'page_namespace', $this->params['namespace'] ); + + if ( !is_null( $this->redirID ) ) { $first = $this->redirTitles[0]; - $title = $db->strencode($first->getDBKey()); + $title = $db->strencode( $first->getDBkey() ); $ns = $first->getNamespace(); $from = $this->redirID; - if($this->hasNS) - $this->addWhere("{$this->bl_ns} > $ns OR ". - "({$this->bl_ns} = $ns AND ". - "({$this->bl_title} > '$title' OR ". - "({$this->bl_title} = '$title' AND ". - "{$this->bl_from} >= $from)))"); + if ( $this->hasNS ) + $this->addWhere( "{$this->bl_ns} > $ns OR " . + "({$this->bl_ns} = $ns AND " . + "({$this->bl_title} > '$title' OR " . + "({$this->bl_title} = '$title' AND " . + "{$this->bl_from} >= $from)))" ); else - $this->addWhere("{$this->bl_title} > '$title' OR ". - "({$this->bl_title} = '$title' AND ". - "{$this->bl_from} >= $from)"); + $this->addWhere( "{$this->bl_title} > '$title' OR " . + "({$this->bl_title} = '$title' AND " . + "{$this->bl_from} >= $from)" ); } - if($this->params['filterredir'] == 'redirects') - $this->addWhereFld('page_is_redirect', 1); - if($this->params['filterredir'] == 'nonredirects') - $this->addWhereFld('page_is_redirect', 0); - $this->addOption('LIMIT', $this->params['limit'] + 1); - $this->addOption('ORDER BY', $this->bl_sort); - $this->addOption('USE INDEX', array('page' => 'PRIMARY')); + if ( $this->params['filterredir'] == 'redirects' ) + $this->addWhereFld( 'page_is_redirect', 1 ); + else if ( $this->params['filterredir'] == 'nonredirects' ) + $this->addWhereFld( 'page_is_redirect', 0 ); + + $this->addOption( 'LIMIT', $this->params['limit'] + 1 ); + $this->addOption( 'ORDER BY', $this->bl_sort ); + $this->addOption( 'USE INDEX', array( 'page' => 'PRIMARY' ) ); } - private function run($resultPageSet = null) { - $this->params = $this->extractRequestParams(false); - $this->redirect = isset($this->params['redirect']) && $this->params['redirect']; - $userMax = ( $this->redirect ? ApiBase::LIMIT_BIG1/2 : ApiBase::LIMIT_BIG1 ); - $botMax = ( $this->redirect ? ApiBase::LIMIT_BIG2/2 : ApiBase::LIMIT_BIG2 ); - if( $this->params['limit'] == 'max' ) { + private function run( $resultPageSet = null ) { + $this->params = $this->extractRequestParams( false ); + $this->redirect = isset( $this->params['redirect'] ) && $this->params['redirect']; + $userMax = ( $this->redirect ? ApiBase::LIMIT_BIG1 / 2 : ApiBase::LIMIT_BIG1 ); + $botMax = ( $this->redirect ? ApiBase::LIMIT_BIG2 / 2 : ApiBase::LIMIT_BIG2 ); + if ( $this->params['limit'] == 'max' ) { $this->params['limit'] = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; $this->getResult()->addValue( 'limits', $this->getModuleName(), $this->params['limit'] ); } $this->processContinue(); - $this->prepareFirstQuery($resultPageSet); + $this->prepareFirstQuery( $resultPageSet ); $db = $this->getDB(); - $res = $this->select(__METHOD__.'::firstQuery'); + $res = $this->select( __METHOD__ . '::firstQuery' ); $count = 0; $this->pageMap = array(); // Maps ns and title to pageid $this->continueStr = null; $this->redirTitles = array(); - while ($row = $db->fetchObject($res)) { - if (++ $count > $this->params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $this->params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // Continue string preserved in case the redirect query doesn't pass the limit - $this->continueStr = $this->getContinueStr($row->page_id); + $this->continueStr = $this->getContinueStr( $row->page_id ); break; } - if (is_null($resultPageSet)) - $this->extractRowInfo($row); + if ( is_null( $resultPageSet ) ) + $this->extractRowInfo( $row ); else { $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id; - if($row->page_is_redirect) - $this->redirTitles[] = Title::makeTitle($row->page_namespace, $row->page_title); - $resultPageSet->processDbRow($row); + if ( $row->page_is_redirect ) + $this->redirTitles[] = Title::makeTitle( $row->page_namespace, $row->page_title ); + + $resultPageSet->processDbRow( $row ); } } - $db->freeResult($res); + $db->freeResult( $res ); - if($this->redirect && count($this->redirTitles)) + if ( $this->redirect && count( $this->redirTitles ) ) { $this->resetQueryParams(); - $this->prepareSecondQuery($resultPageSet); - $res = $this->select(__METHOD__.'::secondQuery'); + $this->prepareSecondQuery( $resultPageSet ); + $res = $this->select( __METHOD__ . '::secondQuery' ); $count = 0; - while($row = $db->fetchObject($res)) + while ( $row = $db->fetchObject( $res ) ) { - if(++$count > $this->params['limit']) + if ( ++$count > $this->params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // We need to keep the parent page of this redir in - if($this->hasNS) - $parentID = $this->pageMap[$row->{$this->bl_ns}][$row->{$this->bl_title}]; + if ( $this->hasNS ) + $parentID = $this->pageMap[$row-> { $this->bl_ns } ][$row-> { $this->bl_title } ]; else - $parentID = $this->pageMap[NS_IMAGE][$row->{$this->bl_title}]; - $this->continueStr = $this->getContinueRedirStr($parentID, $row->page_id); + $parentID = $this->pageMap[NS_IMAGE][$row-> { $this->bl_title } ]; + $this->continueStr = $this->getContinueRedirStr( $parentID, $row->page_id ); break; } - if(is_null($resultPageSet)) - $this->extractRedirRowInfo($row); + if ( is_null( $resultPageSet ) ) + $this->extractRedirRowInfo( $row ); else - $resultPageSet->processDbRow($row); + $resultPageSet->processDbRow( $row ); } - $db->freeResult($res); + $db->freeResult( $res ); } - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { // Try to add the result data in one go and pray that it fits - $fit = $this->getResult()->addValue('query', $this->getModuleName(), array_values($this->resultArr)); - if(!$fit) + $fit = $this->getResult()->addValue( 'query', $this->getModuleName(), array_values( $this->resultArr ) ); + if ( !$fit ) { // It didn't fit. Add elements one by one until the // result is full. - foreach($this->resultArr as $pageID => $arr) + foreach ( $this->resultArr as $pageID => $arr ) { // Add the basic entry without redirlinks first $fit = $this->getResult()->addValue( - array('query', $this->getModuleName()), - null, array_diff_key($arr, array('redirlinks' => ''))); - if(!$fit) + array( 'query', $this->getModuleName() ), + null, array_diff_key( $arr, array( 'redirlinks' => '' ) ) ); + if ( !$fit ) { - $this->continueStr = $this->getContinueStr($pageID); + $this->continueStr = $this->getContinueStr( $pageID ); break; } $hasRedirs = false; - foreach((array)@$arr['redirlinks'] as $key => $redir) + foreach ( (array)@$arr['redirlinks'] as $key => $redir ) { $fit = $this->getResult()->addValue( - array('query', $this->getModuleName(), $pageID, 'redirlinks'), - $key, $redir); - if(!$fit) + array( 'query', $this->getModuleName(), $pageID, 'redirlinks' ), + $key, $redir ); + if ( !$fit ) { - $this->continueStr = $this->getContinueRedirStr($pageID, $redir['pageid']); + $this->continueStr = $this->getContinueRedirStr( $pageID, $redir['pageid'] ); break; } $hasRedirs = true; } - if($hasRedirs) + if ( $hasRedirs ) $this->getResult()->setIndexedTagName_internal( - array('query', $this->getModuleName(), $pageID, 'redirlinks'), - $this->bl_code); - if(!$fit) + array( 'query', $this->getModuleName(), $pageID, 'redirlinks' ), + $this->bl_code ); + if ( !$fit ) break; } - } + } $this->getResult()->setIndexedTagName_internal( - array('query', $this->getModuleName()), - $this->bl_code); + array( 'query', $this->getModuleName() ), + $this->bl_code ); } - if(!is_null($this->continueStr)) - $this->setContinueEnumParameter('continue', $this->continueStr); + if ( !is_null( $this->continueStr ) ) + $this->setContinueEnumParameter( 'continue', $this->continueStr ); } - private function extractRowInfo($row) { + private function extractRowInfo( $row ) { $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id; - $t = Title::makeTitle($row->page_namespace, $row->page_title); - $a = array('pageid' => intval($row->page_id)); - ApiQueryBase::addTitleInfo($a, $t); - if($row->page_is_redirect) + $t = Title::makeTitle( $row->page_namespace, $row->page_title ); + $a = array( 'pageid' => intval( $row->page_id ) ); + ApiQueryBase::addTitleInfo( $a, $t ); + if ( $row->page_is_redirect ) { $a['redirect'] = ''; $this->redirTitles[] = $t; @@ -308,42 +321,42 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { $this->resultArr[$a['pageid']] = $a; } - private function extractRedirRowInfo($row) + private function extractRedirRowInfo( $row ) { - $a['pageid'] = intval($row->page_id); - ApiQueryBase::addTitleInfo($a, Title::makeTitle($row->page_namespace, $row->page_title)); - if($row->page_is_redirect) + $a['pageid'] = intval( $row->page_id ); + ApiQueryBase::addTitleInfo( $a, Title::makeTitle( $row->page_namespace, $row->page_title ) ); + if ( $row->page_is_redirect ) $a['redirect'] = ''; - $ns = $this->hasNS ? $row->{$this->bl_ns} : NS_FILE; - $parentID = $this->pageMap[$ns][$row->{$this->bl_title}]; + $ns = $this->hasNS ? $row-> { $this->bl_ns } : NS_FILE; + $parentID = $this->pageMap[$ns][$row-> { $this->bl_title } ]; // Put all the results in an array first $this->resultArr[$parentID]['redirlinks'][] = $a; - $this->getResult()->setIndexedTagName($this->resultArr[$parentID]['redirlinks'], $this->bl_code); + $this->getResult()->setIndexedTagName( $this->resultArr[$parentID]['redirlinks'], $this->bl_code ); } protected function processContinue() { - if (!is_null($this->params['continue'])) + 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'])); + $this->dieUsageMsg( array( 'invalidtitle', $this->params['title'] ) ); } else { $this->rootTitle = $title; } } else { - $this->dieUsageMsg(array('missingparam', 'title')); + $this->dieUsageMsg( array( 'missingparam', 'title' ) ); } } // only image titles are allowed for the root in imageinfo mode - if (!$this->hasNS && $this->rootTitle->getNamespace() !== NS_FILE) - $this->dieUsage("The title for {$this->getModuleName()} query must be an image", 'bad_image_title'); + if ( !$this->hasNS && $this->rootTitle->getNamespace() !== NS_FILE ) + $this->dieUsage( "The title for {$this->getModuleName()} query must be an image", 'bad_image_title' ); } protected function parseContinueParam() { - $continueList = explode('|', $this->params['continue']); + $continueList = explode( '|', $this->params['continue'] ); // expected format: // ns | key | id1 [| id2] // ns+key: root title @@ -352,33 +365,36 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { // null stuff out now so we know what's set and what isn't $this->rootTitle = $this->contID = $this->redirID = null; - $rootNs = intval($continueList[0]); - if($rootNs === 0 && $continueList[0] !== '0') + $rootNs = intval( $continueList[0] ); + if ( $rootNs === 0 && $continueList[0] !== '0' ) // Illegal continue parameter - $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue"); - $this->rootTitle = Title::makeTitleSafe($rootNs, $continueList[1]); - if(!$this->rootTitle) - $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue"); - $contID = intval($continueList[2]); - if($contID === 0 && $continueList[2] !== '0') - $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue"); + $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue" ); + $this->rootTitle = Title::makeTitleSafe( $rootNs, $continueList[1] ); + + if ( !$this->rootTitle ) + $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue" ); + $contID = intval( $continueList[2] ); + + if ( $contID === 0 && $continueList[2] !== '0' ) + $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "_badcontinue" ); $this->contID = $contID; - $redirID = intval(@$continueList[3]); - if($redirID === 0 && @$continueList[3] !== '0') + $redirID = intval( @$continueList[3] ); + + if ( $redirID === 0 && @$continueList[3] !== '0' ) // This one isn't required return; $this->redirID = $redirID; } - protected function getContinueStr($lastPageID) { + protected function getContinueStr( $lastPageID ) { return $this->rootTitle->getNamespace() . '|' . $this->rootTitle->getDBkey() . '|' . $lastPageID; } - protected function getContinueRedirStr($lastPageID, $lastRedirID) { - return $this->getContinueStr($lastPageID) . '|' . $lastRedirID; + protected function getContinueRedirStr( $lastPageID, $lastRedirID ) { + return $this->getContinueStr( $lastPageID ) . '|' . $lastRedirID; } public function getAllowedParams() { @@ -405,7 +421,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 ) ); - if($this->getModuleName() == 'embeddedin') + if ( $this->getModuleName() == 'embeddedin' ) return $retval; $retval['redirect'] = false; return $retval; @@ -413,23 +429,24 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { public function getParamDescription() { $retval = array ( - 'title' => 'Title to search. If null, titles= parameter will be used instead, but will be obsolete soon.', + 'title' => 'Title to search.', 'continue' => 'When more results are available, use this to continue.', 'namespace' => 'The namespace to enumerate.', - 'filterredir' => 'How to filter for redirects' ); - if($this->getModuleName() != 'embeddedin') - return array_merge($retval, array( + if ( $this->getModuleName() != 'embeddedin' ) + return array_merge( $retval, array( 'redirect' => 'If linking page is a redirect, find all pages that link to that redirect as well. Maximum limit is halved.', - 'limit' => "How many total pages to return. If {$this->bl_code}redirect is enabled, limit applies to each level separately." - )); - return array_merge($retval, array( - 'limit' => "How many total pages to return." - )); + 'filterredir' => "How to filter for redirects. If set to nonredirects when {$this->bl_code}redirect is enabled, this is only applied to the second level", + 'limit' => "How many total pages to return. If {$this->bl_code}redirect is enabled, limit applies to each level separately (which means you may get up to 2 * limit results)." + ) ); + return array_merge( $retval, array( + 'filterredir' => 'How to filter for redirects', + 'limit' => 'How many total pages to return.' + ) ); } public function getDescription() { - switch ($this->getModuleName()) { + switch ( $this->getModuleName() ) { case 'backlinks' : return 'Find all pages that link to the given page'; case 'embeddedin' : @@ -437,9 +454,18 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { case 'imageusage' : return 'Find all pages that use the given image title.'; default : - ApiBase :: dieDebug(__METHOD__, 'Unknown module name'); + ApiBase :: dieDebug( __METHOD__, 'Unknown module name' ); } } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'invalidtitle', 'title' ), + array( 'missingparam', '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' ), + ) ); + } protected function getExamples() { static $examples = array ( @@ -461,6 +487,6 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryBacklinks.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryBacklinks.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php index 7e2b1d5e..893da566 100644 --- a/includes/api/ApiQueryBase.php +++ b/includes/api/ApiQueryBase.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -39,8 +39,8 @@ abstract class ApiQueryBase extends ApiBase { private $mQueryModule, $mDb, $tables, $where, $fields, $options, $join_conds; - public function __construct($query, $moduleName, $paramPrefix = '') { - parent :: __construct($query->getMain(), $moduleName, $paramPrefix); + public function __construct( $query, $moduleName, $paramPrefix = '' ) { + parent :: __construct( $query->getMain(), $moduleName, $paramPrefix ); $this->mQueryModule = $query; $this->mDb = null; $this->resetQueryParams(); @@ -74,14 +74,14 @@ abstract class ApiQueryBase extends ApiBase { * @param $alias mixed Table alias, or null for no alias. Cannot be * used with multiple tables */ - protected function addTables($tables, $alias = null) { - if (is_array($tables)) { - if (!is_null($alias)) - ApiBase :: dieDebug(__METHOD__, 'Multiple table aliases not supported'); - $this->tables = array_merge($this->tables, $tables); + protected function addTables( $tables, $alias = null ) { + if ( is_array( $tables ) ) { + if ( !is_null( $alias ) ) + ApiBase :: dieDebug( __METHOD__, 'Multiple table aliases not supported' ); + $this->tables = array_merge( $this->tables, $tables ); } else { - if (!is_null($alias)) - $tables = $this->getAliasedName($tables, $alias); + if ( !is_null( $alias ) ) + $tables = $this->getAliasedName( $tables, $alias ); $this->tables[] = $tables; } } @@ -92,8 +92,8 @@ abstract class ApiQueryBase extends ApiBase { * @param $alias string Alias * @return string SQL */ - protected function getAliasedName($table, $alias) { - return $this->getDB()->tableName($table) . ' ' . $alias; + protected function getAliasedName( $table, $alias ) { + return $this->getDB()->tableName( $table ) . ' ' . $alias; } /** @@ -105,19 +105,19 @@ abstract class ApiQueryBase extends ApiBase { * addWhere()-style array * @param $join_conds array JOIN conditions */ - protected function addJoinConds($join_conds) { - if(!is_array($join_conds)) - ApiBase::dieDebug(__METHOD__, 'Join conditions have to be arrays'); - $this->join_conds = array_merge($this->join_conds, $join_conds); + protected function addJoinConds( $join_conds ) { + if ( !is_array( $join_conds ) ) + ApiBase::dieDebug( __METHOD__, 'Join conditions have to be arrays' ); + $this->join_conds = array_merge( $this->join_conds, $join_conds ); } /** * Add a set of fields to select to the internal array * @param $value mixed Field name or array of field names */ - protected function addFields($value) { - if (is_array($value)) - $this->fields = array_merge($this->fields, $value); + protected function addFields( $value ) { + if ( is_array( $value ) ) + $this->fields = array_merge( $this->fields, $value ); else $this->fields[] = $value; } @@ -128,9 +128,9 @@ abstract class ApiQueryBase extends ApiBase { * @param $condition bool If false, do nothing * @return bool $condition */ - protected function addFieldsIf($value, $condition) { - if ($condition) { - $this->addFields($value); + protected function addFieldsIf( $value, $condition ) { + if ( $condition ) { + $this->addFields( $value ); return true; } return false; @@ -147,12 +147,12 @@ abstract class ApiQueryBase extends ApiBase { * to "foo=bar AND baz='3' AND bla='foo'" * @param $value mixed String or array */ - protected function addWhere($value) { - if (is_array($value)) { + protected function addWhere( $value ) { + if ( is_array( $value ) ) { // Sanity check: don't insert empty arrays, // Database::makeList() chokes on them if ( count( $value ) ) - $this->where = array_merge($this->where, $value); + $this->where = array_merge( $this->where, $value ); } else $this->where[] = $value; @@ -164,9 +164,9 @@ abstract class ApiQueryBase extends ApiBase { * @param $condition boolIf false, do nothing * @return bool $condition */ - protected function addWhereIf($value, $condition) { - if ($condition) { - $this->addWhere($value); + protected function addWhereIf( $value, $condition ) { + if ( $condition ) { + $this->addWhere( $value ); return true; } return false; @@ -177,7 +177,7 @@ abstract class ApiQueryBase extends ApiBase { * @param $field string Field name * @param $value string Value; ignored if null or empty array; */ - protected function addWhereFld($field, $value) { + protected function addWhereFld( $field, $value ) { // Use count() to its full documented capabilities to simultaneously // test for null, empty array or empty countable object if ( count( $value ) ) @@ -196,24 +196,24 @@ abstract class ApiQueryBase extends ApiBase { * is the upper boundary, otherwise it's the lower boundary * @param $sort bool If false, don't add an ORDER BY clause */ - protected function addWhereRange($field, $dir, $start, $end, $sort = true) { - $isDirNewer = ($dir === 'newer'); - $after = ($isDirNewer ? '>=' : '<='); - $before = ($isDirNewer ? '<=' : '>='); + protected function addWhereRange( $field, $dir, $start, $end, $sort = true ) { + $isDirNewer = ( $dir === 'newer' ); + $after = ( $isDirNewer ? '>=' : '<=' ); + $before = ( $isDirNewer ? '<=' : '>=' ); $db = $this->getDB(); - if (!is_null($start)) - $this->addWhere($field . $after . $db->addQuotes($start)); + if ( !is_null( $start ) ) + $this->addWhere( $field . $after . $db->addQuotes( $start ) ); - if (!is_null($end)) - $this->addWhere($field . $before . $db->addQuotes($end)); + if ( !is_null( $end ) ) + $this->addWhere( $field . $before . $db->addQuotes( $end ) ); - if ($sort) { - $order = $field . ($isDirNewer ? '' : ' DESC'); - if (!isset($this->options['ORDER BY'])) - $this->addOption('ORDER BY', $order); + if ( $sort ) { + $order = $field . ( $isDirNewer ? '' : ' DESC' ); + if ( !isset( $this->options['ORDER BY'] ) ) + $this->addOption( 'ORDER BY', $order ); else - $this->addOption('ORDER BY', $this->options['ORDER BY'] . ', ' . $order); + $this->addOption( 'ORDER BY', $this->options['ORDER BY'] . ', ' . $order ); } } @@ -223,8 +223,8 @@ abstract class ApiQueryBase extends ApiBase { * @param $name string Option name * @param $value string Option value */ - protected function addOption($name, $value = null) { - if (is_null($value)) + protected function addOption( $name, $value = null ) { + if ( is_null( $value ) ) $this->options[] = $name; else $this->options[$name] = $value; @@ -236,13 +236,12 @@ abstract class ApiQueryBase extends ApiBase { * You should usually use __METHOD__ here * @return ResultWrapper */ - protected function select($method) { - + protected function select( $method ) { // getDB has its own profileDBIn/Out calls $db = $this->getDB(); $this->profileDBIn(); - $res = $db->select($this->tables, $this->fields, $this->where, $method, $this->options, $this->join_conds); + $res = $db->select( $this->tables, $this->fields, $this->where, $method, $this->options, $this->join_conds ); $this->profileDBOut(); return $res; @@ -256,11 +255,11 @@ abstract class ApiQueryBase extends ApiBase { protected function checkRowCount() { $db = $this->getDB(); $this->profileDBIn(); - $rowcount = $db->estimateRowCount($this->tables, $this->fields, $this->where, __METHOD__, $this->options); + $rowcount = $db->estimateRowCount( $this->tables, $this->fields, $this->where, __METHOD__, $this->options ); $this->profileDBOut(); global $wgAPIMaxDBRows; - if($rowcount > $wgAPIMaxDBRows) + if ( $rowcount > $wgAPIMaxDBRows ) return false; return true; } @@ -272,8 +271,8 @@ abstract class ApiQueryBase extends ApiBase { * @param $title Title * @param $prefix string Module prefix */ - public static function addTitleInfo(&$arr, $title, $prefix='') { - $arr[$prefix . 'ns'] = intval($title->getNamespace()); + public static function addTitleInfo( &$arr, $title, $prefix = '' ) { + $arr[$prefix . 'ns'] = intval( $title->getNamespace() ); $arr[$prefix . 'title'] = $title->getPrefixedText(); } @@ -282,7 +281,7 @@ abstract class ApiQueryBase extends ApiBase { * using $pageSet->requestField('fieldName') * @param $pageSet ApiPageSet */ - public function requestExtraData($pageSet) { + public function requestExtraData( $pageSet ) { } /** @@ -299,12 +298,12 @@ abstract class ApiQueryBase extends ApiBase { * @param $data array Data array à la ApiResult * @return bool Whether the element fit in the result */ - protected function addPageSubItems($pageId, $data) { + protected function addPageSubItems( $pageId, $data ) { $result = $this->getResult(); - $result->setIndexedTagName($data, $this->getModulePrefix()); - return $result->addValue(array('query', 'pages', intval($pageId)), + $result->setIndexedTagName( $data, $this->getModulePrefix() ); + return $result->addValue( array( 'query', 'pages', intval( $pageId ) ), $this->getModuleName(), - $data); + $data ); } /** @@ -315,16 +314,16 @@ abstract class ApiQueryBase extends ApiBase { * is used * @return bool Whether the element fit in the result */ - protected function addPageSubItem($pageId, $item, $elemname = null) { - if(is_null($elemname)) + protected function addPageSubItem( $pageId, $item, $elemname = null ) { + if ( is_null( $elemname ) ) $elemname = $this->getModulePrefix(); $result = $this->getResult(); - $fit = $result->addValue(array('query', 'pages', $pageId, - $this->getModuleName()), null, $item); - if(!$fit) + $fit = $result->addValue( array( 'query', 'pages', $pageId, + $this->getModuleName() ), null, $item ); + if ( !$fit ) return false; - $result->setIndexedTagName_internal(array('query', 'pages', $pageId, - $this->getModuleName()), $elemname); + $result->setIndexedTagName_internal( array( 'query', 'pages', $pageId, + $this->getModuleName() ), $elemname ); return true; } @@ -333,11 +332,11 @@ abstract class ApiQueryBase extends ApiBase { * @param $paramName string Parameter name * @param $paramValue string Parameter value */ - protected function setContinueEnumParameter($paramName, $paramValue) { - $paramName = $this->encodeParamName($paramName); + protected function setContinueEnumParameter( $paramName, $paramValue ) { + $paramName = $this->encodeParamName( $paramName ); $msg = array( $paramName => $paramValue ); $this->getResult()->disableSizeCheck(); - $this->getResult()->addValue('query-continue', $this->getModuleName(), $msg); + $this->getResult()->addValue( 'query-continue', $this->getModuleName(), $msg ); $this->getResult()->enableSizeCheck(); } @@ -346,7 +345,7 @@ abstract class ApiQueryBase extends ApiBase { * @return Database */ protected function getDB() { - if (is_null($this->mDb)) + if ( is_null( $this->mDb ) ) $this->mDb = $this->getQuery()->getDB(); return $this->mDb; } @@ -359,8 +358,8 @@ abstract class ApiQueryBase extends ApiBase { * @param $groups array Query groups * @return Database */ - public function selectNamedDB($name, $db, $groups) { - $this->mDb = $this->getQuery()->getNamedDB($name, $db, $groups); + public function selectNamedDB( $name, $db, $groups ) { + $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups ); } /** @@ -376,13 +375,13 @@ abstract class ApiQueryBase extends ApiBase { * @param $title string Page title with spaces * @return string Page title with underscores */ - public function titleToKey($title) { - # Don't throw an error if we got an empty string - if(trim($title) == '') + public function titleToKey( $title ) { + // Don't throw an error if we got an empty string + if ( trim( $title ) == '' ) return ''; - $t = Title::newFromText($title); - if(!$t) - $this->dieUsageMsg(array('invalidtitle', $title)); + $t = Title::newFromText( $title ); + if ( !$t ) + $this->dieUsageMsg( array( 'invalidtitle', $title ) ); return $t->getPrefixedDbKey(); } @@ -391,14 +390,14 @@ abstract class ApiQueryBase extends ApiBase { * @param $key string Page title with underscores * @return string Page title with spaces */ - public function keyToTitle($key) { - # Don't throw an error if we got an empty string - if(trim($key) == '') + public function keyToTitle( $key ) { + // Don't throw an error if we got an empty string + if ( trim( $key ) == '' ) return ''; - $t = Title::newFromDbKey($key); - # This really shouldn't happen but we gotta check anyway - if(!$t) - $this->dieUsageMsg(array('invalidtitle', $key)); + $t = Title::newFromDbKey( $key ); + // This really shouldn't happen but we gotta check anyway + if ( !$t ) + $this->dieUsageMsg( array( 'invalidtitle', $key ) ); return $t->getPrefixedText(); } @@ -407,8 +406,8 @@ abstract class ApiQueryBase extends ApiBase { * @param $titlePart string Title part with spaces * @return string Title part with underscores */ - public function titlePartToKey($titlePart) { - return substr($this->titleToKey($titlePart . 'x'), 0, -1); + public function titlePartToKey( $titlePart ) { + return substr( $this->titleToKey( $titlePart . 'x' ), 0, - 1 ); } /** @@ -416,8 +415,15 @@ abstract class ApiQueryBase extends ApiBase { * @param $keyPart string Key part with spaces * @return string Key part with underscores */ - public function keyPartToTitle($keyPart) { - return substr($this->keyToTitle($keyPart . 'x'), 0, -1); + public function keyPartToTitle( $keyPart ) { + return substr( $this->keyToTitle( $keyPart . 'x' ), 0, - 1 ); + } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'invalidtitle', 'title' ), + array( 'invalidtitle', 'key' ), + ) ); } /** @@ -425,7 +431,7 @@ abstract class ApiQueryBase extends ApiBase { * @return string */ public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiQueryBase.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryBase.php 69932 2010-07-26 08:03:21Z tstarling $'; } } @@ -436,8 +442,8 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { private $mIsGenerator; - public function __construct($query, $moduleName, $paramPrefix = '') { - parent :: __construct($query, $moduleName, $paramPrefix); + public function __construct( $query, $moduleName, $paramPrefix = '' ) { + parent :: __construct( $query, $moduleName, $paramPrefix ); $this->mIsGenerator = false; } @@ -454,11 +460,11 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { * @param $paramNames string Parameter name * @return string Prefixed parameter name */ - public function encodeParamName($paramName) { - if ($this->mIsGenerator) - return 'g' . parent :: encodeParamName($paramName); + public function encodeParamName( $paramName ) { + if ( $this->mIsGenerator ) + return 'g' . parent :: encodeParamName( $paramName ); else - return parent :: encodeParamName($paramName); + return parent :: encodeParamName( $paramName ); } /** @@ -466,5 +472,5 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { * @param $resultPageSet ApiPageSet: All output should be appended to * this object */ - public abstract function executeGenerator($resultPageSet); + public abstract function executeGenerator( $resultPageSet ); } diff --git a/includes/api/ApiQueryBlocks.php b/includes/api/ApiQueryBlocks.php index 64790037..8b321044 100644 --- a/includes/api/ApiQueryBlocks.php +++ b/includes/api/ApiQueryBlocks.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -37,157 +37,163 @@ class ApiQueryBlocks extends ApiQueryBase { var $users; - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'bk'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'bk' ); } public function execute() { global $wgUser; $params = $this->extractRequestParams(); - if(isset($params['users']) && isset($params['ip'])) - $this->dieUsage('bkusers and bkip cannot be used together', 'usersandip'); + if ( isset( $params['users'] ) && isset( $params['ip'] ) ) + $this->dieUsage( 'bkusers and bkip cannot be used together', 'usersandip' ); - $prop = array_flip($params['prop']); - $fld_id = isset($prop['id']); - $fld_user = isset($prop['user']); - $fld_by = isset($prop['by']); - $fld_timestamp = isset($prop['timestamp']); - $fld_expiry = isset($prop['expiry']); - $fld_reason = isset($prop['reason']); - $fld_range = isset($prop['range']); - $fld_flags = isset($prop['flags']); + $prop = array_flip( $params['prop'] ); + $fld_id = isset( $prop['id'] ); + $fld_user = isset( $prop['user'] ); + $fld_by = isset( $prop['by'] ); + $fld_timestamp = isset( $prop['timestamp'] ); + $fld_expiry = isset( $prop['expiry'] ); + $fld_reason = isset( $prop['reason'] ); + $fld_range = isset( $prop['range'] ); + $fld_flags = isset( $prop['flags'] ); $result = $this->getResult(); $pageSet = $this->getPageSet(); $titles = $pageSet->getTitles(); $data = array(); - $this->addTables('ipblocks'); - if($fld_id) - $this->addFields('ipb_id'); - if($fld_user) - $this->addFields(array('ipb_address', 'ipb_user', 'ipb_auto')); - if($fld_by) + $this->addTables( 'ipblocks' ); + $this->addFields( 'ipb_auto' ); + + if ( $fld_id ) + $this->addFields( 'ipb_id' ); + if ( $fld_user ) + $this->addFields( array( 'ipb_address', 'ipb_user' ) ); + if ( $fld_by ) { - $this->addTables('user'); - $this->addFields(array('ipb_by', 'user_name')); - $this->addWhere('user_id = ipb_by'); + $this->addTables( 'user' ); + $this->addFields( array( 'ipb_by', 'user_name' ) ); + $this->addWhere( 'user_id = ipb_by' ); } - if($fld_timestamp) - $this->addFields('ipb_timestamp'); - if($fld_expiry) - $this->addFields('ipb_expiry'); - if($fld_reason) - $this->addFields('ipb_reason'); - if($fld_range) - $this->addFields(array('ipb_range_start', 'ipb_range_end')); - if($fld_flags) - $this->addFields(array('ipb_auto', 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock', 'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk')); + if ( $fld_timestamp ) + $this->addFields( 'ipb_timestamp' ); + if ( $fld_expiry ) + $this->addFields( 'ipb_expiry' ); + if ( $fld_reason ) + $this->addFields( 'ipb_reason' ); + if ( $fld_range ) + $this->addFields( array( 'ipb_range_start', 'ipb_range_end' ) ); + if ( $fld_flags ) + $this->addFields( array( 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock', 'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk' ) ); - $this->addOption('LIMIT', $params['limit'] + 1); - $this->addWhereRange('ipb_timestamp', $params['dir'], $params['start'], $params['end']); - if(isset($params['ids'])) - $this->addWhereFld('ipb_id', $params['ids']); - if(isset($params['users'])) + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + $this->addWhereRange( 'ipb_timestamp', $params['dir'], $params['start'], $params['end'] ); + if ( isset( $params['ids'] ) ) + $this->addWhereFld( 'ipb_id', $params['ids'] ); + if ( isset( $params['users'] ) ) { - foreach((array)$params['users'] as $u) - $this->prepareUsername($u); - $this->addWhereFld('ipb_address', $this->usernames); + foreach ( (array)$params['users'] as $u ) + $this->prepareUsername( $u ); + $this->addWhereFld( 'ipb_address', $this->usernames ); + $this->addWhereFld( 'ipb_auto', 0 ); } - if(isset($params['ip'])) + if ( isset( $params['ip'] ) ) { - list($ip, $range) = IP::parseCIDR($params['ip']); - if($ip && $range) + list( $ip, $range ) = IP::parseCIDR( $params['ip'] ); + if ( $ip && $range ) { - # We got a CIDR range - if($range < 16) - $this->dieUsage('CIDR ranges broader than /16 are not accepted', 'cidrtoobroad'); - $lower = wfBaseConvert($ip, 10, 16, 8, false); - $upper = wfBaseConvert($ip + pow(2, 32 - $range) - 1, 10, 16, 8, false); + // We got a CIDR range + if ( $range < 16 ) + $this->dieUsage( 'CIDR ranges broader than /16 are not accepted', 'cidrtoobroad' ); + $lower = wfBaseConvert( $ip, 10, 16, 8, false ); + $upper = wfBaseConvert( $ip + pow( 2, 32 - $range ) - 1, 10, 16, 8, false ); } else - $lower = $upper = IP::toHex($params['ip']); - $prefix = substr($lower, 0, 4); - $this->addWhere(array( - "ipb_range_start LIKE '$prefix%'", + $lower = $upper = IP::toHex( $params['ip'] ); + $prefix = substr( $lower, 0, 4 ); + + $db = $this->getDB(); + $this->addWhere( array( + 'ipb_range_start' . $db->buildLike( $prefix, $db->anyString() ), "ipb_range_start <= '$lower'", - "ipb_range_end >= '$upper'" - )); + "ipb_range_end >= '$upper'", + 'ipb_auto' => 0 + ) ); } - if(!$wgUser->isAllowed('hideuser')) - $this->addWhereFld('ipb_deleted', 0); + if ( !$wgUser->isAllowed( 'hideuser' ) ) + $this->addWhereFld( 'ipb_deleted', 0 ); // Purge expired entries on one in every 10 queries - if(!mt_rand(0, 10)) + if ( !mt_rand( 0, 10 ) ) Block::purgeExpired(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $count = 0; - while($row = $res->fetchObject()) + while ( $row = $res->fetchObject() ) { - if(++$count > $params['limit']) + if ( ++$count > $params['limit'] ) { // We've had enough - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ipb_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ) ); break; } $block = array(); - if($fld_id) + if ( $fld_id ) $block['id'] = $row->ipb_id; - if($fld_user && !$row->ipb_auto) + if ( $fld_user && !$row->ipb_auto ) $block['user'] = $row->ipb_address; - if($fld_by) + if ( $fld_by ) $block['by'] = $row->user_name; - if($fld_timestamp) - $block['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ipb_timestamp); - if($fld_expiry) - $block['expiry'] = Block::decodeExpiry($row->ipb_expiry, TS_ISO_8601); - if($fld_reason) + if ( $fld_timestamp ) + $block['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ); + if ( $fld_expiry ) + $block['expiry'] = Block::decodeExpiry( $row->ipb_expiry, TS_ISO_8601 ); + if ( $fld_reason ) $block['reason'] = $row->ipb_reason; - if($fld_range) + if ( $fld_range && !$row->ipb_auto ) { - $block['rangestart'] = IP::hexToQuad($row->ipb_range_start); - $block['rangeend'] = IP::hexToQuad($row->ipb_range_end); + $block['rangestart'] = IP::hexToQuad( $row->ipb_range_start ); + $block['rangeend'] = IP::hexToQuad( $row->ipb_range_end ); } - if($fld_flags) + if ( $fld_flags ) { // For clarity, these flags use the same names as their action=block counterparts - if($row->ipb_auto) + if ( $row->ipb_auto ) $block['automatic'] = ''; - if($row->ipb_anon_only) + if ( $row->ipb_anon_only ) $block['anononly'] = ''; - if($row->ipb_create_account) + if ( $row->ipb_create_account ) $block['nocreate'] = ''; - if($row->ipb_enable_autoblock) + if ( $row->ipb_enable_autoblock ) $block['autoblock'] = ''; - if($row->ipb_block_email) + if ( $row->ipb_block_email ) $block['noemail'] = ''; - if($row->ipb_deleted) + if ( $row->ipb_deleted ) $block['hidden'] = ''; - if($row->ipb_allow_usertalk) + if ( $row->ipb_allow_usertalk ) $block['allowusertalk'] = ''; } - $fit = $result->addValue(array('query', $this->getModuleName()), null, $block); - if(!$fit) + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $block ); + if ( !$fit ) { - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ipb_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ) ); break; } } - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'block'); + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'block' ); } - protected function prepareUsername($user) + protected function prepareUsername( $user ) { - if(!$user) - $this->dieUsage('User parameter may not be empty', 'param_user'); - $name = User::isIP($user) + if ( !$user ) + $this->dieUsage( 'User parameter may not be empty', 'param_user' ); + $name = User::isIP( $user ) ? $user - : User::getCanonicalName($user, 'valid'); - if($name === false) - $this->dieUsage("User name {$user} is not valid", 'param_user'); + : User::getCanonicalName( $user, 'valid' ); + if ( $name === false ) + $this->dieUsage( "User name {$user} is not valid", 'param_user' ); $this->usernames[] = $name; } @@ -246,7 +252,7 @@ class ApiQueryBlocks extends ApiQueryBase { 'ids' => 'Pipe-separated list of block IDs to list (optional)', 'users' => 'Pipe-separated list of users to search for (optional)', 'ip' => array( 'Get all blocks applying to this IP or CIDR range, including range blocks.', - 'Cannot be used together with bkusers. CIDR ranges broader than /16 are not accepted.'), + 'Cannot be used together with bkusers. CIDR ranges broader than /16 are not accepted.' ), 'limit' => 'The maximum amount of blocks to list', 'prop' => 'Which properties to get', ); @@ -255,6 +261,15 @@ class ApiQueryBlocks extends ApiQueryBase { public function getDescription() { return 'List all blocked users and IP addresses.'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'usersandip', 'info' => 'bkusers and bkip cannot be used together' ), + 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' ), + ) ); + } protected function getExamples() { return array ( 'api.php?action=query&list=blocks', @@ -263,6 +278,6 @@ class ApiQueryBlocks extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryBlocks.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryBlocks.php 69578 2010-07-20 02:46:20Z tstarling $'; } } \ No newline at end of file diff --git a/includes/api/ApiQueryCategories.php b/includes/api/ApiQueryCategories.php index 15e1ce13..03135052 100644 --- a/includes/api/ApiQueryCategories.php +++ b/includes/api/ApiQueryCategories.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryCategories extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'cl'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'cl' ); } public function execute() { @@ -47,144 +47,134 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { - if ($this->getPageSet()->getGoodTitleCount() == 0) + if ( $this->getPageSet()->getGoodTitleCount() == 0 ) return; // nothing to do $params = $this->extractRequestParams(); - $prop = $params['prop']; - $show = array_flip((array)$params['show']); + $prop = array_flip( (array)$params['prop'] ); + $show = array_flip( (array)$params['show'] ); - $this->addFields(array ( + $this->addFields( array ( 'cl_from', 'cl_to' - )); - - $fld_sortkey = $fld_timestamp = false; - if (!is_null($prop)) { - foreach($prop as $p) { - switch ($p) { - case 'sortkey': - $this->addFields('cl_sortkey'); - $fld_sortkey = true; - break; - case 'timestamp': - $this->addFields('cl_timestamp'); - $fld_timestamp = true; - break; - default : - ApiBase :: dieDebug(__METHOD__, "Unknown prop=$p"); - } - } - } + ) ); + + $this->addFieldsIf( 'cl_sortkey', isset( $prop['sortkey'] ) ); + $this->addFieldsIf( 'cl_timestamp', isset( $prop['timestamp'] ) ); - $this->addTables('categorylinks'); - $this->addWhereFld('cl_from', array_keys($this->getPageSet()->getGoodTitles())); - if(!is_null($params['categories'])) + $this->addTables( 'categorylinks' ); + $this->addWhereFld( 'cl_from', array_keys( $this->getPageSet()->getGoodTitles() ) ); + if ( !is_null( $params['categories'] ) ) { $cats = array(); - foreach($params['categories'] as $cat) + foreach ( $params['categories'] as $cat ) { - $title = Title::newFromText($cat); - if(!$title || $title->getNamespace() != NS_CATEGORY) - $this->setWarning("``$cat'' is not a category"); + $title = Title::newFromText( $cat ); + if ( !$title || $title->getNamespace() != NS_CATEGORY ) + $this->setWarning( "``$cat'' is not a category" ); else $cats[] = $title->getDBkey(); } - $this->addWhereFld('cl_to', $cats); + $this->addWhereFld( 'cl_to', $cats ); } - if(!is_null($params['continue'])) { - $cont = explode('|', $params['continue']); - if(count($cont) != 2) - $this->dieUsage("Invalid continue param. You should pass the " . - "original value returned by the previous query", "_badcontinue"); - $clfrom = intval($cont[0]); - $clto = $this->getDB()->strencode($this->titleToKey($cont[1])); - $this->addWhere("cl_from > $clfrom OR ". - "(cl_from = $clfrom AND ". - "cl_to >= '$clto')"); + + if ( !is_null( $params['continue'] ) ) { + $cont = explode( '|', $params['continue'] ); + if ( count( $cont ) != 2 ) + $this->dieUsage( "Invalid continue param. You should pass the " . + "original value returned by the previous query", "_badcontinue" ); + $clfrom = intval( $cont[0] ); + $clto = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) ); + $this->addWhere( "cl_from > $clfrom OR " . + "(cl_from = $clfrom AND " . + "cl_to >= '$clto')" ); } - if(isset($show['hidden']) && isset($show['!hidden'])) - $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show'); - if(isset($show['hidden']) || isset($show['!hidden'])) + + if ( isset( $show['hidden'] ) && isset( $show['!hidden'] ) ) + $this->dieUsageMsg( array( 'show' ) ); + if ( isset( $show['hidden'] ) || isset( $show['!hidden'] ) || isset( $prop['hidden'] ) ) { - $this->addOption('STRAIGHT_JOIN'); - $this->addTables(array('page', 'page_props')); - $this->addJoinConds(array( - 'page' => array('LEFT JOIN', array( + $this->addOption( 'STRAIGHT_JOIN' ); + $this->addTables( array( 'page', 'page_props' ) ); + $this->addFieldsIf( 'pp_propname', isset( $prop['hidden'] ) ); + $this->addJoinConds( array( + 'page' => array( 'LEFT JOIN', array( 'page_namespace' => NS_CATEGORY, - 'page_title = cl_to')), - 'page_props' => array('LEFT JOIN', array( + 'page_title = cl_to' ) ), + 'page_props' => array( 'LEFT JOIN', array( 'pp_page=page_id', - 'pp_propname' => 'hiddencat')) - )); - if(isset($show['hidden'])) - $this->addWhere(array('pp_propname IS NOT NULL')); - else - $this->addWhere(array('pp_propname IS NULL')); + 'pp_propname' => 'hiddencat' ) ) + ) ); + if ( isset( $show['hidden'] ) ) + $this->addWhere( array( 'pp_propname IS NOT NULL' ) ); + else if ( isset( $show['!hidden'] ) ) + $this->addWhere( array( 'pp_propname IS NULL' ) ); } - $this->addOption('USE INDEX', array('categorylinks' => 'cl_from')); - # 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'); + $this->addOption( 'USE INDEX', array( 'categorylinks' => 'cl_from' ) ); + // Don't order by cl_from if it's constant in the WHERE clause + if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) + $this->addOption( 'ORDER BY', 'cl_to' ); else - $this->addOption('ORDER BY', "cl_from, cl_to"); + $this->addOption( 'ORDER BY', "cl_from, cl_to" ); $db = $this->getDB(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $count = 0; - while ($row = $db->fetchObject($res)) { - if (++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', $row->cl_from . - '|' . $this->keyToTitle($row->cl_to)); + $this->setContinueEnumParameter( 'continue', $row->cl_from . + '|' . $this->keyToTitle( $row->cl_to ) ); break; } - $title = Title :: makeTitle(NS_CATEGORY, $row->cl_to); + $title = Title :: makeTitle( NS_CATEGORY, $row->cl_to ); $vals = array(); - ApiQueryBase :: addTitleInfo($vals, $title); - if ($fld_sortkey) + ApiQueryBase :: addTitleInfo( $vals, $title ); + if ( isset( $prop['sortkey'] ) ) $vals['sortkey'] = $row->cl_sortkey; - if ($fld_timestamp) - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp); + if ( isset( $prop['timestamp'] ) ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->cl_timestamp ); + if ( isset( $prop['hidden'] ) && !is_null( $row->pp_propname ) ) + $vals['hidden'] = ''; - $fit = $this->addPageSubItem($row->cl_from, $vals); - if(!$fit) + $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 . + '|' . $this->keyToTitle( $row->cl_to ) ); break; } } } else { $titles = array(); - while ($row = $db->fetchObject($res)) { - if (++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', $row->cl_from . - '|' . $this->keyToTitle($row->cl_to)); + $this->setContinueEnumParameter( 'continue', $row->cl_from . + '|' . $this->keyToTitle( $row->cl_to ) ); break; } - $titles[] = Title :: makeTitle(NS_CATEGORY, $row->cl_to); + $titles[] = Title :: makeTitle( NS_CATEGORY, $row->cl_to ); } - $resultPageSet->populateFromTitles($titles); + $resultPageSet->populateFromTitles( $titles ); } - $db->freeResult($res); + $db->freeResult( $res ); } public function getAllowedParams() { @@ -194,6 +184,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { ApiBase :: PARAM_TYPE => array ( 'sortkey', 'timestamp', + 'hidden', ) ), 'show' => array( @@ -230,6 +221,12 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { public function getDescription() { return 'List all categories the page(s) belong to'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'show' ), + ) ); + } protected function getExamples() { return array ( @@ -241,6 +238,6 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryCategories.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryCategories.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryCategoryInfo.php b/includes/api/ApiQueryCategoryInfo.php index f3d45ccf..4df2f181 100644 --- a/includes/api/ApiQueryCategoryInfo.php +++ b/includes/api/ApiQueryCategoryInfo.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryCategoryInfo extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'ci'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'ci' ); } public function execute() { @@ -50,51 +50,53 @@ class ApiQueryCategoryInfo extends ApiQueryBase { $titles = $this->getPageSet()->getGoodTitles() + $this->getPageSet()->getMissingTitles(); $cattitles = array(); - foreach($categories as $c) + foreach ( $categories as $c ) { $t = $titles[$c]; - $cattitles[$c] = $t->getDBKey(); + $cattitles[$c] = $t->getDBkey(); } - $this->addTables(array('category', 'page', 'page_props')); - $this->addJoinConds(array( - 'page' => array('LEFT JOIN', array( + $this->addTables( array( 'category', 'page', 'page_props' ) ); + $this->addJoinConds( array( + 'page' => array( 'LEFT JOIN', array( 'page_namespace' => NS_CATEGORY, - 'page_title=cat_title')), - 'page_props' => array('LEFT JOIN', array( + 'page_title=cat_title' ) ), + 'page_props' => array( 'LEFT JOIN', array( 'pp_page=page_id', - 'pp_propname' => 'hiddencat')), - )); - $this->addFields(array('cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'pp_propname AS cat_hidden')); - $this->addWhere(array('cat_title' => $cattitles)); - if(!is_null($params['continue'])) + 'pp_propname' => 'hiddencat' ) ), + ) ); + + $this->addFields( array( 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files', 'pp_propname AS cat_hidden' ) ); + $this->addWhere( array( 'cat_title' => $cattitles ) ); + + if ( !is_null( $params['continue'] ) ) { - $title = $this->getDB()->addQuotes($params['continue']); - $this->addWhere("cat_title >= $title"); - } - $this->addOption('ORDER BY', 'cat_title'); + $title = $this->getDB()->addQuotes( $params['continue'] ); + $this->addWhere( "cat_title >= $title" ); + } + $this->addOption( 'ORDER BY', 'cat_title' ); $db = $this->getDB(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); - $catids = array_flip($cattitles); - while($row = $db->fetchObject($res)) + $catids = array_flip( $cattitles ); + while ( $row = $db->fetchObject( $res ) ) { $vals = array(); - $vals['size'] = intval($row->cat_pages); + $vals['size'] = intval( $row->cat_pages ); $vals['pages'] = $row->cat_pages - $row->cat_subcats - $row->cat_files; - $vals['files'] = intval($row->cat_files); - $vals['subcats'] = intval($row->cat_subcats); - if($row->cat_hidden) + $vals['files'] = intval( $row->cat_files ); + $vals['subcats'] = intval( $row->cat_subcats ); + if ( $row->cat_hidden ) $vals['hidden'] = ''; - $fit = $this->addPageSubItems($catids[$row->cat_title], $vals); - if(!$fit) + $fit = $this->addPageSubItems( $catids[$row->cat_title], $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('continue', $row->cat_title); + $this->setContinueEnumParameter( 'continue', $row->cat_title ); break; } } - $db->freeResult($res); + $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -122,6 +124,6 @@ class ApiQueryCategoryInfo extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryCategoryInfo.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryCategoryInfo.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryCategoryMembers.php b/includes/api/ApiQueryCategoryMembers.php index 45461abd..107f5049 100644 --- a/includes/api/ApiQueryCategoryMembers.php +++ b/includes/api/ApiQueryCategoryMembers.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'cm'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'cm' ); } public function execute() { @@ -47,113 +47,128 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { $params = $this->extractRequestParams(); - if ( !isset($params['title']) || is_null($params['title']) ) - $this->dieUsage("The cmtitle parameter is required", 'notitle'); - $categoryTitle = Title::newFromText($params['title']); + if ( !isset( $params['title'] ) || is_null( $params['title'] ) ) + $this->dieUsage( "The cmtitle parameter is required", 'notitle' ); + $categoryTitle = Title::newFromText( $params['title'] ); if ( is_null( $categoryTitle ) || $categoryTitle->getNamespace() != NS_CATEGORY ) - $this->dieUsage("The category name you entered is not valid", 'invalidcategory'); + $this->dieUsage( "The category name you entered is not valid", 'invalidcategory' ); - $prop = array_flip($params['prop']); - $fld_ids = isset($prop['ids']); - $fld_title = isset($prop['title']); - $fld_sortkey = isset($prop['sortkey']); - $fld_timestamp = isset($prop['timestamp']); + $prop = array_flip( $params['prop'] ); + $fld_ids = isset( $prop['ids'] ); + $fld_title = isset( $prop['title'] ); + $fld_sortkey = isset( $prop['sortkey'] ); + $fld_timestamp = isset( $prop['timestamp'] ); - if (is_null($resultPageSet)) { - $this->addFields(array('cl_from', 'cl_sortkey', 'page_namespace', 'page_title')); - $this->addFieldsIf('page_id', $fld_ids); + if ( is_null( $resultPageSet ) ) { + $this->addFields( array( 'cl_from', 'cl_sortkey', 'page_namespace', 'page_title' ) ); + $this->addFieldsIf( 'page_id', $fld_ids ); } else { - $this->addFields($resultPageSet->getPageTableFields()); // will include page_ id, ns, title - $this->addFields(array('cl_from', 'cl_sortkey')); + $this->addFields( $resultPageSet->getPageTableFields() ); // will include page_ id, ns, title + $this->addFields( array( 'cl_from', 'cl_sortkey' ) ); } - $this->addFieldsIf('cl_timestamp', $fld_timestamp || $params['sort'] == 'timestamp'); - $this->addTables(array('page','categorylinks')); // must be in this order for 'USE INDEX' + $this->addFieldsIf( 'cl_timestamp', $fld_timestamp || $params['sort'] == 'timestamp' ); + $this->addTables( array( 'page', 'categorylinks' ) ); // must be in this order for 'USE INDEX' // Not needed after bug 10280 is applied to servers - if($params['sort'] == 'timestamp') - $this->addOption('USE INDEX', 'cl_timestamp'); + if ( $params['sort'] == 'timestamp' ) + $this->addOption( 'USE INDEX', 'cl_timestamp' ); else - $this->addOption('USE INDEX', 'cl_sortkey'); - - $this->addWhere('cl_from=page_id'); - $this->setContinuation($params['continue'], $params['dir']); - $this->addWhereFld('cl_to', $categoryTitle->getDBkey()); - $this->addWhereFld('page_namespace', $params['namespace']); - if($params['sort'] == 'timestamp') - $this->addWhereRange('cl_timestamp', ($params['dir'] == 'asc' ? 'newer' : 'older'), $params['start'], $params['end']); + $this->addOption( 'USE INDEX', 'cl_sortkey' ); + + $this->addWhere( 'cl_from=page_id' ); + $this->setContinuation( $params['continue'], $params['dir'] ); + $this->addWhereFld( 'cl_to', $categoryTitle->getDBkey() ); + // Scanning large datasets for rare categories sucks, and I already told + // how to have efficient subcategory access :-) ~~~~ (oh well, domas) + global $wgMiserMode; + $miser_ns = array(); + if ( $wgMiserMode ) { + $miser_ns = $params['namespace']; + } else { + $this->addWhereFld( 'page_namespace', $params['namespace'] ); + } + if ( $params['sort'] == 'timestamp' ) + $this->addWhereRange( 'cl_timestamp', ( $params['dir'] == 'asc' ? 'newer' : 'older' ), $params['start'], $params['end'] ); else { - $this->addWhereRange('cl_sortkey', ($params['dir'] == 'asc' ? 'newer' : 'older'), $params['startsortkey'], $params['endsortkey']); - $this->addWhereRange('cl_from', ($params['dir'] == 'asc' ? 'newer' : 'older'), null, null); + $this->addWhereRange( 'cl_sortkey', ( $params['dir'] == 'asc' ? 'newer' : 'older' ), $params['startsortkey'], $params['endsortkey'] ); + $this->addWhereRange( 'cl_from', ( $params['dir'] == 'asc' ? 'newer' : 'older' ), null, null ); } $limit = $params['limit']; - $this->addOption('LIMIT', $limit +1); + $this->addOption( 'LIMIT', $limit + 1 ); $db = $this->getDB(); $data = array (); $count = 0; $lastSortKey = null; - $res = $this->select(__METHOD__); - while ($row = $db->fetchObject($res)) { - if (++ $count > $limit) { + $res = $this->select( __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown - if ($params['sort'] == 'timestamp') - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->cl_timestamp)); + if ( $params['sort'] == 'timestamp' ) + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->cl_timestamp ) ); else - $this->setContinueEnumParameter('continue', $this->getContinueStr($row, $lastSortKey)); + $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $row, $lastSortKey ) ); break; } - if (is_null($resultPageSet)) { + // Since domas won't tell anyone what he told long ago, apply + // cmnamespace here. This means the query may return 0 actual + // results, but on the other hand it could save returning 5000 + // useless results to the client. ~~~~ + if ( count( $miser_ns ) && !in_array( $row->page_namespace, $miser_ns ) ) + continue; + + if ( is_null( $resultPageSet ) ) { $vals = array(); - if ($fld_ids) - $vals['pageid'] = intval($row->page_id); - if ($fld_title) { - $title = Title :: makeTitle($row->page_namespace, $row->page_title); - ApiQueryBase::addTitleInfo($vals, $title); + if ( $fld_ids ) + $vals['pageid'] = intval( $row->page_id ); + if ( $fld_title ) { + $title = Title :: makeTitle( $row->page_namespace, $row->page_title ); + ApiQueryBase::addTitleInfo( $vals, $title ); } - if ($fld_sortkey) + if ( $fld_sortkey ) $vals['sortkey'] = $row->cl_sortkey; - if ($fld_timestamp) - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->cl_timestamp); - $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), - null, $vals); - if(!$fit) + if ( $fld_timestamp ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->cl_timestamp ); + $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), + null, $vals ); + if ( !$fit ) { - if ($params['sort'] == 'timestamp') - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->cl_timestamp)); + if ( $params['sort'] == 'timestamp' ) + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->cl_timestamp ) ); else - $this->setContinueEnumParameter('continue', $this->getContinueStr($row, $lastSortKey)); + $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $row, $lastSortKey ) ); break; } } else { - $resultPageSet->processDbRow($row); + $resultPageSet->processDbRow( $row ); } $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys } - $db->freeResult($res); + $db->freeResult( $res ); - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $this->getResult()->setIndexedTagName_internal( - array('query', $this->getModuleName()), 'cm'); + array( 'query', $this->getModuleName() ), 'cm' ); } } - private function getContinueStr($row, $lastSortKey) { + private function getContinueStr( $row, $lastSortKey ) { $ret = $row->cl_sortkey . '|'; - if ($row->cl_sortkey == $lastSortKey) // duplicate sort key, add cl_from + if ( $row->cl_sortkey == $lastSortKey ) // duplicate sort key, add cl_from $ret .= $row->cl_from; return $ret; } @@ -161,24 +176,24 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { /** * Add DB WHERE clause to continue previous query based on 'continue' parameter */ - private function setContinuation($continue, $dir) { - if (is_null($continue)) + private function setContinuation( $continue, $dir ) { + if ( is_null( $continue ) ) return; // This is not a continuation request - $pos = strrpos($continue, '|'); - $sortkey = substr($continue, 0, $pos); - $fromstr = substr($continue, $pos + 1); - $from = intval($fromstr); + $pos = strrpos( $continue, '|' ); + $sortkey = substr( $continue, 0, $pos ); + $fromstr = substr( $continue, $pos + 1 ); + $from = intval( $fromstr ); - if ($from == 0 && strlen($fromstr) > 0) - $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "badcontinue"); + if ( $from == 0 && strlen( $fromstr ) > 0 ) + $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "badcontinue" ); - $encSortKey = $this->getDB()->addQuotes($sortkey); - $encFrom = $this->getDB()->addQuotes($from); + $encSortKey = $this->getDB()->addQuotes( $sortkey ); + $encFrom = $this->getDB()->addQuotes( $from ); - $op = ($dir == 'desc' ? '<' : '>'); + $op = ( $dir == 'desc' ? '<' : '>' ); - if ($from != 0) { + if ( $from != 0 ) { // Duplicate sort key continue $this->addWhere( "cl_sortkey$op$encSortKey OR (cl_sortkey=$encSortKey AND cl_from$op=$encFrom)" ); } else { @@ -237,7 +252,8 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { } public function getParamDescription() { - return array ( + global $wgMiserMode; + $desc = array ( 'title' => 'Which category to enumerate (required). Must include Category: prefix', 'prop' => 'What pieces of information to include', 'namespace' => 'Only include pages in these namespaces', @@ -250,11 +266,27 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { 'continue' => 'For large categories, give the value retured from previous query', 'limit' => 'The maximum number of pages to return.', ); + if ( $wgMiserMode ) { + $desc['namespace'] = array( + $desc['namespace'], + 'NOTE: Due to $wgMiserMode, using this may result in fewer than "limit" results', + 'returned before continuing; in extreme cases, zero results may be returned.', + ); + } + return $desc; } public function getDescription() { return 'List all pages in a given category'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'notitle', 'info' => 'The cmtitle parameter is required' ), + 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' ), + ) ); + } protected function getExamples() { return array ( @@ -266,6 +298,6 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php index bd767b1b..b26c7051 100644 --- a/includes/api/ApiQueryDeletedrevs.php +++ b/includes/api/ApiQueryDeletedrevs.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,27 +35,28 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryDeletedrevs extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'dr'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'dr' ); } public function execute() { global $wgUser; // Before doing anything at all, let's check permissions - if(!$wgUser->isAllowed('deletedhistory')) - $this->dieUsage('You don\'t have permission to view deleted revision information', 'permissiondenied'); + if ( !$wgUser->isAllowed( 'deletedhistory' ) ) + $this->dieUsage( 'You don\'t have permission to view deleted revision information', 'permissiondenied' ); $db = $this->getDB(); - $params = $this->extractRequestParams(false); - $prop = array_flip($params['prop']); - $fld_revid = isset($prop['revid']); - $fld_user = isset($prop['user']); - $fld_comment = isset($prop['comment']); - $fld_minor = isset($prop['minor']); - $fld_len = isset($prop['len']); - $fld_content = isset($prop['content']); - $fld_token = isset($prop['token']); + $params = $this->extractRequestParams( false ); + $prop = array_flip( $params['prop'] ); + $fld_revid = isset( $prop['revid'] ); + $fld_user = isset( $prop['user'] ); + $fld_comment = isset( $prop['comment'] ); + $fld_parsedcomment = isset ( $prop['parsedcomment'] ); + $fld_minor = isset( $prop['minor'] ); + $fld_len = isset( $prop['len'] ); + $fld_content = isset( $prop['content'] ); + $fld_token = isset( $prop['token'] ); $result = $this->getResult(); $pageSet = $this->getPageSet(); @@ -67,36 +68,35 @@ class ApiQueryDeletedrevs extends ApiQueryBase { // 'user': List deleted revs by a certain user // 'all': List all deleted revs $mode = 'all'; - if(count($titles) > 0) + if ( count( $titles ) > 0 ) $mode = 'revs'; - else if(!is_null($params['user'])) + else if ( !is_null( $params['user'] ) ) $mode = 'user'; - if(!is_null($params['user']) && !is_null($params['excludeuser'])) - $this->dieUsage('user and excludeuser cannot be used together', 'badparams'); + if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) + $this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' ); - $this->addTables('archive'); - $this->addWhere('ar_deleted = 0'); - $this->addFields(array('ar_title', 'ar_namespace', 'ar_timestamp')); - if($fld_revid) - $this->addFields('ar_rev_id'); - if($fld_user) - $this->addFields('ar_user_text'); - if($fld_comment) - $this->addFields('ar_comment'); - if($fld_minor) - $this->addFields('ar_minor_edit'); - if($fld_len) - $this->addFields('ar_len'); - if($fld_content) - { - $this->addTables('text'); - $this->addFields(array('ar_text', 'ar_text_id', 'old_text', 'old_flags')); - $this->addWhere('ar_text_id = old_id'); + $this->addTables( 'archive' ); + $this->addWhere( 'ar_deleted = 0' ); + $this->addFields( array( 'ar_title', 'ar_namespace', 'ar_timestamp' ) ); + if ( $fld_revid ) + $this->addFields( 'ar_rev_id' ); + if ( $fld_user ) + $this->addFields( 'ar_user_text' ); + if ( $fld_comment || $fld_parsedcomment ) + $this->addFields( 'ar_comment' ); + if ( $fld_minor ) + $this->addFields( 'ar_minor_edit' ); + if ( $fld_len ) + $this->addFields( 'ar_len' ); + if ( $fld_content ) { + $this->addTables( 'text' ); + $this->addFields( array( 'ar_text', 'ar_text_id', 'old_text', 'old_flags' ) ); + $this->addWhere( 'ar_text_id = old_id' ); // This also means stricter restrictions - if(!$wgUser->isAllowed('undelete')) - $this->dieUsage('You don\'t have permission to view deleted revision content', 'permissiondenied'); + if ( !$wgUser->isAllowed( 'undelete' ) ) + $this->dieUsage( 'You don\'t have permission to view deleted revision content', 'permissiondenied' ); } // Check limits $userMax = $fld_content ? ApiBase :: LIMIT_SML1 : ApiBase :: LIMIT_BIG1; @@ -104,143 +104,136 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $limit = $params['limit']; - if( $limit == 'max' ) { + if ( $limit == 'max' ) { $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; $this->getResult()->addValue( 'limits', $this->getModuleName(), $limit ); } - $this->validateLimit('limit', $limit, 1, $userMax, $botMax); + $this->validateLimit( 'limit', $limit, 1, $userMax, $botMax ); - if($fld_token) + if ( $fld_token ) // Undelete tokens are identical for all pages, so we cache one here $token = $wgUser->editToken(); // We need a custom WHERE clause that matches all titles. - if($mode == 'revs') - { - $lb = new LinkBatch($titles); - $where = $lb->constructSet('ar', $db); - $this->addWhere($where); - } - elseif($mode == 'all') - { - $this->addWhereFld('ar_namespace', $params['namespace']); - if(!is_null($params['from'])) + if ( $mode == 'revs' ) { + $lb = new LinkBatch( $titles ); + $where = $lb->constructSet( 'ar', $db ); + $this->addWhere( $where ); + } elseif ( $mode == 'all' ) { + $this->addWhereFld( 'ar_namespace', $params['namespace'] ); + if ( !is_null( $params['from'] ) ) { - $from = $this->getDB()->strencode($this->titleToKey($params['from'])); - $this->addWhere("ar_title >= '$from'"); + $from = $this->getDB()->strencode( $this->titleToKey( $params['from'] ) ); + $this->addWhere( "ar_title >= '$from'" ); } } - if(!is_null($params['user'])) { - $this->addWhereFld('ar_user_text', $params['user']); - } elseif(!is_null($params['excludeuser'])) { - $this->addWhere('ar_user_text != ' . - $this->getDB()->addQuotes($params['excludeuser'])); + if ( !is_null( $params['user'] ) ) { + $this->addWhereFld( 'ar_user_text', $params['user'] ); + } elseif ( !is_null( $params['excludeuser'] ) ) { + $this->addWhere( 'ar_user_text != ' . + $this->getDB()->addQuotes( $params['excludeuser'] ) ); } - if(!is_null($params['continue']) && ($mode == 'all' || $mode == 'revs')) + if ( !is_null( $params['continue'] ) && ( $mode == 'all' || $mode == 'revs' ) ) { - $cont = explode('|', $params['continue']); - if(count($cont) != 3) - $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "badcontinue"); - $ns = intval($cont[0]); - $title = $this->getDB()->strencode($this->titleToKey($cont[1])); - $ts = $this->getDB()->strencode($cont[2]); - $op = ($params['dir'] == 'newer' ? '>' : '<'); - $this->addWhere("ar_namespace $op $ns OR " . + $cont = explode( '|', $params['continue'] ); + if ( count( $cont ) != 3 ) + $this->dieUsage( "Invalid continue param. You should pass the original value returned by the previous query", "badcontinue" ); + $ns = intval( $cont[0] ); + $title = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) ); + $ts = $this->getDB()->strencode( $cont[2] ); + $op = ( $params['dir'] == 'newer' ? '>' : '<' ); + $this->addWhere( "ar_namespace $op $ns OR " . "(ar_namespace = $ns AND " . "(ar_title $op '$title' OR " . "(ar_title = '$title' AND " . - "ar_timestamp = '$ts')))"); + "ar_timestamp $op= '$ts')))" ); } - $this->addOption('LIMIT', $limit + 1); - $this->addOption('USE INDEX', array('archive' => ($mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp'))); - if($mode == 'all') - { - if($params['unique']) + $this->addOption( 'LIMIT', $limit + 1 ); + $this->addOption( 'USE INDEX', array( 'archive' => ( $mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp' ) ) ); + if ( $mode == 'all' ) { + if ( $params['unique'] ) { - $this->addOption('GROUP BY', 'ar_title'); - $this->addOption('ORDER BY', 'ar_title'); - } - else - $this->addOption('ORDER BY', 'ar_title, ar_timestamp'); - } - else - { - if($mode == 'revs') + $this->addOption( 'GROUP BY', 'ar_title' ); + $this->addOption( 'ORDER BY', 'ar_title' ); + } else + $this->addOption( 'ORDER BY', 'ar_title, ar_timestamp' ); + } else { + if ( $mode == 'revs' ) { // Sort by ns and title in the same order as timestamp for efficiency - $this->addWhereRange('ar_namespace', $params['dir'], null, null); - $this->addWhereRange('ar_title', $params['dir'], null, null); + $this->addWhereRange( 'ar_namespace', $params['dir'], null, null ); + $this->addWhereRange( 'ar_title', $params['dir'], null, null ); } - $this->addWhereRange('ar_timestamp', $params['dir'], $params['start'], $params['end']); + $this->addWhereRange( 'ar_timestamp', $params['dir'], $params['start'], $params['end'] ); } - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $pageMap = array(); // Maps ns&title to (fake) pageid $count = 0; $newPageID = 0; - while($row = $db->fetchObject($res)) + while ( $row = $db->fetchObject( $res ) ) { - if(++$count > $limit) - { + if ( ++$count > $limit ) { // We've had enough - if($mode == 'all' || $mode == 'revs') - $this->setContinueEnumParameter('continue', intval($row->ar_namespace) . '|' . - $this->keyToTitle($row->ar_title) . '|' . $row->ar_timestamp); + if ( $mode == 'all' || $mode == 'revs' ) + $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' . + $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp ); else - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) ); break; } $rev = array(); - $rev['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ar_timestamp); - if($fld_revid) - $rev['revid'] = intval($row->ar_rev_id); - if($fld_user) + $rev['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ar_timestamp ); + if ( $fld_revid ) + $rev['revid'] = intval( $row->ar_rev_id ); + if ( $fld_user ) $rev['user'] = $row->ar_user_text; - if($fld_comment) + if ( $fld_comment ) $rev['comment'] = $row->ar_comment; - if($fld_minor) - if($row->ar_minor_edit == 1) - $rev['minor'] = ''; - if($fld_len) + + $title = Title::makeTitle( $row->ar_namespace, $row->ar_title ); + + if ( $fld_parsedcomment ) { + global $wgUser; + $rev['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->ar_comment, $title ); + } + if ( $fld_minor && $row->ar_minor_edit == 1 ) + $rev['minor'] = ''; + if ( $fld_len ) $rev['len'] = $row->ar_len; - if($fld_content) - ApiResult::setContent($rev, Revision::getRevisionText($row)); + if ( $fld_content ) + ApiResult::setContent( $rev, Revision::getRevisionText( $row ) ); - if(!isset($pageMap[$row->ar_namespace][$row->ar_title])) - { + if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) { $pageID = $newPageID++; $pageMap[$row->ar_namespace][$row->ar_title] = $pageID; - $t = Title::makeTitle($row->ar_namespace, $row->ar_title); - $a['revisions'] = array($rev); - $result->setIndexedTagName($a['revisions'], 'rev'); - ApiQueryBase::addTitleInfo($a, $t); - if($fld_token) + $a['revisions'] = array( $rev ); + $result->setIndexedTagName( $a['revisions'], 'rev' ); + ApiQueryBase::addTitleInfo( $a, $title ); + if ( $fld_token ) $a['token'] = $token; - $fit = $result->addValue(array('query', $this->getModuleName()), $pageID, $a); - } - else - { + $fit = $result->addValue( array( 'query', $this->getModuleName() ), $pageID, $a ); + } else { $pageID = $pageMap[$row->ar_namespace][$row->ar_title]; $fit = $result->addValue( - array('query', $this->getModuleName(), $pageID, 'revisions'), - null, $rev); + array( 'query', $this->getModuleName(), $pageID, 'revisions' ), + null, $rev ); } - if(!$fit) - { - if($mode == 'all' || $mode == 'revs') - $this->setContinueEnumParameter('continue', intval($row->ar_namespace) . '|' . - $this->keyToTitle($row->ar_title) . '|' . $row->ar_timestamp); + if ( !$fit ) { + if ( $mode == 'all' || $mode == 'revs' ) + $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' . + $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp ); else - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) ); break; } } - $db->freeResult($res); - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'page'); + $db->freeResult( $res ); + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'page' ); } public function getAllowedParams() { @@ -284,6 +277,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase { 'revid', 'user', 'comment', + 'parsedcomment', 'minor', 'len', 'content', @@ -320,6 +314,15 @@ class ApiQueryDeletedrevs extends ApiQueryBase { 'For instance, a parameter marked (1) only applies to mode 1 and is ignored in modes 2 and 3.', ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revision information' ), + array( 'code' => 'badparams', 'info' => 'user and excludeuser cannot be used together' ), + array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revision content' ), + array( 'code' => 'badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ), + ) ); + } protected function getExamples() { return array ( @@ -335,6 +338,6 @@ class ApiQueryDeletedrevs extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 69578 2010-07-20 02:46:20Z tstarling $'; } } \ No newline at end of file diff --git a/includes/api/ApiQueryDisabled.php b/includes/api/ApiQueryDisabled.php index 50825464..4bd3f5fd 100644 --- a/includes/api/ApiQueryDisabled.php +++ b/includes/api/ApiQueryDisabled.php @@ -22,9 +22,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } @@ -40,12 +40,12 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryDisabled extends ApiQueryBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { - $this->setWarning("The ``{$this->getModuleName()}'' module has been disabled."); + $this->setWarning( "The ``{$this->getModuleName()}'' module has been disabled." ); } public function getAllowedParams() { @@ -67,6 +67,6 @@ class ApiQueryDisabled extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryDisabled.php 41268 2008-09-25 20:50:50Z catrope $'; + return __CLASS__ . ': $Id: ApiQueryDisabled.php 60930 2010-01-11 15:55:52Z simetrical $'; } } diff --git a/includes/api/ApiQueryDuplicateFiles.php b/includes/api/ApiQueryDuplicateFiles.php index a59ee356..ed070069 100644 --- a/includes/api/ApiQueryDuplicateFiles.php +++ b/includes/api/ApiQueryDuplicateFiles.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'df'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'df' ); } public function execute() { @@ -47,11 +47,11 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { $params = $this->extractRequestParams(); $namespaces = $this->getPageSet()->getAllTitlesByNamespace(); if ( empty( $namespaces[NS_FILE] ) ) { @@ -59,71 +59,74 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { } $images = $namespaces[NS_FILE]; - $this->addTables('image', 'i1'); - $this->addTables('image', 'i2'); - $this->addFields(array( + $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), + ) ); + + $this->addWhere( array( + 'i1.img_name' => array_keys( $images ), 'i1.img_sha1 = i2.img_sha1', 'i1.img_name != i2.img_name', - )); - if(isset($params['continue'])) + ) ); + + 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')"); + $cont = explode( '|', $params['continue'] ); + if ( count( $cont ) != 2 ) + $this->dieUsage( "Invalid continue param. You should pass the " . + "original value returned by the previous query", "_badcontinue" ); + $orig = $this->getDB()->strencode( $this->titleTokey( $cont[0] ) ); + $dup = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) ); + $this->addWhere( "i1.img_name > '$orig' OR " . + "(i1.img_name = '$orig' AND " . + "i2.img_name >= '$dup')" ); } - $this->addOption('ORDER BY', 'i1.img_name'); - $this->addOption('LIMIT', $params['limit'] + 1); - $res = $this->select(__METHOD__); + $this->addOption( 'ORDER BY', 'i1.img_name' ); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + + $res = $this->select( __METHOD__ ); $db = $this->getDB(); $count = 0; $titles = array(); - while($row = $db->fetchObject($res)) + while ( $row = $db->fetchObject( $res ) ) { - if(++$count > $params['limit']) + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', - $this->keyToTitle($row->orig_name) . '|' . - $this->keyToTitle($row->dup_name)); + $this->setContinueEnumParameter( 'continue', + $this->keyToTitle( $row->orig_name ) . '|' . + $this->keyToTitle( $row->dup_name ) ); break; } - if(!is_null($resultPageSet)) - $titles[] = Title::makeTitle(NS_FILE, $row->dup_name); + 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) + 'timestamp' => wfTimestamp( TS_ISO_8601, $row->dup_timestamp ) ); - $fit = $this->addPageSubItem($images[$row->orig_name], $r); - if(!$fit) + $fit = $this->addPageSubItem( $images[$row->orig_name], $r ); + if ( !$fit ) { - $this->setContinueEnumParameter('continue', - $this->keyToTitle($row->orig_name) . '|' . - $this->keyToTitle($row->dup_name)); + $this->setContinueEnumParameter( 'continue', + $this->keyToTitle( $row->orig_name ) . '|' . + $this->keyToTitle( $row->dup_name ) ); break; } } } - if(!is_null($resultPageSet)) - $resultPageSet->populateFromTitles($titles); - $db->freeResult($res); + if ( !is_null( $resultPageSet ) ) + $resultPageSet->populateFromTitles( $titles ); + $db->freeResult( $res ); } public function getAllowedParams() { @@ -149,6 +152,12 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { public function getDescription() { return 'List all files that are duplicates of the given file(s).'; } + + 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' ), + ) ); + } protected function getExamples() { return array ( 'api.php?action=query&titles=File:Albert_Einstein_Head.jpg&prop=duplicatefiles', @@ -157,6 +166,6 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryDuplicateFiles.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryDuplicateFiles.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php index 08f6ab1f..0e171e44 100644 --- a/includes/api/ApiQueryExtLinksUsage.php +++ b/includes/api/ApiQueryExtLinksUsage.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -33,8 +33,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'eu'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'eu' ); } public function execute() { @@ -45,11 +45,11 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { $params = $this->extractRequestParams(); @@ -58,10 +58,10 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { // Find the right prefix global $wgUrlProtocols; - if($protocol && !in_array($protocol, $wgUrlProtocols)) + if ( $protocol && !in_array( $protocol, $wgUrlProtocols ) ) { - foreach ($wgUrlProtocols as $p) { - if( substr( $p, 0, strlen( $protocol ) ) === $protocol ) { + foreach ( $wgUrlProtocols as $p ) { + if ( substr( $p, 0, strlen( $protocol ) ) === $protocol ) { $protocol = $p; break; } @@ -71,91 +71,92 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { $protocol = null; $db = $this->getDB(); - $this->addTables(array('page','externallinks')); // must be in this order for 'USE INDEX' - $this->addOption('USE INDEX', 'el_index'); - $this->addWhere('page_id=el_from'); - $this->addWhereFld('page_namespace', $params['namespace']); + $this->addTables( array( 'page', 'externallinks' ) ); // must be in this order for 'USE INDEX' + $this->addOption( 'USE INDEX', 'el_index' ); + $this->addWhere( 'page_id=el_from' ); + $this->addWhereFld( 'page_namespace', $params['namespace'] ); - if(!is_null($query) || $query != '') + if ( !is_null( $query ) || $query != '' ) { - if(is_null($protocol)) + if ( is_null( $protocol ) ) $protocol = 'http://'; - $likeQuery = LinkFilter::makeLike($query, $protocol); - if (!$likeQuery) - $this->dieUsage('Invalid query', 'bad_query'); - $likeQuery = substr($likeQuery, 0, strpos($likeQuery,'%')+1); - $this->addWhere('el_index LIKE ' . $db->addQuotes( $likeQuery )); + $likeQuery = LinkFilter::makeLikeArray( $query, $protocol ); + if ( !$likeQuery ) + $this->dieUsage( 'Invalid query', 'bad_query' ); + + $likeQuery = LinkFilter::keepOneWildcard( $likeQuery ); + $this->addWhere( 'el_index ' . $db->buildLike( $likeQuery ) ); } - else if(!is_null($protocol)) - $this->addWhere('el_index LIKE ' . $db->addQuotes( "$protocol%" )); + else if ( !is_null( $protocol ) ) + $this->addWhere( 'el_index ' . $db->buildLike( "$protocol", $db->anyString() ) ); - $prop = array_flip($params['prop']); - $fld_ids = isset($prop['ids']); - $fld_title = isset($prop['title']); - $fld_url = isset($prop['url']); + $prop = array_flip( $params['prop'] ); + $fld_ids = isset( $prop['ids'] ); + $fld_title = isset( $prop['title'] ); + $fld_url = isset( $prop['url'] ); - if (is_null($resultPageSet)) { - $this->addFields(array ( + if ( is_null( $resultPageSet ) ) { + $this->addFields( array ( 'page_id', 'page_namespace', 'page_title' - )); - $this->addFieldsIf('el_to', $fld_url); + ) ); + $this->addFieldsIf( 'el_to', $fld_url ); } else { - $this->addFields($resultPageSet->getPageTableFields()); + $this->addFields( $resultPageSet->getPageTableFields() ); } $limit = $params['limit']; $offset = $params['offset']; - $this->addOption('LIMIT', $limit +1); - if (isset ($offset)) - $this->addOption('OFFSET', $offset); + $this->addOption( 'LIMIT', $limit + 1 ); + if ( isset ( $offset ) ) + $this->addOption( 'OFFSET', $offset ); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $result = $this->getResult(); $count = 0; - while ($row = $db->fetchObject($res)) { - if (++ $count > $limit) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('offset', $offset+$limit); + $this->setContinueEnumParameter( 'offset', $offset + $limit ); break; } - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $vals = array(); - if ($fld_ids) - $vals['pageid'] = intval($row->page_id); - if ($fld_title) { - $title = Title :: makeTitle($row->page_namespace, $row->page_title); - ApiQueryBase::addTitleInfo($vals, $title); + if ( $fld_ids ) + $vals['pageid'] = intval( $row->page_id ); + if ( $fld_title ) { + $title = Title :: makeTitle( $row->page_namespace, $row->page_title ); + ApiQueryBase::addTitleInfo( $vals, $title ); } - if ($fld_url) + if ( $fld_url ) $vals['url'] = $row->el_to; - $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('offset', $offset + $count - 1); + $this->setContinueEnumParameter( 'offset', $offset + $count - 1 ); break; } } else { - $resultPageSet->processDbRow($row); + $resultPageSet->processDbRow( $row ); } } - $db->freeResult($res); + $db->freeResult( $res ); - if (is_null($resultPageSet)) { - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), - $this->getModulePrefix()); + if ( is_null( $resultPageSet ) ) { + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), + $this->getModulePrefix() ); } } public function getAllowedParams() { global $wgUrlProtocols; - $protocols = array(''); - foreach ($wgUrlProtocols as $p) { - $protocols[] = substr($p, 0, strpos($p,':')); + $protocols = array( '' ); + foreach ( $wgUrlProtocols as $p ) { + $protocols[] = substr( $p, 0, strpos( $p, ':' ) ); } return array ( @@ -195,7 +196,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { 'prop' => 'What pieces of information to include', 'offset' => 'Used for paging. Use the value returned for "continue"', 'protocol' => array( 'Protocol of the url. If empty and euquery set, the protocol is http.', - 'Leave both this and euquery empty to list all external links'), + 'Leave both this and euquery empty to list all external links' ), 'query' => 'Search string without protocol. See [[Special:LinkSearch]]. Leave empty to list all external links', 'namespace' => 'The page namespace(s) to enumerate.', 'limit' => 'How many pages to return.' @@ -205,6 +206,12 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { public function getDescription() { return 'Enumerate pages that contain a given URL'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'bad_query', 'info' => 'Invalid query' ), + ) ); + } protected function getExamples() { return array ( @@ -213,6 +220,6 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php index 0bddd6df..a748e036 100644 --- a/includes/api/ApiQueryExternalLinks.php +++ b/includes/api/ApiQueryExternalLinks.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryExternalLinks extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'el'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'el' ); } public function execute() { @@ -44,41 +44,43 @@ class ApiQueryExternalLinks extends ApiQueryBase { return; $params = $this->extractRequestParams(); - $this->addFields(array ( + $this->addFields( array ( 'el_from', 'el_to' - )); + ) ); - $this->addTables('externallinks'); - $this->addWhereFld('el_from', array_keys($this->getPageSet()->getGoodTitles())); - # Don't order by el_from if it's constant in the WHERE clause - if(count($this->getPageSet()->getGoodTitles()) != 1) - $this->addOption('ORDER BY', 'el_from'); - $this->addOption('LIMIT', $params['limit'] + 1); - if(!is_null($params['offset'])) - $this->addOption('OFFSET', $params['offset']); + $this->addTables( 'externallinks' ); + $this->addWhereFld( 'el_from', array_keys( $this->getPageSet()->getGoodTitles() ) ); + + // Don't order by el_from if it's constant in the WHERE clause + if ( count( $this->getPageSet()->getGoodTitles() ) != 1 ) + $this->addOption( 'ORDER BY', 'el_from' ); + + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + if ( !is_null( $params['offset'] ) ) + $this->addOption( 'OFFSET', $params['offset'] ); $db = $this->getDB(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $count = 0; - while ($row = $db->fetchObject($res)) { - if (++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('offset', @$params['offset'] + $params['limit']); + $this->setContinueEnumParameter( 'offset', @$params['offset'] + $params['limit'] ); break; } $entry = array(); - ApiResult :: setContent($entry, $row->el_to); - $fit = $this->addPageSubItem($row->el_from, $entry); - if(!$fit) + ApiResult :: setContent( $entry, $row->el_to ); + $fit = $this->addPageSubItem( $row->el_from, $entry ); + if ( !$fit ) { - $this->setContinueEnumParameter('offset', @$params['offset'] + $count - 1); + $this->setContinueEnumParameter( 'offset', @$params['offset'] + $count - 1 ); break; } } - $db->freeResult($res); + $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -117,6 +119,6 @@ class ApiQueryExternalLinks extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryExternalLinks.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryExternalLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php index c4c71075..3704710a 100644 --- a/includes/api/ApiQueryImageInfo.php +++ b/includes/api/ApiQueryImageInfo.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,19 +35,19 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryImageInfo extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'ii'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'ii' ); } public function execute() { $params = $this->extractRequestParams(); - $prop = array_flip($params['prop']); + $prop = array_flip( $params['prop'] ); - if($params['urlheight'] != -1 && $params['urlwidth'] == -1) - $this->dieUsage("iiurlheight cannot be used without iiurlwidth", 'iiurlwidth'); + if ( $params['urlheight'] != - 1 && $params['urlwidth'] == - 1 ) + $this->dieUsage( "iiurlheight cannot be used without iiurlwidth", 'iiurlwidth' ); - if ( $params['urlwidth'] != -1 ) { + if ( $params['urlwidth'] != - 1 ) { $scale = array(); $scale['width'] = $params['urlwidth']; $scale['height'] = $params['urlheight']; @@ -57,23 +57,23 @@ class ApiQueryImageInfo extends ApiQueryBase { $pageIds = $this->getPageSet()->getAllTitlesByNamespace(); if ( !empty( $pageIds[NS_FILE] ) ) { - $titles = array_keys($pageIds[NS_FILE]); - asort($titles); // Ensure the order is always the same + $titles = array_keys( $pageIds[NS_FILE] ); + asort( $titles ); // Ensure the order is always the same $skip = false; - if(!is_null($params['continue'])) + if ( !is_null( $params['continue'] ) ) { $skip = true; - $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"); - $fromTitle = strval($cont[0]); + $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" ); + $fromTitle = strval( $cont[0] ); $fromTimestamp = $cont[1]; // Filter out any titles before $fromTitle - foreach($titles as $key => $title) - if($title < $fromTitle) - unset($titles[$key]); + foreach ( $titles as $key => $title ) + if ( $title < $fromTitle ) + unset( $titles[$key] ); else break; } @@ -81,90 +81,95 @@ class ApiQueryImageInfo extends ApiQueryBase { $result = $this->getResult(); $images = RepoGroup::singleton()->findFiles( $titles ); foreach ( $images as $img ) { + // Skip redirects + if ( $img->getOriginalTitle()->isRedirect() ) + continue; + $start = $skip ? $fromTimestamp : $params['start']; $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ]; $fit = $result->addValue( - array('query', 'pages', intval($pageId)), + array( 'query', 'pages', intval( $pageId ) ), 'imagerepository', $img->getRepoName() ); - if(!$fit) + if ( !$fit ) { - if(count($pageIds[NS_IMAGE]) == 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 - # thing again. When the violating queries have been - # out-continued, the result will get through - $this->setContinueEnumParameter('start', - wfTimestamp(TS_ISO_8601, $img->getTimestamp())); + if ( count( $pageIds[NS_IMAGE] ) == 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 + // thing again. When the violating queries have been + // out-continued, the result will get through + $this->setContinueEnumParameter( 'start', + wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); else - $this->setContinueEnumParameter('continue', - $this->getContinueStr($img)); + $this->setContinueEnumParameter( 'continue', + $this->getContinueStr( $img ) ); break; } // Get information about the current version first // Check that the current version is within the start-end boundaries $gotOne = false; - if((is_null($start) || $img->getTimestamp() <= $start) && - (is_null($params['end']) || $img->getTimestamp() >= $params['end'])) { + if ( ( is_null( $start ) || $img->getTimestamp() <= $start ) && + ( is_null( $params['end'] ) || $img->getTimestamp() >= $params['end'] ) ) { $gotOne = true; - $fit = $this->addPageSubItem($pageId, - self::getInfo( $img, $prop, $result, $scale)); - if(!$fit) + $fit = $this->addPageSubItem( $pageId, + self::getInfo( $img, $prop, $result, $scale ) ); + if ( !$fit ) { - if(count($pageIds[NS_IMAGE]) == 1) - # See the 'the user is screwed' comment above - $this->setContinueEnumParameter('start', - wfTimestamp(TS_ISO_8601, $img->getTimestamp())); + if ( count( $pageIds[NS_IMAGE] ) == 1 ) + // See the 'the user is screwed' comment above + $this->setContinueEnumParameter( 'start', + wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); else - $this->setContinueEnumParameter('continue', - $this->getContinueStr($img)); + $this->setContinueEnumParameter( 'continue', + $this->getContinueStr( $img ) ); break; } } // Now get the old revisions // Get one more to facilitate query-continue functionality - $count = ($gotOne ? 1 : 0); - $oldies = $img->getHistory($params['limit'] - $count + 1, $start, $params['end']); - foreach($oldies as $oldie) { - if(++$count > $params['limit']) { + $count = ( $gotOne ? 1 : 0 ); + $oldies = $img->getHistory( $params['limit'] - $count + 1, $start, $params['end'] ); + foreach ( $oldies as $oldie ) { + if ( ++$count > $params['limit'] ) { // We've reached the extra one which shows that there are additional pages to be had. Stop here... // Only set a query-continue if there was only one title - if(count($pageIds[NS_FILE]) == 1) + if ( count( $pageIds[NS_FILE] ) == 1 ) { - $this->setContinueEnumParameter('start', - wfTimestamp(TS_ISO_8601, $oldie->getTimestamp())); + $this->setContinueEnumParameter( 'start', + wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); } break; } - $fit = $this->addPageSubItem($pageId, - self::getInfo($oldie, $prop, $result)); - if(!$fit) + $fit = $this->addPageSubItem( $pageId, + self::getInfo( $oldie, $prop, $result ) ); + if ( !$fit ) { - if(count($pageIds[NS_IMAGE]) == 1) - $this->setContinueEnumParameter('start', - wfTimestamp(TS_ISO_8601, $oldie->getTimestamp())); + if ( count( $pageIds[NS_IMAGE] ) == 1 ) + $this->setContinueEnumParameter( 'start', + wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); else - $this->setContinueEnumParameter('continue', - $this->getContinueStr($oldie)); + $this->setContinueEnumParameter( 'continue', + $this->getContinueStr( $oldie ) ); break; } } - if(!$fit) + if ( !$fit ) break; $skip = false; } - $missing = array_diff( array_keys( $pageIds[NS_FILE] ), array_keys( $images ) ); - foreach ($missing as $title) { - $result->addValue( - array('query', 'pages', intval($pageIds[NS_FILE][$title])), - 'imagerepository', '' - ); - // The above can't fail because it doesn't increase the result size + $data = $this->getResultData(); + foreach ( $data['query']['pages'] as $pageid => $arr ) { + if ( !isset( $arr['imagerepository'] ) ) + $result->addValue( + array( 'query', 'pages', $pageid ), + 'imagerepository', '' + ); + // The above can't fail because it doesn't increase the result size } } } @@ -174,26 +179,26 @@ class ApiQueryImageInfo extends ApiQueryBase { * @param File f The image * @return array Result array */ - static function getInfo($file, $prop, $result, $scale = null) { + static function getInfo( $file, $prop, $result, $scale = null ) { $vals = array(); - if( isset( $prop['timestamp'] ) ) - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $file->getTimestamp()); - if( isset( $prop['user'] ) ) { + if ( isset( $prop['timestamp'] ) ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() ); + if ( isset( $prop['user'] ) ) { $vals['user'] = $file->getUser(); - if( !$file->getUser( 'id' ) ) + if ( !$file->getUser( 'id' ) ) $vals['anon'] = ''; } - if( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) { + if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) { $vals['size'] = intval( $file->getSize() ); $vals['width'] = intval( $file->getWidth() ); $vals['height'] = intval( $file->getHeight() ); } - if( isset( $prop['url'] ) ) { - if( !is_null( $scale ) && !$file->isOld() ) { + if ( isset( $prop['url'] ) ) { + if ( !is_null( $scale ) && !$file->isOld() ) { $mto = $file->transform( array( 'width' => $scale['width'], 'height' => $scale['height'] ) ); - if( $mto && !$mto->isError() ) + if ( $mto && !$mto->isError() ) { - $vals['thumburl'] = $mto->getUrl(); + $vals['thumburl'] = wfExpandUrl( $mto->getUrl() ); $vals['thumbwidth'] = intval( $mto->getWidth() ); $vals['thumbheight'] = intval( $mto->getHeight() ); } @@ -201,41 +206,41 @@ class ApiQueryImageInfo extends ApiQueryBase { $vals['url'] = $file->getFullURL(); $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl() ); } - if( isset( $prop['comment'] ) ) + if ( isset( $prop['comment'] ) ) $vals['comment'] = $file->getDescription(); - if( isset( $prop['sha1'] ) ) + if ( isset( $prop['sha1'] ) ) $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 ); - if( isset( $prop['metadata'] ) ) { + if ( isset( $prop['metadata'] ) ) { $metadata = $file->getMetadata(); $vals['metadata'] = $metadata ? self::processMetaData( unserialize( $metadata ), $result ) : null; } - if( isset( $prop['mime'] ) ) + if ( isset( $prop['mime'] ) ) $vals['mime'] = $file->getMimeType(); - if( isset( $prop['archivename'] ) && $file->isOld() ) + if ( isset( $prop['archivename'] ) && $file->isOld() ) $vals['archivename'] = $file->getArchiveName(); - if( isset( $prop['bitdepth'] ) ) + if ( isset( $prop['bitdepth'] ) ) $vals['bitdepth'] = $file->getBitDepth(); return $vals; } - public static function processMetaData($metadata, $result) + public static function processMetaData( $metadata, $result ) { $retval = array(); if ( is_array( $metadata ) ) { - foreach($metadata as $key => $value) + foreach ( $metadata as $key => $value ) { - $r = array('name' => $key); - if(is_array($value)) - $r['value'] = self::processMetaData($value, $result); + $r = array( 'name' => $key ); + if ( is_array( $value ) ) + $r['value'] = self::processMetaData( $value, $result ); else $r['value'] = $value; $retval[] = $r; } } - $result->setIndexedTagName($retval, 'metadata'); + $result->setIndexedTagName( $retval, 'metadata' ); return $retval; } @@ -243,7 +248,7 @@ class ApiQueryImageInfo extends ApiQueryBase { return 'public'; } - private function getContinueStr($img) + private function getContinueStr( $img ) { return $img->getOriginalTitle()->getText() . '|' . $img->getTimestamp(); @@ -254,18 +259,7 @@ class ApiQueryImageInfo extends ApiQueryBase { 'prop' => array ( ApiBase :: PARAM_ISMULTI => true, ApiBase :: PARAM_DFLT => 'timestamp|user', - ApiBase :: PARAM_TYPE => array ( - 'timestamp', - 'user', - 'comment', - 'url', - 'size', - 'sha1', - 'mime', - 'metadata', - 'archivename', - 'bitdepth', - ) + ApiBase :: PARAM_TYPE => self::getPropertyNames() ), 'limit' => array( ApiBase :: PARAM_TYPE => 'limit', @@ -282,15 +276,34 @@ class ApiQueryImageInfo extends ApiQueryBase { ), 'urlwidth' => array( ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => -1 + ApiBase :: PARAM_DFLT => - 1 ), 'urlheight' => array( ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => -1 + ApiBase :: PARAM_DFLT => - 1 ), 'continue' => null, ); } + + /** + * Returns all possible parameters to iiprop + */ + public static function getPropertyNames() { + return array ( + 'timestamp', + 'user', + 'comment', + 'url', + 'size', + 'dimensions', // For backwards compatibility with Allimages + 'sha1', + 'mime', + 'metadata', + 'archivename', + 'bitdepth', + ); + } public function getParamDescription() { return array ( @@ -298,8 +311,8 @@ class ApiQueryImageInfo extends ApiQueryBase { 'limit' => 'How many image revisions to return', 'start' => 'Timestamp to start listing from', 'end' => 'Timestamp to stop listing at', - 'urlwidth' => array('If iiprop=url is set, a URL to an image scaled to this width will be returned.', - 'Only the current version of the image can be scaled.'), + 'urlwidth' => array( 'If iiprop=url is set, a URL to an image scaled to this width will be returned.', + 'Only the current version of the image can be scaled.' ), 'urlheight' => 'Similar to iiurlwidth. Cannot be used without iiurlwidth', 'continue' => 'When more results are available, use this to continue', ); @@ -310,6 +323,12 @@ class ApiQueryImageInfo extends ApiQueryBase { 'Returns image information and upload history' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'iiurlwidth', 'info' => 'iiurlheight cannot be used without iiurlwidth' ), + ) ); + } protected function getExamples() { return array ( @@ -319,6 +338,6 @@ class ApiQueryImageInfo extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryImageInfo.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryImageInfo.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryImages.php b/includes/api/ApiQueryImages.php index 9dbe08a6..65df94dc 100644 --- a/includes/api/ApiQueryImages.php +++ b/includes/api/ApiQueryImages.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -35,69 +35,70 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryImages extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'im'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'im' ); } public function execute() { $this->run(); } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { - if ($this->getPageSet()->getGoodTitleCount() == 0) + if ( $this->getPageSet()->getGoodTitleCount() == 0 ) return; // nothing to do $params = $this->extractRequestParams(); - $this->addFields(array ( + $this->addFields( array ( 'il_from', 'il_to' - )); - - $this->addTables('imagelinks'); - $this->addWhereFld('il_from', array_keys($this->getPageSet()->getGoodTitles())); - if(!is_null($params['continue'])) { - $cont = explode('|', $params['continue']); - if(count($cont) != 2) - $this->dieUsage("Invalid continue param. You should pass the " . - "original value returned by the previous query", "_badcontinue"); - $ilfrom = intval($cont[0]); - $ilto = $this->getDB()->strencode($this->titleToKey($cont[1])); - $this->addWhere("il_from > $ilfrom OR ". - "(il_from = $ilfrom AND ". - "il_to >= '$ilto')"); + ) ); + + $this->addTables( 'imagelinks' ); + $this->addWhereFld( 'il_from', array_keys( $this->getPageSet()->getGoodTitles() ) ); + if ( !is_null( $params['continue'] ) ) { + $cont = explode( '|', $params['continue'] ); + if ( count( $cont ) != 2 ) + $this->dieUsage( "Invalid continue param. You should pass the " . + "original value returned by the previous query", "_badcontinue" ); + $ilfrom = intval( $cont[0] ); + $ilto = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) ); + $this->addWhere( "il_from > $ilfrom OR " . + "(il_from = $ilfrom AND " . + "il_to >= '$ilto')" ); } - # Don't order by il_from if it's constant in the WHERE clause - if(count($this->getPageSet()->getGoodTitles()) == 1) - $this->addOption('ORDER BY', 'il_to'); + + // Don't order by il_from if it's constant in the WHERE clause + if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) + $this->addOption( 'ORDER BY', 'il_to' ); else - $this->addOption('ORDER BY', 'il_from, il_to'); - $this->addOption('LIMIT', $params['limit'] + 1); + $this->addOption( 'ORDER BY', 'il_from, il_to' ); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); $db = $this->getDB(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $count = 0; - while ($row = $db->fetchObject($res)) { - if (++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', $row->il_from . - '|' . $this->keyToTitle($row->il_to)); + $this->setContinueEnumParameter( 'continue', $row->il_from . + '|' . $this->keyToTitle( $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) + 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 . + '|' . $this->keyToTitle( $row->il_to ) ); break; } } @@ -105,20 +106,20 @@ class ApiQueryImages extends ApiQueryGeneratorBase { $titles = array(); $count = 0; - while ($row = $db->fetchObject($res)) { - if (++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', $row->il_from . - '|' . $this->keyToTitle($row->il_to)); + $this->setContinueEnumParameter( 'continue', $row->il_from . + '|' . $this->keyToTitle( $row->il_to ) ); break; } - $titles[] = Title :: makeTitle(NS_FILE, $row->il_to); + $titles[] = Title :: makeTitle( NS_FILE, $row->il_to ); } - $resultPageSet->populateFromTitles($titles); + $resultPageSet->populateFromTitles( $titles ); } - $db->freeResult($res); + $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -148,6 +149,12 @@ class ApiQueryImages extends ApiQueryGeneratorBase { public function getDescription() { return 'Returns all images contained on the given page(s)'; } + + 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' ), + ) ); + } protected function getExamples() { return array ( @@ -159,6 +166,6 @@ class ApiQueryImages extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryImages.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryImages.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index f78450b7..b1c2963c 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -34,23 +34,24 @@ if (!defined('MEDIAWIKI')) { * @ingroup API */ class ApiQueryInfo extends ApiQueryBase { - + private $fld_protection = false, $fld_talkid = false, $fld_subjectid = false, $fld_url = false, - $fld_readable = false; + $fld_readable = false, $fld_watched = false, + $fld_preload = false; - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'in'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'in' ); } - public function requestExtraData($pageSet) { - $pageSet->requestField('page_restrictions'); - $pageSet->requestField('page_is_redirect'); - $pageSet->requestField('page_is_new'); - $pageSet->requestField('page_counter'); - $pageSet->requestField('page_touched'); - $pageSet->requestField('page_latest'); - $pageSet->requestField('page_len'); + public function requestExtraData( $pageSet ) { + $pageSet->requestField( 'page_restrictions' ); + $pageSet->requestField( 'page_is_redirect' ); + $pageSet->requestField( 'page_is_new' ); + $pageSet->requestField( 'page_counter' ); + $pageSet->requestField( 'page_touched' ); + $pageSet->requestField( 'page_latest' ); + $pageSet->requestField( 'page_len' ); } /** @@ -61,11 +62,11 @@ class ApiQueryInfo extends ApiQueryBase { */ protected function getTokenFunctions() { // Don't call the hooks twice - if(isset($this->tokenFunctions)) + if ( isset( $this->tokenFunctions ) ) return $this->tokenFunctions; // If we're in JSON callback mode, no tokens can be obtained - if(!is_null($this->getMain()->getRequest()->getVal('callback'))) + if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) return array(); $this->tokenFunctions = array( @@ -78,112 +79,112 @@ class ApiQueryInfo extends ApiQueryBase { 'email' => array( 'ApiQueryInfo', 'getEmailToken' ), 'import' => array( 'ApiQueryInfo', 'getImportToken' ), ); - wfRunHooks('APIQueryInfoTokens', array(&$this->tokenFunctions)); + wfRunHooks( 'APIQueryInfoTokens', array( &$this->tokenFunctions ) ); return $this->tokenFunctions; } - public static function getEditToken($pageid, $title) + public static function getEditToken( $pageid, $title ) { // We could check for $title->userCan('edit') here, // but that's too expensive for this purpose // and would break caching global $wgUser; - if(!$wgUser->isAllowed('edit')) + if ( !$wgUser->isAllowed( 'edit' ) ) return false; - + // The edit token is always the same, let's exploit that static $cachedEditToken = null; - if(!is_null($cachedEditToken)) + if ( !is_null( $cachedEditToken ) ) return $cachedEditToken; $cachedEditToken = $wgUser->editToken(); return $cachedEditToken; } - - public static function getDeleteToken($pageid, $title) + + public static function getDeleteToken( $pageid, $title ) { global $wgUser; - if(!$wgUser->isAllowed('delete')) - return false; + if ( !$wgUser->isAllowed( 'delete' ) ) + return false; static $cachedDeleteToken = null; - if(!is_null($cachedDeleteToken)) + if ( !is_null( $cachedDeleteToken ) ) return $cachedDeleteToken; $cachedDeleteToken = $wgUser->editToken(); return $cachedDeleteToken; } - public static function getProtectToken($pageid, $title) + public static function getProtectToken( $pageid, $title ) { global $wgUser; - if(!$wgUser->isAllowed('protect')) + if ( !$wgUser->isAllowed( 'protect' ) ) return false; static $cachedProtectToken = null; - if(!is_null($cachedProtectToken)) + if ( !is_null( $cachedProtectToken ) ) return $cachedProtectToken; $cachedProtectToken = $wgUser->editToken(); return $cachedProtectToken; } - public static function getMoveToken($pageid, $title) + public static function getMoveToken( $pageid, $title ) { global $wgUser; - if(!$wgUser->isAllowed('move')) + if ( !$wgUser->isAllowed( 'move' ) ) return false; static $cachedMoveToken = null; - if(!is_null($cachedMoveToken)) + if ( !is_null( $cachedMoveToken ) ) return $cachedMoveToken; $cachedMoveToken = $wgUser->editToken(); return $cachedMoveToken; } - public static function getBlockToken($pageid, $title) + public static function getBlockToken( $pageid, $title ) { global $wgUser; - if(!$wgUser->isAllowed('block')) + if ( !$wgUser->isAllowed( 'block' ) ) return false; static $cachedBlockToken = null; - if(!is_null($cachedBlockToken)) + if ( !is_null( $cachedBlockToken ) ) return $cachedBlockToken; $cachedBlockToken = $wgUser->editToken(); return $cachedBlockToken; } - public static function getUnblockToken($pageid, $title) + public static function getUnblockToken( $pageid, $title ) { // Currently, this is exactly the same as the block token - return self::getBlockToken($pageid, $title); + return self::getBlockToken( $pageid, $title ); } - public static function getEmailToken($pageid, $title) + public static function getEmailToken( $pageid, $title ) { global $wgUser; - if(!$wgUser->canSendEmail() || $wgUser->isBlockedFromEmailUser()) + if ( !$wgUser->canSendEmail() || $wgUser->isBlockedFromEmailUser() ) return false; static $cachedEmailToken = null; - if(!is_null($cachedEmailToken)) + if ( !is_null( $cachedEmailToken ) ) return $cachedEmailToken; $cachedEmailToken = $wgUser->editToken(); return $cachedEmailToken; } - - public static function getImportToken($pageid, $title) + + public static function getImportToken( $pageid, $title ) { global $wgUser; - if(!$wgUser->isAllowed('import')) + if ( !$wgUser->isAllowed( 'import' ) ) return false; static $cachedImportToken = null; - if(!is_null($cachedImportToken)) + if ( !is_null( $cachedImportToken ) ) return $cachedImportToken; $cachedImportToken = $wgUser->editToken(); @@ -192,13 +193,15 @@ class ApiQueryInfo extends ApiQueryBase { public function execute() { $this->params = $this->extractRequestParams(); - if(!is_null($this->params['prop'])) { - $prop = array_flip($this->params['prop']); - $this->fld_protection = isset($prop['protection']); - $this->fld_talkid = isset($prop['talkid']); - $this->fld_subjectid = isset($prop['subjectid']); - $this->fld_url = isset($prop['url']); - $this->fld_readable = isset($prop['readable']); + if ( !is_null( $this->params['prop'] ) ) { + $prop = array_flip( $this->params['prop'] ); + $this->fld_protection = isset( $prop['protection'] ); + $this->fld_watched = isset( $prop['watched'] ); + $this->fld_talkid = isset( $prop['talkid'] ); + $this->fld_subjectid = isset( $prop['subjectid'] ); + $this->fld_url = isset( $prop['url'] ); + $this->fld_readable = isset( $prop['readable'] ); + $this->fld_preload = isset ( $prop['preload'] ); } $pageSet = $this->getPageSet(); @@ -207,54 +210,57 @@ class ApiQueryInfo extends ApiQueryBase { $this->everything = $this->titles + $this->missing; $result = $this->getResult(); - uasort($this->everything, array('Title', 'compare')); - if(!is_null($this->params['continue'])) + uasort( $this->everything, array( 'Title', 'compare' ) ); + if ( !is_null( $this->params['continue'] ) ) { // Throw away any titles we're gonna skip so they don't // clutter queries - $cont = explode('|', $this->params['continue']); - if(count($cont) != 2) - $this->dieUsage("Invalid continue param. You should pass the original " . - "value returned by the previous query", "_badcontinue"); - $conttitle = Title::makeTitleSafe($cont[0], $cont[1]); - foreach($this->everything as $pageid => $title) + $cont = explode( '|', $this->params['continue'] ); + if ( count( $cont ) != 2 ) + $this->dieUsage( "Invalid continue param. You should pass the original " . + "value returned by the previous query", "_badcontinue" ); + $conttitle = Title::makeTitleSafe( $cont[0], $cont[1] ); + foreach ( $this->everything as $pageid => $title ) { - if(Title::compare($title, $conttitle) >= 0) + if ( Title::compare( $title, $conttitle ) >= 0 ) break; - unset($this->titles[$pageid]); - unset($this->missing[$pageid]); - unset($this->everything[$pageid]); + unset( $this->titles[$pageid] ); + unset( $this->missing[$pageid] ); + unset( $this->everything[$pageid] ); } } - $this->pageRestrictions = $pageSet->getCustomField('page_restrictions'); - $this->pageIsRedir = $pageSet->getCustomField('page_is_redirect'); - $this->pageIsNew = $pageSet->getCustomField('page_is_new'); - $this->pageCounter = $pageSet->getCustomField('page_counter'); - $this->pageTouched = $pageSet->getCustomField('page_touched'); - $this->pageLatest = $pageSet->getCustomField('page_latest'); - $this->pageLength = $pageSet->getCustomField('page_len'); + $this->pageRestrictions = $pageSet->getCustomField( 'page_restrictions' ); + $this->pageIsRedir = $pageSet->getCustomField( 'page_is_redirect' ); + $this->pageIsNew = $pageSet->getCustomField( 'page_is_new' ); + $this->pageCounter = $pageSet->getCustomField( 'page_counter' ); + $this->pageTouched = $pageSet->getCustomField( 'page_touched' ); + $this->pageLatest = $pageSet->getCustomField( 'page_latest' ); + $this->pageLength = $pageSet->getCustomField( 'page_len' ); $db = $this->getDB(); // Get protection info if requested - if ($this->fld_protection) + if ( $this->fld_protection ) $this->getProtectionInfo(); + if ( $this->fld_watched ) + $this->getWatchedInfo(); + // Run the talkid/subjectid query if requested - if($this->fld_talkid || $this->fld_subjectid) + if ( $this->fld_talkid || $this->fld_subjectid ) $this->getTSIDs(); - foreach($this->everything as $pageid => $title) { - $pageInfo = $this->extractPageInfo($pageid, $title); - $fit = $result->addValue(array ( + foreach ( $this->everything as $pageid => $title ) { + $pageInfo = $this->extractPageInfo( $pageid, $title ); + $fit = $result->addValue( array ( 'query', 'pages' - ), $pageid, $pageInfo); - if(!$fit) + ), $pageid, $pageInfo ); + if ( !$fit ) { - $this->setContinueEnumParameter('continue', + $this->setContinueEnumParameter( 'continue', $title->getNamespace() . '|' . - $title->getText()); + $title->getText() ); break; } } @@ -266,52 +272,67 @@ class ApiQueryInfo extends ApiQueryBase { * @param $title Title object * @return array */ - private function extractPageInfo($pageid, $title) + private function extractPageInfo( $pageid, $title ) { $pageInfo = array(); - if($title->exists()) + if ( $title->exists() ) { - $pageInfo['touched'] = wfTimestamp(TS_ISO_8601, $this->pageTouched[$pageid]); - $pageInfo['lastrevid'] = intval($this->pageLatest[$pageid]); - $pageInfo['counter'] = intval($this->pageCounter[$pageid]); - $pageInfo['length'] = intval($this->pageLength[$pageid]); - if ($this->pageIsRedir[$pageid]) + $pageInfo['touched'] = wfTimestamp( TS_ISO_8601, $this->pageTouched[$pageid] ); + $pageInfo['lastrevid'] = intval( $this->pageLatest[$pageid] ); + $pageInfo['counter'] = intval( $this->pageCounter[$pageid] ); + $pageInfo['length'] = intval( $this->pageLength[$pageid] ); + if ( $this->pageIsRedir[$pageid] ) $pageInfo['redirect'] = ''; - if ($this->pageIsNew[$pageid]) + if ( $this->pageIsNew[$pageid] ) $pageInfo['new'] = ''; } - if (!is_null($this->params['token'])) { + if ( !is_null( $this->params['token'] ) ) { $tokenFunctions = $this->getTokenFunctions(); - $pageInfo['starttimestamp'] = wfTimestamp(TS_ISO_8601, time()); - foreach($this->params['token'] as $t) + $pageInfo['starttimestamp'] = wfTimestamp( TS_ISO_8601, time() ); + foreach ( $this->params['token'] as $t ) { - $val = call_user_func($tokenFunctions[$t], $pageid, $title); - if($val === false) - $this->setWarning("Action '$t' is not allowed for the current user"); + $val = call_user_func( $tokenFunctions[$t], $pageid, $title ); + if ( $val === false ) + $this->setWarning( "Action '$t' is not allowed for the current user" ); else $pageInfo[$t . 'token'] = $val; } } - if($this->fld_protection) { + if ( $this->fld_protection ) { $pageInfo['protection'] = array(); - if (isset($this->protections[$title->getNamespace()][$title->getDBkey()])) + if ( isset( $this->protections[$title->getNamespace()][$title->getDBkey()] ) ) $pageInfo['protection'] = $this->protections[$title->getNamespace()][$title->getDBkey()]; - $this->getResult()->setIndexedTagName($pageInfo['protection'], 'pr'); + $this->getResult()->setIndexedTagName( $pageInfo['protection'], 'pr' ); } - if($this->fld_talkid && isset($this->talkids[$title->getNamespace()][$title->getDBKey()])) - $pageInfo['talkid'] = $this->talkids[$title->getNamespace()][$title->getDBKey()]; - if($this->fld_subjectid && isset($this->subjectids[$title->getNamespace()][$title->getDBKey()])) - $pageInfo['subjectid'] = $this->subjectids[$title->getNamespace()][$title->getDBKey()]; - if($this->fld_url) { + + if ( $this->fld_watched && isset( $this->watched[$title->getNamespace()][$title->getDBkey()] ) ) + $pageInfo['watched'] = ''; + + if ( $this->fld_talkid && isset( $this->talkids[$title->getNamespace()][$title->getDBkey()] ) ) + $pageInfo['talkid'] = $this->talkids[$title->getNamespace()][$title->getDBkey()]; + + if ( $this->fld_subjectid && isset( $this->subjectids[$title->getNamespace()][$title->getDBkey()] ) ) + $pageInfo['subjectid'] = $this->subjectids[$title->getNamespace()][$title->getDBkey()]; + + if ( $this->fld_url ) { $pageInfo['fullurl'] = $title->getFullURL(); - $pageInfo['editurl'] = $title->getFullURL('action=edit'); + $pageInfo['editurl'] = $title->getFullURL( 'action=edit' ); + } + if ( $this->fld_readable && $title->userCanRead() ) + $pageInfo['readable'] = ''; + + if ( $this->fld_preload ) { + if ( $title->exists() ) + $pageInfo['preload'] = ''; + else { + wfRunHooks( 'EditFormPreloadText', array( &$text, &$title ) ); + + $pageInfo['preload'] = $text; + } } - if($this->fld_readable) - if($title->userCanRead()) - $pageInfo['readable'] = ''; return $pageInfo; } @@ -324,36 +345,37 @@ class ApiQueryInfo extends ApiQueryBase { $db = $this->getDB(); // Get normal protections for existing titles - if(count($this->titles)) + if ( count( $this->titles ) ) { - $this->addTables(array('page_restrictions', 'page')); - $this->addWhere('page_id=pr_page'); - $this->addFields(array('pr_page', 'pr_type', 'pr_level', + $this->resetQueryParams(); + $this->addTables( array( 'page_restrictions', 'page' ) ); + $this->addWhere( 'page_id=pr_page' ); + $this->addFields( array( 'pr_page', 'pr_type', 'pr_level', 'pr_expiry', 'pr_cascade', 'page_namespace', - 'page_title')); - $this->addWhereFld('pr_page', array_keys($this->titles)); + 'page_title' ) ); + $this->addWhereFld( 'pr_page', array_keys( $this->titles ) ); - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { + $res = $this->select( __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { $a = array( 'type' => $row->pr_type, 'level' => $row->pr_level, - 'expiry' => Block::decodeExpiry($row->pr_expiry, TS_ISO_8601) + 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ) ); - if($row->pr_cascade) + 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])); - foreach($restrictions as $restrict) { - $temp = explode('=', trim($restrict)); - if(count($temp) == 1) { + // Also check old restrictions + if ( $this->pageRestrictions[$row->pr_page] ) { + $restrictions = explode( ':', trim( $this->pageRestrictions[$row->pr_page] ) ); + foreach ( $restrictions as $restrict ) { + $temp = explode( '=', trim( $restrict ) ); + if ( count( $temp ) == 1 ) { // old old format should be treated as edit/move restriction - $restriction = trim($temp[0]); + $restriction = trim( $temp[0] ); - if($restriction == '') + if ( $restriction == '' ) continue; $this->protections[$row->page_namespace][$row->page_title][] = array( 'type' => 'edit', @@ -366,8 +388,8 @@ class ApiQueryInfo extends ApiQueryBase { 'expiry' => 'infinity', ); } else { - $restriction = trim($temp[1]); - if($restriction == '') + $restriction = trim( $temp[1] ); + if ( $restriction == '' ) continue; $this->protections[$row->page_namespace][$row->page_title][] = array( 'type' => $temp[0], @@ -378,84 +400,84 @@ class ApiQueryInfo extends ApiQueryBase { } } } - $db->freeResult($res); + $db->freeResult( $res ); } // Get protections for missing titles - if(count($this->missing)) + if ( count( $this->missing ) ) { $this->resetQueryParams(); - $lb = new LinkBatch($this->missing); - $this->addTables('protected_titles'); - $this->addFields(array('pt_title', 'pt_namespace', 'pt_create_perm', 'pt_expiry')); - $this->addWhere($lb->constructSet('pt', $db)); - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { + $lb = new LinkBatch( $this->missing ); + $this->addTables( 'protected_titles' ); + $this->addFields( array( 'pt_title', 'pt_namespace', 'pt_create_perm', 'pt_expiry' ) ); + $this->addWhere( $lb->constructSet( 'pt', $db ) ); + $res = $this->select( __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { $this->protections[$row->pt_namespace][$row->pt_title][] = array( 'type' => 'create', 'level' => $row->pt_create_perm, - 'expiry' => Block::decodeExpiry($row->pt_expiry, TS_ISO_8601) + 'expiry' => Block::decodeExpiry( $row->pt_expiry, TS_ISO_8601 ) ); } - $db->freeResult($res); + $db->freeResult( $res ); } // Cascading protections $images = $others = array(); - foreach ($this->everything as $title) - if ($title->getNamespace() == NS_FILE) - $images[] = $title->getDBKey(); + foreach ( $this->everything as $title ) + if ( $title->getNamespace() == NS_FILE ) + $images[] = $title->getDBkey(); else $others[] = $title; - if (count($others)) { + if ( count( $others ) ) { // Non-images: check templatelinks - $lb = new LinkBatch($others); + $lb = new LinkBatch( $others ); $this->resetQueryParams(); - $this->addTables(array('page_restrictions', 'page', 'templatelinks')); - $this->addFields(array('pr_type', 'pr_level', 'pr_expiry', + $this->addTables( array( 'page_restrictions', 'page', 'templatelinks' ) ); + $this->addFields( array( 'pr_type', 'pr_level', 'pr_expiry', 'page_title', 'page_namespace', - 'tl_title', 'tl_namespace')); - $this->addWhere($lb->constructSet('tl', $db)); - $this->addWhere('pr_page = page_id'); - $this->addWhere('pr_page = tl_from'); - $this->addWhereFld('pr_cascade', 1); - - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { - $source = Title::makeTitle($row->page_namespace, $row->page_title); + 'tl_title', 'tl_namespace' ) ); + $this->addWhere( $lb->constructSet( 'tl', $db ) ); + $this->addWhere( 'pr_page = page_id' ); + $this->addWhere( 'pr_page = tl_from' ); + $this->addWhereFld( 'pr_cascade', 1 ); + + $res = $this->select( __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { + $source = Title::makeTitle( $row->page_namespace, $row->page_title ); $this->protections[$row->tl_namespace][$row->tl_title][] = array( 'type' => $row->pr_type, 'level' => $row->pr_level, - 'expiry' => Block::decodeExpiry($row->pr_expiry, TS_ISO_8601), + 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ), 'source' => $source->getPrefixedText() ); } - $db->freeResult($res); + $db->freeResult( $res ); } - if (count($images)) { + if ( count( $images ) ) { // Images: check imagelinks $this->resetQueryParams(); - $this->addTables(array('page_restrictions', 'page', 'imagelinks')); - $this->addFields(array('pr_type', 'pr_level', 'pr_expiry', - 'page_title', 'page_namespace', 'il_to')); - $this->addWhere('pr_page = page_id'); - $this->addWhere('pr_page = il_from'); - $this->addWhereFld('pr_cascade', 1); - $this->addWhereFld('il_to', $images); - - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { - $source = Title::makeTitle($row->page_namespace, $row->page_title); + $this->addTables( array( 'page_restrictions', 'page', 'imagelinks' ) ); + $this->addFields( array( 'pr_type', 'pr_level', 'pr_expiry', + 'page_title', 'page_namespace', 'il_to' ) ); + $this->addWhere( 'pr_page = page_id' ); + $this->addWhere( 'pr_page = il_from' ); + $this->addWhereFld( 'pr_cascade', 1 ); + $this->addWhereFld( 'il_to', $images ); + + $res = $this->select( __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { + $source = Title::makeTitle( $row->page_namespace, $row->page_title ); $this->protections[NS_FILE][$row->il_to][] = array( 'type' => $row->pr_type, 'level' => $row->pr_level, - 'expiry' => Block::decodeExpiry($row->pr_expiry, TS_ISO_8601), + 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ), 'source' => $source->getPrefixedText() ); } - $db->freeResult($res); + $db->freeResult( $res ); } } @@ -467,35 +489,67 @@ class ApiQueryInfo extends ApiQueryBase { { $getTitles = $this->talkids = $this->subjectids = array(); $db = $this->getDB(); - foreach($this->everything as $t) + foreach ( $this->everything as $t ) { - if(MWNamespace::isTalk($t->getNamespace())) + if ( MWNamespace::isTalk( $t->getNamespace() ) ) { - if($this->fld_subjectid) + if ( $this->fld_subjectid ) $getTitles[] = $t->getSubjectPage(); } - else if($this->fld_talkid) + else if ( $this->fld_talkid ) $getTitles[] = $t->getTalkPage(); } - if(!count($getTitles)) + if ( !count( $getTitles ) ) return; - + // Construct a custom WHERE clause that matches // all titles in $getTitles - $lb = new LinkBatch($getTitles); + $lb = new LinkBatch( $getTitles ); $this->resetQueryParams(); - $this->addTables('page'); - $this->addFields(array('page_title', 'page_namespace', 'page_id')); - $this->addWhere($lb->constructSet('page', $db)); - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) + $this->addTables( 'page' ); + $this->addFields( array( 'page_title', 'page_namespace', 'page_id' ) ); + $this->addWhere( $lb->constructSet( 'page', $db ) ); + $res = $this->select( __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { - if(MWNamespace::isTalk($row->page_namespace)) - $this->talkids[MWNamespace::getSubject($row->page_namespace)][$row->page_title] = - intval($row->page_id); + if ( MWNamespace::isTalk( $row->page_namespace ) ) + $this->talkids[MWNamespace::getSubject( $row->page_namespace )][$row->page_title] = + intval( $row->page_id ); else - $this->subjectids[MWNamespace::getTalk($row->page_namespace)][$row->page_title] = - intval($row->page_id); + $this->subjectids[MWNamespace::getTalk( $row->page_namespace )][$row->page_title] = + intval( $row->page_id ); + } + } + + /** + * Get information about watched status and put it in $this->watched + */ + private function getWatchedInfo() + { + global $wgUser; + + if ( $wgUser->isAnon() || count( $this->titles ) == 0 ) + return; + + $this->watched = array(); + $db = $this->getDB(); + + $lb = new LinkBatch( $this->titles ); + + $this->resetQueryParams(); + $this->addTables( array( 'page', 'watchlist' ) ); + $this->addFields( array( 'page_title', 'page_namespace' ) ); + $this->addWhere( array( + $lb->constructSet( 'page', $db ), + 'wl_namespace=page_namespace', + 'wl_title=page_title', + 'wl_user' => $wgUser->getID() + ) ); + + $res = $this->select( __METHOD__ ); + + while ( $row = $db->fetchObject( $res ) ) { + $this->watched[$row->page_namespace][$row->page_title] = true; } } @@ -505,6 +559,7 @@ class ApiQueryInfo extends ApiQueryBase { 'talkid', 'subjectid', 'url', + 'preload', ); if ( !is_null( $params['prop'] ) ) { foreach ( $params['prop'] as $prop ) { @@ -522,21 +577,23 @@ class ApiQueryInfo extends ApiQueryBase { public function getAllowedParams() { return array ( 'prop' => array ( - ApiBase :: PARAM_DFLT => NULL, + ApiBase :: PARAM_DFLT => null, ApiBase :: PARAM_ISMULTI => true, ApiBase :: PARAM_TYPE => array ( 'protection', 'talkid', + 'watched', # private 'subjectid', 'url', 'readable', # private + 'preload' // If you add more properties here, please consider whether they // need to be added to getCacheMode() - )), + ) ), 'token' => array ( - ApiBase :: PARAM_DFLT => NULL, + ApiBase :: PARAM_DFLT => null, ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()) + ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ) ), 'continue' => null, ); @@ -548,7 +605,11 @@ class ApiQueryInfo extends ApiQueryBase { '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', - ' subjectid - The page ID of the parent page for each 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' ), 'token' => 'Request a token to perform a data-modifying action on a page', 'continue' => 'When more results are available, use this to continue', @@ -558,6 +619,12 @@ class ApiQueryInfo extends ApiQueryBase { public function getDescription() { return 'Get basic page information such as namespace, title, last touched date, ...'; } + + 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' ), + ) ); + } protected function getExamples() { return array ( @@ -567,6 +634,6 @@ class ApiQueryInfo extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryInfo.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryInfo.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryLangLinks.php b/includes/api/ApiQueryLangLinks.php index 35f7e67c..9330e380 100644 --- a/includes/api/ApiQueryLangLinks.php +++ b/includes/api/ApiQueryLangLinks.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryLangLinks extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'll'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'll' ); } public function execute() { @@ -44,52 +44,53 @@ class ApiQueryLangLinks extends ApiQueryBase { return; $params = $this->extractRequestParams(); - $this->addFields(array ( + $this->addFields( array ( 'll_from', 'll_lang', 'll_title' - )); + ) ); - $this->addTables('langlinks'); - $this->addWhereFld('ll_from', array_keys($this->getPageSet()->getGoodTitles())); - if(!is_null($params['continue'])) { - $cont = explode('|', $params['continue']); - if(count($cont) != 2) - $this->dieUsage("Invalid continue param. You should pass the " . - "original value returned by the previous query", "_badcontinue"); - $llfrom = intval($cont[0]); - $lllang = $this->getDB()->strencode($cont[1]); - $this->addWhere("ll_from > $llfrom OR ". - "(ll_from = $llfrom AND ". - "ll_lang >= '$lllang')"); + $this->addTables( 'langlinks' ); + $this->addWhereFld( 'll_from', array_keys( $this->getPageSet()->getGoodTitles() ) ); + if ( !is_null( $params['continue'] ) ) { + $cont = explode( '|', $params['continue'] ); + if ( count( $cont ) != 2 ) + $this->dieUsage( "Invalid continue param. You should pass the " . + "original value returned by the previous query", "_badcontinue" ); + $llfrom = intval( $cont[0] ); + $lllang = $this->getDB()->strencode( $cont[1] ); + $this->addWhere( "ll_from > $llfrom OR " . + "(ll_from = $llfrom AND " . + "ll_lang >= '$lllang')" ); } - # Don't order by ll_from if it's constant in the WHERE clause - if(count($this->getPageSet()->getGoodTitles()) == 1) - $this->addOption('ORDER BY', 'll_lang'); + + // Don't order by ll_from if it's constant in the WHERE clause + if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) + $this->addOption( 'ORDER BY', 'll_lang' ); else - $this->addOption('ORDER BY', 'll_from, ll_lang'); - $this->addOption('LIMIT', $params['limit'] + 1); - $res = $this->select(__METHOD__); + $this->addOption( 'ORDER BY', 'll_from, ll_lang' ); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + $res = $this->select( __METHOD__ ); $count = 0; $db = $this->getDB(); - while ($row = $db->fetchObject($res)) { - if (++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', "{$row->ll_from}|{$row->ll_lang}"); + $this->setContinueEnumParameter( 'continue', "{$row->ll_from}|{$row->ll_lang}" ); break; } - $entry = array('lang' => $row->ll_lang); - ApiResult :: setContent($entry, $row->ll_title); - $fit = $this->addPageSubItem($row->ll_from, $entry); - if(!$fit) + $entry = array( 'lang' => $row->ll_lang ); + ApiResult :: setContent( $entry, $row->ll_title ); + $fit = $this->addPageSubItem( $row->ll_from, $entry ); + if ( !$fit ) { - $this->setContinueEnumParameter('continue', "{$row->ll_from}|{$row->ll_lang}"); + $this->setContinueEnumParameter( 'continue', "{$row->ll_from}|{$row->ll_lang}" ); break; } } - $db->freeResult($res); + $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -119,6 +120,12 @@ class ApiQueryLangLinks extends ApiQueryBase { public function getDescription() { return 'Returns all interlanguage links from the given page(s)'; } + + 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' ), + ) ); + } protected function getExamples() { return array ( @@ -128,6 +135,6 @@ class ApiQueryLangLinks extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryLangLinks.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryLangLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryLinks.php b/includes/api/ApiQueryLinks.php index 94b7980c..52dfd591 100644 --- a/includes/api/ApiQueryLinks.php +++ b/includes/api/ApiQueryLinks.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiQueryBase.php"); + require_once ( "ApiQueryBase.php" ); } /** @@ -40,9 +40,9 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { private $table, $prefix, $description; - public function __construct($query, $moduleName) { + public function __construct( $query, $moduleName ) { - switch ($moduleName) { + switch ( $moduleName ) { case self::LINKS : $this->table = 'pagelinks'; $this->prefix = 'pl'; @@ -54,10 +54,10 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { $this->description = 'template'; break; default : - ApiBase :: dieDebug(__METHOD__, 'Unknown module name'); + ApiBase :: dieDebug( __METHOD__, 'Unknown module name' ); } - parent :: __construct($query, $moduleName, $this->prefix); + parent :: __construct( $query, $moduleName, $this->prefix ); } public function execute() { @@ -68,101 +68,101 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { return 'public'; } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { - if ($this->getPageSet()->getGoodTitleCount() == 0) + if ( $this->getPageSet()->getGoodTitleCount() == 0 ) return; // nothing to do $params = $this->extractRequestParams(); - $this->addFields(array ( + $this->addFields( array ( $this->prefix . '_from AS pl_from', $this->prefix . '_namespace AS pl_namespace', $this->prefix . '_title AS pl_title' - )); - - $this->addTables($this->table); - $this->addWhereFld($this->prefix . '_from', array_keys($this->getPageSet()->getGoodTitles())); - $this->addWhereFld($this->prefix . '_namespace', $params['namespace']); - - if(!is_null($params['continue'])) { - $cont = explode('|', $params['continue']); - if(count($cont) != 3) - $this->dieUsage("Invalid continue param. You should pass the " . - "original value returned by the previous query", "_badcontinue"); - $plfrom = intval($cont[0]); - $plns = intval($cont[1]); - $pltitle = $this->getDB()->strencode($this->titleToKey($cont[2])); - $this->addWhere("{$this->prefix}_from > $plfrom OR ". - "({$this->prefix}_from = $plfrom AND ". - "({$this->prefix}_namespace > $plns OR ". - "({$this->prefix}_namespace = $plns AND ". - "{$this->prefix}_title >= '$pltitle')))"); + ) ); + + $this->addTables( $this->table ); + $this->addWhereFld( $this->prefix . '_from', array_keys( $this->getPageSet()->getGoodTitles() ) ); + $this->addWhereFld( $this->prefix . '_namespace', $params['namespace'] ); + + if ( !is_null( $params['continue'] ) ) { + $cont = explode( '|', $params['continue'] ); + if ( count( $cont ) != 3 ) + $this->dieUsage( "Invalid continue param. You should pass the " . + "original value returned by the previous query", "_badcontinue" ); + $plfrom = intval( $cont[0] ); + $plns = intval( $cont[1] ); + $pltitle = $this->getDB()->strencode( $this->titleToKey( $cont[2] ) ); + $this->addWhere( "{$this->prefix}_from > $plfrom OR " . + "({$this->prefix}_from = $plfrom AND " . + "({$this->prefix}_namespace > $plns OR " . + "({$this->prefix}_namespace = $plns AND " . + "{$this->prefix}_title >= '$pltitle')))" ); } - # Here's some MySQL craziness going on: if you use WHERE foo='bar' - # and later ORDER BY foo MySQL doesn't notice the ORDER BY is pointless - # but instead goes and filesorts, because the index for foo was used - # already. To work around this, we drop constant fields in the WHERE - # clause from the ORDER BY clause + // Here's some MySQL craziness going on: if you use WHERE foo='bar' + // and later ORDER BY foo MySQL doesn't notice the ORDER BY is pointless + // but instead goes and filesorts, because the index for foo was used + // already. To work around this, we drop constant fields in the WHERE + // clause from the ORDER BY clause $order = array(); - if(count($this->getPageSet()->getGoodTitles()) != 1) + if ( count( $this->getPageSet()->getGoodTitles() ) != 1 ) $order[] = "{$this->prefix}_from"; - if(count($params['namespace']) != 1) + if ( count( $params['namespace'] ) != 1 ) $order[] = "{$this->prefix}_namespace"; + $order[] = "{$this->prefix}_title"; - $this->addOption('ORDER BY', implode(", ", $order)); - $this->addOption('USE INDEX', "{$this->prefix}_from"); - $this->addOption('LIMIT', $params['limit'] + 1); + $this->addOption( 'ORDER BY', implode( ", ", $order ) ); + $this->addOption( 'USE INDEX', "{$this->prefix}_from" ); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); $db = $this->getDB(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $count = 0; - while ($row = $db->fetchObject($res)) { - if(++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', + $this->setContinueEnumParameter( 'continue', "{$row->pl_from}|{$row->pl_namespace}|" . - $this->keyToTitle($row->pl_title)); + $this->keyToTitle( $row->pl_title ) ); break; } $vals = array(); - ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->pl_namespace, $row->pl_title)); - $fit = $this->addPageSubItem($row->pl_from, $vals); - if(!$fit) + ApiQueryBase :: addTitleInfo( $vals, Title :: makeTitle( $row->pl_namespace, $row->pl_title ) ); + $fit = $this->addPageSubItem( $row->pl_from, $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('continue', + $this->setContinueEnumParameter( 'continue', "{$row->pl_from}|{$row->pl_namespace}|" . - $this->keyToTitle($row->pl_title)); + $this->keyToTitle( $row->pl_title ) ); break; } } } else { - $titles = array(); $count = 0; - while ($row = $db->fetchObject($res)) { - if(++$count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', + $this->setContinueEnumParameter( 'continue', "{$row->pl_from}|{$row->pl_namespace}|" . - $this->keyToTitle($row->pl_title)); + $this->keyToTitle( $row->pl_title ) ); break; } - $titles[] = Title :: makeTitle($row->pl_namespace, $row->pl_title); + $titles[] = Title :: makeTitle( $row->pl_namespace, $row->pl_title ); } - $resultPageSet->populateFromTitles($titles); + $resultPageSet->populateFromTitles( $titles ); } - $db->freeResult($res); + $db->freeResult( $res ); } public function getAllowedParams() @@ -208,6 +208,6 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryLinks.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php index 7afed844..bdeee952 100644 --- a/includes/api/ApiQueryLogEvents.php +++ b/includes/api/ApiQueryLogEvents.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,136 +35,153 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryLogEvents extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'le'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'le' ); } public function execute() { $params = $this->extractRequestParams(); $db = $this->getDB(); - - $prop = $params['prop']; - $this->fld_ids = in_array('ids', $prop); - $this->fld_title = in_array('title', $prop); - $this->fld_type = in_array('type', $prop); - $this->fld_user = in_array('user', $prop); - $this->fld_timestamp = in_array('timestamp', $prop); - $this->fld_comment = in_array('comment', $prop); - $this->fld_details = in_array('details', $prop); - - list($tbl_logging, $tbl_page, $tbl_user) = $db->tableNamesN('logging', 'page', 'user'); - - $hideLogs = LogEventsList::getExcludeClause($db); - if($hideLogs !== false) - $this->addWhere($hideLogs); + + $prop = array_flip( $params['prop'] ); + + $this->fld_ids = isset( $prop['ids'] ); + $this->fld_title = isset( $prop['title'] ); + $this->fld_type = isset( $prop['type'] ); + $this->fld_user = isset( $prop['user'] ); + $this->fld_timestamp = isset( $prop['timestamp'] ); + $this->fld_comment = isset( $prop['comment'] ); + $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); + $this->fld_details = isset( $prop['details'] ); + $this->fld_tags = isset( $prop['tags'] ); + + list( $tbl_logging, $tbl_page, $tbl_user ) = $db->tableNamesN( 'logging', 'page', 'user' ); + + $hideLogs = LogEventsList::getExcludeClause( $db ); + if ( $hideLogs !== false ) + $this->addWhere( $hideLogs ); // Order is significant here - $this->addTables(array('logging', 'user', 'page')); - $this->addOption('STRAIGHT_JOIN'); - $this->addJoinConds(array( - 'user' => array('JOIN', - 'user_id=log_user'), - 'page' => array('LEFT JOIN', + $this->addTables( array( 'logging', 'user', 'page' ) ); + $this->addOption( 'STRAIGHT_JOIN' ); + $this->addJoinConds( array( + 'user' => array( 'JOIN', + 'user_id=log_user' ), + 'page' => array( 'LEFT JOIN', array( 'log_namespace=page_namespace', - 'log_title=page_title')))); - $index = 'times'; // default, may change + 'log_title=page_title' ) ) ) ); + $index = array( 'logging' => 'times' ); // default, may change - $this->addFields(array ( + $this->addFields( array ( 'log_type', 'log_action', 'log_timestamp', 'log_deleted', - )); - - $this->addFieldsIf('log_id', $this->fld_ids); - $this->addFieldsIf('page_id', $this->fld_ids); - $this->addFieldsIf('log_user', $this->fld_user); - $this->addFieldsIf('user_name', $this->fld_user); - $this->addFieldsIf('log_namespace', $this->fld_title); - $this->addFieldsIf('log_title', $this->fld_title); - $this->addFieldsIf('log_comment', $this->fld_comment); - $this->addFieldsIf('log_params', $this->fld_details); + ) ); + + $this->addFieldsIf( 'log_id', $this->fld_ids ); + $this->addFieldsIf( 'page_id', $this->fld_ids ); + $this->addFieldsIf( 'log_user', $this->fld_user ); + $this->addFieldsIf( 'user_name', $this->fld_user ); + $this->addFieldsIf( 'log_namespace', $this->fld_title ); + $this->addFieldsIf( 'log_title', $this->fld_title ); + $this->addFieldsIf( 'log_comment', $this->fld_comment || $this->fld_parsedcomment ); + $this->addFieldsIf( 'log_params', $this->fld_details ); + + if ( $this->fld_tags ) { + $this->addTables( 'tag_summary' ); + $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', 'log_id=ts_log_id' ) ) ); + $this->addFields( 'ts_tags' ); + } + + if ( !is_null( $params['tag'] ) ) { + $this->addTables( 'change_tag' ); + $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'log_id=ct_log_id' ) ) ) ); + $this->addWhereFld( 'ct_tag', $params['tag'] ); + global $wgOldChangeTagsIndex; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; + } - if( !is_null($params['type']) ) { - $this->addWhereFld('log_type', $params['type']); - $index = 'type_time'; + if ( !is_null( $params['type'] ) ) { + $this->addWhereFld( 'log_type', $params['type'] ); + $index['logging'] = 'type_time'; } - $this->addWhereRange('log_timestamp', $params['dir'], $params['start'], $params['end']); + $this->addWhereRange( 'log_timestamp', $params['dir'], $params['start'], $params['end'] ); $limit = $params['limit']; - $this->addOption('LIMIT', $limit +1); + $this->addOption( 'LIMIT', $limit + 1 ); $user = $params['user']; - if (!is_null($user)) { - $userid = User::idFromName($user); - if (!$userid) - $this->dieUsage("User name $user not found", 'param_user'); - $this->addWhereFld('log_user', $userid); - $index = 'user_time'; + if ( !is_null( $user ) ) { + $userid = User::idFromName( $user ); + if ( !$userid ) + $this->dieUsage( "User name $user not found", 'param_user' ); + $this->addWhereFld( 'log_user', $userid ); + $index['logging'] = 'user_time'; } $title = $params['title']; - if (!is_null($title)) { - $titleObj = Title :: newFromText($title); - if (is_null($titleObj)) - $this->dieUsage("Bad title value '$title'", 'param_title'); - $this->addWhereFld('log_namespace', $titleObj->getNamespace()); - $this->addWhereFld('log_title', $titleObj->getDBkey()); + if ( !is_null( $title ) ) { + $titleObj = Title :: newFromText( $title ); + if ( is_null( $titleObj ) ) + $this->dieUsage( "Bad title value '$title'", 'param_title' ); + $this->addWhereFld( 'log_namespace', $titleObj->getNamespace() ); + $this->addWhereFld( 'log_title', $titleObj->getDBkey() ); // Use the title index in preference to the user index if there is a conflict - $index = is_null($user) ? 'page_time' : array('page_time','user_time'); + $index['logging'] = is_null( $user ) ? 'page_time' : array( 'page_time', 'user_time' ); } - $this->addOption( 'USE INDEX', array( 'logging' => $index ) ); + $this->addOption( 'USE INDEX', $index ); // Paranoia: avoid brute force searches (bug 17342) - if (!is_null($title)) { - $this->addWhere('log_deleted & ' . LogPage::DELETED_ACTION . ' = 0'); + if ( !is_null( $title ) ) { + $this->addWhere( $db->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0' ); } - if (!is_null($user)) { - $this->addWhere('log_deleted & ' . LogPage::DELETED_USER . ' = 0'); + if ( !is_null( $user ) ) { + $this->addWhere( $db->bitAnd( 'log_deleted', LogPage::DELETED_USER ) . ' = 0' ); } $count = 0; - $res = $this->select(__METHOD__); - while ($row = $db->fetchObject($res)) { - if (++ $count > $limit) { + $res = $this->select( __METHOD__ ); + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->log_timestamp ) ); break; } - $vals = $this->extractRowInfo($row); - if(!$vals) + $vals = $this->extractRowInfo( $row ); + if ( !$vals ) continue; - $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) + $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->log_timestamp ) ); break; } } - $db->freeResult($res); + $db->freeResult( $res ); - $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item'); + $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' ); } - public static function addLogParams($result, &$vals, $params, $type, $ts) { - $params = explode("\n", $params); - switch ($type) { + public static function addLogParams( $result, &$vals, $params, $type, $ts ) { + $params = explode( "\n", $params ); + switch ( $type ) { case 'move': - if (isset ($params[0])) { - $title = Title :: newFromText($params[0]); - if ($title) { + if ( isset ( $params[0] ) ) { + $title = Title :: newFromText( $params[0] ); + if ( $title ) { $vals2 = array(); - ApiQueryBase :: addTitleInfo($vals2, $title, "new_"); + ApiQueryBase :: addTitleInfo( $vals2, $title, "new_" ); $vals[$type] = $vals2; } } - if (isset ($params[1]) && $params[1]) { + if ( isset ( $params[1] ) && $params[1] ) { $vals[$type]['suppressedredirect'] = ''; - } + } $params = null; break; case 'patrol': @@ -182,77 +199,99 @@ class ApiQueryLogEvents extends ApiQueryBase { case 'block': $vals2 = array(); list( $vals2['duration'], $vals2['flags'] ) = $params; - $vals2['expiry'] = wfTimestamp(TS_ISO_8601, - strtotime($params[0], wfTimestamp(TS_UNIX, $ts))); + $vals2['expiry'] = wfTimestamp( TS_ISO_8601, + strtotime( $params[0], wfTimestamp( TS_UNIX, $ts ) ) ); $vals[$type] = $vals2; $params = null; break; } - if (!is_null($params)) { - $result->setIndexedTagName($params, 'param'); - $vals = array_merge($vals, $params); + if ( !is_null( $params ) ) { + $result->setIndexedTagName( $params, 'param' ); + $vals = array_merge( $vals, $params ); } return $vals; } - private function extractRowInfo($row) { + private function extractRowInfo( $row ) { $vals = array(); - if ($this->fld_ids) { - $vals['logid'] = intval($row->log_id); - $vals['pageid'] = intval($row->page_id); + if ( $this->fld_ids ) { + $vals['logid'] = intval( $row->log_id ); + $vals['pageid'] = intval( $row->page_id ); } - if ($this->fld_title) { - if (LogEventsList::isDeleted($row, LogPage::DELETED_ACTION)) { + $title = Title::makeTitle( $row->log_namespace, $row->log_title ); + + if ( $this->fld_title ) { + if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) { $vals['actionhidden'] = ''; } else { - $title = Title :: makeTitle($row->log_namespace, $row->log_title); - ApiQueryBase :: addTitleInfo($vals, $title); + ApiQueryBase::addTitleInfo( $vals, $title ); } } - if ($this->fld_type) { + if ( $this->fld_type ) { $vals['type'] = $row->log_type; $vals['action'] = $row->log_action; } - if ($this->fld_details && $row->log_params !== '') { - if (LogEventsList::isDeleted($row, LogPage::DELETED_ACTION)) { + if ( $this->fld_details && $row->log_params !== '' ) { + if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) { $vals['actionhidden'] = ''; } else { - self::addLogParams($this->getResult(), $vals, + self::addLogParams( $this->getResult(), $vals, $row->log_params, $row->log_type, - $row->log_timestamp); + $row->log_timestamp ); } } - if ($this->fld_user) { - if (LogEventsList::isDeleted($row, LogPage::DELETED_USER)) { + if ( $this->fld_user ) { + if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) { $vals['userhidden'] = ''; } else { $vals['user'] = $row->user_name; - if(!$row->log_user) + if ( !$row->log_user ) $vals['anon'] = ''; } } - if ($this->fld_timestamp) { - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->log_timestamp); + if ( $this->fld_timestamp ) { + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->log_timestamp ); } - if ($this->fld_comment && isset($row->log_comment)) { - if (LogEventsList::isDeleted($row, LogPage::DELETED_COMMENT)) { + + if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->log_comment ) ) { + if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) { $vals['commenthidden'] = ''; } else { - $vals['comment'] = $row->log_comment; + if ( $this->fld_comment ) + $vals['comment'] = $row->log_comment; + + if ( $this->fld_parsedcomment ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->log_comment, $title ); + } } } + if ( $this->fld_tags ) { + if ( $row->ts_tags ) { + $tags = explode( ',', $row->ts_tags ); + $this->getResult()->setIndexedTagName( $tags, 'tag' ); + $vals['tags'] = $tags; + } else { + $vals['tags'] = array(); + } + } + return $vals; } - - + public function getCacheMode( $params ) { - return 'public'; + if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { + // formatComment() calls wfMsg() among other things + return 'anon-public-user-private'; + } else { + return 'public'; + } } public function getAllowedParams() { @@ -268,7 +307,9 @@ class ApiQueryLogEvents extends ApiQueryBase { 'user', 'timestamp', 'comment', + 'parsedcomment', 'details', + 'tags' ) ), 'type' => array ( @@ -289,6 +330,7 @@ class ApiQueryLogEvents extends ApiQueryBase { ), 'user' => null, 'title' => null, + 'tag' => null, 'limit' => array ( ApiBase :: PARAM_DFLT => 10, ApiBase :: PARAM_TYPE => 'limit', @@ -308,13 +350,21 @@ class ApiQueryLogEvents extends ApiQueryBase { 'dir' => 'In which direction to enumerate.', 'user' => 'Filter entries to those made by the given user.', 'title' => 'Filter entries to those related to a page.', - 'limit' => 'How many total event entries to return.' + 'limit' => 'How many total event entries to return.', + 'tag' => 'Only list event entries tagged with this tag.', ); } public function getDescription() { return 'Get events from logs.'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'param_user', 'info' => 'User name $user not found' ), + array( 'code' => 'param_title', 'info' => 'Bad title value \'title\'' ), + ) ); + } protected function getExamples() { return array ( @@ -323,6 +373,6 @@ class ApiQueryLogEvents extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryLogEvents.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryLogEvents.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryProtectedTitles.php b/includes/api/ApiQueryProtectedTitles.php index 67a2a829..ab794805 100644 --- a/includes/api/ApiQueryProtectedTitles.php +++ b/includes/api/ApiQueryProtectedTitles.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,91 +35,104 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'pt'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'pt' ); } public function execute() { $this->run(); } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { $db = $this->getDB(); $params = $this->extractRequestParams(); - $this->addTables('protected_titles'); - $this->addFields(array('pt_namespace', 'pt_title', 'pt_timestamp')); + $this->addTables( 'protected_titles' ); + $this->addFields( array( 'pt_namespace', 'pt_title', 'pt_timestamp' ) ); - $prop = array_flip($params['prop']); - $this->addFieldsIf('pt_user', isset($prop['user'])); - $this->addFieldsIf('pt_reason', isset($prop['comment'])); - $this->addFieldsIf('pt_expiry', isset($prop['expiry'])); - $this->addFieldsIf('pt_create_perm', isset($prop['level'])); + $prop = array_flip( $params['prop'] ); + $this->addFieldsIf( 'pt_user', isset( $prop['user'] ) ); + $this->addFieldsIf( 'pt_reason', isset( $prop['comment'] ) || isset( $prop['parsedcomment'] ) ); + $this->addFieldsIf( 'pt_expiry', isset( $prop['expiry'] ) ); + $this->addFieldsIf( 'pt_create_perm', isset( $prop['level'] ) ); - $this->addWhereRange('pt_timestamp', $params['dir'], $params['start'], $params['end']); - $this->addWhereFld('pt_namespace', $params['namespace']); - $this->addWhereFld('pt_create_perm', $params['level']); + $this->addWhereRange( 'pt_timestamp', $params['dir'], $params['start'], $params['end'] ); + $this->addWhereFld( 'pt_namespace', $params['namespace'] ); + $this->addWhereFld( 'pt_create_perm', $params['level'] ); - if(isset($prop['user'])) + if ( isset( $prop['user'] ) ) { - $this->addTables('user'); - $this->addFields('user_name'); - $this->addJoinConds(array('user' => array('LEFT JOIN', + $this->addTables( 'user' ); + $this->addFields( 'user_name' ); + $this->addJoinConds( array( 'user' => array( 'LEFT JOIN', 'user_id=pt_user' - ))); + ) ) ); } - $this->addOption('LIMIT', $params['limit'] + 1); - $res = $this->select(__METHOD__); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + $res = $this->select( __METHOD__ ); $count = 0; $result = $this->getResult(); - while ($row = $db->fetchObject($res)) { - if (++ $count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->pt_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->pt_timestamp ) ); break; } - $title = Title::makeTitle($row->pt_namespace, $row->pt_title); - if (is_null($resultPageSet)) { + $title = Title::makeTitle( $row->pt_namespace, $row->pt_title ); + if ( is_null( $resultPageSet ) ) { $vals = array(); - ApiQueryBase::addTitleInfo($vals, $title); - if(isset($prop['timestamp'])) - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->pt_timestamp); - if(isset($prop['user']) && !is_null($row->user_name)) + ApiQueryBase::addTitleInfo( $vals, $title ); + if ( isset( $prop['timestamp'] ) ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->pt_timestamp ); + + if ( isset( $prop['user'] ) && !is_null( $row->user_name ) ) $vals['user'] = $row->user_name; - if(isset($prop['comment'])) + + if ( isset( $prop['comment'] ) ) $vals['comment'] = $row->pt_reason; - if(isset($prop['expiry'])) - $vals['expiry'] = Block::decodeExpiry($row->pt_expiry, TS_ISO_8601); - if(isset($prop['level'])) + + if ( isset( $prop['parsedcomment'] ) ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->pt_reason, $title ); + } + + if ( isset( $prop['expiry'] ) ) + $vals['expiry'] = Block::decodeExpiry( $row->pt_expiry, TS_ISO_8601 ); + + if ( isset( $prop['level'] ) ) $vals['level'] = $row->pt_create_perm; - $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) - { - $this->setContinueEnumParameter('start', - wfTimestamp(TS_ISO_8601, $row->pt_timestamp)); + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'start', + wfTimestamp( TS_ISO_8601, $row->pt_timestamp ) ); break; } } else { $titles[] = $title; } } - $db->freeResult($res); - if(is_null($resultPageSet)) - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), $this->getModulePrefix()); + $db->freeResult( $res ); + if ( is_null( $resultPageSet ) ) + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), $this->getModulePrefix() ); else - $resultPageSet->populateFromTitles($titles); + $resultPageSet->populateFromTitles( $titles ); } public function getCacheMode( $params ) { - return 'public'; + if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { + // formatComment() calls wfMsg() among other things + return 'anon-public-user-private'; + } else { + return 'public'; + } } public function getAllowedParams() { @@ -131,7 +144,7 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { ), 'level' => array( ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array_diff($wgRestrictionLevels, array('')) + ApiBase :: PARAM_TYPE => array_diff( $wgRestrictionLevels, array( '' ) ) ), 'limit' => array ( ApiBase :: PARAM_DFLT => 10, @@ -160,6 +173,7 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { 'timestamp', 'user', 'comment', + 'parsedcomment', 'expiry', 'level' ) @@ -190,6 +204,6 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryProtectedTitles.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryProtectedTitles.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryRandom.php b/includes/api/ApiQueryRandom.php index 1811b7e8..10796810 100644 --- a/includes/api/ApiQueryRandom.php +++ b/includes/api/ApiQueryRandom.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -36,88 +36,88 @@ if (!defined('MEDIAWIKI')) { class ApiQueryRandom extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'rn'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'rn' ); } public function execute() { $this->run(); } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - protected function prepareQuery($randstr, $limit, $namespace, &$resultPageSet, $redirect) { + protected function prepareQuery( $randstr, $limit, $namespace, &$resultPageSet, $redirect ) { $this->resetQueryParams(); - $this->addTables('page'); - $this->addOption('LIMIT', $limit); - $this->addWhereFld('page_namespace', $namespace); - $this->addWhereRange('page_random', 'newer', $randstr, null); - $this->addWhereFld('page_is_redirect', $redirect); - $this->addOption('USE INDEX', 'page_random'); - if(is_null($resultPageSet)) - $this->addFields(array('page_id', 'page_title', 'page_namespace')); + $this->addTables( 'page' ); + $this->addOption( 'LIMIT', $limit ); + $this->addWhereFld( 'page_namespace', $namespace ); + $this->addWhereRange( 'page_random', 'newer', $randstr, null ); + $this->addWhereFld( 'page_is_redirect', $redirect ); + $this->addOption( 'USE INDEX', 'page_random' ); + if ( is_null( $resultPageSet ) ) + $this->addFields( array( 'page_id', 'page_title', 'page_namespace' ) ); else - $this->addFields($resultPageSet->getPageTableFields()); + $this->addFields( $resultPageSet->getPageTableFields() ); } - protected function runQuery(&$resultPageSet) { + protected function runQuery( &$resultPageSet ) { $db = $this->getDB(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); $count = 0; - while($row = $db->fetchObject($res)) { + while ( $row = $db->fetchObject( $res ) ) { $count++; - if(is_null($resultPageSet)) + if ( is_null( $resultPageSet ) ) { // Prevent duplicates - if(!in_array($row->page_id, $this->pageIDs)) + if ( !in_array( $row->page_id, $this->pageIDs ) ) { $fit = $this->getResult()->addValue( - array('query', $this->getModuleName()), - null, $this->extractRowInfo($row)); - if(!$fit) - # We can't really query-continue a random list. - # Return an insanely high value so - # $count < $limit is false + array( 'query', $this->getModuleName() ), + null, $this->extractRowInfo( $row ) ); + if ( !$fit ) + // We can't really query-continue a random list. + // Return an insanely high value so + // $count < $limit is false return 1E9; $this->pageIDs[] = $row->page_id; } } else - $resultPageSet->processDbRow($row); + $resultPageSet->processDbRow( $row ); } - $db->freeResult($res); + $db->freeResult( $res ); return $count; } - public function run($resultPageSet = null) { + public function run( $resultPageSet = null ) { $params = $this->extractRequestParams(); $result = $this->getResult(); $this->pageIDs = array(); - $this->prepareQuery(wfRandom(), $params['limit'], $params['namespace'], $resultPageSet, $params['redirect']); - $count = $this->runQuery($resultPageSet); - if($count < $params['limit']) + $this->prepareQuery( wfRandom(), $params['limit'], $params['namespace'], $resultPageSet, $params['redirect'] ); + $count = $this->runQuery( $resultPageSet ); + if ( $count < $params['limit'] ) { /* We got too few pages, we probably picked a high value * for page_random. We'll just take the lowest ones, see * also the comment in Title::getRandomTitle() */ - $this->prepareQuery(0, $params['limit'] - $count, $params['namespace'], $resultPageSet, $params['redirect']); - $this->runQuery($resultPageSet); + $this->prepareQuery( 0, $params['limit'] - $count, $params['namespace'], $resultPageSet, $params['redirect'] ); + $this->runQuery( $resultPageSet ); } - if(is_null($resultPageSet)) { - $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'page'); + if ( is_null( $resultPageSet ) ) { + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'page' ); } } - private function extractRowInfo($row) { - $title = Title::makeTitle($row->page_namespace, $row->page_title); + private function extractRowInfo( $row ) { + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); $vals = array(); - $vals['id'] = intval($row->page_id); - ApiQueryBase::addTitleInfo($vals, $title); + $vals['id'] = intval( $row->page_id ); + ApiQueryBase::addTitleInfo( $vals, $title ); return $vals; } diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php index b5a56864..1f0de3be 100644 --- a/includes/api/ApiQueryRecentChanges.php +++ b/includes/api/ApiQueryRecentChanges.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -36,49 +36,70 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryRecentChanges extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'rc'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'rc' ); } - private $fld_comment = false, $fld_user = false, $fld_flags = false, + private $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_flags = false, $fld_timestamp = false, $fld_title = false, $fld_ids = false, $fld_sizes = false; - + /** + * Get an array mapping token names to their handler functions. + * The prototype for a token function is func($pageid, $title, $rc) + * it should return a token or false (permission denied) + * @return array(tokenname => function) + */ protected function getTokenFunctions() { - // tokenname => function - // function prototype is func($pageid, $title, $rev) - // should return token or false - // Don't call the hooks twice - if(isset($this->tokenFunctions)) + if ( isset( $this->tokenFunctions ) ) return $this->tokenFunctions; // If we're in JSON callback mode, no tokens can be obtained - if(!is_null($this->getMain()->getRequest()->getVal('callback'))) + if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) return array(); $this->tokenFunctions = array( 'patrol' => array( 'ApiQueryRecentChanges', 'getPatrolToken' ) ); - wfRunHooks('APIQueryRecentChangesTokens', array(&$this->tokenFunctions)); + wfRunHooks( 'APIQueryRecentChangesTokens', array( &$this->tokenFunctions ) ); return $this->tokenFunctions; } - public static function getPatrolToken($pageid, $title, $rc) + public static function getPatrolToken( $pageid, $title, $rc ) { global $wgUser; - if(!$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) + if ( !$wgUser->useRCPatrol() && ( !$wgUser->useNPPatrol() || + $rc->getAttribute( 'rc_type' ) != RC_NEW ) ) return false; // The patrol token is always the same, let's exploit that static $cachedPatrolToken = null; - if(!is_null($cachedPatrolToken)) + if ( !is_null( $cachedPatrolToken ) ) return $cachedPatrolToken; $cachedPatrolToken = $wgUser->editToken(); return $cachedPatrolToken; } + /** + * Sets internal state to include the desired properties in the output. + * @param $prop associative array of properties, only keys are used here + */ + public function initProperties( $prop ) { + $this->fld_comment = isset ( $prop['comment'] ); + $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); + $this->fld_user = isset ( $prop['user'] ); + $this->fld_flags = isset ( $prop['flags'] ); + $this->fld_timestamp = isset ( $prop['timestamp'] ); + $this->fld_title = isset ( $prop['title'] ); + $this->fld_ids = isset ( $prop['ids'] ); + $this->fld_sizes = isset ( $prop['sizes'] ); + $this->fld_redirect = isset( $prop['redirect'] ); + $this->fld_patrolled = isset( $prop['patrolled'] ); + $this->fld_loginfo = isset( $prop['loginfo'] ); + $this->fld_tags = isset( $prop['tags'] ); + } + /** * Generates and outputs the result of this query based upon the provided parameters. */ @@ -92,49 +113,65 @@ class ApiQueryRecentChanges extends ApiQueryBase { * AND rc_deleted = '0' */ $db = $this->getDB(); - $this->addTables('recentchanges'); - $this->addOption('USE INDEX', array('recentchanges' => 'rc_timestamp')); - $this->addWhereRange('rc_timestamp', $params['dir'], $params['start'], $params['end']); - $this->addWhereFld('rc_namespace', $params['namespace']); - $this->addWhereFld('rc_deleted', 0); + $this->addTables( 'recentchanges' ); + $index = array( 'recentchanges' => 'rc_timestamp' ); // May change + $this->addWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] ); + $this->addWhereFld( 'rc_namespace', $params['namespace'] ); + $this->addWhereFld( 'rc_deleted', 0 ); - if(!is_null($params['type'])) - $this->addWhereFld('rc_type', $this->parseRCType($params['type'])); + if ( !is_null( $params['type'] ) ) + $this->addWhereFld( 'rc_type', $this->parseRCType( $params['type'] ) ); - if (!is_null($params['show'])) { - $show = array_flip($params['show']); + if ( !is_null( $params['show'] ) ) { + $show = array_flip( $params['show'] ); /* Check for conflicting parameters. */ - if ((isset ($show['minor']) && isset ($show['!minor'])) - || (isset ($show['bot']) && isset ($show['!bot'])) - || (isset ($show['anon']) && isset ($show['!anon'])) - || (isset ($show['redirect']) && isset ($show['!redirect'])) - || (isset ($show['patrolled']) && isset ($show['!patrolled']))) { + if ( ( isset ( $show['minor'] ) && isset ( $show['!minor'] ) ) + || ( isset ( $show['bot'] ) && isset ( $show['!bot'] ) ) + || ( isset ( $show['anon'] ) && isset ( $show['!anon'] ) ) + || ( isset ( $show['redirect'] ) && isset ( $show['!redirect'] ) ) + || ( isset ( $show['patrolled'] ) && isset ( $show['!patrolled'] ) ) ) { - $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show'); + $this->dieUsageMsg( array( 'show' ) ); } // Check permissions global $wgUser; - if((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) - $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied'); + if ( ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol() ) + $this->dieUsage( "You need the patrol right to request the patrolled flag", 'permissiondenied' ); /* Add additional conditions to query depending upon parameters. */ - $this->addWhereIf('rc_minor = 0', isset ($show['!minor'])); - $this->addWhereIf('rc_minor != 0', isset ($show['minor'])); - $this->addWhereIf('rc_bot = 0', isset ($show['!bot'])); - $this->addWhereIf('rc_bot != 0', isset ($show['bot'])); - $this->addWhereIf('rc_user = 0', isset ($show['anon'])); - $this->addWhereIf('rc_user != 0', isset ($show['!anon'])); - $this->addWhereIf('rc_patrolled = 0', isset($show['!patrolled'])); - $this->addWhereIf('rc_patrolled != 0', isset($show['patrolled'])); - $this->addWhereIf('page_is_redirect = 1', isset ($show['redirect'])); + $this->addWhereIf( 'rc_minor = 0', isset ( $show['!minor'] ) ); + $this->addWhereIf( 'rc_minor != 0', isset ( $show['minor'] ) ); + $this->addWhereIf( 'rc_bot = 0', isset ( $show['!bot'] ) ); + $this->addWhereIf( 'rc_bot != 0', isset ( $show['bot'] ) ); + $this->addWhereIf( 'rc_user = 0', isset ( $show['anon'] ) ); + $this->addWhereIf( 'rc_user != 0', isset ( $show['!anon'] ) ); + $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) ); + $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) ); + $this->addWhereIf( 'page_is_redirect = 1', isset ( $show['redirect'] ) ); + // Don't throw log entries out the window here - $this->addWhereIf('page_is_redirect = 0 OR page_is_redirect IS NULL', isset ($show['!redirect'])); + $this->addWhereIf( 'page_is_redirect = 0 OR page_is_redirect IS NULL', isset ( $show['!redirect'] ) ); } - - /* Add the fields we're concerned with to out query. */ - $this->addFields(array ( + + if ( !is_null( $params['user'] ) && !is_null( $param['excludeuser'] ) ) + $this->dieUsage( 'user and excludeuser cannot be used together', 'user-excludeuser' ); + + if ( !is_null( $params['user'] ) ) + { + $this->addWhereFld( 'rc_user_text', $params['user'] ); + $index['recentchanges'] = 'rc_user_text'; + } + + if ( !is_null( $params['excludeuser'] ) ) + // We don't use the rc_user_text index here because + // * it would require us to sort by rc_user_text before rc_timestamp + // * the != condition doesn't throw out too many rows anyway + $this->addWhere( 'rc_user_text != ' . $this->getDB()->addQuotes( $params['excludeuser'] ) ); + + /* Add the fields we're concerned with to our query. */ + $this->addFields( array ( 'rc_timestamp', 'rc_namespace', 'rc_title', @@ -142,86 +179,93 @@ class ApiQueryRecentChanges extends ApiQueryBase { 'rc_type', 'rc_moved_to_ns', 'rc_moved_to_title' - )); + ) ); /* Determine what properties we need to display. */ - if (!is_null($params['prop'])) { - $prop = array_flip($params['prop']); + if ( !is_null( $params['prop'] ) ) { + $prop = array_flip( $params['prop'] ); /* Set up internal members based upon params. */ - $this->fld_comment = isset ($prop['comment']); - $this->fld_user = isset ($prop['user']); - $this->fld_flags = isset ($prop['flags']); - $this->fld_timestamp = isset ($prop['timestamp']); - $this->fld_title = isset ($prop['title']); - $this->fld_ids = isset ($prop['ids']); - $this->fld_sizes = isset ($prop['sizes']); - $this->fld_redirect = isset($prop['redirect']); - $this->fld_patrolled = isset($prop['patrolled']); - $this->fld_loginfo = isset($prop['loginfo']); + $this->initProperties( $prop ); global $wgUser; - if($this->fld_patrolled && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) - $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied'); + if ( $this->fld_patrolled && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol() ) + $this->dieUsage( "You need the patrol right to request the patrolled flag", 'permissiondenied' ); /* Add fields to our query if they are specified as a needed parameter. */ - $this->addFieldsIf('rc_id', $this->fld_ids); - $this->addFieldsIf('rc_this_oldid', $this->fld_ids); - $this->addFieldsIf('rc_last_oldid', $this->fld_ids); - $this->addFieldsIf('rc_comment', $this->fld_comment); - $this->addFieldsIf('rc_user', $this->fld_user); - $this->addFieldsIf('rc_user_text', $this->fld_user); - $this->addFieldsIf('rc_minor', $this->fld_flags); - $this->addFieldsIf('rc_bot', $this->fld_flags); - $this->addFieldsIf('rc_new', $this->fld_flags); - $this->addFieldsIf('rc_old_len', $this->fld_sizes); - $this->addFieldsIf('rc_new_len', $this->fld_sizes); - $this->addFieldsIf('rc_patrolled', $this->fld_patrolled); - $this->addFieldsIf('rc_logid', $this->fld_loginfo); - $this->addFieldsIf('rc_log_type', $this->fld_loginfo); - $this->addFieldsIf('rc_log_action', $this->fld_loginfo); - $this->addFieldsIf('rc_params', $this->fld_loginfo); - if($this->fld_redirect || isset($show['redirect']) || isset($show['!redirect'])) + $this->addFieldsIf( 'rc_id', $this->fld_ids ); + $this->addFieldsIf( 'rc_this_oldid', $this->fld_ids ); + $this->addFieldsIf( 'rc_last_oldid', $this->fld_ids ); + $this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment ); + $this->addFieldsIf( 'rc_user', $this->fld_user ); + $this->addFieldsIf( 'rc_user_text', $this->fld_user ); + $this->addFieldsIf( 'rc_minor', $this->fld_flags ); + $this->addFieldsIf( 'rc_bot', $this->fld_flags ); + $this->addFieldsIf( 'rc_new', $this->fld_flags ); + $this->addFieldsIf( 'rc_old_len', $this->fld_sizes ); + $this->addFieldsIf( 'rc_new_len', $this->fld_sizes ); + $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled ); + $this->addFieldsIf( 'rc_logid', $this->fld_loginfo ); + $this->addFieldsIf( 'rc_log_type', $this->fld_loginfo ); + $this->addFieldsIf( 'rc_log_action', $this->fld_loginfo ); + $this->addFieldsIf( 'rc_params', $this->fld_loginfo ); + if ( $this->fld_redirect || isset( $show['redirect'] ) || isset( $show['!redirect'] ) ) { - $this->addTables('page'); - $this->addJoinConds(array('page' => array('LEFT JOIN', array('rc_namespace=page_namespace', 'rc_title=page_title')))); - $this->addFields('page_is_redirect'); + $this->addTables( 'page' ); + $this->addJoinConds( array( 'page' => array( 'LEFT JOIN', array( 'rc_namespace=page_namespace', 'rc_title=page_title' ) ) ) ); + $this->addFields( 'page_is_redirect' ); } } + + if ( $this->fld_tags ) { + $this->addTables( 'tag_summary' ); + $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', array( 'rc_id=ts_rc_id' ) ) ) ); + $this->addFields( 'ts_tags' ); + } + + if ( !is_null( $params['tag'] ) ) { + $this->addTables( 'change_tag' ); + $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'rc_id=ct_rc_id' ) ) ) ); + $this->addWhereFld( 'ct_tag' , $params['tag'] ); + global $wgOldChangeTagsIndex; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; + } + $this->token = $params['token']; - $this->addOption('LIMIT', $params['limit'] +1); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + $this->addOption( 'USE INDEX', $index ); $count = 0; /* Perform the actual query. */ $db = $this->getDB(); - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); /* Iterate through the rows, adding data extracted from them to our query result. */ - while ($row = $db->fetchObject($res)) { - if (++ $count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); break; } /* Extract the data from a single row. */ - $vals = $this->extractRowInfo($row); + $vals = $this->extractRowInfo( $row ); /* Add that row's data to our final output. */ - if(!$vals) + if ( !$vals ) continue; - $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) + $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); break; } } - $db->freeResult($res); + $db->freeResult( $res ); /* Format the result */ - $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'rc'); + $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'rc' ); } /** @@ -229,16 +273,16 @@ class ApiQueryRecentChanges extends ApiQueryBase { * * @param $row The row from which to extract the data. * @return An array mapping strings (descriptors) to their respective string values. - * @access private + * @access public */ - private function extractRowInfo($row) { + public function extractRowInfo( $row ) { /* If page was moved somewhere, get the title of the move target. */ $movedToTitle = false; - if (isset($row->rc_moved_to_title) && $row->rc_moved_to_title !== '') - $movedToTitle = Title :: makeTitle($row->rc_moved_to_ns, $row->rc_moved_to_title); + if ( isset( $row->rc_moved_to_title ) && $row->rc_moved_to_title !== '' ) + $movedToTitle = Title :: makeTitle( $row->rc_moved_to_ns, $row->rc_moved_to_title ); /* Determine the title of the page that has been changed. */ - $title = Title :: makeTitle($row->rc_namespace, $row->rc_title); + $title = Title :: makeTitle( $row->rc_namespace, $row->rc_title ); /* Our output data. */ $vals = array (); @@ -247,87 +291,112 @@ class ApiQueryRecentChanges extends ApiQueryBase { /* Determine what kind of change this was. */ switch ( $type ) { - case RC_EDIT: $vals['type'] = 'edit'; break; - case RC_NEW: $vals['type'] = 'new'; break; - case RC_MOVE: $vals['type'] = 'move'; break; - case RC_LOG: $vals['type'] = 'log'; break; - case RC_MOVE_OVER_REDIRECT: $vals['type'] = 'move over redirect'; break; - default: $vals['type'] = $type; + case RC_EDIT: + $vals['type'] = 'edit'; + break; + case RC_NEW: + $vals['type'] = 'new'; + break; + case RC_MOVE: + $vals['type'] = 'move'; + break; + case RC_LOG: + $vals['type'] = 'log'; + break; + case RC_MOVE_OVER_REDIRECT: + $vals['type'] = 'move over redirect'; + break; + default: + $vals['type'] = $type; } /* Create a new entry in the result for the title. */ - if ($this->fld_title) { - ApiQueryBase :: addTitleInfo($vals, $title); - if ($movedToTitle) - ApiQueryBase :: addTitleInfo($vals, $movedToTitle, "new_"); + if ( $this->fld_title ) { + ApiQueryBase :: addTitleInfo( $vals, $title ); + if ( $movedToTitle ) + ApiQueryBase :: addTitleInfo( $vals, $movedToTitle, "new_" ); } /* Add ids, such as rcid, pageid, revid, and oldid to the change's info. */ - if ($this->fld_ids) { - $vals['rcid'] = intval($row->rc_id); - $vals['pageid'] = intval($row->rc_cur_id); - $vals['revid'] = intval($row->rc_this_oldid); + if ( $this->fld_ids ) { + $vals['rcid'] = intval( $row->rc_id ); + $vals['pageid'] = intval( $row->rc_cur_id ); + $vals['revid'] = intval( $row->rc_this_oldid ); $vals['old_revid'] = intval( $row->rc_last_oldid ); } /* Add user data and 'anon' flag, if use is anonymous. */ - if ($this->fld_user) { + if ( $this->fld_user ) { $vals['user'] = $row->rc_user_text; - if(!$row->rc_user) + if ( !$row->rc_user ) $vals['anon'] = ''; } /* Add flags, such as new, minor, bot. */ - if ($this->fld_flags) { - if ($row->rc_bot) + if ( $this->fld_flags ) { + if ( $row->rc_bot ) $vals['bot'] = ''; - if ($row->rc_new) + if ( $row->rc_new ) $vals['new'] = ''; - if ($row->rc_minor) + if ( $row->rc_minor ) $vals['minor'] = ''; } /* Add sizes of each revision. (Only available on 1.10+) */ - if ($this->fld_sizes) { - $vals['oldlen'] = intval($row->rc_old_len); - $vals['newlen'] = intval($row->rc_new_len); + if ( $this->fld_sizes ) { + $vals['oldlen'] = intval( $row->rc_old_len ); + $vals['newlen'] = intval( $row->rc_new_len ); } /* Add the timestamp. */ - if ($this->fld_timestamp) - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp); + if ( $this->fld_timestamp ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp ); /* Add edit summary / log summary. */ - if ($this->fld_comment && isset($row->rc_comment)) { + if ( $this->fld_comment && isset( $row->rc_comment ) ) $vals['comment'] = $row->rc_comment; + + if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->rc_comment, $title ); } - if ($this->fld_redirect) - if($row->page_is_redirect) + if ( $this->fld_redirect ) + if ( $row->page_is_redirect ) $vals['redirect'] = ''; /* Add the patrolled flag */ - if ($this->fld_patrolled && $row->rc_patrolled == 1) + if ( $this->fld_patrolled && $row->rc_patrolled == 1 ) $vals['patrolled'] = ''; - if ($this->fld_loginfo && $row->rc_type == RC_LOG) { - $vals['logid'] = intval($row->rc_logid); + if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) { + $vals['logid'] = intval( $row->rc_logid ); $vals['logtype'] = $row->rc_log_type; $vals['logaction'] = $row->rc_log_action; - ApiQueryLogEvents::addLogParams($this->getResult(), + ApiQueryLogEvents::addLogParams( $this->getResult(), $vals, $row->rc_params, - $row->rc_log_type, $row->rc_timestamp); + $row->rc_log_type, $row->rc_timestamp ); } - if(!is_null($this->token)) + if ( $this->fld_tags ) { + if ( $row->ts_tags ) { + $tags = explode( ',', $row->ts_tags ); + $this->getResult()->setIndexedTagName( $tags, 'tag' ); + $vals['tags'] = $tags; + } else { + $vals['tags'] = array(); + } + } + + if ( !is_null( $this->token ) ) { $tokenFunctions = $this->getTokenFunctions(); - foreach($this->token as $t) + foreach ( $this->token as $t ) { - $val = call_user_func($tokenFunctions[$t], $row->rc_cur_id, - $title, RecentChange::newFromRow($row)); - if($val === false) - $this->setWarning("Action '$t' is not allowed for the current user"); + $val = call_user_func( $tokenFunctions[$t], $row->rc_cur_id, + $title, RecentChange::newFromRow( $row ) ); + if ( $val === false ) + $this->setWarning( "Action '$t' is not allowed for the current user" ); else $vals[$t . 'token'] = $val; } @@ -336,16 +405,16 @@ class ApiQueryRecentChanges extends ApiQueryBase { return $vals; } - private function parseRCType($type) + private function parseRCType( $type ) { - if(is_array($type)) + if ( is_array( $type ) ) { $retval = array(); - foreach($type as $t) - $retval[] = $this->parseRCType($t); + foreach ( $type as $t ) + $retval[] = $this->parseRCType( $t ); return $retval; } - switch($type) + switch( $type ) { case 'edit': return RC_EDIT; case 'new': return RC_NEW; @@ -364,6 +433,10 @@ class ApiQueryRecentChanges extends ApiQueryBase { if ( isset( $params['token'] ) ) { return 'private'; } + if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { + // formatComment() calls wfMsg() among other things + return 'anon-public-user-private'; + } return 'public'; } @@ -386,12 +459,20 @@ class ApiQueryRecentChanges extends ApiQueryBase { ApiBase :: PARAM_ISMULTI => true, ApiBase :: PARAM_TYPE => 'namespace' ), + 'user' => array( + ApiBase :: PARAM_TYPE => 'user' + ), + 'excludeuser' => array( + ApiBase :: PARAM_TYPE => 'user' + ), + 'tag' => null, 'prop' => array ( ApiBase :: PARAM_ISMULTI => true, ApiBase :: PARAM_DFLT => 'title|timestamp|ids', ApiBase :: PARAM_TYPE => array ( 'user', 'comment', + 'parsedcomment', 'flags', 'timestamp', 'title', @@ -400,10 +481,11 @@ class ApiQueryRecentChanges extends ApiQueryBase { 'redirect', 'patrolled', 'loginfo', + 'tags' ) ), 'token' => array( - ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()), + ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ), ApiBase :: PARAM_ISMULTI => true ), 'show' => array ( @@ -445,6 +527,8 @@ class ApiQueryRecentChanges extends ApiQueryBase { 'end' => 'The timestamp to end enumerating.', 'dir' => 'In which direction to enumerate.', 'namespace' => 'Filter log entries to only this namespace(s)', + 'user' => 'Only list changes by this user', + 'excludeuser' => 'Don\'t list changes by this user', 'prop' => 'Include additional pieces of information', 'token' => 'Which tokens to obtain for each change', 'show' => array ( @@ -452,13 +536,22 @@ class ApiQueryRecentChanges extends ApiQueryBase { 'For example, to see only minor edits done by logged-in users, set show=minor|!anon' ), 'type' => 'Which types of changes to show.', - 'limit' => 'How many total changes to return.' + 'limit' => 'How many total changes to return.', + 'tag' => 'Only list changes tagged with this tag.', ); } public function getDescription() { return 'Enumerate recent changes'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'show' ), + array( 'code' => 'permissiondenied', 'info' => 'You need the patrol right to request the patrolled flag' ), + array( 'code' => 'user-excludeuser', 'info' => 'user and excludeuser cannot be used together' ), + ) ); + } protected function getExamples() { return array ( @@ -467,6 +560,6 @@ class ApiQueryRecentChanges extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php index eba526a3..6166b6a2 100644 --- a/includes/api/ApiQueryRevisions.php +++ b/includes/api/ApiQueryRevisions.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -37,12 +37,12 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryRevisions extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'rv'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'rv' ); } private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false, - $fld_comment = false, $fld_user = false, $fld_content = false; + $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_content = false, $fld_tags = false; protected function getTokenFunctions() { // tokenname => function @@ -50,40 +50,40 @@ class ApiQueryRevisions extends ApiQueryBase { // should return token or false // Don't call the hooks twice - if(isset($this->tokenFunctions)) + if ( isset( $this->tokenFunctions ) ) return $this->tokenFunctions; // If we're in JSON callback mode, no tokens can be obtained - if(!is_null($this->getMain()->getRequest()->getVal('callback'))) + if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) return array(); $this->tokenFunctions = array( 'rollback' => array( 'ApiQueryRevisions', 'getRollbackToken' ) ); - wfRunHooks('APIQueryRevisionsTokens', array(&$this->tokenFunctions)); + wfRunHooks( 'APIQueryRevisionsTokens', array( &$this->tokenFunctions ) ); return $this->tokenFunctions; } - public static function getRollbackToken($pageid, $title, $rev) + public static function getRollbackToken( $pageid, $title, $rev ) { global $wgUser; - if(!$wgUser->isAllowed('rollback')) + if ( !$wgUser->isAllowed( 'rollback' ) ) return false; - return $wgUser->editToken(array($title->getPrefixedText(), - $rev->getUserText())); + return $wgUser->editToken( array( $title->getPrefixedText(), + $rev->getUserText() ) ); } public function execute() { - $params = $this->extractRequestParams(false); + $params = $this->extractRequestParams( false ); // If any of those parameters are used, work in 'enumeration' mode. // Enum mode can only be used when exactly one page is provided. // Enumerating revisions on multiple pages make it extremely // difficult to manage continuations and require additional SQL indexes - $enumRevMode = (!is_null($params['user']) || !is_null($params['excludeuser']) || - !is_null($params['limit']) || !is_null($params['startid']) || - !is_null($params['endid']) || $params['dir'] === 'newer' || - !is_null($params['start']) || !is_null($params['end'])); + $enumRevMode = ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) || + !is_null( $params['limit'] ) || !is_null( $params['startid'] ) || + !is_null( $params['endid'] ) || $params['dir'] === 'newer' || + !is_null( $params['start'] ) || !is_null( $params['end'] ) ); $pageSet = $this->getPageSet(); @@ -91,77 +91,99 @@ class ApiQueryRevisions extends ApiQueryBase { $revCount = $pageSet->getRevisionCount(); // Optimization -- nothing to do - if ($revCount === 0 && $pageCount === 0) + if ( $revCount === 0 && $pageCount === 0 ) return; - if ($revCount > 0 && $enumRevMode) - $this->dieUsage('The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).', 'revids'); + if ( $revCount > 0 && $enumRevMode ) + $this->dieUsage( 'The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).', 'revids' ); - if ($pageCount > 1 && $enumRevMode) - $this->dieUsage('titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start and end parameters may only be used on a single page.', 'multpages'); + if ( $pageCount > 1 && $enumRevMode ) + $this->dieUsage( 'titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start and end parameters may only be used on a single page.', 'multpages' ); - if (!is_null($params['diffto'])) { - if ($params['diffto'] == 'cur') + $this->diffto = $this->difftotext = null; + if ( !is_null( $params['difftotext'] ) ) { + $this->difftotext = $params['difftotext']; + } else if ( !is_null( $params['diffto'] ) ) { + if ( $params['diffto'] == 'cur' ) $params['diffto'] = 0; - if ((!ctype_digit($params['diffto']) || $params['diffto'] < 0) - && $params['diffto'] != 'prev' && $params['diffto'] != 'next') - $this->dieUsage('rvdiffto must be set to a non-negative number, "prev", "next" or "cur"', 'diffto'); + if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 ) + && $params['diffto'] != 'prev' && $params['diffto'] != 'next' ) + $this->dieUsage( 'rvdiffto must be set to a non-negative number, "prev", "next" or "cur"', 'diffto' ); // Check whether the revision exists and is readable, // DifferenceEngine returns a rather ambiguous empty // string if that's not the case - if ($params['diffto'] != 0) { - $difftoRev = Revision::newFromID($params['diffto']); - if (!$difftoRev) - $this->dieUsageMsg(array('nosuchrevid', $params['diffto'])); - if (!$difftoRev->userCan(Revision::DELETED_TEXT)) { - $this->setWarning("Couldn't diff to r{$difftoRev->getID()}: content is hidden"); + if ( $params['diffto'] != 0 ) { + $difftoRev = Revision::newFromID( $params['diffto'] ); + if ( !$difftoRev ) + $this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) ); + if ( !$difftoRev->userCan( Revision::DELETED_TEXT ) ) { + $this->setWarning( "Couldn't diff to r{$difftoRev->getID()}: content is hidden" ); $params['diffto'] = null; } } + $this->diffto = $params['diffto']; } - $this->addTables('revision'); - $this->addFields(Revision::selectFields()); - $this->addTables('page'); - $this->addWhere('page_id = rev_page'); + $db = $this->getDB(); + $this->addTables( array( 'page', 'revision' ) ); + $this->addFields( Revision::selectFields() ); + $this->addWhere( 'page_id = rev_page' ); - $prop = array_flip($params['prop']); + $prop = array_flip( $params['prop'] ); // Optional fields - $this->fld_ids = isset ($prop['ids']); + $this->fld_ids = isset ( $prop['ids'] ); // $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed? - $this->fld_flags = isset ($prop['flags']); - $this->fld_timestamp = isset ($prop['timestamp']); - $this->fld_comment = isset ($prop['comment']); - $this->fld_size = isset ($prop['size']); - $this->fld_user = isset ($prop['user']); + $this->fld_flags = isset ( $prop['flags'] ); + $this->fld_timestamp = isset ( $prop['timestamp'] ); + $this->fld_comment = isset ( $prop['comment'] ); + $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); + $this->fld_size = isset ( $prop['size'] ); + $this->fld_user = isset ( $prop['user'] ); $this->token = $params['token']; - $this->diffto = $params['diffto']; - if ( !is_null($this->token) || $pageCount > 0) { + // Possible indexes used + $index = array(); + + if ( !is_null( $this->token ) || $pageCount > 0 ) { $this->addFields( Revision::selectPageFields() ); } - if (isset ($prop['content'])) { + if ( isset ( $prop['tags'] ) ) { + $this->fld_tags = true; + $this->addTables( 'tag_summary' ); + $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', array( 'rev_id=ts_rev_id' ) ) ) ); + $this->addFields( 'ts_tags' ); + } + + if ( !is_null( $params['tag'] ) ) { + $this->addTables( 'change_tag' ); + $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'rev_id=ct_rev_id' ) ) ) ); + $this->addWhereFld( 'ct_tag' , $params['tag'] ); + global $wgOldChangeTagsIndex; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; + } + + if ( isset( $prop['content'] ) || !is_null( $this->difftotext ) ) { // For each page we will request, the user must have read rights for that page - foreach ($pageSet->getGoodTitles() as $title) { - if( !$title->userCanRead() ) + foreach ( $pageSet->getGoodTitles() as $title ) { + if ( !$title->userCanRead() ) $this->dieUsage( 'The current user is not allowed to read ' . $title->getPrefixedText(), - 'accessdenied'); + 'accessdenied' ); } - $this->addTables('text'); - $this->addWhere('rev_text_id=old_id'); - $this->addFields('old_id'); - $this->addFields(Revision::selectTextFields()); + $this->addTables( 'text' ); + $this->addWhere( 'rev_text_id=old_id' ); + $this->addFields( 'old_id' ); + $this->addFields( Revision::selectTextFields() ); - $this->fld_content = true; + $this->fld_content = isset( $prop['content'] ); $this->expandTemplates = $params['expandtemplates']; $this->generateXML = $params['generatexml']; - if(isset($params['section'])) + if ( isset( $params['section'] ) ) $this->section = $params['section']; else $this->section = false; @@ -170,22 +192,22 @@ class ApiQueryRevisions extends ApiQueryBase { $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 ); $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 ); $limit = $params['limit']; - if( $limit == 'max' ) { + if ( $limit == 'max' ) { $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; $this->getResult()->addValue( 'limits', $this->getModuleName(), $limit ); } - if ($enumRevMode) { + if ( $enumRevMode ) { // This is mostly to prevent parameter errors (and optimize SQL?) - if (!is_null($params['startid']) && !is_null($params['start'])) - $this->dieUsage('start and startid cannot be used together', 'badparams'); + if ( !is_null( $params['startid'] ) && !is_null( $params['start'] ) ) + $this->dieUsage( 'start and startid cannot be used together', 'badparams' ); - if (!is_null($params['endid']) && !is_null($params['end'])) - $this->dieUsage('end and endid cannot be used together', 'badparams'); + if ( !is_null( $params['endid'] ) && !is_null( $params['end'] ) ) + $this->dieUsage( 'end and endid cannot be used together', 'badparams' ); - if(!is_null($params['user']) && !is_null($params['excludeuser'])) - $this->dieUsage('user and excludeuser cannot be used together', 'badparams'); + if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) + $this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' ); // This code makes an assumption that sorting by rev_id and rev_timestamp produces // the same result. This way users may request revisions starting at a given time, @@ -194,187 +216,212 @@ class ApiQueryRevisions extends ApiQueryBase { // one row with the same timestamp for the same page. // The order needs to be the same as start parameter to avoid SQL filesort. - if (is_null($params['startid']) && is_null($params['endid'])) - $this->addWhereRange('rev_timestamp', $params['dir'], - $params['start'], $params['end']); + if ( is_null( $params['startid'] ) && is_null( $params['endid'] ) ) + $this->addWhereRange( 'rev_timestamp', $params['dir'], + $params['start'], $params['end'] ); else { - $this->addWhereRange('rev_id', $params['dir'], - $params['startid'], $params['endid']); + $this->addWhereRange( 'rev_id', $params['dir'], + $params['startid'], $params['endid'] ); // One of start and end can be set // If neither is set, this does nothing - $this->addWhereRange('rev_timestamp', $params['dir'], - $params['start'], $params['end'], false); + $this->addWhereRange( 'rev_timestamp', $params['dir'], + $params['start'], $params['end'], false ); } // must manually initialize unset limit - if (is_null($limit)) + if ( is_null( $limit ) ) $limit = 10; - $this->validateLimit('limit', $limit, 1, $userMax, $botMax); + $this->validateLimit( 'limit', $limit, 1, $userMax, $botMax ); // There is only one ID, use it - $this->addWhereFld('rev_page', reset(array_keys($pageSet->getGoodTitles()))); - - if(!is_null($params['user'])) { - $this->addWhereFld('rev_user_text', $params['user']); - } elseif (!is_null($params['excludeuser'])) { - $this->addWhere('rev_user_text != ' . - $this->getDB()->addQuotes($params['excludeuser'])); + $ids = array_keys( $pageSet->getGoodTitles() ); + $this->addWhereFld( 'rev_page', reset( $ids ) ); + + if ( !is_null( $params['user'] ) ) { + $this->addWhereFld( 'rev_user_text', $params['user'] ); + } elseif ( !is_null( $params['excludeuser'] ) ) { + $this->addWhere( 'rev_user_text != ' . + $db->addQuotes( $params['excludeuser'] ) ); } - if(!is_null($params['user']) || !is_null($params['excludeuser'])) { + if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) { // Paranoia: avoid brute force searches (bug 17342) - $this->addWhere('rev_deleted & ' . Revision::DELETED_USER . ' = 0'); + $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' ); } } - elseif ($revCount > 0) { + elseif ( $revCount > 0 ) { $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; $revs = $pageSet->getRevisionIDs(); - if(self::truncateArray($revs, $max)) - $this->setWarning("Too many values supplied for parameter 'revids': the limit is $max"); + if ( self::truncateArray( $revs, $max ) ) + $this->setWarning( "Too many values supplied for parameter 'revids': the limit is $max" ); // Get all revision IDs - $this->addWhereFld('rev_id', array_keys($revs)); + $this->addWhereFld( 'rev_id', array_keys( $revs ) ); - if(!is_null($params['continue'])) - $this->addWhere("rev_id >= '" . intval($params['continue']) . "'"); - $this->addOption('ORDER BY', 'rev_id'); + if ( !is_null( $params['continue'] ) ) + $this->addWhere( "rev_id >= '" . intval( $params['continue'] ) . "'" ); + $this->addOption( 'ORDER BY', 'rev_id' ); // assumption testing -- we should never get more then $revCount rows. $limit = $revCount; } - elseif ($pageCount > 0) { + elseif ( $pageCount > 0 ) { $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; $titles = $pageSet->getGoodTitles(); - if(self::truncateArray($titles, $max)) - $this->setWarning("Too many values supplied for parameter 'titles': the limit is $max"); + if ( self::truncateArray( $titles, $max ) ) + $this->setWarning( "Too many values supplied for parameter 'titles': the limit is $max" ); // When working in multi-page non-enumeration mode, // limit to the latest revision only - $this->addWhere('page_id=rev_page'); - $this->addWhere('page_latest=rev_id'); + $this->addWhere( 'page_id=rev_page' ); + $this->addWhere( 'page_latest=rev_id' ); // Get all page IDs - $this->addWhereFld('page_id', array_keys($titles)); + $this->addWhereFld( 'page_id', array_keys( $titles ) ); // Every time someone relies on equality propagation, god kills a kitten :) - $this->addWhereFld('rev_page', array_keys($titles)); + $this->addWhereFld( 'rev_page', array_keys( $titles ) ); - if(!is_null($params['continue'])) + if ( !is_null( $params['continue'] ) ) { - $cont = explode('|', $params['continue']); - if(count($cont) != 2) - $this->dieUsage("Invalid continue param. You should pass the original " . - "value returned by the previous query", "_badcontinue"); - $pageid = intval($cont[0]); - $revid = intval($cont[1]); - $this->addWhere("rev_page > '$pageid' OR " . + $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" ); + $pageid = intval( $cont[0] ); + $revid = intval( $cont[1] ); + $this->addWhere( "rev_page > '$pageid' OR " . "(rev_page = '$pageid' AND " . - "rev_id >= '$revid')"); + "rev_id >= '$revid')" ); } - $this->addOption('ORDER BY', 'rev_page, rev_id'); + $this->addOption( 'ORDER BY', 'rev_page, rev_id' ); // assumption testing -- we should never get more then $pageCount rows. $limit = $pageCount; } else - ApiBase :: dieDebug(__METHOD__, 'param validation?'); + ApiBase :: dieDebug( __METHOD__, 'param validation?' ); - $this->addOption('LIMIT', $limit +1); + $this->addOption( 'LIMIT', $limit + 1 ); + $this->addOption( 'USE INDEX', $index ); $data = array (); $count = 0; - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); - $db = $this->getDB(); - while ($row = $db->fetchObject($res)) { + while ( $row = $db->fetchObject( $res ) ) { - if (++ $count > $limit) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - if (!$enumRevMode) - ApiBase :: dieDebug(__METHOD__, 'Got more rows then expected'); // bug report - $this->setContinueEnumParameter('startid', intval($row->rev_id)); + if ( !$enumRevMode ) + ApiBase :: dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report + $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) ); break; } - $revision = new Revision( $row ); + // - $fit = $this->addPageSubItem($revision->getPage(), $this->extractRowInfo($revision), 'rev'); - if(!$fit) + $fit = $this->addPageSubItem( $row->rev_page, $this->extractRowInfo( $row ), 'rev' ); + if ( !$fit ) { - if($enumRevMode) - $this->setContinueEnumParameter('startid', intval($row->rev_id)); - else if($revCount > 0) - $this->setContinueEnumParameter('continue', intval($row->rev_id)); + if ( $enumRevMode ) + $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) ); + else if ( $revCount > 0 ) + $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) ); else - $this->setContinueEnumParameter('continue', intval($row->rev_page) . - '|' . intval($row->rev_id)); + $this->setContinueEnumParameter( 'continue', intval( $row->rev_page ) . + '|' . intval( $row->rev_id ) ); break; } } - $db->freeResult($res); + $db->freeResult( $res ); } - private function extractRowInfo( $revision ) { + private function extractRowInfo( $row ) { + $revision = new Revision( $row ); $title = $revision->getTitle(); $vals = array (); - if ($this->fld_ids) { - $vals['revid'] = intval($revision->getId()); + if ( $this->fld_ids ) { + $vals['revid'] = intval( $revision->getId() ); // $vals['oldid'] = intval($row->rev_text_id); // todo: should this be exposed? + if ( !is_null( $revision->getParentId() ) ) + $vals['parentid'] = intval( $revision->getParentId() ); } - if ($this->fld_flags && $revision->isMinor()) + if ( $this->fld_flags && $revision->isMinor() ) $vals['minor'] = ''; - if ($this->fld_user) { - if ($revision->isDeleted(Revision::DELETED_USER)) { + if ( $this->fld_user ) { + if ( $revision->isDeleted( Revision::DELETED_USER ) ) { $vals['userhidden'] = ''; } else { $vals['user'] = $revision->getUserText(); - if (!$revision->getUser()) + if ( !$revision->getUser() ) $vals['anon'] = ''; } } - if ($this->fld_timestamp) { - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp()); + if ( $this->fld_timestamp ) { + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() ); } - if ($this->fld_size && !is_null($revision->getSize())) { - $vals['size'] = intval($revision->getSize()); + if ( $this->fld_size && !is_null( $revision->getSize() ) ) { + $vals['size'] = intval( $revision->getSize() ); } - if ($this->fld_comment) { - if ($revision->isDeleted(Revision::DELETED_COMMENT)) { + if ( $this->fld_comment || $this->fld_parsedcomment ) { + if ( $revision->isDeleted( Revision::DELETED_COMMENT ) ) { $vals['commenthidden'] = ''; } else { $comment = $revision->getComment(); - if (strval($comment) !== '') - $vals['comment'] = $comment; + if ( strval( $comment ) !== '' ) + { + if ( $this->fld_comment ) + $vals['comment'] = $comment; + + if ( $this->fld_parsedcomment ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $comment, $title ); + } + } } - } + } - if(!is_null($this->token)) + if ( $this->fld_tags ) { + if ( $row->ts_tags ) { + $tags = explode( ',', $row->ts_tags ); + $this->getResult()->setIndexedTagName( $tags, 'tag' ); + $vals['tags'] = $tags; + } else { + $vals['tags'] = array(); + } + } + + if ( !is_null( $this->token ) ) { $tokenFunctions = $this->getTokenFunctions(); - foreach($this->token as $t) + foreach ( $this->token as $t ) { - $val = call_user_func($tokenFunctions[$t], $title->getArticleID(), $title, $revision); - if($val === false) - $this->setWarning("Action '$t' is not allowed for the current user"); + $val = call_user_func( $tokenFunctions[$t], $title->getArticleID(), $title, $revision ); + if ( $val === false ) + $this->setWarning( "Action '$t' is not allowed for the current user" ); else $vals[$t . 'token'] = $val; } } - if ($this->fld_content && !$revision->isDeleted(Revision::DELETED_TEXT)) { + $text = null; + if ( $this->fld_content || !is_null( $this->difftotext ) ) { global $wgParser; $text = $revision->getText(); - # Expand templates after getting section content because - # template-added sections don't count and Parser::preprocess() - # will have less input - if ($this->section !== false) { - $text = $wgParser->getSection( $text, $this->section, false); - if($text === false) - $this->dieUsage("There is no section {$this->section} in r".$revision->getId(), 'nosuchsection'); + // Expand templates after getting section content because + // template-added sections don't count and Parser::preprocess() + // will have less input + if ( $this->section !== false ) { + $text = $wgParser->getSection( $text, $this->section, false ); + if ( $text === false ) + $this->dieUsage( "There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection' ); } - if ($this->generateXML) { + } + if ( $this->fld_content && !$revision->isDeleted( Revision::DELETED_TEXT ) ) { + if ( $this->generateXML ) { $wgParser->startExternalParse( $title, new ParserOptions(), OT_PREPROCESS ); $dom = $wgParser->preprocessToDom( $text ); if ( is_callable( array( $dom, 'saveXML' ) ) ) { @@ -385,24 +432,30 @@ class ApiQueryRevisions extends ApiQueryBase { $vals['parsetree'] = $xml; } - if ($this->expandTemplates) { + if ( $this->expandTemplates ) { $text = $wgParser->preprocess( $text, $title, new ParserOptions() ); } - ApiResult :: setContent($vals, $text); - } else if ($this->fld_content) { + ApiResult :: setContent( $vals, $text ); + } else if ( $this->fld_content ) { $vals['texthidden'] = ''; } - if (!is_null($this->diffto)) { + if ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) { global $wgAPIMaxUncachedDiffs; - static $n = 0; // Numer of uncached diffs we've had - if($n< $wgAPIMaxUncachedDiffs) { - $engine = new DifferenceEngine($title, $revision->getID(), $this->diffto); + static $n = 0; // Number of uncached diffs we've had + if ( $n < $wgAPIMaxUncachedDiffs ) { + $vals['diff'] = array(); + if ( !is_null( $this->difftotext ) ) { + $engine = new DifferenceEngine( $title ); + $engine->setText( $text, $this->difftotext ); + } else { + $engine = new DifferenceEngine( $title, $revision->getID(), $this->diffto ); + $vals['diff']['from'] = $engine->getOldid(); + $vals['diff']['to'] = $engine->getNewid(); + } $difftext = $engine->getDiffBody(); - $vals['diff']['from'] = $engine->getOldid(); - $vals['diff']['to'] = $engine->getNewid(); - ApiResult::setContent($vals['diff'], $difftext); - if(!$engine->wasCacheHit()) + ApiResult::setContent( $vals['diff'], $difftext ); + if ( !$engine->wasCacheHit() ) $n++; } else { $vals['diff']['notcached'] = ''; @@ -434,7 +487,9 @@ class ApiQueryRevisions extends ApiQueryBase { 'user', 'size', 'comment', + 'parsedcomment', 'content', + 'tags' ) ), 'limit' => array ( @@ -468,36 +523,41 @@ class ApiQueryRevisions extends ApiQueryBase { 'excludeuser' => array( ApiBase :: PARAM_TYPE => 'user' ), + 'tag' => null, 'expandtemplates' => false, 'generatexml' => false, 'section' => null, 'token' => array( - ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()), + ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ), ApiBase :: PARAM_ISMULTI => true ), 'continue' => null, 'diffto' => null, + 'difftotext' => null, ); } public function getParamDescription() { return array ( 'prop' => 'Which properties to get for each revision.', - 'limit' => 'limit how many revisions will be returned (enum)', - 'startid' => 'from which revision id to start enumeration (enum)', - 'endid' => 'stop revision enumeration on this revid (enum)', - 'start' => 'from which revision timestamp to start enumeration (enum)', - 'end' => 'enumerate up to this timestamp (enum)', - 'dir' => 'direction of enumeration - towards "newer" or "older" revisions (enum)', - 'user' => 'only include revisions made by user', - 'excludeuser' => 'exclude revisions made by user', - 'expandtemplates' => 'expand templates in revision content', - 'generatexml' => 'generate XML parse tree for revision content', - 'section' => 'only retrieve the content of this section', + 'limit' => 'Limit how many revisions will be returned (enum)', + 'startid' => 'From which revision id to start enumeration (enum)', + 'endid' => 'Stop revision enumeration on this revid (enum)', + 'start' => 'From which revision timestamp to start enumeration (enum)', + 'end' => 'Enumerate up to this timestamp (enum)', + 'dir' => 'Direction of enumeration - towards "newer" or "older" revisions (enum)', + 'user' => 'Only include revisions made by user', + 'excludeuser' => 'Exclude revisions made by user', + 'expandtemplates' => 'Expand templates in revision content', + 'generatexml' => 'Generate XML parse tree for revision content', + 'section' => 'Only retrieve the content of this section', 'token' => 'Which tokens to obtain for each revision', 'continue' => 'When more results are available, use this to continue', - 'diffto' => array('Revision ID to diff each revision to.', - 'Use "prev", "next" and "cur" for the previous, next and current revision respectively.'), + 'diffto' => array( 'Revision ID to diff each revision to.', + 'Use "prev", "next" and "cur" for the previous, next and current revision respectively.' ), + 'difftotext' => array( 'Text to diff each revision to. Only diffs a limited number of revisions.', + 'Overrides diffto. If rvsection is set, only that section will be diffed against this text.' ), + 'tag' => 'Only list revisions tagged with this tag', ); } @@ -511,6 +571,19 @@ class ApiQueryRevisions extends ApiQueryBase { 'All parameters marked as (enum) may only be used with a single page (#2).' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'nosuchrevid', 'diffto' ), + array( 'code' => 'revids', 'info' => 'The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).' ), + array( 'code' => 'multpages', 'info' => 'titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start and end parameters may only be used on a single page.' ), + array( 'code' => 'diffto', 'info' => 'rvdiffto must be set to a non-negative number, "prev", "next" or "cur"' ), + array( 'code' => 'badparams', 'info' => 'start and startid cannot be used together' ), + array( 'code' => 'badparams', 'info' => 'end and endid cannot be used together' ), + array( 'code' => 'badparams', 'info' => 'user and excludeuser cannot be used together' ), + array( 'code' => 'nosuchsection', 'info' => 'There is no section section in rID' ), + ) ); + } protected function getExamples() { return array ( @@ -530,6 +603,6 @@ class ApiQueryRevisions extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryRevisions.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryRevisions.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php index a8f3fcc8..4e032321 100644 --- a/includes/api/ApiQuerySearch.php +++ b/includes/api/ApiQuerySearch.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,36 +35,42 @@ if (!defined('MEDIAWIKI')) { */ class ApiQuerySearch extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'sr'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'sr' ); } public function execute() { $this->run(); } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { - + private function run( $resultPageSet = null ) { + global $wgContLang; $params = $this->extractRequestParams(); + // Extract parameters $limit = $params['limit']; $query = $params['search']; $what = $params['what']; - if (strval($query) === '') - $this->dieUsage("empty search string is not allowed", 'param-search'); + $searchInfo = array_flip( $params['info'] ); + $prop = array_flip( $params['prop'] ); + + if ( strval( $query ) === '' ) + $this->dieUsage( "empty search string is not allowed", 'param-search' ); + // Create search engine instance and set options $search = SearchEngine::create(); - $search->setLimitOffset( $limit+1, $params['offset'] ); + $search->setLimitOffset( $limit + 1, $params['offset'] ); $search->setNamespaces( $params['namespace'] ); $search->showRedirects = $params['redirects']; - if ($what == 'text') { + // Perform the actual search + if ( $what == 'text' ) { $matches = $search->searchText( $query ); - } elseif( $what == 'title' ) { + } elseif ( $what == 'title' ) { $matches = $search->searchTitle( $query ); } else { // We default to title searches; this is a terrible legacy @@ -78,36 +84,61 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { // for instance the Lucene-based engine we use on Wikipedia. // In this case, fall back to full-text search (which will // include titles in it!) - if( is_null( $matches ) ) { + if ( is_null( $matches ) ) { $what = 'text'; $matches = $search->searchText( $query ); } } - if (is_null($matches)) - $this->dieUsage("{$what} search is disabled", - "search-{$what}-disabled"); + if ( is_null( $matches ) ) + $this->dieUsage( "{$what} search is disabled", "search-{$what}-disabled" ); + + // Add search meta data to result + if ( isset( $searchInfo['totalhits'] ) ) { + $totalhits = $matches->getTotalHits(); + if ( $totalhits !== null ) { + $this->getResult()->addValue( array( 'query', 'searchinfo' ), + 'totalhits', $totalhits ); + } + } + if ( isset( $searchInfo['suggestion'] ) && $matches->hasSuggestion() ) { + $this->getResult()->addValue( array( 'query', 'searchinfo' ), + 'suggestion', $matches->getSuggestionQuery() ); + } + // Add the search results to the result + $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); $titles = array (); $count = 0; - while( $result = $matches->next() ) { - if (++ $count > $limit) { + while ( $result = $matches->next() ) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional items to be had. Stop here... - $this->setContinueEnumParameter('offset', $params['offset'] + $params['limit']); + $this->setContinueEnumParameter( 'offset', $params['offset'] + $params['limit'] ); break; } // Silently skip broken and missing titles - if ($result->isBrokenTitle() || $result->isMissingRevision()) + if ( $result->isBrokenTitle() || $result->isMissingRevision() ) continue; $title = $result->getTitle(); - if (is_null($resultPageSet)) { + if ( is_null( $resultPageSet ) ) { $vals = array(); - ApiQueryBase::addTitleInfo($vals, $title); - $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) - { - $this->setContinueEnumParameter('offset', $params['offset'] + $count - 1); + ApiQueryBase::addTitleInfo( $vals, $title ); + + if ( isset( $prop['snippet'] ) ) + $vals['snippet'] = $result->getTextSnippet( $terms ); + if ( isset( $prop['size'] ) ) + $vals['size'] = $result->getByteSize(); + if ( isset( $prop['wordcount'] ) ) + $vals['wordcount'] = $result->getWordCount(); + if ( isset( $prop['timestamp'] ) ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() ); + + // Add item to results and see whether it fits + $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), + null, $vals ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'offset', $params['offset'] + $count - 1 ); break; } } else { @@ -115,10 +146,12 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { } } - if (is_null($resultPageSet)) { - $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'p'); + if ( is_null( $resultPageSet ) ) { + $this->getResult()->setIndexedTagName_internal( array( + 'query', $this->getModuleName() + ), 'p' ); } else { - $resultPageSet->populateFromTitles($titles); + $resultPageSet->populateFromTitles( $titles ); } } @@ -141,14 +174,32 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { 'text', ) ), + 'info' => array( + ApiBase :: PARAM_DFLT => 'totalhits|suggestion', + ApiBase :: PARAM_TYPE => array ( + 'totalhits', + 'suggestion', + ), + ApiBase :: PARAM_ISMULTI => true, + ), + 'prop' => array( + ApiBase :: PARAM_DFLT => 'size|wordcount|timestamp|snippet', + ApiBase :: PARAM_TYPE => array ( + 'size', + 'wordcount', + 'timestamp', + 'snippet', + ), + ApiBase :: PARAM_ISMULTI => true, + ), 'redirects' => false, 'offset' => 0, 'limit' => array ( ApiBase :: PARAM_DFLT => 10, ApiBase :: PARAM_TYPE => 'limit', ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + ApiBase :: PARAM_MAX => ApiBase :: LIMIT_SML1, + ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_SML2 ) ); } @@ -158,6 +209,8 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { 'search' => 'Search for all page titles (or content) that has this value.', 'namespace' => 'The namespace(s) to enumerate.', 'what' => 'Search inside the text or titles.', + 'info' => 'What metadata to return.', + 'prop' => 'What properties to return.', 'redirects' => 'Include redirect pages in the search.', 'offset' => 'Use this value to continue paging (return by query)', 'limit' => 'How many total pages to return.' @@ -167,6 +220,14 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { public function getDescription() { return 'Perform a full text search'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'param-search', 'info' => 'empty search string is not allowed' ), + array( 'code' => 'search-text-disabled', 'info' => 'text search is disabled' ), + array( 'code' => 'search-title-disabled', 'info' => 'title search is disabled' ), + ) ); + } protected function getExamples() { return array ( @@ -177,6 +238,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQuerySearch.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQuerySearch.php 69932 2010-07-26 08:03:21Z tstarling $'; } -} \ No newline at end of file +} diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php index 623855f6..0385e192 100644 --- a/includes/api/ApiQuerySiteinfo.php +++ b/includes/api/ApiQuerySiteinfo.php @@ -23,7 +23,7 @@ * http://www.gnu.org/copyleft/gpl.html */ -if( !defined('MEDIAWIKI') ) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production require_once( 'ApiQueryBase.php' ); } @@ -42,7 +42,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { public function execute() { $params = $this->extractRequestParams(); $done = array(); - foreach( $params['prop'] as $p ) + foreach ( $params['prop'] as $p ) { switch ( $p ) { @@ -72,7 +72,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { $fit = $this->appendStatistics( $p ); break; case 'usergroups': - $fit = $this->appendUserGroups( $p ); + $fit = $this->appendUserGroups( $p, $params['numberingroup'] ); break; case 'extensions': $fit = $this->appendExtensions( $p ); @@ -83,15 +83,18 @@ class ApiQuerySiteinfo extends ApiQueryBase { case 'rightsinfo': $fit = $this->appendRightsInfo( $p ); break; + case 'languages': + $fit = $this->appendLanguages( $p ); + break; default : ApiBase :: dieDebug( __METHOD__, "Unknown prop=$p" ); } - if(!$fit) + if ( !$fit ) { - # Abuse siprop as a query-continue parameter - # and set it to all unprocessed props - $this->setContinueEnumParameter('prop', implode('|', - array_diff($params['prop'], $done))); + // Abuse siprop as a query-continue parameter + // and set it to all unprocessed props + $this->setContinueEnumParameter( 'prop', implode( '|', + array_diff( $params['prop'], $done ) ) ); break; } $done[] = $p; @@ -99,45 +102,59 @@ class ApiQuerySiteinfo extends ApiQueryBase { } protected function appendGeneralInfo( $property ) { - global $wgSitename, $wgVersion, $wgCapitalLinks, $wgRightsCode, $wgRightsText, $wgContLang; - global $wgLanguageCode, $IP, $wgEnableWriteAPI, $wgLang, $wgLocaltimezone, $wgLocalTZoffset; + global $wgContLang; + global $wgLang; $data = array(); - $mainPage = Title :: newFromText(wfMsgForContent('mainpage')); + $mainPage = Title :: newFromText( wfMsgForContent( 'mainpage' ) ); $data['mainpage'] = $mainPage->getPrefixedText(); $data['base'] = $mainPage->getFullUrl(); - $data['sitename'] = $wgSitename; - $data['generator'] = "MediaWiki $wgVersion"; - - $svn = SpecialVersion::getSvnRevision( $IP ); - if( $svn ) + $data['sitename'] = $GLOBALS['wgSitename']; + $data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}"; + $data['phpversion'] = phpversion(); + $data['phpsapi'] = php_sapi_name(); + $data['dbtype'] = $GLOBALS['wgDBtype']; + $data['dbversion'] = $this->getDB()->getServerVersion(); + + $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] ); + if ( $svn ) $data['rev'] = $svn; - $data['case'] = $wgCapitalLinks ? 'first-letter' : 'case-sensitive'; // 'case-insensitive' option is reserved for future + // 'case-insensitive' option is reserved for future + $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive'; - if( isset( $wgRightsCode ) ) - $data['rightscode'] = $wgRightsCode; - $data['rights'] = $wgRightsText; - $data['lang'] = $wgLanguageCode; - if( $wgContLang->isRTL() ) + if ( isset( $GLOBALS['wgRightsCode'] ) ) + $data['rightscode'] = $GLOBALS['wgRightsCode']; + $data['rights'] = $GLOBALS['wgRightsText']; + $data['lang'] = $GLOBALS['wgLanguageCode']; + if ( $wgContLang->isRTL() ) $data['rtl'] = ''; $data['fallback8bitEncoding'] = $wgLang->fallback8bitEncoding(); - if( wfReadOnly() ) + if ( wfReadOnly() ) { $data['readonly'] = ''; - if( $wgEnableWriteAPI ) + $data['readonlyreason'] = wfReadOnlyReason(); + } + if ( $GLOBALS['wgEnableWriteAPI'] ) $data['writeapi'] = ''; - $tz = $wgLocaltimezone; - $offset = $wgLocalTZoffset; - if( is_null( $tz ) ) { + $tz = $GLOBALS['wgLocaltimezone']; + $offset = $GLOBALS['wgLocalTZoffset']; + if ( is_null( $tz ) ) { $tz = 'UTC'; $offset = 0; - } elseif( is_null( $offset ) ) { + } elseif ( is_null( $offset ) ) { $offset = 0; } $data['timezone'] = $tz; - $data['timeoffset'] = intval($offset); + $data['timeoffset'] = intval( $offset ); + $data['articlepath'] = $GLOBALS['wgArticlePath']; + $data['scriptpath'] = $GLOBALS['wgScriptPath']; + $data['script'] = $GLOBALS['wgScript']; + $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath']; + $data['server'] = $GLOBALS['wgServer']; + $data['wikiid'] = wfWikiID(); + $data['time'] = wfTimestamp( TS_ISO_8601, time() ); return $this->getResult()->addValue( 'query', $property, $data ); } @@ -145,19 +162,23 @@ class ApiQuerySiteinfo extends ApiQueryBase { protected function appendNamespaces( $property ) { global $wgContLang; $data = array(); - foreach( $wgContLang->getFormattedNamespaces() as $ns => $title ) + foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) { $data[$ns] = array( - 'id' => intval($ns) + 'id' => intval( $ns ), + 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive', ); ApiResult :: setContent( $data[$ns], $title ); $canonical = MWNamespace::getCanonicalName( $ns ); - if( MWNamespace::hasSubpages( $ns ) ) + if ( MWNamespace::hasSubpages( $ns ) ) $data[$ns]['subpages'] = ''; - if( $canonical ) - $data[$ns]['canonical'] = strtr($canonical, '_', ' '); + if ( $canonical ) + $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' ); + + if ( MWNamespace::isContent( $ns ) ) + $data[$ns]['content'] = ''; } $this->getResult()->setIndexedTagName( $data, 'ns' ); @@ -166,17 +187,16 @@ class ApiQuerySiteinfo extends ApiQueryBase { protected function appendNamespaceAliases( $property ) { global $wgNamespaceAliases, $wgContLang; - $wgContLang->load(); - $aliases = array_merge( $wgNamespaceAliases, $wgContLang->namespaceAliases ); + $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() ); $namespaces = $wgContLang->getNamespaces(); $data = array(); - foreach( $aliases as $title => $ns ) { - if( $namespaces[$ns] == $title ) { + foreach ( $aliases as $title => $ns ) { + if ( $namespaces[$ns] == $title ) { // Don't list duplicates continue; } $item = array( - 'id' => intval($ns) + 'id' => intval( $ns ) ); ApiResult :: setContent( $item, strtr( $title, '_', ' ' ) ); $data[] = $item; @@ -189,7 +209,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { protected function appendSpecialPageAliases( $property ) { global $wgLang; $data = array(); - foreach( $wgLang->getSpecialPageAliases() as $specialpage => $aliases ) + foreach ( $wgLang->getSpecialPageAliases() as $specialpage => $aliases ) { $arr = array( 'realname' => $specialpage, 'aliases' => $aliases ); $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' ); @@ -202,16 +222,16 @@ class ApiQuerySiteinfo extends ApiQueryBase { protected function appendMagicWords( $property ) { global $wgContLang; $data = array(); - foreach($wgContLang->getMagicWords() as $magicword => $aliases) + foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) { - $caseSensitive = array_shift($aliases); - $arr = array('name' => $magicword, 'aliases' => $aliases); - if($caseSensitive) + $caseSensitive = array_shift( $aliases ); + $arr = array( 'name' => $magicword, 'aliases' => $aliases ); + if ( $caseSensitive ) $arr['case-sensitive'] = ''; - $this->getResult()->setIndexedTagName($arr['aliases'], 'alias'); + $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' ); $data[] = $arr; } - $this->getResult()->setIndexedTagName($data, 'magicword'); + $this->getResult()->setIndexedTagName( $data, 'magicword' ); return $this->getResult()->addValue( 'query', $property, $data ); } @@ -220,11 +240,11 @@ class ApiQuerySiteinfo extends ApiQueryBase { $this->addTables( 'interwiki' ); $this->addFields( array( 'iw_prefix', 'iw_local', 'iw_url' ) ); - if( $filter === 'local' ) + if ( $filter === 'local' ) $this->addWhere( 'iw_local = 1' ); - elseif( $filter === '!local' ) + elseif ( $filter === '!local' ) $this->addWhere( 'iw_local = 0' ); - elseif( $filter ) + elseif ( $filter ) ApiBase :: dieDebug( __METHOD__, "Unknown filter=$filter" ); $this->addOption( 'ORDER BY', 'iw_prefix' ); @@ -234,14 +254,14 @@ class ApiQuerySiteinfo extends ApiQueryBase { $data = array(); $langNames = Language::getLanguageNames(); - while( $row = $db->fetchObject($res) ) + while ( $row = $db->fetchObject( $res ) ) { $val = array(); $val['prefix'] = $row->iw_prefix; - if( $row->iw_local == '1' ) + if ( $row->iw_local == '1' ) $val['local'] = ''; // $val['trans'] = intval($row->iw_trans); // should this be exposed? - if( isset( $langNames[$row->iw_prefix] ) ) + if ( isset( $langNames[$row->iw_prefix] ) ) $val['language'] = $langNames[$row->iw_prefix]; $val['url'] = $row->iw_url; @@ -256,13 +276,13 @@ class ApiQuerySiteinfo extends ApiQueryBase { protected function appendDbReplLagInfo( $property, $includeAll ) { global $wgShowHostnames; $data = array(); - if( $includeAll ) { + if ( $includeAll ) { if ( !$wgShowHostnames ) - $this->dieUsage('Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied'); + $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' ); $lb = wfGetLB(); $lags = $lb->getLagTimes(); - foreach( $lags as $i => $lag ) { + foreach ( $lags as $i => $lag ) { $data[] = array( 'host' => $lb->getServerName( $i ), 'lag' => $lag @@ -293,16 +313,22 @@ class ApiQuerySiteinfo extends ApiQueryBase { $data['images'] = intval( SiteStats::images() ); $data['users'] = intval( SiteStats::users() ); $data['activeusers'] = intval( SiteStats::activeUsers() ); - $data['admins'] = intval( SiteStats::numberingroup('sysop') ); + $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) ); $data['jobs'] = intval( SiteStats::jobs() ); return $this->getResult()->addValue( 'query', $property, $data ); } - protected function appendUserGroups( $property ) { + protected function appendUserGroups( $property, $numberInGroup ) { global $wgGroupPermissions; $data = array(); - foreach( $wgGroupPermissions as $group => $permissions ) { - $arr = array( 'name' => $group, 'rights' => array_keys( $permissions, true ) ); + foreach ( $wgGroupPermissions as $group => $permissions ) { + $arr = array( + 'name' => $group, + 'rights' => array_keys( $permissions, true ), + ); + if ( $numberInGroup ) + $arr['number'] = SiteStats::numberInGroup( $group ); + $this->getResult()->setIndexedTagName( $arr['rights'], 'permission' ); $data[] = $arr; } @@ -315,7 +341,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { global $wgFileExtensions; $data = array(); - foreach( $wgFileExtensions as $ext ) { + foreach ( $wgFileExtensions as $ext ) { $data[] = array( 'ext' => $ext ); } $this->getResult()->setIndexedTagName( $data, 'fe' ); @@ -329,21 +355,32 @@ class ApiQuerySiteinfo extends ApiQueryBase { foreach ( $extensions as $ext ) { $ret = array(); $ret['type'] = $type; - if ( isset( $ext['name'] ) ) + if ( isset( $ext['name'] ) ) $ret['name'] = $ext['name']; - if ( isset( $ext['description'] ) ) + if ( isset( $ext['description'] ) ) $ret['description'] = $ext['description']; - if ( isset( $ext['descriptionmsg'] ) ) - $ret['descriptionmsg'] = $ext['descriptionmsg']; + if ( isset( $ext['descriptionmsg'] ) ) { + // Can be a string or array( key, param1, param2, ... ) + if ( is_array( $ext['descriptionmsg'] ) ) { + $ret['descriptionmsg'] = $ext['descriptionmsg'][0]; + $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 ); + $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' ); + } else { + $ret['descriptionmsg'] = $ext['descriptionmsg']; + } + } if ( isset( $ext['author'] ) ) { - $ret['author'] = is_array( $ext['author'] ) ? + $ret['author'] = is_array( $ext['author'] ) ? implode( ', ', $ext['author' ] ) : $ext['author']; } + if ( isset( $ext['url'] ) ) { + $ret['url'] = $ext['url']; + } if ( isset( $ext['version'] ) ) { $ret['version'] = $ext['version']; - } elseif ( isset( $ext['svn-revision'] ) && - preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/', - $ext['svn-revision'], $m ) ) + } elseif ( isset( $ext['svn-revision'] ) && + preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/', + $ext['svn-revision'], $m ) ) { $ret['version'] = 'r' . $m[1]; } @@ -361,7 +398,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { $title = Title::newFromText( $wgRightsPage ); $url = $title ? $title->getFullURL() : $wgRightsUrl; $text = $wgRightsText; - if( !$text && $title ) { + if ( !$text && $title ) { $text = $title->getPrefixedText(); } @@ -373,6 +410,17 @@ class ApiQuerySiteinfo extends ApiQueryBase { return $this->getResult()->addValue( 'query', $property, $data ); } + public function appendLanguages( $property ) { + $data = array(); + foreach ( Language::getLanguageNames() as $code => $name ) { + $lang = array( 'code' => $code ); + ApiResult::setContent( $lang, $name ); + $data[] = $lang; + } + $this->getResult()->setIndexedTagName( $data, 'lang' ); + return $this->getResult()->addValue( 'query', $property, $data ); + } + public function getCacheMode( $params ) { return 'public'; } @@ -395,6 +443,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { 'extensions', 'fileextensions', 'rightsinfo', + 'languages', ) ), 'filteriw' => array( @@ -404,6 +453,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { ) ), 'showalldb' => false, + 'numberingroup' => false, ); } @@ -423,9 +473,11 @@ class ApiQuerySiteinfo extends ApiQueryBase { ' extensions - Returns extensions installed on the wiki', ' fileextensions - Returns list of file extensions allowed to be uploaded', ' rightsinfo - Returns wiki rights (license) information if available', + ' languages - Returns a list of languages MediaWiki supports', ), 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map', 'showalldb' => 'List all database servers, not just the one lagging the most', + 'numberingroup' => 'Lists the number of users in user groups', ); } @@ -433,6 +485,12 @@ class ApiQuerySiteinfo extends ApiQueryBase { return 'Return general information about the site.'; } + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ), + ) ); + } + protected function getExamples() { return array( 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics', @@ -442,6 +500,6 @@ class ApiQuerySiteinfo extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryTags.php b/includes/api/ApiQueryTags.php new file mode 100644 index 00000000..a5d152bc --- /dev/null +++ b/includes/api/ApiQueryTags.php @@ -0,0 +1,181 @@ +extractRequestParams(); + + $prop = array_flip( $params['prop'] ); + + $this->fld_displayname = isset( $prop['displayname'] ); + $this->fld_description = isset( $prop['description'] ); + $this->fld_hitcount = isset( $prop['hitcount'] ); + + $this->limit = $params['limit']; + $this->result = $this->getResult(); + + $pageSet = $this->getPageSet(); + $titles = $pageSet->getTitles(); + $data = array(); + + $this->addTables( 'change_tag' ); + $this->addFields( 'ct_tag' ); + + if ( $this->fld_hitcount ) + $this->addFields( 'count(*) AS hitcount' ); + + $this->addOption( 'LIMIT', $this->limit + 1 ); + $this->addOption( 'GROUP BY', 'ct_tag' ); + $this->addWhereRange( 'ct_tag', 'newer', $params['continue'], null ); + + $res = $this->select( __METHOD__ ); + + $ok = true; + + while ( $row = $res->fetchObject() ) { + if ( !$ok ) break; + $ok = $this->doTag( $row->ct_tag, $row->hitcount ); + } + + // include tags with no hits yet + foreach ( ChangeTags::listDefinedTags() as $tag ) { + if ( !$ok ) break; + $ok = $this->doTag( $tag, 0 ); + } + + $this->result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'tag' ); + } + + private function doTag( $tagName, $hitcount ) { + static $count = 0; + static $doneTags = array(); + + if ( in_array( $tagName, $doneTags ) ) { + return true; + } + + if ( ++$count > $this->limit ) + { + $this->setContinueEnumParameter( 'continue', $tagName ); + return false; + } + + $tag = array(); + $tag['name'] = $tagName; + + if ( $this->fld_displayname ) + $tag['displayname'] = ChangeTags::tagDescription( $tagName ); + + if ( $this->fld_description ) + { + $msg = wfMsg( "tag-$tagName-description" ); + $msg = wfEmptyMsg( "tag-$tagName-description", $msg ) ? '' : $msg; + $tag['description'] = $msg; + } + + if ( $this->fld_hitcount ) + $tag['hitcount'] = $hitcount; + + $doneTags[] = $tagName; + + $fit = $this->result->addValue( array( 'query', $this->getModuleName() ), null, $tag ); + if ( !$fit ) + { + $this->setContinueEnumParameter( 'continue', $tagName ); + return false; + } + + return true; + } + + public function getCacheMode( $params ) { + return 'public'; + } + + public function getAllowedParams() { + return array ( + 'continue' => array( + ), + 'limit' => array( + ApiBase :: PARAM_DFLT => 10, + ApiBase :: PARAM_TYPE => 'limit', + ApiBase :: PARAM_MIN => 1, + ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, + ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + ), + 'prop' => array( + ApiBase :: PARAM_DFLT => 'name', + ApiBase :: PARAM_TYPE => array( + 'name', + 'displayname', + 'description', + 'hitcount' + ), + ApiBase :: PARAM_ISMULTI => true + ) + ); + } + + public function getParamDescription() { + return array ( + 'continue' => 'When more results are available, use this to continue', + 'limit' => 'The maximum number of tags to list', + 'prop' => 'Which properties to get', + ); + } + + public function getDescription() { + return 'List change tags.'; + } + + protected function getExamples() { + return array ( + 'api.php?action=query&list=tags&tgprop=displayname|description|hitcount' + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiQueryTags.php 69932 2010-07-26 08:03:21Z tstarling $'; + } +} diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php index 1c5cffa5..b51b9adb 100644 --- a/includes/api/ApiQueryUserContributions.php +++ b/includes/api/ApiQueryUserContributions.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,33 +35,35 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryContributions extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'uc'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'uc' ); } private $params, $username; private $fld_ids = false, $fld_title = false, $fld_timestamp = false, - $fld_comment = false, $fld_flags = false, - $fld_patrolled = false; + $fld_comment = false, $fld_parsedcomment = false, $fld_flags = false, + $fld_patrolled = false, $fld_tags = false; public function execute() { - // Parse some parameters $this->params = $this->extractRequestParams(); - $prop = array_flip($this->params['prop']); - $this->fld_ids = isset($prop['ids']); - $this->fld_title = isset($prop['title']); - $this->fld_comment = isset($prop['comment']); - $this->fld_flags = isset($prop['flags']); - $this->fld_timestamp = isset($prop['timestamp']); - $this->fld_patrolled = isset($prop['patrolled']); + $prop = array_flip( $this->params['prop'] ); + $this->fld_ids = isset( $prop['ids'] ); + $this->fld_title = isset( $prop['title'] ); + $this->fld_comment = isset( $prop['comment'] ); + $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); + $this->fld_size = isset( $prop['size'] ); + $this->fld_flags = isset( $prop['flags'] ); + $this->fld_timestamp = isset( $prop['timestamp'] ); + $this->fld_patrolled = isset( $prop['patrolled'] ); + $this->fld_tags = isset( $prop['tags'] ); // TODO: if the query is going only against the revision table, should this be done? - $this->selectNamedDB('contributions', DB_SLAVE, 'contributions'); + $this->selectNamedDB( 'contributions', DB_SLAVE, 'contributions' ); $db = $this->getDB(); - if(isset($this->params['userprefix'])) + if ( isset( $this->params['userprefix'] ) ) { $this->prefixMode = true; $this->multiUserMode = true; @@ -70,61 +72,63 @@ class ApiQueryContributions extends ApiQueryBase { else { $this->usernames = array(); - if(!is_array($this->params['user'])) - $this->params['user'] = array($this->params['user']); - foreach($this->params['user'] as $u) - $this->prepareUsername($u); + if ( !is_array( $this->params['user'] ) ) + $this->params['user'] = array( $this->params['user'] ); + if ( !count( $this->params['user'] ) ) + $this->dieUsage( 'User parameter may not be empty.', 'param_user' ); + foreach ( $this->params['user'] as $u ) + $this->prepareUsername( $u ); $this->prefixMode = false; - $this->multiUserMode = (count($this->params['user']) > 1); + $this->multiUserMode = ( count( $this->params['user'] ) > 1 ); } $this->prepareQuery(); - //Do the actual query. + // Do the actual query. $res = $this->select( __METHOD__ ); - //Initialise some variables + // Initialise some variables $count = 0; $limit = $this->params['limit']; - //Fetch each row + // Fetch each row while ( $row = $db->fetchObject( $res ) ) { - if (++ $count > $limit) { + if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - if($this->multiUserMode) - $this->setContinueEnumParameter('continue', $this->continueStr($row)); + if ( $this->multiUserMode ) + $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) ); else - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rev_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rev_timestamp ) ); break; } - $vals = $this->extractRowInfo($row); - $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) + $vals = $this->extractRowInfo( $row ); + $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { - if($this->multiUserMode) - $this->setContinueEnumParameter('continue', $this->continueStr($row)); + if ( $this->multiUserMode ) + $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) ); else - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rev_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rev_timestamp ) ); break; } } - //Free the database record so the connection can get on with other stuff - $db->freeResult($res); + // Free the database record so the connection can get on with other stuff + $db->freeResult( $res ); - $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item'); + $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' ); } /** * Validate the 'user' parameter and set the value to compare * against `revision`.`rev_user_text` */ - private function prepareUsername($user) { - if( $user ) { + private function prepareUsername( $user ) { + if ( !is_null( $user ) && $user !== '' ) { $name = User::isIP( $user ) ? $user : User::getCanonicalName( $user, 'valid' ); - if( $name === false ) { + if ( $name === false ) { $this->dieUsage( "User name {$user} is not valid", 'param_user' ); } else { $this->usernames[] = $name; @@ -140,155 +144,202 @@ class ApiQueryContributions extends ApiQueryBase { private function prepareQuery() { // We're after the revision table, and the corresponding page // row for anything we retrieve. We may also need the - // recentchanges row. - $tables = array('page', 'revision'); // Order may change - $this->addWhere('page_id=rev_page'); + // recentchanges row and/or tag summary row. + global $wgUser; + $tables = array( 'page', 'revision' ); // Order may change + $this->addWhere( 'page_id=rev_page' ); // Handle continue parameter - if($this->multiUserMode && !is_null($this->params['continue'])) + if ( $this->multiUserMode && !is_null( $this->params['continue'] ) ) { - $continue = explode('|', $this->params['continue']); - if(count($continue) != 2) - $this->dieUsage("Invalid continue param. You should pass the original " . - "value returned by the previous query", "_badcontinue"); - $encUser = $this->getDB()->strencode($continue[0]); - $encTS = wfTimestamp(TS_MW, $continue[1]); - $op = ($this->params['dir'] == 'older' ? '<' : '>'); - $this->addWhere("rev_user_text $op '$encUser' OR " . + $continue = explode( '|', $this->params['continue'] ); + if ( count( $continue ) != 2 ) + $this->dieUsage( "Invalid continue param. You should pass the original " . + "value returned by the previous query", "_badcontinue" ); + $encUser = $this->getDB()->strencode( $continue[0] ); + $encTS = wfTimestamp( TS_MW, $continue[1] ); + $op = ( $this->params['dir'] == 'older' ? '<' : '>' ); + $this->addWhere( "rev_user_text $op '$encUser' OR " . "(rev_user_text = '$encUser' AND " . - "rev_timestamp $op= '$encTS')"); + "rev_timestamp $op= '$encTS')" ); } - $this->addWhereFld('rev_deleted', 0); + if ( !$wgUser->isAllowed( 'hideuser' ) ) + $this->addWhere( $this->getDB()->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' ); // We only want pages by the specified users. - if($this->prefixMode) - $this->addWhere("rev_user_text LIKE '" . $this->getDB()->escapeLike($this->userprefix) . "%'"); + if ( $this->prefixMode ) + $this->addWhere( 'rev_user_text' . $this->getDB()->buildLike( $this->userprefix, $this->getDB()->anyString() ) ); else - $this->addWhereFld('rev_user_text', $this->usernames); + $this->addWhereFld( 'rev_user_text', $this->usernames ); // ... and in the specified timeframe. // Ensure the same sort order for rev_user_text and rev_timestamp // so our query is indexed - if($this->multiUserMode) - $this->addWhereRange('rev_user_text', $this->params['dir'], null, null); - $this->addWhereRange('rev_timestamp', + if ( $this->multiUserMode ) + $this->addWhereRange( 'rev_user_text', $this->params['dir'], null, null ); + $this->addWhereRange( 'rev_timestamp', $this->params['dir'], $this->params['start'], $this->params['end'] ); - $this->addWhereFld('page_namespace', $this->params['namespace']); + $this->addWhereFld( 'page_namespace', $this->params['namespace'] ); $show = $this->params['show']; - if (!is_null($show)) { - $show = array_flip($show); - if ((isset($show['minor']) && isset($show['!minor'])) - || (isset($show['patrolled']) && isset($show['!patrolled']))) - $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show'); - - $this->addWhereIf('rev_minor_edit = 0', isset($show['!minor'])); - $this->addWhereIf('rev_minor_edit != 0', isset($show['minor'])); - $this->addWhereIf('rc_patrolled = 0', isset($show['!patrolled'])); - $this->addWhereIf('rc_patrolled != 0', isset($show['patrolled'])); + if ( !is_null( $show ) ) { + $show = array_flip( $show ); + if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) ) + || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) ) + $this->dieUsageMsg( array( 'show' ) ); + + $this->addWhereIf( 'rev_minor_edit = 0', isset( $show['!minor'] ) ); + $this->addWhereIf( 'rev_minor_edit != 0', isset( $show['minor'] ) ); + $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) ); + $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) ); } - $this->addOption('LIMIT', $this->params['limit'] + 1); - $index['revision'] = 'usertext_timestamp'; + $this->addOption( 'LIMIT', $this->params['limit'] + 1 ); + $index = array( 'revision' => 'usertext_timestamp' ); // Mandatory fields: timestamp allows request continuation // ns+title checks if the user has access rights for this page // user_text is necessary if multiple users were specified - $this->addFields(array( + $this->addFields( array( 'rev_timestamp', 'page_namespace', 'page_title', 'rev_user_text', - )); + 'rev_deleted' + ) ); - if(isset($show['patrolled']) || isset($show['!patrolled']) || - $this->fld_patrolled) + if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) || + $this->fld_patrolled ) { global $wgUser; - if(!$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) - $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied'); + if ( !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol() ) + $this->dieUsage( "You need the patrol right to request the patrolled flag", 'permissiondenied' ); // Use a redundant join condition on both // timestamp and ID so we can use the timestamp // index $index['recentchanges'] = 'rc_user_text'; - if(isset($show['patrolled']) || isset($show['!patrolled'])) + if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) { // Put the tables in the right order for // STRAIGHT_JOIN - $tables = array('revision', 'recentchanges', 'page'); - $this->addOption('STRAIGHT_JOIN'); - $this->addWhere('rc_user_text=rev_user_text'); - $this->addWhere('rc_timestamp=rev_timestamp'); - $this->addWhere('rc_this_oldid=rev_id'); + $tables = array( 'revision', 'recentchanges', 'page' ); + $this->addOption( 'STRAIGHT_JOIN' ); + $this->addWhere( 'rc_user_text=rev_user_text' ); + $this->addWhere( 'rc_timestamp=rev_timestamp' ); + $this->addWhere( 'rc_this_oldid=rev_id' ); } else { $tables[] = 'recentchanges'; - $this->addJoinConds(array('recentchanges' => array( + $this->addJoinConds( array( 'recentchanges' => array( 'LEFT JOIN', array( 'rc_user_text=rev_user_text', 'rc_timestamp=rev_timestamp', - 'rc_this_oldid=rev_id')))); + 'rc_this_oldid=rev_id' ) ) ) ); } } - $this->addTables($tables); - $this->addOption('USE INDEX', $index); - $this->addFieldsIf('rev_page', $this->fld_ids); - $this->addFieldsIf('rev_id', $this->fld_ids || $this->fld_flags); - $this->addFieldsIf('page_latest', $this->fld_flags); + $this->addTables( $tables ); + $this->addFieldsIf( 'rev_page', $this->fld_ids ); + $this->addFieldsIf( 'rev_id', $this->fld_ids || $this->fld_flags ); + $this->addFieldsIf( 'page_latest', $this->fld_flags ); // $this->addFieldsIf('rev_text_id', $this->fld_ids); // Should this field be exposed? - $this->addFieldsIf('rev_comment', $this->fld_comment); - $this->addFieldsIf('rev_minor_edit', $this->fld_flags); - $this->addFieldsIf('rev_parent_id', $this->fld_flags); - $this->addFieldsIf('rc_patrolled', $this->fld_patrolled); + $this->addFieldsIf( 'rev_comment', $this->fld_comment || $this->fld_parsedcomment ); + $this->addFieldsIf( 'rev_len', $this->fld_size ); + $this->addFieldsIf( 'rev_minor_edit', $this->fld_flags ); + $this->addFieldsIf( 'rev_parent_id', $this->fld_flags ); + $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled ); + + if ( $this->fld_tags ) + { + $this->addTables( 'tag_summary' ); + $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', array( 'rev_id=ts_rev_id' ) ) ) ); + $this->addFields( 'ts_tags' ); + } + + if ( isset( $this->params['tag'] ) ) { + $this->addTables( 'change_tag' ); + $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'rev_id=ct_rev_id' ) ) ) ); + $this->addWhereFld( 'ct_tag', $this->params['tag'] ); + global $wgOldChangeTagsIndex; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; + } + + $this->addOption( 'USE INDEX', $index ); } /** * Extract fields from the database row and append them to a result array */ - private function extractRowInfo($row) { + private function extractRowInfo( $row ) { $vals = array(); $vals['user'] = $row->rev_user_text; - if ($this->fld_ids) { - $vals['pageid'] = intval($row->rev_page); - $vals['revid'] = intval($row->rev_id); + if ( $row->rev_deleted & Revision::DELETED_USER ) + $vals['userhidden'] = ''; + if ( $this->fld_ids ) { + $vals['pageid'] = intval( $row->rev_page ); + $vals['revid'] = intval( $row->rev_id ); // $vals['textid'] = intval($row->rev_text_id); // todo: Should this field be exposed? } - if ($this->fld_title) - ApiQueryBase :: addTitleInfo($vals, - Title :: makeTitle($row->page_namespace, $row->page_title)); + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); - if ($this->fld_timestamp) - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rev_timestamp); + if ( $this->fld_title ) + ApiQueryBase::addTitleInfo( $vals, $title ); - if ($this->fld_flags) { - if ($row->rev_parent_id == 0) + if ( $this->fld_timestamp ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rev_timestamp ); + + if ( $this->fld_flags ) { + if ( $row->rev_parent_id == 0 && !is_null( $row->rev_parent_id ) ) $vals['new'] = ''; - if ($row->rev_minor_edit) + if ( $row->rev_minor_edit ) $vals['minor'] = ''; - if ($row->page_latest == $row->rev_id) + if ( $row->page_latest == $row->rev_id ) $vals['top'] = ''; } - if ($this->fld_comment && isset($row->rev_comment)) - $vals['comment'] = $row->rev_comment; + if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->rev_comment ) ) { + if ( $row->rev_deleted & Revision::DELETED_COMMENT ) + $vals['commenthidden'] = ''; + else { + if ( $this->fld_comment ) + $vals['comment'] = $row->rev_comment; + + if ( $this->fld_parsedcomment ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->rev_comment, $title ); + } + } + } - if ($this->fld_patrolled && $row->rc_patrolled) + if ( $this->fld_patrolled && $row->rc_patrolled ) $vals['patrolled'] = ''; - + + if ( $this->fld_size && !is_null( $row->rev_len ) ) + $vals['size'] = intval( $row->rev_len ); + + if ( $this->fld_tags ) { + if ( $row->ts_tags ) { + $tags = explode( ',', $row->ts_tags ); + $this->getResult()->setIndexedTagName( $tags, 'tag' ); + $vals['tags'] = $tags; + } else { + $vals['tags'] = array(); + } + } + return $vals; } - private function continueStr($row) + private function continueStr( $row ) { return $row->rev_user_text . '|' . - wfTimestamp(TS_ISO_8601, $row->rev_timestamp); + wfTimestamp( TS_ISO_8601, $row->rev_timestamp ); } public function getCacheMode( $params ) { - // This module provides access to patrol flags if + // This module provides access to deleted revisions and patrol flags if // the requester is logged in return 'anon-public-user-private'; } @@ -326,14 +377,17 @@ class ApiQueryContributions extends ApiQueryBase { ), 'prop' => array ( ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'ids|title|timestamp|flags|comment', + ApiBase :: PARAM_DFLT => 'ids|title|timestamp|comment|size|flags', ApiBase :: PARAM_TYPE => array ( 'ids', 'title', 'timestamp', 'comment', + 'parsedcomment', + 'size', 'flags', 'patrolled', + 'tags' ) ), 'show' => array ( @@ -345,6 +399,7 @@ class ApiQueryContributions extends ApiQueryBase { '!patrolled', ) ), + 'tag' => null, ); } @@ -359,14 +414,24 @@ class ApiQueryContributions extends ApiQueryBase { 'dir' => 'The direction to search (older or newer).', 'namespace' => 'Only list contributions in these namespaces', 'prop' => 'Include additional pieces of information', - 'show' => array('Show only items that meet this criteria, e.g. non minor edits only: show=!minor', - 'NOTE: if show=patrolled or show=!patrolled is set, revisions older than $wgRCMaxAge won\'t be shown',), + 'show' => array( 'Show only items that meet this criteria, e.g. non minor edits only: show=!minor', + 'NOTE: if show=patrolled or show=!patrolled is set, revisions older than $wgRCMaxAge won\'t be shown', ), + 'tag' => 'Only list revisions tagged with this tag', ); } public function getDescription() { return 'Get all edits by a user'; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + 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( 'code' => 'permissiondenied', 'info' => 'You need the patrol right to request the patrolled flag' ), + ) ); + } protected function getExamples() { return array ( @@ -376,6 +441,6 @@ class ApiQueryContributions extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryUserContributions.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryUserContributions.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php index e445c46e..42cb47b9 100644 --- a/includes/api/ApiQueryUserInfo.php +++ b/includes/api/ApiQueryUserInfo.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryUserInfo extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'ui'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'ui' ); } public function execute() { @@ -44,59 +44,76 @@ class ApiQueryUserInfo extends ApiQueryBase { $result = $this->getResult(); $r = array(); - if (!is_null($params['prop'])) { - $this->prop = array_flip($params['prop']); + if ( !is_null( $params['prop'] ) ) { + $this->prop = array_flip( $params['prop'] ); } else { $this->prop = array(); } $r = $this->getCurrentUserInfo(); - $result->addValue("query", $this->getModuleName(), $r); + $result->addValue( "query", $this->getModuleName(), $r ); } protected function getCurrentUserInfo() { global $wgUser; $result = $this->getResult(); $vals = array(); - $vals['id'] = intval($wgUser->getId()); + $vals['id'] = intval( $wgUser->getId() ); $vals['name'] = $wgUser->getName(); - if($wgUser->isAnon()) + if ( $wgUser->isAnon() ) $vals['anon'] = ''; - if (isset($this->prop['blockinfo'])) { - if ($wgUser->isBlocked()) { - $vals['blockedby'] = User::whoIs($wgUser->blockedBy()); + + if ( isset( $this->prop['blockinfo'] ) ) { + if ( $wgUser->isBlocked() ) { + $vals['blockedby'] = User::whoIs( $wgUser->blockedBy() ); $vals['blockreason'] = $wgUser->blockedFor(); } } - if (isset($this->prop['hasmsg']) && $wgUser->getNewtalk()) { + + if ( isset( $this->prop['hasmsg'] ) && $wgUser->getNewtalk() ) { $vals['messages'] = ''; } - if (isset($this->prop['groups'])) { + + if ( isset( $this->prop['groups'] ) ) { $vals['groups'] = $wgUser->getGroups(); - $result->setIndexedTagName($vals['groups'], 'g'); // even if empty + $result->setIndexedTagName( $vals['groups'], 'g' ); // even if empty } - if (isset($this->prop['rights'])) { + + if ( isset( $this->prop['rights'] ) ) { // User::getRights() may return duplicate values, strip them - $vals['rights'] = array_values(array_unique($wgUser->getRights())); - $result->setIndexedTagName($vals['rights'], 'r'); // even if empty + $vals['rights'] = array_values( array_unique( $wgUser->getRights() ) ); + $result->setIndexedTagName( $vals['rights'], 'r' ); // even if empty } - if (isset($this->prop['options'])) { - $vals['options'] = (is_null($wgUser->mOptions) ? User::getDefaultOptions() : $wgUser->mOptions); + + if ( isset( $this->prop['changeablegroups'] ) ) { + $vals['changeablegroups'] = $wgUser->changeableGroups(); + $result->setIndexedTagName( $vals['changeablegroups']['add'], 'g' ); + $result->setIndexedTagName( $vals['changeablegroups']['remove'], 'g' ); + $result->setIndexedTagName( $vals['changeablegroups']['add-self'], 'g' ); + $result->setIndexedTagName( $vals['changeablegroups']['remove-self'], 'g' ); } - if (isset($this->prop['preferencestoken']) && is_null($this->getMain()->getRequest()->getVal('callback'))) { + + if ( isset( $this->prop['options'] ) ) { + $vals['options'] = $wgUser->getOptions(); + } + + if ( isset( $this->prop['preferencestoken'] ) && is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) { $vals['preferencestoken'] = $wgUser->editToken(); } - if (isset($this->prop['editcount'])) { - $vals['editcount'] = intval($wgUser->getEditCount()); + + if ( isset( $this->prop['editcount'] ) ) { + $vals['editcount'] = intval( $wgUser->getEditCount() ); } - if (isset($this->prop['ratelimits'])) { + + if ( isset( $this->prop['ratelimits'] ) ) { $vals['ratelimits'] = $this->getRateLimits(); } - if (isset($this->prop['email'])) { + + if ( isset( $this->prop['email'] ) ) { $vals['email'] = $wgUser->getEmail(); $auth = $wgUser->getEmailAuthenticationTimestamp(); - if(!is_null($auth)) - $vals['emailauthenticated'] = wfTimestamp(TS_ISO_8601, $auth); + if ( !is_null( $auth ) ) + $vals['emailauthenticated'] = wfTimestamp( TS_ISO_8601, $auth ); } return $vals; } @@ -104,32 +121,32 @@ class ApiQueryUserInfo extends ApiQueryBase { protected function getRateLimits() { global $wgUser, $wgRateLimits; - if(!$wgUser->isPingLimitable()) + if ( !$wgUser->isPingLimitable() ) return array(); // No limits // Find out which categories we belong to $categories = array(); - if($wgUser->isAnon()) + if ( $wgUser->isAnon() ) $categories[] = 'anon'; else $categories[] = 'user'; - if($wgUser->isNewBie()) + if ( $wgUser->isNewBie() ) { $categories[] = 'ip'; $categories[] = 'subnet'; - if(!$wgUser->isAnon()) + if ( !$wgUser->isAnon() ) $categories[] = 'newbie'; } - $categories = array_merge($categories, $wgUser->getGroups()); + $categories = array_merge( $categories, $wgUser->getGroups() ); // Now get the actual limits $retval = array(); - foreach($wgRateLimits as $action => $limits) - foreach($categories as $cat) - if(isset($limits[$cat]) && !is_null($limits[$cat])) + foreach ( $wgRateLimits as $action => $limits ) + foreach ( $categories as $cat ) + if ( isset( $limits[$cat] ) && !is_null( $limits[$cat] ) ) { - $retval[$action][$cat]['hits'] = intval($limits[$cat][0]); - $retval[$action][$cat]['seconds'] = intval($limits[$cat][1]); + $retval[$action][$cat]['hits'] = intval( $limits[$cat][0] ); + $retval[$action][$cat]['seconds'] = intval( $limits[$cat][1] ); } return $retval; } @@ -137,13 +154,14 @@ class ApiQueryUserInfo extends ApiQueryBase { public function getAllowedParams() { return array ( 'prop' => array ( - ApiBase :: PARAM_DFLT => NULL, + ApiBase :: PARAM_DFLT => null, ApiBase :: PARAM_ISMULTI => true, ApiBase :: PARAM_TYPE => array ( 'blockinfo', 'hasmsg', 'groups', 'rights', + 'changeablegroups', 'options', 'preferencestoken', 'editcount', @@ -161,7 +179,8 @@ class ApiQueryUserInfo extends ApiQueryBase { ' blockinfo - tags if the current user is blocked, by whom, and for what reason', ' hasmsg - adds a tag "message" if the current user has pending messages', ' groups - lists all the groups the current user belongs to', - ' rights - lists of all rights the current user has', + ' rights - lists all the rights the current user has', + ' changeablegroups - lists the groups the current user can add to and remove from', ' options - lists all preferences the current user has set', ' editcount - adds the current user\'s edit count', ' ratelimits - lists all rate limits applying to the current user' @@ -181,6 +200,6 @@ class ApiQueryUserInfo extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryUserInfo.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryUserInfo.php 69578 2010-07-20 02:46:20Z tstarling $'; } } diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php index 1e50c59a..5dc0e4a6 100644 --- a/includes/api/ApiQueryUsers.php +++ b/includes/api/ApiQueryUsers.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -33,11 +33,40 @@ if (!defined('MEDIAWIKI')) { * * @ingroup API */ - class ApiQueryUsers extends ApiQueryBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'us'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'us' ); + } + + /** + * Get an array mapping token names to their handler functions. + * The prototype for a token function is func($user) + * it should return a token or false (permission denied) + * @return array(tokenname => function) + */ + protected function getTokenFunctions() { + // Don't call the hooks twice + if ( isset( $this->tokenFunctions ) ) + return $this->tokenFunctions; + + // If we're in JSON callback mode, no tokens can be obtained + if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) + return array(); + + $this->tokenFunctions = array( + 'userrights' => array( 'ApiQueryUsers', 'getUserrightsToken' ), + ); + wfRunHooks( 'APIQueryUsersTokens', array( &$this->tokenFunctions ) ); + return $this->tokenFunctions; + } + + public static function getUserrightsToken( $user ) + { + global $wgUser; + // Since the permissions check for userrights is non-trivial, + // don't bother with it here + return $wgUser->editToken( $user->getName() ); } public function execute() { @@ -45,8 +74,8 @@ if (!defined('MEDIAWIKI')) { $result = $this->getResult(); $r = array(); - if (!is_null($params['prop'])) { - $this->prop = array_flip($params['prop']); + if ( !is_null( $params['prop'] ) ) { + $this->prop = array_flip( $params['prop'] ); } else { $this->prop = array(); } @@ -55,17 +84,17 @@ if (!defined('MEDIAWIKI')) { $goodNames = $done = array(); $result = $this->getResult(); // Canonicalize user names - foreach($users as $u) { - $n = User::getCanonicalName($u); - if($n === false || $n === '') + foreach ( $users as $u ) { + $n = User::getCanonicalName( $u ); + if ( $n === false || $n === '' ) { - $vals = array('name' => $u, 'invalid' => ''); - $fit = $result->addValue(array('query', $this->getModuleName()), - null, $vals); - if(!$fit) + $vals = array( 'name' => $u, 'invalid' => '' ); + $fit = $result->addValue( array( 'query', $this->getModuleName() ), + null, $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('users', - implode('|', array_diff($users, $done))); + $this->setContinueEnumParameter( 'users', + implode( '|', array_diff( $users, $done ) ) ); $goodNames = array(); break; } @@ -74,78 +103,122 @@ if (!defined('MEDIAWIKI')) { else $goodNames[] = $n; } - if(count($goodNames)) + + if ( count( $goodNames ) ) { $db = $this->getDb(); - $this->addTables('user', 'u1'); - $this->addFields('u1.*'); - $this->addWhereFld('u1.user_name', $goodNames); - - if(isset($this->prop['groups'])) { - $this->addTables('user_groups'); - $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id'))); - $this->addFields('ug_group'); + $this->addTables( 'user', 'u1' ); + $this->addFields( 'u1.*' ); + $this->addWhereFld( 'u1.user_name', $goodNames ); + + if ( isset( $this->prop['groups'] ) ) { + $this->addTables( 'user_groups' ); + $this->addJoinConds( array( 'user_groups' => array( 'LEFT JOIN', 'ug_user=u1.user_id' ) ) ); + $this->addFields( 'ug_group' ); } - if(isset($this->prop['blockinfo'])) { - $this->addTables('ipblocks'); - $this->addTables('user', 'u2'); - $u2 = $this->getAliasedName('user', 'u2'); - $this->addJoinConds(array( - 'ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'), - $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id'))); - $this->addFields(array('ipb_reason', 'u2.user_name AS blocker_name')); + if ( isset( $this->prop['blockinfo'] ) ) { + $this->addTables( 'ipblocks' ); + $this->addTables( 'user', 'u2' ); + $u2 = $this->getAliasedName( 'user', 'u2' ); + $this->addJoinConds( array( + 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=u1.user_id' ), + $u2 => array( 'LEFT JOIN', 'ipb_by=u2.user_id' ) ) ); + $this->addFields( array( 'ipb_reason', 'u2.user_name AS blocker_name' ) ); } $data = array(); - $res = $this->select(__METHOD__); - while(($r = $db->fetchObject($res))) { - $user = User::newFromRow($r); + $res = $this->select( __METHOD__ ); + while ( ( $r = $db->fetchObject( $res ) ) ) { + $user = User::newFromRow( $r ); $name = $user->getName(); $data[$name]['name'] = $name; - if(isset($this->prop['editcount'])) - $data[$name]['editcount'] = intval($user->getEditCount()); - if(isset($this->prop['registration'])) - $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $user->getRegistration()); - if(isset($this->prop['groups']) && !is_null($r->ug_group)) + if ( isset( $this->prop['editcount'] ) ) + $data[$name]['editcount'] = intval( $user->getEditCount() ); + if ( isset( $this->prop['registration'] ) ) + $data[$name]['registration'] = wfTimestampOrNull( TS_ISO_8601, $user->getRegistration() ); + if ( isset( $this->prop['groups'] ) && !is_null( $r->ug_group ) ) // This row contains only one group, others will be added from other rows $data[$name]['groups'][] = $r->ug_group; - if(isset($this->prop['blockinfo']) && !is_null($r->blocker_name)) { + if ( isset( $this->prop['blockinfo'] ) && !is_null( $r->blocker_name ) ) { $data[$name]['blockedby'] = $r->blocker_name; $data[$name]['blockreason'] = $r->ipb_reason; } - if(isset($this->prop['emailable']) && $user->canReceiveEmail()) + if ( isset( $this->prop['emailable'] ) && $user->canReceiveEmail() ) $data[$name]['emailable'] = ''; + + if ( isset( $this->prop['gender'] ) ) { + $gender = $user->getOption( 'gender' ); + if ( strval( $gender ) === '' ) { + $gender = 'unknown'; + } + $data[$name]['gender'] = $gender; + } + + if ( !is_null( $params['token'] ) ) + { + $tokenFunctions = $this->getTokenFunctions(); + foreach ( $params['token'] as $t ) + { + $val = call_user_func( $tokenFunctions[$t], $user ); + if ( $val === false ) + $this->setWarning( "Action '$t' is not allowed for the current user" ); + else + $data[$name][$t . 'token'] = $val; + } + } } } // Second pass: add result data to $retval - foreach($goodNames as $u) { - if(!isset($data[$u])) - $data[$u] = array('name' => $u, 'missing' => ''); - else { - if(isset($this->prop['groups']) && isset($data[$u]['groups'])) - $this->getResult()->setIndexedTagName($data[$u]['groups'], 'g'); + foreach ( $goodNames as $u ) { + if ( !isset( $data[$u] ) ) { + $data[$u] = array( 'name' => $u ); + $urPage = new UserrightsPage; + $iwUser = $urPage->fetchUser( $u ); + if ( $iwUser instanceof UserRightsProxy ) { + $data[$u]['interwiki'] = ''; + if ( !is_null( $params['token'] ) ) + { + $tokenFunctions = $this->getTokenFunctions(); + foreach ( $params['token'] as $t ) + { + $val = call_user_func( $tokenFunctions[$t], $iwUser ); + if ( $val === false ) + $this->setWarning( "Action '$t' is not allowed for the current user" ); + else + $data[$u][$t . 'token'] = $val; + } + } + } else + $data[$u]['missing'] = ''; + } else { + if ( isset( $this->prop['groups'] ) && isset( $data[$u]['groups'] ) ) + $this->getResult()->setIndexedTagName( $data[$u]['groups'], 'g' ); } - $fit = $result->addValue(array('query', $this->getModuleName()), - null, $data[$u]); - if(!$fit) + $fit = $result->addValue( array( 'query', $this->getModuleName() ), + null, $data[$u] ); + if ( !$fit ) { - $this->setContinueEnumParameter('users', - implode('|', array_diff($users, $done))); + $this->setContinueEnumParameter( 'users', + implode( '|', array_diff( $users, $done ) ) ); break; } $done[] = $u; } - return $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'user'); + return $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'user' ); } public function getCacheMode( $params ) { - return 'public'; + if ( isset( $params['token'] ) ) { + return 'private'; + } else { + return 'public'; + } } public function getAllowedParams() { return array ( 'prop' => array ( - ApiBase :: PARAM_DFLT => NULL, + ApiBase :: PARAM_DFLT => null, ApiBase :: PARAM_ISMULTI => true, ApiBase :: PARAM_TYPE => array ( 'blockinfo', @@ -153,11 +226,16 @@ if (!defined('MEDIAWIKI')) { 'editcount', 'registration', 'emailable', + 'gender', ) ), 'users' => array( ApiBase :: PARAM_ISMULTI => true - ) + ), + 'token' => array( + ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ), + ApiBase :: PARAM_ISMULTI => true + ), ); } @@ -170,8 +248,10 @@ if (!defined('MEDIAWIKI')) { ' editcount - adds the user\'s edit count', ' registration - adds the user\'s registration timestamp', ' emailable - tags if the user can and wants to receive e-mail through [[Special:Emailuser]]', + ' gender - tags the gender of the user. Returns "male", "female", or "unknown"', ), - 'users' => 'A list of users to obtain the same information for' + 'users' => 'A list of users to obtain the same information for', + 'token' => 'Which tokens to obtain for each user', ); } @@ -180,10 +260,10 @@ if (!defined('MEDIAWIKI')) { } protected function getExamples() { - return 'api.php?action=query&list=users&ususers=brion|TimStarling&usprop=groups|editcount'; + return 'api.php?action=query&list=users&ususers=brion|TimStarling&usprop=groups|editcount|gender'; } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryUsers.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryUsers.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php index eb5c531f..caac0706 100644 --- a/includes/api/ApiQueryWatchlist.php +++ b/includes/api/ApiQueryWatchlist.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -36,221 +36,244 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryWatchlist extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'wl'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'wl' ); } public function execute() { $this->run(); } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } private $fld_ids = false, $fld_title = false, $fld_patrol = false, $fld_flags = false, - $fld_timestamp = false, $fld_user = false, $fld_comment = false, $fld_sizes = false; + $fld_timestamp = false, $fld_user = false, $fld_comment = false, $fld_parsedcomment = false, $fld_sizes = false, + $fld_notificationtimestamp = false; - private function run($resultPageSet = null) { - global $wgUser, $wgDBtype; + private function run( $resultPageSet = null ) { + global $wgUser; - $this->selectNamedDB('watchlist', DB_SLAVE, 'watchlist'); - - if (!$wgUser->isLoggedIn()) - $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin'); + $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' ); $params = $this->extractRequestParams(); - if (!is_null($params['prop']) && is_null($resultPageSet)) { + if ( !is_null( $params['owner'] ) && !is_null( $params['token'] ) ) { + $user = User::newFromName( $params['owner'], false ); + if ( !$user->getId() ) { + $this->dieUsage( 'Specified user does not exist', 'bad_wlowner' ); + } + $token = $user->getOption( 'watchlisttoken' ); + if ( $token == '' || $token != $params['token'] ) { + $this->dieUsage( 'Incorrect watchlist token provided -- please set a correct token in Special:Preferences', 'bad_wltoken' ); + } + } elseif ( !$wgUser->isLoggedIn() ) { + $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' ); + } else { + $user = $wgUser; + } + + if ( !is_null( $params['prop'] ) && is_null( $resultPageSet ) ) { - $prop = array_flip($params['prop']); + $prop = array_flip( $params['prop'] ); - $this->fld_ids = isset($prop['ids']); - $this->fld_title = isset($prop['title']); - $this->fld_flags = isset($prop['flags']); - $this->fld_user = isset($prop['user']); - $this->fld_comment = isset($prop['comment']); - $this->fld_timestamp = isset($prop['timestamp']); - $this->fld_sizes = isset($prop['sizes']); - $this->fld_patrol = isset($prop['patrol']); + $this->fld_ids = isset( $prop['ids'] ); + $this->fld_title = isset( $prop['title'] ); + $this->fld_flags = isset( $prop['flags'] ); + $this->fld_user = isset( $prop['user'] ); + $this->fld_comment = isset( $prop['comment'] ); + $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); + $this->fld_timestamp = isset( $prop['timestamp'] ); + $this->fld_sizes = isset( $prop['sizes'] ); + $this->fld_patrol = isset( $prop['patrol'] ); + $this->fld_notificationtimestamp = isset( $prop['notificationtimestamp'] ); - if ($this->fld_patrol) { - global $wgUser; - if (!$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) - $this->dieUsage('patrol property is not available', 'patrol'); + if ( $this->fld_patrol ) { + if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) + $this->dieUsage( 'patrol property is not available', 'patrol' ); } } - - if (is_null($resultPageSet)) { - $this->addFields(array ( + + $this->addFields( array ( + 'rc_namespace', + 'rc_title', + 'rc_timestamp' + ) ); + + if ( is_null( $resultPageSet ) ) { + $this->addFields( array ( 'rc_cur_id', - 'rc_this_oldid', - 'rc_namespace', - 'rc_title', - 'rc_timestamp' - )); - - $this->addFieldsIf('rc_new', $this->fld_flags); - $this->addFieldsIf('rc_minor', $this->fld_flags); - $this->addFieldsIf('rc_bot', $this->fld_flags); - $this->addFieldsIf('rc_user', $this->fld_user); - $this->addFieldsIf('rc_user_text', $this->fld_user); - $this->addFieldsIf('rc_comment', $this->fld_comment); - $this->addFieldsIf('rc_patrolled', $this->fld_patrol); - $this->addFieldsIf('rc_old_len', $this->fld_sizes); - $this->addFieldsIf('rc_new_len', $this->fld_sizes); - } - elseif ($params['allrev']) { - $this->addFields(array ( - 'rc_this_oldid', - 'rc_namespace', - 'rc_title', - 'rc_timestamp' - )); + 'rc_this_oldid' + ) ); + + $this->addFieldsIf( 'rc_new', $this->fld_flags ); + $this->addFieldsIf( 'rc_minor', $this->fld_flags ); + $this->addFieldsIf( 'rc_bot', $this->fld_flags ); + $this->addFieldsIf( 'rc_user', $this->fld_user ); + $this->addFieldsIf( 'rc_user_text', $this->fld_user ); + $this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment ); + $this->addFieldsIf( 'rc_patrolled', $this->fld_patrol ); + $this->addFieldsIf( 'rc_old_len', $this->fld_sizes ); + $this->addFieldsIf( 'rc_new_len', $this->fld_sizes ); + $this->addFieldsIf( 'wl_notificationtimestamp', $this->fld_notificationtimestamp ); + } elseif ( $params['allrev'] ) { + $this->addFields( 'rc_this_oldid' ); } else { - $this->addFields(array ( - 'rc_cur_id', - 'rc_namespace', - 'rc_title', - 'rc_timestamp' - )); + $this->addFields( 'rc_cur_id' ); } - $this->addTables(array ( + $this->addTables( array ( 'watchlist', 'page', 'recentchanges' - )); + ) ); - $userId = $wgUser->getId(); - $this->addWhere(array ( + $userId = $user->getId(); + $this->addWhere( array ( 'wl_namespace = rc_namespace', 'wl_title = rc_title', 'rc_cur_id = page_id', 'wl_user' => $userId, 'rc_deleted' => 0, - )); + ) ); - $this->addWhereRange('rc_timestamp', $params['dir'], $params['start'], $params['end']); - $this->addWhereFld('wl_namespace', $params['namespace']); - $this->addWhereIf('rc_this_oldid=page_latest', !$params['allrev']); + $this->addWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] ); + $this->addWhereFld( 'wl_namespace', $params['namespace'] ); + $this->addWhereIf( 'rc_this_oldid=page_latest', !$params['allrev'] ); - if (!is_null($params['show'])) { - $show = array_flip($params['show']); + if ( !is_null( $params['show'] ) ) { + $show = array_flip( $params['show'] ); /* Check for conflicting parameters. */ - if ((isset ($show['minor']) && isset ($show['!minor'])) - || (isset ($show['bot']) && isset ($show['!bot'])) - || (isset ($show['anon']) && isset ($show['!anon'])) - || (isset ($show['patrolled']) && isset ($show['!patrolled']))) { + if ( ( isset ( $show['minor'] ) && isset ( $show['!minor'] ) ) + || ( isset ( $show['bot'] ) && isset ( $show['!bot'] ) ) + || ( isset ( $show['anon'] ) && isset ( $show['!anon'] ) ) + || ( isset ( $show['patrolled'] ) && isset ( $show['!patrolled'] ) ) ) { - $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show'); + $this->dieUsageMsg( array( 'show' ) ); } - // Check permissions - global $wgUser; - if((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) - $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied'); + // Check permissions. + if ( ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol() ) + $this->dieUsage( "You need the patrol right to request the patrolled flag", 'permissiondenied' ); /* Add additional conditions to query depending upon parameters. */ - $this->addWhereIf('rc_minor = 0', isset ($show['!minor'])); - $this->addWhereIf('rc_minor != 0', isset ($show['minor'])); - $this->addWhereIf('rc_bot = 0', isset ($show['!bot'])); - $this->addWhereIf('rc_bot != 0', isset ($show['bot'])); - $this->addWhereIf('rc_user = 0', isset ($show['anon'])); - $this->addWhereIf('rc_user != 0', isset ($show['!anon'])); - $this->addWhereIf('rc_patrolled = 0', isset($show['!patrolled'])); - $this->addWhereIf('rc_patrolled != 0', isset($show['patrolled'])); + $this->addWhereIf( 'rc_minor = 0', isset ( $show['!minor'] ) ); + $this->addWhereIf( 'rc_minor != 0', isset ( $show['minor'] ) ); + $this->addWhereIf( 'rc_bot = 0', isset ( $show['!bot'] ) ); + $this->addWhereIf( 'rc_bot != 0', isset ( $show['bot'] ) ); + $this->addWhereIf( 'rc_user = 0', isset ( $show['anon'] ) ); + $this->addWhereIf( 'rc_user != 0', isset ( $show['!anon'] ) ); + $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) ); + $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) ); } + if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) + $this->dieUsage( 'user and excludeuser cannot be used together', 'user-excludeuser' ); + if ( !is_null( $params['user'] ) ) + $this->addWhereFld( 'rc_user_text', $params['user'] ); + if ( !is_null( $params['excludeuser'] ) ) + $this->addWhere( 'rc_user_text != ' . $this->getDB()->addQuotes( $params['excludeuser'] ) ); - # This is an index optimization for mysql, as done in the Special:Watchlist page - $this->addWhereIf("rc_timestamp > ''", !isset ($params['start']) && !isset ($params['end']) && $wgDBtype == 'mysql'); + $db = $this->getDB(); + + // This is an index optimization for mysql, as done in the Special:Watchlist page + $this->addWhereIf( "rc_timestamp > ''", !isset ( $params['start'] ) && !isset ( $params['end'] ) && $db->getType() == 'mysql' ); - $this->addOption('LIMIT', $params['limit'] +1); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); $ids = array (); $count = 0; - $res = $this->select(__METHOD__); + $res = $this->select( __METHOD__ ); - $db = $this->getDB(); - while ($row = $db->fetchObject($res)) { - if (++ $count > $params['limit']) { + while ( $row = $db->fetchObject( $res ) ) { + if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp)); + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); break; } - if (is_null($resultPageSet)) { - $vals = $this->extractRowInfo($row); - $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals); - if(!$fit) + if ( is_null( $resultPageSet ) ) { + $vals = $this->extractRowInfo( $row ); + $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { - $this->setContinueEnumParameter('start', - wfTimestamp(TS_ISO_8601, $row->rc_timestamp)); + $this->setContinueEnumParameter( 'start', + wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); break; } } else { - if ($params['allrev']) { - $ids[] = intval($row->rc_this_oldid); + if ( $params['allrev'] ) { + $ids[] = intval( $row->rc_this_oldid ); } else { - $ids[] = intval($row->rc_cur_id); + $ids[] = intval( $row->rc_cur_id ); } } } - $db->freeResult($res); + $db->freeResult( $res ); - if (is_null($resultPageSet)) { - $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item'); + if ( is_null( $resultPageSet ) ) { + $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' ); } - elseif ($params['allrev']) { - $resultPageSet->populateFromRevisionIDs($ids); + elseif ( $params['allrev'] ) { + $resultPageSet->populateFromRevisionIDs( $ids ); } else { - $resultPageSet->populateFromPageIDs($ids); + $resultPageSet->populateFromPageIDs( $ids ); } } - private function extractRowInfo($row) { + private function extractRowInfo( $row ) { $vals = array (); - if ($this->fld_ids) { - $vals['pageid'] = intval($row->rc_cur_id); - $vals['revid'] = intval($row->rc_this_oldid); + if ( $this->fld_ids ) { + $vals['pageid'] = intval( $row->rc_cur_id ); + $vals['revid'] = intval( $row->rc_this_oldid ); } - if ($this->fld_title) - ApiQueryBase :: addTitleInfo($vals, Title :: makeTitle($row->rc_namespace, $row->rc_title)); + $title = Title::makeTitle( $row->rc_namespace, $row->rc_title ); + + if ( $this->fld_title ) + ApiQueryBase::addTitleInfo( $vals, $title ); - if ($this->fld_user) { + if ( $this->fld_user ) { $vals['user'] = $row->rc_user_text; - if (!$row->rc_user) + if ( !$row->rc_user ) $vals['anon'] = ''; } - if ($this->fld_flags) { - if ($row->rc_new) + if ( $this->fld_flags ) { + if ( $row->rc_new ) $vals['new'] = ''; - if ($row->rc_minor) + if ( $row->rc_minor ) $vals['minor'] = ''; - if ($row->rc_bot) + if ( $row->rc_bot ) $vals['bot'] = ''; } - if ($this->fld_patrol && isset($row->rc_patrolled)) + if ( $this->fld_patrol && isset( $row->rc_patrolled ) ) $vals['patrolled'] = ''; - if ($this->fld_timestamp) - $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp); + if ( $this->fld_timestamp ) + $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp ); - if ($this->fld_sizes) { - $vals['oldlen'] = intval($row->rc_old_len); - $vals['newlen'] = intval($row->rc_new_len); + if ( $this->fld_sizes ) { + $vals['oldlen'] = intval( $row->rc_old_len ); + $vals['newlen'] = intval( $row->rc_new_len ); } + + if ( $this->fld_notificationtimestamp ) + $vals['notificationtimestamp'] = ( $row->wl_notificationtimestamp == null ) ? '' : wfTimestamp( TS_ISO_8601, $row->wl_notificationtimestamp ); - if ($this->fld_comment && isset( $row->rc_comment )) + if ( $this->fld_comment && isset( $row->rc_comment ) ) $vals['comment'] = $row->rc_comment; + + if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->rc_comment, $title ); + } return $vals; } @@ -268,6 +291,12 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { ApiBase :: PARAM_ISMULTI => true, ApiBase :: PARAM_TYPE => 'namespace' ), + 'user' => array( + ApiBase :: PARAM_TYPE => 'user', + ), + 'excludeuser' => array( + ApiBase :: PARAM_TYPE => 'user', + ), 'dir' => array ( ApiBase :: PARAM_DFLT => 'older', ApiBase :: PARAM_TYPE => array ( @@ -291,9 +320,11 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'flags', 'user', 'comment', + 'parsedcomment', 'timestamp', 'patrol', 'sizes', + 'notificationtimestamp' ) ), 'show' => array ( @@ -308,6 +339,12 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'patrolled', '!patrolled', ) + ), + 'owner' => array ( + ApiBase :: PARAM_TYPE => 'user' + ), + 'token' => array ( + ApiBase :: PARAM_TYPE => 'string' ) ); } @@ -318,19 +355,35 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'start' => 'The timestamp to start enumerating from.', 'end' => 'The timestamp to end enumerating.', 'namespace' => 'Filter changes to only the given namespace(s).', + 'user' => 'Only list changes by this user', + 'excludeuser' => 'Don\'t list changes by this user', 'dir' => 'In which direction to enumerate pages.', 'limit' => 'How many total results to return per request.', 'prop' => 'Which additional items to get (non-generator mode only).', 'show' => array ( 'Show only items that meet this criteria.', 'For example, to see only minor edits done by logged-in users, set show=minor|!anon' - ) + ), + '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" ); } public function getDescription() { return "Get all recent changes to pages in the logged in user's watchlist"; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'bad_wlowner', 'info' => 'Specified user does not exist' ), + array( 'code' => 'bad_wltoken', 'info' => 'Incorrect watchlist token provided -- please set a correct token in Special:Preferences' ), + array( 'code' => 'notloggedin', 'info' => 'You must be logged-in to have a watchlist' ), + array( 'code' => 'patrol', 'info' => 'patrol property is not available' ), + array( 'show' ), + array( 'code' => 'permissiondenied', 'info' => 'You need the patrol right to request the patrolled flag' ), + array( 'code' => 'user-excludeuser', 'info' => 'user and excludeuser cannot be used together' ), + ) ); + } protected function getExamples() { return array ( @@ -338,11 +391,12 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'api.php?action=query&list=watchlist&wlprop=ids|title|timestamp|user|comment', 'api.php?action=query&list=watchlist&wlallrev&wlprop=ids|title|timestamp|user|comment', 'api.php?action=query&generator=watchlist&prop=info', - 'api.php?action=query&generator=watchlist&gwlallrev&prop=revisions&rvprop=timestamp|user' + 'api.php?action=query&generator=watchlist&gwlallrev&prop=revisions&rvprop=timestamp|user', + 'api.php?action=query&list=watchlist&wlowner=Bob_Smith&wltoken=d8d562e9725ea1512894cdab28e5ceebc7f20237' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryWatchlist.php 69986 2010-07-27 03:57:39Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryWatchlist.php 69932 2010-07-26 08:03:21Z tstarling $'; } } diff --git a/includes/api/ApiQueryWatchlistRaw.php b/includes/api/ApiQueryWatchlistRaw.php index f3982bcb..42d4005b 100644 --- a/includes/api/ApiQueryWatchlistRaw.php +++ b/includes/api/ApiQueryWatchlistRaw.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); + require_once ( 'ApiQueryBase.php' ); } /** @@ -36,92 +36,95 @@ if (!defined('MEDIAWIKI')) { */ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase { - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'wr'); + public function __construct( $query, $moduleName ) { + parent :: __construct( $query, $moduleName, 'wr' ); } public function execute() { $this->run(); } - public function executeGenerator($resultPageSet) { - $this->run($resultPageSet); + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); } - private function run($resultPageSet = null) { + private function run( $resultPageSet = null ) { global $wgUser; - $this->selectNamedDB('watchlist', DB_SLAVE, 'watchlist'); + $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' ); - if (!$wgUser->isLoggedIn()) - $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin'); + if ( !$wgUser->isLoggedIn() ) + $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' ); $params = $this->extractRequestParams(); - $prop = array_flip((array)$params['prop']); - $show = array_flip((array)$params['show']); - if(isset($show['changed']) && isset($show['!changed'])) - $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show'); - - $this->addTables('watchlist'); - $this->addFields(array('wl_namespace', 'wl_title')); - $this->addFieldsIf('wl_notificationtimestamp', isset($prop['changed'])); - $this->addWhereFld('wl_user', $wgUser->getId()); - $this->addWhereFld('wl_namespace', $params['namespace']); - $this->addWhereIf('wl_notificationtimestamp IS NOT NULL', isset($show['changed'])); - $this->addWhereIf('wl_notificationtimestamp IS NULL', isset($show['!changed'])); - if(isset($params['continue'])) + $prop = array_flip( (array)$params['prop'] ); + $show = array_flip( (array)$params['show'] ); + if ( isset( $show['changed'] ) && isset( $show['!changed'] ) ) + $this->dieUsageMsg( array( 'show' ) ); + + $this->addTables( 'watchlist' ); + $this->addFields( array( 'wl_namespace', 'wl_title' ) ); + $this->addFieldsIf( 'wl_notificationtimestamp', isset( $prop['changed'] ) ); + $this->addWhereFld( 'wl_user', $wgUser->getId() ); + $this->addWhereFld( 'wl_namespace', $params['namespace'] ); + $this->addWhereIf( 'wl_notificationtimestamp IS NOT NULL', isset( $show['changed'] ) ); + $this->addWhereIf( 'wl_notificationtimestamp IS NULL', isset( $show['!changed'] ) ); + + if ( isset( $params['continue'] ) ) { - $cont = explode('|', $params['continue']); - if(count($cont) != 2) - $this->dieUsage("Invalid continue param. You should pass the " . - "original value returned by the previous query", "_badcontinue"); - $ns = intval($cont[0]); - $title = $this->getDB()->strencode($this->titleToKey($cont[1])); - $this->addWhere("wl_namespace > '$ns' OR ". - "(wl_namespace = '$ns' AND ". - "wl_title >= '$title')"); + $cont = explode( '|', $params['continue'] ); + if ( count( $cont ) != 2 ) + $this->dieUsage( "Invalid continue param. You should pass the " . + "original value returned by the previous query", "_badcontinue" ); + $ns = intval( $cont[0] ); + $title = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) ); + $this->addWhere( "wl_namespace > '$ns' OR " . + "(wl_namespace = '$ns' AND " . + "wl_title >= '$title')" ); } + // Don't ORDER BY wl_namespace if it's constant in the WHERE clause - if(count($params['namespace']) == 1) - $this->addOption('ORDER BY', 'wl_title'); + if ( count( $params['namespace'] ) == 1 ) + $this->addOption( 'ORDER BY', 'wl_title' ); else - $this->addOption('ORDER BY', 'wl_namespace, wl_title'); - $this->addOption('LIMIT', $params['limit'] + 1); - $res = $this->select(__METHOD__); + $this->addOption( 'ORDER BY', 'wl_namespace, wl_title' ); + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + $res = $this->select( __METHOD__ ); $db = $this->getDB(); $titles = array(); $count = 0; - while($row = $db->fetchObject($res)) + while ( $row = $db->fetchObject( $res ) ) { - if(++$count > $params['limit']) + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - $this->setContinueEnumParameter('continue', $row->wl_namespace . '|' . - $this->keyToTitle($row->wl_title)); + $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . + $this->keyToTitle( $row->wl_title ) ); break; } - $t = Title::makeTitle($row->wl_namespace, $row->wl_title); - if(is_null($resultPageSet)) + $t = Title::makeTitle( $row->wl_namespace, $row->wl_title ); + + if ( is_null( $resultPageSet ) ) { $vals = array(); - ApiQueryBase::addTitleInfo($vals, $t); - if(isset($prop['changed']) && !is_null($row->wl_notificationtimestamp)) - $vals['changed'] = wfTimestamp(TS_ISO_8601, $row->wl_notificationtimestamp); - $fit = $this->getResult()->addValue($this->getModuleName(), null, $vals); - if(!$fit) + ApiQueryBase::addTitleInfo( $vals, $t ); + if ( isset( $prop['changed'] ) && !is_null( $row->wl_notificationtimestamp ) ) + $vals['changed'] = wfTimestamp( TS_ISO_8601, $row->wl_notificationtimestamp ); + $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 . '|' . + $this->keyToTitle( $row->wl_title ) ); break; } } else $titles[] = $t; } - if(is_null($resultPageSet)) - $this->getResult()->setIndexedTagName_internal($this->getModuleName(), 'wr'); + if ( is_null( $resultPageSet ) ) + $this->getResult()->setIndexedTagName_internal( $this->getModuleName(), 'wr' ); else - $resultPageSet->populateFromTitles($titles); + $resultPageSet->populateFromTitles( $titles ); } public function getAllowedParams() { @@ -167,6 +170,13 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase { public function getDescription() { return "Get all pages on the logged in user's watchlist"; } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'notloggedin', 'info' => 'You must be logged-in to have a watchlist' ), + array( 'show' ), + ) ); + } protected function getExamples() { return array ( @@ -176,6 +186,6 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryWatchlistRaw.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryWatchlistRaw.php 69578 2010-07-20 02:46:20Z tstarling $'; } } \ No newline at end of file diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php index 3dbee08a..64c2c3fb 100644 --- a/includes/api/ApiResult.php +++ b/includes/api/ApiResult.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -53,8 +53,8 @@ class ApiResult extends ApiBase { * Constructor * @param $main ApiMain object */ - public function __construct($main) { - parent :: __construct($main, 'result'); + public function __construct( $main ) { + parent :: __construct( $main, 'result' ); $this->mIsRawMode = false; $this->mCheckingSize = true; $this->reset(); @@ -91,21 +91,21 @@ class ApiResult extends ApiBase { public function getData() { return $this->mData; } - + /** * Get the 'real' size of a result item. This means the strlen() of the item, * or the sum of the strlen()s of the elements if the item is an array. * @param $value mixed * @return int */ - public static function size($value) { + public static function size( $value ) { $s = 0; - if(is_array($value)) - foreach($value as $v) - $s += self::size($v); - else if(!is_object($value)) + if ( is_array( $value ) ) + foreach ( $value as $v ) + $s += self::size( $v ); + else if ( !is_object( $value ) ) // Objects can't always be cast to string - $s = strlen($value); + $s = strlen( $value ); return $s; } @@ -116,7 +116,7 @@ class ApiResult extends ApiBase { public function getSize() { return $this->mSize; } - + /** * Disable size checking in addValue(). Don't use this unless you * REALLY know what you're doing. Values added while size checking @@ -125,7 +125,7 @@ class ApiResult extends ApiBase { public function disableSizeCheck() { $this->mCheckingSize = false; } - + /** * Re-enable size checking in addValue() */ @@ -140,21 +140,21 @@ class ApiResult extends ApiBase { * @param $name string Index of $arr to add $value at * @param $value mixed */ - public static function setElement(& $arr, $name, $value) { - if ($arr === null || $name === null || $value === null || !is_array($arr) || is_array($name)) - ApiBase :: dieDebug(__METHOD__, 'Bad parameter'); + public static function setElement( & $arr, $name, $value ) { + if ( $arr === null || $name === null || $value === null || !is_array( $arr ) || is_array( $name ) ) + ApiBase :: dieDebug( __METHOD__, 'Bad parameter' ); - if (!isset ($arr[$name])) { + if ( !isset ( $arr[$name] ) ) { $arr[$name] = $value; } - elseif (is_array($arr[$name]) && is_array($value)) { - $merged = array_intersect_key($arr[$name], $value); - if (!count($merged)) + elseif ( is_array( $arr[$name] ) && is_array( $value ) ) { + $merged = array_intersect_key( $arr[$name], $value ); + if ( !count( $merged ) ) $arr[$name] += $value; else - ApiBase :: dieDebug(__METHOD__, "Attempting to merge element $name"); + ApiBase :: dieDebug( __METHOD__, "Attempting to merge element $name" ); } else - ApiBase :: dieDebug(__METHOD__, "Attempting to add element $name=$value, existing value is {$arr[$name]}"); + ApiBase :: dieDebug( __METHOD__, "Attempting to add element $name=$value, existing value is {$arr[$name]}" ); } /** @@ -165,15 +165,15 @@ class ApiResult extends ApiBase { * as a sub item of $arr. Use this parameter to create elements in * format text without attributes */ - public static function setContent(& $arr, $value, $subElemName = null) { - if (is_array($value)) - ApiBase :: dieDebug(__METHOD__, 'Bad parameter'); - if (is_null($subElemName)) { - ApiResult :: setElement($arr, '*', $value); + public static function setContent( & $arr, $value, $subElemName = null ) { + if ( is_array( $value ) ) + ApiBase :: dieDebug( __METHOD__, 'Bad parameter' ); + if ( is_null( $subElemName ) ) { + ApiResult :: setElement( $arr, '*', $value ); } else { - if (!isset ($arr[$subElemName])) + if ( !isset ( $arr[$subElemName] ) ) $arr[$subElemName] = array (); - ApiResult :: setElement($arr[$subElemName], '*', $value); + ApiResult :: setElement( $arr[$subElemName], '*', $value ); } } @@ -184,12 +184,12 @@ class ApiResult extends ApiBase { * @param $arr array * @param $tag string Tag name */ - public function setIndexedTagName(& $arr, $tag) { + public function setIndexedTagName( & $arr, $tag ) { // In raw mode, add the '_element', otherwise just ignore - if (!$this->getIsRawMode()) + if ( !$this->getIsRawMode() ) return; - if ($arr === null || $tag === null || !is_array($arr) || is_array($tag)) - ApiBase :: dieDebug(__METHOD__, 'Bad parameter'); + if ( $arr === null || $tag === null || !is_array( $arr ) || is_array( $tag ) ) + ApiBase :: dieDebug( __METHOD__, 'Bad parameter' ); // Do not use setElement() as it is ok to call this more than once $arr['_element'] = $tag; } @@ -199,17 +199,16 @@ class ApiResult extends ApiBase { * @param $arr array * @param $tag string Tag name */ - public function setIndexedTagName_recursive(&$arr, $tag) - { - if(!is_array($arr)) - return; - foreach($arr as &$a) - { - if(!is_array($a)) - continue; - $this->setIndexedTagName($a, $tag); - $this->setIndexedTagName_recursive($a, $tag); - } + public function setIndexedTagName_recursive( &$arr, $tag ) { + if ( !is_array( $arr ) ) + return; + foreach ( $arr as &$a ) + { + if ( !is_array( $a ) ) + continue; + $this->setIndexedTagName( $a, $tag ); + $this->setIndexedTagName_recursive( $a, $tag ); + } } /** @@ -221,15 +220,15 @@ class ApiResult extends ApiBase { */ public function setIndexedTagName_internal( $path, $tag ) { $data = & $this->mData; - foreach((array)$path as $p) { + foreach ( (array)$path as $p ) { if ( !isset( $data[$p] ) ) { $data[$p] = array(); } $data = & $data[$p]; } - if(is_null($data)) + if ( is_null( $data ) ) return; - $this->setIndexedTagName($data, $tag); + $this->setIndexedTagName( $data, $tag ); } /** @@ -239,34 +238,34 @@ class ApiResult extends ApiBase { * If $name is empty, the $value is added as a next list element data[] = $value * @return bool True if $value fits in the result, false if not */ - public function addValue($path, $name, $value) { + public function addValue( $path, $name, $value ) { global $wgAPIMaxResultSize; $data = & $this->mData; - if( $this->mCheckingSize ) { - $newsize = $this->mSize + self::size($value); - if($newsize > $wgAPIMaxResultSize) + if ( $this->mCheckingSize ) { + $newsize = $this->mSize + self::size( $value ); + if ( $newsize > $wgAPIMaxResultSize ) return false; $this->mSize = $newsize; } - if (!is_null($path)) { - if (is_array($path)) { - foreach ($path as $p) { - if (!isset ($data[$p])) + if ( !is_null( $path ) ) { + if ( is_array( $path ) ) { + foreach ( $path as $p ) { + if ( !isset ( $data[$p] ) ) $data[$p] = array (); $data = & $data[$p]; } } else { - if (!isset ($data[$path])) + if ( !isset ( $data[$path] ) ) $data[$path] = array (); $data = & $data[$path]; } } - if (!$name) + if ( !$name ) $data[] = $value; // Add list element else - ApiResult :: setElement($data, $name, $value); // Add named element + ApiResult :: setElement( $data, $name, $value ); // Add named element return true; } @@ -277,16 +276,16 @@ class ApiResult extends ApiBase { * @param $path array * @param $name string */ - public function unsetValue($path, $name) { + public function unsetValue( $path, $name ) { $data = & $this->mData; - if(!is_null($path)) - foreach((array)$path as $p) { - if(!isset($data[$p])) + if ( !is_null( $path ) ) + foreach ( (array)$path as $p ) { + if ( !isset( $data[$p] ) ) return; $data = & $data[$p]; } - $this->mSize -= self::size($data[$name]); - unset($data[$name]); + $this->mSize -= self::size( $data[$name] ); + unset( $data[$name] ); } /** @@ -294,52 +293,25 @@ class ApiResult extends ApiBase { */ public function cleanUpUTF8() { - array_walk_recursive($this->mData, array('ApiResult', 'cleanUp_helper')); + array_walk_recursive( $this->mData, array( 'ApiResult', 'cleanUp_helper' ) ); } /** * Callback function for cleanUpUTF8() */ - private static function cleanUp_helper(&$s) + private static function cleanUp_helper( &$s ) { - if(!is_string($s)) + if ( !is_string( $s ) ) return; - $s = UtfNormal::cleanUp($s); + global $wgContLang; + $s = $wgContLang->normalize( $s ); } public function execute() { - ApiBase :: dieDebug(__METHOD__, 'execute() is not supported on Result object'); + ApiBase :: dieDebug( __METHOD__, 'execute() is not supported on Result object' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiResult.php 47447 2009-02-18 12:41:28Z tstarling $'; - } -} - -/* For compatibility with PHP versions < 5.1.0, define our own array_intersect_key function. */ -if (!function_exists('array_intersect_key')) { - function array_intersect_key($isec, $keys) { - $argc = func_num_args(); - - if ($argc > 2) { - for ($i = 1; $isec && $i < $argc; $i++) { - $arr = func_get_arg($i); - - foreach (array_keys($isec) as $key) { - if (!isset($arr[$key])) - unset($isec[$key]); - } - } - - return $isec; - } else { - $res = array(); - foreach (array_keys($isec) as $key) { - if (isset($keys[$key])) - $res[$key] = $isec[$key]; - } - - return $res; - } + return __CLASS__ . ': $Id: ApiResult.php 62354 2010-02-12 06:44:16Z mah $'; } } diff --git a/includes/api/ApiRollback.php b/includes/api/ApiRollback.php index 0f0eae10..5c259f4e 100644 --- a/includes/api/ApiRollback.php +++ b/includes/api/ApiRollback.php @@ -22,9 +22,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -32,53 +32,51 @@ if (!defined('MEDIAWIKI')) { */ class ApiRollback extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { $params = $this->extractRequestParams(); - $titleObj = NULL; - if(!isset($params['title'])) - $this->dieUsageMsg(array('missingparam', 'title')); - if(!isset($params['user'])) - $this->dieUsageMsg(array('missingparam', 'user')); - if(!isset($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - - $titleObj = Title::newFromText($params['title']); - if(!$titleObj) - $this->dieUsageMsg(array('invalidtitle', $params['title'])); - if(!$titleObj->exists()) - $this->dieUsageMsg(array('notanarticle')); - - #We need to be able to revert IPs, but getCanonicalName rejects them - $username = User::isIP($params['user']) + $titleObj = null; + if ( !isset( $params['title'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'title' ) ); + if ( !isset( $params['user'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'user' ) ); + + $titleObj = Title::newFromText( $params['title'] ); + if ( !$titleObj ) + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + if ( !$titleObj->exists() ) + $this->dieUsageMsg( array( 'notanarticle' ) ); + + // We need to be able to revert IPs, but getCanonicalName rejects them + $username = User::isIP( $params['user'] ) ? $params['user'] - : User::getCanonicalName($params['user']); - if(!$username) - $this->dieUsageMsg(array('invaliduser', $params['user'])); + : User::getCanonicalName( $params['user'] ); + if ( !$username ) + $this->dieUsageMsg( array( 'invaliduser', $params['user'] ) ); - $articleObj = new Article($titleObj); - $summary = (isset($params['summary']) ? $params['summary'] : ""); + $articleObj = new Article( $titleObj ); + $summary = ( isset( $params['summary'] ) ? $params['summary'] : "" ); $details = null; - $retval = $articleObj->doRollback($username, $summary, $params['token'], $params['markbot'], $details); + $retval = $articleObj->doRollback( $username, $summary, $params['token'], $params['markbot'], $details ); - if($retval) + if ( $retval ) // We don't care about multiple errors, just report one of them - $this->dieUsageMsg(reset($retval)); + $this->dieUsageMsg( reset( $retval ) ); $info = array( 'title' => $titleObj->getPrefixedText(), - 'pageid' => intval($details['current']->getPage()), + 'pageid' => intval( $details['current']->getPage() ), 'summary' => $details['summary'], - 'revid' => intval($titleObj->getLatestRevID()), - 'old_revid' => intval($details['current']->getID()), - 'last_revid' => intval($details['target']->getID()) + 'revid' => intval( $details['newid'] ), + 'old_revid' => intval( $details['current']->getID() ), + 'last_revid' => intval( $details['target']->getID() ) ); - $this->getResult()->addValue(null, $this->getModuleName(), $info); + $this->getResult()->addValue( null, $this->getModuleName(), $info ); } public function mustBePosted() { return true; } @@ -113,6 +111,16 @@ class ApiRollback extends ApiBase { 'they will all be rolled back.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'title' ), + array( 'missingparam', 'user' ), + array( 'invalidtitle', 'title' ), + array( 'notanarticle' ), + array( 'invaliduser', 'user' ), + ) ); + } protected function getExamples() { return array ( @@ -122,6 +130,6 @@ class ApiRollback extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiRollback.php 48122 2009-03-07 12:58:41Z catrope $'; + return __CLASS__ . ': $Id: ApiRollback.php 65371 2010-04-21 10:41:25Z tstarling $'; } } diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php index 9216317a..2ffae504 100644 --- a/includes/api/ApiUnblock.php +++ b/includes/api/ApiUnblock.php @@ -22,9 +22,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -35,8 +35,8 @@ if (!defined('MEDIAWIKI')) { */ class ApiUnblock extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } /** @@ -46,38 +46,37 @@ class ApiUnblock extends ApiBase { global $wgUser; $params = $this->extractRequestParams(); - if($params['gettoken']) + if ( $params['gettoken'] ) { $res['unblocktoken'] = $wgUser->editToken(); - $this->getResult()->addValue(null, $this->getModuleName(), $res); + $this->getResult()->addValue( null, $this->getModuleName(), $res ); return; } - if(is_null($params['id']) && is_null($params['user'])) - $this->dieUsageMsg(array('unblock-notarget')); - if(!is_null($params['id']) && !is_null($params['user'])) - $this->dieUsageMsg(array('unblock-idanduser')); - if(is_null($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); - if(!$wgUser->isAllowed('block')) - $this->dieUsageMsg(array('cantunblock')); + if ( is_null( $params['id'] ) && is_null( $params['user'] ) ) + $this->dieUsageMsg( array( 'unblock-notarget' ) ); + if ( !is_null( $params['id'] ) && !is_null( $params['user'] ) ) + $this->dieUsageMsg( array( 'unblock-idanduser' ) ); + + if ( !$wgUser->isAllowed( 'block' ) ) + $this->dieUsageMsg( array( 'cantunblock' ) ); $id = $params['id']; $user = $params['user']; - $reason = (is_null($params['reason']) ? '' : $params['reason']); - $retval = IPUnblockForm::doUnblock($id, $user, $reason, $range); - if($retval) - $this->dieUsageMsg($retval); + $reason = ( is_null( $params['reason'] ) ? '' : $params['reason'] ); + $retval = IPUnblockForm::doUnblock( $id, $user, $reason, $range ); + if ( $retval ) + $this->dieUsageMsg( $retval ); - $res['id'] = intval($id); + $res['id'] = intval( $id ); $res['user'] = $user; $res['reason'] = $reason; - $this->getResult()->addValue(null, $this->getModuleName(), $res); + $this->getResult()->addValue( null, $this->getModuleName(), $res ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; @@ -108,6 +107,18 @@ class ApiUnblock extends ApiBase { 'Unblock a user.' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'unblock-notarget' ), + array( 'unblock-idanduser' ), + array( 'cantunblock' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { return array ( @@ -117,6 +128,6 @@ class ApiUnblock extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiUnblock.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiUnblock.php 62599 2010-02-16 21:59:16Z reedy $'; } } diff --git a/includes/api/ApiUndelete.php b/includes/api/ApiUndelete.php index ddc9f7f8..9efba5f3 100644 --- a/includes/api/ApiUndelete.php +++ b/includes/api/ApiUndelete.php @@ -22,9 +22,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ("ApiBase.php"); + require_once ( "ApiBase.php" ); } /** @@ -32,58 +32,57 @@ if (!defined('MEDIAWIKI')) { */ class ApiUndelete extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { global $wgUser; $params = $this->extractRequestParams(); - $titleObj = NULL; - if(!isset($params['title'])) - $this->dieUsageMsg(array('missingparam', 'title')); - if(!isset($params['token'])) - $this->dieUsageMsg(array('missingparam', 'token')); + $titleObj = null; + if ( !isset( $params['title'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'title' ) ); - if(!$wgUser->isAllowed('undelete')) - $this->dieUsageMsg(array('permdenied-undelete')); - if($wgUser->isBlocked()) - $this->dieUsageMsg(array('blockedtext')); - if(!$wgUser->matchEditToken($params['token'])) - $this->dieUsageMsg(array('sessionfailure')); + if ( !$wgUser->isAllowed( 'undelete' ) ) + $this->dieUsageMsg( array( 'permdenied-undelete' ) ); - $titleObj = Title::newFromText($params['title']); - if(!$titleObj) - $this->dieUsageMsg(array('invalidtitle', $params['title'])); + if ( $wgUser->isBlocked() ) + $this->dieUsageMsg( array( 'blockedtext' ) ); + + $titleObj = Title::newFromText( $params['title'] ); + if ( !$titleObj ) + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); // Convert timestamps - if(!isset($params['timestamps'])) + if ( !isset( $params['timestamps'] ) ) $params['timestamps'] = array(); - if(!is_array($params['timestamps'])) - $params['timestamps'] = array($params['timestamps']); - foreach($params['timestamps'] as $i => $ts) - $params['timestamps'][$i] = wfTimestamp(TS_MW, $ts); + if ( !is_array( $params['timestamps'] ) ) + $params['timestamps'] = array( $params['timestamps'] ); + foreach ( $params['timestamps'] as $i => $ts ) + $params['timestamps'][$i] = wfTimestamp( TS_MW, $ts ); - $pa = new PageArchive($titleObj); - $dbw = wfGetDB(DB_MASTER); + $pa = new PageArchive( $titleObj ); + $dbw = wfGetDB( DB_MASTER ); $dbw->begin(); - $retval = $pa->undelete((isset($params['timestamps']) ? $params['timestamps'] : array()), $params['reason']); - if(!is_array($retval)) - $this->dieUsageMsg(array('cannotundelete')); + $retval = $pa->undelete( ( isset( $params['timestamps'] ) ? $params['timestamps'] : array() ), $params['reason'] ); + if ( !is_array( $retval ) ) + $this->dieUsageMsg( array( 'cannotundelete' ) ); - if($retval[1]) - wfRunHooks( 'FileUndeleteComplete', - array($titleObj, array(), $wgUser, $params['reason']) ); + if ( $retval[1] ) + wfRunHooks( 'FileUndeleteComplete', + array( $titleObj, array(), $wgUser, $params['reason'] ) ); $info['title'] = $titleObj->getPrefixedText(); - $info['revisions'] = intval($retval[0]); - $info['fileversions'] = intval($retval[1]); - $info['reason'] = intval($retval[2]); - $this->getResult()->addValue(null, $this->getModuleName(), $info); + $info['revisions'] = intval( $retval[0] ); + $info['fileversions'] = intval( $retval[1] ); + $info['reason'] = intval( $retval[2] ); + $this->getResult()->addValue( null, $this->getModuleName(), $info ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; @@ -115,6 +114,20 @@ class ApiUndelete extends ApiBase { 'retrieved through list=deletedrevs' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'title' ), + array( 'permdenied-undelete' ), + array( 'blockedtext' ), + array( 'invalidtitle', 'title' ), + array( 'cannotundelete' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } protected function getExamples() { return array ( @@ -124,6 +137,6 @@ class ApiUndelete extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiUndelete.php 48091 2009-03-06 13:49:44Z catrope $'; + return __CLASS__ . ': $Id: ApiUndelete.php 62599 2010-02-16 21:59:16Z reedy $'; } } diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php new file mode 100644 index 00000000..6b91b223 --- /dev/null +++ b/includes/api/ApiUpload.php @@ -0,0 +1,325 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if ( !defined( 'MEDIAWIKI' ) ) { + // Eclipse helper - will be ignored in production + require_once( "ApiBase.php" ); +} + +/** + * @ingroup API + */ +class ApiUpload extends ApiBase { + protected $mUpload = null; + protected $mParams; + + public function __construct( $main, $action ) { + parent::__construct( $main, $action ); + } + + public function execute() { + global $wgUser, $wgAllowCopyUploads; + + // Check whether upload is enabled + if ( !UploadBase::isEnabled() ) + $this->dieUsageMsg( array( 'uploaddisabled' ) ); + + $this->mParams = $this->extractRequestParams(); + $request = $this->getMain()->getRequest(); + + // Add the uploaded file to the params array + $this->mParams['file'] = $request->getFileName( 'file' ); + + // One and only one of the following parameters is needed + $this->requireOnlyOneParameter( $this->mParams, + 'sessionkey', 'file', 'url' ); + + if ( $this->mParams['sessionkey'] ) { + /** + * Upload stashed in a previous request + */ + // Check the session key + if ( !isset( $_SESSION['wsUploadData'][$this->mParams['sessionkey']] ) ) + $this->dieUsageMsg( array( 'invalid-session-key' ) ); + + $this->mUpload = new UploadFromStash(); + $this->mUpload->initialize( $this->mParams['filename'], + $this->mParams['sessionkey'], + $_SESSION['wsUploadData'][$this->mParams['sessionkey']] ); + } elseif ( isset( $this->mParams['filename'] ) ) { + /** + * Upload from url, etc + * Parameter filename is required + */ + + if ( isset( $this->mParams['file'] ) ) { + $this->mUpload = new UploadFromFile(); + $this->mUpload->initialize( + $this->mParams['filename'], + $request->getFileTempName( 'file' ), + $request->getFileSize( 'file' ) + ); + } elseif ( isset( $this->mParams['url'] ) ) { + // make sure upload by url is enabled: + if ( !$wgAllowCopyUploads ) + $this->dieUsageMsg( array( 'uploaddisabled' ) ); + + // make sure the current user can upload + if ( ! $wgUser->isAllowed( 'upload_by_url' ) ) + $this->dieUsageMsg( array( 'badaccess-groups' ) ); + + $this->mUpload = new UploadFromUrl(); + $this->mUpload->initialize( $this->mParams['filename'], + $this->mParams['url'] ); + + $status = $this->mUpload->fetchFile(); + if ( !$status->isOK() ) { + $this->dieUsage( $status->getWikiText(), 'fetchfileerror' ); + } + } + } else $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); + + if ( !isset( $this->mUpload ) ) + $this->dieUsage( 'No upload module set', 'nomodule' ); + + // Check whether the user has the appropriate permissions to upload anyway + $permission = $this->mUpload->isAllowed( $wgUser ); + + if ( $permission !== true ) { + if ( !$wgUser->isLoggedIn() ) + $this->dieUsageMsg( array( 'mustbeloggedin', 'upload' ) ); + else + $this->dieUsageMsg( array( 'badaccess-groups' ) ); + } + // Perform the upload + $result = $this->performUpload(); + + // Cleanup any temporary mess + $this->mUpload->cleanupTempFile(); + + $this->getResult()->addValue( null, $this->getModuleName(), $result ); + } + + protected function performUpload() { + global $wgUser; + $result = array(); + $permErrors = $this->mUpload->verifyPermissions( $wgUser ); + if ( $permErrors !== true ) { + $this->dieUsageMsg( array( 'badaccess-groups' ) ); + } + + // TODO: Move them to ApiBase's message map + $verification = $this->mUpload->verifyUpload(); + if ( $verification['status'] !== UploadBase::OK ) { + $result['result'] = 'Failure'; + switch( $verification['status'] ) { + case UploadBase::EMPTY_FILE: + $this->dieUsage( 'The file you submitted was empty', 'empty-file' ); + break; + case UploadBase::FILETYPE_MISSING: + $this->dieUsage( 'The file is missing an extension', 'filetype-missing' ); + break; + case UploadBase::FILETYPE_BADTYPE: + global $wgFileExtensions; + $this->dieUsage( 'This type of file is banned', 'filetype-banned', + 0, array( + 'filetype' => $verification['finalExt'], + 'allowed' => $wgFileExtensions + ) ); + break; + case UploadBase::MIN_LENGTH_PARTNAME: + $this->dieUsage( 'The filename is too short', 'filename-tooshort' ); + break; + case UploadBase::ILLEGAL_FILENAME: + $this->dieUsage( 'The filename is not allowed', 'illegal-filename', + 0, array( 'filename' => $verification['filtered'] ) ); + break; + case UploadBase::OVERWRITE_EXISTING_FILE: + $this->dieUsage( 'Overwriting an existing file is not allowed', 'overwrite' ); + break; + case UploadBase::VERIFICATION_ERROR: + $this->getResult()->setIndexedTagName( $verification['details'], 'detail' ); + $this->dieUsage( 'This file did not pass file verification', 'verification-error', + 0, array( 'details' => $verification['details'] ) ); + break; + case UploadBase::HOOK_ABORTED: + $this->dieUsage( "The modification you tried to make was aborted by an extension hook", + 'hookaborted', 0, array( 'error' => $verification['error'] ) ); + break; + default: + $this->dieUsage( 'An unknown error occurred', 'unknown-error', + 0, array( 'code' => $verification['status'] ) ); + break; + } + return $result; + } + if ( !$this->mParams['ignorewarnings'] ) { + $warnings = $this->mUpload->checkWarnings(); + if ( $warnings ) { + // Add indices + $this->getResult()->setIndexedTagName( $warnings, 'warning' ); + + if ( isset( $warnings['duplicate'] ) ) { + $dupes = array(); + foreach ( $warnings['duplicate'] as $key => $dupe ) + $dupes[] = $dupe->getName(); + $this->getResult()->setIndexedTagName( $dupes, 'duplicate' ); + $warnings['duplicate'] = $dupes; + } + + + if ( isset( $warnings['exists'] ) ) { + $warning = $warnings['exists']; + unset( $warnings['exists'] ); + $warnings[$warning['warning']] = $warning['file']->getName(); + } + + $result['result'] = 'Warning'; + $result['warnings'] = $warnings; + + $sessionKey = $this->mUpload->stashSession(); + if ( !$sessionKey ) + $this->dieUsage( 'Stashing temporary file failed', 'stashfailed' ); + + $result['sessionkey'] = $sessionKey; + + return $result; + } + } + + // Use comment as initial page text by default + if ( is_null( $this->mParams['text'] ) ) + $this->mParams['text'] = $this->mParams['comment']; + + // No errors, no warnings: do the upload + $status = $this->mUpload->performUpload( $this->mParams['comment'], + $this->mParams['text'], $this->mParams['watch'], $wgUser ); + + if ( !$status->isGood() ) { + $error = $status->getErrorsArray(); + $this->getResult()->setIndexedTagName( $result['details'], 'error' ); + + $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error ); + } + + $file = $this->mUpload->getLocalFile(); + $result['result'] = 'Success'; + $result['filename'] = $file->getName(); + $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() ); + + return $result; + } + + public function mustBePosted() { + return true; + } + + public function isWriteMode() { + return true; + } + + public function getAllowedParams() { + $params = array( + 'filename' => null, + 'comment' => array( + ApiBase::PARAM_DFLT => '' + ), + 'text' => null, + 'token' => null, + 'watch' => false, + 'ignorewarnings' => false, + 'file' => null, + 'url' => null, + 'sessionkey' => null, + ); + return $params; + + } + + public function getParamDescription() { + return array( + 'filename' => 'Target filename', + 'token' => 'Edit token. You can get one of these through prop=info', + 'comment' => 'Upload comment. Also used as the initial page text for new files if "text" is not specified', + 'text' => 'Initial page text for new files', + 'watch' => 'Watch the page', + 'ignorewarnings' => 'Ignore any warnings', + 'file' => 'File contents', + 'url' => 'Url to fetch the file from', + 'sessionkey' => array( + 'Session key returned by a previous upload that failed due to warnings', + ), + ); + } + + public function getDescription() { + return array( + 'Upload a file, or get the status of pending uploads. Several methods are available:', + ' * Upload file contents directly, using the "file" parameter', + ' * Have the MediaWiki server fetch a file from a URL, using the "url" parameter', + ' * Complete an earlier upload that failed due to warnings, using the "sessionkey" parameter', + 'Note that the HTTP POST must be done as a file upload (i.e. using multipart/form-data) when', + 'sending the "file". Note also that queries using session keys must be', + 'done in the same login session as the query that originally returned the key (i.e. do not', + 'log out and then log back in). Also you must get and send an edit token before doing any upload stuff.' + ); + } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'uploaddisabled' ), + array( 'invalid-session-key' ), + array( 'uploaddisabled' ), + array( 'badaccess-groups' ), + array( 'missingparam', 'filename' ), + array( 'mustbeloggedin', 'upload' ), + array( 'badaccess-groups' ), + array( 'badaccess-groups' ), + array( 'code' => 'fetchfileerror', 'info' => '' ), + array( 'code' => 'nomodule', 'info' => 'No upload module set' ), + array( 'code' => 'empty-file', 'info' => 'The file you submitted was empty' ), + array( 'code' => 'filetype-missing', 'info' => 'The file is missing an extension' ), + array( 'code' => 'filename-tooshort', 'info' => 'The filename is too short' ), + array( 'code' => 'overwrite', 'info' => 'Overwriting an existing file is not allowed' ), + array( 'code' => 'stashfailed', 'info' => 'Stashing temporary file failed' ), + array( 'code' => 'internal-error', 'info' => 'An internal error occurred' ), + ) ); + } + + public function getTokenSalt() { + return ''; + } + + protected function getExamples() { + return array( + 'Upload from a URL:', + ' api.php?action=upload&filename=Wiki.png&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png', + 'Complete an upload that failed due to warnings:', + ' api.php?action=upload&filename=Wiki.png&sessionkey=sessionkey&ignorewarnings=1', + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiUpload.php 51812 2009-06-12 23:45:20Z dale $'; + } +} diff --git a/includes/api/ApiUserrights.php b/includes/api/ApiUserrights.php new file mode 100644 index 00000000..6296a8f8 --- /dev/null +++ b/includes/api/ApiUserrights.php @@ -0,0 +1,128 @@ +.@home.nl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if ( !defined( 'MEDIAWIKI' ) ) { + // Eclipse helper - will be ignored in production + require_once ( "ApiBase.php" ); +} + +/** + * @ingroup API + */ +class ApiUserrights extends ApiBase { + + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); + } + + public function execute() { + $params = $this->extractRequestParams(); + + // User already validated in call to getTokenSalt from Main + $form = new UserrightsPage; + $user = $form->fetchUser( $params['user'] ); + + $r['user'] = $user->getName(); + list( $r['added'], $r['removed'] ) = + $form->doSaveUserGroups( + $user, (array)$params['add'], + (array)$params['remove'], $params['reason'] ); + + $this->getResult()->setIndexedTagName( $r['added'], 'group' ); + $this->getResult()->setIndexedTagName( $r['removed'], 'group' ); + $this->getResult()->addValue( null, $this->getModuleName(), $r ); + } + + public function mustBePosted() { + return true; + } + + public function isWriteMode() { + return true; + } + + public function getAllowedParams() { + return array ( + 'user' => null, + 'add' => array( + ApiBase :: PARAM_TYPE => User::getAllGroups(), + ApiBase :: PARAM_ISMULTI => true + ), + 'remove' => array( + ApiBase :: PARAM_TYPE => User::getAllGroups(), + ApiBase :: PARAM_ISMULTI => true + ), + 'token' => null, + 'reason' => array( + ApiBase :: PARAM_DFLT => '' + ) + ); + } + + public function getParamDescription() { + return array ( + 'user' => 'User name', + 'add' => 'Add the user to these groups', + 'remove' => 'Remove the user from these groups', + 'token' => 'A userrights token previously retrieved through list=users', + 'reason' => 'Reason for the change', + ); + } + + public function getDescription() { + return array( + 'Add/remove a user to/from groups', + ); + } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'user' ), + ) ); + } + + public function getTokenSalt() { + $params = $this->extractRequestParams(); + if ( is_null( $params['user'] ) ) + $this->dieUsageMsg( array( 'missingparam', 'user' ) ); + + $form = new UserrightsPage; + $user = $form->fetchUser( $params['user'] ); + if ( $user instanceof WikiErrorMsg ) + $this->dieUsageMsg( array_merge( + (array)$user->getMessageKey(), $user->getMessageArgs() ) ); + + return $user->getName(); + } + + protected function getExamples() { + return array ( + 'api.php?action=userrights&user=FooBot&add=bot&remove=sysop|bureaucrat&token=123ABC' + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiUserrights.php 62686 2010-02-19 01:25:57Z reedy $'; + } +} diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index 7901b6ac..391d91e2 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -23,9 +23,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -if (!defined('MEDIAWIKI')) { +if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ('ApiBase.php'); + require_once ( 'ApiBase.php' ); } /** @@ -35,21 +35,25 @@ if (!defined('MEDIAWIKI')) { */ class ApiWatch extends ApiBase { - public function __construct($main, $action) { - parent :: __construct($main, $action); + public function __construct( $main, $action ) { + parent :: __construct( $main, $action ); } public function execute() { global $wgUser; - if(!$wgUser->isLoggedIn()) - $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin'); + if ( !$wgUser->isLoggedIn() ) + $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' ); + $params = $this->extractRequestParams(); - $title = Title::newFromText($params['title']); - if(!$title) - $this->dieUsageMsg(array('invalidtitle', $params['title'])); - $article = new Article($title); - $res = array('title' => $title->getPrefixedText()); - if($params['unwatch']) + $title = Title::newFromText( $params['title'] ); + + if ( !$title ) + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + + $article = new Article( $title ); + $res = array( 'title' => $title->getPrefixedText() ); + + if ( $params['unwatch'] ) { $res['unwatched'] = ''; $success = $article->doUnwatch(); @@ -59,14 +63,14 @@ class ApiWatch extends ApiBase { $res['watched'] = ''; $success = $article->doWatch(); } - if(!$success) - $this->dieUsageMsg(array('hookaborted')); - $this->getResult()->addValue(null, $this->getModuleName(), $res); + if ( !$success ) + $this->dieUsageMsg( array( 'hookaborted' ) ); + $this->getResult()->addValue( null, $this->getModuleName(), $res ); } public function isWriteMode() { return true; - } + } public function getAllowedParams() { return array ( @@ -87,6 +91,14 @@ class ApiWatch extends ApiBase { 'Add or remove a page from/to the current user\'s watchlist' ); } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'notloggedin', 'info' => 'You must be logged-in to have a watchlist' ), + array( 'invalidtitle', 'title' ), + array( 'hookaborted' ), + ) ); + } protected function getExamples() { return array( @@ -96,6 +108,6 @@ class ApiWatch extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiWatch.php 69579 2010-07-20 02:49:55Z tstarling $'; + return __CLASS__ . ': $Id: ApiWatch.php 69578 2010-07-20 02:46:20Z tstarling $'; } } -- cgit v1.2.2