From 9db190c7e736ec8d063187d4241b59feaf7dc2d1 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Wed, 22 Jun 2011 11:28:20 +0200 Subject: update to MediaWiki 1.17.0 --- includes/api/ApiBase.php | 275 +++++++++++++---- includes/api/ApiBlock.php | 38 ++- includes/api/ApiDelete.php | 81 +++-- includes/api/ApiDisabled.php | 14 +- includes/api/ApiEditPage.php | 293 +++++++++++-------- includes/api/ApiEmailUser.php | 111 ++++--- includes/api/ApiExpandTemplates.php | 39 +-- includes/api/ApiFeedWatchlist.php | 104 ++++--- includes/api/ApiFormatBase.php | 85 ++++-- includes/api/ApiFormatDbg.php | 22 +- includes/api/ApiFormatDump.php | 64 ++++ includes/api/ApiFormatJson.php | 44 +-- includes/api/ApiFormatPhp.php | 22 +- includes/api/ApiFormatRaw.php | 39 +-- includes/api/ApiFormatTxt.php | 22 +- includes/api/ApiFormatWddx.php | 47 +-- includes/api/ApiFormatXml.php | 115 ++++---- includes/api/ApiFormatYaml.php | 24 +- includes/api/ApiFormatYaml_spyc.php | 236 --------------- includes/api/ApiHelp.php | 115 +++++++- includes/api/ApiImport.php | 115 ++++---- includes/api/ApiLogin.php | 24 +- includes/api/ApiLogout.php | 29 +- includes/api/ApiMain.php | 422 +++++++++++++++----------- includes/api/ApiMove.php | 158 +++++----- includes/api/ApiOpenSearch.php | 74 +++-- includes/api/ApiPageSet.php | 239 +++++++++------ includes/api/ApiParamInfo.php | 148 +++++----- includes/api/ApiParse.php | 445 +++++++++++++++++++++------- includes/api/ApiPatrol.php | 58 ++-- includes/api/ApiProtect.php | 141 +++++---- includes/api/ApiPurge.php | 57 ++-- includes/api/ApiQuery.php | 329 +++++++++++++-------- includes/api/ApiQueryAllCategories.php | 91 +++--- includes/api/ApiQueryAllLinks.php | 148 ++++++---- includes/api/ApiQueryAllUsers.php | 141 ++++----- includes/api/ApiQueryAllimages.php | 107 ++++--- includes/api/ApiQueryAllmessages.php | 81 ++--- includes/api/ApiQueryAllpages.php | 158 +++++----- includes/api/ApiQueryBacklinks.php | 268 +++++++++-------- includes/api/ApiQueryBase.php | 185 ++++++++---- includes/api/ApiQueryBlocks.php | 221 ++++++++------ includes/api/ApiQueryCategories.php | 139 ++++----- includes/api/ApiQueryCategoryInfo.php | 42 ++- includes/api/ApiQueryCategoryMembers.php | 312 ++++++++++++-------- includes/api/ApiQueryDeletedrevs.php | 210 +++++++------ includes/api/ApiQueryDisabled.php | 27 +- includes/api/ApiQueryDuplicateFiles.php | 83 +++--- includes/api/ApiQueryExtLinksUsage.php | 117 ++++---- includes/api/ApiQueryExternalLinks.php | 65 +++-- includes/api/ApiQueryFilearchive.php | 264 +++++++++++++++++ includes/api/ApiQueryIWBacklinks.php | 217 ++++++++++++++ includes/api/ApiQueryIWLinks.php | 158 ++++++++++ includes/api/ApiQueryImageInfo.php | 379 ++++++++++++++++-------- includes/api/ApiQueryImages.php | 92 +++--- includes/api/ApiQueryInfo.php | 355 +++++++++++++--------- includes/api/ApiQueryLangLinks.php | 89 +++--- includes/api/ApiQueryLinks.php | 159 +++++----- includes/api/ApiQueryLogEvents.php | 221 ++++++++------ includes/api/ApiQueryPageProps.php | 150 ++++++++++ includes/api/ApiQueryProtectedTitles.php | 123 ++++---- includes/api/ApiQueryRandom.php | 77 ++--- includes/api/ApiQueryRecentChanges.php | 382 +++++++++++++----------- includes/api/ApiQueryRevisions.php | 376 ++++++++++++++---------- includes/api/ApiQuerySearch.php | 166 +++++++---- includes/api/ApiQuerySiteinfo.php | 191 +++++++----- includes/api/ApiQueryStashImageInfo.php | 152 ++++++++++ includes/api/ApiQueryTags.php | 141 ++++----- includes/api/ApiQueryUserContributions.php | 260 +++++++++-------- includes/api/ApiQueryUserInfo.php | 113 ++++--- includes/api/ApiQueryUsers.php | 190 +++++++----- includes/api/ApiQueryWatchlist.php | 269 +++++++++-------- includes/api/ApiQueryWatchlistRaw.php | 135 +++++---- includes/api/ApiResult.php | 171 ++++++----- includes/api/ApiRollback.php | 146 ++++++--- includes/api/ApiRsd.php | 180 ++++++++++++ includes/api/ApiUnblock.php | 67 +++-- includes/api/ApiUndelete.php | 86 +++--- includes/api/ApiUpload.php | 455 ++++++++++++++++++++--------- includes/api/ApiUserrights.php | 92 +++--- includes/api/ApiWatch.php | 55 ++-- 81 files changed, 7730 insertions(+), 4605 deletions(-) create mode 100644 includes/api/ApiFormatDump.php delete mode 100644 includes/api/ApiFormatYaml_spyc.php create mode 100644 includes/api/ApiQueryFilearchive.php create mode 100644 includes/api/ApiQueryIWBacklinks.php create mode 100644 includes/api/ApiQueryIWLinks.php create mode 100644 includes/api/ApiQueryPageProps.php create mode 100644 includes/api/ApiQueryStashImageInfo.php create mode 100644 includes/api/ApiRsd.php (limited to 'includes/api') diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 708a3a40..175fa6a1 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -1,10 +1,9 @@ @gmail.com * * This program is free software; you can redistribute it and/or modify @@ -19,8 +18,10 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ /** @@ -50,6 +51,8 @@ abstract class ApiBase { 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 PARAM_REQUIRED = 8; // Boolean, is the parameter required? + const PARAM_RANGE_ENFORCE = 9; // Boolean, if MIN/MAX are set, enforce (die) these? Only applies if TYPE='integer' Use with extreme caution const LIMIT_BIG1 = 500; // Fast query, std user limit const LIMIT_BIG2 = 5000; // Fast query, bot/sysop limit @@ -179,8 +182,7 @@ abstract class ApiBase { 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 ( preg_match( "/{$warn_regex}(\\n|$)/", $data['warnings'][$this->getModuleName()]['*'] ) ) { return; } $oldwarning = $data['warnings'][$this->getModuleName()]['*']; @@ -224,13 +226,13 @@ abstract class ApiBase { $msg = $lnPrfx . implode( $lnPrfx, $msg ) . "\n"; if ( $this->isReadMode() ) { - $msg .= "\nThis module requires read rights."; + $msg .= "\nThis module requires read rights"; } if ( $this->isWriteMode() ) { - $msg .= "\nThis module requires write rights."; + $msg .= "\nThis module requires write rights"; } if ( $this->mustBePosted() ) { - $msg .= "\nThis module only accepts POST requests."; + $msg .= "\nThis module only accepts POST requests"; } if ( $this->isReadMode() || $this->isWriteMode() || $this->mustBePosted() ) @@ -252,8 +254,11 @@ abstract class ApiBase { $examples ); } - $msg .= 'Example' . ( count( $examples ) > 1 ? 's' : '' ) . ":\n "; - $msg .= implode( $lnPrfx, $examples ) . "\n"; + + if ( count( $examples ) > 0 ) { + $msg .= 'Example' . ( count( $examples ) > 1 ? 's' : '' ) . ":\n "; + $msg .= implode( $lnPrfx, $examples ) . "\n"; + } } if ( $this->getMain()->getShowVersions() ) { @@ -295,12 +300,24 @@ abstract class ApiBase { $desc = implode( $paramPrefix, $desc ); } + if ( !is_array( $paramSettings ) ) { + $paramSettings = array( + self::PARAM_DFLT => $paramSettings, + ); + } + $deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] ) ? $paramSettings[self::PARAM_DEPRECATED] : false; if ( $deprecated ) { $desc = "DEPRECATED! $desc"; } + $required = isset( $paramSettings[self::PARAM_REQUIRED] ) ? + $paramSettings[self::PARAM_REQUIRED] : false; + if ( $required ) { + $desc .= $paramPrefix . "This parameter is required"; + } + $type = isset( $paramSettings[self::PARAM_TYPE] ) ? $paramSettings[self::PARAM_TYPE] : null; if ( isset( $type ) ) { if ( isset( $paramSettings[self::PARAM_ISMULTI] ) ) { @@ -312,21 +329,26 @@ abstract class ApiBase { if ( is_array( $type ) ) { $choices = array(); $nothingPrompt = false; - foreach ( $type as $t ) + foreach ( $type as $t ) { if ( $t === '' ) { $nothingPrompt = 'Can be empty, or '; } else { $choices[] = $t; } + } $desc .= $paramPrefix . $nothingPrompt . $prompt . implode( ', ', $choices ); } else { 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( ', ', MWNamespace::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]}"; + if ( isset( $paramSettings[self::PARAM_MAX2] ) ) { + $desc .= " ({$paramSettings[self::PARAM_MAX2]} for bots)"; + } + $desc .= ' allowed'; break; case 'integer': $hasMin = isset( $paramSettings[self::PARAM_MIN] ); @@ -344,10 +366,22 @@ abstract class ApiBase { } break; } + + if ( isset( $paramSettings[self::PARAM_ISMULTI] ) ) { + $isArray = is_array( $paramSettings[self::PARAM_TYPE] ); + + if ( !$isArray + || $isArray && count( $paramSettings[self::PARAM_TYPE] ) > self::LIMIT_SML1) { + $desc .= $paramPrefix . "Maximum number of values " . + self::LIMIT_SML1 . " (" . self::LIMIT_SML2 . " for bots)"; + } + } } } - $default = is_array( $paramSettings ) ? ( isset( $paramSettings[self::PARAM_DFLT] ) ? $paramSettings[self::PARAM_DFLT] : null ) : $paramSettings; + $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"; } @@ -480,7 +514,7 @@ abstract class ApiBase { if ( $params ) { // getFinalParams() can return false foreach ( $params as $paramName => $paramSettings ) { - $results[$paramName] = $this->getParameterFromSettings( + $results[$paramName] = $this->getParameterFromSettings( $paramName, $paramSettings, $parseLimit ); } } @@ -510,8 +544,8 @@ abstract class ApiBase { array_shift( $required ); $intersection = array_intersect( array_keys( array_filter( $params, - create_function( '$x', 'return !is_null($x) && $x !== false;' ) - ) ), $required ); + array( $this, "parameterNotEmpty" ) ) ), $required ); + if ( count( $intersection ) > 1 ) { $this->dieUsage( 'The parameters ' . implode( ', ', $intersection ) . ' can not be used together', 'invalidparammix' ); } elseif ( count( $intersection ) == 0 ) { @@ -520,24 +554,81 @@ abstract class ApiBase { } /** - * Returns an array of the namespaces (by integer id) that exist on the - * wiki. Used primarily in help documentation. - * @return array + * Callback function used in requireOnlyOneParameter to check whether reequired parameters are set + * + * @param $x object Parameter to check is not null/false + * @return bool + */ + private function parameterNotEmpty( $x ) { + return !is_null( $x ) && $x !== false; + } + + /** + * @deprecated use MWNamespace::getValidNamespaces() */ public static function getValidNamespaces() { - static $mValidNamespaces = null; - - if ( is_null( $mValidNamespaces ) ) { - global $wgContLang; - $mValidNamespaces = array(); - foreach ( array_keys( $wgContLang->getNamespaces() ) as $ns ) { - if ( $ns >= 0 ) { - $mValidNamespaces[] = $ns; + return MWNamespace::getValidNamespaces(); + } + + /** + * Return true if we're to watch the page, false if not, null if no change. + * @param $watchlist String Valid values: 'watch', 'unwatch', 'preferences', 'nochange' + * @param $titleObj Title the page under consideration + * @param $userOption String The user option to consider when $watchlist=preferences. + * If not set will magically default to either watchdefault or watchcreations + * @returns Boolean + */ + protected function getWatchlistValue ( $watchlist, $titleObj, $userOption = null ) { + + $userWatching = $titleObj->userIsWatching(); + + global $wgUser; + switch ( $watchlist ) { + case 'watch': + return true; + + case 'unwatch': + return false; + + case 'preferences': + # If the user is already watching, don't bother checking + if ( $userWatching ) { + return true; } - } + # If no user option was passed, use watchdefault or watchcreation + if ( is_null( $userOption ) ) { + $userOption = $titleObj->exists() + ? 'watchdefault' : 'watchcreations'; + } + # Watch the article based on the user preference + return (bool)$wgUser->getOption( $userOption ); + + case 'nochange': + return $userWatching; + + default: + return $userWatching; } + } - return $mValidNamespaces; + /** + * Set a watch (or unwatch) based the based on a watchlist parameter. + * @param $watch String Valid values: 'watch', 'unwatch', 'preferences', 'nochange' + * @param $titleObj Title the article's title to change + * @param $userOption String The user option to consider when $watch=preferences + */ + protected function setWatch ( $watch, $titleObj, $userOption = null ) { + $value = $this->getWatchlistValue( $watch, $titleObj, $userOption ); + if ( $value === null ) { + return; + } + + $articleObj = new Article( $titleObj ); + if ( $value ) { + $articleObj->doWatch(); + } else { + $articleObj->doUnwatch(); + } } /** @@ -559,12 +650,14 @@ abstract class ApiBase { $type = gettype( $paramSettings ); $dupes = false; $deprecated = false; + $required = 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; $deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] ) ? $paramSettings[self::PARAM_DEPRECATED] : false; + $required = isset( $paramSettings[self::PARAM_REQUIRED] ) ? $paramSettings[self::PARAM_REQUIRED] : false; // When type is not given, and no choices, the type is the same as $default if ( !isset( $type ) ) { @@ -587,7 +680,7 @@ abstract class ApiBase { $value = $this->getMain()->getRequest()->getVal( $encParamName, $default ); if ( isset( $value ) && $type == 'namespace' ) { - $type = ApiBase::getValidNamespaces(); + $type = MWNamespace::getValidNamespaces(); } } @@ -602,19 +695,28 @@ abstract class ApiBase { switch ( $type ) { case 'NULL': // nothing to do break; - case 'string': // nothing to do + case 'string': + if ( $required && $value === '' ) { + $this->dieUsageMsg( array( 'missingparam', $paramName ) ); + } + break; 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; + $enforceLimits = isset ( $paramSettings[self::PARAM_RANGE_ENFORCE] ) + ? $paramSettings[self::PARAM_RANGE_ENFORCE] : false; 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_array( $value ) ) { + $value = array_map( 'intval', $value ); + foreach ( $value as &$v ) { + $this->validateLimit( $paramName, $v, $min, $max, null, $enforceLimits ); + } + } else { + $value = intval( $value ); + $this->validateLimit( $paramName, $value, $min, $max, null, $enforceLimits ); + } } break; case 'limit': @@ -631,15 +733,16 @@ abstract class ApiBase { $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 ); + $this->getResult()->setParsedLimit( $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 ) + if ( $multi ) { ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); + } break; case 'timestamp': if ( $multi ) { @@ -652,11 +755,21 @@ abstract class ApiBase { $value = wfTimestamp( TS_MW, $value ); break; case 'user': - $title = Title::makeTitleSafe( NS_USER, $value ); - if ( is_null( $title ) ) { - $this->dieUsage( "Invalid value for user parameter $encParamName", "baduser_{$encParamName}" ); + if ( !is_array( $value ) ) { + $value = array( $value ); + } + + foreach ( $value as $key => $val ) { + $title = Title::makeTitleSafe( NS_USER, $val ); + if ( is_null( $title ) ) { + $this->dieUsage( "Invalid value for user parameter $encParamName", "baduser_{$encParamName}" ); + } + $value[$key] = $title->getText(); + } + + if ( !$multi ) { + $value = $value[0]; } - $value = $title->getText(); break; default: ApiBase::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" ); @@ -672,6 +785,8 @@ abstract class ApiBase { if ( $deprecated && $value !== false ) { $this->setWarning( "The $encParamName parameter has been deprecated." ); } + } else if ( $required ) { + $this->dieUsageMsg( array( 'missingparam', $paramName ) ); } return $value; @@ -736,10 +851,13 @@ abstract class ApiBase { * @param $min int Minimum value * @param $max int Maximum value for users * @param $botMax int Maximum value for sysops/bots + * @param $enforceLimits Boolean Whether to enforce (die) if value is outside limits */ - function validateLimit( $paramName, &$value, $min, $max, $botMax = null ) { + function validateLimit( $paramName, &$value, $min, $max, $botMax = null, $enforceLimits = false ) { if ( !is_null( $min ) && $value < $min ) { - $this->setWarning( $this->encodeParamName( $paramName ) . " may not be less than $min (set to $value)" ); + + $msg = $this->encodeParamName( $paramName ) . " may not be less than $min (set to $value)"; + $this->warnOrDie( $msg, $enforceLimits ); $value = $min; } @@ -753,16 +871,32 @@ abstract class ApiBase { 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" ); + $msg = $this->encodeParamName( $paramName ) . " may not be over $botMax (set to $value) for bots or sysops"; + $this->warnOrDie( $msg, $enforceLimits ); $value = $botMax; } } else { - $this->setWarning( $this->encodeParamName( $paramName ) . " may not be over $max (set to $value) for users" ); + $msg = $this->encodeParamName( $paramName ) . " may not be over $max (set to $value) for users"; + $this->warnOrDie( $msg, $enforceLimits ); $value = $max; } } } + /** + * Adds a warning to the output, else dies + * + * @param $msg String Message to show as a warning, or error message if dying + * @param $enforceLimits Boolean Whether this is an enforce (die) + */ + private function warnOrDie( $msg, $enforceLimits = false ) { + if ( $enforceLimits ) { + $this->dieUsage( $msg, 'integeroutofrange' ); + } else { + $this->setWarning( $msg ); + } + } + /** * Truncate an array to a certain length. * @param $arr array Array to truncate @@ -772,7 +906,7 @@ abstract class ApiBase { public static function truncateArray( &$arr, $limit ) { $modified = false; while ( count( $arr ) > $limit ) { - $junk = array_pop( $arr ); + array_pop( $arr ); $modified = true; } return $modified; @@ -848,6 +982,8 @@ abstract class ApiBase { '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" ), + 'ipbblocked' => array( 'code' => 'ipbblocked', 'info' => 'You cannot block or unblock users while you are yourself blocked' ), + 'ipbnounblockself' => array( 'code' => 'ipbnounblockself', 'info' => 'You are not allowed to unblock yourself' ), '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" ), @@ -860,6 +996,7 @@ abstract class ApiBase { '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''" ), + 'summaryrequired' => array( 'code' => 'summaryrequired', 'info' => 'Summary required' ), // API-specific messages 'readrequired' => array( 'code' => 'readapidenied', 'info' => "You need read permission to use this module" ), @@ -886,7 +1023,6 @@ abstract class ApiBase { '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" ), @@ -927,6 +1063,7 @@ abstract class ApiBase { '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' ), + 'copyuploaddisabled' => array( 'code' => 'copyuploaddisabled', 'info' => 'Uploads by URL is not enabled. Make sure $wgAllowCopyUploads is set to true in LocalSettings.php.' ), ); /** @@ -1021,6 +1158,31 @@ abstract class ApiBase { return false; } + /** + * Gets the user for whom to get the watchlist + * + * @returns User + */ + public function getWatchlistUser( $params ) { + global $wgUser; + 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' ); + } + } else { + if ( !$wgUser->isLoggedIn() ) { + $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' ); + } + $user = $wgUser; + } + return $user; + } + /** * 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' => ... ) @@ -1028,6 +1190,15 @@ abstract class ApiBase { public function getPossibleErrors() { $ret = array(); + $params = $this->getFinalParams(); + if ( $params ) { + foreach ( $params as $paramName => $paramSettings ) { + if ( isset( $paramSettings[ApiBase::PARAM_REQUIRED] ) ) { + $ret[] = array( 'missingparam', $paramName ); + } + } + } + if ( $this->mustBePosted() ) { $ret[] = array( 'mustbeposted', $this->getModuleName() ); } @@ -1192,6 +1363,6 @@ abstract class ApiBase { * @return string */ public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiBase.php 79562 2011-01-04 06:15:54Z tstarling $'; + return __CLASS__ . ': $Id: ApiBase.php 82730 2011-02-24 16:03:05Z reedy $'; } } diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php index 23de07d6..875b8aeb 100644 --- a/includes/api/ApiBlock.php +++ b/includes/api/ApiBlock.php @@ -1,9 +1,9 @@ .@home.nl * * This program is free software; you can redistribute it and/or modify @@ -18,8 +18,10 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { @@ -58,12 +60,16 @@ class ApiBlock extends ApiBase { return; } - if ( is_null( $params['user'] ) ) { - $this->dieUsageMsg( array( 'missingparam', 'user' ) ); - } if ( !$wgUser->isAllowed( 'block' ) ) { $this->dieUsageMsg( array( 'cantblock' ) ); } + # bug 15810: blocked admins should have limited access here + if ( $wgUser->isBlocked() ) { + $status = IPBlockForm::checkUnblockSelf( $params['user'] ); + if ( $status !== true ) { + $this->dieUsageMsg( array( $status ) ); + } + } if ( $params['hidename'] && !$wgUser->isAllowed( 'hideuser' ) ) { $this->dieUsageMsg( array( 'canthide' ) ); } @@ -128,7 +134,10 @@ class ApiBlock extends ApiBase { public function getAllowedParams() { return array( - 'user' => null, + 'user' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'token' => null, 'gettoken' => false, 'expiry' => 'never', @@ -161,20 +170,19 @@ class ApiBlock extends ApiBase { } public function getDescription() { - return array( - 'Block a user.' - ); + return 'Block a user'; } - + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( - array( 'missingparam', 'user' ), array( 'cantblock' ), array( 'canthide' ), array( 'cantblock-email' ), + array( 'ipbblocked' ), + array( 'ipbnounblockself' ), ) ); } - + public function needsToken() { return true; } @@ -186,11 +194,11 @@ class ApiBlock extends ApiBase { protected function getExamples() { 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' + 'api.php?action=block&user=Vandal&expiry=never&reason=Vandalism&nocreate=&autoblock=&noemail=' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiBlock.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiBlock.php 77192 2010-11-23 22:05:27Z btongminh $'; } } diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php index c4550a96..fbf62391 100644 --- a/includes/api/ApiDelete.php +++ b/includes/api/ApiDelete.php @@ -1,9 +1,9 @@ .@home.nl * * This program is free software; you can redistribute it and/or modify @@ -18,8 +18,10 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { @@ -28,7 +30,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { } /** - * API module that facilitates deleting pages. The API eqivalent of action=delete. + * API module that facilitates deleting pages. The API equivalent of action=delete. * Requires API write mode to be enabled. * * @ingroup API @@ -47,8 +49,6 @@ class ApiDelete extends ApiBase { * result object. */ public function execute() { - global $wgUser; - $params = $this->extractRequestParams(); $this->requireOnlyOneParameter( $params, 'title', 'pageid' ); @@ -82,17 +82,26 @@ class ApiDelete extends ApiBase { $this->dieUsageMsg( reset( $retval ) ); // We don't care about multiple errors, just report one of them } - if ( $params['watch'] || $wgUser->getOption( 'watchdeletion' ) ) { - $articleObj->doWatch(); + // Deprecated parameters + if ( $params['watch'] ) { + $watch = 'watch'; } elseif ( $params['unwatch'] ) { - $articleObj->doUnwatch(); + $watch = 'unwatch'; + } else { + $watch = $params['watchlist']; } + $this->setWatch( $watch, $titleObj, 'watchdeletion' ); } $r = array( 'title' => $titleObj->getPrefixedText(), 'reason' => $reason ); $this->getResult()->addValue( null, $this->getModuleName(), $r ); } + /** + * + * @param &$title Title + * @param $token String + */ private static function getPermissionsError( &$title, $token ) { global $wgUser; @@ -108,9 +117,9 @@ class ApiDelete extends ApiBase { /** * We have our own delete() function, since Article.php's implementation is split in two phases * - * @param Article $article - Article object to work on - * @param string $token - Delete token (same as edit token) - * @param string $reason - Reason for the deletion. Autogenerated if NULL + * @param $article Article object to work on + * @param $token String: delete token (same as edit token) + * @param $reason String: reason for the deletion. Autogenerated if NULL * @return Title::getUserPermissionsErrors()-like array */ public static function delete( &$article, $token, &$reason = null ) { @@ -137,8 +146,8 @@ class ApiDelete extends ApiBase { } $error = ''; - if ( !wfRunHooks( 'ArticleDelete', array( &$article, &$wgUser, &$reason, $error ) ) ) { - $this->dieUsageMsg( array( 'hookaborted', $error ) ); + if ( !wfRunHooks( 'ArticleDelete', array( &$article, &$wgUser, &$reason, &$error ) ) ) { + return array( array( 'hookaborted', $error ) ); } // Luckily, Article.php provides a reusable delete function that does the hard work for us @@ -149,6 +158,15 @@ class ApiDelete extends ApiBase { return array( array( 'cannotdelete', $article->mTitle->getPrefixedText() ) ); } + /** + * @static + * @param $token + * @param $title + * @param $oldimage + * @param $reason + * @param $suppress bool + * @return \type|array|Title + */ public static function deleteFile( $token, &$title, $oldimage, &$reason = null, $suppress = false ) { $errors = self::getPermissionsError( $title, $token ); if ( count( $errors ) ) { @@ -197,28 +215,43 @@ class ApiDelete extends ApiBase { ), 'token' => null, 'reason' => null, - 'watch' => false, - 'unwatch' => false, - 'oldimage' => null + 'watch' => array( + ApiBase::PARAM_DFLT => false, + ApiBase::PARAM_DEPRECATED => true, + ), + 'watchlist' => array( + ApiBase::PARAM_DFLT => 'preferences', + ApiBase::PARAM_TYPE => array( + 'watch', + 'unwatch', + 'preferences', + 'nochange' + ), + ), + 'unwatch' => array( + ApiBase::PARAM_DFLT => false, + ApiBase::PARAM_DEPRECATED => true, + ), + 'oldimage' => null, ); } public function getParamDescription() { + $p = $this->getModulePrefix(); 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', + 'title' => "Title of the page you want to delete. Cannot be used together with {$p}pageid", + 'pageid' => "Page ID of the page you want to delete. Cannot be used together with {$p}title", 'token' => 'A delete token previously retrieved through prop=info', - 'reason' => 'Reason for the deletion. If not set, an automatically generated reason will be used.', + 'reason' => 'Reason for the deletion. If not set, an automatically generated reason will be used', 'watch' => 'Add the page to your watchlist', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', 'unwatch' => 'Remove the page from your watchlist', 'oldimage' => 'The name of the old image to delete as provided by iiprop=archivename' ); } public function getDescription() { - return array( - 'Delete a page.' - ); + return 'Delete a page'; } public function getPossibleErrors() { @@ -246,6 +279,6 @@ class ApiDelete extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiDelete.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiDelete.php 77141 2010-11-23 10:04:38Z ialex $'; } } \ No newline at end of file diff --git a/includes/api/ApiDisabled.php b/includes/api/ApiDisabled.php index 60e0e7ee..f83bfdc9 100644 --- a/includes/api/ApiDisabled.php +++ b/includes/api/ApiDisabled.php @@ -1,9 +1,9 @@ .@home.nl * * This program is free software; you can redistribute it and/or modify @@ -18,8 +18,10 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { @@ -60,9 +62,7 @@ class ApiDisabled extends ApiBase { } public function getDescription() { - return array( - 'This module has been disabled.' - ); + return 'This module has been disabled'; } protected function getExamples() { @@ -70,6 +70,6 @@ class ApiDisabled extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiDisabled.php 62783 2010-02-21 18:09:00Z ashley $'; + return __CLASS__ . ': $Id: ApiDisabled.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index e78f66bc..75cc0ba2 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -1,11 +1,10 @@ @gmail.com + * Created on August 16, 2007 + * + * Copyright © 2007 Iker Labarga @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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { - // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + // Eclipse helper - will be ignored in production + require_once( "ApiBase.php" ); } /** @@ -38,40 +39,71 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiEditPage extends ApiBase { public function __construct( $query, $moduleName ) { - parent :: __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' ) ); + } $titleObj = Title::newFromText( $params['title'] ); - if ( !$titleObj || $titleObj->isExternal() ) + if ( !$titleObj || $titleObj->isExternal() ) { $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); - + } + + if( $params['redirect'] ) { + if( $titleObj->isRedirect() ) { + $oldTitle = $titleObj; + + $titles = Title::newFromRedirectArray( Revision::newFromTitle( $oldTitle )->getText( Revision::FOR_THIS_USER ) ); + + $redirValues = array(); + foreach ( $titles as $id => $newTitle ) { + + if( !isset( $titles[ $id - 1 ] ) ) { + $titles[ $id - 1 ] = $oldTitle; + } + + $redirValues[] = array( + 'from' => $titles[ $id - 1 ]->getPrefixedText(), + 'to' => $newTitle->getPrefixedText() + ); + + $titleObj = $newTitle; + } + + $this->getResult()->setIndexedTagName( $redirValues, 'r' ); + $this->getResult()->addValue( null, 'redirects', $redirValues ); + + } + } + // Some functions depend on $wgTitle == $ep->mTitle global $wgTitle; $wgTitle = $titleObj; - if ( $params['createonly'] && $titleObj->exists() ) + if ( $params['createonly'] && $titleObj->exists() ) { $this->dieUsageMsg( array( 'createonly-exists' ) ); - if ( $params['nocreate'] && !$titleObj->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() ) + if ( !$titleObj->exists() ) { $errors = array_merge( $errors, $titleObj->getUserPermissionsErrors( 'create', $wgUser ) ); - if ( count( $errors ) ) + } + if ( count( $errors ) ) { $this->dieUsageMsg( $errors[0] ); + } $articleObj = new Article( $titleObj ); $toMD5 = $params['text']; @@ -81,127 +113,126 @@ class ApiEditPage extends ApiBase { // 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 + } else { $content = $articleObj->getContent(); - - if ( !is_null( $params['section'] ) ) - { + } + + 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 ) + 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['undoafter'] > 0 ) - { - if ( $params['undo'] < $params['undoafter'] ) + + if ( $params['undo'] > 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'] ); } $undoRev = Revision::newFromID( $params['undo'] ); - if ( is_null( $undoRev ) || $undoRev->isDeleted( Revision::DELETED_TEXT ) ) + if ( is_null( $undoRev ) || $undoRev->isDeleted( Revision::DELETED_TEXT ) ) { $this->dieUsageMsg( array( 'nosuchrevid', $params['undo'] ) ); + } - if ( $params['undoafter'] == 0 ) + if ( $params['undoafter'] == 0 ) { $undoafterRev = $undoRev->getPrevious(); - if ( is_null( $undoafterRev ) || $undoafterRev->isDeleted( Revision::DELETED_TEXT ) ) + } + if ( is_null( $undoafterRev ) || $undoafterRev->isDeleted( Revision::DELETED_TEXT ) ) { $this->dieUsageMsg( array( 'nosuchrevid', $params['undoafter'] ) ); + } - if ( $undoRev->getPage() != $articleObj->getID() ) + if ( $undoRev->getPage() != $articleObj->getID() ) { $this->dieUsageMsg( array( 'revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText() ) ); - if ( $undoafterRev->getPage() != $articleObj->getID() ) + } + if ( $undoafterRev->getPage() != $articleObj->getID() ) { $this->dieUsageMsg( array( 'revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText() ) ); - + } + $newtext = $articleObj->getUndoText( $undoRev, $undoafterRev ); - if ( $newtext === false ) + 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'] ) + 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'] ) && md5( $toMD5 ) !== $params['md5'] ) + if ( !is_null( $params['md5'] ) && md5( $toMD5 ) !== $params['md5'] ) { $this->dieUsageMsg( array( 'hashcheckfailed' ) ); - + } + $ep = new EditPage( $articleObj ); // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable - $reqArr = array( 'wpTextbox1' => $params['text'], - 'wpEditToken' => $params['token'], - 'wpIgnoreBlankSummary' => '' + $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'] != '' ) + if ( !is_null( $params['basetimestamp'] ) && $params['basetimestamp'] != '' ) { $reqArr['wpEdittime'] = wfTimestamp( TS_MW, $params['basetimestamp'] ); - else + } else { $reqArr['wpEdittime'] = $articleObj->getTimestamp(); + } - if ( !is_null( $params['starttimestamp'] ) && $params['starttimestamp'] != '' ) + if ( !is_null( $params['starttimestamp'] ) && $params['starttimestamp'] != '' ) { $reqArr['wpStarttime'] = wfTimestamp( TS_MW, $params['starttimestamp'] ); - else - $reqArr['wpStarttime'] = $reqArr['wpEdittime']; // Fake wpStartime + } else { + $reqArr['wpStarttime'] = wfTimestampNow(); // Fake wpStartime + } - if ( $params['minor'] || ( !$params['notminor'] && $wgUser->getOption( 'minordefault' ) ) ) + 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' ) + 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 + } else { $reqArr['wpSection'] = ''; - - // 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(); } + + $watch = $this->getWatchlistValue( $params['watchlist'], $titleObj ); + // Deprecated parameters - if ( $params['watch'] ) + if ( $params['watch'] ) { $watch = true; - elseif ( $params['unwatch'] ) + } elseif ( $params['unwatch'] ) { $watch = false; - - if ( $watch ) + } + + if ( $watch ) { $reqArr['wpWatchthis'] = ''; + } $req = new FauxRequest( $reqArr, true ); $ep->importFormData( $req ); @@ -209,22 +240,22 @@ class ApiEditPage extends ApiBase { // 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 ( count( $r ) ) - { - $r['result'] = "Failure"; + if ( !wfRunHooks( 'APIEditBeforeSave', array( $ep, $ep->textbox1, &$r ) ) ) { + if ( count( $r ) ) { + $r['result'] = 'Failure'; $this->getResult()->addValue( null, $this->getModuleName(), $r ); return; - } - else + } else { $this->dieUsageMsg( array( 'hookaborted' ) ); + } } // Do the actual save @@ -237,8 +268,9 @@ class ApiEditPage extends ApiBase { $retval = $ep->internalAttemptSave( $result, $wgUser->isAllowed( 'bot' ) && $params['bot'] ); $wgRequest = $oldRequest; - switch( $retval ) - { + global $wgMaxArticleSize; + + switch( $retval ) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: $this->dieUsageMsg( array( 'hookaborted' ) ); @@ -260,7 +292,6 @@ class ApiEditPage extends ApiBase { case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: case EditPage::AS_CONTENT_TOO_BIG: - global $wgMaxArticleSize; $this->dieUsageMsg( array( 'contenttoobig', $wgMaxArticleSize ) ); case EditPage::AS_READ_ONLY_PAGE_ANON: @@ -293,8 +324,9 @@ class ApiEditPage extends ApiBase { case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = ''; + case EditPage::AS_SUCCESS_UPDATE: - $r['result'] = "Success"; + $r['result'] = 'Success'; $r['pageid'] = intval( $titleObj->getArticleID() ); $r['title'] = $titleObj->getPrefixedText(); // HACK: We create a new Article object here because getRevIdFetched() @@ -303,10 +335,9 @@ class ApiEditPage extends ApiBase { // don't want to do. $newArticle = new Article( $titleObj ); $newRevId = $newArticle->getRevIdFetched(); - if ( $newRevId == $oldRevId ) + if ( $newRevId == $oldRevId ) { $r['nochange'] = ''; - else - { + } else { $r['oldrevid'] = intval( $oldRevId ); $r['newrevid'] = intval( $newRevId ); $r['newtimestamp'] = wfTimestamp( TS_ISO_8601, @@ -314,12 +345,15 @@ class ApiEditPage extends ApiBase { } break; + case EditPage::AS_SUMMARY_NEEDED: + $this->dieUsageMsg( array( 'summaryrequired' ) ); + case EditPage::AS_END: // This usually means some kind of race condition - // or DB weirdness occurred. Fall through to throw an unknown + // or DB weirdness occurred. Fall through to throw an unknown // error. - // This needs fixing higher up, as Article::doEdit should be + // This needs fixing higher up, as Article::doEdit should be // used rather than Article::updateArticle, so that specific // error conditions can be returned default: @@ -339,12 +373,11 @@ 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' ), @@ -358,6 +391,7 @@ class ApiEditPage extends ApiBase { array( 'noimageredirect-anon' ), array( 'noimageredirect-logged' ), array( 'spamdetected', 'spam' ), + array( 'summaryrequired' ), array( 'filtered' ), array( 'blockedtext' ), array( 'contenttoobig', $wgMaxArticleSize ), @@ -376,8 +410,11 @@ class ApiEditPage extends ApiBase { } protected function getAllowedParams() { - return array ( - 'title' => null, + return array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'section' => null, 'text' => null, 'token' => null, @@ -393,16 +430,16 @@ class ApiEditPage extends ApiBase { 'captchaword' => null, 'captchaid' => null, 'watch' => array( - ApiBase :: PARAM_DFLT => false, - ApiBase :: PARAM_DEPRECATED => true, + ApiBase::PARAM_DFLT => false, + ApiBase::PARAM_DEPRECATED => true, ), 'unwatch' => array( - ApiBase :: PARAM_DFLT => false, - ApiBase :: PARAM_DEPRECATED => true, + ApiBase::PARAM_DFLT => false, + ApiBase::PARAM_DEPRECATED => true, ), 'watchlist' => array( - ApiBase :: PARAM_DFLT => 'preferences', - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'preferences', + ApiBase::PARAM_TYPE => array( 'watch', 'unwatch', 'preferences', @@ -413,16 +450,21 @@ class ApiEditPage extends ApiBase { 'prependtext' => null, 'appendtext' => null, 'undo' => array( - ApiBase :: PARAM_TYPE => 'integer' + ApiBase::PARAM_TYPE => 'integer' ), 'undoafter' => array( - ApiBase :: PARAM_TYPE => 'integer' + ApiBase::PARAM_TYPE => 'integer' + ), + 'redirect' => array( + ApiBase::PARAM_TYPE => 'boolean', + ApiBase::PARAM_DFLT => false, ), ); } protected function getParamDescription() { - return array ( + $p = $this->getModulePrefix(); + return array( 'title' => 'Page title', 'section' => 'Section number. 0 for the top section, \'new\' for a new section', 'text' => 'Page content', @@ -435,7 +477,7 @@ class ApiEditPage extends ApiBase { 'Used to detect edit conflicts; leave unset to ignore conflicts.' ), 'starttimestamp' => array( 'Timestamp when you obtained the edit token.', - 'Used to detect edit conflicts; leave unset to ignore conflicts.' + 'Used to detect edit conflicts; leave unset to ignore conflicts' ), 'recreate' => 'Override any errors about the article having been deleted in the meantime', 'createonly' => 'Don\'t edit the page if it exists already', @@ -445,15 +487,16 @@ class ApiEditPage extends ApiBase { '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.', + 'md5' => array( "The MD5 hash of the {$p}text parameter, or the {$p}prependtext and {$p}appendtext parameters concatenated.", 'If set, the edit won\'t be done unless the hash is correct' ), - 'prependtext' => 'Add this text to the beginning of the page. Overrides text.', - 'appendtext' => 'Add this text to the end of the page. Overrides text', - 'undo' => 'Undo this revision. Overrides text, prependtext and appendtext', + 'prependtext' => "Add this text to the beginning of the page. Overrides {$p}text", + 'appendtext' => "Add this text to the end of the page. Overrides {$p}text", + 'undo' => "Undo this revision. Overrides {$p}text, {$p}prependtext and {$p}appendtext", 'undoafter' => 'Undo all revisions from undo to this one. If not set, just undo one revision', + 'redirect' => 'Automatically resolve redirects', ); } - + public function needsToken() { return true; } @@ -463,17 +506,17 @@ class ApiEditPage extends ApiBase { } protected function getExamples() { - return array ( - "Edit a page (anonymous user):", - " api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\", - "Prepend __NOTOC__ to a page (anonymous user):", - " api.php?action=edit&title=Test&summary=NOTOC&minor&prependtext=__NOTOC__%0A&basetimestamp=20070824123454&token=%2B\\", - "Undo r13579 through r13585 with autosummary(anonymous user):", - " api.php?action=edit&title=Test&undo=13585&undoafter=13579&basetimestamp=20070824123454&token=%2B\\", + return array( + 'Edit a page (anonymous user):', + ' api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\', + 'Prepend __NOTOC__ to a page (anonymous user):', + ' api.php?action=edit&title=Test&summary=NOTOC&minor=&prependtext=__NOTOC__%0A&basetimestamp=20070824123454&token=%2B\\', + 'Undo r13579 through r13585 with autosummary (anonymous user):', + ' api.php?action=edit&title=Test&undo=13585&undoafter=13579&basetimestamp=20070824123454&token=%2B\\', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiEditPage.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiEditPage.php 90492 2011-06-20 22:39:10Z reedy $'; } } diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php index 66f2dff5..ab58eb18 100644 --- a/includes/api/ApiEmailUser.php +++ b/includes/api/ApiEmailUser.php @@ -1,10 +1,10 @@ + * Created on June 1, 2008 + * + * Copyright © 2008 Bryan Tong Minh * * 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 @@ -18,58 +18,74 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** + * API Module to facilitate sending of emails to users * @ingroup API */ class ApiEmailUser extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { global $wgUser; - // Check whether email is enabled - if ( !EmailUserForm::userEmailEnabled() ) - $this->dieUsageMsg( array( 'usermaildisabled' ) ); $params = $this->extractRequestParams(); - // Check required parameters - if ( !isset( $params['target'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'target' ) ); - if ( !isset( $params['text'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'text' ) ); - - // Validate target - $targetUser = EmailUserForm::validateEmailTarget( $params['target'] ); - if ( !( $targetUser instanceof User ) ) + + // Validate target + $targetUser = SpecialEmailUser::getTarget( $params['target'] ); + if ( !( $targetUser instanceof User ) ) { $this->dieUsageMsg( array( $targetUser ) ); - - // Check permissions - $error = EmailUserForm::getPermissionsError( $wgUser, $params['token'] ); - if ( $error ) + } + + // Check permissions and errors + $error = SpecialEmailUser::getPermissionsError( $wgUser, $params['token'] ); + if ( $error ) { $this->dieUsageMsg( array( $error ) ); + } + + $data = array( + 'Target' => $targetUser->getName(), + 'Text' => $params['text'], + 'Subject' => $params['subject'], + 'CCMe' => $params['ccme'], + ); + $retval = SpecialEmailUser::submit( $data ); - $form = new EmailUserForm( $targetUser, $params['text'], $params['subject'], $params['ccme'] ); - $retval = $form->doSubmit(); - if ( is_null( $retval ) ) + if ( $retval instanceof Status ) { + // SpecialEmailUser sometimes returns a status + // sometimes it doesn't. + if ( $retval->isGood() ) { + $retval = true; + } else { + $retval = $retval->getErrorsArray(); + } + } + + if ( $retval === true ) { $result = array( 'result' => 'Success' ); - else - $result = array( 'result' => 'Failure', - 'message' => $retval->getMessage() ); - + } else { + $result = array( + 'result' => 'Failure', + 'message' => $retval + ); + } + $this->getResult()->addValue( null, $this->getModuleName(), $result ); } - + public function mustBePosted() { return true; } @@ -79,17 +95,23 @@ class ApiEmailUser extends ApiBase { } public function getAllowedParams() { - return array ( - 'target' => null, + return array( + 'target' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'subject' => null, - 'text' => null, + 'text' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'token' => null, 'ccme' => false, ); } public function getParamDescription() { - return array ( + return array( 'target' => 'User to send email to', 'subject' => 'Subject header', 'text' => 'Mail body', @@ -99,19 +121,15 @@ class ApiEmailUser extends ApiBase { } public function getDescription() { - return array( - 'Email a user.' - ); + return 'Email a user.'; } - - public function getPossibleErrors() { + + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'usermaildisabled' ), - array( 'missingparam', 'target' ), - array( 'missingparam', 'text' ), - ) ); + ) ); } - + public function needsToken() { return true; } @@ -121,13 +139,12 @@ class ApiEmailUser extends ApiBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=emailuser&target=WikiSysop&text=Content' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiEmailUser.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiEmailUser.php 85354 2011-04-04 18:25:31Z demon $'; } } - \ No newline at end of file diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php index d0c00db7..6f2df1b8 100644 --- a/includes/api/ApiExpandTemplates.php +++ b/includes/api/ApiExpandTemplates.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 05, 2007 + * + * Copyright © 2007 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -38,7 +39,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiExpandTemplates extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { @@ -49,18 +50,18 @@ class ApiExpandTemplates extends ApiBase { $params = $this->extractRequestParams(); // Create title for parser - $title_obj = Title :: newFromText( $params['title'] ); - if ( !$title_obj ) - $title_obj = Title :: newFromText( "API" ); // default + $title_obj = Title::newFromText( $params['title'] ); + if ( !$title_obj ) { + $title_obj = Title::newFromText( 'API' ); // default + } $result = $this->getResult(); // Parse text global $wgParser; $options = new ParserOptions(); - - if ( $params['generatexml'] ) - { + + if ( $params['generatexml'] ) { $wgParser->startExternalParse( $title_obj, $options, OT_PREPROCESS ); $dom = $wgParser->preprocessToDom( $params['text'] ); if ( is_callable( array( $dom, 'saveXML' ) ) ) { @@ -81,9 +82,9 @@ class ApiExpandTemplates extends ApiBase { } public function getAllowedParams() { - return array ( + return array( 'title' => array( - ApiBase :: PARAM_DFLT => 'API', + ApiBase::PARAM_DFLT => 'API', ), 'text' => null, 'generatexml' => false, @@ -91,7 +92,7 @@ class ApiExpandTemplates extends ApiBase { } public function getParamDescription() { - return array ( + return array( 'text' => 'Wikitext to convert', 'title' => 'Title of page', 'generatexml' => 'Generate XML parse tree', @@ -103,12 +104,12 @@ class ApiExpandTemplates extends ApiBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=expandtemplates&text={{Project:Sandbox}}' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiExpandTemplates.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiExpandTemplates.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php index 03d12800..e1ba61f6 100644 --- a/includes/api/ApiFeedWatchlist.php +++ b/includes/api/ApiFeedWatchlist.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 13, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -38,7 +39,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiFeedWatchlist extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } /** @@ -48,13 +49,14 @@ class ApiFeedWatchlist extends ApiBase { return new ApiFormatFeedWrapper( $this->getMain() ); } + private $linkToDiffs = false; + /** * Make a nested call to the API to request watchlist items in the last $hours. * Wrap the result as an RSS/Atom feed. */ public function execute() { - - global $wgFeedClasses, $wgFeedLimit, $wgSitename, $wgContLanguageCode; + global $wgFeedClasses, $wgFeedLimit, $wgSitename, $wgLanguageCode; try { $params = $this->extractRequestParams(); @@ -62,16 +64,15 @@ class ApiFeedWatchlist extends ApiBase { // limit to the number of hours going from now back $endTime = wfTimestamp( TS_MW, time() - intval( $params['hours'] * 60 * 60 ) ); - $dbr = wfGetDB( DB_SLAVE ); // Prepare parameters for nested request - $fauxReqArr = array ( + $fauxReqArr = array( 'action' => 'query', 'meta' => 'siteinfo', 'siprop' => 'general', 'list' => 'watchlist', 'wlprop' => 'title|user|comment|timestamp', - 'wldir' => 'older', // reverse order - from newest to oldest - 'wlend' => $dbr->timestamp( $endTime ), // stop at this time + 'wldir' => 'older', // reverse order - from newest to oldest + 'wlend' => $endTime, // stop at this time 'wllimit' => ( 50 > $wgFeedLimit ) ? $wgFeedLimit : 50 ); @@ -82,13 +83,19 @@ class ApiFeedWatchlist extends ApiBase { $fauxReqArr['wltoken'] = $params['wltoken']; } + // Support linking to diffs instead of article + if ( $params['linktodiffs'] ) { + $this->linkToDiffs = true; + $fauxReqArr['wlprop'] .= '|ids'; + } + // Check for 'allrev' parameter, and if found, show all revisions to each page on wl. - if ( !is_null ( $params['allrev'] ) ) { + if ( !is_null( $params['allrev'] ) ) { $fauxReqArr['wlallrev'] = ''; } // Create the request - $fauxReq = new FauxRequest ( $fauxReqArr ); + $fauxReq = new FauxRequest( $fauxReqArr ); // Execute $module = new ApiMain( $fauxReq ); @@ -102,20 +109,20 @@ class ApiFeedWatchlist extends ApiBase { $feedItems[] = $this->createFeedItem( $info ); } - $feedTitle = $wgSitename . ' - ' . wfMsgForContent( 'watchlist' ) . ' [' . $wgContLanguageCode . ']'; - $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl(); + $feedTitle = $wgSitename . ' - ' . wfMsgForContent( 'watchlist' ) . ' [' . $wgLanguageCode . ']'; + $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL(); $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 ) { // Error results should not be cached $this->getMain()->setCacheMaxAge( 0 ); - $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent( 'watchlist' ) . ' [' . $wgContLanguageCode . ']'; - $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl(); + $feedTitle = $wgSitename . ' - Error - ' . wfMsgForContent( 'watchlist' ) . ' [' . $wgLanguageCode . ']'; + $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullURL(); $feedFormat = isset( $params['feedformat'] ) ? $params['feedformat'] : 'rss'; $feed = new $wgFeedClasses[$feedFormat] ( $feedTitle, htmlspecialchars( wfMsgForContent( 'watchlist' ) ), $feedUrl ); @@ -128,15 +135,19 @@ 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 ) { $titleStr = $info['title']; - $title = Title :: newFromText( $titleStr ); - $titleUrl = $title->getFullUrl(); + $title = Title::newFromText( $titleStr ); + if ( $this->linkToDiffs && isset( $info['revid'] ) ) { + $titleUrl = $title->getFullURL( array( 'diff' => $info['revid'] ) ); + } else { + $titleUrl = $title->getFullURL(); + } $comment = isset( $info['comment'] ) ? $info['comment'] : null; $timestamp = $info['timestamp']; $user = $info['user']; @@ -150,33 +161,35 @@ class ApiFeedWatchlist extends ApiBase { global $wgFeedClasses; $feedFormatNames = array_keys( $wgFeedClasses ); return array ( - 'feedformat' => array ( - ApiBase :: PARAM_DFLT => 'rss', - ApiBase :: PARAM_TYPE => $feedFormatNames + 'feedformat' => array( + ApiBase::PARAM_DFLT => 'rss', + ApiBase::PARAM_TYPE => $feedFormatNames ), - 'hours' => array ( - ApiBase :: PARAM_DFLT => 24, - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => 72, + 'hours' => array( + ApiBase::PARAM_DFLT => 24, + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => 72, ), 'allrev' => null, - 'wlowner' => array ( - ApiBase :: PARAM_TYPE => 'user' + 'wlowner' => array( + ApiBase::PARAM_TYPE => 'user' ), - 'wltoken' => array ( - ApiBase :: PARAM_TYPE => 'string' - ) + 'wltoken' => array( + ApiBase::PARAM_TYPE => 'string' + ), + 'linktodiffs' => false, ); } public function getParamDescription() { - return array ( + 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.', - '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' + 'allrev' => 'Include multiple revisions of the same page within given timeframe', + 'wlowner' => "The user whose watchlist you want (must be accompanied by {$this->getModulePrefix()}token if it's not you)", + 'wltoken' => 'Security token that requested user set in their preferences', + 'linktodiffs'=> 'Link to change differences instead of article pages' ); } @@ -185,12 +198,13 @@ class ApiFeedWatchlist extends ApiBase { } protected function getExamples() { - return array ( - 'api.php?action=feedwatchlist' + return array( + 'api.php?action=feedwatchlist', + 'api.php?action=feedwatchlist&allrev=allrev&linktodiffs=&hours=6' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFeedWatchlist.php 69357 2010-07-14 22:39:23Z mah $'; + return __CLASS__ . ': $Id: ApiFeedWatchlist.php 77674 2010-12-03 19:47:22Z catrope $'; } } diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php index de211fe9..9d1dfbc1 100644 --- a/includes/api/ApiFormatBase.php +++ b/includes/api/ApiFormatBase.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 19, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { abstract class ApiFormatBase extends ApiBase { private $mIsHtml, $mFormat, $mUnescapeAmps, $mHelp, $mCleared; - private $mBufferResult = false, $mBuffer; + private $mBufferResult = false, $mBuffer, $mDisabled = false; /** * Constructor @@ -45,13 +46,14 @@ abstract class ApiFormatBase extends ApiBase { * @param $format string Format name */ public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); $this->mIsHtml = ( substr( $format, - 2, 2 ) === 'fm' ); // ends with 'fm' - if ( $this->mIsHtml ) + if ( $this->mIsHtml ) { $this->mFormat = substr( $format, 0, - 2 ); // remove ending 'fm' - else + } else { $this->mFormat = $format; + } $this->mFormat = strtoupper( $this->mFormat ); $this->mCleared = false; } @@ -112,6 +114,18 @@ abstract class ApiFormatBase extends ApiBase { return $this->getIsHtml(); } + /** + * Disable the formatter completely. This causes calls to initPrinter(), + * printText() and closePrinter() to be ignored. + */ + public function disable() { + $this->mDisabled = true; + } + + public function isDisabled() { + return $this->mDisabled; + } + /** * Initialize the printer function and prepare the output headers, etc. * This method must be the first outputing method during execution. @@ -119,14 +133,18 @@ abstract class ApiFormatBase extends ApiBase { * @param $isError bool Whether an error message is printed */ function initPrinter( $isError ) { + if ( $this->mDisabled ) { + return; + } $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" ); @@ -170,6 +188,9 @@ See complete documentation, or * Finish printing. Closes HTML tags. */ public function closePrinter() { + if ( $this->mDisabled ) { + return; + } if ( $this->getIsHtml() ) { ?> @@ -189,6 +210,9 @@ See complete documentation, or * @param $text string */ public function printText( $text ) { + if ( $this->mDisabled ) { + return; + } if ( $this->mBufferResult ) { $this->mBuffer = $text; } elseif ( $this->getIsHtml() ) { @@ -197,8 +221,7 @@ See complete documentation, or // 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; } @@ -224,15 +247,15 @@ See complete documentation, or * @param $help bool */ public function setHelp( $help = true ) { - $this->mHelp = true; + $this->mHelp = $help; } /** - * Prety-print various elements in HTML format, such as xml tags and - * URLs. This method also escapes characters like < - * @param $text string - * @return string - */ + * Pretty-print various elements in HTML format, such as xml tags and + * URLs. This method also escapes characters like < + * @param $text string + * @return string + */ protected function formatHTML( $text ) { global $wgUrlProtocols; @@ -254,12 +277,15 @@ See complete documentation, or $text = preg_replace( "#\\$[^<>\n]+\\$#", '\\0', $text ); } - /* Temporary fix for bad links in help messages. As a special case, + /** + * Temporary fix for bad links in help messages. As a special case, * XML-escaped metachars are de-escaped one level in the help message - * for legibility. Should be removed once we have completed a fully-html - * version of the help message. */ - if ( $this->mUnescapeAmps ) + * for legibility. Should be removed once we have completed a fully-HTML + * version of the help message. + */ + if ( $this->mUnescapeAmps ) { $text = preg_replace( '/&(amp|quot|lt|gt);/', '&\1;', $text ); + } return $text; } @@ -273,7 +299,7 @@ See complete documentation, or } public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiFormatBase.php 62367 2010-02-12 14:09:42Z siebrand $'; + return __CLASS__ . ': $Id: ApiFormatBase.php 75970 2010-11-04 00:55:30Z reedy $'; } } @@ -284,7 +310,7 @@ See complete documentation, or class ApiFormatFeedWrapper extends ApiFormatBase { public function __construct( $main ) { - parent :: __construct( $main, 'feed' ); + parent::__construct( $main, 'feed' ); } /** @@ -326,13 +352,14 @@ 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 ) + foreach ( $items as & $item ) { $feed->outItem( $item ); + } $feed->outFooter(); } else { // Error has occured, print something useful @@ -341,6 +368,6 @@ class ApiFormatFeedWrapper extends ApiFormatBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatBase.php 62367 2010-02-12 14:09:42Z siebrand $'; + return __CLASS__ . ': $Id: ApiFormatBase.php 75970 2010-11-04 00:55:30Z reedy $'; } } \ No newline at end of file diff --git a/includes/api/ApiFormatDbg.php b/includes/api/ApiFormatDbg.php index 26afd329..d4aeb0b8 100644 --- a/includes/api/ApiFormatDbg.php +++ b/includes/api/ApiFormatDbg.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Oct 22, 2006 + * + * 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 @@ -19,22 +18,25 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** + * API PHP's var_export() output formatter * @ingroup API */ class ApiFormatDbg extends ApiFormatBase { public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); } public function getMimeType() { @@ -49,10 +51,10 @@ class ApiFormatDbg extends ApiFormatBase { } public function getDescription() { - return 'Output data in PHP\'s var_export() format' . parent :: getDescription(); + return 'Output data in PHP\'s var_export() format' . parent::getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatDbg.php 61444 2010-01-23 22:52:40Z reedy $'; + return __CLASS__ . ': $Id: ApiFormatDbg.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFormatDump.php b/includes/api/ApiFormatDump.php new file mode 100644 index 00000000..6197563d --- /dev/null +++ b/includes/api/ApiFormatDump.php @@ -0,0 +1,64 @@ +getResultData() ); + $result = ob_get_contents(); + ob_end_clean(); + $this->printText( $result ); + } + + public function getDescription() { + return 'Output data in PHP\'s var_dump() format' . parent::getDescription(); + } + + public function getVersion() { + return __CLASS__ . ': $Id$'; + } +} diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php index 69686bfb..7c02baa0 100644 --- a/includes/api/ApiFormatJson.php +++ b/includes/api/ApiFormatJson.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 19, 2006 + * + * Copyright © 2006 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 @@ -19,16 +18,19 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** + * API JSON output formatter * @ingroup API */ class ApiFormatJson extends ApiFormatBase { @@ -36,7 +38,7 @@ class ApiFormatJson extends ApiFormatBase { private $mIsRaw; public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); $this->mIsRaw = ( $format === 'rawfm' ); } @@ -59,40 +61,42 @@ class ApiFormatJson extends ApiFormatBase { } public function execute() { - $prefix = $suffix = ""; + $prefix = $suffix = ''; $params = $this->extractRequestParams(); $callback = $params['callback']; if ( !is_null( $callback ) ) { - $prefix = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", "", $callback ) . "("; - $suffix = ")"; + $prefix = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", '', $callback ) . '('; + $suffix = ')'; } $this->printText( $prefix . - FormatJson::encode( $this->getResultData(), $this->getIsHtml() ) . - $suffix ); + FormatJson::encode( $this->getResultData(), $this->getIsHtml() ) . + $suffix + ); } public function getAllowedParams() { - return array ( + return array( 'callback' => null, ); } public function getParamDescription() { - return array ( + return array( 'callback' => 'If specified, wraps the output into a given function call. For safety, all user-specific data will be restricted.', ); } public function getDescription() { - if ( $this->mIsRaw ) - return 'Output data with the debuging elements in JSON format' . parent :: getDescription(); - else - return 'Output data in JSON format' . parent :: getDescription(); + 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 62354 2010-02-12 06:44:16Z mah $'; + return __CLASS__ . ': $Id: ApiFormatJson.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php index dd03c300..e83941d4 100644 --- a/includes/api/ApiFormatPhp.php +++ b/includes/api/ApiFormatPhp.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 22, 2006 + * + * Copyright © 2006 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 @@ -19,22 +18,25 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** + * API Serialized PHP output formatter * @ingroup API */ class ApiFormatPhp extends ApiFormatBase { public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); } public function getMimeType() { @@ -46,10 +48,10 @@ class ApiFormatPhp extends ApiFormatBase { } public function getDescription() { - return 'Output data in serialized PHP format' . parent :: getDescription(); + return 'Output data in serialized PHP format' . parent::getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatPhp.php 60930 2010-01-11 15:55:52Z simetrical $'; + return __CLASS__ . ': $Id: ApiFormatPhp.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFormatRaw.php b/includes/api/ApiFormatRaw.php index 8bb66aea..98a50652 100644 --- a/includes/api/ApiFormatRaw.php +++ b/includes/api/ApiFormatRaw.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Feb 2, 2009 + * + * Copyright © 2009 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** @@ -40,36 +41,38 @@ class ApiFormatRaw extends ApiFormatBase { * @param $errorFallback Formatter object to fall back on for errors */ public function __construct( $main, $errorFallback ) { - parent :: __construct( $main, 'raw' ); + 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" ); + + 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 61437 2010-01-23 22:26:40Z reedy $'; + return __CLASS__ . ': $Id: ApiFormatRaw.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFormatTxt.php b/includes/api/ApiFormatTxt.php index 1627dde6..bbb268f1 100644 --- a/includes/api/ApiFormatTxt.php +++ b/includes/api/ApiFormatTxt.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Oct 22, 2006 + * + * 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 @@ -19,22 +18,25 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** + * API Text output formatter * @ingroup API */ class ApiFormatTxt extends ApiFormatBase { public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); } public function getMimeType() { @@ -49,10 +51,10 @@ class ApiFormatTxt extends ApiFormatBase { } public function getDescription() { - return 'Output data in PHP\'s print_r() format' . parent :: getDescription(); + return 'Output data in PHP\'s print_r() format' . parent::getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatTxt.php 61444 2010-01-23 22:52:40Z reedy $'; + return __CLASS__ . ': $Id: ApiFormatTxt.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php index e95e540b..6c1e3066 100644 --- a/includes/api/ApiFormatWddx.php +++ b/includes/api/ApiFormatWddx.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 22, 2006 + * + * Copyright © 2006 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 @@ -19,22 +18,25 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** + * API WDDX output formatter * @ingroup API */ class ApiFormatWddx extends ApiFormatBase { public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); } public function getMimeType() { @@ -53,8 +55,8 @@ class ApiFormatWddx extends ApiFormatBase { } else { // Don't do newlines and indentation if we weren't asked // for pretty output - $nl = ( $this->getIsHtml() ? "" : "\n" ); - $indstr = " "; + $nl = ( $this->getIsHtml() ? '' : "\n" ); + $indstr = ' '; $this->printText( "$nl" ); $this->printText( "$nl" ); $this->printText( "$indstr
$nl" ); @@ -69,11 +71,11 @@ class ApiFormatWddx extends ApiFormatBase { * 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" ); + $indstr = ( $this->getIsHtml() ? '' : str_repeat( ' ', $indent ) ); + $indstr2 = ( $this->getIsHtml() ? '' : str_repeat( ' ', $indent + 2 ) ); + $nl = ( $this->getIsHtml() ? '' : "\n" ); switch ( gettype( $elemValue ) ) { - case 'array' : + case 'array': // Check whether we've got an associative array () // or a regular array () $cnt = count( $elemValue ); @@ -81,8 +83,9 @@ class ApiFormatWddx extends ApiFormatBase { // Regular array $this->printText( $indstr . Xml::element( 'array', array( 'length' => $cnt ), null ) . $nl ); - foreach ( $elemValue as $subElemValue ) + foreach ( $elemValue as $subElemValue ) { $this->slowWddxPrinter( $subElemValue, $indent + 2 ); + } $this->printText( "$indstr$nl" ); } else { // Associative array () @@ -97,23 +100,23 @@ class ApiFormatWddx extends ApiFormatBase { $this->printText( "$indstr$nl" ); } break; - case 'integer' : - case 'double' : + case 'integer': + case 'double': $this->printText( $indstr . Xml::element( 'number', null, $elemValue ) . $nl ); break; - case 'string' : + case 'string': $this->printText( $indstr . Xml::element( 'string', null, $elemValue ) . $nl ); break; - default : - ApiBase :: dieDebug( __METHOD__, 'Unknown type ' . gettype( $elemValue ) ); + default: + ApiBase::dieDebug( __METHOD__, 'Unknown type ' . gettype( $elemValue ) ); } } public function getDescription() { - return 'Output data in WDDX format' . parent :: getDescription(); + return 'Output data in WDDX format' . parent::getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatWddx.php 61437 2010-01-23 22:26:40Z reedy $'; + return __CLASS__ . ': $Id: ApiFormatWddx.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php index a3758a49..45ab73ef 100644 --- a/includes/api/ApiFormatXml.php +++ b/includes/api/ApiFormatXml.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 19, 2006 + * + * Copyright © 2006 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 @@ -19,16 +18,19 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** + * API XML output formatter * @ingroup API */ class ApiFormatXml extends ApiFormatBase { @@ -38,7 +40,7 @@ class ApiFormatXml extends ApiFormatBase { private $mXslt = null; public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); } public function getMimeType() { @@ -59,45 +61,50 @@ class ApiFormatXml extends ApiFormatBase { $this->mXslt = $params['xslt']; $this->printText( '' ); - if ( !is_null( $this->mXslt ) ) + if ( !is_null( $this->mXslt ) ) { $this->addXslt(); - $this->printText( self::recXmlPrint( $this->mRootElemName, + } + $this->printText( + self::recXmlPrint( $this->mRootElemName, $this->getResultData(), $this->getIsHtml() ? - 2 : null, - $this->mDoubleQuote ) ); + $this->mDoubleQuote + ) + ); } /** - * This method takes an array and converts it to XML. - * There are several noteworthy cases: - * - * If array contains a key '_element', then the code assumes that ALL other keys are not important and replaces them with the value['_element']. - * Example: name='root', value = array( '_element'=>'page', 'x', 'y', 'z') creates x y z - * - * If any of the array's element key is '*', then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content. - * Example: name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10) creates text - * - * 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. - */ + * This method takes an array and converts it to XML. + * There are several noteworthy cases: + * + * If array contains a key '_element', then the code assumes that ALL other keys are not important and replaces them with the value['_element']. + * Example: name='root', value = array( '_element'=>'page', 'x', 'y', 'z') creates x y z + * + * If any of the array's element key is '*', then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content. + * Example: name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10) creates text + * + * 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. + */ 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 ); switch ( gettype( $elemValue ) ) { - case 'array' : - if ( isset ( $elemValue['*'] ) ) { + case 'array': + if ( isset( $elemValue['*'] ) ) { $subElemContent = $elemValue['*']; - if ( $doublequote ) + if ( $doublequote ) { $subElemContent = Sanitizer::encodeAttribute( $subElemContent ); - unset ( $elemValue['*'] ); - + } + unset( $elemValue['*'] ); + // Add xml:space="preserve" to the // element so XML parsers will leave // whitespace in the content alone @@ -106,59 +113,65 @@ 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 (); + $indElements = array(); + $subElements = array(); foreach ( $elemValue as $subElemId => & $subElemValue ) { - if ( is_string( $subElemValue ) && $doublequote ) + if ( is_string( $subElemValue ) && $doublequote ) { $subElemValue = Sanitizer::encodeAttribute( $subElemValue ); - + } + if ( gettype( $subElemId ) === 'integer' ) { $indElements[] = $subElemValue; - unset ( $elemValue[$subElemId] ); + unset( $elemValue[$subElemId] ); } elseif ( is_array( $subElemValue ) ) { $subElements[$subElemId] = $subElemValue; 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 ) ) { $retval .= $indstr . Xml::element( $elemName, $elemValue, $subElemContent ); } elseif ( !count( $indElements ) && !count( $subElements ) ) { - $retval .= $indstr . Xml::element( $elemName, $elemValue ); + $retval .= $indstr . Xml::element( $elemName, $elemValue ); } else { $retval .= $indstr . Xml::element( $elemName, $elemValue, null ); - foreach ( $subElements as $subElemId => & $subElemValue ) + foreach ( $subElements as $subElemId => & $subElemValue ) { $retval .= self::recXmlPrint( $subElemId, $subElemValue, $indent ); + } - foreach ( $indElements as $subElemId => & $subElemValue ) + foreach ( $indElements as &$subElemValue ) { $retval .= self::recXmlPrint( $subElemIndName, $subElemValue, $indent ); + } $retval .= $indstr . Xml::closeElement( $elemName ); } break; - case 'object' : + case 'object': // ignore break; - default : + default: $retval .= $indstr . Xml::element( $elemName, null, $elemValue ); break; } return $retval; } + function addXslt() { $nt = Title::newFromText( $this->mXslt ); if ( is_null( $nt ) || !$nt->exists() ) { @@ -175,26 +188,26 @@ class ApiFormatXml extends ApiFormatBase { } $this->printText( 'escapeLocalURL( 'action=raw' ) . '" type="text/xsl" ?>' ); } - + public function getAllowedParams() { - return array ( + return array( 'xmldoublequote' => false, 'xslt' => null, ); } public function getParamDescription() { - return array ( - 'xmldoublequote' => 'If specified, double quotes all attributes and content.', + 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(); + return 'Output data in XML format' . parent::getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatXml.php 62402 2010-02-13 00:09:05Z reedy $'; + return __CLASS__ . ': $Id: ApiFormatXml.php 73753 2010-09-25 16:56:03Z reedy $'; } } diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php index 39381b0f..ccf52746 100644 --- a/includes/api/ApiFormatYaml.php +++ b/includes/api/ApiFormatYaml.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 19, 2006 + * + * Copyright © 2006 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 @@ -19,22 +18,25 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiFormatBase.php' ); + require_once( 'ApiFormatBase.php' ); } /** + * API YAML output formatter * @ingroup API */ class ApiFormatYaml extends ApiFormatBase { public function __construct( $main, $format ) { - parent :: __construct( $main, $format ); + parent::__construct( $main, $format ); } public function getMimeType() { @@ -42,14 +44,14 @@ class ApiFormatYaml extends ApiFormatBase { } public function execute() { - $this->printText( Spyc :: YAMLDump( $this->getResultData() ) ); + $this->printText( Spyc::YAMLDump( $this->getResultData() ) ); } public function getDescription() { - return 'Output data in YAML format' . parent :: getDescription(); + return 'Output data in YAML format' . parent::getDescription(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiFormatYaml.php 60930 2010-01-11 15:55:52Z simetrical $'; + return __CLASS__ . ': $Id: ApiFormatYaml.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php deleted file mode 100644 index 30f860dd..00000000 --- a/includes/api/ApiFormatYaml_spyc.php +++ /dev/null @@ -1,236 +0,0 @@ - - * @see http://spyc.sourceforge.net/ - * @copyright Copyright 2005-2006 Chris Wanstrath - * @license http://www.opensource.org/licenses/mit-license.php MIT License - */ - -/** - * The Simple PHP YAML Class. - * - * This class can be used to read a YAML file and convert its contents - * into a PHP array. It currently supports a very limited subsection of - * the YAML spec. - * - * @ingroup API - */ -class Spyc { - - /** - * Dump YAML from PHP array statically - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as nothing.yml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @return string - * @param $array Array: PHP array - * @param $indent Integer: Pass in false to use the default, which is 2 - * @param $wordwrap Integer: Pass in 0 for no wordwrap, false for default (40) - */ - public static function YAMLDump( $array, $indent = false, $wordwrap = false ) { - $spyc = new Spyc; - return $spyc->dump( $array, $indent, $wordwrap ); - } - - /** - * Dump PHP array to YAML - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as tasteful.yml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @public - * @return string - * @param $array Array: PHP array - * @param $indent Integer: Pass in false to use the default, which is 2 - * @param $wordwrap Integer: Pass in 0 for no wordwrap, false for default (40) - */ - function dump( $array, $indent = false, $wordwrap = false ) { - // Dumps to some very clean YAML. We'll have to add some more features - // and options soon. And better support for folding. - - // New features and options. - if ( $indent === false or !is_numeric( $indent ) ) { - $this->_dumpIndent = 2; - } else { - $this->_dumpIndent = $indent; - } - - if ( $wordwrap === false or !is_numeric( $wordwrap ) ) { - $this->_dumpWordWrap = 40; - } else { - $this->_dumpWordWrap = $wordwrap; - } - - // New YAML document - $string = "---\n"; - - // Start at the base of the array and move through it. - foreach ( $array as $key => $value ) { - $string .= $this->_yamlize( $key, $value, 0 ); - } - return $string; - } - - /**** Private Properties ****/ - - private $_haveRefs; - private $_allNodes; - private $_lastIndent; - private $_lastNode; - private $_inBlock; - private $_isInline; - private $_dumpIndent; - private $_dumpWordWrap; - - /**** Private Methods ****/ - - /** - * Attempts to convert a key / value array item to YAML - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - private function _yamlize( $key, $value, $indent ) { - if ( is_array( $value ) ) { - // It has children. What to do? - // Make it the right kind of item - $string = $this->_dumpNode( $key, null, $indent ); - // Add the indent - $indent += $this->_dumpIndent; - // Yamlize the array - $string .= $this->_yamlizeArray( $value, $indent ); - } elseif ( !is_array( $value ) ) { - // It doesn't have children. Yip. - $string = $this->_dumpNode( $key, $value, $indent ); - } - return $string; - } - - /** - * Attempts to convert an array to YAML - * @return string - * @param $array The array you want to convert - * @param $indent The indent of the current level - */ - private function _yamlizeArray( $array, $indent ) { - if ( is_array( $array ) ) { - $string = ''; - foreach ( $array as $key => $value ) { - $string .= $this->_yamlize( $key, $value, $indent ); - } - return $string; - } else { - return false; - } - } - - /** - * Find out whether a string needs to be output as a literal rather than in plain style. - * Added by Roan Kattouw 13-03-2008 - * @param $value The string to check - * @return bool - */ - function _needLiteral( $value ) { - // Check whether the string contains # or : or begins with any of: - // [ - ? , [ ] { } ! * & | > ' " % @ ` ] - // or is a number or contains newlines - return (bool)( gettype( $value ) == "string" && - ( is_numeric( $value ) || - strpos( $value, "\n" ) || - preg_match( "/[#:]/", $value ) || - preg_match( "/^[-?,[\]{}!*&|>'\"%@`]/", $value ) ) ); - } - - /** - * Returns YAML from a key and a value - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - private function _dumpNode( $key, $value, $indent ) { - // do some folding here, for blocks - if ( $this->_needLiteral( $value ) ) { - $value = $this->_doLiteralBlock( $value, $indent ); - } else { - $value = $this->_doFolding( $value, $indent ); - } - - $spaces = str_repeat( ' ', $indent ); - - if ( is_int( $key ) ) { - // It's a sequence - if ( $value !== '' && !is_null( $value ) ) - $string = $spaces . '- ' . $value . "\n"; - else - $string = $spaces . "-\n"; - } else { - if ($key == '*') //bug 21922 - Quote asterix used as keys - $key = "'*'"; - - // It's mapped - if ( $value !== '' && !is_null( $value ) ) - $string = $spaces . $key . ': ' . $value . "\n"; - else - $string = $spaces . $key . ":\n"; - } - return $string; - } - - /** - * Creates a literal block for dumping - * @return string - * @param $value - * @param $indent int The value of the indent - */ - private function _doLiteralBlock( $value, $indent ) { - $exploded = explode( "\n", $value ); - $newValue = '|-'; - $indent += $this->_dumpIndent; - $spaces = str_repeat( ' ', $indent ); - foreach ( $exploded as $line ) { - $newValue .= "\n" . $spaces . trim( $line ); - } - return $newValue; - } - - /** - * Folds a string of text, if necessary - * @return string - * @param $value The string you wish to fold - */ - private function _doFolding( $value, $indent ) { - // Don't do anything if wordwrap is set to 0 - if ( $this->_dumpWordWrap === 0 ) { - return $value; - } - - if ( strlen( $value ) > $this->_dumpWordWrap ) { - $indent += $this->_dumpIndent; - $indent = str_repeat( ' ', $indent ); - $wrapped = wordwrap( $value, $this->_dumpWordWrap, "\n$indent" ); - $value = ">-\n" . $indent . $wrapped; - } - return $value; - } -} diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index 1f32e019..eedbde13 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 6, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -36,14 +37,71 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiHelp extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } /** - * Stub module for displaying help when no parameters are given + * Module for displaying help */ public function execute() { - $this->dieUsage( '', 'help' ); + // Get parameters + $params = $this->extractRequestParams(); + + if ( !isset( $params['modules'] ) && !isset( $params['querymodules'] ) ) { + $this->dieUsage( '', 'help' ); + } + + $this->getMain()->setHelp(); + + $result = $this->getResult(); + $queryObj = new ApiQuery( $this->getMain(), 'query' ); + $r = array(); + if ( is_array( $params['modules'] ) ) { + $modArr = $this->getMain()->getModules(); + + foreach ( $params['modules'] as $m ) { + if ( !isset( $modArr[$m] ) ) { + $r[] = array( 'name' => $m, 'missing' => '' ); + continue; + } + $module = new $modArr[$m]( $this->getMain(), $m ); + + $r[] = $this->buildModuleHelp( $module, 'action' ); + } + } + + if ( is_array( $params['querymodules'] ) ) { + $qmodArr = $queryObj->getModules(); + + foreach ( $params['querymodules'] as $qm ) { + if ( !isset( $qmodArr[$qm] ) ) { + $r[] = array( 'name' => $qm, 'missing' => '' ); + continue; + } + $module = new $qmodArr[$qm]( $this, $qm ); + $type = $queryObj->getModuleType( $qm ); + + if ( $type === null ) { + $r[] = array( 'name' => $qm, 'missing' => '' ); + continue; + } + + $r[] = $this->buildModuleHelp( $module, $type ); + } + } + $result->setIndexedTagName( $r, 'module' ); + $result->addValue( null, $this->getModuleName(), $r ); + } + + private function buildModuleHelp( $module, $type ) { + $msg = ApiMain::makeHelpMsgHeader( $module, $type ); + + $msg2 = $module->makeHelpMsg(); + if ( $msg2 !== false ) { + $msg .= $msg2; + } + + return $msg; } public function shouldCheckMaxlag() { @@ -54,13 +112,44 @@ class ApiHelp extends ApiBase { return false; } + public function getAllowedParams() { + return array( + 'modules' => array( + ApiBase::PARAM_ISMULTI => true + ), + 'querymodules' => array( + ApiBase::PARAM_ISMULTI => true + ), + ); + } + + public function getParamDescription() { + return array( + 'modules' => 'List of module names (value of the action= parameter)', + 'querymodules' => 'List of query module names (value of prop=, meta= or list= parameter)', + ); + } + public function getDescription() { - return array ( - 'Display this help screen.' + return 'Display this help screen. Or the help screen for the specified module'; + } + + protected function getExamples() { + return array( + 'Whole help page:', + ' api.php?action=help', + 'Module (action) help page:', + ' api.php?action=help&modules=protect', + 'Query (list) modules help page:', + ' api.php?action=help&querymodules=categorymembers', + 'Query (prop) modules help page:', + ' api.php?action=help&querymodules=info', + 'Query (meta) modules help page:', + ' api.php?action=help&querymodules=siteinfo', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiHelp.php 60930 2010-01-11 15:55:52Z simetrical $'; + return __CLASS__ . ': $Id: ApiHelp.php 73863 2010-09-28 02:33:43Z brion $'; } } diff --git a/includes/api/ApiImport.php b/includes/api/ApiImport.php index d33a472a..1b5153f9 100644 --- a/includes/api/ApiImport.php +++ b/includes/api/ApiImport.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Feb 4, 2009 + * + * Copyright © 2009 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -36,58 +37,54 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiImport extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { global $wgUser; - if ( !$wgUser->isAllowed( 'import' ) ) + if ( !$wgUser->isAllowed( 'import' ) ) { $this->dieUsageMsg( array( 'cantimport' ) ); + } $params = $this->extractRequestParams(); - $source = null; $isUpload = false; - if ( isset( $params['interwikisource'] ) ) - { - if ( !isset( $params['interwikipage'] ) ) + if ( isset( $params['interwikisource'] ) ) { + if ( !isset( $params['interwikipage'] ) ) { $this->dieUsageMsg( array( 'missingparam', 'interwikipage' ) ); + } $source = ImportStreamSource::newFromInterwiki( - $params['interwikisource'], - $params['interwikipage'], - $params['fullhistory'], - $params['templates'] ); - } - else - { + $params['interwikisource'], + $params['interwikipage'], + $params['fullhistory'], + $params['templates'] + ); + } else { $isUpload = true; - if ( !$wgUser->isAllowed( 'importupload' ) ) + 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 ) ) - // This shouldn't happen - $this->dieUsageMsg( array( 'import-unknownerror', $source->getMessage() ) ); - - $importer = new WikiImporter( $source ); - if ( isset( $params['namespace'] ) ) + if ( !$source->isOK() ) { + $this->dieUsageMsg( $source->getErrorsArray() ); + } + + $importer = new WikiImporter( $source->value ); + if ( isset( $params['namespace'] ) ) { $importer->setTargetNamespace( $params['namespace'] ); - $reporter = new ApiImportReporter( $importer, $isUpload, - $params['interwikisource'], - $params['summary'] ); - - $result = $importer->doImport(); - 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->dieUsageMsg( array( 'import-unknownerror', $result->getMessage() ) ); // This shouldn't happen + } + $reporter = new ApiImportReporter( + $importer, + $isUpload, + $params['interwikisource'], + $params['summary'] + ); + + try { + $importer->doImport(); + } catch ( MWException $e ) { + $this->dieUsageMsg( array( 'import-unknownerror', $e->getMessage() ) ); + } $resultData = $reporter->getData(); $this->getResult()->setIndexedTagName( $resultData, 'page' ); @@ -104,24 +101,24 @@ class ApiImport extends ApiBase { public function getAllowedParams() { global $wgImportSources; - return array ( + return array( 'token' => null, 'summary' => null, 'xml' => null, 'interwikisource' => array( - ApiBase :: PARAM_TYPE => $wgImportSources + ApiBase::PARAM_TYPE => $wgImportSources ), 'interwikipage' => null, 'fullhistory' => false, 'templates' => false, 'namespace' => array( - ApiBase :: PARAM_TYPE => 'namespace' + ApiBase::PARAM_TYPE => 'namespace' ) ); } public function getParamDescription() { - return array ( + return array( 'token' => 'Import token obtained through prop=info', 'summary' => 'Import summary', 'xml' => 'Uploaded XML file', @@ -134,11 +131,9 @@ class ApiImport extends ApiBase { } public function getDescription() { - return array ( - 'Import a page from another wiki, or an XML file' - ); + return 'Import a page from another wiki, or an XML file'; } - + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'cantimport' ), @@ -148,7 +143,7 @@ class ApiImport extends ApiBase { array( 'import-unknownerror', 'result' ), ) ); } - + public function needsToken() { return true; } @@ -160,12 +155,12 @@ class ApiImport extends ApiBase { protected function getExamples() { return array( 'Import [[meta:Help:Parserfunctions]] to namespace 100 with full history:', - ' api.php?action=import&interwikisource=meta&interwikipage=Help:ParserFunctions&namespace=100&fullhistory&token=123ABC', + ' api.php?action=import&interwikisource=meta&interwikipage=Help:ParserFunctions&namespace=100&fullhistory=&token=123ABC', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiImport.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiImport.php 77800 2010-12-05 14:22:49Z ialex $'; } } @@ -176,8 +171,7 @@ class ApiImport extends ApiBase { class ApiImportReporter extends ImportReporter { private $mResultArr = array(); - function reportPage( $title, $origTitle, $revisionCount, $successCount ) - { + function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) { // Add a result entry $r = array(); ApiQueryBase::addTitleInfo( $r, $title ); @@ -185,11 +179,10 @@ class ApiImportReporter extends ImportReporter { $this->mResultArr[] = $r; // Piggyback on the parent to do the logging - parent::reportPage( $title, $origTitle, $revisionCount, $successCount ); + parent::reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ); } - function getData() - { + function getData() { return $this->mResultArr; } -} \ No newline at end of file +} diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php index 442bc44c..0675de7b 100644 --- a/includes/api/ApiLogin.php +++ b/includes/api/ApiLogin.php @@ -1,10 +1,9 @@ @gmail.com, * Daniel Cannon (cannon dot danielc at gmail dot com) * @@ -20,8 +19,10 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { @@ -68,10 +69,11 @@ class ApiLogin extends ApiBase { } $loginForm = new LoginForm( $req ); + + global $wgCookiePrefix, $wgUser, $wgPasswordAttemptThrottle; + switch ( $authRes = $loginForm->authenticateUserData() ) { case LoginForm::SUCCESS: - global $wgUser, $wgCookiePrefix; - $wgUser->setOption( 'rememberpassword', 1 ); $wgUser->setCookies(); @@ -87,15 +89,14 @@ class ApiLogin extends ApiBase { $result['cookieprefix'] = $wgCookiePrefix; $result['sessionid'] = session_id(); break; - + case LoginForm::NEED_TOKEN: - global $wgCookiePrefix; $result['result'] = 'NeedToken'; $result['token'] = $loginForm->getLoginToken(); $result['cookieprefix'] = $wgCookiePrefix; $result['sessionid'] = session_id(); break; - + case LoginForm::WRONG_TOKEN: $result['result'] = 'WrongToken'; break; @@ -131,7 +132,6 @@ class ApiLogin extends ApiBase { break; case LoginForm::THROTTLED: - global $wgPasswordAttemptThrottle; $result['result'] = 'Throttled'; $result['wait'] = intval( $wgPasswordAttemptThrottle['seconds'] ); break; @@ -179,7 +179,7 @@ class ApiLogin extends ApiBase { 'In the event of a successful log-in, a cookie will be attached', 'to your session. In the event of a failed log-in, you will not ', 'be able to attempt another log-in through this method for 5 seconds.', - 'This is to prevent password guessing by automated password crackers.' + 'This is to prevent password guessing by automated password crackers' ); } @@ -206,6 +206,6 @@ class ApiLogin extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiLogin.php 64697 2010-04-07 09:05:05Z catrope $'; + return __CLASS__ . ': $Id: ApiLogin.php 76080 2010-11-05 11:54:35Z catrope $'; } } diff --git a/includes/api/ApiLogout.php b/includes/api/ApiLogout.php index 6637ee09..89326915 100644 --- a/includes/api/ApiLogout.php +++ b/includes/api/ApiLogout.php @@ -1,11 +1,10 @@ @gmail.com, + * Created on Jan 4, 2008 + * + * Copyright © 2008 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -37,14 +38,14 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiLogout extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { global $wgUser; $oldName = $wgUser->getName(); $wgUser->logout(); - + // Give extensions to do something after user logout $injected_html = ''; wfRunHooks( 'UserLogoutComplete', array( &$wgUser, &$injected_html, $oldName ) ); @@ -55,17 +56,15 @@ class ApiLogout extends ApiBase { } public function getAllowedParams() { - return array (); + return array(); } public function getParamDescription() { - return array (); + return array(); } public function getDescription() { - return array ( - 'This module is used to logout and clear session data' - ); + return 'This module is used to logout and clear session data'; } protected function getExamples() { @@ -75,6 +74,6 @@ class ApiLogout extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiLogout.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiLogout.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index fa6957b6..d5238a51 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 4, 2006 + * + * Copyright © 2006 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 @@ -19,19 +18,18 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @defgroup API API */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } -/** - * @defgroup API API - */ - /** * This is the main API class, used for both external and internal processing. * When executed, it will create the requested formatter object, @@ -55,7 +53,7 @@ class ApiMain extends ApiBase { /** * List of available modules: action name => module class */ - private static $Modules = array ( + private static $Modules = array( 'login' => 'ApiLogin', 'logout' => 'ApiLogout', 'query' => 'ApiQuery', @@ -65,6 +63,7 @@ class ApiMain extends ApiBase { 'feedwatchlist' => 'ApiFeedWatchlist', 'help' => 'ApiHelp', 'paraminfo' => 'ApiParamInfo', + 'rsd' => 'ApiRsd', // Write modules 'purge' => 'ApiPurge', @@ -87,7 +86,7 @@ class ApiMain extends ApiBase { /** * List of available formats: format name => format class */ - private static $Formats = array ( + private static $Formats = array( 'json' => 'ApiFormatJson', 'jsonfm' => 'ApiFormatJson', 'php' => 'ApiFormatPhp', @@ -102,7 +101,9 @@ class ApiMain extends ApiBase { 'txt' => 'ApiFormatTxt', 'txtfm' => 'ApiFormatTxt', 'dbg' => 'ApiFormatDbg', - 'dbgfm' => 'ApiFormatDbg' + 'dbgfm' => 'ApiFormatDbg', + 'dump' => 'ApiFormatDump', + 'dumpfm' => 'ApiFormatDump', ); /** @@ -111,17 +112,17 @@ class ApiMain extends ApiBase { * 'params' => array ( $someVarToSubst ) ), * ); */ - private static $mRights = array( 'writeapi' => array( - 'msg' => 'Use of the write API', - 'params' => array() - ), - 'apihighlimits' => array( - 'msg' => 'Use higher limits in API queries (Slow queries: $1 results; Fast queries: $2 results). The limits for slow queries also apply to multivalue parameters.', - 'params' => array ( ApiMain::LIMIT_SML2, ApiMain::LIMIT_BIG2 ) - ) + private 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( ApiBase::LIMIT_SML2, ApiBase::LIMIT_BIG2 ) + ) ); - private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames; private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest; private $mInternalMode, $mSquidMaxage, $mModule; @@ -130,20 +131,18 @@ class ApiMain extends ApiBase { private $mCacheControl = array(); /** - * Constructs an instance of ApiMain that utilizes the module and format specified by $request. - * - * @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 - */ + * Constructs an instance of ApiMain that utilizes the module and format specified by $request. + * + * @param $request WebRequest - 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 ) { - $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 ) { - // Impose module restrictions. // If the current user cannot read, // Remove all modules other than login @@ -158,17 +157,17 @@ class ApiMain extends ApiBase { } global $wgAPIModules; // extension modules - $this->mModules = $wgAPIModules + self :: $Modules; + $this->mModules = $wgAPIModules + self::$Modules; $this->mModuleNames = array_keys( $this->mModules ); - $this->mFormats = self :: $Formats; + $this->mFormats = self::$Formats; $this->mFormatNames = array_keys( $this->mFormats ); $this->mResult = new ApiResult( $this ); $this->mShowVersions = false; $this->mEnableWrite = $enableWrite; - $this->mRequest = & $request; + $this->mRequest = &$request; $this->mSquidMaxage = - 1; // flag for executeActionWithErrorHandling() $this->mCommit = false; @@ -183,6 +182,7 @@ class ApiMain extends ApiBase { /** * Return the request object that contains client's request + * @return WebRequest */ public function getRequest() { return $this->mRequest; @@ -190,6 +190,8 @@ class ApiMain extends ApiBase { /** * Get the ApiResult object associated with current request + * + * @return ApiResult */ public function getResult() { return $this->mResult; @@ -203,14 +205,12 @@ class ApiMain extends ApiBase { } /** - * Only kept for backwards compatibility - * @deprecated Use isWriteMode() instead + * Get the result formatter object. Only works after setupExecuteAction() + * + * @return ApiFormatBase */ - public function requestWriteMode() { - if ( !$this->mEnableWrite ) - $this->dieUsageMsg( array( 'writedisabled' ) ); - if ( wfReadOnly() ) - $this->dieUsageMsg( array( 'readonlytext' ) ); + public function getPrinter() { + return $this->mPrinter; } /** @@ -226,31 +226,31 @@ class ApiMain extends ApiBase { /** * Set the type of caching headers which will be sent. * - * @param $mode One of: - * - 'public': Cache this object in public caches, if the maxage or smaxage + * @param $mode String One of: + * - 'public': Cache this object in public caches, if the maxage or smaxage * parameter is set, or if setCacheMaxAge() was called. If a maximum age is * not provided by any of these means, the object will be private. * - 'private': Cache this object only in private client-side caches. * - 'anon-public-user-private': Make this object cacheable for logged-out - * users, but private for logged-in users. IMPORTANT: If this is set, it must be - * set consistently for a given URL, it cannot be set differently depending on + * users, but private for logged-in users. IMPORTANT: If this is set, it must be + * set consistently for a given URL, it cannot be set differently depending on * things like the contents of the database, or whether the user is logged in. * * If the wiki does not allow anonymous users to read it, the mode set here - * will be ignored, and private caching headers will always be sent. In other words, + * will be ignored, and private caching headers will always be sent. In other words, * the "public" mode is equivalent to saying that the data sent is as public as a page * view. * - * For user-dependent data, the private mode should generally be used. The - * anon-public-user-private mode should only be used where there is a particularly + * For user-dependent data, the private mode should generally be used. The + * anon-public-user-private mode should only be used where there is a particularly * good performance reason for caching the anonymous response, but where the - * response to logged-in users may differ, or may contain private data. + * response to logged-in users may differ, or may contain private data. * * If this function is never called, then the default will be the private mode. */ public function setCacheMode( $mode ) { if ( !in_array( $mode, array( 'private', 'public', 'anon-public-user-private' ) ) ) { - wfDebug( __METHOD__.": unrecognised cache mode \"$mode\"\n" ); + wfDebug( __METHOD__ . ": unrecognised cache mode \"$mode\"\n" ); // Ignore for forwards-compatibility return; } @@ -258,18 +258,18 @@ class ApiMain extends ApiBase { if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { // Private wiki, only private headers if ( $mode !== 'private' ) { - wfDebug( __METHOD__.": ignoring request for $mode cache mode, private wiki\n" ); + wfDebug( __METHOD__ . ": ignoring request for $mode cache mode, private wiki\n" ); return; } } - wfDebug( __METHOD__.": setting cache mode $mode\n" ); + wfDebug( __METHOD__ . ": setting cache mode $mode\n" ); $this->mCacheMode = $mode; } - + /** - * @deprecated Private caching is now the default, so there is usually no - * need to call this function. If there is a need, you can use + * @deprecated Private caching is now the default, so there is usually no + * need to call this function. If there is a need, you can use * $this->setCacheMode('private') */ public function setCachePrivate() { @@ -281,13 +281,13 @@ class ApiMain extends ApiBase { * Boolean values will be formatted as such, by including or omitting * without an equals sign. * - * Cache control values set here will only be used if the cache mode is not + * Cache control values set here will only be used if the cache mode is not * private, see setCacheMode(). */ public function setCacheControl( $directives ) { $this->mCacheControl = $directives + $this->mCacheControl; } - + /** * Make sure Vary: Cookie and friends are set. Use this when the output of a request * may be cached for anons but may not be cached for logged-in users. @@ -306,8 +306,9 @@ class ApiMain extends ApiBase { * Create an instance of an output formatter by its name */ public function createPrinterByName( $format ) { - if ( !isset( $this->mFormats[$format] ) ) + if ( !isset( $this->mFormats[$format] ) ) { $this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' ); + } return new $this->mFormats[$format] ( $this, $format ); } @@ -316,10 +317,11 @@ class ApiMain extends ApiBase { */ public function execute() { $this->profileIn(); - if ( $this->mInternalMode ) + if ( $this->mInternalMode ) { $this->executeAction(); - else + } else { $this->executeActionWithErrorHandling(); + } $this->profileOut(); } @@ -329,7 +331,6 @@ class ApiMain extends ApiBase { * have been accumulated, and replace it with an error message and a help screen. */ protected function executeActionWithErrorHandling() { - // In case an error occurs during data output, // clear the output buffer and print just the error information ob_start(); @@ -354,10 +355,11 @@ class ApiMain extends ApiBase { $this->setCacheMode( 'private' ); $headerStr = 'MediaWiki-API-Error: ' . $errCode; - if ( $e->getCode() === 0 ) + if ( $e->getCode() === 0 ) { header( $headerStr ); - else + } else { header( $headerStr, true, $e->getCode() ); + } // Reset and print just the error message ob_clean(); @@ -367,11 +369,11 @@ class ApiMain extends ApiBase { $this->printResult( true ); } - // Send cache headers after any code which might generate an error, to + // 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() && !$this->mPrinter->isDisabled() ) { echo wfReportTime(); } @@ -401,12 +403,6 @@ class ApiMain extends ApiBase { header( 'Cache-Control: private' ); return; } // 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; - if ( !( $wgUser instanceof StubUser ) ) { - wfDebug( __METHOD__." \$wgUser is unstubbed on a public request!\n" ); - } } // If nobody called setCacheMaxAge(), use the (s)maxage parameters @@ -446,26 +442,28 @@ class ApiMain extends ApiBase { $separator = ', '; } } - + header( "Cache-Control: $ccHeader" ); } /** * Replace the result data with the information about an exception. * Returns the error code + * @param $e Exception */ 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 ) ) + if ( !in_array( $value, $this->mFormatNames ) ) { $value = self::API_DEFAULT_FORMAT; + } $this->mPrinter = $this->createPrinterByName( $value ); - if ( $this->mPrinter->getNeedsRawData() ) + if ( $this->mPrinter->getNeedsRawData() ) { $this->getResult()->setRawMode(); + } } if ( $e instanceof UsageException ) { @@ -475,8 +473,9 @@ class ApiMain extends ApiBase { $errMessage = $e->getMessageArray(); // 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() ); + if ( $this->mPrinter->getWantsHelp() || $this->mAction == 'help' ) { + ApiResult::setContent( $errMessage, $this->makeHelpMsg() ); + } } else { global $wgShowSQLErrors, $wgShowExceptionDetails; @@ -484,37 +483,45 @@ class ApiMain extends ApiBase { // Something is seriously wrong // if ( ( $e instanceof DBQueryError ) && !$wgShowSQLErrors ) { - $info = "Database query error"; + $info = 'Database query error'; } else { $info = "Exception Caught: {$e->getMessage()}"; } - $errMessage = array ( + $errMessage = array( 'code' => 'internal_api_error_' . get_class( $e ), 'info' => $info, ); - ApiResult :: setContent( $errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : "" ); + 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 ) ) + if ( !is_null( $requestid ) ) { $this->getResult()->addValue( null, 'requestid', $requestid ); + } + // servedby is especially useful when debugging errors + $this->getResult()->addValue( null, 'servedby', wfHostName() ); $this->getResult()->addValue( null, 'error', $errMessage ); return $errMessage['code']; } /** - * Execute the actual module, without any error handling + * Set up for the execution. */ - protected function executeAction() { + protected function setupExecuteAction() { // First add the id to the top element $requestid = $this->getParameter( 'requestid' ); - if ( !is_null( $requestid ) ) + if ( !is_null( $requestid ) ) { $this->getResult()->addValue( null, 'requestid', $requestid ); + } + $servedby = $this->getParameter( 'servedby' ); + if ( $servedby ) { + $this->getResult()->addValue( null, 'servedby', wfHostName() ); + } $params = $this->extractRequestParams(); @@ -522,19 +529,26 @@ class ApiMain extends ApiBase { $this->mAction = $params['action']; if ( !is_string( $this->mAction ) ) { - $this->dieUsage( "The API requires a valid action parameter", 'unknown_action' ); + $this->dieUsage( 'The API requires a valid action parameter', 'unknown_action' ); } - + + return $params; + } + + /** + * Set up the module for response + * @return ApiBase The module that will handle this action + */ + protected function setupModule() { // Instantiate the module requested by the user $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 ( $salt !== false && !isset( $moduleParams['gettoken'] ) ) { if ( !isset( $moduleParams['token'] ) ) { $this->dieUsageMsg( array( 'missingparam', 'token' ) ); } else { @@ -544,7 +558,16 @@ class ApiMain extends ApiBase { } } } + return $module; + } + /** + * Check the max lag if necessary + * @param $module ApiBase object: Api module being used + * @param $params Array an array containing the request parameters. + * @return boolean True on success, false should exit immediately + */ + protected function checkMaxLag( $module, $params ) { if ( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) { // Check for maxlag global $wgShowHostnames; @@ -558,36 +581,75 @@ class ApiMain extends ApiBase { } else { $this->dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' ); } - return; + return false; } } + return true; + } + - global $wgUser, $wgGroupPermissions; - if ( $module->isReadMode() && !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) && !$wgUser->isAllowed( 'read' ) ) + /** + * Check for sufficient permissions to execute + * @param $module ApiBase An Api module + */ + protected function checkExecutePermissions( $module ) { + global $wgUser; + if ( $module->isReadMode() && !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) && + !$wgUser->isAllowed( 'read' ) ) + { $this->dieUsageMsg( array( 'readrequired' ) ); + } if ( $module->isWriteMode() ) { - if ( !$this->mEnableWrite ) + if ( !$this->mEnableWrite ) { $this->dieUsageMsg( array( 'writedisabled' ) ); - if ( !$wgUser->isAllowed( 'writeapi' ) ) + } + if ( !$wgUser->isAllowed( 'writeapi' ) ) { $this->dieUsageMsg( array( 'writerequired' ) ); - if ( wfReadOnly() ) + } + if ( wfReadOnly() ) { $this->dieReadOnly(); + } } + } - if ( !$this->mInternalMode ) { - // Ignore mustBePosted() for internal calls - 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 ) ) { - // Create an appropriate printer - $this->mPrinter = $this->createPrinterByName( $params['format'] ); - } + /** + * Check POST for external response and setup result printer + * @param $module ApiBase An Api module + * @param $params Array an array with the request parameters + */ + protected function setupExternalResponse( $module, $params ) { + // Ignore mustBePosted() for internal calls + if ( $module->mustBePosted() && !$this->mRequest->wasPosted() ) { + $this->dieUsageMsg( array( 'mustbeposted', $this->mAction ) ); + } - if ( $this->mPrinter->getNeedsRawData() ) - $this->getResult()->setRawMode(); + // See if custom printer is used + $this->mPrinter = $module->getCustomPrinter(); + if ( is_null( $this->mPrinter ) ) { + // Create an appropriate printer + $this->mPrinter = $this->createPrinterByName( $params['format'] ); + } + + if ( $this->mPrinter->getNeedsRawData() ) { + $this->getResult()->setRawMode(); + } + } + + /** + * Execute the actual module, without any error handling + */ + protected function executeAction() { + $params = $this->setupExecuteAction(); + $module = $this->setupModule(); + + $this->checkExecutePermissions( $module ); + + if ( !$this->checkMaxLag( $module, $params ) ) { + return; + } + + if ( !$this->mInternalMode ) { + $this->setupExternalResponse( $module, $params ); } // Execute @@ -610,10 +672,12 @@ class ApiMain extends ApiBase { $printer = $this->mPrinter; $printer->profileIn(); - /* If the help message is requested in the default (xmlfm) format, + /** + * If the help message is requested in the default (xmlfm) format, * tell the printer not to escape ampersands so that our links do - * not break. */ - $printer->setUnescapeAmps ( ( $this->mAction == 'help' || $isError ) + * not break. + */ + $printer->setUnescapeAmps( ( $this->mAction == 'help' || $isError ) && $printer->getFormat() == 'XML' && $printer->getIsHtml() ); $printer->initPrinter( $isError ); @@ -631,28 +695,29 @@ class ApiMain extends ApiBase { * See ApiBase for description. */ public function getAllowedParams() { - return array ( - 'format' => array ( - ApiBase :: PARAM_DFLT => ApiMain :: API_DEFAULT_FORMAT, - ApiBase :: PARAM_TYPE => $this->mFormatNames + return array( + 'format' => array( + ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT, + ApiBase::PARAM_TYPE => $this->mFormatNames ), - 'action' => array ( - ApiBase :: PARAM_DFLT => 'help', - ApiBase :: PARAM_TYPE => $this->mModuleNames + 'action' => array( + ApiBase::PARAM_DFLT => 'help', + ApiBase::PARAM_TYPE => $this->mModuleNames ), 'version' => false, - 'maxlag' => array ( - ApiBase :: PARAM_TYPE => 'integer' + 'maxlag' => array( + ApiBase::PARAM_TYPE => 'integer' ), - 'smaxage' => array ( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => 0 + 'smaxage' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_DFLT => 0 ), - 'maxage' => array ( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => 0 + 'maxage' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_DFLT => 0 ), 'requestid' => null, + 'servedby' => false, ); } @@ -660,14 +725,15 @@ class ApiMain extends ApiBase { * See ApiBase for description. */ public function getParamDescription() { - return array ( + return array( 'format' => 'The format of the output', - 'action' => 'What action you would like to perform', + 'action' => 'What action you would like to perform. See below for module help', 'version' => 'When showing help, include version for each module', 'maxlag' => 'Maximum lag', 'smaxage' => 'Set the s-maxage header to this many seconds. Errors are never cached', 'maxage' => 'Set the max-age header to this many seconds. Errors are never cached', 'requestid' => 'Request ID to distinguish requests. This will just be output back to you', + 'servedby' => 'Include the hostname that served the request in the results. Unconditionally shown on error', ); } @@ -675,25 +741,26 @@ class ApiMain extends ApiBase { * See ApiBase for description. */ public function getDescription() { - return array ( + return array( '', '', - '******************************************************************', - '** **', - '** This is an auto-generated MediaWiki API documentation page **', - '** **', - '** Documentation and Examples: **', - '** http://www.mediawiki.org/wiki/API **', - '** **', - '******************************************************************', + '******************************************************************************************', + '** **', + '** This is an auto-generated MediaWiki API documentation page **', + '** **', + '** Documentation and Examples: **', + '** http://www.mediawiki.org/wiki/API **', + '** **', + '******************************************************************************************', '', - 'Status: All features shown on this page should be working, but the API', - ' is still in active development, and may change at any time.', - ' Make sure to monitor our mailing list for any updates.', + 'Status: All features shown on this page should be working, but the API', + ' is still in active development, and may change at any time.', + ' Make sure to monitor our mailing list for any updates', '', - 'Documentation: http://www.mediawiki.org/wiki/API', - 'Mailing list: http://lists.wikimedia.org/mailman/listinfo/mediawiki-api', - 'Bugs & Requests: http://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts', + 'Documentation: http://www.mediawiki.org/wiki/API', + 'Mailing list: http://lists.wikimedia.org/mailman/listinfo/mediawiki-api', + 'Api Announcements: http://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce', + 'Bugs & Requests: http://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts', '', '', '', @@ -702,14 +769,14 @@ class ApiMain extends ApiBase { ); } - public function getPossibleErrors() { + 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' ), - ) ); + ) ); } /** @@ -728,74 +795,83 @@ class ApiMain extends ApiBase { 'or file a bug report at http://bugzilla.wikimedia.org/' ); } + /** + * Sets whether the pretty-printer should format *bold* and $italics$ + */ + public function setHelp( $help = true ) { + $this->mPrinter->setHelp( $help ); + } /** * Override the parent to generate help messages for all available modules. */ public function makeHelpMsg() { global $wgMemc, $wgAPICacheHelp, $wgAPICacheHelpTimeout; - $this->mPrinter->setHelp(); + $this->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 ) + if ( $cached ) { return $cached; + } } $retval = $this->reallyMakeHelpMsg(); - if ( $wgAPICacheHelp ) + if ( $wgAPICacheHelp ) { $wgMemc->set( $key, $retval, $wgAPICacheHelpTimeout ); + } return $retval; } public function reallyMakeHelpMsg() { - - $this->mPrinter->setHelp(); + $this->setHelp(); // Use parent to make default message for the main module - $msg = parent :: makeHelpMsg(); + $msg = parent::makeHelpMsg(); $astriks = str_repeat( '*** ', 10 ); $msg .= "\n\n$astriks Modules $astriks\n\n"; - foreach ( $this->mModules as $moduleName => $unused ) { + foreach ( array_keys( $this->mModules ) as $moduleName ) { $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"; } $msg .= "\n$astriks Permissions $astriks\n\n"; - foreach ( self :: $mRights as $right => $rightMsg ) { + foreach ( self::$mRights as $right => $rightMsg ) { $groups = User::getGroupsWithPermission( $right ); $msg .= "* " . $right . " *\n " . wfMsgReplaceArgs( $rightMsg[ 'msg' ], $rightMsg[ 'params' ] ) . - "\nGranted to:\n " . str_replace( "*", "all", implode( ", ", $groups ) ) . "\n"; + "\nGranted to:\n " . str_replace( '*', 'all', implode( ', ', $groups ) ) . "\n\n"; } $msg .= "\n$astriks Formats $astriks\n\n"; - foreach ( $this->mFormats as $formatName => $unused ) { + foreach ( array_keys( $this->mFormats ) as $formatName ) { $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"; - return $msg; } public static function makeHelpMsgHeader( $module, $paramName ) { $modulePrefix = $module->getModulePrefix(); - if ( strval( $modulePrefix ) !== '' ) + if ( strval( $modulePrefix ) !== '' ) { $modulePrefix = "($modulePrefix) "; + } return "* $paramName={$module->getModuleName()} $modulePrefix*"; } @@ -809,7 +885,7 @@ 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' ); } @@ -822,7 +898,7 @@ 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() ); } @@ -858,10 +934,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 70066 2010-07-28 05:52:32Z tstarling $'; - $vers[] = ApiBase :: getBaseVersion(); - $vers[] = ApiFormatBase :: getBaseVersion(); - $vers[] = ApiQueryBase :: getBaseVersion(); + $vers[] = __CLASS__ . ': $Id: ApiMain.php 76196 2010-11-06 16:11:19Z reedy $'; + $vers[] = ApiBase::getBaseVersion(); + $vers[] = ApiFormatBase::getBaseVersion(); + $vers[] = ApiQueryBase::getBaseVersion(); return $vers; } @@ -870,7 +946,6 @@ class ApiMain extends ApiBase { * classes who wish to add their own modules to their lexicon or override the * behavior of inherent ones. * - * @access protected * @param $mdlName String The identifier for this module. * @param $mdlClass String The class where this module is implemented. */ @@ -882,7 +957,6 @@ class ApiMain extends ApiBase { * Add or overwrite an output format for this ApiMain. Intended for use by extending * classes who wish to add to or modify current formatters. * - * @access protected * @param $fmtName The identifier for this format. * @param $fmtClass The class implementing this format. */ @@ -910,22 +984,26 @@ class UsageException extends Exception { private $mExtraData; public function __construct( $message, $codestr, $code = 0, $extradata = null ) { - parent :: __construct( $message, $code ); + 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() + $result = array( + 'code' => $this->mCodestr, + 'info' => $this->getMessage() ); - if ( is_array( $this->mExtraData ) ) + 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 c234f084..a93188bf 100644 --- a/includes/api/ApiMove.php +++ b/includes/api/ApiMove.php @@ -1,10 +1,10 @@ .@home.nl + * Created on Oct 31, 2007 + * + * 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 @@ -18,55 +18,57 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } - /** + * API Module to move pages * @ingroup API */ class ApiMove extends ApiBase { public function __construct( $main, $action ) { - parent :: __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['from'] ) ) - { + if ( isset( $params['from'] ) ) { $fromTitle = Title::newFromText( $params['from'] ); - if ( !$fromTitle ) + if ( !$fromTitle ) { $this->dieUsageMsg( array( 'invalidtitle', $params['from'] ) ); - } - else if ( isset( $params['fromid'] ) ) - { + } + } elseif ( isset( $params['fromid'] ) ) { $fromTitle = Title::newFromID( $params['fromid'] ); - if ( !$fromTitle ) + if ( !$fromTitle ) { $this->dieUsageMsg( array( 'nosuchpageid', $params['fromid'] ) ); + } } - if ( !$fromTitle->exists() ) + if ( !$fromTitle->exists() ) { $this->dieUsageMsg( array( 'notanarticle' ) ); + } $fromTalk = $fromTitle->getTalkPage(); $toTitle = Title::newFromText( $params['to'] ); - if ( !$toTitle ) + if ( !$toTitle ) { $this->dieUsageMsg( array( 'invalidtitle', $params['to'] ) ); + } $toTalk = $toTitle->getTalkPage(); if ( $toTitle->getNamespace() == NS_FILE @@ -81,27 +83,24 @@ class ApiMove extends ApiBase { } // Move the page - $hookErr = null; $retval = $fromTitle->moveTo( $toTitle, true, $params['reason'], !$params['noredirect'] ); - if ( $retval !== true ) + if ( $retval !== true ) { $this->dieUsageMsg( reset( $retval ) ); + } $r = array( 'from' => $fromTitle->getPrefixedText(), 'to' => $toTitle->getPrefixedText(), 'reason' => $params['reason'] ); - if ( !$params['noredirect'] || !$wgUser->isAllowed( 'suppressredirect' ) ) + if ( !$params['noredirect'] || !$wgUser->isAllowed( 'suppressredirect' ) ) { $r['redirectcreated'] = ''; + } // Move the talk page - if ( $params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage() ) - { + if ( $params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage() ) { $retval = $fromTalk->moveTo( $toTalk, true, $params['reason'], !$params['noredirect'] ); - if ( $retval === true ) - { + if ( $retval === true ) { $r['talkfrom'] = $fromTalk->getPrefixedText(); $r['talkto'] = $toTalk->getPrefixedText(); - } - // We're not gonna dieUsage() on failure, since we already changed something - else - { + } else { + // We're not gonna dieUsage() on failure, since we already changed something $parsed = $this->parseMsg( reset( $retval ) ); $r['talkmove-error-code'] = $parsed['code']; $r['talkmove-error-info'] = $parsed['info']; @@ -109,51 +108,49 @@ class ApiMove extends ApiBase { } // Move subpages - if ( $params['movesubpages'] ) - { + if ( $params['movesubpages'] ) { $r['subpages'] = $this->moveSubpages( $fromTitle, $toTitle, $params['reason'], $params['noredirect'] ); $this->getResult()->setIndexedTagName( $r['subpages'], 'subpage' ); - if ( $params['movetalk'] ) - { + if ( $params['movetalk'] ) { $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' ) ) - { - $wgUser->addWatch( $fromTitle ); - $wgUser->addWatch( $toTitle ); - } - else if ( $params['unwatch'] ) - { - $wgUser->removeWatch( $fromTitle ); - $wgUser->removeWatch( $toTitle ); + $watch = "preferences"; + if ( isset( $params['watchlist'] ) ) { + $watch = $params['watchlist']; + } elseif ( $params['watch'] ) { + $watch = 'watch'; + } elseif ( $params['unwatch'] ) { + $watch = 'unwatch'; } + + // Watch pages + $this->setWatch( $watch, $fromTitle, 'watchmoves' ); + $this->setWatch( $watch, $toTitle, 'watchmoves' ); + $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] ) ) + if ( isset( $success[0] ) ) { return array( 'error' => $this->parseMsg( $success ) ); - else - { + } 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 ) ) + if ( is_array( $newTitle ) ) { $r['error'] = $this->parseMsg( reset( $newTitle ) ); - else + } else { // Success $r['to'] = $newTitle; + } $retval[] = $r; } } @@ -169,48 +166,65 @@ class ApiMove extends ApiBase { } public function getAllowedParams() { - return array ( + return array( 'from' => null, 'fromid' => array( ApiBase::PARAM_TYPE => 'integer' ), - 'to' => null, + 'to' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'token' => null, 'reason' => null, 'movetalk' => false, 'movesubpages' => false, 'noredirect' => false, - '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' + ), + ), 'ignorewarnings' => false ); } public function getParamDescription() { - return array ( - 'from' => 'Title of the page you want to move. Cannot be used together with fromid.', - 'fromid' => 'Page ID of the page you want to move. Cannot be used together with from.', - 'to' => 'Title you want to rename the page to.', + $p = $this->getModulePrefix(); + return array( + 'from' => "Title of the page you want to move. Cannot be used together with {$p}fromid", + 'fromid' => "Page ID of the page you want to move. Cannot be used together with {$p}from", + 'to' => 'Title you want to rename the page to', 'token' => 'A move token previously retrieved through prop=info', - 'reason' => 'Reason for the move (optional).', - 'movetalk' => 'Move the talk page, if it exists.', + 'reason' => 'Reason for the move (optional)', + 'movetalk' => 'Move the talk page, if it exists', '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', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', 'ignorewarnings' => 'Ignore any warnings' ); } public function getDescription() { - return array( - 'Move a page.' - ); + return 'Move a page'; } - + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( - array( 'missingparam', 'to' ), array( 'invalidtitle', 'from' ), array( 'nosuchpageid', 'fromid' ), array( 'notanarticle' ), @@ -228,12 +242,12 @@ class ApiMove extends ApiBase { } protected function getExamples() { - return array ( - 'api.php?action=move&from=Exampel&to=Example&token=123ABC&reason=Misspelled%20title&movetalk&noredirect' + return array( + 'api.php?action=move&from=Exampel&to=Example&token=123ABC&reason=Misspelled%20title&movetalk=&noredirect=' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiMove.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiMove.php 77192 2010-11-23 22:05:27Z btongminh $'; } } diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php index e145d80c..885766d2 100644 --- a/includes/api/ApiOpenSearch.php +++ b/includes/api/ApiOpenSearch.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 13, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -34,7 +35,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiOpenSearch extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function getCustomPrinter() { @@ -50,44 +51,63 @@ class ApiOpenSearch extends ApiBase { $suggest = $params['suggest']; // MWSuggest or similar hit - if ( $suggest && !$wgEnableOpenSearchSuggest ) - $srchres = array(); - else { - // Open search results may be stored for a very long - // time + if ( $suggest && !$wgEnableOpenSearchSuggest ) { + $searches = 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, + $searches = PrefixSearch::titleSearch( $search, $limit, $namespaces ); + + // if the content language has variants, try to retrieve fallback results + $fallbackLimit = $limit - count( $searches ); + if ( $fallbackLimit > 0 ) { + global $wgContLang; + + $fallbackSearches = $wgContLang->autoConvertToAllVariants( $search ); + $fallbackSearches = array_diff( array_unique( $fallbackSearches ), array( $search ) ); + + foreach ( $fallbackSearches as $fbs ) { + $fallbackSearchResult = PrefixSearch::titleSearch( $fbs, $fallbackLimit, + $namespaces ); + $searches = array_merge( $searches, $fallbackSearchResult ); + $fallbackLimit -= count( $fallbackSearchResult ); + + if ( $fallbackLimit == 0 ) { + break; + } + } + } } // Set top level elements $result = $this->getResult(); $result->addValue( null, 0, $search ); - $result->addValue( null, 1, $srchres ); + $result->addValue( null, 1, $searches ); } public function getAllowedParams() { - return array ( + return array( 'search' => null, 'limit' => array( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => 100, - ApiBase :: PARAM_MAX2 => 100 + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => 100, + ApiBase::PARAM_MAX2 => 100 ), 'namespace' => array( - ApiBase :: PARAM_DFLT => NS_MAIN, - ApiBase :: PARAM_TYPE => 'namespace', - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_DFLT => NS_MAIN, + ApiBase::PARAM_TYPE => 'namespace', + ApiBase::PARAM_ISMULTI => true ), 'suggest' => false, ); } public function getParamDescription() { - return array ( + return array( 'search' => 'Search string', 'limit' => 'Maximum amount of results to return', 'namespace' => 'Namespaces to search', @@ -100,12 +120,12 @@ class ApiOpenSearch extends ApiBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=opensearch&search=Te' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiOpenSearch.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiOpenSearch.php 79720 2011-01-06 14:48:34Z catrope $'; } } diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php index 361f1d8b..1cb12c07 100644 --- a/includes/api/ApiPageSet.php +++ b/includes/api/ApiPageSet.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 24, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -45,9 +46,10 @@ class ApiPageSet extends ApiQueryBase { private $mAllPages; // [ns][dbkey] => page_id or negative when missing private $mTitles, $mGoodTitles, $mMissingTitles, $mInvalidTitles; - private $mMissingPageIDs, $mRedirectTitles; + private $mMissingPageIDs, $mRedirectTitles, $mSpecialTitles; private $mNormalizedTitles, $mInterwikiTitles; private $mResolveRedirects, $mPendingRedirectIDs; + private $mConvertTitles, $mConvertedTitles; private $mGoodRevIDs, $mMissingRevIDs; private $mFakePageId; @@ -58,25 +60,30 @@ 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, $convertTitles = false ) { + parent::__construct( $query, 'query' ); - $this->mAllPages = array (); + $this->mAllPages = array(); $this->mTitles = array(); - $this->mGoodTitles = array (); - $this->mMissingTitles = array (); - $this->mInvalidTitles = array (); - $this->mMissingPageIDs = array (); - $this->mRedirectTitles = array (); - $this->mNormalizedTitles = array (); - $this->mInterwikiTitles = array (); + $this->mGoodTitles = array(); + $this->mMissingTitles = array(); + $this->mInvalidTitles = array(); + $this->mMissingPageIDs = array(); + $this->mRedirectTitles = array(); + $this->mNormalizedTitles = array(); + $this->mInterwikiTitles = array(); $this->mGoodRevIDs = array(); $this->mMissingRevIDs = array(); + $this->mSpecialTitles = array(); - $this->mRequestedPageFields = array (); + $this->mRequestedPageFields = array(); $this->mResolveRedirects = $resolveRedirects; - if ( $resolveRedirects ) + if ( $resolveRedirects ) { $this->mPendingRedirectIDs = array(); + } + + $this->mConvertTitles = $convertTitles; + $this->mConvertedTitles = array(); $this->mFakePageId = - 1; } @@ -117,14 +124,15 @@ class ApiPageSet extends ApiQueryBase { public function getPageTableFields() { // Ensure we get minimum required fields // DON'T change this order - $pageFlds = array ( + $pageFlds = array( 'page_namespace' => null, 'page_title' => null, '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 ); @@ -219,6 +227,15 @@ class ApiPageSet extends ApiQueryBase { return $this->mNormalizedTitles; } + /** + * Get a list of title conversions - maps a title to its converted + * version. + * @return array raw_prefixed_title (string) => prefixed_title (string) + */ + public function getConvertedTitles() { + return $this->mConvertedTitles; + } + /** * Get a list of interwiki titles - maps a title to its interwiki * prefix. @@ -244,6 +261,14 @@ class ApiPageSet extends ApiQueryBase { return $this->mMissingRevIDs; } + /** + * Get the list of titles with negative namespace + * @return array Title + */ + public function getSpecialTitles() { + return $this->mSpecialTitles; + } + /** * Returns the number of revisions (requested with revids= parameter)\ * @return int @@ -261,34 +286,38 @@ 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 ) ) + } + 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 ) ) + if ( isset( $params['revids'] ) ) { + if ( isset( $dataSource ) ) { $this->dieUsage( "Cannot use 'revids' at the same time as '$dataSource'", 'multisource' ); + } $dataSource = 'revids'; } switch ( $dataSource ) { - case 'titles' : + case 'titles': $this->initFromTitles( $params['titles'] ); break; - case 'pageids' : + case 'pageids': $this->initFromPageIds( $params['pageids'] ); break; - case 'revids' : - if ( $this->mResolveRedirects ) + 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.' ); + } $this->mResolveRedirects = false; $this->initFromRevIDs( $params['revids'] ); break; - default : + default: // Do nothing - some queries do not need any of the data sources. break; } @@ -318,7 +347,7 @@ class ApiPageSet extends ApiQueryBase { /** * Populate this PageSet from a rowset returned from the database * @param $db Database object - * @param $queryResult Query result object + * @param $queryResult ResultWrapper Query result object */ public function populateFromQueryResult( $db, $queryResult ) { $this->profileIn(); @@ -341,9 +370,8 @@ class ApiPageSet extends ApiQueryBase { * @param $row Result 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 ); $this->mAllPages[$row->page_namespace][$row->page_title] = $pageId; @@ -355,8 +383,9 @@ class ApiPageSet extends ApiQueryBase { $this->mGoodTitles[$pageId] = $title; } - foreach ( $this->mRequestedPageFields as $fieldName => & $fieldValues ) + foreach ( $this->mRequestedPageFields as $fieldName => &$fieldValues ) { $fieldValues[$pageId] = $row-> $fieldName; + } } /** @@ -385,11 +414,11 @@ class ApiPageSet extends ApiQueryBase { * @param $titles array of Title objects or strings */ private function initFromTitles( $titles ) { - // Get validated and normalized title objects $linkBatch = $this->processTitlesArray( $titles ); - if ( $linkBatch->isEmpty() ) + if ( $linkBatch->isEmpty() ) { return; + } $db = $this->getDB(); $set = $linkBatch->constructSet( 'page', $db ); @@ -401,7 +430,7 @@ class ApiPageSet extends ApiQueryBase { $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(); @@ -412,11 +441,12 @@ class ApiPageSet extends ApiQueryBase { * @param $pageids array of page IDs */ private function initFromPageIds( $pageids ) { - if ( !count( $pageids ) ) + if ( !count( $pageids ) ) { return; + } $pageids = array_map( 'intval', $pageids ); // paranoia - $set = array ( + $set = array( 'page_id' => $pageids ); $db = $this->getDB(); @@ -438,7 +468,7 @@ class ApiPageSet extends ApiQueryBase { * Iterate through the result of the query on 'page' table, * and for each row create and store title object and save any extra fields requested. * @param $db Database - * @param $res DB Query result + * @param $res ResultWrapper DB Query result * @param $remaining array of either pageID or ns/title elements (optional). * If given, any missing items will go to $mMissingPageIDs and $mMissingTitles * @param $processTitles bool Must be provided together with $remaining. @@ -446,47 +476,46 @@ class ApiPageSet extends ApiQueryBase { * 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' ); - - while ( $row = $db->fetchObject( $res ) ) { + if ( !is_null( $remaining ) && is_null( $processTitles ) ) { + ApiBase::dieDebug( __METHOD__, 'Missing $processTitles parameter when $remaining is provided' ); + } + foreach ( $res as $row ) { $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] ); - else - unset ( $remaining[$pageId] ); + if ( $processTitles ) { + unset( $remaining[$row->page_namespace][$row->page_title] ); + } else { + unset( $remaining[$pageId] ); + } } // Store any extra fields requested by modules $this->processDbRow( $row ); } - $db->freeResult( $res ); if ( isset( $remaining ) ) { // Any items left in the $remaining list are added as missing if ( $processTitles ) { // The remaining titles in $remaining are non-existent pages foreach ( $remaining as $ns => $dbkeys ) { - foreach ( $dbkeys as $dbkey => $unused ) { - $title = Title :: makeTitle( $ns, $dbkey ); + foreach ( array_keys( $dbkeys ) as $dbkey ) { + $title = Title::makeTitle( $ns, $dbkey ); $this->mAllPages[$ns][$dbkey] = $this->mFakePageId; $this->mMissingTitles[$this->mFakePageId] = $title; $this->mFakePageId--; $this->mTitles[] = $title; } } - } - else - { + } else { // The remaining pageids do not exist - if ( !$this->mMissingPageIDs ) + if ( !$this->mMissingPageIDs ) { $this->mMissingPageIDs = array_keys( $remaining ); - else + } else { $this->mMissingPageIDs = array_merge( $this->mMissingPageIDs, array_keys( $remaining ) ); + } } } } @@ -497,9 +526,9 @@ class ApiPageSet extends ApiQueryBase { * @param $revids array of revision IDs */ private function initFromRevIDs( $revids ) { - - if ( !count( $revids ) ) + if ( !count( $revids ) ) { return; + } $revids = array_map( 'intval', $revids ); // paranoia $db = $this->getDB(); @@ -513,14 +542,13 @@ class ApiPageSet extends ApiQueryBase { // Get pageIDs data from the `page` table $this->profileDBIn(); $res = $db->select( $tables, $fields, $where, __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { $revid = intval( $row->rev_id ); $pageid = intval( $row->rev_page ); $this->mGoodRevIDs[$revid] = $pageid; $pageids[$pageid] = ''; unset( $remaining[$revid] ); } - $db->freeResult( $res ); $this->profileDBOut(); $this->mMissingRevIDs = array_keys( $remaining ); @@ -535,7 +563,6 @@ class ApiPageSet extends ApiQueryBase { * have been resolved. */ private function resolvePendingRedirects() { - if ( $this->mResolveRedirects ) { $db = $this->getDB(); $pageFlds = $this->getPageTableFields(); @@ -543,17 +570,18 @@ class ApiPageSet extends ApiQueryBase { // Repeat until all redirects have been resolved // The infinite loop is prevented by keeping all known pages in $this->mAllPages while ( $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 ) + if ( $set === false ) { break; + } // Get pageIDs data from the `page` table $this->profileDBIn(); @@ -578,7 +606,9 @@ 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' @@ -587,28 +617,27 @@ class ApiPageSet extends ApiQueryBase { ); $this->profileDBOut(); - while ( $row = $db->fetchObject( $res ) ) - { + foreach ( $res as $row ) { $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] ) ) + 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 ) - { + + if ( $this->mPendingRedirectIDs ) { // We found pages that aren't in the redirect table // Add them - foreach ( $this->mPendingRedirectIDs as $id => $title ) - { + foreach ( $this->mPendingRedirectIDs as $id => $title ) { $article = new Article( $title ); $rt = $article->insertRedirect(); - if ( !$rt ) + if ( !$rt ) { // What the hell. Let's just ignore this continue; + } $lb->addObj( $rt ); $this->mRedirectTitles[$title->getPrefixedText()] = $rt->getPrefixedText(); unset( $this->mPendingRedirectIDs[$id] ); @@ -627,31 +656,45 @@ class ApiPageSet extends ApiQueryBase { * @return LinkBatch */ private function processTitlesArray( $titles ) { - $linkBatch = new LinkBatch(); 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 $this->mAllpages[0][$title] = $this->mFakePageId; $this->mInvalidTitles[$this->mFakePageId] = $title; $this->mFakePageId--; continue; // There's nothing else we can do } + $unconvertedTitle = $titleObj->getPrefixedText(); + $titleWasConverted = false; $iw = $titleObj->getInterwiki(); if ( strval( $iw ) !== '' ) { // This title is an interwiki link. $this->mInterwikiTitles[$titleObj->getPrefixedText()] = $iw; } else { + // Variants checking + global $wgContLang; + if ( $this->mConvertTitles && + count( $wgContLang->getVariants() ) > 1 && + !$titleObj->exists() ) { + // Language::findVariantLink will modify titleObj into + // the canonical variant if possible + $wgContLang->findVariantLink( $title, $titleObj ); + $titleWasConverted = $unconvertedTitle !== $titleObj->getPrefixedText(); + } + - // Validation - if ( $titleObj->getNamespace() < 0 ) - $this->setWarning( "No support for special pages has been implemented" ); - else + if ( $titleObj->getNamespace() < 0 ) { + // Handle Special and Media pages + $titleObj = $titleObj->fixSpecialName(); + $this->mSpecialTitles[$this->mFakePageId] = $titleObj; + $this->mFakePageId--; + } else { + // Regular page $linkBatch->addObj( $titleObj ); + } } // Make sure we remember the original title that was @@ -659,7 +702,9 @@ 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 ( $titleWasConverted ) { + $this->mConvertedTitles[$title] = $titleObj->getPrefixedText(); + } elseif ( is_string( $title ) && $title !== $titleObj->getPrefixedText() ) { $this->mNormalizedTitles[$title] = $titleObj->getPrefixedText(); } } @@ -668,23 +713,23 @@ class ApiPageSet extends ApiQueryBase { } protected function getAllowedParams() { - return array ( - 'titles' => array ( - ApiBase :: PARAM_ISMULTI => true + return array( + 'titles' => array( + ApiBase::PARAM_ISMULTI => true ), - 'pageids' => array ( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_ISMULTI => true + 'pageids' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_ISMULTI => true ), - 'revids' => array ( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_ISMULTI => true + 'revids' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_ISMULTI => true ) ); } protected function getParamDescription() { - return array ( + return array( 'titles' => 'A list of titles to work on', 'pageids' => 'A list of page IDs to work on', 'revids' => 'A list of revision IDs to work on' @@ -699,6 +744,6 @@ class ApiPageSet extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiPageSet.php 62410 2010-02-13 01:21:52Z reedy $'; + return __CLASS__ . ': $Id: ApiPageSet.php 76196 2010-11-06 16:11:19Z reedy $'; } } diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php index 8fe2cad2..a2c0bd11 100644 --- a/includes/api/ApiParamInfo.php +++ b/includes/api/ApiParamInfo.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Dec 01, 2007 + * + * 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -34,7 +35,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiParamInfo extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { @@ -43,14 +44,11 @@ class ApiParamInfo extends ApiBase { $result = $this->getResult(); $queryObj = new ApiQuery( $this->getMain(), 'query' ); $r = array(); - if ( is_array( $params['modules'] ) ) - { + if ( is_array( $params['modules'] ) ) { $modArr = $this->getMain()->getModules(); $r['modules'] = array(); - foreach ( $params['modules'] as $m ) - { - if ( !isset( $modArr[$m] ) ) - { + foreach ( $params['modules'] as $m ) { + if ( !isset( $modArr[$m] ) ) { $r['modules'][] = array( 'name' => $m, 'missing' => '' ); continue; } @@ -61,14 +59,11 @@ class ApiParamInfo extends ApiBase { } $result->setIndexedTagName( $r['modules'], 'module' ); } - if ( is_array( $params['querymodules'] ) ) - { + if ( is_array( $params['querymodules'] ) ) { $qmodArr = $queryObj->getModules(); $r['querymodules'] = array(); - foreach ( $params['querymodules'] as $qm ) - { - if ( !isset( $qmodArr[$qm] ) ) - { + foreach ( $params['querymodules'] as $qm ) { + if ( !isset( $qmodArr[$qm] ) ) { $r['querymodules'][] = array( 'name' => $qm, 'missing' => '' ); continue; } @@ -79,60 +74,64 @@ class ApiParamInfo extends ApiBase { } $result->setIndexedTagName( $r['querymodules'], 'module' ); } - if ( $params['mainmodule'] ) + if ( $params['mainmodule'] ) { $r['mainmodule'] = $this->getClassInfo( $this->getMain() ); - if ( $params['pagesetmodule'] ) - { + } + if ( $params['pagesetmodule'] ) { $pageSet = new ApiPageSet( $queryObj ); $r['pagesetmodule'] = $this->getClassInfo( $pageSet ); } $result->addValue( null, $this->getModuleName(), $r ); } - function getClassInfo( $obj ) - { + function getClassInfo( $obj ) { $result = $this->getResult(); $retval['classname'] = get_class( $obj ); $retval['description'] = implode( "\n", (array)$obj->getDescription() ); + $retval['examples'] = implode( "\n", (array)$obj->getExamples() ); $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 ) + } + 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 ( isset( $paramDesc[$n] ) ) + if ( isset( $paramDesc[$n] ) ) { $a['description'] = implode( "\n", (array)$paramDesc[$n] ); - if ( isset( $p[ApiBase::PARAM_DEPRECATED] ) && $p[ApiBase::PARAM_DEPRECATED] ) + } + if ( isset( $p[ApiBase::PARAM_DEPRECATED] ) && $p[ApiBase::PARAM_DEPRECATED] ) { $a['deprecated'] = ''; - if ( !is_array( $p ) ) - { - if ( is_bool( $p ) ) - { + } + if ( isset( $p[ApiBase::PARAM_REQUIRED] ) && $p[ApiBase::PARAM_REQUIRED] ) { + $a['required'] = ''; + } + + if ( !is_array( $p ) ) { + if ( is_bool( $p ) ) { $a['type'] = 'bool'; $a['default'] = ( $p ? 'true' : 'false' ); - } - else if ( is_string( $p ) || is_null( $p ) ) - { + } elseif ( is_string( $p ) || is_null( $p ) ) { $a['type'] = 'string'; $a['default'] = strval( $p ); - } - else if ( is_int( $p ) ) - { + } elseif ( is_int( $p ) ) { $a['type'] = 'integer'; $a['default'] = intval( $p ); } @@ -140,42 +139,45 @@ class ApiParamInfo extends ApiBase { 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] ) - { - $a['multi'] = ''; - $a['limit'] = $this->getMain()->canApiHighLimits() ? - ApiBase::LIMIT_SML2 : - ApiBase::LIMIT_SML1; - } + } + if ( isset( $p[ApiBase::PARAM_ISMULTI] ) && $p[ApiBase::PARAM_ISMULTI] ) { + $a['multi'] = ''; + $a['limit'] = $this->getMain()->canApiHighLimits() ? + ApiBase::LIMIT_SML2 : + ApiBase::LIMIT_SML1; + } - if ( isset( $p[ApiBase::PARAM_ALLOW_DUPLICATES] ) ) - if ( $p[ApiBase::PARAM_ALLOW_DUPLICATES] ) - $a['allowsduplicates'] = ''; + if ( isset( $p[ApiBase::PARAM_ALLOW_DUPLICATES] ) && $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'] ) ) + if ( is_array( $a['type'] ) ) { + $a['type'] = array_values( $a['type'] ); // to prevent sparse arrays from being serialized to JSON as objects $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]; + } $retval['parameters'][] = $a; } $result->setIndexedTagName( $retval['parameters'], 'param' ); - + // Errors $retval['errors'] = $this->parseErrors( $obj->getPossibleErrors() ); - + $result->setIndexedTagName( $retval['errors'], 'error' ); - + return $retval; } @@ -184,12 +186,12 @@ class ApiParamInfo extends ApiBase { } public function getAllowedParams() { - return array ( + return array( 'modules' => array( - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true ), 'querymodules' => array( - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true ), 'mainmodule' => false, 'pagesetmodule' => false, @@ -197,7 +199,7 @@ class ApiParamInfo extends ApiBase { } public function getParamDescription() { - return array ( + return array( 'modules' => 'List of module names (value of the action= parameter)', 'querymodules' => 'List of query module names (value of prop=, meta= or list= parameter)', 'mainmodule' => 'Get information about the main (top-level) module as well', @@ -206,16 +208,16 @@ class ApiParamInfo extends ApiBase { } public function getDescription() { - return 'Obtain information about certain API parameters'; + return 'Obtain information about certain API parameters and errors'; } protected function getExamples() { - return array ( + return array( 'api.php?action=paraminfo&modules=parse&querymodules=allpages|siteinfo' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiParamInfo.php 62336 2010-02-11 22:22:20Z reedy $'; + return __CLASS__ . ': $Id: ApiParamInfo.php 87170 2011-04-30 16:57:22Z catrope $'; } } diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php index db389bdb..2d12c233 100644 --- a/includes/api/ApiParse.php +++ b/includes/api/ApiParse.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Dec 01, 2007 + * + * Copyright © 2007 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -33,8 +34,10 @@ if ( !defined( 'MEDIAWIKI' ) ) { */ class ApiParse extends ApiBase { + private $section, $text, $pstText = null; + public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { @@ -46,141 +49,238 @@ class ApiParse extends ApiBase { $text = $params['text']; $title = $params['title']; $page = $params['page']; + $pageid = $params['pageid']; $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' ); + + 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; + + if ( isset( $params['section'] ) ) { + $this->section = $params['section']; + } else { + $this->section = false; + } // The parser needs $wgTitle to be set, apparently the // $title parameter in Parser::parse isn't enough *sigh* - global $wgParser, $wgUser, $wgTitle, $wgEnableParserCache; + global $wgParser, $wgUser, $wgTitle, $wgLang; + + // Currently unnecessary, code to act as a safeguard against any change in current behaviour of uselang breaks + $oldLang = null; + if ( isset( $params['uselang'] ) && $params['uselang'] != $wgLang->getCode() ) { + $oldLang = $wgLang; // Backup wgLang + $wgLang = Language::factory( $params['uselang'] ); + } + $popts = new ParserOptions(); $popts->setTidy( true ); - $popts->enableLimitReport(); + $popts->enableLimitReport( !$params['disablepp'] ); + $redirValues = null; - if ( !is_null( $oldid ) || !is_null( $page ) ) - { - if ( !is_null( $oldid ) ) - { + + if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) { + + if ( !is_null( $oldid ) ) { // Don't use the parser cache $rev = Revision::newFromID( $oldid ); - if ( !$rev ) + if ( !$rev ) { $this->dieUsage( "There is no revision ID $oldid", 'missingrev' ); - if ( !$rev->userCan( Revision::DELETED_TEXT ) ) + } + 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 ); - } - else - { - if ( $params['redirects'] ) - { - $req = new FauxRequest( array( - 'action' => 'query', - 'redirects' => '', - 'titles' => $page - ) ); - $main = new ApiMain( $req ); - $main->execute(); - $data = $main->getResultData(); - $redirValues = @$data['query']['redirects']; - $to = $page; - foreach ( (array)$redirValues as $r ) - $to = $r['to']; + + //If for some reason the "oldid" is actually the current revision, it may be cached + if ( $titleObj->getLatestRevID() === $oldid ) { + $articleObj = new Article( $titleObj, 0 ); + + $p_result = $this->getParsedSectionOrText( $articleObj, $titleObj, $popts, $pageid, + isset( $prop['wikitext'] ) ) ; + + } else { // This is an old revision, so get the text differently + $this->text = $rev->getText( Revision::FOR_THIS_USER ); + + $wgTitle = $titleObj; + + if ( $this->section !== false ) { + $this->text = $this->getSectionText( $this->text, 'r' . $rev->getId() ); + } + + $p_result = $wgParser->parse( $this->text, $titleObj, $popts ); } - else - $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'] ) ) + + } else { // Not $oldid + + if ( !is_null ( $pageid ) ) { + $titleObj = Title::newFromID( $pageid ); + + if ( !$titleObj ) { + $this->dieUsageMsg( array( 'nosuchpageid', $pageid ) ); + } + } else { // $page + + if ( $params['redirects'] ) { + $req = new FauxRequest( array( + 'action' => 'query', + 'redirects' => '', + 'titles' => $page + ) ); + $main = new ApiMain( $req ); + $main->execute(); + $data = $main->getResultData(); + $redirValues = @$data['query']['redirects']; + $to = $page; + foreach ( (array)$redirValues as $r ) { + $to = $r['to']; + } + } else { + $to = $page; + } + $titleObj = Title::newFromText( $to ); + if ( !$titleObj || !$titleObj->exists() ) { + $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' ); + } + } + $wgTitle = $titleObj; + + $articleObj = new Article( $titleObj, 0 ); + if ( isset( $prop['revid'] ) ) { $oldid = $articleObj->getRevIdFetched(); - // Try the parser cache first - $p_result = false; - $pcache = ParserCache::singleton(); - if ( $wgEnableParserCache ) - $p_result = $pcache->get( $articleObj, $wgUser ); - if ( !$p_result ) - { - $p_result = $wgParser->parse( $articleObj->getContent(), $titleObj, $popts ); - - if ( $wgEnableParserCache ) - $pcache->save( $p_result, $articleObj, $popts ); } + + $p_result = $this->getParsedSectionOrText( $articleObj, $titleObj, $popts, $pageid, + isset( $prop['wikitext'] ) ) ; } - } - else - { + + } else { // Not $oldid, $pageid, $page. Hence based on $text + + $this->text = $text; $titleObj = Title::newFromText( $title ); - if ( !$titleObj ) - $titleObj = Title::newFromText( "API" ); + if ( !$titleObj ) { + $titleObj = Title::newFromText( 'API' ); + } $wgTitle = $titleObj; - if ( $params['pst'] || $params['onlypst'] ) - $text = $wgParser->preSaveTransform( $text, $titleObj, $wgUser, $popts ); - if ( $params['onlypst'] ) - { + + if ( $this->section !== false ) { + $this->text = $this->getSectionText( $this->text, $titleObj->getText() ); + } + + if ( $params['pst'] || $params['onlypst'] ) { + $this->pstText = $wgParser->preSaveTransform( $this->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()->setContent( $result_array['text'], $this->pstText ); + if ( isset( $prop['wikitext'] ) ) { + $result_array['wikitext'] = array(); + $this->getResult()->setContent( $result_array['wikitext'], $this->text ); + } $this->getResult()->addValue( null, $this->getModuleName(), $result_array ); return; } - $p_result = $wgParser->parse( $text, $titleObj, $popts ); + $p_result = $wgParser->parse( $params['pst'] ? $this->pstText : $this->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'] ) ) { $result_array['text'] = array(); $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'] ) ) + + if ( isset( $prop['langlinks'] ) ) { $result_array['langlinks'] = $this->formatLangLinks( $p_result->getLanguageLinks() ); - if ( isset( $prop['categories'] ) ) + } + if ( isset( $prop['languageshtml'] ) ) { + $languagesHtml = $this->languagesHtml( $p_result->getLanguageLinks() ); + $result_array['languageshtml'] = array(); + $result->setContent( $result_array['languageshtml'], $languagesHtml ); + } + if ( isset( $prop['categories'] ) ) { $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() ); - if ( isset( $prop['links'] ) ) + } + if ( isset( $prop['categorieshtml'] ) ) { + $categoriesHtml = $this->categoriesHtml( $p_result->getCategories() ); + $result_array['categorieshtml'] = array(); + $result->setContent( $result_array['categorieshtml'], $categoriesHtml ); + } + if ( isset( $prop['links'] ) ) { $result_array['links'] = $this->formatLinks( $p_result->getLinks() ); - if ( isset( $prop['templates'] ) ) + } + if ( isset( $prop['templates'] ) ) { $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() ); - if ( isset( $prop['images'] ) ) + } + if ( isset( $prop['images'] ) ) { $result_array['images'] = array_keys( $p_result->getImages() ); - if ( isset( $prop['externallinks'] ) ) + } + if ( isset( $prop['externallinks'] ) ) { $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() ); - if ( isset( $prop['sections'] ) ) + } + 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 ( isset( $prop['headitems'] ) ) - $result_array['headitems'] = $this->formatHeadItems( $p_result->getHeadItems() ); - - if ( isset( $prop['headhtml'] ) ) { + } + + if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) { $out = new OutputPage; $out->addParserOutputNoText( $p_result ); + $userSkin = $wgUser->getSkin(); + } + + if ( isset( $prop['headitems'] ) ) { + $headItems = $this->formatHeadItems( $p_result->getHeadItems() ); + + $userSkin->setupUserCss( $out ); + $css = $this->formatCss( $out->buildCssLinksArray() ); + + $scripts = array( $out->getHeadScripts( $userSkin ) ); + + $result_array['headitems'] = array_merge( $headItems, $css, $scripts ); + } + + if ( isset( $prop['headhtml'] ) ) { $result_array['headhtml'] = array(); - $result->setContent( $result_array['headhtml'], $out->headElement( $wgUser->getSkin() ) ); + $result->setContent( $result_array['headhtml'], $out->headElement( $userSkin ) ); + } + + if ( isset( $prop['iwlinks'] ) ) { + $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() ); } - if ( !is_null( $oldid ) ) + if ( isset( $prop['wikitext'] ) ) { + $result_array['wikitext'] = array(); + $result->setContent( $result_array['wikitext'], $this->text ); + if ( !is_null( $this->pstText ) ) { + $result_array['psttext'] = array(); + $result->setContent( $result_array['psttext'], $this->pstText ); + } + } + + if ( !is_null( $oldid ) ) { $result_array['revid'] = intval( $oldid ); + } $result_mapping = array( 'redirects' => 'r', @@ -190,11 +290,54 @@ class ApiParse extends ApiBase { 'templates' => 'tl', 'images' => 'img', 'externallinks' => 'el', + 'iwlinks' => 'iw', 'sections' => 's', - 'headitems' => 'hi' + 'headitems' => 'hi', ); $this->setIndexedTagNames( $result_array, $result_mapping ); $result->addValue( null, $this->getModuleName(), $result_array ); + + if ( !is_null( $oldLang ) ) { + $wgLang = $oldLang; // Reset $wgLang to $oldLang + } + } + + /** + * @param $articleObj Article + * @param $titleObj Title + * @param $popts ParserOptions + * @param $pageId Int + * @param $getWikitext Bool + * @return ParserOutput + */ + private function getParsedSectionOrText( $articleObj, $titleObj, $popts, $pageId = null, $getWikitext = false ) { + if ( $this->section !== false ) { + global $wgParser; + + $this->text = $this->getSectionText( $articleObj->getRawText(), !is_null ( $pageId ) + ? 'page id ' . $pageId : $titleObj->getText() ); + + return $wgParser->parse( $this->text, $titleObj, $popts ); + } else { + // Try the parser cache first + $pout = $articleObj->getParserOutput(); + if ( $getWikitext ) { + $rev = Revision::newFromTitle( $titleObj ); + if ( $rev ) { + $this->text = $rev->getText(); + } + } + return $pout; + } + } + + private function getSectionText( $text, $what ) { + global $wgParser; + $text = $wgParser->getSection( $text, $this->section, false ); + if ( $text === false ) { + $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' ); + } + return $text; } private function formatLangLinks( $links ) { @@ -202,7 +345,12 @@ class ApiParse extends ApiBase { foreach ( $links as $link ) { $entry = array(); $bits = explode( ':', $link, 2 ); + $title = Title::newFromText( $link ); + $entry['lang'] = $bits[0]; + if ( $title ) { + $entry['url'] = $title->getFullURL(); + } $this->getResult()->setContent( $entry, $bits[1] ); $result[] = $entry; } @@ -220,6 +368,20 @@ class ApiParse extends ApiBase { return $result; } + private function categoriesHtml( $categories ) { + global $wgOut, $wgUser; + $wgOut->addCategoryLinks( $categories ); + $sk = $wgUser->getSkin(); + return $sk->getCategories(); + } + + private function languagesHtml( $languages ) { + global $wgOut, $wgUser; + $wgOut->setLanguageLinks( $languages ); + $sk = $wgUser->getSkin(); + return $sk->otherLanguages(); + } + private function formatLinks( $links ) { $result = array(); foreach ( $links as $ns => $nslinks ) { @@ -227,8 +389,28 @@ class ApiParse extends ApiBase { $entry = array(); $entry['ns'] = $ns; $this->getResult()->setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() ); - if ( $id != 0 ) + if ( $id != 0 ) { $entry['exists'] = ''; + } + $result[] = $entry; + } + } + return $result; + } + + private function formatIWLinks( $iw ) { + $result = array(); + foreach ( $iw as $prefix => $titles ) { + foreach ( array_keys( $titles ) as $title ) { + $entry = array(); + $entry['prefix'] = $prefix; + + $title = Title::newFromText( "{$prefix}:{$title}" ); + if ( $title ) { + $entry['url'] = $title->getFullURL(); + } + + $this->getResult()->setContent( $entry, $title->getFullText() ); $result[] = $entry; } } @@ -246,30 +428,45 @@ class ApiParse extends ApiBase { return $result; } + private function formatCss( $css ) { + $result = array(); + foreach ( $css as $file => $link ) { + $entry = array(); + $entry['file'] = $file; + $this->getResult()->setContent( $entry, $link ); + $result[] = $entry; + } + return $result; + } + private function setIndexedTagNames( &$array, $mapping ) { foreach ( $mapping as $key => $name ) { - if ( isset( $array[$key] ) ) + if ( isset( $array[$key] ) ) { $this->getResult()->setIndexedTagName( $array[$key], $name ); + } } } public function getAllowedParams() { - return array ( + return array( 'title' => array( - ApiBase :: PARAM_DFLT => 'API', + ApiBase::PARAM_DFLT => 'API', ), 'text' => null, 'summary' => null, 'page' => null, + 'pageid' => null, 'redirects' => false, 'oldid' => null, 'prop' => array( - ApiBase :: PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid|displaytitle', - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid|displaytitle', + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'text', 'langlinks', + 'languageshtml', 'categories', + 'categorieshtml', 'links', 'templates', 'images', @@ -278,54 +475,84 @@ class ApiParse extends ApiBase { 'revid', 'displaytitle', 'headitems', - 'headhtml' + 'headhtml', + 'iwlinks', + 'wikitext', ) ), 'pst' => false, 'onlypst' => false, + 'uselang' => null, + 'section' => null, + 'disablepp' => false, ); } public function getParamDescription() { - return array ( + $p = $this->getModulePrefix(); + return array( 'text' => 'Wikitext to parse', 'summary' => 'Summary to parse', - 'redirects' => 'If the page parameter is set to a redirect, resolve it', + 'redirects' => "If the {$p}page parameter is set to a redirect, resolve it", 'title' => 'Title of page the text belongs to', - 'page' => 'Parse the content of this page. Cannot be used together with text and title', - 'oldid' => 'Parse the content of this revision. Overrides page', - 'prop' => array( 'Which pieces of information to get.', - 'NOTE: Section tree is only generated if there are more than 4 sections, or if the __TOC__ keyword is present' + 'page' => "Parse the content of this page. Cannot be used together with {$p}text and {$p}title", + 'pageid' => "Parse the content of this page. Overrides {$p}page", + 'oldid' => "Parse the content of this revision. Overrides {$p}page and {$p}pageid", + 'prop' => array( + 'Which pieces of information to get', + ' text - Gives the parsed text of the wikitext', + ' langlinks - Gives the langlinks the parsed wikitext', + ' categories - Gives the categories of the parsed wikitext', + ' categorieshtml - Gives the html version of the categories', + ' languageshtml - Gives the html version of the languagelinks', + ' links - Gives the internal links in the parsed wikitext', + ' templates - Gives the templates in the parsed wikitext', + ' images - Gives the images in the parsed wikitext', + ' externallinks - Gives the external links in the parsed wikitext', + ' sections - Gives the sections in the parsed wikitext', + ' revid - Adds the revision id of the parsed page', + ' displaytitle - Adds the title of the parsed wikitext', + ' headitems - Gives items to put in the of the page', + ' headhtml - Gives parsed of the page', + ' iwlinks - Gives interwiki links in the parsed wikitext', + ' wikitext - Gives the original wikitext that was parsed', ), - 'pst' => array( 'Do a pre-save transform on the input before parsing it.', - 'Ignored if page or oldid is used.' + 'pst' => array( + 'Do a pre-save transform on the input before parsing it', + 'Ignored if page, pageid or oldid is used' ), - 'onlypst' => array( 'Do a PST on the input, but don\'t parse it.', - 'Returns PSTed wikitext. Ignored if page or oldid is used.' + 'onlypst' => array( + 'Do a pre-save transform (PST) on the input, but don\'t parse it', + 'Returns the same wikitext, after a PST has been applied. Ignored if page, pageid or oldid is used' ), + 'uselang' => 'Which language to parse the request in', + 'section' => 'Only retrieve the content of this section number', + 'disablepp' => 'Disable the PP Report from the parser output', ); } 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' ), + array( 'code' => 'nosuchsection', 'info' => 'There is no section sectionnumber in page' ), + array( 'nosuchpageid' ), ) ); } protected function getExamples() { - return array ( + return array( 'api.php?action=parse&text={{Project:Sandbox}}' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiParse.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiParse.php 89672 2011-06-07 18:45:20Z catrope $'; } } diff --git a/includes/api/ApiPatrol.php b/includes/api/ApiPatrol.php index 79916117..08835743 100644 --- a/includes/api/ApiPatrol.php +++ b/includes/api/ApiPatrol.php @@ -1,11 +1,10 @@ extractRequestParams(); - - if ( !isset( $params['rcid'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'rcid' ) ); $rc = RecentChange::newFromID( $params['rcid'] ); - if ( !$rc instanceof RecentChange ) + if ( !$rc instanceof RecentChange ) { $this->dieUsageMsg( array( 'nosuchrcid', $params['rcid'] ) ); + } $retval = RecentChange::markPatrolled( $params['rcid'] ); - - if ( $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 ); } + public function mustBePosted() { + return true; + } + public function isWriteMode() { return true; } public function getAllowedParams() { - return array ( + return array( 'token' => null, 'rcid' => array( - ApiBase :: PARAM_TYPE => 'integer' + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_REQUIRED => true ), ); } public function getParamDescription() { - return array ( + return array( 'token' => 'Patrol token obtained from list=recentchanges', 'rcid' => 'Recentchanges ID to patrol', ); } public function getDescription() { - return array ( - 'Patrol a page or revision. ' - ); + return 'Patrol a page or revision'; } - - public function getPossibleErrors() { + + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( - array( 'missingparam', 'rcid' ), array( 'nosuchrcid', 'rcid' ), - ) ); + ) ); } - + public function needsToken() { return true; } public function getTokenSalt() { - return ''; + return 'patrol'; } protected function getExamples() { @@ -107,6 +109,6 @@ class ApiPatrol extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiPatrol.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiPatrol.php 78437 2010-12-15 14:14:16Z catrope $'; } -} \ No newline at end of file +} diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php index 0b1ae4c8..3a1d18e0 100644 --- a/includes/api/ApiProtect.php +++ b/includes/api/ApiProtect.php @@ -1,10 +1,10 @@ .@home.nl + * Created on Sep 1, 2007 + * + * 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 @@ -18,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -33,68 +35,68 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiProtect extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { - global $wgUser, $wgRestrictionTypes, $wgRestrictionLevels; + global $wgUser, $wgRestrictionLevels; $params = $this->extractRequestParams(); - $titleObj = null; - if ( !isset( $params['title'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'title' ) ); - if ( empty( $params['protections'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'protections' ) ); - $titleObj = Title::newFromText( $params['title'] ); - if ( !$titleObj ) + if ( !$titleObj ) { $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + } $errors = $titleObj->getUserPermissionsErrors( 'protect', $wgUser ); - if ( $errors ) + if ( $errors ) { // We don't care about multiple errors, just report one of them $this->dieUsageMsg( reset( $errors ) ); + } $expiry = (array)$params['expiry']; - if ( count( $expiry ) != count( $params['protections'] ) ) - { - if ( count( $expiry ) == 1 ) + if ( count( $expiry ) != count( $params['protections'] ) ) { + if ( count( $expiry ) == 1 ) { $expiry = array_fill( 0, count( $params['protections'] ), $expiry[0] ); - else + } else { $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' ) + if ( $titleObj->exists() && $p[0] == 'create' ) { $this->dieUsageMsg( array( 'create-titleexists' ) ); - if ( !$titleObj->exists() && $p[0] != 'create' ) + } + if ( !$titleObj->exists() && $p[0] != 'create' ) { $this->dieUsageMsg( array( 'missingtitle-createonly' ) ); + } - if ( !in_array( $p[0], $restrictionTypes ) && $p[0] != 'create' ) + 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' ) + } + 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' ) ) ) + if ( in_array( $expiry[$i], array( 'infinite', 'indefinite', 'never' ) ) ) { $expiryarray[$p[0]] = Block::infinity(); - else - { + } else { $exp = strtotime( $expiry[$i] ); - if ( $exp < 0 || $exp == false ) + if ( $exp < 0 || !$exp ) { $this->dieUsageMsg( array( 'invalidexpiry', $expiry[$i] ) ); + } $exp = wfTimestamp( TS_MW, $exp ); - if ( $exp < wfTimestampNow() ) + if ( $exp < wfTimestampNow() ) { $this->dieUsageMsg( array( 'pastexpiry', $expiry[$i] ) ); + } $expiryarray[$p[0]] = $exp; } $resultProtections[] = array( $p[0] => $protections[$p[0]], @@ -105,19 +107,27 @@ class ApiProtect extends ApiBase { $cascade = $params['cascade']; $articleObj = new Article( $titleObj ); - if ( $params['watch'] ) - $articleObj->doWatch(); - if ( $titleObj->exists() ) + + $watch = $params['watch'] ? 'watch' : $params['watchlist']; + $this->setWatch( $watch, $titleObj ); + + if ( $titleObj->exists() ) { $ok = $articleObj->updateRestrictions( $protections, $params['reason'], $cascade, $expiryarray ); - else + } else { $ok = $titleObj->updateTitleProtection( $protections['create'], $params['reason'], $expiryarray['create'] ); - if ( !$ok ) + } + 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 ) + } + $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 ); @@ -132,26 +142,42 @@ class ApiProtect extends ApiBase { } public function getAllowedParams() { - return array ( - 'title' => null, + return array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'token' => null, 'protections' => array( - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_REQUIRED => true, ), 'expiry' => array( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_ALLOW_DUPLICATES => true, - ApiBase :: PARAM_DFLT => 'infinite', + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_ALLOW_DUPLICATES => true, + ApiBase::PARAM_DFLT => 'infinite', ), 'reason' => '', 'cascade' => false, - 'watch' => false, + 'watch' => array( + ApiBase::PARAM_DFLT => false, + ApiBase::PARAM_DEPRECATED => true, + ), + 'watchlist' => array( + ApiBase::PARAM_DFLT => 'preferences', + ApiBase::PARAM_TYPE => array( + 'watch', + 'unwatch', + 'preferences', + 'nochange' + ), + ), ); } public function getParamDescription() { - return array ( - 'title' => 'Title of the page you want to (un)protect.', + return array( + '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.', @@ -160,19 +186,16 @@ class ApiProtect extends ApiBase { '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', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', ); } public function getDescription() { - return array( - 'Change the protection level of a page.' - ); + return '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' ), @@ -183,7 +206,7 @@ class ApiProtect extends ApiBase { array( 'pastexpiry', 'expiry' ), ) ); } - + public function needsToken() { return true; } @@ -193,13 +216,13 @@ class ApiProtect extends ApiBase { } protected function getExamples() { - return array ( - 'api.php?action=protect&title=Main%20Page&token=123ABC&protections=edit=sysop|move=sysop&cascade&expiry=20070901163000|never', + return array( + 'api.php?action=protect&title=Main%20Page&token=123ABC&protections=edit=sysop|move=sysop&cascade=&expiry=20070901163000|never', 'api.php?action=protect&title=Main%20Page&token=123ABC&protections=edit=all|move=all&reason=Lifting%20restrictions' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiProtect.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiProtect.php 77192 2010-11-23 22:05:27Z btongminh $'; } } diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php index 76d45404..a17abf16 100644 --- a/includes/api/ApiPurge.php +++ b/includes/api/ApiPurge.php @@ -1,11 +1,11 @@ extractRequestParams(); - if ( !$wgUser->isAllowed( 'purge' ) ) - $this->dieUsageMsg( array( 'cantpurge' ) ); - if ( !isset( $params['titles'] ) ) - $this->dieUsageMsg( array( 'missingparam', 'titles' ) ); + if ( !$wgUser->isAllowed( 'purge' ) && !$this->getMain()->isInternalMode() && + !$this->getMain()->getRequest()->wasPosted() ) { + $this->dieUsageMsg( array( 'mustbeposted', $this->getModuleName() ) ); + } $result = array(); foreach ( $params['titles'] as $t ) { $r = array(); $title = Title::newFromText( $t ); - if ( !$title instanceof Title ) - { + if ( !$title instanceof Title ) { $r['title'] = $t; $r['invalid'] = ''; $result[] = $r; continue; } ApiQueryBase::addTitleInfo( $r, $title ); - if ( !$title->exists() ) - { + if ( !$title->exists() ) { $r['missing'] = ''; $result[] = $r; continue; } - $article = Mediawiki::articleFromTitle( $title ); + $article = MediaWiki::articleFromTitle( $title ); $article->doPurge(); // Directly purge and skip the UI part of purge(). $r['purged'] = ''; $result[] = $r; @@ -74,40 +74,35 @@ class ApiPurge extends ApiBase { $this->getResult()->addValue( null, $this->getModuleName(), $result ); } - public function mustBePosted() { - global $wgUser; - return $wgUser->isAnon(); - } - public function isWriteMode() { return true; } public function getAllowedParams() { - return array ( + return array( 'titles' => array( - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_REQUIRED => true ) ); } public function getParamDescription() { - return array ( + return array( 'titles' => 'A list of titles', ); } public function getDescription() { - return array ( - 'Purge the cache for the given titles.' + return array( 'Purge the cache for the given titles.', + 'This module requires a POST request if the user is not logged in.' ); } - - public function getPossibleErrors() { + + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'cantpurge' ), - array( 'missingparam', 'titles' ), - ) ); + ) ); } protected function getExamples() { @@ -117,6 +112,6 @@ class ApiPurge extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiPurge.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiPurge.php 74944 2010-10-18 09:19:20Z catrope $'; } } diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php index 8d3ef616..f88aa850 100644 --- a/includes/api/ApiQuery.php +++ b/includes/api/ApiQuery.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 7, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -43,23 +44,26 @@ class ApiQuery extends ApiBase { private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames; private $mPageSet; - private $params, $redirect; + private $params, $redirects, $convertTitles; - private $mQueryPropModules = array ( + private $mQueryPropModules = array( 'info' => 'ApiQueryInfo', 'revisions' => 'ApiQueryRevisions', 'links' => 'ApiQueryLinks', + 'iwlinks' => 'ApiQueryIWLinks', 'langlinks' => 'ApiQueryLangLinks', 'images' => 'ApiQueryImages', 'imageinfo' => 'ApiQueryImageInfo', + 'stashimageinfo' => 'ApiQueryStashImageInfo', 'templates' => 'ApiQueryLinks', 'categories' => 'ApiQueryCategories', 'extlinks' => 'ApiQueryExternalLinks', 'categoryinfo' => 'ApiQueryCategoryInfo', 'duplicatefiles' => 'ApiQueryDuplicateFiles', + 'pageprops' => 'ApiQueryPageProps', ); - private $mQueryListModules = array ( + private $mQueryListModules = array( 'allimages' => 'ApiQueryAllimages', 'allpages' => 'ApiQueryAllpages', 'alllinks' => 'ApiQueryAllLinks', @@ -70,7 +74,9 @@ class ApiQuery extends ApiBase { 'categorymembers' => 'ApiQueryCategoryMembers', 'deletedrevs' => 'ApiQueryDeletedrevs', 'embeddedin' => 'ApiQueryBacklinks', + 'filearchive' => 'ApiQueryFilearchive', 'imageusage' => 'ApiQueryBacklinks', + 'iwbacklinks' => 'ApiQueryIWBacklinks', 'logevents' => 'ApiQueryLogEvents', 'recentchanges' => 'ApiQueryRecentChanges', 'search' => 'ApiQuerySearch', @@ -84,7 +90,7 @@ class ApiQuery extends ApiBase { 'protectedtitles' => 'ApiQueryProtectedTitles', ); - private $mQueryMetaModules = array ( + private $mQueryMetaModules = array( 'siteinfo' => 'ApiQuerySiteinfo', 'userinfo' => 'ApiQueryUserInfo', 'allmessages' => 'ApiQueryAllmessages', @@ -94,13 +100,13 @@ class ApiQuery extends ApiBase { private $mNamedDB = array(); public function __construct( $main, $action ) { - parent :: __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 ); @@ -129,7 +135,7 @@ 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->profileDBOut(); @@ -172,14 +178,37 @@ class ApiQuery extends ApiBase { return array_merge( $this->mQueryPropModules, $this->mQueryListModules, $this->mQueryMetaModules ); } + /** + * Get whether the specified module is a prop, list or a meta query module + * @param $moduleName string Name of the module to find type for + * @return mixed string or null + */ + function getModuleType( $moduleName ) { + if ( array_key_exists ( $moduleName, $this->mQueryPropModules ) ) { + return 'prop'; + } + + if ( array_key_exists ( $moduleName, $this->mQueryListModules ) ) { + return 'list'; + } + + if ( array_key_exists ( $moduleName, $this->mQueryMetaModules ) ) { + return 'meta'; + } + + return null; + } + 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' ) ); - else + } else { return null; + } } /** @@ -193,26 +222,26 @@ class ApiQuery extends ApiBase { * #5 Execute all requested modules */ public function execute() { - $this->params = $this->extractRequestParams(); $this->redirects = $this->params['redirects']; + $this->convertTitles = $this->params['converttitles']; // Create PageSet - $this->mPageSet = new ApiPageSet( $this, $this->redirects ); + $this->mPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles ); // Instantiate requested modules - $modules = array (); - $this->InstantiateModules( $modules, 'prop', $this->mQueryPropModules ); - $this->InstantiateModules( $modules, 'list', $this->mQueryListModules ); - $this->InstantiateModules( $modules, 'meta', $this->mQueryMetaModules ); + $modules = array(); + $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'] ) ) { + if ( isset( $this->params['generator'] ) ) { $generator = $this->newGenerator( $this->params['generator'] ); $params = $generator->extractRequestParams(); - $cacheMode = $this->mergeCacheMode( $cacheMode, + $cacheMode = $this->mergeCacheMode( $cacheMode, $generator->getCacheMode( $params ) ); $this->executeGeneratorModule( $generator, $modules ); } else { @@ -227,7 +256,7 @@ class ApiQuery extends ApiBase { // Execute all requested modules. foreach ( $modules as $module ) { $params = $module->extractRequestParams(); - $cacheMode = $this->mergeCacheMode( + $cacheMode = $this->mergeCacheMode( $cacheMode, $module->getCacheMode( $params ) ); $module->profileIn(); $module->execute(); @@ -241,7 +270,7 @@ class ApiQuery extends ApiBase { /** * Update a cache mode string, applying the cache mode of a new module to it. - * The cache mode may increase in the level of privacy, but public modules + * The cache mode may increase in the level of privacy, but public modules * added to private data do not decrease the level of privacy. */ protected function mergeCacheMode( $cacheMode, $modCacheMode ) { @@ -273,15 +302,17 @@ class ApiQuery extends ApiBase { /** * Create instances of all modules requested by the client - * @param $modules array to append instatiated modules to + * @param $modules Array to append instantiated modules to * @param $param string Parameter name to read modules from - * @param $moduleList array(modulename => classname) + * @param $moduleList Array 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 ) + if ( !is_null ( $list ) ) { + foreach ( $list as $moduleName ) { $modules[] = new $moduleList[$moduleName] ( $this, $moduleName ); + } + } } /** @@ -290,7 +321,6 @@ class ApiQuery extends ApiBase { * and missing or invalid title/pageids/revids. */ private function outputGeneralPageInfo() { - $pageSet = $this->getPageSet(); $result = $this->getResult(); @@ -299,9 +329,9 @@ class ApiQuery extends ApiBase { // and the maximum result size must be even higher than that. // Title normalizations - $normValues = array (); + $normValues = array(); foreach ( $pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr ) { - $normValues[] = array ( + $normValues[] = array( 'from' => $rawTitleStr, 'to' => $titleStr ); @@ -312,10 +342,24 @@ class ApiQuery extends ApiBase { $result->addValue( 'query', 'normalized', $normValues ); } + // Title conversions + $convValues = array(); + foreach ( $pageSet->getConvertedTitles() as $rawTitleStr => $titleStr ) { + $convValues[] = array( + 'from' => $rawTitleStr, + 'to' => $titleStr + ); + } + + if ( count( $convValues ) ) { + $result->setIndexedTagName( $convValues, 'c' ); + $result->addValue( 'query', 'converted', $convValues ); + } + // Interwiki titles - $intrwValues = array (); + $intrwValues = array(); foreach ( $pageSet->getInterwikiTitles() as $rawTitleStr => $interwikiStr ) { - $intrwValues[] = array ( + $intrwValues[] = array( 'title' => $rawTitleStr, 'iw' => $interwikiStr ); @@ -327,9 +371,9 @@ class ApiQuery extends ApiBase { } // Show redirect information - $redirValues = array (); + $redirValues = array(); foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleStrTo ) { - $redirValues[] = array ( + $redirValues[] = array( 'from' => strval( $titleStrFrom ), 'to' => $titleStrTo ); @@ -340,14 +384,12 @@ class ApiQuery extends ApiBase { $result->addValue( 'query', 'redirects', $redirValues ); } - // // Missing revision elements - // $missingRevIDs = $pageSet->getMissingRevisionIDs(); if ( count( $missingRevIDs ) ) { - $revids = array (); + $revids = array(); foreach ( $missingRevIDs as $revid ) { - $revids[$revid] = array ( + $revids[$revid] = array( 'revid' => $revid ); } @@ -355,39 +397,51 @@ class ApiQuery extends ApiBase { $result->addValue( 'query', 'badrevids', $revids ); } - // // Page elements - // - $pages = array (); + $pages = array(); // Report any missing titles 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 ) + foreach ( $pageSet->getInvalidTitles() as $fakeId => $title ) { $pages[$fakeId] = array( 'title' => $title, 'invalid' => '' ); + } // Report any missing page ids foreach ( $pageSet->getMissingPageIDs() as $pageid ) { - $pages[$pageid] = array ( + $pages[$pageid] = array( 'pageid' => $pageid, 'missing' => '' ); } + // Report special pages + foreach ( $pageSet->getSpecialTitles() as $fakeId => $title ) { + $vals = array(); + ApiQueryBase::addTitleInfo( $vals, $title ); + $vals['special'] = ''; + if ( $title->getNamespace() == NS_SPECIAL && + !SpecialPage::exists( $title->getDbKey() ) ) { + $vals['missing'] = ''; + } elseif ( $title->getNamespace() == NS_MEDIA && + !wfFindFile( $title ) ) { + $vals['missing'] = ''; + } + $pages[$fakeId] = $vals; + } // Output general page information for found titles 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 ( $this->params['indexpageids'] ) { $pageIDs = array_keys( $pages ); // json treats all map keys as strings - converting to match @@ -400,57 +454,76 @@ class ApiQuery extends ApiBase { $result->addValue( 'query', 'pages', $pages ); } 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 ); - $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'] ) { - $result->reset(); - // Raw formatter will handle this - $result->addValue( null, 'text', $exportxml ); - $result->addValue( null, 'mime', 'text/xml' ); - } else { - $r = array(); - ApiResult::setContent( $r, $exportxml ); - $result->addValue( 'query', 'export', $r ); + $this->doExport( $pageSet, $result ); + } + } + + /** + * @param $pageSet ApiPageSet Pages to be exported + * @param $result ApiResult Result to output to + */ + private function doExport( $pageSet, $result ) { + $exportTitles = array(); + $titles = $pageSet->getGoodTitles(); + if( count( $titles ) ) { + foreach ( $titles as $title ) { + if ( $title->userCanRead() ) { + $exportTitles[] = $title; + } } - $result->enableSizeCheck(); } + // only export when there are titles + if ( !count( $exportTitles ) ) { + return; + } + + $exporter = new WikiExporter( $this->getDB() ); + // WikiExporter writes to stdout, so catch its + // output with an ob + ob_start(); + $exporter->openStream(); + foreach ( $exportTitles as $title ) { + $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'] ) { + $result->reset(); + // Raw formatter will handle this + $result->addValue( null, 'text', $exportxml ); + $result->addValue( null, 'mime', 'text/xml' ); + } else { + $r = array(); + ApiResult::setContent( $r, $exportxml ); + $result->addValue( 'query', 'export', $r ); + } + $result->enableSizeCheck(); } /** * Create a generator object of the given type and return it + * @param $generatorName string Module name + * @return ApiQueryGeneratorBase */ 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 ); - - // 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" ); + if ( !$generator instanceof ApiQueryGeneratorBase ) { + $this->dieUsage( "Module $generatorName cannot be used as a generator", 'badgenerator' ); + } $generator->setGeneratorMode(); return $generator; } @@ -458,7 +531,7 @@ class ApiQuery extends ApiBase { /** * For generator mode, execute generator, and use its output as new * ApiPageSet - * @param $generatorName string Module name + * @param $generator ApiQueryGeneratorBase Generator Module * @param $modules array of module objects */ protected function executeGeneratorModule( $generator, $modules ) { @@ -484,23 +557,24 @@ class ApiQuery extends ApiBase { } public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => $this->mPropModuleNames + return array( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => $this->mPropModuleNames ), - 'list' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => $this->mListModuleNames + 'list' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => $this->mListModuleNames ), - 'meta' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => $this->mMetaModuleNames + 'meta' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => $this->mMetaModuleNames ), - 'generator' => array ( - ApiBase :: PARAM_TYPE => $this->mAllowedGenerators + 'generator' => array( + ApiBase::PARAM_TYPE => $this->mAllowedGenerators ), 'redirects' => false, + 'converttitles' => false, 'indexpageids' => false, 'export' => false, 'exportnowrap' => false, @@ -512,49 +586,48 @@ class ApiQuery extends ApiBase { * @return string */ public function makeHelpMsg() { - $msg = ''; // Make sure the internal object is empty // (just in case a sub-module decides to optimize during instantiation) $this->mPageSet = null; - $this->mAllowedGenerators = array(); // Will be repopulated + $this->mAllowedGenerators = array(); // Will be repopulated - $astriks = str_repeat( '--- ', 8 ); - $astriks2 = str_repeat( '*** ', 10 ); - $msg .= "\n$astriks Query: Prop $astriks\n\n"; + $querySeparator = str_repeat( '--- ', 8 ); + $moduleSeparator = str_repeat( '*** ', 10 ); + $msg .= "\n$querySeparator Query: Prop $querySeparator\n\n"; $msg .= $this->makeHelpMsgHelper( $this->mQueryPropModules, 'prop' ); - $msg .= "\n$astriks Query: List $astriks\n\n"; + $msg .= "\n$querySeparator Query: List $querySeparator\n\n"; $msg .= $this->makeHelpMsgHelper( $this->mQueryListModules, 'list' ); - $msg .= "\n$astriks Query: Meta $astriks\n\n"; + $msg .= "\n$querySeparator Query: Meta $querySeparator\n\n"; $msg .= $this->makeHelpMsgHelper( $this->mQueryMetaModules, 'meta' ); - $msg .= "\n\n$astriks2 Modules: continuation $astriks2\n\n"; + $msg .= "\n\n$moduleSeparator Modules: continuation $moduleSeparator\n\n"; // Perform the base call last because the $this->mAllowedGenerators // will be updated inside makeHelpMsgHelper() // Use parent to make default message for the query module - $msg = parent :: makeHelpMsg() . $msg; + $msg = parent::makeHelpMsg() . $msg; return $msg; } /** * For all modules in $moduleList, generate help messages and join them together - * @param $moduleList array(modulename => classname) + * @param $moduleList Array array(modulename => classname) * @param $paramName string Parameter name * @return string */ private function makeHelpMsgHelper( $moduleList, $paramName ) { - - $moduleDescriptions = array (); + $moduleDescriptions = array(); foreach ( $moduleList as $moduleName => $moduleClass ) { $module = new $moduleClass ( $this, $moduleName, null ); $msg = ApiMain::makeHelpMsgHeader( $module, $paramName ); $msg2 = $module->makeHelpMsg(); - if ( $msg2 !== false ) + if ( $msg2 !== false ) { $msg .= $msg2; + } if ( $module instanceof ApiQueryGeneratorBase ) { $this->mAllowedGenerators[] = $moduleName; $msg .= "Generator:\n This module may be used as a generator\n"; @@ -571,7 +644,7 @@ class ApiQuery extends ApiBase { */ public function makeHelpMsgParameters() { $psModule = new ApiPageSet( $this ); - return $psModule->makeHelpMsgParameters() . parent :: makeHelpMsgParameters(); + return $psModule->makeHelpMsgParameters() . parent::makeHelpMsgParameters(); } public function shouldCheckMaxlag() { @@ -579,27 +652,29 @@ class ApiQuery extends ApiBase { } public function getParamDescription() { - return array ( - 'prop' => 'Which properties to get for the titles/revisions/pageids', - 'list' => 'Which lists to get', - 'meta' => 'Which meta data to get about the site', + return array( + 'prop' => 'Which properties to get for the titles/revisions/pageids. Module help is available below', + 'list' => 'Which lists to get. Module help is available below', + 'meta' => 'Which metadata to get about the site. Module help is available below', '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.' ), + '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.', + 'converttitles' => array( "Convert titles to other variants if necessary. Only works if the wiki's content language supports variant conversion.", + 'Languages that support variant conversion include kk, ku, gan, tg, sr, zh' ), + 'indexpageids' => 'Include an additional pageids section listing all returned page IDs', 'export' => 'Export the current revisions of all given or generated pages', 'exportnowrap' => 'Return the export XML without wrapping it in an XML result (same format as Special:Export). Can only be used with export', ); } public function getDescription() { - return array ( + return array( 'Query API module allows applications to get needed pieces of data from the MediaWiki databases,', 'and is loosely based on the old query.php interface.', - 'All data modifications will first have to use query to acquire a token to prevent abuse from malicious sites.' + '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' ), @@ -607,7 +682,7 @@ class ApiQuery extends ApiBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&prop=revisions&meta=siteinfo&titles=Main%20Page&rvprop=user|comment', 'api.php?action=query&generator=allpages&gapprefix=API/&prop=revisions', ); @@ -615,8 +690,8 @@ class ApiQuery extends ApiBase { public function getVersion() { $psModule = new ApiPageSet( $this ); - $vers = array (); - $vers[] = __CLASS__ . ': $Id: ApiQuery.php 69932 2010-07-26 08:03:21Z tstarling $'; + $vers = array(); + $vers[] = __CLASS__ . ': $Id: ApiQuery.php 80897 2011-01-24 18:57:42Z catrope $'; $vers[] = $psModule->getVersion(); return $vers; } diff --git a/includes/api/ApiQueryAllCategories.php b/includes/api/ApiQueryAllCategories.php index 8f24fc7c..c1473252 100644 --- a/includes/api/ApiQueryAllCategories.php +++ b/includes/api/ApiQueryAllCategories.php @@ -1,11 +1,10 @@ .@home.nl + * Created on December 12, 2007 + * + * 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -37,7 +38,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryAllCategories extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'ac' ); + parent::__construct( $query, $moduleName, 'ac' ); } public function execute() { @@ -53,7 +54,6 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { } private function run( $resultPageSet = null ) { - $db = $this->getDB(); $params = $this->extractRequestParams(); @@ -62,17 +62,19 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { $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'] ) ) + $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) ); + $this->addWhereRange( 'cat_title', $dir, $from, $to ); + + 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' : '' ) ); $prop = array_flip( $params['prop'] ); $this->addFieldsIf( array( 'cat_pages', 'cat_subcats', 'cat_files' ), isset( $prop['size'] ) ); - if ( isset( $prop['hidden'] ) ) - { + if ( isset( $prop['hidden'] ) ) { $this->addTables( array( 'page', 'page_props' ) ); $this->addJoinConds( array( 'page' => array( 'LEFT JOIN', array( @@ -88,10 +90,10 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { $res = $this->select( __METHOD__ ); $pages = array(); - $categories = array(); + $result = $this->getResult(); $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional cats to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown @@ -101,9 +103,9 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { // Normalize titles $titleObj = Title::makeTitle( NS_CATEGORY, $row->cat_title ); - if ( !is_null( $resultPageSet ) ) + if ( !is_null( $resultPageSet ) ) { $pages[] = $titleObj->getPrefixedText(); - else { + } else { $item = array(); $result->setContent( $item, $titleObj->getText() ); if ( isset( $prop['size'] ) ) { @@ -112,17 +114,16 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { $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 ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) ); break; } } } - $db->freeResult( $res ); if ( is_null( $resultPageSet ) ) { $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'c' ); @@ -132,38 +133,44 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { } public function getAllowedParams() { - return array ( + return array( 'from' => null, + 'to' => null, 'prefix' => null, 'dir' => array( - ApiBase :: PARAM_DFLT => 'ascending', - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'ascending', + ApiBase::PARAM_TYPE => array( 'ascending', 'descending' ), ), - '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 + '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_TYPE => array( 'size', 'hidden' ), - ApiBase :: PARAM_DFLT => '', - ApiBase :: PARAM_ISMULTI => true + 'prop' => array( + ApiBase::PARAM_TYPE => array( 'size', 'hidden' ), + ApiBase::PARAM_DFLT => '', + ApiBase::PARAM_ISMULTI => true ), ); } public function getParamDescription() { - return array ( - 'from' => 'The category to start enumerating from.', - 'prefix' => 'Search for all category titles that begin with this value.', - 'dir' => 'Direction to sort in.', - 'limit' => 'How many categories to return.', - 'prop' => 'Which properties to get', + return array( + 'from' => 'The category to start enumerating from', + 'to' => 'The category to stop enumerating at', + 'prefix' => 'Search for all category titles that begin with this value', + 'dir' => 'Direction to sort in', + 'limit' => 'How many categories to return', + 'prop' => array( + 'Which properties to get', + ' size - Adds number of pages in the category', + ' hidden - Tags categories that are hidden with __HIDDENCAT__', + ), ); } @@ -172,13 +179,13 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=allcategories&acprop=size', 'api.php?action=query&generator=allcategories&gacprefix=List&prop=info', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllCategories.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllCategories.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php index 6b6fc2c0..78784845 100644 --- a/includes/api/ApiQueryAllLinks.php +++ b/includes/api/ApiQueryAllLinks.php @@ -1,11 +1,10 @@ @gmail.com + * Created on July 7, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryAllLinks extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'al' ); + parent::__construct( $query, $moduleName, 'al' ); } public function execute() { @@ -52,7 +53,6 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { } private function run( $resultPageSet = null ) { - $db = $this->getDB(); $params = $this->extractRequestParams(); @@ -61,36 +61,46 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { $fld_title = isset( $prop['title'] ); if ( $params['unique'] ) { - if ( !is_null( $resultPageSet ) ) + if ( !is_null( $resultPageSet ) ) { $this->dieUsage( $this->getModuleName() . ' cannot be used as a generator in unique links mode', 'params' ); - if ( $fld_ids ) + } + 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'] ); - - if ( !is_null( $params['from'] ) && !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'] ) ) - { + } + if ( !is_null( $params['continue'] ) ) { $arr = explode( '|', $params['continue'] ); - if ( count( $arr ) != 2 ) - $this->dieUsage( "Invalid continue parameter", 'badcontinue' ); + 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)" ); + $this->addWhere( + "pl_title > '$from' OR " . + "(pl_title = '$from' AND " . + "pl_from > $id)" + ); } - if ( !is_null( $params['from'] ) ) + if ( !is_null( $params['from'] ) ) { $this->addWhere( 'pl_title>=' . $db->addQuotes( $this->titlePartToKey( $params['from'] ) ) ); - if ( isset ( $params['prefix'] ) ) + } + if ( !is_null( $params['to'] ) ) { + $this->addWhere( 'pl_title<=' . $db->addQuotes( $this->titlePartToKey( $params['to'] ) ) ); + } + 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'] ); @@ -98,49 +108,51 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { $this->addOption( 'USE INDEX', 'pl_namespace' ); $limit = $params['limit']; $this->addOption( 'LIMIT', $limit + 1 ); - if ( $params['unique'] ) + if ( $params['unique'] ) { $this->addOption( 'ORDER BY', 'pl_title' ); - else + } else { $this->addOption( 'ORDER BY', 'pl_title, pl_from' ); + } $res = $this->select( __METHOD__ ); - $pageids = array (); + $pageids = array(); $count = 0; $result = $this->getResult(); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown - if ( $params['unique'] ) + if ( $params['unique'] ) { $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) ); - else + } else { $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from ); + } break; } if ( is_null( $resultPageSet ) ) { $vals = array(); - if ( $fld_ids ) + if ( $fld_ids ) { $vals['fromid'] = intval( $row->pl_from ); + } if ( $fld_title ) { - $title = Title :: makeTitle( $params['namespace'], $row->pl_title ); + $title = Title::makeTitle( $params['namespace'], $row->pl_title ); ApiQueryBase::addTitleInfo( $vals, $title ); } $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); - if ( !$fit ) - { - if ( $params['unique'] ) + if ( !$fit ) { + if ( $params['unique'] ) { $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) ); - else + } else { $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from ); + } break; } } else { $pageids[] = $row->pl_from; } } - $db->freeResult( $res ); if ( is_null( $resultPageSet ) ) { $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'l' ); @@ -150,65 +162,73 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase { } public function getAllowedParams() { - return array ( + return array( 'continue' => null, 'from' => null, + 'to' => null, 'prefix' => null, 'unique' => false, - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'title', - ApiBase :: PARAM_TYPE => array ( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'title', + ApiBase::PARAM_TYPE => array( 'ids', 'title' ) ), - 'namespace' => array ( - ApiBase :: PARAM_DFLT => 0, - ApiBase :: PARAM_TYPE => 'namespace' + 'namespace' => array( + ApiBase::PARAM_DFLT => 0, + ApiBase::PARAM_TYPE => 'namespace' ), - 'limit' => array ( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ) ); } public function getParamDescription() { - return array ( - 'from' => 'The page title to start enumerating from.', - 'prefix' => 'Search for all page titles that begin with this value.', - 'unique' => 'Only show unique links. Cannot be used with generator or prop=ids', - 'prop' => 'What pieces of information to include', - 'namespace' => 'The namespace to enumerate.', - 'limit' => 'How many total links to return.', - 'continue' => 'When more results are available, use this to continue.', + $p = $this->getModulePrefix(); + return array( + 'from' => 'The page title to start enumerating from', + 'to' => 'The page title to stop enumerating at', + 'prefix' => 'Search for all page titles that begin with this value', + 'unique' => "Only show unique links. Cannot be used with generator or {$p}prop=ids", + 'prop' => array( + 'What pieces of information to include', + " ids - Adds pageid of where the link is from (Cannot be used with {$p}unique)", + ' title - Adds the title of the link', + ), + 'namespace' => 'The namespace to enumerate', + 'limit' => 'How many total links to return', + 'continue' => 'When more results are available, use this to continue', ); } public function getDescription() { return 'Enumerate all links that point to a given namespace'; } - + public function getPossibleErrors() { + $m = $this->getModuleName(); 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' => "{$m} cannot be used as a generator in unique links mode" ), + array( 'code' => 'params', 'info' => "{$m} 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 ( - 'api.php?action=query&list=alllinks&alunique&alfrom=B', + return array( + 'api.php?action=query&list=alllinks&alunique=&alfrom=B', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllLinks.php 77192 2010-11-23 22:05:27Z btongminh $'; } } diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php index 611fc98c..77f507fc 100644 --- a/includes/api/ApiQueryAllUsers.php +++ b/includes/api/ApiQueryAllUsers.php @@ -1,11 +1,10 @@ @gmail.com + * Created on July 7, 2007 + * + * Copyright © 2007 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -34,9 +35,8 @@ if ( !defined( 'MEDIAWIKI' ) ) { * @ingroup API */ class ApiQueryAllUsers extends ApiQueryBase { - public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'au' ); + parent::__construct( $query, $moduleName, 'au' ); } public function execute() { @@ -51,18 +51,23 @@ class ApiQueryAllUsers extends ApiQueryBase { $fld_groups = isset( $prop['groups'] ); $fld_registration = isset( $prop['registration'] ); } else { - $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = false; + $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = false; } $limit = $params['limit']; $this->addTables( 'user', 'u1' ); $useIndex = true; - if ( !is_null( $params['from'] ) ) + if ( !is_null( $params['from'] ) ) { $this->addWhere( 'u1.user_name >= ' . $db->addQuotes( $this->keyToTitle( $params['from'] ) ) ); + } + if ( !is_null( $params['to'] ) ) { + $this->addWhere( 'u1.user_name <= ' . $db->addQuotes( $this->keyToTitle( $params['to'] ) ) ); + } - if ( !is_null( $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'] ) ) { $useIndex = false; @@ -73,8 +78,9 @@ class ApiQueryAllUsers extends ApiQueryBase { 'ug1.ug_group' => $params['group'] ) ) ) ); } - if ( $params['witheditsonly'] ) + if ( $params['witheditsonly'] ) { $this->addWhere( 'u1.user_editcount > 0' ); + } if ( $fld_groups ) { // Show the groups the given users belong to @@ -89,19 +95,14 @@ class ApiQueryAllUsers extends ApiQueryBase { } else { $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 AS blocker_name' ) ); - } + $this->showHiddenUsersAddBlockInfo( $fld_blockinfo ); $this->addOption( 'LIMIT', $sqlLimit ); - $this->addFields( 'u1.user_name' ); + $this->addFields( array( + 'u1.user_name', + 'u1.user_id' + ) ); $this->addFieldsIf( 'u1.user_editcount', $fld_editcount ); $this->addFieldsIf( 'u1.user_registration', $fld_registration ); @@ -113,7 +114,6 @@ class ApiQueryAllUsers extends ApiQueryBase { $res = $this->select( __METHOD__ ); - $data = array (); $count = 0; $lastUserData = false; $lastUser = false; @@ -125,30 +125,25 @@ class ApiQueryAllUsers extends ApiQueryBase { // Otherwise, the group of the new row is appended to the last entry. // 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 ) { - $row = $db->fetchObject( $res ); + foreach ( $res as $row ) { $count++; - if ( !$row || $lastUser !== $row->user_name ) { + if ( $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 ) - { + + $lastUserData = null; + + if ( !$fit ) { $this->setContinueEnumParameter( 'from', $this->keyToTitle( $lastUserData['name'] ) ); break; } } - // No more rows left - if ( !$row ) - break; - if ( $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->user_name ) ); @@ -157,23 +152,31 @@ class ApiQueryAllUsers extends ApiQueryBase { // Record new user's data $lastUser = $row->user_name; - $lastUserData = array( 'name' => $lastUser ); - if ( $fld_blockinfo ) { + $lastUserData = array( + 'name' => $lastUser, + 'userid' => $row->user_id, + ); + if ( $fld_blockinfo && !is_null( $row->blocker_name ) ) { $lastUserData['blockedby'] = $row->blocker_name; $lastUserData['blockreason'] = $row->ipb_reason; } - if ( $fld_editcount ) + if ( $row->ipb_deleted ) { + $lastUserData['hidden'] = ''; + } + if ( $fld_editcount ) { $lastUserData['editcount'] = intval( $row->user_editcount ); - if ( $fld_registration ) + } + if ( $fld_registration ) { $lastUserData['registration'] = $row->user_registration ? wfTimestamp( TS_ISO_8601, $row->user_registration ) : ''; + } } if ( $sqlLimit == $count ) { // BUG! database contains group name that User::getAllGroups() does not return // TODO: should handle this more gracefully - ApiBase :: dieDebug( __METHOD__, + ApiBase::dieDebug( __METHOD__, 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function' ); } @@ -185,57 +188,63 @@ class ApiQueryAllUsers extends ApiQueryBase { } if ( is_array( $lastUserData ) ) { - $fit = $result->addValue( array( 'query', $this->getModuleName() ), - null, $lastUserData ); - if ( !$fit ) { - $this->setContinueEnumParameter( 'from', - $this->keyToTitle( $lastUserData['name'] ) ); - } + $fit = $result->addValue( array( 'query', $this->getModuleName() ), + null, $lastUserData ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'from', + $this->keyToTitle( $lastUserData['name'] ) ); + } } $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'u' ); } public function getCacheMode( $params ) { - return 'public'; + return 'anon-public-user-private'; } public function getAllowedParams() { - return array ( + return array( 'from' => null, + 'to' => null, 'prefix' => null, 'group' => array( - ApiBase :: PARAM_TYPE => User::getAllGroups() + ApiBase::PARAM_TYPE => User::getAllGroups() ), - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'blockinfo', 'groups', 'editcount', 'registration' ) ), - '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 + '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 ), 'witheditsonly' => false, ); } public function getParamDescription() { - return array ( - 'from' => 'The user name to start enumerating from.', - 'prefix' => 'Search for all page titles that begin with this value.', + return array( + 'from' => 'The user name to start enumerating from', + 'to' => 'The user name to stop enumerating at', + 'prefix' => 'Search for all users that begin with this value', '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.' ), - 'limit' => 'How many total user names to return.', + ' blockinfo - Adds the information about a current block on the user', + ' groups - Lists groups that the user is in', + ' editcount - Adds the edit count of the user', + ' registration - Adds the timestamp of when the user registered', + '`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', ); } @@ -245,12 +254,12 @@ class ApiQueryAllUsers extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=allusers&aufrom=Y', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllUsers.php 79562 2011-01-04 06:15:54Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllUsers.php 85354 2011-04-04 18:25:31Z demon $'; } } diff --git a/includes/api/ApiQueryAllimages.php b/includes/api/ApiQueryAllimages.php index 0a745516..a7825519 100644 --- a/includes/api/ApiQueryAllimages.php +++ b/includes/api/ApiQueryAllimages.php @@ -1,11 +1,11 @@ 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() { @@ -60,16 +62,18 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { } public function executeGenerator( $resultPageSet ) { - if ( $resultPageSet->isResolvingRedirects() ) + if ( $resultPageSet->isResolvingRedirects() ) { $this->dieUsage( 'Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator', 'params' ); + } $this->run( $resultPageSet ); } private function run( $resultPageSet = null ) { $repo = $this->mRepo; - if ( !$repo instanceof LocalRepo ) + if ( !$repo instanceof LocalRepo ) { $this->dieUsage( 'Local file repository does not support querying all images', 'unsupportedrepo' ); + } $db = $this->getDB(); @@ -78,15 +82,17 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { // 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'] ) ) + $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) ); + $this->addWhereRange( 'img_name', $dir, $from, $to ); + + if ( isset( $params['prefix'] ) ) $this->addWhere( 'img_name' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) ); - if ( isset ( $params['minsize'] ) ) { + if ( isset( $params['minsize'] ) ) { $this->addWhere( 'img_size>=' . intval( $params['minsize'] ) ); } - if ( isset ( $params['maxsize'] ) ) { + if ( isset( $params['maxsize'] ) ) { $this->addWhere( 'img_size<=' . intval( $params['maxsize'] ) ); } @@ -115,7 +121,7 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { $titles = array(); $count = 0; $result = $this->getResult(); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown @@ -136,7 +142,6 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { $titles[] = Title::makeTitle( NS_IMAGE, $row->img_name ); } } - $db->freeResult( $res ); if ( is_null( $resultPageSet ) ) { $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'img' ); @@ -148,55 +153,71 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { public function getAllowedParams() { return array ( 'from' => null, + 'to' => null, 'prefix' => null, - 'minsize' => array ( - ApiBase :: PARAM_TYPE => 'integer', + 'minsize' => array( + ApiBase::PARAM_TYPE => 'integer', ), - 'maxsize' => array ( - ApiBase :: PARAM_TYPE => 'integer', + 'maxsize' => array( + ApiBase::PARAM_TYPE => 'integer', ), - 'limit' => array ( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'ascending', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'ascending', + ApiBase::PARAM_TYPE => array( 'ascending', 'descending' ) ), 'sha1' => null, 'sha1base36' => null, - 'prop' => array ( - ApiBase :: PARAM_TYPE => ApiQueryImageInfo::getPropertyNames(), - ApiBase :: PARAM_DFLT => 'timestamp|url', - ApiBase :: PARAM_ISMULTI => true + 'prop' => array( + ApiBase::PARAM_TYPE => ApiQueryImageInfo::getPropertyNames(), + ApiBase::PARAM_DFLT => 'timestamp|url', + ApiBase::PARAM_ISMULTI => true ) ); } public function getParamDescription() { - return array ( - 'from' => 'The image title to start enumerating from.', - 'prefix' => 'Search for all image titles that begin with this value.', + return array( + 'from' => 'The image title to start enumerating from', + 'to' => 'The image title to stop enumerating at', + 'prefix' => 'Search for all image titles that begin with this value', 'dir' => 'The direction in which to list', 'minsize' => 'Limit to images with at least this many bytes', 'maxsize' => 'Limit to images with at most this many bytes', - 'limit' => 'How many total images to return.', - 'sha1' => 'SHA1 hash of image', + 'limit' => 'How many images in total to return', + 'sha1' => "SHA1 hash of image. Overrides {$this->getModulePrefix()}sha1base36", 'sha1base36' => 'SHA1 hash of image in base 36 (used in MediaWiki)', - 'prop' => 'Which properties to get', + 'prop' => array( + 'Which properties to get', + ' timestamp - Adds the timestamp when the image was upload', + ' user - Adds the username of the last uploader', + ' userid - Adds the user id of the last uploader', + ' comment - Adds the comment of the last upload', + ' url - Adds the URL of the image and its description page', + ' size - Adds the size of the image in bytes and its height and width', + ' dimensions - Alias of size', + ' sha1 - Adds the sha1 of the image', + ' mime - Adds the MIME of the image', + ' thumbmime - Adds the MIME of the tumbnail for the image', + ' archivename - Adds the file name of the archive version for non-latest versions', + ' bitdepth - Adds the bit depth of the version', + ), ); } 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' ), @@ -205,7 +226,7 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( + return array( 'Simple Use', ' Show a list of images starting at the letter "B"', ' api.php?action=query&list=allimages&aifrom=B', @@ -216,6 +237,6 @@ class ApiQueryAllimages extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllimages.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllimages.php 71838 2010-08-28 01:18:18Z reedy $'; } } diff --git a/includes/api/ApiQueryAllmessages.php b/includes/api/ApiQueryAllmessages.php index 7dd9d874..81ff255a 100644 --- a/includes/api/ApiQueryAllmessages.php +++ b/includes/api/ApiQueryAllmessages.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Dec 1, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -36,22 +37,23 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryAllmessages extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'am' ); + parent::__construct( $query, $moduleName, 'am' ); } public function execute() { $params = $this->extractRequestParams(); - if ( !is_null( $params['lang'] ) ) - { - global $wgLang; + global $wgLang; + + $oldLang = null; + if ( !is_null( $params['lang'] ) ) { + $oldLang = $wgLang; // Keep $wgLang for restore later $wgLang = Language::factory( $params['lang'] ); } - + $prop = array_flip( (array)$params['prop'] ); // Determine which messages should we print - $messages_target = array(); if ( in_array( '*', $params['messages'] ) ) { $message_names = array_keys( Language::getMessagesFor( 'en' ) ); sort( $message_names ); @@ -64,7 +66,8 @@ class ApiQueryAllmessages extends ApiQueryBase { 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 + // !== is used because filter can be at the beginning of the string + if ( strpos( $message, $params['filter'] ) !== false ) { $messages_filtered[] = $message; } } @@ -72,13 +75,18 @@ class ApiQueryAllmessages extends ApiQueryBase { } // Get all requested messages and print the result - $messages = array(); $skip = !is_null( $params['from'] ); + $useto = !is_null( $params['to'] ); $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( $useto && $message > $params['to'] ) { + break; + } if ( !$skip ) { $a = array( 'name' => $message ); @@ -89,35 +97,40 @@ class ApiQueryAllmessages extends ApiQueryBase { // Check if the parser is enabled: if ( $params['enableparser'] ) { $msg = wfMsgExt( $message, array( 'parsemag' ), $args ); - } else if ( $args ) { + } elseif ( $args ) { $msgString = wfMsgGetKey( $message, true, false, false ); $msg = wfMsgReplaceArgs( $msgString, $args ); } else { $msg = wfMsgGetKey( $message, true, false, false ); } - if ( wfEmptyMsg( $message, $msg ) ) + if ( wfEmptyMsg( $message, $msg ) ) { $a['missing'] = ''; - else { + } else { ApiResult::setContent( $a, $msg ); if ( isset( $prop['default'] ) ) { $default = wfMsgGetKey( $message, false, false, false ); if ( $default !== $msg ) { - if ( wfEmptyMsg( $message, $default ) ) + if ( wfEmptyMsg( $message, $default ) ) { $a['defaultmissing'] = ''; - else + } else { $a['default'] = $default; + } } } } $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $a ); if ( !$fit ) { - $this->setContinueEnumParameter( 'from', $name ); + $this->setContinueEnumParameter( 'from', $message ); break; } } } $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'message' ); + + if ( !is_null( $oldLang ) ) { + $wgLang = $oldLang; // Restore $oldLang + } } public function getCacheMode( $params ) { @@ -134,29 +147,30 @@ class ApiQueryAllmessages extends ApiQueryBase { } public function getAllowedParams() { - return array ( - 'messages' => array ( - ApiBase :: PARAM_DFLT => '*', - ApiBase :: PARAM_ISMULTI => true, + return array( + 'messages' => array( + ApiBase::PARAM_DFLT => '*', + ApiBase::PARAM_ISMULTI => true, ), 'prop' => array( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'default' ) ), 'enableparser' => false, 'args' => array( - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true ), 'filter' => array(), 'lang' => null, 'from' => null, + 'to' => null, ); } public function getParamDescription() { - return array ( + 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', @@ -165,21 +179,22 @@ class ApiQueryAllmessages extends ApiQueryBase { 'filter' => 'Return only messages that contain this string', 'lang' => 'Return messages in this language', 'from' => 'Return messages starting at this message', + 'to' => 'Return messages ending at this message', ); } public function getDescription() { - return 'Return messages from this site.'; + return 'Return messages from this site'; } protected function getExamples() { return array( 'api.php?action=query&meta=allmessages&amfilter=ipb-', 'api.php?action=query&meta=allmessages&ammessages=august|mainpage&amlang=de', - ); + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllmessages.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllmessages.php 73756 2010-09-25 17:08:23Z reedy $'; } } diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php index 37f22ee2..21f72916 100644 --- a/includes/api/ApiQueryAllpages.php +++ b/includes/api/ApiQueryAllpages.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 25, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryAllpages extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'ap' ); + parent::__construct( $query, $moduleName, 'ap' ); } public function execute() { @@ -48,8 +49,9 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { } public function executeGenerator( $resultPageSet ) { - if ( $resultPageSet->isResolvingRedirects() ) + if ( $resultPageSet->isResolvingRedirects() ) { $this->dieUsage( 'Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator', 'params' ); + } $this->run( $resultPageSet ); } @@ -61,22 +63,25 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { // Page filters $this->addTables( 'page' ); - - if ( $params['filterredir'] == 'redirects' ) + + if ( $params['filterredir'] == 'redirects' ) { $this->addWhereFld( 'page_is_redirect', 1 ); - else if ( $params['filterredir'] == 'nonredirects' ) + } elseif ( $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 ); + $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) ); + $this->addWhereRange( 'page_title', $dir, $from, $to ); - if ( isset ( $params['prefix'] ) ) + if ( isset( $params['prefix'] ) ) { $this->addWhere( 'page_title' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) ); + } if ( is_null( $resultPageSet ) ) { - $selectFields = array ( + $selectFields = array( 'page_namespace', 'page_title', 'page_id' @@ -87,40 +92,42 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { $this->addFields( $selectFields ); $forceNameTitleIndex = true; - if ( isset ( $params['minsize'] ) ) { + if ( isset( $params['minsize'] ) ) { $this->addWhere( 'page_len>=' . intval( $params['minsize'] ) ); $forceNameTitleIndex = false; } - if ( isset ( $params['maxsize'] ) ) { + if ( isset( $params['maxsize'] ) ) { $this->addWhere( 'page_len<=' . intval( $params['maxsize'] ) ); $forceNameTitleIndex = false; } // Page protection filtering - if ( !empty ( $params['prtype'] ) ) { + if ( !empty( $params['prtype'] ) ) { $this->addTables( 'page_restrictions' ); $this->addWhere( 'page_id=pr_page' ); $this->addWhere( 'pr_expiry>' . $db->addQuotes( $db->timestamp() ) ); $this->addWhereFld( 'pr_type', $params['prtype'] ); - if ( isset ( $params['prlevel'] ) ) { + if ( isset( $params['prlevel'] ) ) { // Remove the empty string and '*' from the prlevel array $prlevel = array_diff( $params['prlevel'], array( '', '*' ) ); - - if ( !empty( $prlevel ) ) + + if ( !empty( $prlevel ) ) { $this->addWhereFld( 'pr_level', $prlevel ); + } } - if ( $params['prfiltercascade'] == 'cascading' ) + if ( $params['prfiltercascade'] == 'cascading' ) { $this->addWhereFld( 'pr_cascade', 1 ); - else if ( $params['prfiltercascade'] == 'noncascading' ) + } elseif ( $params['prfiltercascade'] == 'noncascading' ) { $this->addWhereFld( 'pr_cascade', 0 ); + } $this->addOption( 'DISTINCT' ); $forceNameTitleIndex = false; - } else if ( isset ( $params['prlevel'] ) ) { + } elseif ( isset( $params['prlevel'] ) ) { $this->dieUsage( 'prlevel may not be used without prtype', 'params' ); } @@ -129,7 +136,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { $this->addJoinConds( array( 'langlinks' => array( 'LEFT JOIN', 'page_id=ll_from' ) ) ); $this->addWhere( 'll_from IS NULL' ); $forceNameTitleIndex = false; - } else if ( $params['filterlanglinks'] == 'withlanglinks' ) { + } elseif ( $params['filterlanglinks'] == 'withlanglinks' ) { $this->addTables( 'langlinks' ); $this->addWhere( 'page_id=ll_from' ); $this->addOption( 'STRAIGHT_JOIN' ); @@ -139,8 +146,9 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { $forceNameTitleIndex = false; } - if ( $forceNameTitleIndex ) + if ( $forceNameTitleIndex ) { $this->addOption( 'USE INDEX', 'name_title' ); + } $limit = $params['limit']; $this->addOption( 'LIMIT', $limit + 1 ); @@ -148,7 +156,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { $count = 0; $result = $this->getResult(); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown @@ -157,14 +165,14 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { } if ( is_null( $resultPageSet ) ) { - $title = Title :: makeTitle( $row->page_namespace, $row->page_title ); + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); $vals = array( 'pageid' => intval( $row->page_id ), 'ns' => intval( $title->getNamespace() ), - 'title' => $title->getPrefixedText() ); + 'title' => $title->getPrefixedText() + ); $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) ); break; } @@ -172,7 +180,6 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { $resultPageSet->processDbRow( $row ); } } - $db->freeResult( $res ); if ( is_null( $resultPageSet ) ) { $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'p' ); @@ -180,82 +187,85 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { } public function getAllowedParams() { - global $wgRestrictionTypes, $wgRestrictionLevels; + global $wgRestrictionLevels; - return array ( + return array( 'from' => null, + 'to' => null, 'prefix' => null, - 'namespace' => array ( - ApiBase :: PARAM_DFLT => 0, - ApiBase :: PARAM_TYPE => 'namespace', + 'namespace' => array( + ApiBase::PARAM_DFLT => 0, + ApiBase::PARAM_TYPE => 'namespace', ), - 'filterredir' => array ( - ApiBase :: PARAM_DFLT => 'all', - ApiBase :: PARAM_TYPE => array ( + 'filterredir' => array( + ApiBase::PARAM_DFLT => 'all', + ApiBase::PARAM_TYPE => array( 'all', 'redirects', 'nonredirects' ) ), - 'minsize' => array ( - ApiBase :: PARAM_TYPE => 'integer', + 'minsize' => array( + ApiBase::PARAM_TYPE => 'integer', ), - 'maxsize' => array ( - ApiBase :: PARAM_TYPE => 'integer', + 'maxsize' => array( + ApiBase::PARAM_TYPE => 'integer', ), - 'prtype' => array ( - ApiBase :: PARAM_TYPE => $wgRestrictionTypes, - ApiBase :: PARAM_ISMULTI => true + 'prtype' => array( + ApiBase::PARAM_TYPE => Title::getFilteredRestrictionTypes( true ), + ApiBase::PARAM_ISMULTI => true ), - 'prlevel' => array ( - ApiBase :: PARAM_TYPE => $wgRestrictionLevels, - ApiBase :: PARAM_ISMULTI => true + 'prlevel' => array( + ApiBase::PARAM_TYPE => $wgRestrictionLevels, + ApiBase::PARAM_ISMULTI => true ), - 'prfiltercascade' => array ( - ApiBase :: PARAM_DFLT => 'all', - ApiBase :: PARAM_TYPE => array ( + 'prfiltercascade' => array( + ApiBase::PARAM_DFLT => 'all', + ApiBase::PARAM_TYPE => array( 'cascading', 'noncascading', 'all' ), ), - '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 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'ascending', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'ascending', + ApiBase::PARAM_TYPE => array( 'ascending', 'descending' ) ), 'filterlanglinks' => array( - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_TYPE => array( 'withlanglinks', 'withoutlanglinks', 'all' ), - ApiBase :: PARAM_DFLT => 'all' + ApiBase::PARAM_DFLT => 'all' ) ); } public function getParamDescription() { - return array ( - 'from' => 'The page title to start enumerating from.', - 'prefix' => 'Search for all page titles that begin with this value.', - 'namespace' => 'The namespace to enumerate.', - 'filterredir' => 'Which pages to list.', + $p = $this->getModulePrefix(); + return array( + 'from' => 'The page title to start enumerating from', + 'to' => 'The page title to stop enumerating at', + 'prefix' => 'Search for all page titles that begin with this value', + 'namespace' => 'The namespace to enumerate', + 'filterredir' => 'Which pages to list', 'dir' => 'The direction in which to list', 'minsize' => 'Limit to pages with at least this many bytes', 'maxsize' => 'Limit to pages with at most this many bytes', 'prtype' => 'Limit to protected pages only', - 'prlevel' => 'The protection level (must be used with apprtype= parameter)', - 'prfiltercascade' => 'Filter protections based on cascadingness (ignored when apprtype isn\'t set)', + 'prlevel' => "The protection level (must be used with {$p}prtype= parameter)", + 'prfiltercascade' => "Filter protections based on cascadingness (ignored when {$p}prtype isn't set)", 'filterlanglinks' => 'Filter based on whether a page has langlinks', 'limit' => 'How many total pages to return.' ); @@ -264,7 +274,7 @@ 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' ), @@ -273,7 +283,7 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( + return array( 'Simple Use', ' Show a list of pages starting at the letter "B"', ' api.php?action=query&list=allpages&apfrom=B', @@ -286,6 +296,6 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllpages.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllpages.php 85354 2011-04-04 18:25:31Z demon $'; } } diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php index 648da069..b412d2d6 100644 --- a/includes/api/ApiQueryBacklinks.php +++ b/includes/api/ApiQueryBacklinks.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 16, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -38,23 +39,23 @@ if ( !defined( 'MEDIAWIKI' ) ) { */ class ApiQueryBacklinks extends ApiQueryGeneratorBase { - private $params, $rootTitle, $contRedirs, $contLevel, $contTitle, $contID, $redirID, $redirect; + private $params, $rootTitle, $contID, $redirID, $redirect; private $bl_ns, $bl_from, $bl_table, $bl_code, $bl_title, $bl_sort, $bl_fields, $hasNS; private $pageMap, $resultArr; // output element name, database column field prefix, database table - private $backlinksSettings = array ( - 'backlinks' => array ( + private $backlinksSettings = array( + 'backlinks' => array( 'code' => 'bl', 'prefix' => 'pl', 'linktbl' => 'pagelinks' ), - 'embeddedin' => array ( + 'embeddedin' => array( 'code' => 'ei', 'prefix' => 'tl', 'linktbl' => 'templatelinks' ), - 'imageusage' => array ( + 'imageusage' => array( 'code' => 'iu', 'prefix' => 'il', 'linktbl' => 'imagelinks' @@ -62,27 +63,29 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { ); public function __construct( $query, $moduleName ) { - extract( $this->backlinksSettings[$moduleName] ); + $settings = $this->backlinksSettings[$moduleName]; + $prefix = $settings['prefix']; + $code = $settings['code']; $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_table = $settings['linktbl']; $this->bl_code = $code; $this->hasNS = $moduleName !== 'imageusage'; if ( $this->hasNS ) { $this->bl_title = $prefix . '_title'; $this->bl_sort = "{$this->bl_ns}, {$this->bl_title}, {$this->bl_from}"; - $this->bl_fields = array ( + $this->bl_fields = array( $this->bl_ns, $this->bl_title ); } else { $this->bl_title = $prefix . '_to'; $this->bl_sort = "{$this->bl_title}, {$this->bl_from}"; - $this->bl_fields = array ( + $this->bl_fields = array( $this->bl_title ); } @@ -106,29 +109,32 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { * AND pl_title='Foo' AND pl_namespace=0 * LIMIT 11 ORDER BY pl_from */ - $db = $this->getDB(); $this->addTables( array( $this->bl_table, 'page' ) ); $this->addWhere( "{$this->bl_from}=page_id" ); - if ( is_null( $resultPageSet ) ) + if ( is_null( $resultPageSet ) ) { $this->addFields( array( 'page_id', 'page_title', 'page_namespace' ) ); - else + } else { $this->addFields( $resultPageSet->getPageTableFields() ); + } $this->addFields( 'page_is_redirect' ); $this->addWhereFld( $this->bl_title, $this->rootTitle->getDBkey() ); - if ( $this->hasNS ) + if ( $this->hasNS ) { $this->addWhereFld( $this->bl_ns, $this->rootTitle->getNamespace() ); + } $this->addWhereFld( 'page_namespace', $this->params['namespace'] ); - if ( !is_null( $this->contID ) ) + if ( !is_null( $this->contID ) ) { $this->addWhere( "{$this->bl_from}>={$this->contID}" ); + } - if ( $this->params['filterredir'] == 'redirects' ) + if ( $this->params['filterredir'] == 'redirects' ) { $this->addWhereFld( 'page_is_redirect', 1 ); - else if ( $this->params['filterredir'] == 'nonredirects' && !$this->redirect ) + } elseif ( $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 ); @@ -145,45 +151,48 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { $this->addTables( array( 'page', $this->bl_table ) ); $this->addWhere( "{$this->bl_from}=page_id" ); - if ( is_null( $resultPageSet ) ) + if ( is_null( $resultPageSet ) ) { $this->addFields( array( 'page_id', 'page_title', 'page_namespace', 'page_is_redirect' ) ); - else + } else { $this->addFields( $resultPageSet->getPageTableFields() ); + } $this->addFields( $this->bl_title ); - if ( $this->hasNS ) + 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 ) + foreach ( $this->redirTitles as $t ) { $titleWhere[] = "{$this->bl_title} = " . $db->addQuotes( $t->getDBkey() ) . - ( $this->hasNS ? " AND {$this->bl_ns} = '{$t->getNamespace()}'" : "" ); + ( $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 ) ) - { + if ( !is_null( $this->redirID ) ) { $first = $this->redirTitles[0]; $title = $db->strencode( $first->getDBkey() ); $ns = $first->getNamespace(); $from = $this->redirID; - if ( $this->hasNS ) + 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 + } else { $this->addWhere( "{$this->bl_title} > '$title' OR " . "({$this->bl_title} = '$title' AND " . "{$this->bl_from} >= $from)" ); - + } } - if ( $this->params['filterredir'] == 'redirects' ) + if ( $this->params['filterredir'] == 'redirects' ) { $this->addWhereFld( 'page_is_redirect', 1 ); - else if ( $this->params['filterredir'] == 'nonredirects' ) + } elseif ( $this->params['filterredir'] == 'nonredirects' ) { $this->addWhereFld( 'page_is_redirect', 0 ); + } $this->addOption( 'LIMIT', $this->params['limit'] + 1 ); $this->addOption( 'ORDER BY', $this->bl_sort ); @@ -197,20 +206,19 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { $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->getResult()->setParsedLimit( $this->getModuleName(), $this->params['limit'] ); } $this->processContinue(); $this->prepareFirstQuery( $resultPageSet ); - $db = $this->getDB(); $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 ) ) { + foreach ( $res as $row ) { 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 @@ -218,93 +226,89 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { break; } - if ( is_null( $resultPageSet ) ) + if ( is_null( $resultPageSet ) ) { $this->extractRowInfo( $row ); - else - { + } else { $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id; - if ( $row->page_is_redirect ) + if ( $row->page_is_redirect ) { $this->redirTitles[] = Title::makeTitle( $row->page_namespace, $row->page_title ); + } $resultPageSet->processDbRow( $row ); } } - $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' ); $count = 0; - while ( $row = $db->fetchObject( $res ) ) - { - if ( ++$count > $this->params['limit'] ) - { + foreach ( $res as $row ) { + 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 } ]; - else - $parentID = $this->pageMap[NS_IMAGE][$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 ); break; } - if ( is_null( $resultPageSet ) ) + if ( is_null( $resultPageSet ) ) { $this->extractRedirRowInfo( $row ); - else + } else { $resultPageSet->processDbRow( $row ); + } } - $db->freeResult( $res ); } 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 ) - { + 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 ) - { + if ( !$fit ) { $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 ) - { + if ( !$fit ) { $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 ) + } + 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 ) ) + if ( !is_null( $this->continueStr ) ) { $this->setContinueEnumParameter( 'continue', $this->continueStr ); + } } private function extractRowInfo( $row ) { @@ -312,8 +316,7 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { $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 ) - { + if ( $row->page_is_redirect ) { $a['redirect'] = ''; $this->redirTitles[] = $t; } @@ -321,12 +324,12 @@ 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 ) + if ( $row->page_is_redirect ) { $a['redirect'] = ''; + } $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 @@ -335,24 +338,23 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { } protected function processContinue() { - if ( !is_null( $this->params['continue'] ) ) + if ( !is_null( $this->params['continue'] ) ) { $this->parseContinueParam(); - else { - if ( $this->params['title'] !== "" ) { + } else { + if ( $this->params['title'] !== '' ) { $title = Title::newFromText( $this->params['title'] ); if ( !$title ) { $this->dieUsageMsg( array( 'invalidtitle', $this->params['title'] ) ); } else { $this->rootTitle = $title; } - } else { - $this->dieUsageMsg( array( 'missingparam', 'title' ) ); } } // only image titles are allowed for the root in imageinfo mode - if ( !$this->hasNS && $this->rootTitle->getNamespace() !== NS_FILE ) + 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() { @@ -366,23 +368,27 @@ 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' ) + 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->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" ); + 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" ); + 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' ) + + if ( $redirID === 0 && @$continueList[3] !== '0' ) { // This one isn't required return; + } $this->redirID = $redirID; } @@ -398,88 +404,92 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { } public function getAllowedParams() { - $retval = array ( - 'title' => null, + $retval = array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'continue' => null, - 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace' + 'namespace' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace' ), 'filterredir' => array( - ApiBase :: PARAM_DFLT => 'all', - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'all', + ApiBase::PARAM_TYPE => array( 'all', 'redirects', 'nonredirects' ) ), - '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 + '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 ) ); - if ( $this->getModuleName() == 'embeddedin' ) + if ( $this->getModuleName() == 'embeddedin' ) { return $retval; + } $retval['redirect'] = false; return $retval; } public function getParamDescription() { - $retval = array ( - 'title' => 'Title to search.', - 'continue' => 'When more results are available, use this to continue.', - 'namespace' => 'The namespace to enumerate.', + $retval = array( + 'title' => 'Title to search', + 'continue' => 'When more results are available, use this to continue', + 'namespace' => 'The namespace to enumerate', ); - if ( $this->getModuleName() != 'embeddedin' ) + 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.', '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.' + 'limit' => 'How many total pages to return' ) ); } public function getDescription() { switch ( $this->getModuleName() ) { - case 'backlinks' : + case 'backlinks': return 'Find all pages that link to the given page'; - case 'embeddedin' : + case 'embeddedin': return 'Find all pages that embed (transclude) the given title'; - case 'imageusage' : + case 'imageusage': return 'Find all pages that use the given image title.'; - default : - ApiBase :: dieDebug( __METHOD__, 'Unknown module name' ); + default: + 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 ( - 'backlinks' => array ( - "api.php?action=query&list=backlinks&bltitle=Main%20Page", - "api.php?action=query&generator=backlinks&gbltitle=Main%20Page&prop=info" + static $examples = array( + 'backlinks' => array( + 'api.php?action=query&list=backlinks&bltitle=Main%20Page', + 'api.php?action=query&generator=backlinks&gbltitle=Main%20Page&prop=info' ), - 'embeddedin' => array ( - "api.php?action=query&list=embeddedin&eititle=Template:Stub", - "api.php?action=query&generator=embeddedin&geititle=Template:Stub&prop=info" + 'embeddedin' => array( + 'api.php?action=query&list=embeddedin&eititle=Template:Stub', + 'api.php?action=query&generator=embeddedin&geititle=Template:Stub&prop=info' ), - 'imageusage' => array ( - "api.php?action=query&list=imageusage&iutitle=File:Albert%20Einstein%20Head.jpg", - "api.php?action=query&generator=imageusage&giutitle=File:Albert%20Einstein%20Head.jpg&prop=info" + 'imageusage' => array( + 'api.php?action=query&list=imageusage&iutitle=File:Albert%20Einstein%20Head.jpg', + 'api.php?action=query&generator=imageusage&giutitle=File:Albert%20Einstein%20Head.jpg&prop=info' ) ); @@ -487,6 +497,6 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryBacklinks.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryBacklinks.php 75921 2010-11-03 12:49:21Z demon $'; } } diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php index 893da566..61a5b4c8 100644 --- a/includes/api/ApiQueryBase.php +++ b/includes/api/ApiQueryBase.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 7, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -39,18 +40,19 @@ 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( ApiBase $query, $moduleName, $paramPrefix = '' ) { + parent::__construct( $query->getMain(), $moduleName, $paramPrefix ); $this->mQueryModule = $query; $this->mDb = null; $this->resetQueryParams(); } /** - * Get the cache mode for the data generated by this module. Override this - * in the module subclass. + * Get the cache mode for the data generated by this module. Override + * this in the module subclass. For possible return values and other + * details about cache modes, see ApiMain::setCacheMode() * - * Public caching will only be allowed if *all* the modules that supply + * Public caching will only be allowed if *all* the modules that supply * data for a given request return a cache mode of public. */ public function getCacheMode( $params ) { @@ -61,11 +63,11 @@ abstract class ApiQueryBase extends ApiBase { * Blank the internal arrays with query parameters */ protected function resetQueryParams() { - $this->tables = array (); - $this->where = array (); - $this->fields = array (); - $this->options = array (); - $this->join_conds = array (); + $this->tables = array(); + $this->where = array(); + $this->fields = array(); + $this->options = array(); + $this->join_conds = array(); } /** @@ -76,16 +78,18 @@ abstract class ApiQueryBase extends ApiBase { */ protected function addTables( $tables, $alias = null ) { if ( is_array( $tables ) ) { - if ( !is_null( $alias ) ) - ApiBase :: dieDebug( __METHOD__, 'Multiple table aliases not supported' ); + if ( !is_null( $alias ) ) { + ApiBase::dieDebug( __METHOD__, 'Multiple table aliases not supported' ); + } $this->tables = array_merge( $this->tables, $tables ); } else { - if ( !is_null( $alias ) ) + if ( !is_null( $alias ) ) { $tables = $this->getAliasedName( $tables, $alias ); + } $this->tables[] = $tables; } } - + /** * Get the SQL for a table name with alias * @param $table string Table name @@ -95,7 +99,7 @@ abstract class ApiQueryBase extends ApiBase { protected function getAliasedName( $table, $alias ) { return $this->getDB()->tableName( $table ) . ' ' . $alias; } - + /** * Add a set of JOIN conditions to the internal array * @@ -106,8 +110,9 @@ abstract class ApiQueryBase extends ApiBase { * @param $join_conds array JOIN conditions */ protected function addJoinConds( $join_conds ) { - if ( !is_array( $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 ); } @@ -116,10 +121,11 @@ abstract class ApiQueryBase extends ApiBase { * @param $value mixed Field name or array of field names */ protected function addFields( $value ) { - if ( is_array( $value ) ) + if ( is_array( $value ) ) { $this->fields = array_merge( $this->fields, $value ); - else + } else { $this->fields[] = $value; + } } /** @@ -151,17 +157,18 @@ abstract class ApiQueryBase extends ApiBase { if ( is_array( $value ) ) { // Sanity check: don't insert empty arrays, // Database::makeList() chokes on them - if ( count( $value ) ) + if ( count( $value ) ) { $this->where = array_merge( $this->where, $value ); - } - else + } + } else { $this->where[] = $value; + } } /** * Same as addWhere(), but add the WHERE clauses only if a condition is met * @param $value mixed See addWhere() - * @param $condition boolIf false, do nothing + * @param $condition bool If false, do nothing * @return bool $condition */ protected function addWhereIf( $value, $condition ) { @@ -178,10 +185,11 @@ abstract class ApiQueryBase extends ApiBase { * @param $value string Value; ignored if null or empty array; */ protected function addWhereFld( $field, $value ) { - // Use count() to its full documented capabilities to simultaneously + // Use count() to its full documented capabilities to simultaneously // test for null, empty array or empty countable object - if ( count( $value ) ) + if ( count( $value ) ) { $this->where[$field] = $value; + } } /** @@ -202,18 +210,21 @@ abstract class ApiQueryBase extends ApiBase { $before = ( $isDirNewer ? '<=' : '>=' ); $db = $this->getDB(); - if ( !is_null( $start ) ) + if ( !is_null( $start ) ) { $this->addWhere( $field . $after . $db->addQuotes( $start ) ); + } - if ( !is_null( $end ) ) + if ( !is_null( $end ) ) { $this->addWhere( $field . $before . $db->addQuotes( $end ) ); + } if ( $sort ) { $order = $field . ( $isDirNewer ? '' : ' DESC' ); - if ( !isset( $this->options['ORDER BY'] ) ) + if ( !isset( $this->options['ORDER BY'] ) ) { $this->addOption( 'ORDER BY', $order ); - else + } else { $this->addOption( 'ORDER BY', $this->options['ORDER BY'] . ', ' . $order ); + } } } @@ -224,24 +235,34 @@ abstract class ApiQueryBase extends ApiBase { * @param $value string Option value */ protected function addOption( $name, $value = null ) { - if ( is_null( $value ) ) + if ( is_null( $value ) ) { $this->options[] = $name; - else + } else { $this->options[$name] = $value; + } } /** * Execute a SELECT query based on the values in the internal arrays * @param $method string Function the query should be attributed to. * You should usually use __METHOD__ here + * @param $extraQuery array Query data to add but not store in the object + * Format is array( 'tables' => ..., 'fields' => ..., 'where' => ..., 'options' => ..., 'join_conds' => ... ) * @return ResultWrapper */ - protected function select( $method ) { + protected function select( $method, $extraQuery = array() ) { + + $tables = array_merge( $this->tables, isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : array() ); + $fields = array_merge( $this->fields, isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : array() ); + $where = array_merge( $this->where, isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : array() ); + $options = array_merge( $this->options, isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : array() ); + $join_conds = array_merge( $this->join_conds, isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : array() ); + // 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( $tables, $fields, $where, $method, $options, $join_conds ); $this->profileDBOut(); return $res; @@ -259,8 +280,9 @@ abstract class ApiQueryBase extends ApiBase { $this->profileDBOut(); global $wgAPIMaxDBRows; - if ( $rowcount > $wgAPIMaxDBRows ) + if ( $rowcount > $wgAPIMaxDBRows ) { return false; + } return true; } @@ -295,7 +317,7 @@ abstract class ApiQueryBase extends ApiBase { /** * Add a sub-element under the page element with the given page ID * @param $pageId int Page ID - * @param $data array Data array à la ApiResult + * @param $data array Data array à la ApiResult * @return bool Whether the element fit in the result */ protected function addPageSubItems( $pageId, $data ) { @@ -305,23 +327,25 @@ abstract class ApiQueryBase extends ApiBase { $this->getModuleName(), $data ); } - + /** * Same as addPageSubItems(), but one element of $data at a time * @param $pageId int Page ID - * @param $data array Data array à la ApiResult + * @param $item array Data array à la ApiResult * @param $elemname string XML element name. If null, getModuleName() * is used * @return bool Whether the element fit in the result */ protected function addPageSubItem( $pageId, $item, $elemname = null ) { - if ( is_null( $elemname ) ) + if ( is_null( $elemname ) ) { $elemname = $this->getModulePrefix(); + } $result = $this->getResult(); $fit = $result->addValue( array( 'query', 'pages', $pageId, $this->getModuleName() ), null, $item ); - if ( !$fit ) + if ( !$fit ) { return false; + } $result->setIndexedTagName_internal( array( 'query', 'pages', $pageId, $this->getModuleName() ), $elemname ); return true; @@ -345,8 +369,10 @@ abstract class ApiQueryBase extends ApiBase { * @return Database */ protected function getDB() { - if ( is_null( $this->mDb ) ) - $this->mDb = $this->getQuery()->getDB(); + if ( is_null( $this->mDb ) ) { + $apiQuery = $this->getQuery(); + $this->mDb = $apiQuery->getDB(); + } return $this->mDb; } @@ -356,7 +382,7 @@ abstract class ApiQueryBase extends ApiBase { * @param $name string Name to assign to the database connection * @param $db int One of the DB_* constants * @param $groups array Query groups - * @return Database + * @return Database */ public function selectNamedDB( $name, $db, $groups ) { $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups ); @@ -377,11 +403,13 @@ abstract class ApiQueryBase extends ApiBase { */ public function titleToKey( $title ) { // Don't throw an error if we got an empty string - if ( trim( $title ) == '' ) + if ( trim( $title ) == '' ) { return ''; + } $t = Title::newFromText( $title ); - if ( !$t ) + if ( !$t ) { $this->dieUsageMsg( array( 'invalidtitle', $title ) ); + } return $t->getPrefixedDbKey(); } @@ -392,15 +420,17 @@ abstract class ApiQueryBase extends ApiBase { */ public function keyToTitle( $key ) { // Don't throw an error if we got an empty string - if ( trim( $key ) == '' ) + if ( trim( $key ) == '' ) { return ''; + } $t = Title::newFromDbKey( $key ); // This really shouldn't happen but we gotta check anyway - if ( !$t ) + if ( !$t ) { $this->dieUsageMsg( array( 'invalidtitle', $key ) ); + } return $t->getPrefixedText(); } - + /** * An alternative to titleToKey() that doesn't trim trailing spaces * @param $titlePart string Title part with spaces @@ -409,7 +439,7 @@ abstract class ApiQueryBase extends ApiBase { public function titlePartToKey( $titlePart ) { return substr( $this->titleToKey( $titlePart . 'x' ), 0, - 1 ); } - + /** * An alternative to keyToTitle() that doesn't trim trailing spaces * @param $keyPart string Key part with spaces @@ -418,7 +448,37 @@ abstract class ApiQueryBase extends ApiBase { public function keyPartToTitle( $keyPart ) { return substr( $this->keyToTitle( $keyPart . 'x' ), 0, - 1 ); } - + + /** + * Filters hidden users (where the user doesn't have the right to view them) + * Also adds relevant block information + * + * @param bool $showBlockInfo + * @return void + */ + public function showHiddenUsersAddBlockInfo( $showBlockInfo ) { + global $wgUser; + $userCanViewHiddenUsers = $wgUser->isAllowed( 'hideuser' ); + + if ( $showBlockInfo || !$userCanViewHiddenUsers ) { + $this->addTables( 'ipblocks' ); + $this->addJoinConds( array( + 'ipblocks' => array( 'LEFT JOIN', 'ipb_user=user_id' ), + ) ); + + $this->addFields( 'ipb_deleted' ); + + if ( $showBlockInfo ) { + $this->addFields( array( 'ipb_reason', 'ipb_by_text', 'ipb_expiry' ) ); + } + + // Don't show hidden names + if ( !$userCanViewHiddenUsers ) { + $this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' ); + } + } + } + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'invalidtitle', 'title' ), @@ -431,7 +491,7 @@ abstract class ApiQueryBase extends ApiBase { * @return string */ public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiQueryBase.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryBase.php 85435 2011-04-05 14:00:08Z demon $'; } } @@ -443,7 +503,7 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { private $mIsGenerator; public function __construct( $query, $moduleName, $paramPrefix = '' ) { - parent :: __construct( $query, $moduleName, $paramPrefix ); + parent::__construct( $query, $moduleName, $paramPrefix ); $this->mIsGenerator = false; } @@ -457,14 +517,15 @@ abstract class ApiQueryGeneratorBase extends ApiQueryBase { /** * Overrides base class to prepend 'g' to every generator parameter - * @param $paramNames string Parameter name + * @param $paramName string Parameter name * @return string Prefixed parameter name */ public function encodeParamName( $paramName ) { - if ( $this->mIsGenerator ) - return 'g' . parent :: encodeParamName( $paramName ); - else - return parent :: encodeParamName( $paramName ); + if ( $this->mIsGenerator ) { + return 'g' . parent::encodeParamName( $paramName ); + } else { + return parent::encodeParamName( $paramName ); + } } /** diff --git a/includes/api/ApiQueryBlocks.php b/includes/api/ApiQueryBlocks.php index 8b321044..4edda645 100644 --- a/includes/api/ApiQueryBlocks.php +++ b/includes/api/ApiQueryBlocks.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Sep 10, 2007 + * + * 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -34,19 +35,20 @@ if ( !defined( 'MEDIAWIKI' ) ) { * @ingroup API */ class ApiQueryBlocks extends ApiQueryBase { - + var $users; public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'bk' ); + parent::__construct( $query, $moduleName, 'bk' ); } public function execute() { global $wgUser; $params = $this->extractRequestParams(); - if ( isset( $params['users'] ) && isset( $params['ip'] ) ) + 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'] ); @@ -59,60 +61,62 @@ class ApiQueryBlocks extends ApiQueryBase { $fld_flags = isset( $prop['flags'] ); $result = $this->getResult(); - $pageSet = $this->getPageSet(); - $titles = $pageSet->getTitles(); - $data = array(); $this->addTables( 'ipblocks' ); $this->addFields( 'ipb_auto' ); - if ( $fld_id ) + if ( $fld_id ) { $this->addFields( 'ipb_id' ); - if ( $fld_user ) + } + if ( $fld_user ) { $this->addFields( array( 'ipb_address', 'ipb_user' ) ); - if ( $fld_by ) - { + } + if ( $fld_by ) { $this->addTables( 'user' ); $this->addFields( array( 'ipb_by', 'user_name' ) ); $this->addWhere( 'user_id = ipb_by' ); } - if ( $fld_timestamp ) + if ( $fld_timestamp ) { $this->addFields( 'ipb_timestamp' ); - if ( $fld_expiry ) + } + if ( $fld_expiry ) { $this->addFields( 'ipb_expiry' ); - if ( $fld_reason ) + } + if ( $fld_reason ) { $this->addFields( 'ipb_reason' ); - if ( $fld_range ) + } + if ( $fld_range ) { $this->addFields( array( 'ipb_range_start', 'ipb_range_end' ) ); - if ( $fld_flags ) + } + 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'] ) ) + if ( isset( $params['ids'] ) ) { $this->addWhereFld( 'ipb_id', $params['ids'] ); - if ( isset( $params['users'] ) ) - { - foreach ( (array)$params['users'] as $u ) + } + if ( isset( $params['users'] ) ) { + 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 ) - { + if ( $ip && $range ) { // We got a CIDR range if ( $range < 16 ) $this->dieUsage( 'CIDR ranges broader than /16 are not accepted', 'cidrtoobroad' ); $lower = wfBaseConvert( $ip, 10, 16, 8, false ); $upper = wfBaseConvert( $ip + pow( 2, 32 - $range ) - 1, 10, 16, 8, false ); - } - else + } else { $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() ), @@ -121,147 +125,169 @@ class ApiQueryBlocks extends ApiQueryBase { 'ipb_auto' => 0 ) ); } - if ( !$wgUser->isAllowed( 'hideuser' ) ) + + 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__ ); $count = 0; - while ( $row = $res->fetchObject() ) - { - if ( ++$count > $params['limit'] ) - { + foreach ( $res as $row ) { + if ( ++$count > $params['limit'] ) { // We've had enough $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 ) + } + if ( $fld_timestamp ) { $block['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ); - if ( $fld_expiry ) + } + if ( $fld_expiry ) { $block['expiry'] = Block::decodeExpiry( $row->ipb_expiry, TS_ISO_8601 ); - if ( $fld_reason ) + } + if ( $fld_reason ) { $block['reason'] = $row->ipb_reason; - if ( $fld_range && !$row->ipb_auto ) - { + } + if ( $fld_range && !$row->ipb_auto ) { $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 ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ) ); break; } } $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'block' ); } - - protected function prepareUsername( $user ) - { - if ( !$user ) + + protected function prepareUsername( $user ) { + if ( !$user ) { $this->dieUsage( 'User parameter may not be empty', 'param_user' ); + } $name = User::isIP( $user ) ? $user : User::getCanonicalName( $user, 'valid' ); - if ( $name === false ) + if ( $name === false ) { $this->dieUsage( "User name {$user} is not valid", 'param_user' ); + } $this->usernames[] = $name; } public function getAllowedParams() { - return array ( + return array( 'start' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'end' => array( - ApiBase :: PARAM_TYPE => 'timestamp', + ApiBase::PARAM_TYPE => 'timestamp', ), 'dir' => array( - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_TYPE => array( 'newer', 'older' ), - ApiBase :: PARAM_DFLT => 'older' + ApiBase::PARAM_DFLT => 'older' ), 'ids' => array( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_ISMULTI => true ), 'users' => array( - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true ), 'ip' => null, 'limit' => array( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 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 => 'id|user|by|timestamp|expiry|reason|flags', - ApiBase :: PARAM_TYPE => array( - 'id', - 'user', - 'by', - 'timestamp', - 'expiry', - 'reason', - 'range', - 'flags' - ), - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_DFLT => 'id|user|by|timestamp|expiry|reason|flags', + ApiBase::PARAM_TYPE => array( + 'id', + 'user', + 'by', + 'timestamp', + 'expiry', + 'reason', + 'range', + 'flags' + ), + ApiBase::PARAM_ISMULTI => true ) ); } public function getParamDescription() { - return array ( + return array( 'start' => 'The timestamp to start enumerating from', 'end' => 'The timestamp to stop enumerating at', 'dir' => 'The direction in which to enumerate', 'ids' => 'Pipe-separated list of block IDs to list (optional)', 'users' => 'Pipe-separated list of users to search for (optional)', 'ip' => array( 'Get all blocks applying to this IP or CIDR range, including range blocks.', - 'Cannot be used together with bkusers. CIDR ranges broader than /16 are not accepted.' ), + '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', + 'prop' => array( + 'Which properties to get', + ' id - Adds the id of the block', + ' user - Adds the username of the blocked user', + ' by - Adds the username of the blocking admin', + ' timestamp - Adds the timestamp of when the block was given', + ' expiry - Adds the timestamp of when the block expires', + ' reason - Adds the reason given for the block', + ' range - Adds the range of IPs affected by the block', + ' flags - Tags the ban with (autoblock, anononly, etc)', + ), ); } public function getDescription() { - return 'List all blocked users and IP addresses.'; + 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' ), @@ -272,12 +298,13 @@ class ApiQueryBlocks extends ApiQueryBase { } protected function getExamples() { - return array ( 'api.php?action=query&list=blocks', - 'api.php?action=query&list=blocks&bkusers=Alice|Bob' + return array( + 'api.php?action=query&list=blocks', + 'api.php?action=query&list=blocks&bkusers=Alice|Bob' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryBlocks.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryBlocks.php 73858 2010-09-28 01:21:15Z reedy $'; } -} \ No newline at end of file +} diff --git a/includes/api/ApiQueryCategories.php b/includes/api/ApiQueryCategories.php index 03135052..b2769dc2 100644 --- a/includes/api/ApiQueryCategories.php +++ b/includes/api/ApiQueryCategories.php @@ -1,11 +1,10 @@ @gmail.com + * Created on May 13, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryCategories extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'cl' ); + parent::__construct( $query, $moduleName, 'cl' ); } public function execute() { @@ -52,52 +53,55 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { } private function run( $resultPageSet = null ) { - - if ( $this->getPageSet()->getGoodTitleCount() == 0 ) + if ( $this->getPageSet()->getGoodTitleCount() == 0 ) { return; // nothing to do + } $params = $this->extractRequestParams(); $prop = array_flip( (array)$params['prop'] ); $show = array_flip( (array)$params['show'] ); - $this->addFields( array ( + $this->addFields( array( 'cl_from', 'cl_to' ) ); - $this->addFieldsIf( 'cl_sortkey', isset( $prop['sortkey'] ) ); + $this->addFieldsIf( array( 'cl_sortkey', 'cl_sortkey_prefix' ), 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'] ) ) - { + 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 ) + if ( !$title || $title->getNamespace() != NS_CATEGORY ) { $this->setWarning( "``$cat'' is not a category" ); - else + } else { $cats[] = $title->getDBkey(); + } } $this->addWhereFld( 'cl_to', $cats ); } if ( !is_null( $params['continue'] ) ) { $cont = explode( '|', $params['continue'] ); - if ( count( $cont ) != 2 ) + 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')" ); + $this->addWhere( + "cl_from > $clfrom OR " . + "(cl_from = $clfrom AND " . + "cl_to >= '$clto')" + ); } - 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' ); @@ -111,26 +115,26 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { 'pp_page=page_id', 'pp_propname' => 'hiddencat' ) ) ) ); - if ( isset( $show['hidden'] ) ) + if ( isset( $show['hidden'] ) ) { $this->addWhere( array( 'pp_propname IS NOT NULL' ) ); - else if ( isset( $show['!hidden'] ) ) + } elseif ( 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 ) + if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) { $this->addOption( 'ORDER BY', 'cl_to' ); - else + } else { $this->addOption( 'ORDER BY', "cl_from, cl_to" ); + } - $db = $this->getDB(); $res = $this->select( __METHOD__ ); + $count = 0; if ( is_null( $resultPageSet ) ) { - - $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -139,28 +143,30 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { break; } - $title = Title :: makeTitle( NS_CATEGORY, $row->cl_to ); + $title = Title::makeTitle( NS_CATEGORY, $row->cl_to ); $vals = array(); - ApiQueryBase :: addTitleInfo( $vals, $title ); - if ( isset( $prop['sortkey'] ) ) - $vals['sortkey'] = $row->cl_sortkey; - if ( isset( $prop['timestamp'] ) ) + ApiQueryBase::addTitleInfo( $vals, $title ); + if ( isset( $prop['sortkey'] ) ) { + $vals['sortkey'] = bin2hex( $row->cl_sortkey ); + $vals['sortkeyprefix'] = $row->cl_sortkey_prefix; + } + if ( isset( $prop['timestamp'] ) ) { $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->cl_timestamp ); - if ( isset( $prop['hidden'] ) && !is_null( $row->pp_propname ) ) + } + if ( isset( $prop['hidden'] ) && !is_null( $row->pp_propname ) ) { $vals['hidden'] = ''; + } $fit = $this->addPageSubItem( $row->cl_from, $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $this->keyToTitle( $row->cl_to ) ); break; } } } else { - $titles = array(); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -173,44 +179,47 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { } $resultPageSet->populateFromTitles( $titles ); } - - $db->freeResult( $res ); } public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + return array( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array ( 'sortkey', 'timestamp', 'hidden', ) ), 'show' => array( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'hidden', '!hidden', ) ), '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_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), 'continue' => null, 'categories' => array( - ApiBase :: PARAM_ISMULTI => true, + ApiBase::PARAM_ISMULTI => true, ), ); } public function getParamDescription() { - return array ( - 'prop' => 'Which additional properties to get for each category.', + return array( + 'prop' => array( + 'Which additional properties to get for each category', + ' sortkey - Adds the sortkey (hexadecimal string) and sortkey prefix (human-readable part) for the category', + ' timestamp - Adds timestamp of when the category was added', + ' hidden - Tags categories that are hidden with __HIDDENCAT__', + ), 'limit' => 'How many categories to return', 'show' => 'Which kind of categories to show', 'continue' => 'When more results are available, use this to continue', @@ -221,7 +230,7 @@ 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' ), @@ -229,15 +238,15 @@ class ApiQueryCategories extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( - "Get a list of categories [[Albert Einstein]] belongs to:", - " api.php?action=query&prop=categories&titles=Albert%20Einstein", - "Get information about all categories used in the [[Albert Einstein]]:", - " api.php?action=query&generator=categories&titles=Albert%20Einstein&prop=info" - ); + return array( + 'Get a list of categories [[Albert Einstein]] belongs to:', + ' api.php?action=query&prop=categories&titles=Albert%20Einstein', + 'Get information about all categories used in the [[Albert Einstein]]:', + ' api.php?action=query&generator=categories&titles=Albert%20Einstein&prop=info' + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryCategories.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryCategories.php 86474 2011-04-20 13:22:05Z catrope $'; } } diff --git a/includes/api/ApiQueryCategoryInfo.php b/includes/api/ApiQueryCategoryInfo.php index 4df2f181..d4b64025 100644 --- a/includes/api/ApiQueryCategoryInfo.php +++ b/includes/api/ApiQueryCategoryInfo.php @@ -1,11 +1,10 @@ @gmail.com + * Created on May 13, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryCategoryInfo extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'ci' ); + parent::__construct( $query, $moduleName, 'ci' ); } public function execute() { @@ -50,8 +51,7 @@ 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(); } @@ -69,34 +69,30 @@ class ApiQueryCategoryInfo extends ApiQueryBase { $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'] ) ) - { + if ( !is_null( $params['continue'] ) ) { $title = $this->getDB()->addQuotes( $params['continue'] ); $this->addWhere( "cat_title >= $title" ); } $this->addOption( 'ORDER BY', 'cat_title' ); - $db = $this->getDB(); $res = $this->select( __METHOD__ ); $catids = array_flip( $cattitles ); - while ( $row = $db->fetchObject( $res ) ) - { + foreach ( $res as $row ) { $vals = array(); $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 ) + if ( $row->cat_hidden ) { $vals['hidden'] = ''; + } $fit = $this->addPageSubItems( $catids[$row->cat_title], $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $row->cat_title ); break; } } - $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -104,13 +100,13 @@ class ApiQueryCategoryInfo extends ApiQueryBase { } public function getAllowedParams() { - return array ( + return array( 'continue' => null, ); } public function getParamDescription() { - return array ( + return array( 'continue' => 'When more results are available, use this to continue', ); } @@ -120,10 +116,10 @@ class ApiQueryCategoryInfo extends ApiQueryBase { } protected function getExamples() { - return "api.php?action=query&prop=categoryinfo&titles=Category:Foo|Category:Bar"; + return 'api.php?action=query&prop=categoryinfo&titles=Category:Foo|Category:Bar'; } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryCategoryInfo.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryCategoryInfo.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiQueryCategoryMembers.php b/includes/api/ApiQueryCategoryMembers.php index 107f5049..bbcf8b9b 100644 --- a/includes/api/ApiQueryCategoryMembers.php +++ b/includes/api/ApiQueryCategoryMembers.php @@ -1,11 +1,10 @@ @gmail.com + * Created on June 14, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'cm' ); + parent::__construct( $query, $moduleName, 'cm' ); } public function execute() { @@ -52,42 +53,40 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { } 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 ( is_null( $categoryTitle ) || $categoryTitle->getNamespace() != NS_CATEGORY ) - $this->dieUsage( "The category name you entered is not valid", 'invalidcategory' ); + if ( is_null( $categoryTitle ) || $categoryTitle->getNamespace() != NS_CATEGORY ) { + $this->dieUsage( 'The category name you entered is not valid', 'invalidcategory' ); + } $prop = array_flip( $params['prop'] ); $fld_ids = isset( $prop['ids'] ); $fld_title = isset( $prop['title'] ); $fld_sortkey = isset( $prop['sortkey'] ); + $fld_sortkeyprefix = isset( $prop['sortkeyprefix'] ); $fld_timestamp = isset( $prop['timestamp'] ); + $fld_type = isset( $prop['type'] ); if ( is_null( $resultPageSet ) ) { - $this->addFields( array( 'cl_from', 'cl_sortkey', 'page_namespace', 'page_title' ) ); + $this->addFields( array( 'cl_from', 'cl_sortkey', 'cl_type', 'page_namespace', 'page_title' ) ); $this->addFieldsIf( 'page_id', $fld_ids ); + $this->addFieldsIf( 'cl_sortkey_prefix', $fld_sortkeyprefix ); } else { $this->addFields( $resultPageSet->getPageTableFields() ); // will include page_ id, ns, title - $this->addFields( array( 'cl_from', 'cl_sortkey' ) ); + $this->addFields( array( 'cl_from', 'cl_sortkey', 'cl_type' ) ); } $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' ); - 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() ); - // Scanning large datasets for rare categories sucks, and I already told + $queryTypes = $params['type']; + $contWhere = false; + + // 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(); @@ -96,69 +95,146 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { } 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 ); + + $dir = $params['dir'] == 'asc' ? 'newer' : 'older'; + + if ( $params['sort'] == 'timestamp' ) { + $this->addWhereRange( 'cl_timestamp', + $dir, + $params['start'], + $params['end'] ); + + $this->addOption( 'USE INDEX', 'cl_timestamp' ); + } else { + if ( $params['continue'] ) { + $cont = explode( '|', $params['continue'], 3 ); + if ( count( $cont ) != 3 ) { + $this->dieUsage( 'Invalid continue param. You should pass the original value returned '. + 'by the previous query', '_badcontinue' + ); + } + + // Remove the types to skip from $queryTypes + $contTypeIndex = array_search( $cont[0], $queryTypes ); + $queryTypes = array_slice( $queryTypes, $contTypeIndex ); + + // Add a WHERE clause for sortkey and from + // pack( "H*", $foo ) is used to convert hex back to binary + $escSortkey = $this->getDB()->addQuotes( pack( "H*", $cont[1] ) ); + $from = intval( $cont[2] ); + $op = $dir == 'newer' ? '>' : '<'; + // $contWhere is used further down + $contWhere = "cl_sortkey $op $escSortkey OR " . + "(cl_sortkey = $escSortkey AND " . + "cl_from $op= $from)"; + + } else { + // The below produces ORDER BY cl_sortkey, cl_from, possibly with DESC added to each of them + $this->addWhereRange( 'cl_sortkey', + $dir, + $params['startsortkey'], + $params['endsortkey'] ); + $this->addWhereRange( 'cl_from', $dir, null, null ); + } + $this->addOption( 'USE INDEX', 'cl_sortkey' ); } + $this->addWhere( 'cl_from=page_id' ); + $limit = $params['limit']; $this->addOption( 'LIMIT', $limit + 1 ); - $db = $this->getDB(); - - $data = array (); + if ( $params['sort'] == 'sortkey' ) { + // Run a separate SELECT query for each value of cl_type. + // This is needed because cl_type is an enum, and MySQL has + // inconsistencies between ORDER BY cl_type and + // WHERE cl_type >= 'foo' making proper paging impossible + // and unindexed. + $rows = array(); + $first = true; + foreach ( $queryTypes as $type ) { + $extraConds = array( 'cl_type' => $type ); + if ( $first && $contWhere ) { + // Continuation condition. Only added to the + // first query, otherwise we'll skip things + $extraConds[] = $contWhere; + } + $res = $this->select( __METHOD__, array( 'where' => $extraConds ) ); + $rows = array_merge( $rows, iterator_to_array( $res ) ); + if ( count( $rows ) >= $limit + 1 ) { + break; + } + $first = false; + } + } else { + // Sorting by timestamp + // No need to worry about per-type queries because we + // aren't sorting or filtering by type anyway + $res = $this->select( __METHOD__ ); + $rows = iterator_to_array( $res ); + } $count = 0; - $lastSortKey = null; - $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $rows as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... // TODO: Security issue - if the user has no right to view next title, it will still be shown - if ( $params['sort'] == 'timestamp' ) + if ( $params['sort'] == 'timestamp' ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->cl_timestamp ) ); - else - $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $row, $lastSortKey ) ); + } else { + $sortkey = bin2hex( $row->cl_sortkey ); + $this->setContinueEnumParameter( 'continue', + "{$row->cl_type}|$sortkey|{$row->cl_from}" + ); + } break; } - // 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 + // 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 ) ) + if ( count( $miser_ns ) && !in_array( $row->page_namespace, $miser_ns ) ) { continue; + } if ( is_null( $resultPageSet ) ) { $vals = array(); - if ( $fld_ids ) + if ( $fld_ids ) { $vals['pageid'] = intval( $row->page_id ); + } if ( $fld_title ) { - $title = Title :: makeTitle( $row->page_namespace, $row->page_title ); + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); ApiQueryBase::addTitleInfo( $vals, $title ); } - if ( $fld_sortkey ) - $vals['sortkey'] = $row->cl_sortkey; - if ( $fld_timestamp ) + if ( $fld_sortkey ) { + $vals['sortkey'] = bin2hex( $row->cl_sortkey ); + } + if ( $fld_sortkeyprefix ) { + $vals['sortkeyprefix'] = $row->cl_sortkey_prefix; + } + if ( $fld_type ) { + $vals['type'] = $row->cl_type; + } + 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' ) + if ( !$fit ) { + if ( $params['sort'] == 'timestamp' ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->cl_timestamp ) ); - else - $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $row, $lastSortKey ) ); + } else { + $sortkey = bin2hex( $row->cl_sortkey ); + $this->setContinueEnumParameter( 'continue', + "{$row->cl_type}|$sortkey|{$row->cl_from}" + ); + } break; } } else { $resultPageSet->processDbRow( $row ); } - $lastSortKey = $row->cl_sortkey; // detect duplicate sortkeys } - $db->freeResult( $res ); if ( is_null( $resultPageSet ) ) { $this->getResult()->setIndexedTagName_internal( @@ -166,85 +242,64 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { } } - private function getContinueStr( $row, $lastSortKey ) { - $ret = $row->cl_sortkey . '|'; - if ( $row->cl_sortkey == $lastSortKey ) // duplicate sort key, add cl_from - $ret .= $row->cl_from; - return $ret; - } - - /** - * Add DB WHERE clause to continue previous query based on 'continue' parameter - */ - 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 ); - - 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 ); - - $op = ( $dir == 'desc' ? '<' : '>' ); - - if ( $from != 0 ) { - // Duplicate sort key continue - $this->addWhere( "cl_sortkey$op$encSortKey OR (cl_sortkey=$encSortKey AND cl_from$op=$encFrom)" ); - } else { - $this->addWhere( "cl_sortkey$op=$encSortKey" ); - } - } - public function getAllowedParams() { - return array ( - 'title' => null, - 'prop' => array ( - ApiBase :: PARAM_DFLT => 'ids|title', - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + return array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), + 'prop' => array( + ApiBase::PARAM_DFLT => 'ids|title', + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array ( 'ids', 'title', 'sortkey', + 'sortkeyprefix', + 'type', 'timestamp', ) ), 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace', + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace', + ), + 'type' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'page|subcat|file', + ApiBase::PARAM_TYPE => array( + 'page', + 'subcat', + 'file' + ) ), 'continue' => null, - 'limit' => array ( - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), 'sort' => array( - ApiBase :: PARAM_DFLT => 'sortkey', - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'sortkey', + ApiBase::PARAM_TYPE => array( 'sortkey', 'timestamp' ) ), 'dir' => array( - ApiBase :: PARAM_DFLT => 'asc', - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'asc', + ApiBase::PARAM_TYPE => array( 'asc', 'desc' ) ), 'start' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'end' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'startsortkey' => null, 'endsortkey' => null, @@ -253,16 +308,26 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { public function getParamDescription() { global $wgMiserMode; - $desc = array ( + $p = $this->getModulePrefix(); + $desc = array( 'title' => 'Which category to enumerate (required). Must include Category: prefix', - 'prop' => 'What pieces of information to include', + 'prop' => array( + 'What pieces of information to include', + ' ids - Adds the page ID', + ' title - Adds the title and namespace ID of the page', + ' sortkey - Adds the sortkey used for sorting in the category (hexadecimal string)', + ' sortkeyprefix - Adds the sortkey prefix used for sorting in the category (human-readable part of the sortkey)', + ' type - Adds the type that the page has been categorised as (page, subcat or file)', + ' timestamp - Adds the timestamp of when the page was included', + ), 'namespace' => 'Only include pages in these namespaces', + 'type' => "What type of category members to include. Ignored when {$p}sort=timestamp is set", 'sort' => 'Property to sort by', 'dir' => 'In which direction to sort', - 'start' => 'Timestamp to start listing from. Can only be used with cmsort=timestamp', - 'end' => 'Timestamp to end listing at. Can only be used with cmsort=timestamp', - 'startsortkey' => 'Sortkey to start listing from. Can only be used with cmsort=sortkey', - 'endsortkey' => 'Sortkey to end listing at. Can only be used with cmsort=sortkey', + 'start' => "Timestamp to start listing from. Can only be used with {$p}sort=timestamp", + 'end' => "Timestamp to end listing at. Can only be used with {$p}sort=timestamp", + 'startsortkey' => "Sortkey to start listing from. Can only be used with {$p}sort=sortkey", + 'endsortkey' => "Sortkey to end listing at. Can only be used with {$p}sort=sortkey", 'continue' => 'For large categories, give the value retured from previous query', 'limit' => 'The maximum number of pages to return.', ); @@ -271,6 +336,7 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { $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.', + 'Note that you can use cmtype=subcat or cmtype=file instead of cmnamespace=14 or 6.', ); } return $desc; @@ -279,7 +345,7 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { 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' ), @@ -289,15 +355,15 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( - "Get first 10 pages in [[Category:Physics]]:", - " api.php?action=query&list=categorymembers&cmtitle=Category:Physics", - "Get page info about first 10 pages in [[Category:Physics]]:", - " api.php?action=query&generator=categorymembers&gcmtitle=Category:Physics&prop=info", - ); + return array( + 'Get first 10 pages in [[Category:Physics]]:', + ' api.php?action=query&list=categorymembers&cmtitle=Category:Physics', + 'Get page info about first 10 pages in [[Category:Physics]]:', + ' api.php?action=query&generator=categorymembers&gcmtitle=Category:Physics&prop=info', + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryCategoryMembers.php 86474 2011-04-20 13:22:05Z catrope $'; } } diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php index b26c7051..523862c0 100644 --- a/includes/api/ApiQueryDeletedrevs.php +++ b/includes/api/ApiQueryDeletedrevs.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Jul 2, 2007 + * + * 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 @@ -19,101 +18,115 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** - * Query module to enumerate all available pages. + * Query module to enumerate all deleted revisions. * * @ingroup API */ class ApiQueryDeletedrevs extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'dr' ); + parent::__construct( $query, $moduleName, 'dr' ); } public function execute() { - global $wgUser; // Before doing anything at all, let's check permissions - if ( !$wgUser->isAllowed( 'deletedhistory' ) ) + 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_userid = isset( $prop['userid'] ); $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(); $titles = $pageSet->getTitles(); - $data = array(); - + // This module operates in three modes: // 'revs': List deleted revs for certain titles // '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'] ) ) + } elseif ( !is_null( $params['user'] ) ) { $mode = 'user'; - - if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) + } + + 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 ) + if ( $fld_revid ) { $this->addFields( 'ar_rev_id' ); - if ( $fld_user ) + } + if ( $fld_user ) { $this->addFields( 'ar_user_text' ); - if ( $fld_comment || $fld_parsedcomment ) + } + if ( $fld_userid ) { + $this->addFields( 'ar_user' ); + } + if ( $fld_comment || $fld_parsedcomment ) { $this->addFields( 'ar_comment' ); - if ( $fld_minor ) + } + if ( $fld_minor ) { $this->addFields( 'ar_minor_edit' ); - if ( $fld_len ) + } + 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' ) ) + 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; - $botMax = $fld_content ? ApiBase :: LIMIT_SML2 : ApiBase :: LIMIT_BIG2; + $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1; + $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2; $limit = $params['limit']; if ( $limit == 'max' ) { $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; - $this->getResult()->addValue( 'limits', $this->getModuleName(), $limit ); + $this->getResult()->setParsedLimit( $this->getModuleName(), $limit ); } $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' ) { @@ -122,25 +135,25 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $this->addWhere( $where ); } elseif ( $mode == 'all' ) { $this->addWhereFld( 'ar_namespace', $params['namespace'] ); - if ( !is_null( $params['from'] ) ) - { + if ( !is_null( $params['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['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" ); + 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] ); @@ -155,15 +168,14 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $this->addOption( 'LIMIT', $limit + 1 ); $this->addOption( 'USE INDEX', array( 'archive' => ( $mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp' ) ) ); if ( $mode == 'all' ) { - if ( $params['unique'] ) - { + if ( $params['unique'] ) { $this->addOption( 'GROUP BY', 'ar_title' ); $this->addOption( 'ORDER BY', 'ar_title' ); - } else + } else { $this->addOption( 'ORDER BY', 'ar_title, ar_timestamp' ); + } } else { - if ( $mode == 'revs' ) - { + 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 ); @@ -174,39 +186,47 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $pageMap = array(); // Maps ns&title to (fake) pageid $count = 0; $newPageID = 0; - while ( $row = $db->fetchObject( $res ) ) - { + foreach ( $res as $row ) { if ( ++$count > $limit ) { // We've had enough - if ( $mode == 'all' || $mode == 'revs' ) + if ( $mode == 'all' || $mode == 'revs' ) { $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' . $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp ); - else + } else { $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 ) + if ( $fld_revid ) { $rev['revid'] = intval( $row->ar_rev_id ); - if ( $fld_user ) + } + if ( $fld_user ) { $rev['user'] = $row->ar_user_text; - if ( $fld_comment ) + } + if ( $fld_userid ) { + $rev['userid'] = $row->ar_user; + } + if ( $fld_comment ) { $rev['comment'] = $row->ar_comment; + } $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 ) + if ( $fld_minor && $row->ar_minor_edit == 1 ) { $rev['minor'] = ''; - if ( $fld_len ) + } + if ( $fld_len ) { $rev['len'] = $row->ar_len; - if ( $fld_content ) + } + if ( $fld_content ) { ApiResult::setContent( $rev, Revision::getRevisionText( $row ) ); + } if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) { $pageID = $newPageID++; @@ -214,8 +234,9 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $a['revisions'] = array( $rev ); $result->setIndexedTagName( $a['revisions'], 'rev' ); ApiQueryBase::addTitleInfo( $a, $title ); - if ( $fld_token ) + if ( $fld_token ) { $a['token'] = $token; + } $fit = $result->addValue( array( 'query', $this->getModuleName() ), $pageID, $a ); } else { $pageID = $pageMap[$row->ar_namespace][$row->ar_title]; @@ -224,58 +245,59 @@ class ApiQueryDeletedrevs extends ApiQueryBase { null, $rev ); } if ( !$fit ) { - if ( $mode == 'all' || $mode == 'revs' ) + if ( $mode == 'all' || $mode == 'revs' ) { $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' . $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp ); - else + } else { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) ); + } break; } } - $db->freeResult( $res ); $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'page' ); } public function getAllowedParams() { - return array ( + return array( 'start' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'end' => array( - ApiBase :: PARAM_TYPE => 'timestamp', + ApiBase::PARAM_TYPE => 'timestamp', ), 'dir' => array( - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_TYPE => array( 'newer', 'older' ), - ApiBase :: PARAM_DFLT => 'older' + ApiBase::PARAM_DFLT => 'older' ), 'from' => null, 'continue' => null, 'unique' => false, 'user' => array( - ApiBase :: PARAM_TYPE => 'user' + ApiBase::PARAM_TYPE => 'user' ), 'excludeuser' => array( - ApiBase :: PARAM_TYPE => 'user' + ApiBase::PARAM_TYPE => 'user' ), 'namespace' => array( - ApiBase :: PARAM_TYPE => 'namespace', - ApiBase :: PARAM_DFLT => 0, + ApiBase::PARAM_TYPE => 'namespace', + ApiBase::PARAM_DFLT => 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_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 => 'user|comment', - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'user|comment', + ApiBase::PARAM_TYPE => array( 'revid', 'user', + 'userid', 'comment', 'parsedcomment', 'minor', @@ -283,18 +305,29 @@ class ApiQueryDeletedrevs extends ApiQueryBase { 'content', 'token' ), - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true ), ); } public function getParamDescription() { - return array ( - 'start' => 'The timestamp to start enumerating from. (1,2)', - 'end' => 'The timestamp to stop enumerating at. (1,2)', - 'dir' => 'The direction in which to enumerate. (1,2)', + return array( + 'start' => 'The timestamp to start enumerating from (1,2)', + 'end' => 'The timestamp to stop enumerating at (1,2)', + 'dir' => 'The direction in which to enumerate (1,2)', 'limit' => 'The maximum amount of revisions to list', - 'prop' => 'Which properties to get', + 'prop' => array( + 'Which properties to get', + ' revid - Adds the revision id of the deleted revision', + ' user - Adds the user who made the revision', + ' userid - Adds the user id whom made the revision', + ' comment - Adds the comment of the revision', + ' parsedcomment - Adds the parsed comment of the revision', + ' minor - Tags if the revision is minor', + ' len - Adds the length of the revision', + ' content - Adds the content of the revision', + ' token - Gives the edit token', + ), 'namespace' => 'Only list pages in this namespace (3)', 'user' => 'Only list revisions by this user', 'excludeuser' => 'Don\'t list revisions by this user', @@ -305,16 +338,17 @@ class ApiQueryDeletedrevs extends ApiQueryBase { } public function getDescription() { - return array( 'List deleted revisions.', - 'This module operates in three modes:', - '1) List deleted revisions for the given title(s), sorted by timestamp', - '2) List deleted contributions for the given user, sorted by timestamp (no titles specified)', - '3) List all deleted revisions in the given namespace, sorted by title and timestamp (no titles specified, druser not set)', - 'Certain parameters only apply to some modes and are ignored in others.', - 'For instance, a parameter marked (1) only applies to mode 1 and is ignored in modes 2 and 3.', + return array( + 'List deleted revisions.', + 'This module operates in three modes:', + '1) List deleted revisions for the given title(s), sorted by timestamp', + '2) List deleted contributions for the given user, sorted by timestamp (no titles specified)', + '3) List all deleted revisions in the given namespace, sorted by title and timestamp (no titles specified, druser not set)', + 'Certain parameters only apply to some modes and are ignored in others.', + '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' ), @@ -325,7 +359,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'List the last deleted revisions of Main Page and Talk:Main Page, with content (mode 1):', ' api.php?action=query&list=deletedrevs&titles=Main%20Page|Talk:Main%20Page&drprop=user|comment|content', 'List the last 50 deleted contributions by Bob (mode 2):', @@ -333,11 +367,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase { 'List the first 50 deleted revisions in the main namespace (mode 3):', ' api.php?action=query&list=deletedrevs&drdir=newer&drlimit=50', 'List the first 50 deleted pages in the Talk namespace (mode 3):', - ' api.php?action=query&list=deletedrevs&drdir=newer&drlimit=50&drnamespace=1&drunique', + ' api.php?action=query&list=deletedrevs&drdir=newer&drlimit=50&drnamespace=1&drunique=', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryDeletedrevs.php 77192 2010-11-23 22:05:27Z btongminh $'; } -} \ No newline at end of file +} diff --git a/includes/api/ApiQueryDisabled.php b/includes/api/ApiQueryDisabled.php index 4bd3f5fd..b5712069 100644 --- a/includes/api/ApiQueryDisabled.php +++ b/includes/api/ApiQueryDisabled.php @@ -1,10 +1,10 @@ .@home.nl + * Created on Sep 25, 2008 + * + * 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 @@ -18,18 +18,19 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } - /** - * API module that does nothing + * API module that does nothing * * Use this to disable core modules with e.g. * $wgAPIPropModules['modulename'] = 'ApiQueryDisabled'; @@ -41,7 +42,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryDisabled extends ApiQueryBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { @@ -49,11 +50,11 @@ class ApiQueryDisabled extends ApiQueryBase { } public function getAllowedParams() { - return array (); + return array(); } public function getParamDescription() { - return array (); + return array(); } public function getDescription() { @@ -63,10 +64,10 @@ class ApiQueryDisabled extends ApiQueryBase { } protected function getExamples() { - return array (); + return array(); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryDisabled.php 60930 2010-01-11 15:55:52Z simetrical $'; + return __CLASS__ . ': $Id: ApiQueryDisabled.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiQueryDuplicateFiles.php b/includes/api/ApiQueryDuplicateFiles.php index ed070069..ffe98038 100644 --- a/includes/api/ApiQueryDuplicateFiles.php +++ b/includes/api/ApiQueryDuplicateFiles.php @@ -1,11 +1,10 @@ ,@home.nl + * Created on Sep 27, 2008 + * + * Copyright © 2008 Roan Kattow ,@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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'df' ); + parent::__construct( $query, $moduleName, 'df' ); } public function execute() { @@ -58,7 +59,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { return; } $images = $namespaces[NS_FILE]; - + $this->addTables( 'image', 'i1' ); $this->addTables( 'image', 'i2' ); $this->addFields( array( @@ -74,30 +75,29 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { '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" ); + 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->addWhere( + "i1.img_name > '$orig' OR " . + "(i1.img_name = '$orig' AND " . + "i2.img_name >= '$dup')" + ); } $this->addOption( 'ORDER BY', 'i1.img_name' ); $this->addOption( 'LIMIT', $params['limit'] + 1 ); $res = $this->select( __METHOD__ ); - $db = $this->getDB(); $count = 0; $titles = array(); - while ( $row = $db->fetchObject( $res ) ) - { - if ( ++$count > $params['limit'] ) - { + foreach ( $res as $row ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'continue', @@ -105,18 +105,16 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { $this->keyToTitle( $row->dup_name ) ); break; } - if ( !is_null( $resultPageSet ) ) + if ( !is_null( $resultPageSet ) ) { $titles[] = Title::makeTitle( NS_FILE, $row->dup_name ); - else - { + } else { $r = array( 'name' => $row->dup_name, 'user' => $row->dup_user_text, 'timestamp' => wfTimestamp( TS_ISO_8601, $row->dup_timestamp ) ); $fit = $this->addPageSubItem( $images[$row->orig_name], $r ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->orig_name ) . '|' . $this->keyToTitle( $row->dup_name ) ); @@ -124,35 +122,35 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { } } } - if ( !is_null( $resultPageSet ) ) + if ( !is_null( $resultPageSet ) ) { $resultPageSet->populateFromTitles( $titles ); - $db->freeResult( $res ); + } } public function getAllowedParams() { - return array ( + return array( 'limit' => array( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), 'continue' => null, ); } public function getParamDescription() { - return array ( + return array( 'limit' => 'How many files to return', 'continue' => 'When more results are available, use this to continue', ); } public function getDescription() { - return 'List all files that are duplicates of the given file(s).'; + 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' ), @@ -160,12 +158,13 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( 'api.php?action=query&titles=File:Albert_Einstein_Head.jpg&prop=duplicatefiles', - 'api.php?action=query&generator=allimages&prop=duplicatefiles', - ); + return array( + 'api.php?action=query&titles=File:Albert_Einstein_Head.jpg&prop=duplicatefiles', + 'api.php?action=query&generator=allimages&prop=duplicatefiles', + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryDuplicateFiles.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryDuplicateFiles.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php index 0e171e44..ecd9e699 100644 --- a/includes/api/ApiQueryExtLinksUsage.php +++ b/includes/api/ApiQueryExtLinksUsage.php @@ -1,11 +1,10 @@ @gmail.com + * Created on July 7, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -34,7 +35,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'eu' ); + parent::__construct( $query, $moduleName, 'eu' ); } public function execute() { @@ -50,7 +51,6 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { } private function run( $resultPageSet = null ) { - $params = $this->extractRequestParams(); $protocol = $params['protocol']; @@ -58,17 +58,16 @@ 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 ) { $protocol = $p; break; } } - } - else + } else { $protocol = null; + } $db = $this->getDB(); $this->addTables( array( 'page', 'externallinks' ) ); // must be in this order for 'USE INDEX' @@ -76,20 +75,21 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { $this->addWhere( 'page_id=el_from' ); $this->addWhereFld( 'page_namespace', $params['namespace'] ); - if ( !is_null( $query ) || $query != '' ) - { - if ( is_null( $protocol ) ) + if ( !is_null( $query ) || $query != '' ) { + if ( is_null( $protocol ) ) { $protocol = 'http://'; + } $likeQuery = LinkFilter::makeLikeArray( $query, $protocol ); - if ( !$likeQuery ) + if ( !$likeQuery ) { $this->dieUsage( 'Invalid query', 'bad_query' ); + } $likeQuery = LinkFilter::keepOneWildcard( $likeQuery ); $this->addWhere( 'el_index ' . $db->buildLike( $likeQuery ) ); - } - else if ( !is_null( $protocol ) ) + } elseif ( !is_null( $protocol ) ) { $this->addWhere( 'el_index ' . $db->buildLike( "$protocol", $db->anyString() ) ); + } $prop = array_flip( $params['prop'] ); $fld_ids = isset( $prop['ids'] ); @@ -97,7 +97,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { $fld_url = isset( $prop['url'] ); if ( is_null( $resultPageSet ) ) { - $this->addFields( array ( + $this->addFields( array( 'page_id', 'page_namespace', 'page_title' @@ -110,14 +110,15 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { $limit = $params['limit']; $offset = $params['offset']; $this->addOption( 'LIMIT', $limit + 1 ); - if ( isset ( $offset ) ) + if ( isset( $offset ) ) { $this->addOption( 'OFFSET', $offset ); + } $res = $this->select( __METHOD__ ); $result = $this->getResult(); $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'offset', $offset + $limit ); @@ -126,17 +127,18 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { if ( is_null( $resultPageSet ) ) { $vals = array(); - if ( $fld_ids ) + if ( $fld_ids ) { $vals['pageid'] = intval( $row->page_id ); + } if ( $fld_title ) { - $title = Title :: makeTitle( $row->page_namespace, $row->page_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 ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'offset', $offset + $count - 1 ); break; } @@ -144,7 +146,6 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { $resultPageSet->processDbRow( $row ); } } - $db->freeResult( $res ); if ( is_null( $resultPageSet ) ) { $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), @@ -159,44 +160,52 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { $protocols[] = substr( $p, 0, strpos( $p, ':' ) ); } - return array ( - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'ids|title|url', - ApiBase :: PARAM_TYPE => array ( + return array( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'ids|title|url', + ApiBase::PARAM_TYPE => array( 'ids', 'title', 'url' ) ), - 'offset' => array ( - ApiBase :: PARAM_TYPE => 'integer' + 'offset' => array( + ApiBase::PARAM_TYPE => 'integer' ), - 'protocol' => array ( - ApiBase :: PARAM_TYPE => $protocols, - ApiBase :: PARAM_DFLT => '', + 'protocol' => array( + ApiBase::PARAM_TYPE => $protocols, + ApiBase::PARAM_DFLT => '', ), 'query' => null, - 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace' + 'namespace' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace' ), - 'limit' => array ( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ) ); } public function getParamDescription() { - return array ( - 'prop' => 'What pieces of information to include', + $p = $this->getModulePrefix(); + return array( + 'prop' => array( + 'What pieces of information to include', + ' ids - Adds the id of page', + ' title - Adds the title and namespace id of the page', + ' url - Adds the URL used in the page', + ), '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' ), + 'protocol' => array( + "Protocol of the url. If empty and {$p}query set, the protocol is http.", + "Leave both this and {$p}query 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.' @@ -206,7 +215,7 @@ 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' ), @@ -214,12 +223,12 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=exturlusage&euquery=www.mediawiki.org' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryExtLinksUsage.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php index a748e036..fbfcbfb9 100644 --- a/includes/api/ApiQueryExternalLinks.php +++ b/includes/api/ApiQueryExternalLinks.php @@ -1,11 +1,10 @@ @gmail.com + * Created on May 13, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -36,15 +37,16 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryExternalLinks extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'el' ); + parent::__construct( $query, $moduleName, 'el' ); } public function execute() { - if ( $this->getPageSet()->getGoodTitleCount() == 0 ) + if ( $this->getPageSet()->getGoodTitleCount() == 0 ) { return; + } $params = $this->extractRequestParams(); - $this->addFields( array ( + $this->addFields( array( 'el_from', 'el_to' ) ); @@ -53,18 +55,19 @@ class ApiQueryExternalLinks extends ApiQueryBase { $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 ) + if ( count( $this->getPageSet()->getGoodTitles() ) != 1 ) { $this->addOption( 'ORDER BY', 'el_from' ); + } $this->addOption( 'LIMIT', $params['limit'] + 1 ); - if ( !is_null( $params['offset'] ) ) + if ( !is_null( $params['offset'] ) ) { $this->addOption( 'OFFSET', $params['offset'] ); + } - $db = $this->getDB(); $res = $this->select( __METHOD__ ); $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -72,15 +75,13 @@ class ApiQueryExternalLinks extends ApiQueryBase { break; } $entry = array(); - ApiResult :: setContent( $entry, $row->el_to ); + ApiResult::setContent( $entry, $row->el_to ); $fit = $this->addPageSubItem( $row->el_from, $entry ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'offset', @$params['offset'] + $count - 1 ); break; } } - $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -89,18 +90,18 @@ class ApiQueryExternalLinks extends ApiQueryBase { public function getAllowedParams() { return array( - 'limit' => array( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 - ), - 'offset' => null, + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ), + 'offset' => null, ); } - public function getParamDescription () { + public function getParamDescription() { return array( 'limit' => 'How many links to return', 'offset' => 'When more results are available, use this to continue', @@ -112,13 +113,13 @@ class ApiQueryExternalLinks extends ApiQueryBase { } protected function getExamples() { - return array ( - "Get a list of external links on the [[Main Page]]:", - " api.php?action=query&prop=extlinks&titles=Main%20Page", - ); + return array( + 'Get a list of external links on the [[Main Page]]:', + ' api.php?action=query&prop=extlinks&titles=Main%20Page', + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryExternalLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryExternalLinks.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiQueryFilearchive.php b/includes/api/ApiQueryFilearchive.php new file mode 100644 index 00000000..05ccb346 --- /dev/null +++ b/includes/api/ApiQueryFilearchive.php @@ -0,0 +1,264 @@ +isAllowed( 'deletedhistory' ) ) { + $this->dieUsage( 'You don\'t have permission to view deleted file information', 'permissiondenied' ); + } + + $db = $this->getDB(); + + $params = $this->extractRequestParams(); + + $prop = array_flip( $params['prop'] ); + $fld_sha1 = isset( $prop['sha1'] ); + $fld_timestamp = isset( $prop['timestamp'] ); + $fld_user = isset( $prop['user'] ); + $fld_size = isset( $prop['size'] ); + $fld_dimensions = isset( $prop['dimensions'] ); + $fld_description = isset( $prop['description'] ); + $fld_mime = isset( $prop['mime'] ); + $fld_metadata = isset( $prop['metadata'] ); + $fld_bitdepth = isset( $prop['bitdepth'] ); + + $this->addTables( 'filearchive' ); + + $this->addFields( array( 'fa_name', 'fa_deleted' ) ); + $this->addFieldsIf( 'fa_storage_key', $fld_sha1 ); + $this->addFieldsIf( 'fa_timestamp', $fld_timestamp ); + + if ( $fld_user ) { + $this->addFields( array( 'fa_user', 'fa_user_text' ) ); + } + $this->addFieldsIf( 'fa_size', $fld_size ); + + if ( $fld_dimensions ) { + $this->addFields( array( 'fa_height', 'fa_width' ) ); + } + + $this->addFieldsIf( 'fa_description', $fld_description ); + + if ( $fld_mime ) { + $this->addFields( array( 'fa_major_mime', 'fa_minor_mime' ) ); + } + + $this->addFieldsIf( 'fa_metadata', $fld_metadata ); + $this->addFieldsIf( 'fa_bits', $fld_bitdepth ); + + // Image filters + $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' ); + $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) ); + $this->addWhereRange( 'fa_name', $dir, $from, null ); + if ( isset( $params['prefix'] ) ) { + $this->addWhere( 'fa_name' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) ); + } + + if ( !$wgUser->isAllowed( 'suppressrevision' ) ) { + // Filter out revisions that the user is not allowed to see. There + // is no way to indicate that we have skipped stuff because the + // continuation parameter is fa_name + + // Note that this field is unindexed. This should however not be + // a big problem as files with fa_deleted are rare + $this->addWhereFld( 'fa_deleted', 0 ); + } + + + + $limit = $params['limit']; + $this->addOption( 'LIMIT', $limit + 1 ); + $this->addOption( 'ORDER BY', 'fa_name' . + ( $params['dir'] == 'descending' ? ' DESC' : '' ) ); + + $res = $this->select( __METHOD__ ); + + $count = 0; + $result = $this->getResult(); + foreach ( $res as $row ) { + if ( ++$count > $limit ) { + // We've reached the one extra which shows that there are additional pages to be had. Stop here... + // TODO: Security issue - if the user has no right to view next title, it will still be shown + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->fa_name ) ); + break; + } + + $file = array(); + $file['name'] = $row->fa_name; + + if ( $fld_sha1 ) { + $file['sha1'] = wfBaseConvert( $row->fa_storage_key, 36, 16, 40 ); + } + if ( $fld_timestamp ) { + $file['timestamp'] = wfTimestamp( TS_ISO_8601, $row->fa_timestamp ); + } + if ( $fld_user ) { + $file['userid'] = $row->fa_user; + $file['user'] = $row->fa_user_text; + } + if ( $fld_size ) { + $file['size'] = $row->fa_size; + } + if ( $fld_dimensions ) { + $file['height'] = $row->fa_height; + $file['width'] = $row->fa_width; + } + if ( $fld_description ) { + $file['description'] = $row->fa_description; + } + if ( $fld_metadata ) { + $file['metadata'] = $row->fa_metadata ? ApiQueryImageInfo::processMetaData( unserialize( $row->fa_metadata ), $result ) : null; + } + if ( $fld_bitdepth ) { + $file['bitdepth'] = $row->fa_bits; + } + if ( $fld_mime ) { + $file['mime'] = "$row->fa_major_mime/$row->fa_minor_mime"; + } + + if ( $row->fa_deleted & File::DELETED_FILE ) { + $file['filehidden'] = ''; + } + if ( $row->fa_deleted & File::DELETED_COMMENT ) { + $file['commenthidden'] = ''; + } + if ( $row->fa_deleted & File::DELETED_USER ) { + $file['userhidden'] = ''; + } + if ( $row->fa_deleted & File::DELETED_RESTRICTED ) { + // This file is deleted for normal admins + $file['suppressed'] = ''; + } + + + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $file ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->fa_name ) ); + break; + } + } + + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'fa' ); + } + + public function getAllowedParams() { + return array ( + 'from' => null, + 'prefix' => null, + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ), + 'dir' => array( + ApiBase::PARAM_DFLT => 'ascending', + ApiBase::PARAM_TYPE => array( + 'ascending', + 'descending' + ) + ), + 'prop' => array( + ApiBase::PARAM_DFLT => 'timestamp', + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( + 'sha1', + 'timestamp', + 'user', + 'size', + 'dimensions', + 'description', + 'mime', + 'metadata', + 'bitdepth' + ), + ), + ); + } + + public function getParamDescription() { + return array( + 'from' => 'The image title to start enumerating from', + 'prefix' => 'Search for all image titles that begin with this value', + 'dir' => 'The direction in which to list', + 'limit' => 'How many total images to return', + 'prop' => array( + 'What image information to get:', + ' sha1 - Adds sha1 hash for the image', + ' timestamp - Adds timestamp for the uploaded version', + ' user - Adds user who uploaded the image version', + ' size - Adds the size of the image in bytes', + ' dimensions - Adds the height and width of the image', + ' description - Adds description the image version', + ' mime - Adds MIME of the image', + ' metadata - Lists EXIF metadata for the version of the image', + ' bitdepth - Adds the bit depth of the version', + ), + ); + } + + public function getDescription() { + return 'Enumerate all deleted files sequentially'; + } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted file information' ), + ) ); + } + + protected function getExamples() { + return array( + 'Simple Use', + ' Show a list of all deleted files', + ' api.php?action=query&list=filearchive', + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiQueryFilearchive.php 85354 2011-04-04 18:25:31Z demon $'; + } +} diff --git a/includes/api/ApiQueryIWBacklinks.php b/includes/api/ApiQueryIWBacklinks.php new file mode 100644 index 00000000..6958a253 --- /dev/null +++ b/includes/api/ApiQueryIWBacklinks.php @@ -0,0 +1,217 @@ +@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +if ( !defined( 'MEDIAWIKI' ) ) { + // Eclipse helper - will be ignored in production + require_once( "ApiQueryBase.php" ); +} + +/** + * This gives links pointing to the given interwiki + * @ingroup API + */ +class ApiQueryIWBacklinks extends ApiQueryGeneratorBase { + + public function __construct( $query, $moduleName ) { + parent::__construct( $query, $moduleName, 'iwbl' ); + } + + public function execute() { + $this->run(); + } + + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); + } + + public function run( $resultPageSet = null ) { + $params = $this->extractRequestParams(); + + if ( isset( $params['title'] ) && !isset( $params['prefix'] ) ) { + $this->dieUsageMsg( array( 'missingparam', 'prefix' ) ); + } + + 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' ); + } + + $prefix = $this->getDB()->strencode( $cont[0] ); + $title = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) ); + $from = intval( $cont[2] ); + $this->addWhere( + "iwl_prefix > '$prefix' OR " . + "(iwl_prefix = '$prefix' AND " . + "(iwl_title > '$title' OR " . + "(iwl_title = '$title' AND " . + "iwl_from >= $from)))" + ); + } + + $prop = array_flip( $params['prop'] ); + $iwprefix = isset( $prop['iwprefix'] ); + $iwtitle = isset( $prop['iwtitle'] ); + + $this->addTables( array( 'iwlinks', 'page' ) ); + $this->addWhere( 'iwl_from = page_id' ); + + $this->addFields( array( 'page_id', 'page_title', 'page_namespace', 'page_is_redirect', + 'iwl_from', 'iwl_prefix', 'iwl_title' ) ); + + if ( isset( $params['prefix'] ) ) { + $this->addWhereFld( 'iwl_prefix', $params['prefix'] ); + if ( isset( $params['title'] ) ) { + $this->addWhereFld( 'iwl_title', $params['title'] ); + $this->addOption( 'ORDER BY', 'iwl_from' ); + } else { + $this->addOption( 'ORDER BY', 'iwl_title, iwl_from' ); + } + } else { + $this->addOption( 'ORDER BY', 'iwl_prefix, iwl_title, iwl_from' ); + } + + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + + $res = $this->select( __METHOD__ ); + + $pages = array(); + + $count = 0; + $result = $this->getResult(); + foreach ( $res as $row ) { + if ( ++ $count > $params['limit'] ) { + // We've reached the one extra which shows that there are additional pages to be had. Stop here... + // Continue string preserved in case the redirect query doesn't pass the limit + $this->setContinueEnumParameter( 'continue', "{$row->iwl_prefix}|{$row->iwl_title}|{$row->iwl_from}" ); + break; + } + + if ( !is_null( $resultPageSet ) ) { + $pages[] = Title::newFromRow( $row ); + } else { + $entry = array(); + + $entry['pageid'] = intval( $row->page_id ); + $entry['ns'] = intval( $row->page_namespace ); + $entry['title'] = $row->page_title; + + if ( $row->page_is_redirect ) { + $entry['redirect'] = ''; + } + + if ( $iwprefix ) { + $entry['iwprefix'] = $row->iwl_prefix; + } + + if ( $iwtitle ) { + $entry['iwtitle'] = $row->iwl_title; + } + + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $entry ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'continue', "{$row->iwl_prefix}|{$row->iwl_title}|{$row->iwl_from}" ); + break; + } + } + } + + if ( is_null( $resultPageSet ) ) { + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'iw' ); + } else { + $resultPageSet->populateFromTitles( $pages ); + } + } + + public function getCacheMode( $params ) { + return 'public'; + } + + public function getAllowedParams() { + return array( + 'prefix' => null, + 'title' => null, + 'continue' => null, + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ), + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => '', + ApiBase::PARAM_TYPE => array( + 'iwprefix', + 'iwtitle', + ), + ), + ); + } + + public function getParamDescription() { + return array( + 'prefix' => 'Prefix for the interwiki', + 'title' => "Interwiki link to search for. Must be used with {$this->getModulePrefix()}prefix", + 'continue' => 'When more results are available, use this to continue', + 'prop' => array( + 'Which properties to get', + ' iwprefix - Adds the prefix of the interwiki', + ' iwtitle - Adds the title of the interwiki', + ), + 'limit' => 'How many total pages to return', + ); + } + + public function getDescription() { + return array( 'Find all pages that link to the given interwiki link.', + 'Can be used to find all links with a prefix, or', + 'all links to a title (with a given prefix).', + 'Using neither parameter is effectively "All IW Links"', + ); + } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'missingparam', 'prefix' ), + 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&list=iwbacklinks&iwbltitle=Test&iwblprefix=wikibooks', + 'api.php?action=query&generator=iwbacklinks&giwbltitle=Test&iwblprefix=wikibooks&prop=info' + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiQueryIWBacklinks.php 70647 2010-08-07 19:59:42Z ialex $'; + } +} diff --git a/includes/api/ApiQueryIWLinks.php b/includes/api/ApiQueryIWLinks.php new file mode 100644 index 00000000..e980d6a5 --- /dev/null +++ b/includes/api/ApiQueryIWLinks.php @@ -0,0 +1,158 @@ +@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +if ( !defined( 'MEDIAWIKI' ) ) { + // Eclipse helper - will be ignored in production + require_once( "ApiQueryBase.php" ); +} + +/** + * A query module to list all interwiki links on a page + * + * @ingroup API + */ +class ApiQueryIWLinks extends ApiQueryBase { + + public function __construct( $query, $moduleName ) { + parent::__construct( $query, $moduleName, 'iw' ); + } + + public function execute() { + if ( $this->getPageSet()->getGoodTitleCount() == 0 ) { + return; + } + + $params = $this->extractRequestParams(); + $this->addFields( array( + 'iwl_from', + 'iwl_prefix', + 'iwl_title' + ) ); + + $this->addTables( 'iwlinks' ); + $this->addWhereFld( 'iwl_from', array_keys( $this->getPageSet()->getGoodTitles() ) ); + + 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' ); + } + $iwlfrom = intval( $cont[0] ); + $iwlprefix = $this->getDB()->strencode( $cont[1] ); + $iwltitle = $this->getDB()->strencode( $this->titleToKey( $cont[2] ) ); + $this->addWhere( + "iwl_from > $iwlfrom OR " . + "(iwl_from = $iwlfrom AND " . + "(iwl_prefix > '$iwlprefix' OR " . + "(iwl_prefix = '$iwlprefix' AND " . + "iwl_title >= '$iwltitle')))" + ); + } + + // Don't order by iwl_from if it's constant in the WHERE clause + if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) { + $this->addOption( 'ORDER BY', 'iwl_prefix' ); + } else { + $this->addOption( 'ORDER BY', 'iwl_from, iwl_prefix' ); + } + $this->addOption( 'LIMIT', $params['limit'] + 1 ); + $res = $this->select( __METHOD__ ); + + $count = 0; + foreach ( $res as $row ) { + if ( ++$count > $params['limit'] ) { + // We've reached the one extra which shows that + // there are additional pages to be had. Stop here... + $this->setContinueEnumParameter( 'continue', "{$row->iwl_from}|{$row->iwl_prefix}|{$row->iwl_title}" ); + break; + } + $entry = array( 'prefix' => $row->iwl_prefix ); + + if ( !is_null( $params['url'] ) ) { + $title = Title::newFromText( "{$row->iwl_prefix}:{$row->iwl_title}" ); + if ( $title ) { + $entry['url'] = $title->getFullURL(); + } + } + + ApiResult::setContent( $entry, $row->iwl_title ); + $fit = $this->addPageSubItem( $row->iwl_from, $entry ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'continue', "{$row->iwl_from}|{$row->iwl_prefix}|{$row->iwl_title}" ); + break; + } + } + } + + public function getCacheMode( $params ) { + return 'public'; + } + + public function getAllowedParams() { + return array( + 'url' => null, + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ), + 'continue' => null, + ); + } + + public function getParamDescription() { + return array( + 'url' => 'Whether to get the full URL', + 'limit' => 'How many interwiki links to return', + 'continue' => 'When more results are available, use this to continue', + ); + } + + public function getDescription() { + return 'Returns all interwiki 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( + 'Get interwiki links from the [[Main Page]]:', + ' api.php?action=query&prop=iwlinks&titles=Main%20Page', + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiQueryIWLinks.php 77080 2010-11-21 17:27:13Z reedy $'; + } +} diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php index 3704710a..21696be2 100644 --- a/includes/api/ApiQueryImageInfo.php +++ b/includes/api/ApiQueryImageInfo.php @@ -1,11 +1,10 @@ @gmail.com + * Created on July 6, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -35,8 +36,13 @@ if ( !defined( 'MEDIAWIKI' ) ) { */ class ApiQueryImageInfo extends ApiQueryBase { - public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'ii' ); + public function __construct( $query, $moduleName, $prefix = 'ii' ) { + // We allow a subclass to override the prefix, to create a related API module. + // Some other parts of MediaWiki construct this with a null $prefix, which used to be ignored when this only took two arguments + if ( is_null( $prefix ) ) { + $prefix = 'ii'; + } + parent::__construct( $query, $moduleName, $prefix ); } public function execute() { @@ -44,16 +50,7 @@ class ApiQueryImageInfo extends ApiQueryBase { $prop = array_flip( $params['prop'] ); - if ( $params['urlheight'] != - 1 && $params['urlwidth'] == - 1 ) - $this->dieUsage( "iiurlheight cannot be used without iiurlwidth", 'iiurlwidth' ); - - if ( $params['urlwidth'] != - 1 ) { - $scale = array(); - $scale['width'] = $params['urlwidth']; - $scale['height'] = $params['urlheight']; - } else { - $scale = null; - } + $scale = $this->getScale( $params ); $pageIds = $this->getPageSet()->getAllTitlesByNamespace(); if ( !empty( $pageIds[NS_FILE] ) ) { @@ -61,30 +58,33 @@ class ApiQueryImageInfo extends ApiQueryBase { 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" ); + 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 ) + foreach ( $titles as $key => $title ) { + if ( $title < $fromTitle ) { unset( $titles[$key] ); - else + } else { break; + } + } } $result = $this->getResult(); $images = RepoGroup::singleton()->findFiles( $titles ); foreach ( $images as $img ) { // Skip redirects - if ( $img->getOriginalTitle()->isRedirect() ) + if ( $img->getOriginalTitle()->isRedirect() ) { continue; - + } + $start = $skip ? $fromTimestamp : $params['start']; $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ]; @@ -92,9 +92,8 @@ class ApiQueryImageInfo extends ApiQueryBase { array( 'query', 'pages', intval( $pageId ) ), 'imagerepository', $img->getRepoName() ); - if ( !$fit ) - { - if ( count( $pageIds[NS_IMAGE] ) == 1 ) + 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 @@ -102,29 +101,33 @@ class ApiQueryImageInfo extends ApiQueryBase { // out-continued, the result will get through $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); - else + } else { $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 ) - { - if ( count( $pageIds[NS_IMAGE] ) == 1 ) + 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() ) ); - else + } else { $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $img ) ); + } break; } } @@ -137,8 +140,7 @@ class ApiQueryImageInfo extends ApiQueryBase { if ( ++$count > $params['limit'] ) { // We've reached the extra one which shows that there are additional pages to be had. Stop here... // Only set a query-continue if there was only one title - if ( count( $pageIds[NS_FILE] ) == 1 ) - { + if ( count( $pageIds[NS_FILE] ) == 1 ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); } @@ -146,97 +148,210 @@ class ApiQueryImageInfo extends ApiQueryBase { } $fit = $this->addPageSubItem( $pageId, self::getInfo( $oldie, $prop, $result ) ); - if ( !$fit ) - { - if ( count( $pageIds[NS_IMAGE] ) == 1 ) + if ( !$fit ) { + if ( count( $pageIds[NS_IMAGE] ) == 1 ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); - else + } else { $this->setContinueEnumParameter( 'continue', $this->getContinueStr( $oldie ) ); + } break; } } - if ( !$fit ) + if ( !$fit ) { break; + } $skip = false; } - + $data = $this->getResultData(); foreach ( $data['query']['pages'] as $pageid => $arr ) { - if ( !isset( $arr['imagerepository'] ) ) + if ( !isset( $arr['imagerepository'] ) ) { $result->addValue( array( 'query', 'pages', $pageid ), 'imagerepository', '' ); - // The above can't fail because it doesn't increase the result size + } + // The above can't fail because it doesn't increase the result size } } } + /** + * From parameters, construct a 'scale' array + * @param $params Array: + * @return Array or Null: key-val array of 'width' and 'height', or null + */ + public function getScale( $params ) { + $p = $this->getModulePrefix(); + if ( $params['urlheight'] != -1 && $params['urlwidth'] == -1 ) { + $this->dieUsage( "${p}urlheight cannot be used without {$p}urlwidth", "{$p}urlwidth" ); + } + + if ( $params['urlwidth'] != -1 ) { + $scale = array(); + $scale['width'] = $params['urlwidth']; + $scale['height'] = $params['urlheight']; + } else { + $scale = null; + } + return $scale; + } + + /** * Get result information for an image revision - * @param File f The image - * @return array Result array + * + * @param $file File object + * @param $prop Array of properties to get (in the keys) + * @param $result ApiResult object + * @param $scale Array containing 'width' and 'height' items, or null + * @return Array: result array */ static function getInfo( $file, $prop, $result, $scale = null ) { $vals = array(); - if ( isset( $prop['timestamp'] ) ) + // Timestamp is shown even if the file is revdelete'd in interface + // so do same here. + if ( isset( $prop['timestamp'] ) ) { $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() ); - if ( isset( $prop['user'] ) ) { - $vals['user'] = $file->getUser(); - if ( !$file->getUser( 'id' ) ) - $vals['anon'] = ''; } + + $user = isset( $prop['user'] ); + $userid = isset( $prop['userid'] ); + + if ( $user || $userid ) { + if ( $file->isDeleted( File::DELETED_USER ) ) { + $vals['userhidden'] = ''; + } else { + if ( $user ) { + $vals['user'] = $file->getUser(); + } + if ( $userid ) { + $vals['userid'] = $file->getUser( 'id' ); + } + if ( !$file->getUser( 'id' ) ) { + $vals['anon'] = ''; + } + } + } + + // This is shown even if the file is revdelete'd in interface + // so do same here. if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) { $vals['size'] = intval( $file->getSize() ); $vals['width'] = intval( $file->getWidth() ); $vals['height'] = intval( $file->getHeight() ); + + $pageCount = $file->pageCount(); + if ( $pageCount !== false ) { + $vals['pagecount'] = $pageCount; + } + } + + $pcomment = isset( $prop['parsedcomment'] ); + $comment = isset( $prop['comment'] ); + + if ( $pcomment || $comment ) { + if ( $file->isDeleted( File::DELETED_COMMENT ) ) { + $vals['commenthidden'] = ''; + } else { + if ( $pcomment ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( + $file->getDescription(), $file->getTitle() ); + } + if ( $comment ) { + $vals['comment'] = $file->getDescription(); + } + } } - if ( isset( $prop['url'] ) ) { + + $url = isset( $prop['url'] ); + $sha1 = isset( $prop['sha1'] ); + $meta = isset( $prop['metadata'] ); + $mime = isset( $prop['mime'] ); + $archive = isset( $prop['archivename'] ); + $bitdepth = isset( $prop['bitdepth'] ); + + if ( ( $url || $sha1 || $meta || $mime || $archive || $bitdepth ) + && $file->isDeleted( File::DELETED_FILE ) ) { + $vals['filehidden'] = ''; + + //Early return, tidier than indenting all following things one level + return $vals; + } + + if ( $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'] = wfExpandUrl( $mto->getUrl() ); - $vals['thumbwidth'] = intval( $mto->getWidth() ); - $vals['thumbheight'] = intval( $mto->getHeight() ); + + // bug 23834 - If the URL's are the same, we haven't resized it, so shouldn't give the wanted + // thumbnail sizes for the thumbnail actual size + if ( $mto->getUrl() !== $file->getUrl() ) { + $vals['thumbwidth'] = intval( $mto->getWidth() ); + $vals['thumbheight'] = intval( $mto->getHeight() ); + } else { + $vals['thumbwidth'] = intval( $file->getWidth() ); + $vals['thumbheight'] = intval( $file->getHeight() ); + } + + if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) { + list( $ext, $mime ) = $file->getHandler()->getThumbType( + substr( $mto->getPath(), strrpos( $mto->getPath(), '.' ) + 1 ), + $file->getMimeType(), $thumbParams ); + $vals['thumbmime'] = $mime; + } + } else if ( $mto && $mto->isError() ) { + $vals['thumberror'] = $mto->toText(); } } $vals['url'] = $file->getFullURL(); $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl() ); } - if ( isset( $prop['comment'] ) ) - $vals['comment'] = $file->getDescription(); - if ( isset( $prop['sha1'] ) ) + + if ( $sha1 ) { $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 ); - if ( isset( $prop['metadata'] ) ) { + } + + if ( $meta ) { $metadata = $file->getMetadata(); $vals['metadata'] = $metadata ? self::processMetaData( unserialize( $metadata ), $result ) : null; } - if ( isset( $prop['mime'] ) ) + + if ( $mime ) { $vals['mime'] = $file->getMimeType(); - - if ( isset( $prop['archivename'] ) && $file->isOld() ) + } + + if ( $archive && $file->isOld() ) { $vals['archivename'] = $file->getArchiveName(); - - if ( isset( $prop['bitdepth'] ) ) + } + + if ( $bitdepth ) { $vals['bitdepth'] = $file->getBitDepth(); + } return $vals; } - - public static function processMetaData( $metadata, $result ) - { + + /* + * + * @param $metadata Array + * @param $result ApiResult + * @return Array + */ + 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 ) ) + if ( is_array( $value ) ) { $r['value'] = self::processMetaData( $value, $result ); - else + } else { $r['value'] = $value; + } $retval[] = $r; } } @@ -248,82 +363,104 @@ class ApiQueryImageInfo extends ApiQueryBase { return 'public'; } - private function getContinueStr( $img ) - { + private function getContinueStr( $img ) { return $img->getOriginalTitle()->getText() . '|' . $img->getTimestamp(); } public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'timestamp|user', - ApiBase :: PARAM_TYPE => self::getPropertyNames() + return array( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'timestamp|user', + ApiBase::PARAM_TYPE => self::getPropertyNames() ), 'limit' => array( - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_DFLT => 1, - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_DFLT => 1, + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), 'start' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'end' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'urlwidth' => array( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => - 1 + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_DFLT => -1 ), 'urlheight' => array( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => - 1 + ApiBase::PARAM_TYPE => 'integer', + 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', - ); + return array( + 'timestamp', + 'user', + 'userid', + 'comment', + 'parsedcomment', + 'url', + 'size', + 'dimensions', // For backwards compatibility with Allimages + 'sha1', + 'mime', + 'thumbmime', + 'metadata', + 'archivename', + 'bitdepth', + ); } + + /** + * Return the API documentation for the parameters. + * @return {Array} parameter documentation. + */ public function getParamDescription() { - return array ( - 'prop' => 'What image information to get.', + $p = $this->getModulePrefix(); + return array( + 'prop' => array( + 'What image information to get:', + ' timestamp - Adds timestamp for the uploaded version', + ' user - Adds the user who uploaded the image version', + ' userid - Add the user id that uploaded the image version', + ' comment - Comment on the version', + ' parsedcomment - Parse the comment on the version', + ' url - Gives URL to the image and the description page', + ' size - Adds the size of the image in bytes and the height and width', + ' dimensions - Alias for size', + ' sha1 - Adds sha1 hash for the image', + ' mime - Adds MIME of the image', + ' thumbmime - Adss MIME of the image thumbnail (requires url)', + ' metadata - Lists EXIF metadata for the version of the image', + ' archivename - Adds the file name of the archive version for non-latest versions', + ' bitdepth - Adds the bit depth of the version', + ), + 'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.", + 'Only the current version of the image can be scaled' ), + 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth", '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.' ), - 'urlheight' => 'Similar to iiurlwidth. Cannot be used without iiurlwidth', - 'continue' => 'When more results are available, use this to continue', + 'continue' => 'If the query response includes a continue value, use it here to get another page of results' ); } public function getDescription() { - return array ( - 'Returns image information and upload history' - ); + return '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' ), @@ -331,13 +468,13 @@ class ApiQueryImageInfo extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&titles=File:Albert%20Einstein%20Head.jpg&prop=imageinfo', 'api.php?action=query&titles=File:Test.jpg&prop=imageinfo&iilimit=50&iiend=20071231235959&iiprop=timestamp|user|url', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryImageInfo.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryImageInfo.php 85435 2011-04-05 14:00:08Z demon $'; } } diff --git a/includes/api/ApiQueryImages.php b/includes/api/ApiQueryImages.php index 65df94dc..af2920c7 100644 --- a/includes/api/ApiQueryImages.php +++ b/includes/api/ApiQueryImages.php @@ -1,11 +1,10 @@ @gmail.com + * Created on May 13, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryImages extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'im' ); + parent::__construct( $query, $moduleName, 'im' ); } public function execute() { @@ -48,12 +49,12 @@ class ApiQueryImages extends ApiQueryGeneratorBase { } 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' ) ); @@ -62,29 +63,32 @@ class ApiQueryImages extends ApiQueryGeneratorBase { $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" ); + 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->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 ) + if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) { $this->addOption( 'ORDER BY', 'il_to' ); - else + } else { $this->addOption( 'ORDER BY', 'il_from, il_to' ); + } $this->addOption( 'LIMIT', $params['limit'] + 1 ); - $db = $this->getDB(); $res = $this->select( __METHOD__ ); if ( is_null( $resultPageSet ) ) { $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -93,20 +97,18 @@ class ApiQueryImages extends ApiQueryGeneratorBase { break; } $vals = array(); - ApiQueryBase :: addTitleInfo( $vals, Title :: makeTitle( NS_FILE, $row->il_to ) ); + ApiQueryBase::addTitleInfo( $vals, Title::makeTitle( NS_FILE, $row->il_to ) ); $fit = $this->addPageSubItem( $row->il_from, $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $this->keyToTitle( $row->il_to ) ); break; } } } else { - $titles = array(); $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -114,12 +116,10 @@ class ApiQueryImages extends ApiQueryGeneratorBase { '|' . $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 ); } - - $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -128,18 +128,18 @@ class ApiQueryImages extends ApiQueryGeneratorBase { public function getAllowedParams() { return array( - 'limit' => array( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 - ), - 'continue' => null, + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ), + 'continue' => null, ); } - public function getParamDescription () { + public function getParamDescription() { return array( 'limit' => 'How many images to return', 'continue' => 'When more results are available, use this to continue', @@ -149,7 +149,7 @@ 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' ), @@ -157,15 +157,15 @@ class ApiQueryImages extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( - "Get a list of images used in the [[Main Page]]:", - " api.php?action=query&prop=images&titles=Main%20Page", - "Get information about all images used in the [[Main Page]]:", - " api.php?action=query&generator=images&titles=Main%20Page&prop=info" - ); + return array( + 'Get a list of images used in the [[Main Page]]:', + ' api.php?action=query&prop=images&titles=Main%20Page', + 'Get information about all images used in the [[Main Page]]:', + ' api.php?action=query&generator=images&titles=Main%20Page&prop=info' + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryImages.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryImages.php 73543 2010-09-22 16:50:09Z platonides $'; } } diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index b1c2963c..59f61de1 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 25, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -38,17 +39,23 @@ class ApiQueryInfo extends ApiQueryBase { private $fld_protection = false, $fld_talkid = false, $fld_subjectid = false, $fld_url = false, $fld_readable = false, $fld_watched = false, - $fld_preload = false; + $fld_preload = false, $fld_displaytitle = false; + + private $tokenFunctions; public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'in' ); + parent::__construct( $query, $moduleName, 'in' ); } public function requestExtraData( $pageSet ) { + global $wgDisableCounters; + $pageSet->requestField( 'page_restrictions' ); $pageSet->requestField( 'page_is_redirect' ); $pageSet->requestField( 'page_is_new' ); - $pageSet->requestField( 'page_counter' ); + if ( !$wgDisableCounters ) { + $pageSet->requestField( 'page_counter' ); + } $pageSet->requestField( 'page_touched' ); $pageSet->requestField( 'page_latest' ); $pageSet->requestField( 'page_len' ); @@ -62,12 +69,14 @@ 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( 'edit' => array( 'ApiQueryInfo', 'getEditToken' ), @@ -83,109 +92,115 @@ class ApiQueryInfo extends ApiQueryBase { 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' ) ) + 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 ); } - 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(); return $cachedImportToken; @@ -201,7 +216,8 @@ class ApiQueryInfo extends ApiQueryBase { $this->fld_subjectid = isset( $prop['subjectid'] ); $this->fld_url = isset( $prop['url'] ); $this->fld_readable = isset( $prop['readable'] ); - $this->fld_preload = isset ( $prop['preload'] ); + $this->fld_preload = isset( $prop['preload'] ); + $this->fld_displaytitle = isset( $prop['displaytitle'] ); } $pageSet = $this->getPageSet(); @@ -211,19 +227,19 @@ class ApiQueryInfo extends ApiQueryBase { $result = $this->getResult(); uasort( $this->everything, array( 'Title', 'compare' ) ); - if ( !is_null( $this->params['continue'] ) ) - { + 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" ); + 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 ) + foreach ( $this->everything as $pageid => $title ) { + if ( Title::compare( $title, $conttitle ) >= 0 ) { break; + } unset( $this->titles[$pageid] ); unset( $this->missing[$pageid] ); unset( $this->everything[$pageid] ); @@ -233,31 +249,41 @@ class ApiQueryInfo extends ApiQueryBase { $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' ); + + global $wgDisableCounters; + + if ( !$wgDisableCounters ) { + $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 ) + 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(); + } + + if ( $this->fld_displaytitle ) { + $this->getDisplayTitle(); + } foreach ( $this->everything as $pageid => $title ) { $pageInfo = $this->extractPageInfo( $pageid, $title ); - $fit = $result->addValue( array ( + $fit = $result->addValue( array( 'query', 'pages' ), $pageid, $pageInfo ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $title->getNamespace() . '|' . $title->getText() ); @@ -272,81 +298,99 @@ 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() ) { + global $wgDisableCounters; + $pageInfo['touched'] = wfTimestamp( TS_ISO_8601, $this->pageTouched[$pageid] ); $pageInfo['lastrevid'] = intval( $this->pageLatest[$pageid] ); - $pageInfo['counter'] = intval( $this->pageCounter[$pageid] ); + $pageInfo['counter'] = $wgDisableCounters + ? "" + : intval( $this->pageCounter[$pageid] ); $pageInfo['length'] = intval( $this->pageLength[$pageid] ); - if ( $this->pageIsRedir[$pageid] ) + + if ( $this->pageIsRedir[$pageid] ) { $pageInfo['redirect'] = ''; - if ( $this->pageIsNew[$pageid] ) + } + if ( $this->pageIsNew[$pageid] ) { $pageInfo['new'] = ''; + } } if ( !is_null( $this->params['token'] ) ) { $tokenFunctions = $this->getTokenFunctions(); $pageInfo['starttimestamp'] = wfTimestamp( TS_ISO_8601, time() ); - foreach ( $this->params['token'] as $t ) - { + foreach ( $this->params['token'] as $t ) { $val = call_user_func( $tokenFunctions[$t], $pageid, $title ); - if ( $val === false ) + if ( $val === false ) { $this->setWarning( "Action '$t' is not allowed for the current user" ); - else + } else { $pageInfo[$t . 'token'] = $val; + } } } 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' ); } - if ( $this->fld_watched && isset( $this->watched[$title->getNamespace()][$title->getDBkey()] ) ) + if ( $this->fld_watched && isset( $this->watched[$title->getNamespace()][$title->getDBkey()] ) ) { $pageInfo['watched'] = ''; - - if ( $this->fld_talkid && isset( $this->talkids[$title->getNamespace()][$title->getDBkey()] ) ) + } + + 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()] ) ) + 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' ); } - if ( $this->fld_readable && $title->userCanRead() ) + if ( $this->fld_readable && $title->userCanRead() ) { $pageInfo['readable'] = ''; - + } + if ( $this->fld_preload ) { - if ( $title->exists() ) + if ( $title->exists() ) { $pageInfo['preload'] = ''; - else { + } else { + $text = null; wfRunHooks( 'EditFormPreloadText', array( &$text, &$title ) ); - + $pageInfo['preload'] = $text; } } + + if ( $this->fld_displaytitle ) { + if ( isset( $this->displaytitles[$title->getArticleId()] ) ) { + $pageInfo['displaytitle'] = $this->displaytitles[$title->getArticleId()]; + } else { + $pageInfo['displaytitle'] = $title->getPrefixedText(); + } + } + return $pageInfo; } /** * Get information about protections and put it in $protections */ - private function getProtectionInfo() - { + private function getProtectionInfo() { $this->protections = array(); $db = $this->getDB(); // Get normal protections for existing titles - if ( count( $this->titles ) ) - { + if ( count( $this->titles ) ) { $this->resetQueryParams(); $this->addTables( array( 'page_restrictions', 'page' ) ); $this->addWhere( 'page_id=pr_page' ); @@ -356,14 +400,15 @@ class ApiQueryInfo extends ApiQueryBase { $this->addWhereFld( 'pr_page', array_keys( $this->titles ) ); $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { $a = array( 'type' => $row->pr_type, 'level' => $row->pr_level, '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 @@ -375,8 +420,9 @@ class ApiQueryInfo extends ApiQueryBase { // old old format should be treated as edit/move restriction $restriction = trim( $temp[0] ); - if ( $restriction == '' ) + if ( $restriction == '' ) { continue; + } $this->protections[$row->page_namespace][$row->page_title][] = array( 'type' => 'edit', 'level' => $restriction, @@ -389,8 +435,9 @@ class ApiQueryInfo extends ApiQueryBase { ); } else { $restriction = trim( $temp[1] ); - if ( $restriction == '' ) + if ( $restriction == '' ) { continue; + } $this->protections[$row->page_namespace][$row->page_title][] = array( 'type' => $temp[0], 'level' => $restriction, @@ -400,35 +447,34 @@ class ApiQueryInfo extends ApiQueryBase { } } } - $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 ) ) { + foreach ( $res as $row ) { $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 ) ); } - $db->freeResult( $res ); } // Cascading protections $images = $others = array(); - foreach ( $this->everything as $title ) - if ( $title->getNamespace() == NS_FILE ) + foreach ( $this->everything as $title ) { + if ( $title->getNamespace() == NS_FILE ) { $images[] = $title->getDBkey(); - else + } else { $others[] = $title; + } + } if ( count( $others ) ) { // Non-images: check templatelinks @@ -444,7 +490,7 @@ class ApiQueryInfo extends ApiQueryBase { $this->addWhereFld( 'pr_cascade', 1 ); $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { $source = Title::makeTitle( $row->page_namespace, $row->page_title ); $this->protections[$row->tl_namespace][$row->tl_title][] = array( 'type' => $row->pr_type, @@ -453,7 +499,6 @@ class ApiQueryInfo extends ApiQueryBase { 'source' => $source->getPrefixedText() ); } - $db->freeResult( $res ); } if ( count( $images ) ) { @@ -468,7 +513,7 @@ class ApiQueryInfo extends ApiQueryBase { $this->addWhereFld( 'il_to', $images ); $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { $source = Title::makeTitle( $row->page_namespace, $row->page_title ); $this->protections[NS_FILE][$row->il_to][] = array( 'type' => $row->pr_type, @@ -477,30 +522,30 @@ class ApiQueryInfo extends ApiQueryBase { 'source' => $source->getPrefixedText() ); } - $db->freeResult( $res ); } } /** * Get talk page IDs (if requested) and subject page IDs (if requested) - * and put them in $talkids and $subjectids + * and put them in $talkids and $subjectids */ - private function getTSIDs() - { + private function getTSIDs() { $getTitles = $this->talkids = $this->subjectids = array(); - $db = $this->getDB(); - foreach ( $this->everything as $t ) - { - if ( MWNamespace::isTalk( $t->getNamespace() ) ) - { - if ( $this->fld_subjectid ) + + foreach ( $this->everything as $t ) { + if ( MWNamespace::isTalk( $t->getNamespace() ) ) { + if ( $this->fld_subjectid ) { $getTitles[] = $t->getSubjectPage(); - } - else if ( $this->fld_talkid ) + } + } elseif ( $this->fld_talkid ) { $getTitles[] = $t->getTalkPage(); + } } - if ( !count( $getTitles ) ) + if ( !count( $getTitles ) ) { return; + } + + $db = $this->getDB(); // Construct a custom WHERE clause that matches // all titles in $getTitles @@ -510,46 +555,65 @@ class ApiQueryInfo extends ApiQueryBase { $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 ) ) + foreach ( $res as $row ) { + if ( MWNamespace::isTalk( $row->page_namespace ) ) { $this->talkids[MWNamespace::getSubject( $row->page_namespace )][$row->page_title] = intval( $row->page_id ); - else + } else { $this->subjectids[MWNamespace::getTalk( $row->page_namespace )][$row->page_title] = intval( $row->page_id ); + } + } + } + + private function getDisplayTitle() { + $this->displaytitles = array(); + + $pageIds = array_keys( $this->titles ); + + if ( !count( $pageIds ) ) { + return; + } + + $this->resetQueryParams(); + $this->addTables( 'page_props' ); + $this->addFields( array( 'pp_page', 'pp_value' ) ); + $this->addWhereFld( 'pp_page', $pageIds ); + $this->addWhereFld( 'pp_propname', 'displaytitle' ); + $res = $this->select( __METHOD__ ); + + foreach ( $res as $row ) { + $this->displaytitles[$row->pp_page] = $row->pp_value; } } /** * Get information about watched status and put it in $this->watched */ - private function getWatchedInfo() - { + private function getWatchedInfo() { global $wgUser; - if ( $wgUser->isAnon() || count( $this->titles ) == 0 ) + if ( $wgUser->isAnon() || count( $this->everything ) == 0 ) { return; + } $this->watched = array(); $db = $this->getDB(); - $lb = new LinkBatch( $this->titles ); + $lb = new LinkBatch( $this->everything ); $this->resetQueryParams(); - $this->addTables( array( 'page', 'watchlist' ) ); - $this->addFields( array( 'page_title', 'page_namespace' ) ); + $this->addTables( array( 'watchlist' ) ); + $this->addFields( array( 'wl_title', 'wl_namespace' ) ); $this->addWhere( array( - $lb->constructSet( 'page', $db ), - 'wl_namespace=page_namespace', - 'wl_title=page_title', + $lb->constructSet( 'wl', $db ), 'wl_user' => $wgUser->getID() ) ); $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { - $this->watched[$row->page_namespace][$row->page_title] = true; + foreach ( $res as $row ) { + $this->watched[$row->wl_namespace][$row->wl_title] = true; } } @@ -560,6 +624,7 @@ class ApiQueryInfo extends ApiQueryBase { 'subjectid', 'url', 'preload', + 'displaytitle', ); if ( !is_null( $params['prop'] ) ) { foreach ( $params['prop'] as $prop ) { @@ -575,33 +640,34 @@ class ApiQueryInfo extends ApiQueryBase { } public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_DFLT => null, - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + return array( + 'prop' => array( + 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 + 'preload', + 'displaytitle', + // If you add more properties here, please consider whether they // need to be added to getCacheMode() ) ), - 'token' => array ( - ApiBase :: PARAM_DFLT => null, - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ) + 'token' => array( + ApiBase::PARAM_DFLT => null, + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ) ), 'continue' => null, ); } public function getParamDescription() { - return array ( - 'prop' => array ( + return array( + 'prop' => array( 'Which additional properties to get:', ' protection - List the protection level of each page', ' talkid - The page ID of the talk page for each non-talk page', @@ -609,7 +675,8 @@ class ApiQueryInfo extends ApiQueryBase { ' 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' + ' preload - Gives the text returned by EditFormPreloadText', + ' displaytitle - Gives the way the page title is actually displayed', ), 'token' => 'Request a token to perform a data-modifying action on a page', 'continue' => 'When more results are available, use this to continue', @@ -619,7 +686,7 @@ 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' ), @@ -627,13 +694,13 @@ class ApiQueryInfo extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&prop=info&titles=Main%20Page', 'api.php?action=query&prop=info&inprop=protection&titles=Main%20Page' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryInfo.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryInfo.php 78439 2010-12-15 14:23:46Z catrope $'; } } diff --git a/includes/api/ApiQueryLangLinks.php b/includes/api/ApiQueryLangLinks.php index 9330e380..c2ecbfee 100644 --- a/includes/api/ApiQueryLangLinks.php +++ b/includes/api/ApiQueryLangLinks.php @@ -1,11 +1,10 @@ @gmail.com + * Created on May 13, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -36,15 +37,16 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryLangLinks extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'll' ); + parent::__construct( $query, $moduleName, 'll' ); } public function execute() { - if ( $this->getPageSet()->getGoodTitleCount() == 0 ) + if ( $this->getPageSet()->getGoodTitleCount() == 0 ) { return; + } $params = $this->extractRequestParams(); - $this->addFields( array ( + $this->addFields( array( 'll_from', 'll_lang', 'll_title' @@ -54,27 +56,30 @@ class ApiQueryLangLinks extends ApiQueryBase { $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" ); + 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->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 ) + if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) { $this->addOption( 'ORDER BY', 'll_lang' ); - else + } else { $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 ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -82,15 +87,19 @@ class ApiQueryLangLinks extends ApiQueryBase { break; } $entry = array( 'lang' => $row->ll_lang ); - ApiResult :: setContent( $entry, $row->ll_title ); + if ( $params['url'] ) { + $title = Title::newFromText( "{$row->ll_lang}:{$row->ll_title}" ); + if ( $title ) { + $entry['url'] = $title->getFullURL(); + } + } + ApiResult::setContent( $entry, $row->ll_title ); $fit = $this->addPageSubItem( $row->ll_from, $entry ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', "{$row->ll_from}|{$row->ll_lang}" ); break; } } - $db->freeResult( $res ); } public function getCacheMode( $params ) { @@ -99,28 +108,30 @@ class ApiQueryLangLinks extends ApiQueryBase { public function getAllowedParams() { return array( - 'limit' => array( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 - ), - 'continue' => null, + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ), + 'continue' => null, + 'url' => false, ); } - public function getParamDescription () { + public function getParamDescription() { return array( 'limit' => 'How many langlinks to return', 'continue' => 'When more results are available, use this to continue', + 'url' => 'Whether to get the full URL', ); } 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' ), @@ -128,13 +139,13 @@ class ApiQueryLangLinks extends ApiQueryBase { } protected function getExamples() { - return array ( - "Get interlanguage links from the [[Main Page]]:", - " api.php?action=query&prop=langlinks&titles=Main%20Page&redirects", - ); + return array( + 'Get interlanguage links from the [[Main Page]]:', + ' api.php?action=query&prop=langlinks&titles=Main%20Page&redirects=', + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryLangLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryLangLinks.php 77660 2010-12-03 14:44:07Z catrope $'; } } diff --git a/includes/api/ApiQueryLinks.php b/includes/api/ApiQueryLinks.php index 52dfd591..4f3bad3b 100644 --- a/includes/api/ApiQueryLinks.php +++ b/includes/api/ApiQueryLinks.php @@ -1,11 +1,10 @@ @gmail.com + * Created on May 12, 2007 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiQueryBase.php" ); + require_once( "ApiQueryBase.php" ); } /** @@ -41,23 +42,24 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { private $table, $prefix, $description; public function __construct( $query, $moduleName ) { - switch ( $moduleName ) { - case self::LINKS : + case self::LINKS: $this->table = 'pagelinks'; $this->prefix = 'pl'; $this->description = 'link'; + $this->titlesParam = 'titles'; break; - case self::TEMPLATES : + case self::TEMPLATES: $this->table = 'templatelinks'; $this->prefix = 'tl'; $this->description = 'template'; + $this->titlesParam = 'templates'; break; - default : - ApiBase :: dieDebug( __METHOD__, 'Unknown module name' ); + default: + ApiBase::dieDebug( __METHOD__, 'Unknown module name' ); } - parent :: __construct( $query, $moduleName, $this->prefix ); + parent::__construct( $query, $moduleName, $this->prefix ); } public function execute() { @@ -73,13 +75,13 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { } 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' @@ -89,19 +91,38 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { $this->addWhereFld( $this->prefix . '_from', array_keys( $this->getPageSet()->getGoodTitles() ) ); $this->addWhereFld( $this->prefix . '_namespace', $params['namespace'] ); + if ( !is_null( $params[$this->titlesParam] ) ) { + $lb = new LinkBatch; + foreach ( $params[$this->titlesParam] as $t ) { + $title = Title::newFromText( $t ); + if ( !$title ) { + $this->setWarning( "``$t'' is not a valid title" ); + } else { + $lb->addObj( $title ); + } + } + $cond = $lb->constructSet( $this->prefix, $this->getDB() ); + if ( $cond ) { + $this->addWhere( $cond ); + } + } + 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" ); + 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->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' @@ -110,22 +131,23 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { // 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( 'ORDER BY', implode( ', ', $order ) ); $this->addOption( 'USE INDEX', "{$this->prefix}_from" ); $this->addOption( 'LIMIT', $params['limit'] + 1 ); - $db = $this->getDB(); $res = $this->select( __METHOD__ ); if ( is_null( $resultPageSet ) ) { $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -135,10 +157,9 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { break; } $vals = array(); - ApiQueryBase :: addTitleInfo( $vals, Title :: makeTitle( $row->pl_namespace, $row->pl_title ) ); + ApiQueryBase::addTitleInfo( $vals, Title::makeTitle( $row->pl_namespace, $row->pl_title ) ); $fit = $this->addPageSubItem( $row->pl_from, $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', "{$row->pl_from}|{$row->pl_namespace}|" . $this->keyToTitle( $row->pl_title ) ); @@ -148,7 +169,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { } else { $titles = array(); $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... @@ -157,39 +178,45 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { $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 ); } - - $db->freeResult( $res ); } - public function getAllowedParams() - { + public function getAllowedParams() { return array( - 'namespace' => array( - ApiBase :: PARAM_TYPE => 'namespace', - ApiBase :: PARAM_ISMULTI => true - ), - 'limit' => array( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 - ), - 'continue' => null, - ); + 'namespace' => array( + ApiBase::PARAM_TYPE => 'namespace', + ApiBase::PARAM_ISMULTI => true + ), + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ), + 'continue' => null, + $this->titlesParam => array( + ApiBase::PARAM_ISMULTI => true, + ), + ); } - public function getParamDescription() - { - return array( - 'namespace' => "Show {$this->description}s in this namespace(s) only", - 'limit' => "How many {$this->description}s to return", - 'continue' => 'When more results are available, use this to continue', + public function getParamDescription() { + $desc = $this->description; + $arr = array( + 'namespace' => "Show {$desc}s in this namespace(s) only", + 'limit' => "How many {$desc}s to return", + 'continue' => 'When more results are available, use this to continue', ); + if ( $this->getModuleName() == self::LINKS ) { + $arr[$this->titlesParam] = 'Only list links to these titles. Useful for checking whether a certain page links to a certain title.'; + } else if ( $this->getModuleName() == self::TEMPLATES ) { + $arr[$this->titlesParam] = 'Only list these templates. Useful for checking whether a certain page uses a certain template.'; + } + return $arr; } public function getDescription() { @@ -197,17 +224,17 @@ class ApiQueryLinks extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( - "Get {$this->description}s from the [[Main Page]]:", - " api.php?action=query&prop={$this->getModuleName()}&titles=Main%20Page", - "Get information about the {$this->description} pages in the [[Main Page]]:", - " api.php?action=query&generator={$this->getModuleName()}&titles=Main%20Page&prop=info", - "Get {$this->description}s from the Main Page in the User and Template namespaces:", - " api.php?action=query&prop={$this->getModuleName()}&titles=Main%20Page&{$this->prefix}namespace=2|10" - ); + return array( + "Get {$this->description}s from the [[Main Page]]:", + " api.php?action=query&prop={$this->getModuleName()}&titles=Main%20Page", + "Get information about the {$this->description} pages in the [[Main Page]]:", + " api.php?action=query&generator={$this->getModuleName()}&titles=Main%20Page&prop=info", + "Get {$this->description}s from the Main Page in the User and Template namespaces:", + " api.php?action=query&prop={$this->getModuleName()}&titles=Main%20Page&{$this->prefix}namespace=2|10" + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryLinks.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryLinks.php 70647 2010-08-07 19:59:42Z ialex $'; } } diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php index bdeee952..7d69ca39 100644 --- a/includes/api/ApiQueryLogEvents.php +++ b/includes/api/ApiQueryLogEvents.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 16, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -36,30 +37,36 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryLogEvents extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'le' ); + parent::__construct( $query, $moduleName, 'le' ); } + private $fld_ids = false, $fld_title = false, $fld_type = false, + $fld_action = false, $fld_user = false, $fld_userid = false, + $fld_timestamp = false, $fld_comment = false, $fld_parsedcomment = false, + $fld_details = false, $fld_tags = false; + public function execute() { $params = $this->extractRequestParams(); $db = $this->getDB(); - + $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_action = isset ( $prop['action'] ); $this->fld_user = isset( $prop['user'] ); + $this->fld_userid = isset( $prop['userid'] ); $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 ) + if ( $hideLogs !== false ) { $this->addWhere( $hideLogs ); + } // Order is significant here $this->addTables( array( 'logging', 'user', 'page' ) ); @@ -72,7 +79,7 @@ class ApiQueryLogEvents extends ApiQueryBase { 'log_title=page_title' ) ) ) ); $index = array( 'logging' => 'times' ); // default, may change - $this->addFields( array ( + $this->addFields( array( 'log_type', 'log_action', 'log_timestamp', @@ -83,30 +90,36 @@ class ApiQueryLogEvents extends ApiQueryBase { $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( 'user_id', $this->fld_userid ); + $this->addFieldsIf( 'log_namespace', $this->fld_title || $this->fld_parsedcomment ); + $this->addFieldsIf( 'log_title', $this->fld_title || $this->fld_parsedcomment ); $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'; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; } - - if ( !is_null( $params['type'] ) ) { + + if ( !is_null( $params['action'] ) ) { + list( $type, $action ) = explode( '/', $params['action'] ); + $this->addWhereFld( 'log_type', $type ); + $this->addWhereFld( 'log_action', $action ); + } + else 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'] ); $limit = $params['limit']; @@ -115,17 +128,19 @@ class ApiQueryLogEvents extends ApiQueryBase { $user = $params['user']; if ( !is_null( $user ) ) { $userid = User::idFromName( $user ); - if ( !$userid ) + 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 ) ) + $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() ); @@ -145,7 +160,7 @@ class ApiQueryLogEvents extends ApiQueryBase { $count = 0; $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->log_timestamp ) ); @@ -153,33 +168,40 @@ class ApiQueryLogEvents extends ApiQueryBase { } $vals = $this->extractRowInfo( $row ); - if ( !$vals ) + if ( !$vals ) { continue; + } $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->log_timestamp ) ); break; } } - $db->freeResult( $res ); - $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' ); } - + + /** + * @static + * @param $result ApiResult + * @param $vals + * @param $params + * @param $type + * @param $ts + * @return array + */ 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 ( 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; @@ -199,8 +221,12 @@ class ApiQueryLogEvents extends ApiQueryBase { case 'block': $vals2 = array(); list( $vals2['duration'], $vals2['flags'] ) = $params; - $vals2['expiry'] = wfTimestamp( TS_ISO_8601, + + // Indefinite blocks have no expiry time + if ( Block::parseExpiryInput( $params[0] ) !== Block::infinity() ) { + $vals2['expiry'] = wfTimestamp( TS_ISO_8601, strtotime( $params[0], wfTimestamp( TS_UNIX, $ts ) ) ); + } $vals[$type] = $vals2; $params = null; break; @@ -220,7 +246,9 @@ class ApiQueryLogEvents extends ApiQueryBase { $vals['pageid'] = intval( $row->page_id ); } - $title = Title::makeTitle( $row->log_namespace, $row->log_title ); + if ( $this->fld_title || $this->fld_parsedcomment ) { + $title = Title::makeTitle( $row->log_namespace, $row->log_title ); + } if ( $this->fld_title ) { if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) { @@ -230,7 +258,7 @@ class ApiQueryLogEvents extends ApiQueryBase { } } - if ( $this->fld_type ) { + if ( $this->fld_type || $this->fld_action ) { $vals['type'] = $row->log_type; $vals['action'] = $row->log_action; } @@ -239,32 +267,42 @@ class ApiQueryLogEvents extends ApiQueryBase { 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 ( $this->fld_user || $this->fld_userid ) { if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) { $vals['userhidden'] = ''; } else { - $vals['user'] = $row->user_name; - if ( !$row->log_user ) + if ( $this->fld_user ) { + $vals['user'] = $row->user_name; + } + if ( $this->fld_userid ) { + $vals['userid'] = $row->user_id; + } + + if ( !$row->log_user ) { $vals['anon'] = ''; + } } } if ( $this->fld_timestamp ) { $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->log_timestamp ); } - + if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->log_comment ) ) { if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) { $vals['commenthidden'] = ''; } else { - if ( $this->fld_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 ); @@ -281,10 +319,10 @@ class ApiQueryLogEvents extends ApiQueryBase { $vals['tags'] = array(); } } - + return $vals; } - + public function getCacheMode( $params ) { if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { // formatComment() calls wfMsg() among other things @@ -295,16 +333,17 @@ class ApiQueryLogEvents extends ApiQueryBase { } public function getAllowedParams() { - global $wgLogTypes; - return array ( - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'ids|title|type|user|timestamp|comment|details', - ApiBase :: PARAM_TYPE => array ( + global $wgLogTypes, $wgLogActions; + return array( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'ids|title|type|user|timestamp|comment|details', + ApiBase::PARAM_TYPE => array( 'ids', 'title', 'type', 'user', + 'userid', 'timestamp', 'comment', 'parsedcomment', @@ -312,18 +351,21 @@ class ApiQueryLogEvents extends ApiQueryBase { 'tags' ) ), - 'type' => array ( - ApiBase :: PARAM_TYPE => $wgLogTypes + 'type' => array( + ApiBase::PARAM_TYPE => $wgLogTypes + ), + 'action' => array( + ApiBase::PARAM_TYPE => array_keys( $wgLogActions ) ), - 'start' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'start' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'end' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'end' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'older', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'older', + ApiBase::PARAM_TYPE => array( 'newer', 'older' ) @@ -331,34 +373,47 @@ class ApiQueryLogEvents extends ApiQueryBase { 'user' => null, 'title' => null, 'tag' => null, - 'limit' => array ( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ) ); } public function getParamDescription() { - return array ( - 'prop' => 'Which properties to get', + return array( + 'prop' => array( + 'Which properties to get', + ' ids - Adds the id of the log event', + ' title - Adds the title of the page for the log event', + ' type - Adds the type of log event', + ' user - Adds the user responsible for the log event', + ' userid - Adds the user id who was responsible for the log event', + ' timestamp - Adds the timestamp for the event', + ' comment - Adds the comment of the event', + ' parsedcomment - Adds the parsed comment of the event', + ' details - Lists addtional details about the event', + ' tags - Lists tags for the event', + ), 'type' => 'Filter log entries to only this type(s)', - 'start' => 'The timestamp to start enumerating from.', - 'end' => 'The timestamp to end enumerating.', - 'dir' => 'In which direction to enumerate.', - 'user' => 'Filter entries to those made by the given user.', - 'title' => 'Filter entries to those related to a page.', - 'limit' => 'How many total event entries to return.', - 'tag' => 'Only list event entries tagged with this tag.', + 'action' => "Filter log actions to only this type. Overrides {$this->getModulePrefix()}type", + 'start' => 'The timestamp to start enumerating from', + 'end' => 'The timestamp to end enumerating', + 'dir' => 'In which direction to enumerate', + 'user' => 'Filter entries to those made by the given user', + 'title' => 'Filter entries to those related to a page', + 'limit' => 'How many total event entries to return', + 'tag' => 'Only list event entries tagged with this tag', ); } public function getDescription() { - return 'Get events from logs.'; + return 'Get events from logs'; } - + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'code' => 'param_user', 'info' => 'User name $user not found' ), @@ -367,12 +422,12 @@ class ApiQueryLogEvents extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=logevents' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryLogEvents.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryLogEvents.php 74535 2010-10-09 00:01:45Z reedy $'; } } diff --git a/includes/api/ApiQueryPageProps.php b/includes/api/ApiQueryPageProps.php new file mode 100644 index 00000000..894e812d --- /dev/null +++ b/includes/api/ApiQueryPageProps.php @@ -0,0 +1,150 @@ +params = $this->extractRequestParams(); + + # Only operate on existing pages + $pages = $this->getPageSet()->getGoodTitles(); + if ( !count( $pages ) ) { + # Nothing to do + return; + } + + $this->addTables( 'page_props' ); + $this->addFields( array( 'pp_page', 'pp_propname', 'pp_value' ) ); + $this->addWhereFld( 'pp_page', array_keys( $pages ) ); + + if ( $this->params['continue'] ) { + $this->addWhere( 'pp_page >=' . intval( $this->params['continue'] ) ); + } + + # Force a sort order to ensure that properties are grouped by page + $this->addOption( 'ORDER BY', 'pp_page' ); + + $res = $this->select( __METHOD__ ); + $currentPage = 0; # Id of the page currently processed + $props = array(); + $result = $this->getResult(); + + foreach ( $res as $row ) { + if ( $currentPage != $row->pp_page ) { + # Different page than previous row, so add the properties to + # the result and save the new page id + + if ( $currentPage ) { + if ( !$this->addPageProps( $result, $currentPage, $props ) ) { + # addPageProps() indicated that the result did not fit + # so stop adding data. Reset props so that it doesn't + # get added again after loop exit + + $props = array(); + break; + } + + $props = array(); + } + + $currentPage = $row->pp_page; + } + + $props[$row->pp_propname] = $row->pp_value; + } + + if ( count( $props ) ) { + # Add any remaining properties to the results + $this->addPageProps( $result, $currentPage, $props ); + } + } + + /** + * Add page properties to an ApiResult, adding a continue + * parameter if it doesn't fit. + * + * @param $result ApiResult + * @param $page int + * @param $props array + * @return bool True if it fits in the result + */ + private function addPageProps( $result, $page, $props ) { + $fit = $result->addValue( array( 'query', 'pages', $page ), 'pageprops', $props ); + + if ( !$fit ) { + $this->setContinueEnumParameter( 'continue', $page ); + } + return $fit; + } + + public function getCacheMode( $params ) { + return 'public'; + } + + public function getAllowedParams() { + return array( 'continue' => null ); + } + + public function getParamDescription() { + return array( 'continue' => 'When more results are available, use this to continue' ); + } + + public function getDescription() { + return 'Get various properties defined in the page content'; + } + + 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&prop=pageprops&titles=Category:Foo', + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiQueryPageProps.php 85211 2011-04-02 21:01:00Z demon $'; + } +} diff --git a/includes/api/ApiQueryProtectedTitles.php b/includes/api/ApiQueryProtectedTitles.php index ab794805..e647c39f 100644 --- a/includes/api/ApiQueryProtectedTitles.php +++ b/includes/api/ApiQueryProtectedTitles.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Feb 13, 2009 + * + * Copyright © 2009 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'pt' ); + parent::__construct( $query, $moduleName, 'pt' ); } public function execute() { @@ -48,14 +49,13 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { } private function run( $resultPageSet = null ) { - $db = $this->getDB(); $params = $this->extractRequestParams(); $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_user', isset( $prop['user'] ) || isset( $prop['userid'] ) ); $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'] ) ); @@ -63,9 +63,8 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { $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', @@ -78,7 +77,7 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { $count = 0; $result = $this->getResult(); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->pt_timestamp ) ); @@ -89,26 +88,35 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { if ( is_null( $resultPageSet ) ) { $vals = array(); ApiQueryBase::addTitleInfo( $vals, $title ); - if ( isset( $prop['timestamp'] ) ) + if ( isset( $prop['timestamp'] ) ) { $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->pt_timestamp ); - - if ( isset( $prop['user'] ) && !is_null( $row->user_name ) ) + } + + if ( isset( $prop['user'] ) && !is_null( $row->user_name ) ) { $vals['user'] = $row->user_name; - - if ( isset( $prop['comment'] ) ) + } + + if ( isset( $prop['user'] ) ) { + $vals['userid'] = $row->pt_user; + } + + if ( isset( $prop['comment'] ) ) { $vals['comment'] = $row->pt_reason; - + } + if ( isset( $prop['parsedcomment'] ) ) { global $wgUser; $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->pt_reason, $title ); } - - if ( isset( $prop['expiry'] ) ) + + if ( isset( $prop['expiry'] ) ) { $vals['expiry'] = Block::decodeExpiry( $row->pt_expiry, TS_ISO_8601 ); - - if ( isset( $prop['level'] ) ) + } + + if ( isset( $prop['level'] ) ) { $vals['level'] = $row->pt_create_perm; - + } + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); if ( !$fit ) { $this->setContinueEnumParameter( 'start', @@ -119,11 +127,12 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { $titles[] = $title; } } - $db->freeResult( $res ); - if ( is_null( $resultPageSet ) ) + + if ( is_null( $resultPageSet ) ) { $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), $this->getModulePrefix() ); - else + } else { $resultPageSet->populateFromTitles( $titles ); + } } public function getCacheMode( $params ) { @@ -137,41 +146,42 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { public function getAllowedParams() { global $wgRestrictionLevels; - return array ( - 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace', + return array( + 'namespace' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace', ), 'level' => array( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array_diff( $wgRestrictionLevels, array( '' ) ) + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array_diff( $wgRestrictionLevels, 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 + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'older', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'older', + ApiBase::PARAM_TYPE => array( 'older', 'newer' ) ), 'start' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'end' => array( - ApiBase :: PARAM_TYPE => 'timestamp' + ApiBase::PARAM_TYPE => 'timestamp' ), 'prop' => array( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'timestamp|level', - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'timestamp|level', + ApiBase::PARAM_TYPE => array( 'timestamp', 'user', + 'userid', 'comment', 'parsedcomment', 'expiry', @@ -182,13 +192,22 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { } public function getParamDescription() { - return array ( + return array( 'namespace' => 'Only list titles in these namespaces', 'start' => 'Start listing at this protection timestamp', 'end' => 'Stop listing at this protection timestamp', 'dir' => 'The direction in which to list', - 'limit' => 'How many total pages to return.', - 'prop' => 'Which properties to get', + 'limit' => 'How many total pages to return', + 'prop' => array( + 'Which properties to get', + ' timestamp - Adds the timestamp of when protection was added', + ' user - Adds the user to add the protection', + ' userid - Adds the user id to add the protection', + ' comment - Adds the comment for the protection', + ' parsedcomment - Adds the parsed comment for the protection', + ' expiry - Adds the timestamp of when the protection will be lifted', + ' level - Adds the protection level', + ), 'level' => 'Only list titles with these protection levels', ); } @@ -198,12 +217,12 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=protectedtitles', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryProtectedTitles.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryProtectedTitles.php 71838 2010-08-28 01:18:18Z reedy $'; } } diff --git a/includes/api/ApiQueryRandom.php b/includes/api/ApiQueryRandom.php index 10796810..b3b840fd 100644 --- a/includes/api/ApiQueryRandom.php +++ b/includes/api/ApiQueryRandom.php @@ -1,11 +1,11 @@ addWhereRange( 'page_random', 'newer', $randstr, null ); $this->addWhereFld( 'page_is_redirect', $redirect ); $this->addOption( 'USE INDEX', 'page_random' ); - if ( is_null( $resultPageSet ) ) + if ( is_null( $resultPageSet ) ) { $this->addFields( array( 'page_id', 'page_title', 'page_namespace' ) ); - else + } else { $this->addFields( $resultPageSet->getPageTableFields() ); + } } protected function runQuery( &$resultPageSet ) { - $db = $this->getDB(); $res = $this->select( __METHOD__ ); $count = 0; - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { $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 ) + 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 + } else { $resultPageSet->processDbRow( $row ); + } } - $db->freeResult( $res ); + return $count; } @@ -95,17 +96,16 @@ if ( !defined( 'MEDIAWIKI' ) ) { $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'] ) - { + 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 ) ) { @@ -126,24 +126,24 @@ if ( !defined( 'MEDIAWIKI' ) ) { } public function getAllowedParams() { - return array ( + return array( 'namespace' => array( - ApiBase :: PARAM_TYPE => 'namespace', - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_TYPE => 'namespace', + ApiBase::PARAM_ISMULTI => true ), - 'limit' => array ( - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_DFLT => 1, - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => 10, - ApiBase :: PARAM_MAX2 => 20 + 'limit' => array( + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_DFLT => 1, + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => 10, + ApiBase::PARAM_MAX2 => 20 ), 'redirect' => false, ); } public function getParamDescription() { - return array ( + return array( 'namespace' => 'Return pages in these namespaces only', 'limit' => 'Limit how many random pages will be returned', 'redirect' => 'Load a random redirect instead of a random page' @@ -151,10 +151,11 @@ if ( !defined( 'MEDIAWIKI' ) ) { } public function getDescription() { - return array( 'Get a set of random pages', - 'NOTE: Pages are listed in a fixed sequence, only the starting point is random. This means that if, for example, "Main Page" is the first ', - ' random page on your list, "List of fictional monkeys" will *always* be second, "List of people on stamps of Vanuatu" third, etc.', - 'NOTE: If the number of pages in the namespace is lower than rnlimit, you will get fewer pages. You will not get the same page twice.' + return array( + 'Get a set of random pages', + 'NOTE: Pages are listed in a fixed sequence, only the starting point is random. This means that if, for example, "Main Page" is the first ', + ' random page on your list, "List of fictional monkeys" will *always* be second, "List of people on stamps of Vanuatu" third, etc', + 'NOTE: If the number of pages in the namespace is lower than rnlimit, you will get fewer pages. You will not get the same page twice' ); } diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php index 1f0de3be..fb0d42b8 100644 --- a/includes/api/ApiQueryRecentChanges.php +++ b/includes/api/ApiQueryRecentChanges.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 19, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -37,12 +38,15 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryRecentChanges extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'rc' ); + parent::__construct( $query, $moduleName, 'rc' ); } - 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; + private $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false, + $fld_flags = false, $fld_timestamp = false, $fld_title = false, $fld_ids = false, + $fld_sizes = false, $fld_redirect = false, $fld_patrolled = false, $fld_loginfo = false, $fld_tags = false; + + private $tokenFunctions; + /** * Get an array mapping token names to their handler functions. * The prototype for a token function is func($pageid, $title, $rc) @@ -51,12 +55,14 @@ class ApiQueryRecentChanges 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( 'patrol' => array( 'ApiQueryRecentChanges', 'getPatrolToken' ) @@ -64,36 +70,38 @@ class ApiQueryRecentChanges extends ApiQueryBase { 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() || - $rc->getAttribute( 'rc_type' ) != RC_NEW ) ) + $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 ) ) - return $cachedPatrolToken; - - $cachedPatrolToken = $wgUser->editToken(); + if ( is_null( $cachedPatrolToken ) ) { + $cachedPatrolToken = $wgUser->editToken( 'patrol' ); + } + return $cachedPatrolToken; } /** * Sets internal state to include the desired properties in the output. - * @param $prop associative array of properties, only keys are used here + * @param $prop Array 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_comment = isset( $prop['comment'] ); + $this->fld_parsedcomment = isset( $prop['parsedcomment'] ); + $this->fld_user = isset( $prop['user'] ); + $this->fld_userid = isset( $prop['userid'] ); + $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'] ); @@ -104,6 +112,7 @@ class ApiQueryRecentChanges extends ApiQueryBase { * Generates and outputs the result of this query based upon the provided parameters. */ public function execute() { + global $wgUser; /* Get the parameters of the request. */ $params = $this->extractRequestParams(); @@ -112,73 +121,78 @@ class ApiQueryRecentChanges extends ApiQueryBase { * AND rc_timestamp < $end AND rc_namespace = $namespace * AND rc_deleted = '0' */ - $db = $this->getDB(); $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'] ); /* 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->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'] ) ) { + if ( !$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_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( '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'] ) ); } - - if ( !is_null( $params['user'] ) && !is_null( $param['excludeuser'] ) ) + + 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'] ) ) - { + } + + if ( !is_null( $params['user'] ) ) { $this->addWhereFld( 'rc_user_text', $params['user'] ); $index['recentchanges'] = 'rc_user_text'; } - - if ( !is_null( $params['excludeuser'] ) ) + + 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 ( + $this->addFields( array( 'rc_timestamp', 'rc_namespace', 'rc_title', 'rc_cur_id', 'rc_type', 'rc_moved_to_ns', - 'rc_moved_to_title' + 'rc_moved_to_title', + 'rc_deleted' ) ); /* Determine what properties we need to display. */ @@ -188,9 +202,9 @@ class ApiQueryRecentChanges extends ApiQueryBase { /* Set up internal members based upon params. */ $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 ); @@ -198,7 +212,7 @@ class ApiQueryRecentChanges extends ApiQueryBase { $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_user_text', $this->fld_user || $this->fld_userid ); $this->addFieldsIf( 'rc_minor', $this->fld_flags ); $this->addFieldsIf( 'rc_bot', $this->fld_flags ); $this->addFieldsIf( 'rc_new', $this->fld_flags ); @@ -209,39 +223,37 @@ class ApiQueryRecentChanges extends ApiQueryBase { $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'] ) ) - { + 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' ); } } - + 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'; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; } - + $this->token = $params['token']; $this->addOption( 'LIMIT', $params['limit'] + 1 ); $this->addOption( 'USE INDEX', $index ); $count = 0; /* Perform the actual query. */ - $db = $this->getDB(); $res = $this->select( __METHOD__ ); /* Iterate through the rows, adding data extracted from them to our query result. */ - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); @@ -252,18 +264,16 @@ class ApiQueryRecentChanges extends ApiQueryBase { $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 ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); break; } } - $db->freeResult( $res ); - /* Format the result */ $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'rc' ); } @@ -279,15 +289,17 @@ class ApiQueryRecentChanges extends ApiQueryBase { /* 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 ); + { + $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 (); + $vals = array(); - $type = intval ( $row->rc_type ); + $type = intval( $row->rc_type ); /* Determine what kind of change this was. */ switch ( $type ) { @@ -312,9 +324,10 @@ class ApiQueryRecentChanges extends ApiQueryBase { /* 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_" ); + 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. */ @@ -326,20 +339,32 @@ class ApiQueryRecentChanges extends ApiQueryBase { } /* Add user data and 'anon' flag, if use is anonymous. */ - if ( $this->fld_user ) { - $vals['user'] = $row->rc_user_text; - if ( !$row->rc_user ) + if ( $this->fld_user || $this->fld_userid ) { + + if ( $this->fld_user ) { + $vals['user'] = $row->rc_user_text; + } + + if ( $this->fld_userid ) { + $vals['userid'] = $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 ( $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+) */ @@ -349,35 +374,42 @@ class ApiQueryRecentChanges extends ApiQueryBase { } /* Add the timestamp. */ - if ( $this->fld_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 ); $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 ( $this->fld_tags ) { if ( $row->ts_tags ) { $tags = explode( ',', $row->ts_tags ); @@ -387,39 +419,39 @@ class ApiQueryRecentChanges extends ApiQueryBase { $vals['tags'] = array(); } } - - if ( !is_null( $this->token ) ) - { + + 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 ) + if ( $val === false ) { $this->setWarning( "Action '$t' is not allowed for the current user" ); - else + } else { $vals[$t . 'token'] = $val; + } } } return $vals; } - private function parseRCType( $type ) - { - if ( is_array( $type ) ) - { - $retval = array(); - foreach ( $type as $t ) - $retval[] = $this->parseRCType( $t ); - return $retval; - } - switch( $type ) - { - case 'edit': return RC_EDIT; - case 'new': return RC_NEW; - case 'log': return RC_LOG; + private function parseRCType( $type ) { + if ( is_array( $type ) ) { + $retval = array(); + foreach ( $type as $t ) { + $retval[] = $this->parseRCType( $t ); } + return $retval; + } + switch( $type ) { + case 'edit': + return RC_EDIT; + case 'new': + return RC_NEW; + case 'log': + return RC_LOG; + } } public function getCacheMode( $params ) { @@ -441,36 +473,37 @@ class ApiQueryRecentChanges extends ApiQueryBase { } public function getAllowedParams() { - return array ( - 'start' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + return array( + 'start' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'end' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'end' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'older', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'older', + ApiBase::PARAM_TYPE => array( 'newer', 'older' ) ), - 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace' + 'namespace' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace' ), 'user' => array( - ApiBase :: PARAM_TYPE => 'user' + ApiBase::PARAM_TYPE => 'user' ), 'excludeuser' => array( - ApiBase :: PARAM_TYPE => 'user' + ApiBase::PARAM_TYPE => 'user' ), 'tag' => null, - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'title|timestamp|ids', - ApiBase :: PARAM_TYPE => array ( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'title|timestamp|ids', + ApiBase::PARAM_TYPE => array( 'user', + 'userid', 'comment', 'parsedcomment', 'flags', @@ -485,12 +518,12 @@ class ApiQueryRecentChanges extends ApiQueryBase { ) ), 'token' => array( - ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ), - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ), + ApiBase::PARAM_ISMULTI => true ), - 'show' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + 'show' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'minor', '!minor', 'bot', @@ -503,16 +536,16 @@ class ApiQueryRecentChanges extends ApiQueryBase { '!patrolled' ) ), - '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 + '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 ), - 'type' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + 'type' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'edit', 'new', 'log' @@ -522,29 +555,44 @@ class ApiQueryRecentChanges extends ApiQueryBase { } public function getParamDescription() { - return array ( - 'start' => 'The timestamp to start enumerating from.', - 'end' => 'The timestamp to end enumerating.', - 'dir' => 'In which direction to enumerate.', + return array( + 'start' => 'The timestamp to start enumerating from', + 'end' => 'The timestamp to end enumerating', + 'dir' => 'In which direction to enumerate', 'namespace' => 'Filter log entries to only this namespace(s)', 'user' => 'Only list changes by this user', 'excludeuser' => 'Don\'t list changes by this user', - 'prop' => 'Include additional pieces of information', + 'prop' => array( + 'Include additional pieces of information', + ' user - Adds the user responsible for the edit and tags if they are an IP', + ' userid - Adds the user id responsible for the edit', + ' comment - Adds the comment for the edit', + ' parsedcomment - Adds the parsed comment for the edit', + ' flags - Adds flags for the edit', + ' timestamp - Adds timestamp of the edit', + ' title - Adds the page title of the edit', + ' ids - Adds the page id, recent changes id and the new and old revision id', + ' sizes - Adds the new and old page length in bytes', + ' redirect - Tags edit if page is a redirect', + ' patrolled - Tags edits have have been patrolled', + ' loginfo - Adds log information (logid, logtype, etc) to log entries', + ' tags - Lists tags for the entry', + ), 'token' => 'Which tokens to obtain for each change', - 'show' => array ( + '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' + "For example, to see only minor edits done by logged-in users, set {$this->getModulePrefix()}show=minor|!anon" ), - 'type' => 'Which types of changes to show.', - 'limit' => 'How many total changes to return.', - 'tag' => 'Only list changes tagged with this tag.', + 'type' => 'Which types of changes to show', + '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' ), @@ -554,12 +602,12 @@ class ApiQueryRecentChanges extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=recentchanges' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryRecentChanges.php 78437 2010-12-15 14:14:16Z catrope $'; } } diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php index 3992d6a9..64a0a4ea 100644 --- a/includes/api/ApiQueryRevisions.php +++ b/includes/api/ApiQueryRevisions.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 7, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -37,12 +38,18 @@ if ( !defined( 'MEDIAWIKI' ) ) { */ class ApiQueryRevisions extends ApiQueryBase { + private $diffto, $difftotext, $expandTemplates, $generateXML, $section, + $token; + public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'rv' ); + parent::__construct( $query, $moduleName, 'rv' ); } private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false, - $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_content = false, $fld_tags = false; + $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false, + $fld_content = false, $fld_tags = false; + + private $tokenFunctions; protected function getTokenFunctions() { // tokenname => function @@ -50,12 +57,14 @@ 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' ) @@ -64,11 +73,11 @@ class ApiQueryRevisions extends ApiQueryBase { 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() ) ); } @@ -91,31 +100,37 @@ 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 ) + 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 ) + 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' ); + } - $this->diffto = $this->difftotext = null; if ( !is_null( $params['difftotext'] ) ) { $this->difftotext = $params['difftotext']; - } else if ( !is_null( $params['diffto'] ) ) { - if ( $params['diffto'] == 'cur' ) + } elseif ( !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' ); + } // 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 ) + 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; @@ -139,39 +154,49 @@ class ApiQueryRevisions extends ApiQueryBase { $this->fld_comment = isset ( $prop['comment'] ); $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); $this->fld_size = isset ( $prop['size'] ); + $this->fld_userid = isset( $prop['userid'] ); $this->fld_user = isset ( $prop['user'] ); $this->token = $params['token']; // Possible indexes used $index = array(); + $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 ); + $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 ); + $limit = $params['limit']; + if ( $limit == 'max' ) { + $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; + $this->getResult()->setParsedLimit( $this->getModuleName(), $limit ); + } + + if ( !is_null( $this->token ) || $pageCount > 0 ) { $this->addFields( Revision::selectPageFields() ); } - if ( isset ( $prop['tags'] ) ) { + 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'; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; } - - if ( isset( $prop['content'] ) || !is_null( $this->difftotext ) ) { + 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() ) + if ( !$title->userCanRead() ) { $this->dieUsage( 'The current user is not allowed to read ' . $title->getPrefixedText(), 'accessdenied' ); + } } $this->addTables( 'text' ); @@ -183,34 +208,39 @@ class ApiQueryRevisions extends ApiQueryBase { $this->expandTemplates = $params['expandtemplates']; $this->generateXML = $params['generatexml']; - if ( isset( $params['section'] ) ) + $this->parseContent = $params['parse']; + if ( $this->parseContent ) { + // Must manually initialize unset limit + if ( is_null( $limit ) ) { + $limit = 1; + } + // We are only going to parse 1 revision per request + $this->validateLimit( 'limit', $limit, 1, 1, 1 ); + } + if ( isset( $params['section'] ) ) { $this->section = $params['section']; - else + } else { $this->section = false; + } } //Bug 24166 - API error when using rvprop=tags $this->addTables( 'revision' ); - $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 ); - $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 ); - $limit = $params['limit']; - if ( $limit == 'max' ) { - $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; - $this->getResult()->addValue( 'limits', $this->getModuleName(), $limit ); - } if ( $enumRevMode ) { - // This is mostly to prevent parameter errors (and optimize SQL?) - if ( !is_null( $params['startid'] ) && !is_null( $params['start'] ) ) + 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'] ) ) + 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'] ) ) + 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, @@ -218,11 +248,10 @@ class ApiQueryRevisions extends ApiQueryBase { // Switching to rev_id removes the potential problem of having more than // one row with the same timestamp for the same page. // The order needs to be the same as start parameter to avoid SQL filesort. - - if ( is_null( $params['startid'] ) && is_null( $params['endid'] ) ) + if ( is_null( $params['startid'] ) && is_null( $params['endid'] ) ) { $this->addWhereRange( 'rev_timestamp', $params['dir'], $params['start'], $params['end'] ); - else { + } else { $this->addWhereRange( 'rev_id', $params['dir'], $params['startid'], $params['endid'] ); // One of start and end can be set @@ -232,8 +261,9 @@ class ApiQueryRevisions extends ApiQueryBase { } // must manually initialize unset limit - if ( is_null( $limit ) ) + if ( is_null( $limit ) ) { $limit = 10; + } $this->validateLimit( 'limit', $limit, 1, $userMax, $botMax ); // There is only one ID, use it @@ -250,114 +280,125 @@ class ApiQueryRevisions extends ApiQueryBase { // Paranoia: avoid brute force searches (bug 17342) $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 ) ) + 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 ) ); - if ( !is_null( $params['continue'] ) ) + 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 ) ) + 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' ); - + // Get all page IDs $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 ) ); - - 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" ); + 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')" ); + $this->addWhere( + "rev_page > '$pageid' OR " . + "(rev_page = '$pageid' AND " . + "rev_id >= '$revid')" + ); } $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?' ); + } else { + ApiBase::dieDebug( __METHOD__, 'param validation?' ); + } $this->addOption( 'LIMIT', $limit + 1 ); $this->addOption( 'USE INDEX', $index ); - $data = array (); $count = 0; $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { - + foreach ( $res as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - if ( !$enumRevMode ) - ApiBase :: dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report + if ( !$enumRevMode ) { + ApiBase::dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report + } $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) ); break; } - - // + $fit = $this->addPageSubItem( $row->rev_page, $this->extractRowInfo( $row ), 'rev' ); - if ( !$fit ) - { - if ( $enumRevMode ) + if ( !$fit ) { + if ( $enumRevMode ) { $this->setContinueEnumParameter( 'startid', intval( $row->rev_id ) ); - else if ( $revCount > 0 ) + } elseif ( $revCount > 0 ) { $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) ); - else + } else { $this->setContinueEnumParameter( 'continue', intval( $row->rev_page ) . '|' . intval( $row->rev_id ) ); + } break; } } - $db->freeResult( $res ); } private function extractRowInfo( $row ) { $revision = new Revision( $row ); $title = $revision->getTitle(); - $vals = array (); + $vals = array(); 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['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 ( $this->fld_user || $this->fld_userid ) { if ( $revision->isDeleted( Revision::DELETED_USER ) ) { $vals['userhidden'] = ''; } else { - $vals['user'] = $revision->getUserText(); - if ( !$revision->getUser() ) + if ( $this->fld_user ) { + $vals['user'] = $revision->getUserText(); + } + $userid = $revision->getUser(); + if ( !$userid ) { $vals['anon'] = ''; + } + + if ( $this->fld_userid ) { + $vals['userid'] = $userid; + } } } @@ -374,15 +415,14 @@ class ApiQueryRevisions extends ApiQueryBase { $vals['commenthidden'] = ''; } else { $comment = $revision->getComment(); - if ( strval( $comment ) !== '' ) - { - if ( $this->fld_comment ) - $vals['comment'] = $comment; - - if ( $this->fld_parsedcomment ) { - global $wgUser; - $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $comment, $title ); - } + + if ( $this->fld_comment ) { + $vals['comment'] = $comment; + } + + if ( $this->fld_parsedcomment ) { + global $wgUser; + $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $comment, $title ); } } } @@ -396,31 +436,31 @@ class ApiQueryRevisions extends ApiQueryBase { $vals['tags'] = array(); } } - - if ( !is_null( $this->token ) ) - { + + 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 ) + if ( $val === false ) { $this->setWarning( "Action '$t' is not allowed for the current user" ); - else + } else { $vals[$t . 'token'] = $val; + } } } - + $text = null; + global $wgParser; 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 ) + if ( $text === false ) { $this->dieUsage( "There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection' ); + } } } if ( $this->fld_content && !$revision->isDeleted( Revision::DELETED_TEXT ) ) { @@ -433,13 +473,36 @@ class ApiQueryRevisions extends ApiQueryBase { $xml = $dom->__toString(); } $vals['parsetree'] = $xml; - + } - if ( $this->expandTemplates ) { + if ( $this->expandTemplates && !$this->parseContent ) { $text = $wgParser->preprocess( $text, $title, new ParserOptions() ); } - ApiResult :: setContent( $vals, $text ); - } else if ( $this->fld_content ) { + if ( $this->parseContent ) { + global $wgEnableParserCache; + + $popts = new ParserOptions(); + $popts->setTidy( true ); + + $articleObj = new Article( $title ); + + $p_result = false; + $pcache = ParserCache::singleton(); + if ( $wgEnableParserCache ) { + $p_result = $pcache->get( $articleObj, $popts ); + } + if ( !$p_result ) { + $p_result = $wgParser->parse( $text, $title, $popts ); + + if ( $wgEnableParserCache ) { + $pcache->save( $p_result, $articleObj, $popts ); + } + } + + $text = $p_result->getText(); + } + ApiResult::setContent( $vals, $text ); + } elseif ( $this->fld_content ) { $vals['texthidden'] = ''; } @@ -458,8 +521,9 @@ class ApiQueryRevisions extends ApiQueryBase { } $difftext = $engine->getDiffBody(); ApiResult::setContent( $vals['diff'], $difftext ); - if ( !$engine->wasCacheHit() ) + if ( !$engine->wasCacheHit() ) { $n++; + } } else { $vals['diff']['notcached'] = ''; } @@ -474,20 +538,21 @@ class ApiQueryRevisions extends ApiQueryBase { if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { // formatComment() calls wfMsg() among other things return 'anon-public-user-private'; - } + } return 'public'; } public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'ids|timestamp|flags|comment|user', - ApiBase :: PARAM_TYPE => array ( + return array( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'ids|timestamp|flags|comment|user', + ApiBase::PARAM_TYPE => array( 'ids', 'flags', 'timestamp', 'user', + 'userid', 'size', 'comment', 'parsedcomment', @@ -495,44 +560,45 @@ class ApiQueryRevisions extends ApiQueryBase { 'tags' ) ), - 'limit' => array ( - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), - 'startid' => array ( - ApiBase :: PARAM_TYPE => 'integer' + 'startid' => array( + ApiBase::PARAM_TYPE => 'integer' ), - 'endid' => array ( - ApiBase :: PARAM_TYPE => 'integer' + 'endid' => array( + ApiBase::PARAM_TYPE => 'integer' ), - 'start' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'start' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'end' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'end' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'older', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'older', + ApiBase::PARAM_TYPE => array( 'newer', 'older' ) ), 'user' => array( - ApiBase :: PARAM_TYPE => 'user' + ApiBase::PARAM_TYPE => 'user' ), 'excludeuser' => array( - ApiBase :: PARAM_TYPE => 'user' + ApiBase::PARAM_TYPE => 'user' ), 'tag' => null, 'expandtemplates' => false, 'generatexml' => false, + 'parse' => false, 'section' => null, 'token' => array( - ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ), - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ), + ApiBase::PARAM_ISMULTI => true ), 'continue' => null, 'diffto' => null, @@ -541,8 +607,21 @@ class ApiQueryRevisions extends ApiQueryBase { } public function getParamDescription() { - return array ( - 'prop' => 'Which properties to get for each revision.', + $p = $this->getModulePrefix(); + return array( + 'prop' => array( + 'Which properties to get for each revision:', + ' ids - The ID of the revision', + ' flags - Revision flags (minor)', + ' timestamp - The timestamp of the revision', + ' user - User that made the revision', + ' userid - User id of revision creator', + ' size - Length of the revision', + ' comment - Comment by the user for revision', + ' parsedcomment - Parsed comment by the user for the revision', + ' content - Text of the revision', + ' tags - Tags for the 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)', @@ -553,28 +632,29 @@ class ApiQueryRevisions extends ApiQueryBase { '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', + 'parse' => 'Parse revision content. For performance reasons if this option is used, rvlimit is enforced to 1.', + 'section' => 'Only retrieve the content of this section number', '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.' ), + '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.' ), + "Overrides {$p}diffto. If {$p}section is set, only that section will be diffed against this text" ), 'tag' => 'Only list revisions tagged with this tag', ); } public function getDescription() { - return array ( - 'Get revision information.', + return array( + 'Get revision information', 'This module may be used in several ways:', - ' 1) Get data about a set of pages (last revision), by setting titles or pageids parameter.', - ' 2) Get revisions for one given page, by using titles/pageids with start/end/limit params.', - ' 3) Get data about a set of revisions by setting their IDs with revids parameter.', - 'All parameters marked as (enum) may only be used with a single page (#2).' + ' 1) Get data about a set of pages (last revision), by setting titles or pageids parameter', + ' 2) Get revisions for one given page, by using titles/pageids with start/end/limit params', + ' 3) Get data about a set of revisions by setting their IDs with revids parameter', + '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' ), @@ -589,7 +669,7 @@ class ApiQueryRevisions extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'Get data with content for the last revision of titles "API" and "Main Page":', ' api.php?action=query&prop=revisions&titles=API|Main%20Page&rvprop=timestamp|user|comment|content', 'Get last 5 revisions of the "Main Page":', @@ -606,6 +686,6 @@ class ApiQueryRevisions extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryRevisions.php 72117 2010-09-01 16:50:07Z reedy $'; + return __CLASS__ . ': $Id: ApiQueryRevisions.php 75521 2010-10-27 11:50:20Z catrope $'; } } diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php index 4e032321..3cf693af 100644 --- a/includes/api/ApiQuerySearch.php +++ b/includes/api/ApiQuerySearch.php @@ -1,11 +1,10 @@ @gmail.com + * Created on July 30, 2007 + * + * Copyright © 2007 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -36,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQuerySearch extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'sr' ); + parent::__construct( $query, $moduleName, 'sr' ); } public function execute() { @@ -57,9 +58,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { $what = $params['what']; $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(); @@ -72,6 +70,8 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { $matches = $search->searchText( $query ); } elseif ( $what == 'title' ) { $matches = $search->searchTitle( $query ); + } elseif ( $what == 'nearmatch' ) { + $matches = SearchEngine::getNearMatchResultSet( $query ); } else { // We default to title searches; this is a terrible legacy // of the way we initially set up the MySQL fulltext-based @@ -79,7 +79,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { // In the future, the default should be for a combined index. $what = 'title'; $matches = $search->searchTitle( $query ); - + // Not all search engines support a separate title search, // for instance the Lucene-based engine we use on Wikipedia. // In this case, fall back to full-text search (which will @@ -89,9 +89,10 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { $matches = $search->searchText( $query ); } } - if ( is_null( $matches ) ) + 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(); @@ -107,7 +108,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { // Add the search results to the result $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); - $titles = array (); + $titles = array(); $count = 0; while ( $result = $matches->next() ) { if ( ++ $count > $limit ) { @@ -117,23 +118,53 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { } // Silently skip broken and missing titles - if ( $result->isBrokenTitle() || $result->isMissingRevision() ) + if ( $result->isBrokenTitle() || $result->isMissingRevision() ) { continue; - + } + $title = $result->getTitle(); if ( is_null( $resultPageSet ) ) { $vals = array(); ApiQueryBase::addTitleInfo( $vals, $title ); - - if ( isset( $prop['snippet'] ) ) + + if ( isset( $prop['snippet'] ) ) { $vals['snippet'] = $result->getTextSnippet( $terms ); - if ( isset( $prop['size'] ) ) + } + if ( isset( $prop['size'] ) ) { $vals['size'] = $result->getByteSize(); - if ( isset( $prop['wordcount'] ) ) + } + if ( isset( $prop['wordcount'] ) ) { $vals['wordcount'] = $result->getWordCount(); - if ( isset( $prop['timestamp'] ) ) + } + if ( isset( $prop['timestamp'] ) ) { $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() ); - + } + if ( !is_null( $result->getScore() ) && isset( $prop['score'] ) ) { + $vals['score'] = $result->getScore(); + } + if ( isset( $prop['titlesnippet'] ) ) { + $vals['titlesnippet'] = $result->getTitleSnippet( $terms ); + } + if ( !is_null( $result->getRedirectTitle() ) ) { + if ( isset( $prop['redirecttitle'] ) ) { + $vals['redirecttitle'] = $result->getRedirectTitle(); + } + if ( isset( $prop['redirectsnippet'] ) ) { + $vals['redirectsnippet'] = $result->getRedirectSnippet( $terms ); + } + } + if ( !is_null( $result->getSectionTitle() ) ) { + if ( isset( $prop['sectiontitle'] ) ) { + $vals['sectiontitle'] = $result->getSectionTitle(); + } + if ( isset( $prop['sectionsnippet'] ) ) { + $vals['sectionsnippet'] = $result->getSectionSnippet(); + } + } + if ( isset( $prop['hasrelated'] ) && $result->hasRelated() ) { + $vals['hasrelated'] = ""; + } + // Add item to results and see whether it fits $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); @@ -160,77 +191,100 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { } public function getAllowedParams() { - return array ( - 'search' => null, - 'namespace' => array ( - ApiBase :: PARAM_DFLT => 0, - ApiBase :: PARAM_TYPE => 'namespace', - ApiBase :: PARAM_ISMULTI => true, + return array( + 'search' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), + 'namespace' => array( + ApiBase::PARAM_DFLT => 0, + ApiBase::PARAM_TYPE => 'namespace', + ApiBase::PARAM_ISMULTI => true, ), - 'what' => array ( - ApiBase :: PARAM_DFLT => null, - ApiBase :: PARAM_TYPE => array ( + 'what' => array( + ApiBase::PARAM_DFLT => null, + ApiBase::PARAM_TYPE => array( 'title', 'text', + 'nearmatch', ) ), 'info' => array( - ApiBase :: PARAM_DFLT => 'totalhits|suggestion', - ApiBase :: PARAM_TYPE => array ( + ApiBase::PARAM_DFLT => 'totalhits|suggestion', + ApiBase::PARAM_TYPE => array( 'totalhits', 'suggestion', ), - ApiBase :: PARAM_ISMULTI => true, + ApiBase::PARAM_ISMULTI => true, ), 'prop' => array( - ApiBase :: PARAM_DFLT => 'size|wordcount|timestamp|snippet', - ApiBase :: PARAM_TYPE => array ( + ApiBase::PARAM_DFLT => 'size|wordcount|timestamp|snippet', + ApiBase::PARAM_TYPE => array( 'size', 'wordcount', 'timestamp', + 'score', 'snippet', + 'titlesnippet', + 'redirecttitle', + 'redirectsnippet', + 'sectiontitle', + 'sectionsnippet', + 'hasrelated', ), - ApiBase :: PARAM_ISMULTI => true, + 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_SML1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_SML2 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_SML1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_SML2 ) ); } public function getParamDescription() { - return array ( - '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.', + return array( + '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' => array( + 'What properties to return', + ' size - Adds the size of the page in bytes', + ' wordcount - Adds the word count of the page', + ' timestamp - Adds the timestamp of when the page was last edited', + ' score - Adds the score (if any) from the search engine', + ' snippet - Adds a parsed snippet of the page', + ' titlesnippet - Adds a parsed snippet of the page title', + ' redirectsnippet - Adds a parsed snippet of the redirect', + ' redirecttitle - Adds a parsed snippet of the redirect title', + ' sectionsnippet - Adds a parsed snippet of the matching section', + ' sectiontitle - Adds a parsed snippet of the matching section title', + ' hasrelated - Indicates whether a related search is available', + ), + 'redirects' => 'Include redirect pages in the search', 'offset' => 'Use this value to continue paging (return by query)', - 'limit' => 'How many total pages to return.' + 'limit' => 'How many total pages to return' ); } 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 ( + return array( 'api.php?action=query&list=search&srsearch=meaning', 'api.php?action=query&list=search&srwhat=text&srsearch=meaning', 'api.php?action=query&generator=search&gsrsearch=meaning&prop=info', @@ -238,6 +292,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQuerySearch.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQuerySearch.php 76300 2010-11-08 12:23:24Z reedy $'; } } diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php index 0385e192..379a4228 100644 --- a/includes/api/ApiQuerySiteinfo.php +++ b/includes/api/ApiQuerySiteinfo.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 25, 2006 + * + * Copyright © 2006 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 @@ -19,8 +18,10 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { @@ -36,16 +37,14 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQuerySiteinfo extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'si' ); + parent::__construct( $query, $moduleName, 'si' ); } public function execute() { $params = $this->extractRequestParams(); $done = array(); - foreach ( $params['prop'] as $p ) - { - switch ( $p ) - { + foreach ( $params['prop'] as $p ) { + switch ( $p ) { case 'general': $fit = $this->appendGeneralInfo( $p ); break; @@ -86,11 +85,10 @@ class ApiQuerySiteinfo extends ApiQueryBase { case 'languages': $fit = $this->appendLanguages( $p ); break; - default : - ApiBase :: dieDebug( __METHOD__, "Unknown prop=$p" ); + 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( '|', @@ -103,10 +101,9 @@ class ApiQuerySiteinfo extends ApiQueryBase { protected function appendGeneralInfo( $property ) { global $wgContLang; - global $wgLang; $data = array(); - $mainPage = Title :: newFromText( wfMsgForContent( 'mainpage' ) ); + $mainPage = Title::newMainPage(); $data['mainpage'] = $mainPage->getPrefixedText(); $data['base'] = $mainPage->getFullUrl(); $data['sitename'] = $GLOBALS['wgSitename']; @@ -117,26 +114,30 @@ class ApiQuerySiteinfo extends ApiQueryBase { $data['dbversion'] = $this->getDB()->getServerVersion(); $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] ); - if ( $svn ) + if ( $svn ) { $data['rev'] = $svn; + } // 'case-insensitive' option is reserved for future $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive'; - if ( isset( $GLOBALS['wgRightsCode'] ) ) + if ( isset( $GLOBALS['wgRightsCode'] ) ) { $data['rightscode'] = $GLOBALS['wgRightsCode']; + } $data['rights'] = $GLOBALS['wgRightsText']; $data['lang'] = $GLOBALS['wgLanguageCode']; - if ( $wgContLang->isRTL() ) + if ( $wgContLang->isRTL() ) { $data['rtl'] = ''; - $data['fallback8bitEncoding'] = $wgLang->fallback8bitEncoding(); - + } + $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding(); + if ( wfReadOnly() ) { $data['readonly'] = ''; $data['readonlyreason'] = wfReadOnlyReason(); } - if ( $GLOBALS['wgEnableWriteAPI'] ) + if ( $GLOBALS['wgEnableWriteAPI'] ) { $data['writeapi'] = ''; + } $tz = $GLOBALS['wgLocaltimezone']; $offset = $GLOBALS['wgLocalTZoffset']; @@ -162,23 +163,25 @@ 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 ), 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive', ); - ApiResult :: setContent( $data[$ns], $title ); + ApiResult::setContent( $data[$ns], $title ); $canonical = MWNamespace::getCanonicalName( $ns ); - - if ( MWNamespace::hasSubpages( $ns ) ) + + if ( MWNamespace::hasSubpages( $ns ) ) { $data[$ns]['subpages'] = ''; - - if ( $canonical ) + } + + if ( $canonical ) { $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' ); - - if ( MWNamespace::isContent( $ns ) ) + } + + if ( MWNamespace::isContent( $ns ) ) { $data[$ns]['content'] = ''; + } } $this->getResult()->setIndexedTagName( $data, 'ns' ); @@ -198,7 +201,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { $item = array( 'id' => intval( $ns ) ); - ApiResult :: setContent( $item, strtr( $title, '_', ' ' ) ); + ApiResult::setContent( $item, strtr( $title, '_', ' ' ) ); $data[] = $item; } @@ -207,9 +210,9 @@ class ApiQuerySiteinfo extends ApiQueryBase { } protected function appendSpecialPageAliases( $property ) { - global $wgLang; + global $wgContLang; $data = array(); - foreach ( $wgLang->getSpecialPageAliases() as $specialpage => $aliases ) + foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) { $arr = array( 'realname' => $specialpage, 'aliases' => $aliases ); $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' ); @@ -218,16 +221,16 @@ class ApiQuerySiteinfo extends ApiQueryBase { $this->getResult()->setIndexedTagName( $data, 'specialpage' ); return $this->getResult()->addValue( 'query', $property, $data ); } - + 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 ) + if ( $caseSensitive ) { $arr['case-sensitive'] = ''; + } $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' ); $data[] = $arr; } @@ -240,34 +243,34 @@ 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 ) - ApiBase :: dieDebug( __METHOD__, "Unknown filter=$filter" ); + } elseif ( $filter ) { + ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" ); + } $this->addOption( 'ORDER BY', 'iw_prefix' ); - $db = $this->getDB(); $res = $this->select( __METHOD__ ); $data = array(); $langNames = Language::getLanguageNames(); - while ( $row = $db->fetchObject( $res ) ) - { + foreach ( $res as $row ) { $val = array(); $val['prefix'] = $row->iw_prefix; - if ( $row->iw_local == '1' ) + if ( $row->iw_local == '1' ) { $val['local'] = ''; -// $val['trans'] = intval($row->iw_trans); // should this be exposed? - if ( isset( $langNames[$row->iw_prefix] ) ) + } + // $val['trans'] = intval( $row->iw_trans ); // should this be exposed? + if ( isset( $langNames[$row->iw_prefix] ) ) { $val['language'] = $langNames[$row->iw_prefix]; + } $val['url'] = $row->iw_url; $data[] = $val; } - $db->freeResult( $res ); $this->getResult()->setIndexedTagName( $data, 'iw' ); return $this->getResult()->addValue( 'query', $property, $data ); @@ -277,8 +280,9 @@ class ApiQuerySiteinfo extends ApiQueryBase { global $wgShowHostnames; $data = array(); if ( $includeAll ) { - if ( !$wgShowHostnames ) + if ( !$wgShowHostnames ) { $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' ); + } $lb = wfGetLB(); $lags = $lb->getLagTimes(); @@ -319,27 +323,52 @@ class ApiQuerySiteinfo extends ApiQueryBase { } protected function appendUserGroups( $property, $numberInGroup ) { - global $wgGroupPermissions; + global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf; + $data = array(); foreach ( $wgGroupPermissions as $group => $permissions ) { $arr = array( 'name' => $group, 'rights' => array_keys( $permissions, true ), ); - if ( $numberInGroup ) - $arr['number'] = SiteStats::numberInGroup( $group ); + + if ( $numberInGroup ) { + global $wgAutopromote; + + if ( $group == 'user' ) { + $arr['number'] = SiteStats::users(); + + // '*' and autopromote groups have no size + } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) { + $arr['number'] = SiteStats::numberInGroup( $group ); + } + } + + $groupArr = array( + 'add' => $wgAddGroups, + 'remove' => $wgRemoveGroups, + 'add-self' => $wgGroupsAddToSelf, + 'remove-self' => $wgGroupsRemoveFromSelf + ); + + foreach( $groupArr as $type => $rights ) { + if( isset( $rights[$group] ) ) { + $arr[$type] = $rights[$group]; + $this->getResult()->setIndexedTagName( $arr[$type], 'group' ); + } + } $this->getResult()->setIndexedTagName( $arr['rights'], 'permission' ); $data[] = $arr; } - + $this->getResult()->setIndexedTagName( $data, 'group' ); return $this->getResult()->addValue( 'query', $property, $data ); } - + protected function appendFileExtensions( $property ) { global $wgFileExtensions; - + $data = array(); foreach ( $wgFileExtensions as $ext ) { $data[] = array( 'ext' => $ext ); @@ -355,10 +384,12 @@ 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'] ) ) { // Can be a string or array( key, param1, param2, ... ) if ( is_array( $ext['descriptionmsg'] ) ) { @@ -428,9 +459,9 @@ class ApiQuerySiteinfo extends ApiQueryBase { public function getAllowedParams() { return array( 'prop' => array( - ApiBase :: PARAM_DFLT => 'general', - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_DFLT => 'general', + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'general', 'namespaces', 'namespacealiases', @@ -447,7 +478,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { ) ), 'filteriw' => array( - ApiBase :: PARAM_TYPE => array( + ApiBase::PARAM_TYPE => array( 'local', '!local', ) @@ -461,19 +492,19 @@ class ApiQuerySiteinfo extends ApiQueryBase { return array( 'prop' => array( 'Which sysinfo properties to get:', - ' general - Overall system information', - ' namespaces - List of registered namespaces and their canonical names', - ' namespacealiases - List of registered namespace aliases', - ' specialpagealiases - List of special page aliases', - ' magicwords - List of magic words and their aliases', - ' statistics - Returns site statistics', - ' interwikimap - Returns interwiki map (optionally filtered)', - ' dbrepllag - Returns database server with the highest replication lag', - ' usergroups - Returns user groups and the associated permissions', - ' extensions - Returns extensions installed on the wiki', - ' 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', + ' general - Overall system information', + ' namespaces - List of registered namespaces and their canonical names', + ' namespacealiases - List of registered namespace aliases', + ' specialpagealiases - List of special page aliases', + ' magicwords - List of magic words and their aliases', + ' statistics - Returns site statistics', + ' interwikimap - Returns interwiki map (optionally filtered)', + ' dbrepllag - Returns database server with the highest replication lag', + ' usergroups - Returns user groups and the associated permissions', + ' extensions - Returns extensions installed on the wiki', + ' 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', @@ -482,7 +513,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { } public function getDescription() { - return 'Return general information about the site.'; + return 'Return general information about the site'; } public function getPossibleErrors() { @@ -495,11 +526,11 @@ class ApiQuerySiteinfo extends ApiQueryBase { return array( 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics', 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local', - 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb', - ); + 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=', + ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQuerySiteinfo.php 77192 2010-11-23 22:05:27Z btongminh $'; } } diff --git a/includes/api/ApiQueryStashImageInfo.php b/includes/api/ApiQueryStashImageInfo.php new file mode 100644 index 00000000..769b3e9d --- /dev/null +++ b/includes/api/ApiQueryStashImageInfo.php @@ -0,0 +1,152 @@ +extractRequestParams(); + $modulePrefix = $this->getModulePrefix(); + + $prop = array_flip( $params['prop'] ); + + $scale = $this->getScale( $params ); + + $result = $this->getResult(); + + try { + $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash(); + + foreach ( $params['sessionkey'] as $sessionkey ) { + $file = $stash->getFile( $sessionkey ); + $imageInfo = self::getInfo( $file, $prop, $result, $scale ); + $result->addValue( array( 'query', $this->getModuleName() ), null, $imageInfo ); + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), $modulePrefix ); + } + + } catch ( UploadStashNotAvailableException $e ) { + $this->dieUsage( "Session not available: " . $e->getMessage(), "nosession" ); + } catch ( UploadStashFileNotFoundException $e ) { + $this->dieUsage( "File not found: " . $e->getMessage(), "invalidsessiondata" ); + } catch ( UploadStashBadPathException $e ) { + $this->dieUsage( "Bad path: " . $e->getMessage(), "invalidsessiondata" ); + } + + } + + /** + * Returns all valid parameters to siiprop + */ + public static function getPropertyNames() { + return array( + 'timestamp', + 'url', + 'size', + 'dimensions', // For backwards compatibility with Allimages + 'sha1', + 'mime', + 'thumbmime', + 'metadata', + 'bitdepth', + ); + } + + + public function getAllowedParams() { + return array( + 'sessionkey' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_REQUIRED => true, + ApiBase::PARAM_DFLT => null + ), + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'timestamp|url', + ApiBase::PARAM_TYPE => self::getPropertyNames() + ), + 'urlwidth' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_DFLT => -1 + ), + 'urlheight' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_DFLT => -1 + ) + ); + } + + /** + * Return the API documentation for the parameters. + * @return {Array} parameter documentation. + */ + public function getParamDescription() { + $p = $this->getModulePrefix(); + return array( + 'prop' => array( + 'What image information to get:', + ' timestamp - Adds timestamp for the uploaded version', + ' url - Gives URL to the image and the description page', + ' size - Adds the size of the image in bytes and the height and width', + ' dimensions - Alias for size', + ' sha1 - Adds sha1 hash for the image', + ' mime - Adds MIME of the image', + ' thumbmime - Adss MIME of the image thumbnail (requires url)', + ' metadata - Lists EXIF metadata for the version of the image', + ' bitdepth - Adds the bit depth of the version', + ), + 'sessionkey' => 'Session key that identifies a previous upload that was stashed temporarily.', + 'urlwidth' => "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.", + 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth" + ); + } + + public function getDescription() { + return 'Returns image information for stashed images'; + } + + public function getPossibleErrors() { + return array_merge( parent::getPossibleErrors(), array( + array( 'code' => 'siiurlwidth', 'info' => 'siiurlheight cannot be used without iiurlwidth' ), + ) ); + } + + protected function getExamples() { + return array( + 'api.php?action=query&prop=stashimageinfo&siisessionkey=124sd34rsdf567', + 'api.php?action=query&prop=stashimageinfo&siisessionkey=b34edoe3|bceffd4&siiurlwidth=120&siiprop=url', + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiQueryStashImageInfo.php 81000 2011-01-25 22:49:34Z catrope $'; + } + +} + diff --git a/includes/api/ApiQueryTags.php b/includes/api/ApiQueryTags.php index a5d152bc..e88ec9b5 100644 --- a/includes/api/ApiQueryTags.php +++ b/includes/api/ApiQueryTags.php @@ -1,11 +1,10 @@ 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 ) + + 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; + + foreach ( $res as $row ) { + 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; + 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 ) - { + + if ( ++$count > $this->limit ) { $this->setContinueEnumParameter( 'continue', $tagName ); return false; } - + $tag = array(); $tag['name'] = $tagName; - - if ( $this->fld_displayname ) + + if ( $this->fld_displayname ) { $tag['displayname'] = ChangeTags::tagDescription( $tagName ); - - if ( $this->fld_description ) - { + } + + if ( $this->fld_description ) { $msg = wfMsg( "tag-$tagName-description" ); $msg = wfEmptyMsg( "tag-$tagName-description", $msg ) ? '' : $msg; $tag['description'] = $msg; } - - if ( $this->fld_hitcount ) + + if ( $this->fld_hitcount ) { $tag['hitcount'] = $hitcount; - + } + $doneTags[] = $tagName; - + $fit = $this->result->addValue( array( 'query', $this->getModuleName() ), null, $tag ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $tagName ); return false; } - + return true; } - + public function getCacheMode( $params ) { return 'public'; } public function getAllowedParams() { - return array ( + 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 + 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 + ApiBase::PARAM_DFLT => 'name', + ApiBase::PARAM_TYPE => array( + 'name', + 'displayname', + 'description', + 'hitcount' + ), + ApiBase::PARAM_ISMULTI => true ) ); } public function getParamDescription() { - return array ( + 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', + 'prop' => array( + 'Which properties to get', + ' name - Adds name of tag', + ' displayname - Adds system messsage for the tag', + ' description - Adds description of the tag', + ' hitcount - Adds the amount of revisions that have this tag', + ), ); } public function getDescription() { - return 'List change tags.'; + return 'List change tags'; } protected function getExamples() { - return array ( + 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 $'; + return __CLASS__ . ': $Id: ApiQueryTags.php 73858 2010-09-28 01:21:15Z reedy $'; } } diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php index b51b9adb..5d63fa60 100644 --- a/includes/api/ApiQueryUserContributions.php +++ b/includes/api/ApiQueryUserContributions.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Oct 16, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -36,13 +37,13 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryContributions extends ApiQueryBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'uc' ); + parent::__construct( $query, $moduleName, 'uc' ); } - private $params, $username; + private $params, $prefixMode, $userprefix, $multiUserMode, $usernames; private $fld_ids = false, $fld_title = false, $fld_timestamp = false, $fld_comment = false, $fld_parsedcomment = false, $fld_flags = false, - $fld_patrolled = false, $fld_tags = false; + $fld_patrolled = false, $fld_tags = false, $fld_size = false; public function execute() { // Parse some parameters @@ -61,23 +62,22 @@ class ApiQueryContributions extends ApiQueryBase { // TODO: if the query is going only against the revision table, should this be done? $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; $this->userprefix = $this->params['userprefix']; - } - else - { + } else { $this->usernames = array(); - if ( !is_array( $this->params['user'] ) ) + if ( !is_array( $this->params['user'] ) ) { $this->params['user'] = array( $this->params['user'] ); - if ( !count( $this->params['user'] ) ) + } + if ( !count( $this->params['user'] ) ) { $this->dieUsage( 'User parameter may not be empty.', 'param_user' ); - foreach ( $this->params['user'] as $u ) + } + foreach ( $this->params['user'] as $u ) { $this->prepareUsername( $u ); + } $this->prefixMode = false; $this->multiUserMode = ( count( $this->params['user'] ) > 1 ); } @@ -91,31 +91,29 @@ class ApiQueryContributions extends ApiQueryBase { $limit = $this->params['limit']; // Fetch each row - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $limit ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... - if ( $this->multiUserMode ) + if ( $this->multiUserMode ) { $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) ); - else + } else { $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 ) - { - if ( $this->multiUserMode ) + if ( !$fit ) { + if ( $this->multiUserMode ) { $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) ); - else + } else { $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 ); - $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' ); } @@ -150,32 +148,37 @@ class ApiQueryContributions extends ApiQueryBase { $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" ); + if ( count( $continue ) != 2 ) { + $this->dieUsage( 'Invalid continue param. You should pass the original ' . + 'value returned by the previous query', '_badcontinue' ); + } $encUser = $this->getDB()->strencode( $continue[0] ); $encTS = wfTimestamp( TS_MW, $continue[1] ); $op = ( $this->params['dir'] == 'older' ? '<' : '>' ); - $this->addWhere( "rev_user_text $op '$encUser' OR " . - "(rev_user_text = '$encUser' AND " . - "rev_timestamp $op= '$encTS')" ); + $this->addWhere( + "rev_user_text $op '$encUser' OR " . + "(rev_user_text = '$encUser' AND " . + "rev_timestamp $op= '$encTS')" + ); } - if ( !$wgUser->isAllowed( 'hideuser' ) ) + 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 ) + if ( $this->prefixMode ) { $this->addWhere( 'rev_user_text' . $this->getDB()->buildLike( $this->userprefix, $this->getDB()->anyString() ) ); - else + } else { $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 ) + 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'] ); @@ -184,8 +187,9 @@ class ApiQueryContributions extends ApiQueryBase { if ( !is_null( $show ) ) { $show = array_flip( $show ); if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) ) - || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) ) + || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) ) { $this->dieUsageMsg( array( 'show' ) ); + } $this->addWhereIf( 'rev_minor_edit = 0', isset( $show['!minor'] ) ); $this->addWhereIf( 'rev_minor_edit != 0', isset( $show['minor'] ) ); @@ -202,22 +206,22 @@ class ApiQueryContributions extends ApiQueryBase { 'rev_timestamp', 'page_namespace', 'page_title', + 'rev_user', 'rev_user_text', 'rev_deleted' ) ); - + 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' ); + $this->fld_patrolled ) { + 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' ); @@ -225,9 +229,7 @@ class ApiQueryContributions extends ApiQueryBase { $this->addWhere( 'rc_user_text=rev_user_text' ); $this->addWhere( 'rc_timestamp=rev_timestamp' ); $this->addWhere( 'rc_this_oldid=rev_id' ); - } - else - { + } else { $tables[] = 'recentchanges'; $this->addJoinConds( array( 'recentchanges' => array( 'LEFT JOIN', array( @@ -241,28 +243,27 @@ class ApiQueryContributions extends ApiQueryBase { $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_text_id', $this->fld_ids ); // Should this field be exposed? $this->addFieldsIf( 'rev_comment', $this->fld_comment || $this->fld_parsedcomment ); $this->addFieldsIf( 'rev_len', $this->fld_size ); $this->addFieldsIf( '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 ) - { + + 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'; + $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; } - + $this->addOption( 'USE INDEX', $index ); } @@ -270,42 +271,49 @@ class ApiQueryContributions extends ApiQueryBase { * Extract fields from the database row and append them to a result array */ private function extractRowInfo( $row ) { - $vals = array(); + $vals['userid'] = $row->rev_user; $vals['user'] = $row->rev_user_text; - if ( $row->rev_deleted & Revision::DELETED_USER ) + 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? + // $vals['textid'] = intval( $row->rev_text_id ); // todo: Should this field be exposed? } $title = Title::makeTitle( $row->page_namespace, $row->page_title ); - if ( $this->fld_title ) + if ( $this->fld_title ) { ApiQueryBase::addTitleInfo( $vals, $title ); + } - if ( $this->fld_timestamp ) + 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 ) ) + 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 || $this->fld_parsedcomment ) && isset( $row->rev_comment ) ) { - if ( $row->rev_deleted & Revision::DELETED_COMMENT ) + if ( $row->rev_deleted & Revision::DELETED_COMMENT ) { $vals['commenthidden'] = ''; - else { - if ( $this->fld_comment ) + } 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 ); @@ -313,11 +321,13 @@ class ApiQueryContributions extends ApiQueryBase { } } - 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 ) ) + } + + if ( $this->fld_size && !is_null( $row->rev_len ) ) { $vals['size'] = intval( $row->rev_len ); + } if ( $this->fld_tags ) { if ( $row->ts_tags ) { @@ -328,12 +338,11 @@ class ApiQueryContributions extends ApiQueryBase { $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 ); } @@ -345,40 +354,40 @@ class ApiQueryContributions extends ApiQueryBase { } public function getAllowedParams() { - return array ( - 'limit' => array ( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + return array( + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), - 'start' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'start' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'end' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'end' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), 'continue' => null, - 'user' => array ( - ApiBase :: PARAM_ISMULTI => true + 'user' => array( + ApiBase::PARAM_ISMULTI => true ), 'userprefix' => null, - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'older', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'older', + ApiBase::PARAM_TYPE => array( 'newer', 'older' ) ), - 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace' + 'namespace' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace' ), - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_DFLT => 'ids|title|timestamp|comment|size|flags', - ApiBase :: PARAM_TYPE => array ( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'ids|title|timestamp|comment|size|flags', + ApiBase::PARAM_TYPE => array( 'ids', 'title', 'timestamp', @@ -390,9 +399,9 @@ class ApiQueryContributions extends ApiQueryBase { 'tags' ) ), - 'show' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + 'show' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'minor', '!minor', 'patrolled', @@ -404,18 +413,31 @@ class ApiQueryContributions extends ApiQueryBase { } public function getParamDescription() { - return array ( - 'limit' => 'The maximum number of contributions to return.', - 'start' => 'The start timestamp to return from.', - 'end' => 'The end timestamp to return to.', - 'continue' => 'When more results are available, use this to continue.', - 'user' => 'The user to retrieve contributions for.', - 'userprefix' => 'Retrieve contibutions for all users whose names begin with this value. Overrides ucuser.', - 'dir' => 'The direction to search (older or newer).', + global $wgRCMaxAge; + $p = $this->getModulePrefix(); + return array( + 'limit' => 'The maximum number of contributions to return', + 'start' => 'The start timestamp to return from', + 'end' => 'The end timestamp to return to', + 'continue' => 'When more results are available, use this to continue', + 'user' => 'The users to retrieve contributions for', + 'userprefix' => "Retrieve contibutions for all users whose names begin with this value. Overrides {$p}user", + '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', ), + 'prop' => array( + 'Include additional pieces of information', + ' ids - Adds the page id and revision id', + ' title - Adds the title and namespace id of the page', + ' timestamp - Adds the timestamp of the edit', + ' comment - Adds the comment of the edit', + ' parsedcomment - Adds the parsed comment of the edit', + ' size - Adds the size of the page', + ' flags - Adds flags of the edit', + ' patrolled - Tags patrolled edits', + ' tags - Lists tags for the edit', + ), + 'show' => array( "Show only items that meet this criteria, e.g. non minor edits only: {$p}show=!minor", + "NOTE: if {$p}show=patrolled or {$p}show=!patrolled is set, revisions older than $wgRCMaxAge won\'t be shown", ), 'tag' => 'Only list revisions tagged with this tag', ); } @@ -423,7 +445,7 @@ class ApiQueryContributions extends ApiQueryBase { 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.' ), @@ -434,13 +456,13 @@ class ApiQueryContributions extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=usercontribs&ucuser=YurikBot', 'api.php?action=query&list=usercontribs&ucuserprefix=217.121.114.', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryUserContributions.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryUserContributions.php 75096 2010-10-20 18:50:33Z reedy $'; } } diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php index 42cb47b9..ec7b74b3 100644 --- a/includes/api/ApiQueryUserInfo.php +++ b/includes/api/ApiQueryUserInfo.php @@ -1,11 +1,10 @@ @gmail.com + * Created on July 30, 2007 + * + * Copyright © 2007 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -35,33 +36,34 @@ if ( !defined( 'MEDIAWIKI' ) ) { */ class ApiQueryUserInfo extends ApiQueryBase { + private $prop = array(); + public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'ui' ); + parent::__construct( $query, $moduleName, 'ui' ); } public function execute() { $params = $this->extractRequestParams(); $result = $this->getResult(); - $r = array(); 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; + global $wgUser, $wgRequest; $result = $this->getResult(); $vals = array(); $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() ) { @@ -75,7 +77,9 @@ class ApiQueryUserInfo extends ApiQueryBase { } if ( isset( $this->prop['groups'] ) ) { - $vals['groups'] = $wgUser->getGroups(); + $autolist = ApiQueryUsers::getAutoGroups( $wgUser ); + + $vals['groups'] = array_merge( $autolist, $wgUser->getGroups() ); $result->setIndexedTagName( $vals['groups'], 'g' ); // even if empty } @@ -97,7 +101,11 @@ class ApiQueryUserInfo extends ApiQueryBase { $vals['options'] = $wgUser->getOptions(); } - if ( isset( $this->prop['preferencestoken'] ) && is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) { + if ( + isset( $this->prop['preferencestoken'] ) && + is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) + ) + { $vals['preferencestoken'] = $wgUser->editToken(); } @@ -112,26 +120,39 @@ class ApiQueryUserInfo extends ApiQueryBase { if ( isset( $this->prop['email'] ) ) { $vals['email'] = $wgUser->getEmail(); $auth = $wgUser->getEmailAuthenticationTimestamp(); - if ( !is_null( $auth ) ) + if ( !is_null( $auth ) ) { $vals['emailauthenticated'] = wfTimestamp( TS_ISO_8601, $auth ); + } + } + + if ( isset( $this->prop['acceptlang'] ) ) { + $langs = $wgRequest->getAcceptLang(); + $acceptLang = array(); + foreach ( $langs as $lang => $val ) { + $r = array( 'q' => $val ); + ApiResult::setContent( $r, $lang ); + $acceptLang[] = $r; + } + $result->setIndexedTagName( $acceptLang, 'lang' ); + $vals['acceptlang'] = $acceptLang; } return $vals; } - protected function getRateLimits() - { + 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 + } else { $categories[] = 'user'; - if ( $wgUser->isNewBie() ) - { + } + if ( $wgUser->isNewbie() ) { $categories[] = 'ip'; $categories[] = 'subnet'; if ( !$wgUser->isAnon() ) @@ -141,22 +162,23 @@ class ApiQueryUserInfo extends ApiQueryBase { // 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] ); } + } + } return $retval; } public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_DFLT => null, - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + return array( + 'prop' => array( + ApiBase::PARAM_DFLT => null, + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'blockinfo', 'hasmsg', 'groups', @@ -167,23 +189,26 @@ class ApiQueryUserInfo extends ApiQueryBase { 'editcount', 'ratelimits', 'email', + 'acceptlang', ) ) ); } public function getParamDescription() { - return array ( + return array( 'prop' => array( 'What pieces of information to include', - ' 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 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' + ' 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 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', + ' email - Adds the user\'s email address and email authentication date', + ' acceptlang - Echoes the Accept-Language header sent by the client in a structured format', ) ); } @@ -193,13 +218,13 @@ class ApiQueryUserInfo extends ApiQueryBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&meta=userinfo', 'api.php?action=query&meta=userinfo&uiprop=blockinfo|groups|rights|hasmsg', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryUserInfo.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryUserInfo.php 75937 2010-11-03 17:01:21Z reedy $'; } } diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php index 5dc0e4a6..2619d200 100644 --- a/includes/api/ApiQueryUsers.php +++ b/includes/api/ApiQueryUsers.php @@ -1,11 +1,10 @@ .@home.nl + * Created on July 30, 2007 + * + * 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -35,24 +36,28 @@ if ( !defined( 'MEDIAWIKI' ) ) { */ class ApiQueryUsers extends ApiQueryBase { + private $tokenFunctions, $prop; + public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'us' ); + 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) + * @return Array tokenname => function */ 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( 'userrights' => array( 'ApiQueryUsers', 'getUserrightsToken' ), @@ -60,9 +65,8 @@ if ( !defined( 'MEDIAWIKI' ) ) { wfRunHooks( 'APIQueryUsersTokens', array( &$this->tokenFunctions ) ); return $this->tokenFunctions; } - - public static function getUserrightsToken( $user ) - { + + public static function getUserrightsToken( $user ) { global $wgUser; // Since the permissions check for userrights is non-trivial, // don't bother with it here @@ -71,8 +75,6 @@ if ( !defined( 'MEDIAWIKI' ) ) { public function execute() { $params = $this->extractRequestParams(); - $result = $this->getResult(); - $r = array(); if ( !is_null( $params['prop'] ) ) { $this->prop = array_flip( $params['prop'] ); @@ -86,27 +88,23 @@ if ( !defined( 'MEDIAWIKI' ) ) { // Canonicalize user names foreach ( $users as $u ) { $n = User::getCanonicalName( $u ); - if ( $n === false || $n === '' ) - { + if ( $n === false || $n === '' ) { $vals = array( 'name' => $u, 'invalid' => '' ); $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'users', implode( '|', array_diff( $users, $done ) ) ); $goodNames = array(); break; } $done[] = $u; - } - else + } else { $goodNames[] = $n; + } } - if ( count( $goodNames ) ) - { - $db = $this->getDb(); + if ( count( $goodNames ) ) { $this->addTables( 'user', 'u1' ); $this->addFields( 'u1.*' ); $this->addWhereFld( 'u1.user_name', $goodNames ); @@ -116,35 +114,50 @@ if ( !defined( 'MEDIAWIKI' ) ) { $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' ) ); - } + + $this->showHiddenUsersAddBlockInfo( isset( $this->prop['blockinfo'] ) ); $data = array(); $res = $this->select( __METHOD__ ); - while ( ( $r = $db->fetchObject( $res ) ) ) { - $user = User::newFromRow( $r ); + foreach ( $res as $row ) { + $user = User::newFromRow( $row ); $name = $user->getName(); $data[$name]['name'] = $name; - if ( isset( $this->prop['editcount'] ) ) + + if ( isset( $this->prop['editcount'] ) ) { $data[$name]['editcount'] = intval( $user->getEditCount() ); - if ( isset( $this->prop['registration'] ) ) + } + + 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['groups'] ) && !is_null( $row->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 ) ) { - $data[$name]['blockedby'] = $r->blocker_name; - $data[$name]['blockreason'] = $r->ipb_reason; + $data[$name]['groups'][] = $row->ug_group; } - if ( isset( $this->prop['emailable'] ) && $user->canReceiveEmail() ) + + if ( isset( $this->prop['rights'] ) && !is_null( $row->ug_group ) ) { + if ( !isset( $data[$name]['rights'] ) ) { + $data[$name]['rights'] = User::getGroupPermissions( User::getImplicitGroups() ); + } + + $data[$name]['rights'] = array_unique( array_merge( $data[$name]['rights'], + User::getGroupPermissions( array( $row->ug_group ) ) ) ); + $result->setIndexedTagName( $data[$name]['rights'], 'r' ); + } + if ( $row->ipb_deleted ) { + $data[$name]['hidden'] = ''; + } + if ( isset( $this->prop['blockinfo'] ) && !is_null( $row->ipb_by_text ) ) { + $data[$name]['blockedby'] = $row->ipb_by_text; + $data[$name]['blockreason'] = $row->ipb_reason; + $data[$name]['blockexpiry'] = $row->ipb_expiry; + } + + if ( isset( $this->prop['emailable'] ) && $user->canReceiveEmail() ) { $data[$name]['emailable'] = ''; + } if ( isset( $this->prop['gender'] ) ) { $gender = $user->getOption( 'gender' ); @@ -154,16 +167,15 @@ if ( !defined( 'MEDIAWIKI' ) ) { $data[$name]['gender'] = $gender; } - if ( !is_null( $params['token'] ) ) - { + if ( !is_null( $params['token'] ) ) { $tokenFunctions = $this->getTokenFunctions(); - foreach ( $params['token'] as $t ) - { + foreach ( $params['token'] as $t ) { $val = call_user_func( $tokenFunctions[$t], $user ); - if ( $val === false ) + if ( $val === false ) { $this->setWarning( "Action '$t' is not allowed for the current user" ); - else + } else { $data[$name][$t . 'token'] = $val; + } } } } @@ -174,30 +186,37 @@ if ( !defined( 'MEDIAWIKI' ) ) { $data[$u] = array( 'name' => $u ); $urPage = new UserrightsPage; $iwUser = $urPage->fetchUser( $u ); + if ( $iwUser instanceof UserRightsProxy ) { $data[$u]['interwiki'] = ''; - if ( !is_null( $params['token'] ) ) - { + + if ( !is_null( $params['token'] ) ) { $tokenFunctions = $this->getTokenFunctions(); - foreach ( $params['token'] as $t ) - { + + foreach ( $params['token'] as $t ) { $val = call_user_func( $tokenFunctions[$t], $iwUser ); - if ( $val === false ) + if ( $val === false ) { $this->setWarning( "Action '$t' is not allowed for the current user" ); - else + } else { $data[$u][$t . 'token'] = $val; + } } } - } else + } else { $data[$u]['missing'] = ''; + } } else { - if ( isset( $this->prop['groups'] ) && isset( $data[$u]['groups'] ) ) + if ( isset( $this->prop['groups'] ) && isset( $data[$u]['groups'] ) ) { + $autolist = ApiQueryUsers::getAutoGroups( User::newFromName( $u ) ); + + $data[$u]['groups'] = array_merge( $autolist, $data[$u]['groups'] ); + $this->getResult()->setIndexedTagName( $data[$u]['groups'], 'g' ); + } } $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $data[$u] ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'users', implode( '|', array_diff( $users, $done ) ) ); break; @@ -207,20 +226,34 @@ if ( !defined( 'MEDIAWIKI' ) ) { return $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'user' ); } + /** + * Gets all the groups that a user is automatically a member of + * @return array + */ + public static function getAutoGroups( $user ) { + $groups = array( '*' ); + + if ( !$user->isAnon() ) { + $groups[] = 'user'; + } + + return array_merge( $groups, Autopromote::getAutopromoteGroups( $user ) ); + } + public function getCacheMode( $params ) { if ( isset( $params['token'] ) ) { return 'private'; } else { - return 'public'; + return 'anon-public-user-private'; } } public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_DFLT => null, - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + return array( + 'prop' => array( + ApiBase::PARAM_DFLT => null, + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'blockinfo', 'groups', 'editcount', @@ -230,25 +263,26 @@ if ( !defined( 'MEDIAWIKI' ) ) { ) ), 'users' => array( - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_ISMULTI => true ), 'token' => array( - ApiBase :: PARAM_TYPE => array_keys( $this->getTokenFunctions() ), - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ), + ApiBase::PARAM_ISMULTI => true ), ); } public function getParamDescription() { - return array ( + return array( 'prop' => array( 'What pieces of information to include', - ' blockinfo - tags if the user is blocked, by whom, and for what reason', - ' groups - lists all the groups the user belongs to', - ' editcount - adds the user\'s edit count', - ' 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"', + ' blockinfo - Tags if the user is blocked, by whom, and for what reason', + ' groups - Lists all the groups the user(s) belongs to', + ' rights - Lists all the rights the user(s) has', + ' 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', 'token' => 'Which tokens to obtain for each user', @@ -264,6 +298,6 @@ if ( !defined( 'MEDIAWIKI' ) ) { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryUsers.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryUsers.php 85354 2011-04-04 18:25:31Z demon $'; } } diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php index caac0706..784f89c0 100644 --- a/includes/api/ApiQueryWatchlist.php +++ b/includes/api/ApiQueryWatchlist.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 25, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -37,7 +38,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryWatchlist extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'wl' ); + parent::__construct( $query, $moduleName, 'wl' ); } public function execute() { @@ -50,38 +51,23 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { private $fld_ids = false, $fld_title = false, $fld_patrol = false, $fld_flags = false, $fld_timestamp = false, $fld_user = false, $fld_comment = false, $fld_parsedcomment = false, $fld_sizes = false, - $fld_notificationtimestamp = false; + $fld_notificationtimestamp = false, $fld_userid = false; private function run( $resultPageSet = null ) { - global $wgUser; - $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' ); $params = $this->extractRequestParams(); - 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; - } + $user = $this->getWatchlistUser( $params ); if ( !is_null( $params['prop'] ) && is_null( $resultPageSet ) ) { - $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_userid = isset( $prop['userid'] ); $this->fld_comment = isset( $prop['comment'] ); $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); $this->fld_timestamp = isset( $prop['timestamp'] ); @@ -90,19 +76,20 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->fld_notificationtimestamp = isset( $prop['notificationtimestamp'] ); if ( $this->fld_patrol ) { - if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) + if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) { $this->dieUsage( 'patrol property is not available', 'patrol' ); + } } } - - $this->addFields( array ( + + $this->addFields( array( 'rc_namespace', 'rc_title', 'rc_timestamp' ) ); if ( is_null( $resultPageSet ) ) { - $this->addFields( array ( + $this->addFields( array( 'rc_cur_id', 'rc_this_oldid' ) ); @@ -110,7 +97,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $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', $this->fld_user || $this->fld_userid ); $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 ); @@ -123,22 +110,26 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->addFields( 'rc_cur_id' ); } - $this->addTables( array ( + $this->addTables( array( 'watchlist', 'page', 'recentchanges' ) ); $userId = $user->getId(); - $this->addWhere( array ( + $this->addWhere( array( 'wl_namespace = rc_namespace', 'wl_title = rc_title', 'rc_cur_id = page_id', 'wl_user' => $userId, 'rc_deleted' => 0, ) ); + + $db = $this->getDB(); - $this->addWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] ); + $this->addWhereRange( 'rc_timestamp', $params['dir'], + $db->timestamp( $params['start'] ), + $db->timestamp( $params['end'] ) ); $this->addWhereFld( 'wl_namespace', $params['namespace'] ); $this->addWhereIf( 'rc_this_oldid=page_latest', !$params['allrev'] ); @@ -149,45 +140,53 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 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'] ) ) ) { - + || ( isset ( $show['patrolled'] ) && isset ( $show['!patrolled'] ) ) + ) + { $this->dieUsageMsg( array( 'show' ) ); } - + // 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' ); + if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) { + global $wgUser; + if ( !$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_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'] ) ) + 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'] ) ) + } + if ( !is_null( $params['user'] ) ) { $this->addWhereFld( 'rc_user_text', $params['user'] ); - if ( !is_null( $params['excludeuser'] ) ) + } + if ( !is_null( $params['excludeuser'] ) ) { $this->addWhere( 'rc_user_text != ' . $this->getDB()->addQuotes( $params['excludeuser'] ) ); + } + + - $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->addWhereIf( "rc_timestamp > ''", !isset( $params['start'] ) && !isset( $params['end'] ) && $db->getType() == 'mysql' ); $this->addOption( 'LIMIT', $params['limit'] + 1 ); - $ids = array (); + $ids = array(); $count = 0; $res = $this->select( __METHOD__ ); - while ( $row = $db->fetchObject( $res ) ) { + foreach ( $res as $row ) { if ( ++ $count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); @@ -197,8 +196,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { if ( is_null( $resultPageSet ) ) { $vals = $this->extractRowInfo( $row ); $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); - if ( !$fit ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); break; @@ -212,12 +210,9 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } } - $db->freeResult( $res ); - if ( is_null( $resultPageSet ) ) { $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' ); - } - elseif ( $params['allrev'] ) { + } elseif ( $params['allrev'] ) { $resultPageSet->populateFromRevisionIDs( $ids ); } else { $resultPageSet->populateFromPageIDs( $ids ); @@ -225,8 +220,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } private function extractRowInfo( $row ) { - - $vals = array (); + $vals = array(); if ( $this->fld_ids ) { $vals['pageid'] = intval( $row->rc_cur_id ); @@ -235,41 +229,60 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $title = Title::makeTitle( $row->rc_namespace, $row->rc_title ); - if ( $this->fld_title ) + if ( $this->fld_title ) { ApiQueryBase::addTitleInfo( $vals, $title ); + } - if ( $this->fld_user ) { - $vals['user'] = $row->rc_user_text; - if ( !$row->rc_user ) + if ( $this->fld_user || $this->fld_userid ) { + + if ( $this->fld_user ) { + $vals['user'] = $row->rc_user_text; + } + + if ( $this->fld_userid ) { + $vals['user'] = $row->rc_user; + } + + if ( !$row->rc_user ) { $vals['anon'] = ''; + } } if ( $this->fld_flags ) { - if ( $row->rc_new ) + 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 ) + 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_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_notificationtimestamp ) { + $vals['notificationtimestamp'] = ( $row->wl_notificationtimestamp == null ) + ? '' + : wfTimestamp( TS_ISO_8601, $row->wl_notificationtimestamp ); + } + + 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 ); @@ -279,46 +292,47 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } public function getAllowedParams() { - return array ( + return array( 'allrev' => false, - 'start' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'start' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), - 'end' => array ( - ApiBase :: PARAM_TYPE => 'timestamp' + 'end' => array( + ApiBase::PARAM_TYPE => 'timestamp' ), 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace' + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace' ), 'user' => array( - ApiBase :: PARAM_TYPE => 'user', + ApiBase::PARAM_TYPE => 'user', ), 'excludeuser' => array( - ApiBase :: PARAM_TYPE => 'user', + ApiBase::PARAM_TYPE => 'user', ), - 'dir' => array ( - ApiBase :: PARAM_DFLT => 'older', - ApiBase :: PARAM_TYPE => array ( + 'dir' => array( + ApiBase::PARAM_DFLT => 'older', + ApiBase::PARAM_TYPE => array( 'newer', 'older' ) ), - 'limit' => array ( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), - 'prop' => array ( - APIBase :: PARAM_ISMULTI => true, - APIBase :: PARAM_DFLT => 'ids|title|flags', - APIBase :: PARAM_TYPE => array ( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_DFLT => 'ids|title|flags', + ApiBase::PARAM_TYPE => array( 'ids', 'title', 'flags', 'user', + 'userid', 'comment', 'parsedcomment', 'timestamp', @@ -327,9 +341,9 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'notificationtimestamp' ) ), - 'show' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + 'show' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'minor', '!minor', 'bot', @@ -340,39 +354,52 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { '!patrolled', ) ), - 'owner' => array ( - ApiBase :: PARAM_TYPE => 'user' + 'owner' => array( + ApiBase::PARAM_TYPE => 'user' ), - 'token' => array ( - ApiBase :: PARAM_TYPE => 'string' + 'token' => array( + ApiBase::PARAM_TYPE => 'string' ) ); } public function getParamDescription() { - return array ( - 'allrev' => 'Include multiple revisions of the same page within given timeframe.', - 'start' => 'The timestamp to start enumerating from.', - 'end' => 'The timestamp to end enumerating.', - 'namespace' => 'Filter changes to only the given namespace(s).', + return array( + 'allrev' => 'Include multiple revisions of the same page within given timeframe', + 'start' => 'The timestamp to start enumerating from', + 'end' => 'The timestamp to end enumerating', + 'namespace' => 'Filter changes to only the given namespace(s)', '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 ( + 'dir' => 'In which direction to enumerate pages', + 'limit' => 'How many total results to return per request', + 'prop' => array( + 'Which additional items to get (non-generator mode only).', + ' ids - Adds revision ids and page ids', + ' title - Adds title of the page', + ' flags - Adds flags for the edit', + ' user - Adds the user who made the edit', + ' userid - Adds user id of whom made the edit', + ' comment - Adds comment of the edit', + ' parsedcomment - Adds parsed comment of the edit', + ' timestamp - Adds timestamp of the edit', + ' patrol - Tags edits that are patrolled', + ' size - Adds the old and new lengths of the page', + ' notificationtimestamp - Adds timestamp of when the user was last notified about the edit', + ), + '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' + "For example, to see only minor edits done by logged-in users, set {$this->getModulePrefix()}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" + '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' ), @@ -386,17 +413,17 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=watchlist', '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&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 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryWatchlist.php 85435 2011-04-05 14:00:08Z demon $'; } } diff --git a/includes/api/ApiQueryWatchlistRaw.php b/includes/api/ApiQueryWatchlistRaw.php index 42d4005b..0e5617e3 100644 --- a/includes/api/ApiQueryWatchlistRaw.php +++ b/includes/api/ApiQueryWatchlistRaw.php @@ -1,11 +1,10 @@ .@home.nl + * Created on Oct 4, 2008 + * + * 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiQueryBase.php' ); + require_once( 'ApiQueryBase.php' ); } /** @@ -37,7 +38,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { - parent :: __construct( $query, $moduleName, 'wr' ); + parent::__construct( $query, $moduleName, 'wr' ); } public function execute() { @@ -49,54 +50,54 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase { } private function run( $resultPageSet = null ) { - global $wgUser; - $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' ); - if ( !$wgUser->isLoggedIn() ) - $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' ); $params = $this->extractRequestParams(); + + $user = $this->getWatchlistUser( $params ); + $prop = array_flip( (array)$params['prop'] ); $show = array_flip( (array)$params['show'] ); - if ( isset( $show['changed'] ) && isset( $show['!changed'] ) ) + 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_user', $user->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'] ) ) - { + if ( isset( $params['continue'] ) ) { $cont = explode( '|', $params['continue'] ); - if ( count( $cont ) != 2 ) + 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')" ); + $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 ) + if ( count( $params['namespace'] ) == 1 ) { $this->addOption( 'ORDER BY', 'wl_title' ); - else + } else { $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 ) ) - { - if ( ++$count > $params['limit'] ) - { + foreach ( $res as $row ) { + if ( ++$count > $params['limit'] ) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $this->keyToTitle( $row->wl_title ) ); @@ -104,88 +105,102 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase { } $t = Title::makeTitle( $row->wl_namespace, $row->wl_title ); - if ( is_null( $resultPageSet ) ) - { + 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 ) - { + if ( !$fit ) { $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $this->keyToTitle( $row->wl_title ) ); break; } - } - else + } else { $titles[] = $t; + } } - if ( is_null( $resultPageSet ) ) + if ( is_null( $resultPageSet ) ) { $this->getResult()->setIndexedTagName_internal( $this->getModuleName(), 'wr' ); - else + } else { $resultPageSet->populateFromTitles( $titles ); + } } public function getAllowedParams() { - return array ( + return array( 'continue' => null, - 'namespace' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => 'namespace' + 'namespace' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => 'namespace' ), - 'limit' => array ( - ApiBase :: PARAM_DFLT => 10, - ApiBase :: PARAM_TYPE => 'limit', - ApiBase :: PARAM_MIN => 1, - ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1, - ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + 'limit' => array( + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 ), - 'prop' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + 'prop' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'changed', ) ), - 'show' => array ( - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( + 'show' => array( + ApiBase::PARAM_ISMULTI => true, + ApiBase::PARAM_TYPE => array( 'changed', '!changed', ) + ), + 'owner' => array( + ApiBase::PARAM_TYPE => 'user' + ), + 'token' => array( + ApiBase::PARAM_TYPE => 'string' ) ); } public function getParamDescription() { - return array ( + return array( 'continue' => 'When more results are available, use this to continue', - 'namespace' => 'Only list pages in the given namespace(s).', - 'limit' => 'How many total results to return per request.', - 'prop' => 'Which additional properties to get (non-generator mode only).', - 'show' => 'Only list items that meet these criteria.', + 'namespace' => 'Only list pages in the given namespace(s)', + 'limit' => 'How many total results to return per request', + 'prop' => array( + 'Which additional properties to get (non-generator mode only)', + ' changed - Adds timestamp of when the user was last notified about the edit', + ), + 'show' => 'Only list items that meet these criteria', + 'owner' => 'The name of the user whose watchlist you\'d like to access', + 'token' => 'Give a security token (settable in preferences) to allow access to another user\'s watchlist', ); } 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' ), + 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' ), ) ); } protected function getExamples() { - return array ( + return array( 'api.php?action=query&list=watchlistraw', 'api.php?action=query&generator=watchlistraw&gwrshow=changed&prop=revisions', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryWatchlistRaw.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryWatchlistRaw.php 70647 2010-08-07 19:59:42Z ialex $'; } } \ No newline at end of file diff --git a/includes/api/ApiResult.php b/includes/api/ApiResult.php index 64c2c3fb..9d42a58e 100644 --- a/includes/api/ApiResult.php +++ b/includes/api/ApiResult.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 4, 2006 + * + * Copyright © 2006 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -54,7 +55,7 @@ class ApiResult extends ApiBase { * @param $main ApiMain object */ public function __construct( $main ) { - parent :: __construct( $main, 'result' ); + parent::__construct( $main, 'result' ); $this->mIsRawMode = false; $this->mCheckingSize = true; $this->reset(); @@ -64,7 +65,7 @@ class ApiResult extends ApiBase { * Clear the current result data. */ public function reset() { - $this->mData = array (); + $this->mData = array(); $this->mSize = 0; } @@ -100,12 +101,14 @@ class ApiResult extends ApiBase { */ public static function size( $value ) { $s = 0; - if ( is_array( $value ) ) - foreach ( $value as $v ) + if ( is_array( $value ) ) { + foreach ( $value as $v ) { $s += self::size( $v ); - else if ( !is_object( $value ) ) + } + } elseif ( !is_object( $value ) ) { // Objects can't always be cast to string $s = strlen( $value ); + } return $s; } @@ -139,57 +142,65 @@ class ApiResult extends ApiBase { * @param $arr array to add $value to * @param $name string Index of $arr to add $value at * @param $value mixed + * @param $overwrite bool Whether overwriting an existing element is allowed */ - 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, $overwrite = false ) { + 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] ) || $overwrite ) { $arr[$name] = $value; - } - elseif ( is_array( $arr[$name] ) && is_array( $value ) ) { + } elseif ( is_array( $arr[$name] ) && is_array( $value ) ) { $merged = array_intersect_key( $arr[$name], $value ); - if ( !count( $merged ) ) + if ( !count( $merged ) ) { $arr[$name] += $value; - else - ApiBase :: dieDebug( __METHOD__, "Attempting to merge element $name" ); - } else - ApiBase :: dieDebug( __METHOD__, "Attempting to add element $name=$value, existing value is {$arr[$name]}" ); + } else { + ApiBase::dieDebug( __METHOD__, "Attempting to merge element $name" ); + } + } else { + ApiBase::dieDebug( __METHOD__, "Attempting to add element $name=$value, existing value is {$arr[$name]}" ); + } } /** * Adds a content element to an array. * Use this function instead of hardcoding the '*' element. * @param $arr array to add the content element to + * @param $value Mixed * @param $subElemName string when present, content element is created * as a sub item of $arr. Use this parameter to create elements in * format text without attributes */ - public static function setContent( & $arr, $value, $subElemName = null ) { - if ( is_array( $value ) ) - ApiBase :: dieDebug( __METHOD__, 'Bad parameter' ); + 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 ); + ApiResult::setElement( $arr, '*', $value ); } else { - if ( !isset ( $arr[$subElemName] ) ) - $arr[$subElemName] = array (); - ApiResult :: setElement( $arr[$subElemName], '*', $value ); + if ( !isset( $arr[$subElemName] ) ) { + $arr[$subElemName] = array(); + } + ApiResult::setElement( $arr[$subElemName], '*', $value ); } } /** * In case the array contains indexed values (in addition to named), * give all indexed values the given tag name. This function MUST be - * called on every arrray that has numerical indexes. + * called on every array that has numerical indexes. * @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; } @@ -200,14 +211,15 @@ class ApiResult extends ApiBase { * @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 ); + if ( !is_array( $arr ) ) { + return; + } + foreach ( $arr as &$a ) { + if ( !is_array( $a ) ) { + continue; + } + $this->setIndexedTagName( $a, $tag ); + $this->setIndexedTagName_recursive( $a, $tag ); } } @@ -219,56 +231,72 @@ class ApiResult extends ApiBase { * @param $tag string */ public function setIndexedTagName_internal( $path, $tag ) { - $data = & $this->mData; + $data = &$this->mData; foreach ( (array)$path as $p ) { if ( !isset( $data[$p] ) ) { $data[$p] = array(); } - $data = & $data[$p]; + $data = &$data[$p]; } - if ( is_null( $data ) ) + if ( is_null( $data ) ) { return; + } $this->setIndexedTagName( $data, $tag ); } /** * Add value to the output data at the given path. - * Path is an indexed array, each element specifing the branch at which to add the new value + * Path is an indexed array, each element specifying the branch at which to add the new value * Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value * If $name is empty, the $value is added as a next list element data[] = $value * @return bool True if $value fits in the result, false if not */ - public function addValue( $path, $name, $value ) { + public function addValue( $path, $name, $value, $overwrite = false ) { global $wgAPIMaxResultSize; - $data = & $this->mData; + $data = &$this->mData; if ( $this->mCheckingSize ) { $newsize = $this->mSize + self::size( $value ); - if ( $newsize > $wgAPIMaxResultSize ) + if ( $newsize > $wgAPIMaxResultSize ) { return false; + } $this->mSize = $newsize; } if ( !is_null( $path ) ) { if ( is_array( $path ) ) { foreach ( $path as $p ) { - if ( !isset ( $data[$p] ) ) - $data[$p] = array (); - $data = & $data[$p]; + if ( !isset( $data[$p] ) ) { + $data[$p] = array(); + } + $data = &$data[$p]; } } else { - if ( !isset ( $data[$path] ) ) - $data[$path] = array (); - $data = & $data[$path]; + if ( !isset( $data[$path] ) ) { + $data[$path] = array(); + } + $data = &$data[$path]; } } - if ( !$name ) - $data[] = $value; // Add list element - else - ApiResult :: setElement( $data, $name, $value ); // Add named element + if ( !$name ) { + $data[] = $value; // Add list element + } else { + self::setElement( $data, $name, $value, $overwrite ); // Add named element + } return true; } + /** + * Add a parsed limit=max to the result. + * + * @param $moduleName string + * @param $limit int + */ + public function setParsedLimit( $moduleName, $limit ) { + // Add value, allowing overwriting + $this->addValue( 'limits', $moduleName, $limit, true ); + } + /** * Unset a value previously added to the result set. * Fails silently if the value isn't found. @@ -277,13 +305,15 @@ class ApiResult extends ApiBase { * @param $name string */ public function unsetValue( $path, $name ) { - $data = & $this->mData; - if ( !is_null( $path ) ) + $data = &$this->mData; + if ( !is_null( $path ) ) { foreach ( (array)$path as $p ) { - if ( !isset( $data[$p] ) ) + if ( !isset( $data[$p] ) ) { return; - $data = & $data[$p]; + } + $data = &$data[$p]; } + } $this->mSize -= self::size( $data[$name] ); unset( $data[$name] ); } @@ -291,27 +321,26 @@ class ApiResult extends ApiBase { /** * Ensure all values in this result are valid UTF-8. */ - public function cleanUpUTF8() - { + public function cleanUpUTF8() { array_walk_recursive( $this->mData, array( 'ApiResult', 'cleanUp_helper' ) ); } /** * Callback function for cleanUpUTF8() */ - private static function cleanUp_helper( &$s ) - { - if ( !is_string( $s ) ) + private static function cleanUp_helper( &$s ) { + if ( !is_string( $s ) ) { return; + } 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 62354 2010-02-12 06:44:16Z mah $'; + return __CLASS__ . ': $Id: ApiResult.php 74230 2010-10-03 19:07:11Z reedy $'; } } diff --git a/includes/api/ApiRollback.php b/includes/api/ApiRollback.php index 5c259f4e..e31bfed8 100644 --- a/includes/api/ApiRollback.php +++ b/includes/api/ApiRollback.php @@ -1,10 +1,10 @@ .@home.nl + * Created on Jun 20, 2007 + * + * 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 @@ -18,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -33,39 +35,27 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiRollback extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } + private $mTitleObj = null, $mUser = null; + 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' ) ); - - $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 and title already validated in call to getTokenSalt from Main + $titleObj = $this->getTitle(); $articleObj = new Article( $titleObj ); - $summary = ( isset( $params['summary'] ) ? $params['summary'] : "" ); + $summary = ( isset( $params['summary'] ) ? $params['summary'] : '' ); $details = null; - $retval = $articleObj->doRollback( $username, $summary, $params['token'], $params['markbot'], $details ); + $retval = $articleObj->doRollback( $this->getUser(), $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->setWatch( $params['watchlist'], $titleObj ); $info = array( 'title' => $titleObj->getPrefixedText(), @@ -79,57 +69,121 @@ class ApiRollback extends ApiBase { $this->getResult()->addValue( null, $this->getModuleName(), $info ); } - public function mustBePosted() { return true; } + public function mustBePosted() { + return true; + } public function isWriteMode() { return true; } public function getAllowedParams() { - return array ( - 'title' => null, - 'user' => null, + return array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), + 'user' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'token' => null, 'summary' => null, - 'markbot' => false + 'markbot' => false, + 'watchlist' => array( + ApiBase::PARAM_DFLT => 'preferences', + ApiBase::PARAM_TYPE => array( + 'watch', + 'unwatch', + 'preferences', + 'nochange' + ), + ), ); } public function getParamDescription() { - return array ( + return array( 'title' => 'Title of the page you want to rollback.', 'user' => 'Name of the user whose edits are to be rolled back. If set incorrectly, you\'ll get a badtoken error.', - 'token' => 'A rollback token previously retrieved through prop=revisions', - 'summary' => 'Custom edit summary. If not set, default summary will be used.', - 'markbot' => 'Mark the reverted edits and the revert as bot edits' + 'token' => "A rollback token previously retrieved through {$this->getModulePrefix()}prop=revisions", + 'summary' => 'Custom edit summary. If not set, default summary will be used', + 'markbot' => 'Mark the reverted edits and the revert as bot edits', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', ); } public function getDescription() { return array( - 'Undo the last edit to the page. If the last user who edited the page made multiple edits in a row,', - 'they will all be rolled back.' - ); + 'Undo the last edit to the page. If the last user who edited the page made multiple edits in a row,', + 'they will all be rolled back' + ); } - + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( - array( 'missingparam', 'title' ), - array( 'missingparam', 'user' ), array( 'invalidtitle', 'title' ), array( 'notanarticle' ), array( 'invaliduser', 'user' ), ) ); } + public function needsToken() { + return true; + } + + public function getTokenSalt() { + return array( $this->getTitle()->getPrefixedText(), $this->getUser() ); + } + + private function getUser() { + if ( $this->mUser !== null ) { + return $this->mUser; + } + + $params = $this->extractRequestParams(); + + // We need to be able to revert IPs, but getCanonicalName rejects them + $this->mUser = User::isIP( $params['user'] ) + ? $params['user'] + : User::getCanonicalName( $params['user'] ); + if ( !$this->mUser ) { + $this->dieUsageMsg( array( 'invaliduser', $params['user'] ) ); + } + + return $this->mUser; + } + + /** + * @return Title + */ + private function getTitle() { + if ( $this->mTitleObj !== null ) { + return $this->mTitleObj; + } + + $params = $this->extractRequestParams(); + + $this->mTitleObj = Title::newFromText( $params['title'] ); + + if ( !$this->mTitleObj ) { + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + } + if ( !$this->mTitleObj->exists() ) { + $this->dieUsageMsg( array( 'notanarticle' ) ); + } + + return $this->mTitleObj; + } + protected function getExamples() { - return array ( + return array( 'api.php?action=rollback&title=Main%20Page&user=Catrope&token=123ABC', 'api.php?action=rollback&title=Main%20Page&user=217.121.114.116&token=123ABC&summary=Reverting%20vandalism&markbot=1' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiRollback.php 65371 2010-04-21 10:41:25Z tstarling $'; + return __CLASS__ . ': $Id: ApiRollback.php 75602 2010-10-28 00:04:48Z reedy $'; } } diff --git a/includes/api/ApiRsd.php b/includes/api/ApiRsd.php new file mode 100644 index 00000000..7bc4722c --- /dev/null +++ b/includes/api/ApiRsd.php @@ -0,0 +1,180 @@ +getResult(); + + $result->addValue( null, 'version', '1.0' ); + $result->addValue( null, 'xmlns', 'http://archipelago.phrasewise.com/rsd' ); + + $service = array( 'apis' => $this->formatRsdApiList() ); + ApiResult::setContent( $service, 'MediaWiki', 'engineName' ); + ApiResult::setContent( $service, 'http://www.mediawiki.org/', 'engineLink' ); + + $result->setIndexedTagName( $service['apis'], 'api' ); + + $result->addValue( null, 'service', $service ); + } + + public function getCustomPrinter() { + return new ApiFormatXmlRsd( $this->getMain(), 'xml' ); + } + + public function getAllowedParams() { + return array(); + } + + public function getParamDescription() { + return array(); + } + + public function getDescription() { + return 'Export an RSD schema'; + } + + protected function getExamples() { + return array( + 'api.php?action=rsd' + ); + } + + /** + * Builds an internal list of APIs to expose information about. + * Normally this only lists the MediaWiki API, with its base URL, + * link to documentation, and a marker as to available authentication + * (to aid in OAuth client apps switching to support in the future). + * + * Extensions can expose other APIs, such as WordPress or Twitter- + * compatible APIs, by hooking 'ApiRsdServiceApis' and adding more + * elements to the array. + * + * See http://cyber.law.harvard.edu/blogs/gems/tech/rsd.html for + * the base RSD spec, and check WordPress and StatusNet sites for + * in-production examples listing several blogging and micrblogging + * APIs. + * + * @return array + */ + protected function getRsdApiList() { + $apis = array( + 'MediaWiki' => array( + // The API link is required for all RSD API entries. + 'apiLink' => wfExpandUrl( wfScript( 'api' ) ), + + // Docs link is optional, but recommended. + 'docs' => 'http://mediawiki.org/wiki/API', + + // Some APIs may need a blog ID, but it may be left blank. + 'blogID' => '', + + // Additional settings are optional. + 'settings' => array( + // Change this to true in the future as an aid to + // machine discovery of OAuth for API access. + 'OAuth' => false, + ) + ), + ); + wfRunHooks( 'ApiRsdServiceApis', array( &$apis ) ); + return $apis; + } + + /** + * Formats the internal list of exposed APIs into an array suitable + * to pass to the API's XML formatter. + * + * @return array + */ + protected function formatRsdApiList() { + $apis = $this->getRsdApiList(); + + $outputData = array(); + foreach ( $apis as $name => $info ) { + $data = array( + 'name' => $name, + 'preferred' => wfBoolToStr( $name == 'MediaWiki' ), + 'apiLink' => $info['apiLink'], + 'blogID' => isset( $info['blogID'] ) ? $info['blogID'] : '', + ); + $settings = array(); + if ( isset( $info['docs'] ) ) { + ApiResult::setContent( $settings, $info['docs'], 'docs' ); + } + if ( isset( $info['settings'] ) ) { + foreach ( $info['settings'] as $setting => $val ) { + if ( is_bool( $val ) ) { + $xmlVal = wfBoolToStr( $val ); + } else { + $xmlVal = $val; + } + $setting = array( 'name' => $setting ); + ApiResult::setContent( $setting, $xmlVal ); + $settings[] = $setting; + } + } + if ( count( $settings ) ) { + $this->getResult()->setIndexedTagName( $settings, 'setting' ); + $data['settings'] = $settings; + } + $outputData[] = $data; + } + return $outputData; + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiRsd.php 76195 2010-11-06 15:57:15Z btongminh $'; + } +} + +class ApiFormatXmlRsd extends ApiFormatXml { + public function __construct( $main, $format ) { + parent::__construct( $main, $format ); + $this->setRootElement( 'rsd' ); + } + + public function getMimeType() { + return 'application/rsd+xml'; + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiRsd.php 76195 2010-11-06 15:57:15Z btongminh $'; + } +} diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php index 1c4a4ade..4f6e4fb7 100644 --- a/includes/api/ApiUnblock.php +++ b/includes/api/ApiUnblock.php @@ -1,10 +1,10 @@ .@home.nl + * Created on Sep 7, 2007 + * + * 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 @@ -18,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -36,7 +38,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiUnblock extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } /** @@ -46,27 +48,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 ); return; } - if ( is_null( $params['id'] ) && is_null( $params['user'] ) ) + if ( is_null( $params['id'] ) && is_null( $params['user'] ) ) { $this->dieUsageMsg( array( 'unblock-notarget' ) ); - if ( !is_null( $params['id'] ) && !is_null( $params['user'] ) ) + } + if ( !is_null( $params['id'] ) && !is_null( $params['user'] ) ) { $this->dieUsageMsg( array( 'unblock-idanduser' ) ); + } - if ( !$wgUser->isAllowed( 'block' ) ) + if ( !$wgUser->isAllowed( 'block' ) ) { $this->dieUsageMsg( array( 'cantunblock' ) ); + } + # bug 15810: blocked admins should have limited access here + if ( $wgUser->isBlocked() ) { + $status = IPBlockForm::checkUnblockSelf( $params['user'] ); + if ( $status !== true ) { + $this->dieUsageMsg( array( $status ) ); + } + } $id = $params['id']; $user = $params['user']; $reason = ( is_null( $params['reason'] ) ? '' : $params['reason'] ); $retval = IPUnblockForm::doUnblock( $id, $user, $reason, $range ); - if ( $retval ) + if ( $retval ) { $this->dieUsageMsg( $retval ); + } $res['id'] = intval( $id ); $res['user'] = $user; @@ -83,7 +95,7 @@ class ApiUnblock extends ApiBase { } public function getAllowedParams() { - return array ( + return array( 'id' => null, 'user' => null, 'token' => null, @@ -93,29 +105,30 @@ class ApiUnblock extends ApiBase { } public function getParamDescription() { - return array ( - 'id' => 'ID of the block you want to unblock (obtained through list=blocks). Cannot be used together with user', - 'user' => 'Username, IP address or IP range you want to unblock. Cannot be used together with id', - 'token' => 'An unblock token previously obtained through the gettoken parameter or prop=info', + $p = $this->getModulePrefix(); + return array( + 'id' => "ID of the block you want to unblock (obtained through list=blocks). Cannot be used together with {$p}user", + 'user' => "Username, IP address or IP range you want to unblock. Cannot be used together with {$p}id", + 'token' => "An unblock token previously obtained through the gettoken parameter or {$p}prop=info", 'gettoken' => 'If set, an unblock token will be returned, and no other action will be taken', 'reason' => 'Reason for unblock (optional)', ); } public function getDescription() { - return array( - 'Unblock a user.' - ); + return 'Unblock a user'; } - - public function getPossibleErrors() { + + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'unblock-notarget' ), array( 'unblock-idanduser' ), array( 'cantunblock' ), - ) ); + array( 'ipbblocked' ), + array( 'ipbnounblockself' ), + ) ); } - + public function needsToken() { return true; } @@ -125,13 +138,13 @@ class ApiUnblock extends ApiBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=unblock&id=105', 'api.php?action=unblock&user=Bob&reason=Sorry%20Bob' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiUnblock.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiUnblock.php 74098 2010-10-01 20:12:50Z reedy $'; } } diff --git a/includes/api/ApiUndelete.php b/includes/api/ApiUndelete.php index ae705b69..3c7d91a5 100644 --- a/includes/api/ApiUndelete.php +++ b/includes/api/ApiUndelete.php @@ -1,10 +1,10 @@ .@home.nl + * Created on Jul 3, 2007 + * + * 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 @@ -18,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -33,45 +35,49 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiUndelete extends ApiBase { public function __construct( $main, $action ) { - parent :: __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 ( !$wgUser->isAllowed( 'undelete' ) ) + if ( !$wgUser->isAllowed( 'undelete' ) ) { $this->dieUsageMsg( array( 'permdenied-undelete' ) ); + } - if ( $wgUser->isBlocked() ) + if ( $wgUser->isBlocked() ) { $this->dieUsageMsg( array( 'blockedtext' ) ); + } $titleObj = Title::newFromText( $params['title'] ); - if ( !$titleObj ) + 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'] ) ) + } + if ( !is_array( $params['timestamps'] ) ) { $params['timestamps'] = array( $params['timestamps'] ); - foreach ( $params['timestamps'] as $i => $ts ) + } + foreach ( $params['timestamps'] as $i => $ts ) { $params['timestamps'][$i] = wfTimestamp( TS_MW, $ts ); + } $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 ) ) + if ( !is_array( $retval ) ) { $this->dieUsageMsg( array( 'cannotundelete' ) ); + } - if ( $retval[1] ) + if ( $retval[1] ) { wfRunHooks( 'FileUndeleteComplete', array( $titleObj, array(), $wgUser, $params['reason'] ) ); + } + + $this->setWatch( $params['watchlist'], $titleObj ); $info['title'] = $titleObj->getPrefixedText(); $info['revisions'] = intval( $retval[0] ); @@ -89,22 +95,35 @@ class ApiUndelete extends ApiBase { } public function getAllowedParams() { - return array ( - 'title' => null, + return array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'token' => null, - 'reason' => "", + 'reason' => '', 'timestamps' => array( - ApiBase :: PARAM_ISMULTI => true - ) + ApiBase::PARAM_ISMULTI => true + ), + 'watchlist' => array( + ApiBase::PARAM_DFLT => 'preferences', + ApiBase::PARAM_TYPE => array( + 'watch', + 'unwatch', + 'preferences', + 'nochange' + ), + ), ); } public function getParamDescription() { - return array ( - 'title' => 'Title of the page you want to restore.', + return array( + 'title' => 'Title of the page you want to restore', 'token' => 'An undelete token previously retrieved through list=deletedrevs', 'reason' => 'Reason for restoring (optional)', - 'timestamps' => 'Timestamps of the revisions to restore. If not set, all revisions will be restored.' + 'timestamps' => 'Timestamps of the revisions to restore. If not set, all revisions will be restored.', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', ); } @@ -114,17 +133,16 @@ 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 needsToken() { return true; } @@ -134,13 +152,13 @@ class ApiUndelete extends ApiBase { } protected function getExamples() { - return array ( + return array( 'api.php?action=undelete&title=Main%20Page&token=123ABC&reason=Restoring%20main%20page', 'api.php?action=undelete&title=Main%20Page&token=123ABC×tamps=20070703220045|20070702194856' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiUndelete.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiUndelete.php 74098 2010-10-01 20:12:50Z reedy $'; } } diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index 06688997..e7d7b939 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -1,9 +1,10 @@ + * Created on Aug 21, 2008 + * + * Copyright © 2008 - 2010 Bryan Tong Minh * * 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 @@ -17,8 +18,10 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { @@ -38,141 +41,269 @@ class ApiUpload extends ApiBase { } public function execute() { - global $wgUser, $wgAllowCopyUploads; + global $wgUser; // Check whether upload is enabled - if ( !UploadBase::isEnabled() ) + if ( !UploadBase::isEnabled() ) { $this->dieUsageMsg( array( 'uploaddisabled' ) ); + } + // Parameter handling $this->mParams = $this->extractRequestParams(); $request = $this->getMain()->getRequest(); - // Add the uploaded file to the params array $this->mParams['file'] = $request->getFileName( 'file' ); + // Select an upload module + if ( !$this->selectUploadModule() ) { + // This is not a true upload, but a status request or similar + return; + } + if ( !isset( $this->mUpload ) ) { + $this->dieUsage( 'No upload module set', 'nomodule' ); + } + + // First check permission to upload + $this->checkPermissions( $wgUser ); + + // Fetch the file + $status = $this->mUpload->fetchFile(); + if ( !$status->isGood() ) { + $errors = $status->getErrorsArray(); + $error = array_shift( $errors[0] ); + $this->dieUsage( 'Error fetching file from remote source', $error, 0, $errors[0] ); + } + + // Check if the uploaded file is sane + $this->verifyUpload(); + + // Check permission to upload this file + $permErrors = $this->mUpload->verifyPermissions( $wgUser ); + if ( $permErrors !== true ) { + // TODO: stash the upload and allow choosing a new name + $this->dieUsageMsg( array( 'badaccess-groups' ) ); + } + + // Prepare the API result + $result = array(); + + $warnings = $this->getApiWarnings(); + if ( $warnings ) { + $result['result'] = 'Warning'; + $result['warnings'] = $warnings; + // in case the warnings can be fixed with some further user action, let's stash this upload + // and return a key they can use to restart it + try { + $result['sessionkey'] = $this->performStash(); + } catch ( MWException $e ) { + $result['warnings']['stashfailed'] = $e->getMessage(); + } + } elseif ( $this->mParams['stash'] ) { + // Some uploads can request they be stashed, so as not to publish them immediately. + // In this case, a failure to stash ought to be fatal + try { + $result['result'] = 'Success'; + $result['sessionkey'] = $this->performStash(); + } catch ( MWException $e ) { + $this->dieUsage( $e->getMessage(), 'stashfailed' ); + } + } else { + // This is the most common case -- a normal upload with no warnings + // $result will be formatted properly for the API already, with a status + $result = $this->performUpload(); + } + + if ( $result['result'] === 'Success' ) { + $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() ); + } + + $this->getResult()->addValue( null, $this->getModuleName(), $result ); + + // Cleanup any temporary mess + $this->mUpload->cleanupTempFile(); + } + + /** + * Stash the file and return the session key + * Also re-raises exceptions with slightly more informative message strings (useful for API) + * @throws MWException + * @return {String} session key + */ + function performStash() { + try { + $sessionKey = $this->mUpload->stashSessionFile()->getSessionKey(); + } catch ( MWException $e ) { + throw new MWException( 'Stashing temporary file failed: ' . get_class($e) . ' ' . $e->getMessage() ); + } + return $sessionKey; + } + + + /** + * Select an upload module and set it to mUpload. Dies on failure. If the + * request was a status request and not a true upload, returns false; + * otherwise true + * + * @return bool + */ + protected function selectUploadModule() { + global $wgAllowAsyncCopyUploads; + $request = $this->getMain()->getRequest(); + // One and only one of the following parameters is needed $this->requireOnlyOneParameter( $this->mParams, - 'sessionkey', 'file', 'url' ); + 'sessionkey', 'file', 'url', 'statuskey' ); + + if ( $wgAllowAsyncCopyUploads && $this->mParams['statuskey'] ) { + // Status request for an async upload + $sessionData = UploadFromUrlJob::getSessionData( $this->mParams['statuskey'] ); + if ( !isset( $sessionData['result'] ) ) { + $this->dieUsage( 'No result in session data', 'missingresult'); + } + if ( $sessionData['result'] == 'Warning' ) { + $sessionData['warnings'] = $this->transformWarnings( $sessionData['warnings'] ); + $sessionData['sessionkey'] = $this->mParams['statuskey']; + } + $this->getResult()->addValue( null, $this->getModuleName(), $sessionData ); + return false; + + } + + + // The following modules all require the filename parameter to be set + if ( is_null( $this->mParams['filename'] ) ) { + $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); + } + if ( $this->mParams['sessionkey'] ) { - /** - * Upload stashed in a previous request - */ - // Check the session key - if ( !isset( $_SESSION['wsUploadData'][$this->mParams['sessionkey']] ) ) + // Upload stashed in a previous request + $sessionData = $request->getSessionData( UploadBase::getSessionKeyName() ); + if ( !UploadFromStash::isValidSessionKey( $this->mParams['sessionkey'], $sessionData ) ) { $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' ); + $sessionData[$this->mParams['sessionkey']] ); + + + } elseif ( isset( $this->mParams['file'] ) ) { + $this->mUpload = new UploadFromFile(); + $this->mUpload->initialize( + $this->mParams['filename'], + $request->getUpload( 'file' ) + ); + } elseif ( isset( $this->mParams['url'] ) ) { + // Make sure upload by URL is enabled: + if ( !UploadFromUrl::isEnabled() ) { + $this->dieUsageMsg( array( 'copyuploaddisabled' ) ); + } + + $async = false; + if ( $this->mParams['asyncdownload'] ) { + if ( $this->mParams['leavemessage'] && !$this->mParams['ignorewarnings'] ) { + $this->dieUsage( 'Using leavemessage without ignorewarnings is not supported', + 'missing-ignorewarnings' ); + } + + if ( $this->mParams['leavemessage'] ) { + $async = 'async-leavemessage'; + } else { + $async = 'async'; } } - } else $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); + $this->mUpload = new UploadFromUrl; + $this->mUpload->initialize( $this->mParams['filename'], + $this->mParams['url'], $async ); - if ( !isset( $this->mUpload ) ) - $this->dieUsage( 'No upload module set', 'nomodule' ); + } + + return true; + } + /** + * Checks that the user has permissions to perform this upload. + * Dies with usage message on inadequate permissions. + * @param $user User The user to check. + */ + protected function checkPermissions( $user ) { // Check whether the user has the appropriate permissions to upload anyway - $permission = $this->mUpload->isAllowed( $wgUser ); + $permission = $this->mUpload->isAllowed( $user ); if ( $permission !== true ) { - if ( !$wgUser->isLoggedIn() ) + if ( !$user->isLoggedIn() ) { $this->dieUsageMsg( array( 'mustbeloggedin', 'upload' ) ); - else + } 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' ) ); + /** + * Performs file verification, dies on error. + */ + protected function verifyUpload( ) { + global $wgFileExtensions; + + $verification = $this->mUpload->verifyUpload( ); + if ( $verification['status'] === UploadBase::OK ) { + return; } // 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; + switch( $verification['status'] ) { + case UploadBase::EMPTY_FILE: + $this->dieUsage( 'The file you submitted was empty', 'empty-file' ); + break; + case UploadBase::FILE_TOO_LARGE: + $this->dieUsage( 'The file you submitted was too large', 'file-too-large' ); + break; + case UploadBase::FILETYPE_MISSING: + $this->dieUsage( 'The file is missing an extension', 'filetype-missing' ); + break; + case UploadBase::FILETYPE_BADTYPE: + $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::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; } + } + + + /** + * Check warnings if ignorewarnings is not set. + * Returns a suitable array for inclusion into API results if there were warnings + * Returns the empty array if there were no warnings + * + * @return array + */ + protected function getApiWarnings() { + $warnings = array(); + if ( !$this->mParams['ignorewarnings'] ) { $warnings = $this->mUpload->checkWarnings(); if ( $warnings ) { @@ -181,51 +312,70 @@ class ApiUpload extends ApiBase { if ( isset( $warnings['duplicate'] ) ) { $dupes = array(); - foreach ( $warnings['duplicate'] as $key => $dupe ) + foreach ( $warnings['duplicate'] as $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; } } + return $warnings; + } + + /** + * Perform the actual upload. Returns a suitable result array on success; + * dies on failure. + */ + protected function performUpload() { + global $wgUser; + // Use comment as initial page text by default - if ( is_null( $this->mParams['text'] ) ) + if ( is_null( $this->mParams['text'] ) ) { $this->mParams['text'] = $this->mParams['comment']; + } + + $file = $this->mUpload->getLocalFile(); + $watch = $this->getWatchlistValue( $this->mParams['watchlist'], $file->getTitle() ); + + // Deprecated parameters + if ( $this->mParams['watch'] ) { + $watch = true; + } // No errors, no warnings: do the upload $status = $this->mUpload->performUpload( $this->mParams['comment'], - $this->mParams['text'], $this->mParams['watch'], $wgUser ); + $this->mParams['text'], $watch, $wgUser ); if ( !$status->isGood() ) { $error = $status->getErrorsArray(); - $this->getResult()->setIndexedTagName( $result['details'], 'error' ); - $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error ); + if ( count( $error ) == 1 && $error[0][0] == 'async' ) { + // The upload can not be performed right now, because the user + // requested so + return array( + 'result' => 'Queued', + 'statuskey' => $error[0][1], + ); + } else { + $this->getResult()->setIndexedTagName( $error, '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; } @@ -240,36 +390,70 @@ class ApiUpload extends ApiBase { public function getAllowedParams() { $params = array( - 'filename' => null, + 'filename' => array( + ApiBase::PARAM_TYPE => 'string', + ), 'comment' => array( ApiBase::PARAM_DFLT => '' ), 'text' => null, 'token' => null, - 'watch' => false, + 'watch' => array( + ApiBase::PARAM_DFLT => false, + ApiBase::PARAM_DEPRECATED => true, + ), + 'watchlist' => array( + ApiBase::PARAM_DFLT => 'preferences', + ApiBase::PARAM_TYPE => array( + 'watch', + 'preferences', + 'nochange' + ), + ), 'ignorewarnings' => false, 'file' => null, 'url' => null, 'sessionkey' => null, + 'stash' => false, ); - return $params; + global $wgAllowAsyncCopyUploads; + if ( $wgAllowAsyncCopyUploads ) { + $params += array( + 'asyncdownload' => false, + 'leavemessage' => false, + 'statuskey' => null, + ); + } + return $params; } public function getParamDescription() { - return array( + $params = 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', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', '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', - ), + 'sessionkey' => 'Session key that identifies a previous upload that was stashed temporarily.', + 'stash' => 'If set, the server will not add the file to the repository and stash it temporarily.' ); + + global $wgAllowAsyncCopyUploads; + if ( $wgAllowAsyncCopyUploads ) { + $params += array( + 'asyncdownload' => 'Make fetching a URL asynchronous', + 'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished', + 'statuskey' => 'Fetch the upload status for this session key', + ); + } + + return $params; + } public function getDescription() { @@ -281,17 +465,16 @@ class ApiUpload extends ApiBase { '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.' + 'log out and then log back in). Also you must get and send an edit token before doing any upload stuff' ); } - - public function getPossibleErrors() { + + 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' ), @@ -303,9 +486,9 @@ class ApiUpload extends ApiBase { 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 needsToken() { return true; } diff --git a/includes/api/ApiUserrights.php b/includes/api/ApiUserrights.php index be0750d6..f9fe9ad2 100644 --- a/includes/api/ApiUserrights.php +++ b/includes/api/ApiUserrights.php @@ -1,10 +1,11 @@ .@home.nl + * Created on Mar 24, 2009 + * + * Copyright © 2009 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 @@ -18,13 +19,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( "ApiBase.php" ); + require_once( "ApiBase.php" ); } /** @@ -33,16 +36,17 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiUserrights extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } + private $mUser = null; + public function execute() { $params = $this->extractRequestParams(); - - // User already validated in call to getTokenSalt from Main + + $user = $this->getUser(); + $form = new UserrightsPage; - $user = $form->fetchUser( $params['user'] ); - $r['user'] = $user->getName(); list( $r['added'], $r['removed'] ) = $form->doSaveUserGroups( @@ -54,6 +58,29 @@ class ApiUserrights extends ApiBase { $this->getResult()->addValue( null, $this->getModuleName(), $r ); } + /** + * @return User + */ + private function getUser() { + if ( $this->mUser !== null ) { + return $this->mUser; + } + + $params = $this->extractRequestParams(); + + $form = new UserrightsPage; + $status = $form->fetchUser( $params['user'] ); + if ( !$status->isOK() ) { + $errors = $status->getErrorsArray(); + $this->dieUsageMsg( $errors[0] ); + } else { + $user = $status->value; + } + + $this->mUser = $user; + return $user; + } + public function mustBePosted() { return true; } @@ -64,24 +91,27 @@ class ApiUserrights extends ApiBase { public function getAllowedParams() { return array ( - 'user' => null, + 'user' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), 'add' => array( - ApiBase :: PARAM_TYPE => User::getAllGroups(), - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_TYPE => User::getAllGroups(), + ApiBase::PARAM_ISMULTI => true ), 'remove' => array( - ApiBase :: PARAM_TYPE => User::getAllGroups(), - ApiBase :: PARAM_ISMULTI => true + ApiBase::PARAM_TYPE => User::getAllGroups(), + ApiBase::PARAM_ISMULTI => true ), 'token' => null, 'reason' => array( - ApiBase :: PARAM_DFLT => '' + ApiBase::PARAM_DFLT => '' ) ); } public function getParamDescription() { - return array ( + return array( 'user' => 'User name', 'add' => 'Add the user to these groups', 'remove' => 'Remove the user from these groups', @@ -91,42 +121,24 @@ class ApiUserrights extends ApiBase { } public function getDescription() { - return array( - 'Add/remove a user to/from groups', - ); - } - - public function getPossibleErrors() { - return array_merge( parent::getPossibleErrors(), array( - array( 'missingparam', 'user' ), - ) ); + return 'Add/remove a user to/from groups'; } public function needsToken() { return true; } - - 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(); + public function getTokenSalt() { + return $this->getUser()->getName(); } protected function getExamples() { - return array ( + return array( 'api.php?action=userrights&user=FooBot&add=bot&remove=sysop|bureaucrat&token=123ABC' ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiUserrights.php 74217 2010-10-03 15:53:07Z reedy $'; + return __CLASS__ . ': $Id: ApiUserrights.php 75602 2010-10-28 00:04:48Z reedy $'; } } diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index 391d91e2..e9560a4d 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -1,11 +1,10 @@ @gmail.com, + * Created on Jan 4, 2008 + * + * Copyright © 2008 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 @@ -19,13 +18,15 @@ * * 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. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } /** @@ -36,35 +37,37 @@ if ( !defined( 'MEDIAWIKI' ) ) { class ApiWatch extends ApiBase { public function __construct( $main, $action ) { - parent :: __construct( $main, $action ); + parent::__construct( $main, $action ); } public function execute() { global $wgUser; - if ( !$wgUser->isLoggedIn() ) + 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 ) + if ( !$title ) { $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + } $article = new Article( $title ); $res = array( 'title' => $title->getPrefixedText() ); - if ( $params['unwatch'] ) - { + if ( $params['unwatch'] ) { $res['unwatched'] = ''; + $res['message'] = wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() ); $success = $article->doUnwatch(); - } - else - { + } else { $res['watched'] = ''; + $res['message'] = wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() ); $success = $article->doWatch(); } - if ( !$success ) + if ( !$success ) { $this->dieUsageMsg( array( 'hookaborted' ) ); + } $this->getResult()->addValue( null, $this->getModuleName(), $res ); } @@ -73,25 +76,27 @@ class ApiWatch extends ApiBase { } public function getAllowedParams() { - return array ( - 'title' => null, + return array( + 'title' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => true + ), + 'unwatch' => false, ); } public function getParamDescription() { - return array ( + return array( 'title' => 'The page to (un)watch', 'unwatch' => 'If set the page will be unwatched rather than watched', ); } public function getDescription() { - return array ( - 'Add or remove a page from/to the current user\'s watchlist' - ); + return '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' ), @@ -103,11 +108,11 @@ class ApiWatch extends ApiBase { protected function getExamples() { return array( 'api.php?action=watch&title=Main_Page', - 'api.php?action=watch&title=Main_Page&unwatch', + 'api.php?action=watch&title=Main_Page&unwatch=', ); } public function getVersion() { - return __CLASS__ . ': $Id: ApiWatch.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiWatch.php 77192 2010-11-23 22:05:27Z btongminh $'; } } -- cgit v1.2.2