'feedwatchlist' => 'ApiFeedWatchlist',
'help' => 'ApiHelp',
'paraminfo' => 'ApiParamInfo',
- 'purge' => 'ApiPurge',
- );
- private static $WriteModules = array (
+ // Write modules
+ 'purge' => 'ApiPurge',
'rollback' => 'ApiRollback',
'delete' => 'ApiDelete',
'undelete' => 'ApiUndelete',
'emailuser' => 'ApiEmailUser',
'watch' => 'ApiWatch',
'patrol' => 'ApiPatrol',
+ 'import' => 'ApiImport',
);
/**
private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames;
- private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage;
+ private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode;
+ private $mCacheMode = 'private';
+ private $mCacheControl = array();
/**
* Constructs an instance of ApiMain that utilizes the module and format specified by $request.
wfDebug( "API: stripping user credentials for JSON callback\n" );
$wgUser = new User();
}
-
- if (!$wgUser->isAllowed('read')) {
- self::$Modules = array(
- 'login' => self::$Modules['login'],
- 'logout' => self::$Modules['logout'],
- 'help' => self::$Modules['help'],
- );
- }
}
- global $wgAPIModules, $wgEnableWriteAPI; // extension modules
+ global $wgAPIModules; // extension modules
$this->mModules = $wgAPIModules + self :: $Modules;
- if($wgEnableWriteAPI)
- $this->mModules += self::$WriteModules;
$this->mModuleNames = array_keys($this->mModules);
$this->mFormats = self :: $Formats;
$this->mRequest = & $request;
- $this->mSquidMaxage = -1; // flag for executeActionWithErrorHandling()
$this->mCommit = false;
}
}
/**
- * This method will simply cause an error if the write mode was disabled
- * or if the current user doesn't have the right to use it
+ * Only kept for backwards compatibility
+ * @deprecated Use isWriteMode() instead
*/
public function requestWriteMode() {
- global $wgUser;
if (!$this->mEnableWrite)
- $this->dieUsage('Editing of this wiki through the API' .
- ' is disabled. Make sure the $wgEnableWriteAPI=true; ' .
- 'statement is included in the wiki\'s ' .
- 'LocalSettings.php file', 'noapiwrite');
- if (!$wgUser->isAllowed('writeapi'))
- $this->dieUsage('You\'re not allowed to edit this ' .
- 'wiki through the API', 'writeapidenied');
+ $this->dieUsageMsg(array('writedisabled'));
if (wfReadOnly())
- $this->dieUsageMsg(array('readonlytext'));
+ $this->dieUsageMsg(array('readonlytext'));
}
/**
* Set how long the response should be cached.
*/
public function setCacheMaxAge($maxage) {
- $this->mSquidMaxage = $maxage;
+ $this->setCacheControl( array(
+ 'max-age' => $maxage,
+ 's-maxage' => $maxage
+ ) );
+ }
+
+ /**
+ * 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
+ * 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
+ * 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,
+ * 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
+ * good performance reason for caching the anonymous response, but where the
+ * 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" );
+ // Ignore for forwards-compatibility
+ return;
+ }
+
+ 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" );
+ return;
+ }
+ }
+
+ 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
+ * $this->setCacheMode('private')
+ */
+ public function setCachePrivate() {
+ $this->setCacheMode( 'private' );
+ }
+
+ /**
+ * Set directives (key/value pairs) for the Cache-Control header.
+ * 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
+ * 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.
+ *
+ * WARNING: This function must be called CONSISTENTLY for a given URL. This means that a
+ * given URL must either always or never call this function; if it sometimes does and
+ * sometimes doesn't, stuff will break.
+ *
+ * @deprecated Use setCacheMode( 'anon-public-user-private' )
+ */
+ public function setVaryCookie() {
+ $this->setCacheMode( 'anon-public-user-private' );
}
/**
$errCode = $this->substituteResultWithError($e);
// Error results should not be cached
- $this->setCacheMaxAge(0);
+ $this->setCacheMode( 'private' );
$headerStr = 'MediaWiki-API-Error: ' . $errCode;
if ($e->getCode() === 0)
$this->printResult(true);
}
- if($this->mSquidMaxage == -1)
- {
- # Nobody called setCacheMaxAge(), use the (s)maxage parameters
- $smaxage = $this->getParameter('smaxage');
- $maxage = $this->getParameter('maxage');
- }
- else
- $smaxage = $maxage = $this->mSquidMaxage;
-
- // Set the cache expiration at the last moment, as any errors may change the expiration.
- // if $this->mSquidMaxage == 0, the expiry time is set to the first second of unix epoch
- $exp = min($smaxage, $maxage);
- $expires = ($exp == 0 ? 1 : time() + $exp);
- header('Expires: ' . wfTimestamp(TS_RFC2822, $expires));
- header('Cache-Control: s-maxage=' . $smaxage . ', must-revalidate, max-age=' . $maxage);
+ // 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())
echo wfReportTime();
ob_end_flush();
}
+ protected function sendCacheHeaders() {
+ if ( $this->mCacheMode == 'private' ) {
+ header( 'Cache-Control: private' );
+ return;
+ }
+
+ if ( $this->mCacheMode == 'anon-public-user-private' ) {
+ global $wgOut;
+ header( 'Vary: Accept-Encoding, Cookie' );
+ header( $wgOut->getXVO() );
+ if ( session_id() != '' || $wgOut->haveCacheVaryCookies() ) {
+ // Logged in, mark this request private
+ header( 'Cache-Control: private' );
+ return;
+ }
+ // Logged out, send normal 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
+ if ( !isset( $this->mCacheControl['s-maxage'] ) ) {
+ $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' );
+ }
+ if ( !isset( $this->mCacheControl['max-age'] ) ) {
+ $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' );
+ }
+
+ if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) {
+ // Public cache not requested
+ // Sending a Vary header in this case is harmless, and protects us
+ // against conditional calls of setCacheMaxAge().
+ header( 'Cache-Control: private' );
+ return;
+ }
+
+ $this->mCacheControl['public'] = true;
+
+ // Send an Expires header
+ $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] );
+ $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge );
+ header( 'Expires: ' . wfTimestamp( TS_RFC2822, $expiryUnixTime ) );
+
+ // Construct the Cache-Control header
+ $ccHeader = '';
+ $separator = '';
+ foreach ( $this->mCacheControl as $name => $value ) {
+ if ( is_bool( $value ) ) {
+ if ( $value ) {
+ $ccHeader .= $separator . $name;
+ $separator = ', ';
+ }
+ } else {
+ $ccHeader .= $separator . "$name=$value";
+ $separator = ', ';
+ }
+ }
+
+ header( "Cache-Control: $ccHeader" );
+ }
+
/**
* Replace the result data with the information about an exception.
* Returns the error code
}
$this->getResult()->reset();
+ $this->getResult()->disableSizeCheck();
// Re-add the id
- if($this->mRequest->getCheck('requestid'))
- $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
+ $requestid = $this->getParameter('requestid');
+ if(!is_null($requestid))
+ $this->getResult()->addValue(null, 'requestid', $requestid);
$this->getResult()->addValue(null, 'error', $errMessage);
return $errMessage['code'];
*/
protected function executeAction() {
// First add the id to the top element
- if($this->mRequest->getCheck('requestid'))
- $this->getResult()->addValue(null, 'requestid', $this->mRequest->getVal('requestid'));
+ $requestid = $this->getParameter('requestid');
+ if(!is_null($requestid))
+ $this->getResult()->addValue(null, 'requestid', $requestid);
$params = $this->extractRequestParams();
header( 'X-Database-Lag: ' . intval( $lag ) );
// XXX: should we return a 503 HTTP error code like wfMaxlagError() does?
if( $wgShowHostnames ) {
- ApiBase :: dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' );
+ $this->dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' );
} else {
- ApiBase :: dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' );
+ $this->dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' );
}
return;
}
}
+ global $wgUser;
+ if ($module->isReadMode() && !$wgUser->isAllowed('read'))
+ $this->dieUsageMsg(array('readrequired'));
+ if ($module->isWriteMode()) {
+ if (!$this->mEnableWrite)
+ $this->dieUsageMsg(array('writedisabled'));
+ if (!$wgUser->isAllowed('writeapi'))
+ $this->dieUsageMsg(array('writerequired'));
+ if (wfReadOnly())
+ $this->dieUsageMsg(array('readonlytext'));
+ }
+
if (!$this->mInternalMode) {
// Ignore mustBePosted() for internal calls
if($module->mustBePosted() && !$this->mRequest->wasPosted())
* Print results using the current printer
*/
protected function printResult($isError) {
- $this->getResult()->cleanupUTF8();
+ $this->getResult()->cleanUpUTF8();
$printer = $this->mPrinter;
$printer->profileIn();
$printer->closePrinter();
$printer->profileOut();
}
+
+ public function isReadMode() {
+ return false;
+ }
/**
* See ApiBase for description.
public function getVersion() {
$vers = array ();
$vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/";
- $vers[] = __CLASS__ . ': $Id: ApiMain.php 45752 2009-01-14 21:36:57Z catrope $';
+ $vers[] = __CLASS__ . ': $Id: ApiMain.php 69990 2010-07-27 08:44:08Z tstarling $';
$vers[] = ApiBase :: getBaseVersion();
$vers[] = ApiFormatBase :: getBaseVersion();
$vers[] = ApiQueryBase :: getBaseVersion();