X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fapi%2FApiBase.php;h=e1ba493ff3b7e46b4198b9858192f5cb5ee94731;hb=53f96171cccb378824f8708c3fe41cfb0fcdd62e;hp=2e204d589cf5357c059af34f69cb6dc287ddf912;hpb=f8dc68bd9885f86c52790d555581e5cda42315bc;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 2e204d589c..e1ba493ff3 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -39,7 +39,7 @@ * * @ingroup API */ -abstract class ApiBase { +abstract class ApiBase extends ContextSource { // These constants allow modules to specify exactly how to treat incoming parameters. @@ -72,6 +72,10 @@ abstract class ApiBase { $this->mMainModule = $mainModule; $this->mModuleName = $moduleName; $this->mModulePrefix = $modulePrefix; + + if ( !$this->isMain() ) { + $this->setContext( $mainModule->getContext() ); + } } /***************************************************************************** @@ -122,6 +126,9 @@ abstract class ApiBase { /** * Get the name of the module as shown in the profiler log + * + * @param $db DatabaseBase + * * @return string */ public function getModuleProfileName( $db = false ) { @@ -170,6 +177,20 @@ abstract class ApiBase { return $this->getResult()->getData(); } + /** + * Create a new RequestContext object to use e.g. for calls to other parts + * the software. + * The object will have the WebRequest and the User object set to the ones + * used in this instance. + * + * @deprecated since 1.19 use getContext to get the current context + * @return DerivativeContext + */ + public function createContext() { + wfDeprecated( __METHOD__, '1.19' ); + return new DerivativeContext( $this->getContext() ); + } + /** * Set warning section for this module. Users should monitor this * section to notice any changes in API. Multiple calls to this @@ -178,7 +199,8 @@ abstract class ApiBase { * @param $warning string Warning message */ public function setWarning( $warning ) { - $data = $this->getResult()->getData(); + $result = $this->getResult(); + $data = $result->getData(); if ( isset( $data['warnings'][$this->getModuleName()] ) ) { // Don't add duplicate warnings $warn_regex = preg_quote( $warning, '/' ); @@ -188,13 +210,13 @@ abstract class ApiBase { $oldwarning = $data['warnings'][$this->getModuleName()]['*']; // If there is a warning already, append it to the existing one $warning = "$oldwarning\n$warning"; - $this->getResult()->unsetValue( 'warnings', $this->getModuleName() ); + $result->unsetValue( 'warnings', $this->getModuleName() ); } $msg = array(); ApiResult::setContent( $msg, $warning ); - $this->getResult()->disableSizeCheck(); - $this->getResult()->addValue( 'warnings', $this->getModuleName(), $msg ); - $this->getResult()->enableSizeCheck(); + $result->disableSizeCheck(); + $result->addValue( 'warnings', $this->getModuleName(), $msg ); + $result->enableSizeCheck(); } /** @@ -214,7 +236,7 @@ abstract class ApiBase { public function makeHelpMsg() { static $lnPrfx = "\n "; - $msg = $this->getDescription(); + $msg = $this->getFinalDescription(); if ( $msg !== false ) { @@ -235,8 +257,7 @@ abstract class ApiBase { $msg .= "\nThis module only accepts POST requests"; } if ( $this->isReadMode() || $this->isWriteMode() || - $this->mustBePosted() ) - { + $this->mustBePosted() ) { $msg .= "\n"; } @@ -246,20 +267,8 @@ abstract class ApiBase { $msg .= "Parameters:\n$paramsMsg"; } - // Examples - $examples = $this->getExamples(); - if ( $examples !== false ) { - if ( !is_array( $examples ) ) { - $examples = array( - $examples - ); - } - - if ( count( $examples ) > 0 ) { - $msg .= 'Example' . ( count( $examples ) > 1 ? 's' : '' ) . ":\n "; - $msg .= implode( $lnPrfx, $examples ) . "\n"; - } - } + $msg .= $this->makeHelpArrayToString( $lnPrfx, "Example", $this->getExamples() ); + $msg .= $this->makeHelpArrayToString( $lnPrfx, "Help page", $this->getHelpUrls() ); if ( $this->getMain()->getShowVersions() ) { $versions = $this->getVersion(); @@ -282,10 +291,34 @@ abstract class ApiBase { return $msg; } + /** + * @param $prefix string Text to split output items + * @param $title string What is being output + * @param $input string|array + * @return string + */ + protected function makeHelpArrayToString( $prefix, $title, $input ) { + if ( $input === false ) { + return ''; + } + if ( !is_array( $input ) ) { + $input = array( + $input + ); + } + + if ( count( $input ) > 0 ) { + $msg = $title . ( count( $input ) > 1 ? 's' : '' ) . ":\n "; + $msg .= implode( $prefix, $input ) . "\n"; + return $msg; + } + return ''; + } + /** * Generates the parameter descriptions for this module, to be displayed in the * module's help. - * @return string + * @return string or false */ public function makeHelpMsgParameters() { $params = $this->getFinalParams(); @@ -344,7 +377,9 @@ abstract class ApiBase { switch ( $type ) { case 'namespace': // Special handling because namespaces are type-limited, yet they are not given - $desc .= $paramPrefix . $prompt . implode( ', ', MWNamespace::getValidNamespaces() ); + $desc .= $paramPrefix . $prompt; + $desc .= wordwrap( implode( ', ', MWNamespace::getValidNamespaces() ), + 100, $descWordwrap ); break; case 'limit': $desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]}"; @@ -401,6 +436,9 @@ abstract class ApiBase { /** * Callback for preg_replace_callback() call in makeHelpMsg(). * Replaces a source file name with a link to ViewVC + * + * @param $matches array + * @return string */ public function makeHelpMsg_callback( $matches ) { global $wgAutoloadClasses, $wgAutoloadLocalClasses; @@ -426,7 +464,7 @@ abstract class ApiBase { // This is necessary to make stuff like ApiMain::getVersion() // returning the version string for ApiBase work if ( $path ) { - return "{$matches[0]}\n http://svn.wikimedia.org/" . + return "{$matches[0]}\n https://svn.wikimedia.org/" . "viewvc/mediawiki/trunk/" . dirname( $path ) . "/{$matches[2]}"; } @@ -442,8 +480,8 @@ abstract class ApiBase { } /** - * Returns usage examples for this module. Return null if no examples are available. - * @return mixed string or array of strings + * Returns usage examples for this module. Return false if no examples are available. + * @return false|string|array */ protected function getExamples() { return false; @@ -454,7 +492,7 @@ abstract class ApiBase { * value) or (parameter name) => (array with PARAM_* constants as keys) * Don't call this function directly: use getFinalParams() to allow * hooks to modify parameters as needed. - * @return array + * @return array or false */ protected function getAllowedParams() { return false; @@ -464,7 +502,7 @@ abstract class ApiBase { * Returns an array of parameter descriptions. * Don't call this functon directly: use getFinalParamDescription() to * allow hooks to modify descriptions as needed. - * @return array + * @return array or false */ protected function getParamDescription() { return false; @@ -473,7 +511,8 @@ abstract class ApiBase { /** * Get final list of parameters, after hooks have had a chance to * tweak it as needed. - * @return array + * + * @return array or false */ public function getFinalParams() { $params = $this->getAllowedParams(); @@ -482,8 +521,9 @@ abstract class ApiBase { } /** - * Get final description, after hooks have had a chance to tweak it as + * Get final parameter descriptions, after hooks have had a chance to tweak it as * needed. + * * @return array */ public function getFinalParamDescription() { @@ -492,6 +532,18 @@ abstract class ApiBase { return $desc; } + /** + * Get final module description, after hooks have had a chance to tweak it as + * needed. + * + * @return array + */ + public function getFinalDescription() { + $desc = $this->getDescription(); + wfRunHooks( 'APIGetDescription', array( &$this, &$desc ) ); + return $desc; + } + /** * This method mangles parameter name based on the prefix supplied to the constructor. * Override this method to change parameter name during runtime @@ -574,6 +626,38 @@ abstract class ApiBase { ); } + /** + * Die if more than one of a certain set of parameters is set and not false. + * + * @param $params array + */ + public function requireMaxOneParameter( $params ) { + $required = func_get_args(); + array_shift( $required ); + + $intersection = array_intersect( array_keys( array_filter( $params, + array( $this, "parameterNotEmpty" ) ) ), $required ); + + if ( count( $intersection ) > 1 ) { + $this->dieUsage( 'The parameters ' . implode( ', ', $intersection ) . ' can not be used together', 'invalidparammix' ); + } + } + + /** + * Generates the possible error requireMaxOneParameter() can die with + * + * @param $params array + * @return array + */ + public function getRequireMaxOneParameterErrorMessages( $params ) { + $p = $this->getModulePrefix(); + $params = implode( ", {$p}", $params ); + + return array( + array( 'code' => "{$p}invalidparammix", 'info' => "The parameters {$p}{$params} can not be used together" ) + ); + } + /** * Callback function used in requireOnlyOneParameter to check whether reequired parameters are set * @@ -585,9 +669,12 @@ abstract class ApiBase { } /** - * @deprecated use MWNamespace::getValidNamespaces() + * @deprecated since 1.17 use MWNamespace::getValidNamespaces() + * + * @return array */ public static function getValidNamespaces() { + wfDeprecated( __METHOD__, '1.17' ); return MWNamespace::getValidNamespaces(); } @@ -597,13 +684,12 @@ abstract class ApiBase { * @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 + * @return bool */ protected function getWatchlistValue ( $watchlist, $titleObj, $userOption = null ) { $userWatching = $titleObj->userIsWatching(); - global $wgUser; switch ( $watchlist ) { case 'watch': return true; @@ -622,7 +708,7 @@ abstract class ApiBase { ? 'watchdefault' : 'watchcreations'; } # Watch the article based on the user preference - return (bool)$wgUser->getOption( $userOption ); + return (bool)$this->getUser()->getOption( $userOption ); case 'nochange': return $userWatching; @@ -638,17 +724,17 @@ abstract class ApiBase { * @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 ) { + protected function setWatch( $watch, $titleObj, $userOption = null ) { $value = $this->getWatchlistValue( $watch, $titleObj, $userOption ); if ( $value === null ) { return; } - $articleObj = new Article( $titleObj ); + $user = $this->getUser(); if ( $value ) { - Action::factory( 'watch', $articleObj )->execute(); + WatchAction::doWatch( $titleObj, $user ); } else { - Action::factory( 'unwatch', $articleObj )->execute(); + WatchAction::doUnwatch( $titleObj, $user ); } } @@ -696,9 +782,9 @@ abstract class ApiBase { ApiBase::dieDebug( __METHOD__, "Boolean param $encParamName's default is set to '$default'" ); } - $value = $this->getMain()->getRequest()->getCheck( $encParamName ); + $value = $this->getRequest()->getCheck( $encParamName ); } else { - $value = $this->getMain()->getRequest()->getVal( $encParamName, $default ); + $value = $this->getRequest()->getVal( $encParamName, $default ); if ( isset( $value ) && $type == 'namespace' ) { $type = MWNamespace::getValidNamespaces(); @@ -768,14 +854,13 @@ abstract class ApiBase { } break; case 'timestamp': - if ( $multi ) { - ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); - } - $value = wfTimestamp( TS_UNIX, $value ); - if ( $value === 0 ) { - $this->dieUsage( "Invalid value '$value' for timestamp parameter $encParamName", "badtimestamp_{$encParamName}" ); + if ( is_array( $value ) ) { + foreach ( $value as $key => $val ) { + $value[$key] = $this->validateTimestamp( $val, $encParamName ); + } + } else { + $value = $this->validateTimestamp( $value, $encParamName ); } - $value = wfTimestamp( TS_MW, $value ); break; case 'user': if ( !is_array( $value ) ) { @@ -808,7 +893,7 @@ abstract class ApiBase { if ( $deprecated && $value !== false ) { $this->setWarning( "The $encParamName parameter has been deprecated." ); } - } else if ( $required ) { + } elseif ( $required ) { $this->dieUsageMsg( array( 'missingparam', $paramName ) ); } @@ -906,6 +991,19 @@ abstract class ApiBase { } } + /** + * @param $value string + * @param $paramName string + * @return string + */ + function validateTimestamp( $value, $paramName ) { + $value = wfTimestamp( TS_UNIX, $value ); + if ( $value === 0 ) { + $this->dieUsage( "Invalid value '$value' for timestamp parameter $paramName", "badtimestamp_{$paramName}" ); + } + return wfTimestamp( TS_MW, $value ); + } + /** * Adds a warning to the output, else dies * @@ -963,7 +1061,8 @@ abstract class ApiBase { 'ns-specialprotected' => array( 'code' => 'unsupportednamespace', 'info' => "Pages in the Special namespace can't be edited" ), 'protectedinterface' => array( 'code' => 'protectednamespace-interface', 'info' => "You're not allowed to edit interface messages" ), 'namespaceprotected' => array( 'code' => 'protectednamespace', 'info' => "You're not allowed to edit pages in the ``\$1'' namespace" ), - 'customcssjsprotected' => array( 'code' => 'customcssjsprotected', 'info' => "You're not allowed to edit custom CSS and JavaScript pages" ), + 'customcssprotected' => array( 'code' => 'customcssprotected', 'info' => "You're not allowed to edit custom CSS pages" ), + 'customjsprotected' => array( 'code' => 'customjsprotected', 'info' => "You're not allowed to edit custom JavaScript pages" ), 'cascadeprotected' => array( 'code' => 'cascadeprotected', 'info' => "The page you're trying to edit is protected because it's included in a cascade-protected page" ), 'protectedpagetext' => array( 'code' => 'protectedpage', 'info' => "The ``\$1'' right is required to edit this page" ), 'protect-cantedit' => array( 'code' => 'cantedit', 'info' => "You can't protect this page because you can't edit it" ), @@ -1065,6 +1164,8 @@ abstract class ApiBase { 'mustbeposted' => array( 'code' => 'mustbeposted', 'info' => "The \$1 module requires a POST request" ), 'show' => array( 'code' => 'show', 'info' => 'Incorrect parameter - mutually exclusive values may not be supplied' ), 'specialpage-cantexecute' => array( 'code' => 'specialpage-cantexecute', 'info' => "You don't have permission to view the results of this special page" ), + 'invalidoldimage' => array( 'code' => 'invalidoldimage', 'info' => 'The oldimage parameter has invalid format' ), + 'nodeleteablefile' => array( 'code' => 'nodeleteablefile', 'info' => 'No such old version of the file' ), // ApiEditPage messages 'noimageredirect-anon' => array( 'code' => 'noimageredirect-anon', 'info' => "Anonymous users can't create image redirects" ), @@ -1083,13 +1184,20 @@ abstract class ApiBase { 'revwrongpage' => array( 'code' => 'revwrongpage', 'info' => "r\$1 is not a revision of ``\$2''" ), 'undo-failure' => array( 'code' => 'undofailure', 'info' => 'Undo failed due to conflicting intermediate edits' ), + // Messages from WikiPage::doEit() + 'edit-hook-aborted' => array( 'code' => 'edit-hook-aborted', 'info' => "Your edit was aborted by an ArticleSave hook" ), + 'edit-gone-missing' => array( 'code' => 'edit-gone-missing', 'info' => "The page you tried to edit doesn't seem to exist anymore" ), + 'edit-conflict' => array( 'code' => 'editconflict', 'info' => "Edit conflict detected" ), + 'edit-already-exists' => array( 'code' => 'edit-already-exists', 'info' => "It seems the page you tried to create already exist" ), + // uploadMsgs 'invalid-session-key' => array( 'code' => 'invalid-session-key', 'info' => 'Not a valid session key' ), '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.' ), - + 'filename-tooshort' => array( 'code' => 'filename-tooshort', 'info' => 'The filename is too short' ), + 'filename-toolong' => array( 'code' => 'filename-toolong', 'info' => 'The filename is too long' ), 'illegal-filename' => array( 'code' => 'illegal-filename', 'info' => 'The filename is not allowed' ), 'filetype-missing' => array( 'code' => 'filetype-missing', 'info' => 'The file is missing an extension' ), ); @@ -1105,9 +1213,14 @@ abstract class ApiBase { /** * Output the error message related to a certain array - * @param $error array Element of a getUserPermissionsErrors()-style array + * @param $error (array|string) Element of a getUserPermissionsErrors()-style array */ public function dieUsageMsg( $error ) { + # most of the time we send a 1 element, so we might as well send it as + # a string and make this an array here. + if( is_string( $error ) ) { + $error = array( $error ); + } $parsed = $this->parseMsg( $error ); $this->dieUsage( $parsed['info'], $parsed['code'] ); } @@ -1119,6 +1232,14 @@ abstract class ApiBase { */ public function parseMsg( $error ) { $key = array_shift( $error ); + + // Check whether the error array was nested + // array( array( , ), array( , ) ) + if( is_array( $key ) ){ + $error = $key; + $key = array_shift( $error ); + } + if ( isset( self::$messageMap[$key] ) ) { return array( 'code' => wfMsgReplaceArgs( self::$messageMap[$key]['code'], $error ), @@ -1126,6 +1247,7 @@ abstract class ApiBase { wfMsgReplaceArgs( self::$messageMap[$key]['info'], $error ) ); } + // If the key isn't present, throw an "unknown error" return $this->parseMsg( array( 'unknownerror', $key ) ); } @@ -1172,7 +1294,7 @@ abstract class ApiBase { /** * Returns whether this module requires a Token to execute - * @returns bool + * @return bool */ public function needsToken() { return false; @@ -1180,22 +1302,22 @@ abstract class ApiBase { /** * Returns the token salt if there is one, '' if the module doesn't require a salt, else false if the module doesn't need a token - * @returns bool + * @return bool|string */ public function getTokenSalt() { return false; } /** - * Gets the user for whom to get the watchlist - * - * @returns User - */ + * Gets the user for whom to get the watchlist + * + * @param $params array + * @return 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() ) { + if ( !($user && $user->getId()) ) { $this->dieUsage( 'Specified user does not exist', 'bad_wlowner' ); } $token = $user->getOption( 'watchlisttoken' ); @@ -1203,14 +1325,21 @@ abstract class ApiBase { $this->dieUsage( 'Incorrect watchlist token provided -- please set a correct token in Special:Preferences', 'bad_wltoken' ); } } else { - if ( !$wgUser->isLoggedIn() ) { + if ( !$this->getUser()->isLoggedIn() ) { $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' ); } - $user = $wgUser; + $user = $this->getUser(); } return $user; } + /** + * @return false|string|array Returns a false if the module has no help url, else returns a (array of) string + */ + public function getHelpUrls() { + return false; + } + /** * Returns a list of all possible errors returned by the module * @return array in the format of array( key, param1, param2, ... ) or array( 'code' => ..., 'info' => ... ) @@ -1371,6 +1500,13 @@ abstract class ApiBase { return $this->mDBTime; } + /** + * @return DatabaseBase + */ + protected function getDB() { + return wfGetDB( DB_SLAVE, 'api' ); + } + /** * Debugging function that prints a value and an optional backtrace * @param $value mixed Value to print