summaryrefslogtreecommitdiff
path: root/includes/api
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2013-12-08 09:55:49 +0100
committerPierre Schmitz <pierre@archlinux.de>2013-12-08 09:55:49 +0100
commit4ac9fa081a7c045f6a9f1cfc529d82423f485b2e (patch)
treeaf68743f2f4a47d13f2b0eb05f5c4aaf86d8ea37 /includes/api
parentaf4da56f1ad4d3ef7b06557bae365da2ea27a897 (diff)
Update to MediaWiki 1.22.0
Diffstat (limited to 'includes/api')
-rw-r--r--includes/api/ApiBase.php119
-rw-r--r--includes/api/ApiBlock.php2
-rw-r--r--includes/api/ApiComparePages.php8
-rw-r--r--includes/api/ApiCreateAccount.php52
-rw-r--r--includes/api/ApiDelete.php3
-rw-r--r--includes/api/ApiEditPage.php59
-rw-r--r--includes/api/ApiExpandTemplates.php4
-rw-r--r--includes/api/ApiFeedContributions.php15
-rw-r--r--includes/api/ApiFeedWatchlist.php90
-rw-r--r--includes/api/ApiFormatBase.php8
-rw-r--r--includes/api/ApiFormatJson.php24
-rw-r--r--includes/api/ApiFormatWddx.php75
-rw-r--r--includes/api/ApiFormatXml.php137
-rw-r--r--includes/api/ApiImageRotate.php32
-rw-r--r--includes/api/ApiImport.php6
-rw-r--r--includes/api/ApiMain.php36
-rw-r--r--includes/api/ApiMove.php4
-rw-r--r--includes/api/ApiOpenSearch.php21
-rw-r--r--includes/api/ApiOptions.php8
-rw-r--r--includes/api/ApiPageSet.php18
-rw-r--r--includes/api/ApiParamInfo.php6
-rw-r--r--includes/api/ApiParse.php157
-rw-r--r--includes/api/ApiPatrol.php46
-rw-r--r--includes/api/ApiProtect.php3
-rw-r--r--includes/api/ApiPurge.php29
-rw-r--r--includes/api/ApiQuery.php10
-rw-r--r--includes/api/ApiQueryAllCategories.php4
-rw-r--r--includes/api/ApiQueryAllImages.php4
-rw-r--r--includes/api/ApiQueryAllLinks.php116
-rw-r--r--includes/api/ApiQueryAllMessages.php2
-rw-r--r--includes/api/ApiQueryAllPages.php7
-rw-r--r--includes/api/ApiQueryAllUsers.php4
-rw-r--r--includes/api/ApiQueryBacklinks.php15
-rw-r--r--includes/api/ApiQueryBase.php4
-rw-r--r--includes/api/ApiQueryBlocks.php61
-rw-r--r--includes/api/ApiQueryCategories.php5
-rw-r--r--includes/api/ApiQueryCategoryMembers.php6
-rw-r--r--includes/api/ApiQueryDeletedrevs.php6
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php23
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php6
-rw-r--r--includes/api/ApiQueryExternalLinks.php8
-rw-r--r--includes/api/ApiQueryFileRepoInfo.php115
-rw-r--r--includes/api/ApiQueryFilearchive.php8
-rw-r--r--includes/api/ApiQueryIWBacklinks.php4
-rw-r--r--includes/api/ApiQueryIWLinks.php13
-rw-r--r--includes/api/ApiQueryImageInfo.php80
-rw-r--r--includes/api/ApiQueryInfo.php20
-rw-r--r--includes/api/ApiQueryLangBacklinks.php9
-rw-r--r--includes/api/ApiQueryLangLinks.php17
-rw-r--r--includes/api/ApiQueryLogEvents.php24
-rw-r--r--includes/api/ApiQueryORM.php4
-rw-r--r--includes/api/ApiQueryPagesWithProp.php2
-rw-r--r--includes/api/ApiQueryProtectedTitles.php2
-rw-r--r--includes/api/ApiQueryQueryPage.php19
-rw-r--r--includes/api/ApiQueryRandom.php4
-rw-r--r--includes/api/ApiQueryRecentChanges.php43
-rw-r--r--includes/api/ApiQueryRevisions.php29
-rw-r--r--includes/api/ApiQuerySearch.php39
-rw-r--r--includes/api/ApiQuerySiteinfo.php62
-rw-r--r--includes/api/ApiQueryTags.php7
-rw-r--r--includes/api/ApiQueryUserContributions.php21
-rw-r--r--includes/api/ApiQueryUserInfo.php20
-rw-r--r--includes/api/ApiQueryUsers.php6
-rw-r--r--includes/api/ApiQueryWatchlist.php95
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php4
-rw-r--r--includes/api/ApiRsd.php2
-rw-r--r--includes/api/ApiSetNotificationTimestamp.php7
-rw-r--r--includes/api/ApiUpload.php84
-rw-r--r--includes/api/ApiUserrights.php7
-rw-r--r--includes/api/ApiWatch.php11
70 files changed, 1349 insertions, 652 deletions
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index 9351a8d8..ce6ecda6 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -304,15 +304,14 @@ abstract class ApiBase extends ContextSource {
}
$examples = $this->getExamples();
- if ( $examples !== false && $examples !== '' ) {
+ if ( $examples ) {
if ( !is_array( $examples ) ) {
$examples = array(
$examples
);
}
$msg .= "Example" . ( count( $examples ) > 1 ? 's' : '' ) . ":\n";
- foreach( $examples as $k => $v ) {
-
+ foreach ( $examples as $k => $v ) {
if ( is_numeric( $k ) ) {
$msg .= " $v\n";
} else {
@@ -445,7 +444,7 @@ abstract class ApiBase extends ContextSource {
$hintPipeSeparated = false;
break;
case 'limit':
- $desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]}";
+ $desc .= $paramPrefix . "No more than {$paramSettings[self::PARAM_MAX]}";
if ( isset( $paramSettings[self::PARAM_MAX2] ) ) {
$desc .= " ({$paramSettings[self::PARAM_MAX2]} for bots)";
}
@@ -689,9 +688,9 @@ abstract class ApiBase extends ContextSource {
array( $this, "parameterNotEmpty" ) ) ), $required );
if ( count( $intersection ) > 1 ) {
- $this->dieUsage( "The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', "{$p}invalidparammix" );
+ $this->dieUsage( "The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', 'invalidparammix' );
} elseif ( count( $intersection ) == 0 ) {
- $this->dieUsage( "One of the parameters {$p}" . implode( ", {$p}", $required ) . ' is required', "{$p}missingparam" );
+ $this->dieUsage( "One of the parameters {$p}" . implode( ", {$p}", $required ) . ' is required', 'missingparam' );
}
}
@@ -725,7 +724,7 @@ abstract class ApiBase extends ContextSource {
array( $this, "parameterNotEmpty" ) ) ), $required );
if ( count( $intersection ) > 1 ) {
- $this->dieUsage( "The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', "{$p}invalidparammix" );
+ $this->dieUsage( "The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', 'invalidparammix' );
}
}
@@ -822,9 +821,9 @@ abstract class ApiBase extends ContextSource {
* If not set will magically default to either watchdefault or watchcreations
* @return bool
*/
- protected function getWatchlistValue ( $watchlist, $titleObj, $userOption = null ) {
+ protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) {
- $userWatching = $this->getUser()->isWatched( $titleObj );
+ $userWatching = $this->getUser()->isWatched( $titleObj, WatchedItem::IGNORE_USER_RIGHTS );
switch ( $watchlist ) {
case 'watch':
@@ -866,12 +865,7 @@ abstract class ApiBase extends ContextSource {
return;
}
- $user = $this->getUser();
- if ( $value ) {
- WatchAction::doWatch( $titleObj, $user );
- } else {
- WatchAction::doUnwatch( $titleObj, $user );
- }
+ WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser() );
}
/**
@@ -967,9 +961,9 @@ abstract class ApiBase extends ContextSource {
}
break;
case 'integer': // Force everything using intval() and optionally validate limits
- $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] )
+ $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_array( $value ) ) {
@@ -1081,7 +1075,7 @@ abstract class ApiBase extends ContextSource {
if ( !$allowMultiple && count( $valuesList ) != 1 ) {
// Bug 33482 - Allow entries with | in them for non-multiple values
- if ( in_array( $value, $allowedValues ) ) {
+ if ( in_array( $value, $allowedValues, true ) ) {
return $value;
}
@@ -1165,7 +1159,7 @@ abstract class ApiBase extends ContextSource {
/**
* Validate and normalize of parameters of type 'user'
* @param string $value Parameter value
- * @param string $encParamName Parameter value
+ * @param string $encParamName Parameter name
* @return string Validated and normalized parameter
*/
private function validateUser( $value, $encParamName ) {
@@ -1223,6 +1217,44 @@ abstract class ApiBase extends ContextSource {
}
/**
+ * Throw a UsageException based on the errors in the Status object.
+ *
+ * @since 1.22
+ * @param Status $status Status object
+ * @throws UsageException
+ */
+ public function dieStatus( $status ) {
+ if ( $status->isGood() ) {
+ throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
+ }
+
+ $errors = $status->getErrorsArray();
+ if ( !$errors ) {
+ // No errors? Assume the warnings should be treated as errors
+ $errors = $status->getWarningsArray();
+ }
+ if ( !$errors ) {
+ // Still no errors? Punt
+ $errors = array( array( 'unknownerror-nocode' ) );
+ }
+
+ // Cannot use dieUsageMsg() because extensions might return custom
+ // error messages.
+ if ( $errors[0] instanceof Message ) {
+ $msg = $errors[0];
+ $code = $msg->getKey();
+ } else {
+ $code = array_shift( $errors[0] );
+ $msg = wfMessage( $code, $errors[0] );
+ }
+ if ( isset( ApiBase::$messageMap[$code] ) ) {
+ // Translate message to code, for backwards compatability
+ $code = ApiBase::$messageMap[$code]['code'];
+ }
+ $this->dieUsage( $msg->inLanguage( 'en' )->useDatabase( false )->plain(), $code );
+ }
+
+ /**
* Array that maps message keys to error messages. $1 and friends are replaced.
*/
public static $messageMap = array(
@@ -1372,6 +1404,7 @@ abstract class ApiBase extends ContextSource {
'uploaddisabled' => array( 'code' => 'uploaddisabled', 'info' => 'Uploads are not enabled. Make sure $wgEnableUploads is set to true in LocalSettings.php and the PHP ini setting file_uploads is true' ),
'copyuploaddisabled' => array( 'code' => 'copyuploaddisabled', 'info' => 'Uploads by URL is not enabled. Make sure $wgAllowCopyUploads is set to true in LocalSettings.php.' ),
'copyuploadbaddomain' => array( 'code' => 'copyuploadbaddomain', 'info' => 'Uploads by URL are not allowed from this domain.' ),
+ 'copyuploadbadurl' => array( 'code' => 'copyuploadbadurl', 'info' => 'Upload not allowed from this URL.' ),
'filename-tooshort' => array( 'code' => 'filename-tooshort', 'info' => 'The filename is too short' ),
'filename-toolong' => array( 'code' => 'filename-toolong', 'info' => 'The filename is too long' ),
@@ -1397,7 +1430,7 @@ abstract class ApiBase extends ContextSource {
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 ) ) {
+ if ( is_string( $error ) ) {
$error = array( $error );
}
$parsed = $this->parseMsg( $error );
@@ -1412,10 +1445,10 @@ abstract class ApiBase extends ContextSource {
*/
public function dieUsageMsgOrDebug( $error ) {
global $wgDebugAPI;
- if( $wgDebugAPI !== true ) {
+ if ( $wgDebugAPI !== true ) {
$this->dieUsageMsg( $error );
} else {
- if( is_string( $error ) ) {
+ if ( is_string( $error ) ) {
$error = array( $error );
}
$parsed = $this->parseMsg( $error );
@@ -1448,7 +1481,7 @@ abstract class ApiBase extends ContextSource {
// Check whether the error array was nested
// array( array( <code>, <params> ), array( <another_code>, <params> ) )
- if( is_array( $key ) ) {
+ if ( is_array( $key ) ) {
$error = $key;
$key = array_shift( $error );
}
@@ -1470,7 +1503,7 @@ abstract class ApiBase extends ContextSource {
* @param string $message Error message
*/
protected static function dieDebug( $method, $message ) {
- wfDebugDieBacktrace( "Internal error in $method: $message" );
+ throw new MWException( "Internal error in $method: $message" );
}
/**
@@ -1535,7 +1568,7 @@ abstract class ApiBase extends ContextSource {
public function getWatchlistUser( $params ) {
if ( !is_null( $params['owner'] ) && !is_null( $params['token'] ) ) {
$user = User::newFromName( $params['owner'], false );
- if ( !($user && $user->getId()) ) {
+ if ( !( $user && $user->getId() ) ) {
$this->dieUsage( 'Specified user does not exist', 'bad_wlowner' );
}
$token = $user->getOption( 'watchlisttoken' );
@@ -1546,6 +1579,9 @@ abstract class ApiBase extends ContextSource {
if ( !$this->getUser()->isLoggedIn() ) {
$this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' );
}
+ if ( !$this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
+ $this->dieUsage( 'You don\'t have permission to view your watchlist', 'permissiondenied' );
+ }
$user = $this->getUser();
}
return $user;
@@ -1560,6 +1596,10 @@ abstract class ApiBase extends ContextSource {
/**
* Returns a list of all possible errors returned by the module
+ *
+ * Don't call this function directly: use getFinalPossibleErrors() to allow
+ * hooks to modify parameters as needed.
+ *
* @return array in the format of array( key, param1, param2, ... ) or array( 'code' => ..., 'info' => ... )
*/
public function getPossibleErrors() {
@@ -1574,10 +1614,9 @@ abstract class ApiBase extends ContextSource {
}
if ( array_key_exists( 'continue', $params ) ) {
$ret[] = array(
- array(
- 'code' => 'badcontinue',
- 'info' => 'Invalid continue param. You should pass the original value returned by the previous query'
- ) );
+ 'code' => 'badcontinue',
+ 'info' => 'Invalid continue param. You should pass the original value returned by the previous query'
+ );
}
}
@@ -1595,7 +1634,12 @@ abstract class ApiBase extends ContextSource {
}
if ( $this->needsToken() ) {
- $ret[] = array( 'missingparam', 'token' );
+ if ( !isset( $params['token'][ApiBase::PARAM_REQUIRED] )
+ || !$params['token'][ApiBase::PARAM_REQUIRED]
+ ) {
+ // Add token as possible missing parameter, if not already done
+ $ret[] = array( 'missingparam', 'token' );
+ }
$ret[] = array( 'sessionfailure' );
}
@@ -1603,6 +1647,19 @@ abstract class ApiBase extends ContextSource {
}
/**
+ * Get final list of possible errors, after hooks have had a chance to
+ * tweak it as needed.
+ *
+ * @return array
+ * @since 1.22
+ */
+ public function getFinalPossibleErrors() {
+ $possibleErrors = $this->getPossibleErrors();
+ wfRunHooks( 'APIGetPossibleErrors', array( $this, &$possibleErrors ) );
+ return $possibleErrors;
+ }
+
+ /**
* Parses a list of errors into a standardised format
* @param array $errors List of errors. Items can be in the for array( key, param1, param2, ... ) or array( 'code' => ..., 'info' => ... )
* @return array Parsed list of errors with items in the form array( 'code' => ..., 'info' => ... )
diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php
index 6f3d1e4f..975153ac 100644
--- a/includes/api/ApiBlock.php
+++ b/includes/api/ApiBlock.php
@@ -98,7 +98,7 @@ class ApiBlock extends ApiBase {
$res['userID'] = $target instanceof User ? $target->getId() : 0;
$block = Block::newFromTarget( $target );
- if( $block instanceof Block ) {
+ if ( $block instanceof Block ) {
$res['expiry'] = $block->mExpiry == $this->getDB()->getInfinity()
? 'infinite'
: wfTimestamp( TS_ISO_8601, $block->mExpiry );
diff --git a/includes/api/ApiComparePages.php b/includes/api/ApiComparePages.php
index 79ffcb0a..1e35c349 100644
--- a/includes/api/ApiComparePages.php
+++ b/includes/api/ApiComparePages.php
@@ -81,17 +81,17 @@ class ApiComparePages extends ApiBase {
* @return int
*/
private function revisionOrTitleOrId( $revision, $titleText, $titleId ) {
- if( $revision ) {
+ if ( $revision ) {
return $revision;
- } elseif( $titleText ) {
+ } elseif ( $titleText ) {
$title = Title::newFromText( $titleText );
- if( !$title || $title->isExternal() ) {
+ if ( !$title || $title->isExternal() ) {
$this->dieUsageMsg( array( 'invalidtitle', $titleText ) );
}
return $title->getLatestRevID();
} elseif ( $titleId ) {
$title = Title::newFromID( $titleId );
- if( !$title ) {
+ if ( !$title ) {
$this->dieUsageMsg( array( 'nosuchpageid', $titleId ) );
}
return $title->getLatestRevID();
diff --git a/includes/api/ApiCreateAccount.php b/includes/api/ApiCreateAccount.php
index 69748c93..0e752c56 100644
--- a/includes/api/ApiCreateAccount.php
+++ b/includes/api/ApiCreateAccount.php
@@ -47,17 +47,19 @@ class ApiCreateAccount extends ApiBase {
$params = $this->extractRequestParams();
- $result = array();
-
// Init session if necessary
if ( session_id() == '' ) {
wfSetupSession();
}
- if( $params['mailpassword'] && !$params['email'] ) {
+ if ( $params['mailpassword'] && !$params['email'] ) {
$this->dieUsageMsg( 'noemail' );
}
+ if ( $params['language'] && !Language::isSupportedLanguage( $params['language'] ) ) {
+ $this->dieUsage( 'Invalid language parameter', 'langinvalid' );
+ }
+
$context = new DerivativeContext( $this->getContext() );
$context->setRequest( new DerivativeRequest(
$this->getContext()->getRequest(),
@@ -82,22 +84,20 @@ class ApiCreateAccount extends ApiBase {
$status = $loginForm->addNewaccountInternal();
$result = array();
- if( $status->isGood() ) {
+ if ( $status->isGood() ) {
// Success!
+ global $wgEmailAuthentication;
$user = $status->getValue();
- // If we showed up language selection links, and one was in use, be
- // smart (and sensible) and save that language as the user's preference
- global $wgLoginLanguageSelector, $wgEmailAuthentication;
- if( $wgLoginLanguageSelector && $params['language'] ) {
+ if ( $params['language'] ) {
$user->setOption( 'language', $params['language'] );
}
- if( $params['mailpassword'] ) {
+ if ( $params['mailpassword'] ) {
// If mailpassword was set, disable the password and send an email.
$user->setPassword( null );
$status->merge( $loginForm->mailPasswordInternal( $user, false, 'createaccount-title', 'createaccount-text' ) );
- } elseif( $wgEmailAuthentication && Sanitizer::validateEmail( $user->getEmail() ) ) {
+ } elseif ( $wgEmailAuthentication && Sanitizer::validateEmail( $user->getEmail() ) ) {
// Send out an email authentication message if needed
$status->merge( $user->sendConfirmationMail() );
}
@@ -124,33 +124,23 @@ class ApiCreateAccount extends ApiBase {
$apiResult = $this->getResult();
- if( $status->hasMessage( 'sessionfailure' ) || $status->hasMessage( 'nocookiesfornew' ) ) {
+ if ( $status->hasMessage( 'sessionfailure' ) || $status->hasMessage( 'nocookiesfornew' ) ) {
// Token was incorrect, so add it to result, but don't throw an exception
// since not having the correct token is part of the normal
// flow of events.
$result['token'] = LoginForm::getCreateaccountToken();
$result['result'] = 'needtoken';
- } elseif( !$status->isOK() ) {
+ } elseif ( !$status->isOK() ) {
// There was an error. Die now.
- // Cannot use dieUsageMsg() directly because extensions
- // might return custom error messages.
- $errors = $status->getErrorsArray();
- if( $errors[0] instanceof Message ) {
- $code = 'aborted';
- $desc = $errors[0];
- } else {
- $code = array_shift( $errors[0] );
- $desc = wfMessage( $code, $errors[0] );
- }
- $this->dieUsage( $desc, $code );
- } elseif( !$status->isGood() ) {
+ $this->dieStatus( $status );
+ } elseif ( !$status->isGood() ) {
// Status is not good, but OK. This means warnings.
$result['result'] = 'warning';
// Add any warnings to the result
$warnings = $status->getErrorsByType( 'warning' );
- if( $warnings ) {
- foreach( $warnings as &$warning ) {
+ if ( $warnings ) {
+ foreach ( $warnings as &$warning ) {
$apiResult->setIndexedTagName( $warning['params'], 'param' );
}
$apiResult->setIndexedTagName( $warnings, 'warning' );
@@ -263,8 +253,8 @@ class ApiCreateAccount extends ApiBase {
$errors = parent::getPossibleErrors();
// All local errors are from LoginForm, which means they're actually message keys.
- foreach( $localErrors as $error ) {
- $errors[] = array( 'code' => $error, 'info' => wfMessage( $error )->parse() );
+ foreach ( $localErrors as $error ) {
+ $errors[] = array( 'code' => $error, 'info' => wfMessage( $error )->inLanguage( 'en' )->useDatabase( false )->parse() );
}
$errors[] = array(
@@ -279,12 +269,16 @@ class ApiCreateAccount extends ApiBase {
'code' => 'aborted',
'info' => 'Account creation aborted by hook (info may vary)'
);
+ $errors[] = array(
+ 'code' => 'langinvalid',
+ 'info' => 'Invalid language parameter'
+ );
// 'passwordtooshort' has parameters. :(
global $wgMinimalPasswordLength;
$errors[] = array(
'code' => 'passwordtooshort',
- 'info' => wfMessage( 'passwordtooshort', $wgMinimalPasswordLength )->parse()
+ 'info' => wfMessage( 'passwordtooshort', $wgMinimalPasswordLength )->inLanguage( 'en' )->useDatabase( false )->parse()
);
return $errors;
}
diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php
index d1f0806e..aea10482 100644
--- a/includes/api/ApiDelete.php
+++ b/includes/api/ApiDelete.php
@@ -61,8 +61,7 @@ class ApiDelete extends ApiBase {
$this->dieUsageMsg( $status[0] );
}
if ( !$status->isGood() ) {
- $errors = $status->getErrorsArray();
- $this->dieUsageMsg( $errors[0] ); // We don't care about multiple errors, just report one of them
+ $this->dieStatus( $status );
}
// Deprecated parameters
diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php
index 4916145b..bd61895b 100644
--- a/includes/api/ApiEditPage.php
+++ b/includes/api/ApiEditPage.php
@@ -100,7 +100,7 @@ class ApiEditPage extends ApiBase {
$name = $titleObj->getPrefixedDBkey();
$model = $contentHandler->getModelID();
- $this->dieUsage( "The requested format $contentFormat is not supported for content model ".
+ $this->dieUsage( "The requested format $contentFormat is not supported for content model " .
" $model used by $name", 'badformat' );
}
@@ -146,7 +146,7 @@ class ApiEditPage extends ApiBase {
}
}
- // @todo: Add support for appending/prepending to the Content interface
+ // @todo Add support for appending/prepending to the Content interface
if ( !( $content instanceof TextContent ) ) {
$mode = $contentHandler->getModelID();
@@ -159,12 +159,17 @@ class ApiEditPage extends ApiBase {
$this->dieUsage( "Sections are not supported for this content model: $modelName.", 'sectionsnotsupported' );
}
- // Process the content for section edits
- $section = intval( $params['section'] );
- $content = $content->getSection( $section );
+ if ( $params['section'] == 'new' ) {
+ // DWIM if they're trying to prepend/append to a new section.
+ $content = null;
+ } else {
+ // Process the content for section edits
+ $section = intval( $params['section'] );
+ $content = $content->getSection( $section );
- if ( !$content ) {
- $this->dieUsage( "There is no section {$section}.", 'nosuchsection' );
+ if ( !$content ) {
+ $this->dieUsage( "There is no section {$section}.", 'nosuchsection' );
+ }
}
}
@@ -262,7 +267,7 @@ class ApiEditPage extends ApiBase {
$requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime
}
- if ( $params['minor'] || ( !$params['notminor'] && $user->getOption( 'minordefault' ) ) ) {
+ if ( $params['minor'] || ( !$params['notminor'] && $user->getOption( 'minordefault' ) ) ) {
$requestArray['wpMinoredit'] = '';
}
@@ -275,6 +280,10 @@ class ApiEditPage extends ApiBase {
if ( $section == 0 && $params['section'] != '0' && $params['section'] != 'new' ) {
$this->dieUsage( "The section parameter must be set to an integer or 'new'", "invalidsection" );
}
+ $content = $pageObj->getContent();
+ if ( $section !== 0 && ( !$content || !$content->getSection( $section ) ) ) {
+ $this->dieUsage( "There is no section {$section}.", 'nosuchsection' );
+ }
$requestArray['wpSection'] = $params['section'];
} else {
$requestArray['wpSection'] = '';
@@ -293,6 +302,10 @@ class ApiEditPage extends ApiBase {
$requestArray['wpWatchthis'] = '';
}
+ // Pass through anything else we might have been given, to support extensions
+ // This is kind of a hack but it's the best we can do to make extensions work
+ $requestArray += $this->getRequest()->getValues();
+
global $wgTitle, $wgRequest;
$req = new DerivativeRequest( $this->getRequest(), $requestArray, true );
@@ -316,11 +329,37 @@ class ApiEditPage extends ApiBase {
$ep->setContextTitle( $titleObj );
$ep->importFormData( $req );
+ $content = $ep->textbox1;
+
+ // The following is needed to give the hook the full content of the
+ // new revision rather than just the current section. (Bug 52077)
+ if ( !is_null( $params['section'] ) && $contentHandler->supportsSections() && $titleObj->exists() ) {
+
+ $sectionTitle = '';
+ // If sectiontitle is set, use it, otherwise use the summary as the section title (for
+ // backwards compatibility with old forms/bots).
+ if ( $ep->sectiontitle !== '' ) {
+ $sectionTitle = $ep->sectiontitle;
+ } else {
+ $sectionTitle = $ep->summary;
+ }
+
+ $contentObj = $contentHandler->unserializeContent( $content, $contentFormat );
+
+ $fullContentObj = $articleObject->replaceSectionContent( $params['section'], $contentObj, $sectionTitle );
+ if ( $fullContentObj ) {
+ $content = $fullContentObj->serialize( $contentFormat );
+ } else {
+ // This most likely means we have an edit conflict which means that the edit
+ // wont succeed anyway.
+ $this->dieUsageMsg( 'editconflict' );
+ }
+ }
// Run hooks
// Handle APIEditBeforeSave parameters
$r = array();
- if ( !wfRunHooks( 'APIEditBeforeSave', array( $ep, $ep->textbox1, &$r ) ) ) {
+ if ( !wfRunHooks( 'APIEditBeforeSave', array( $ep, $content, &$r ) ) ) {
if ( count( $r ) ) {
$r['result'] = 'Failure';
$apiResult->addValue( null, $this->getModuleName(), $r );
@@ -342,7 +381,7 @@ class ApiEditPage extends ApiBase {
$wgRequest = $oldRequest;
global $wgMaxArticleSize;
- switch( $status->value ) {
+ switch ( $status->value ) {
case EditPage::AS_HOOK_ERROR:
case EditPage::AS_HOOK_ERROR_EXPECTED:
$this->dieUsageMsg( 'hookaborted' );
diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php
index f5898fb3..d5c789c3 100644
--- a/includes/api/ApiExpandTemplates.php
+++ b/includes/api/ApiExpandTemplates.php
@@ -65,14 +65,14 @@ class ApiExpandTemplates extends ApiBase {
$xml = $dom->__toString();
}
$xml_result = array();
- $result->setContent( $xml_result, $xml );
+ ApiResult::setContent( $xml_result, $xml );
$result->addValue( null, 'parsetree', $xml_result );
}
$retval = $wgParser->preprocess( $params['text'], $title_obj, $options );
// Return result
$retval_array = array();
- $result->setContent( $retval_array, $retval );
+ ApiResult::setContent( $retval_array, $retval );
$result->addValue( null, $this->getModuleName(), $retval_array );
}
diff --git a/includes/api/ApiFeedContributions.php b/includes/api/ApiFeedContributions.php
index 015a9922..05691093 100644
--- a/includes/api/ApiFeedContributions.php
+++ b/includes/api/ApiFeedContributions.php
@@ -43,11 +43,11 @@ class ApiFeedContributions extends ApiBase {
global $wgFeed, $wgFeedClasses, $wgSitename, $wgLanguageCode;
- if( !$wgFeed ) {
+ if ( !$wgFeed ) {
$this->dieUsage( 'Syndication feeds are not available', 'feed-unavailable' );
}
- if( !isset( $wgFeedClasses[$params['feedformat']] ) ) {
+ if ( !isset( $wgFeedClasses[$params['feedformat']] ) ) {
$this->dieUsage( 'Invalid subscription feed type', 'feed-invalid' );
}
@@ -82,7 +82,7 @@ class ApiFeedContributions extends ApiBase {
) );
$feedItems = array();
- if( $pager->getNumRows() > 0 ) {
+ if ( $pager->getNumRows() > 0 ) {
foreach ( $pager->mResult as $row ) {
$feedItems[] = $this->feedItem( $row );
}
@@ -93,7 +93,7 @@ class ApiFeedContributions extends ApiBase {
protected function feedItem( $row ) {
$title = Title::makeTitle( intval( $row->page_namespace ), $row->page_title );
- if( $title ) {
+ if ( $title && $title->userCan( 'read', $this->getUser() ) ) {
$date = $row->rev_timestamp;
$comments = $title->getTalkPage()->getFullURL();
$revision = Revision::newFromRow( $row );
@@ -106,9 +106,8 @@ class ApiFeedContributions extends ApiBase {
$this->feedItemAuthor( $revision ),
$comments
);
- } else {
- return null;
}
+ return null;
}
/**
@@ -124,7 +123,7 @@ class ApiFeedContributions extends ApiBase {
* @return string
*/
protected function feedItemDesc( $revision ) {
- if( $revision ) {
+ if ( $revision ) {
$msg = wfMessage( 'colon-separator' )->inContentLanguage()->text();
$content = $revision->getContent();
@@ -149,7 +148,7 @@ class ApiFeedContributions extends ApiBase {
public function getAllowedParams() {
global $wgFeedClasses;
$feedFormatNames = array_keys( $wgFeedClasses );
- return array (
+ return array(
'feedformat' => array(
ApiBase::PARAM_DFLT => 'rss',
ApiBase::PARAM_TYPE => $feedFormatNames
diff --git a/includes/api/ApiFeedWatchlist.php b/includes/api/ApiFeedWatchlist.php
index 6c793b36..fbb70fbc 100644
--- a/includes/api/ApiFeedWatchlist.php
+++ b/includes/api/ApiFeedWatchlist.php
@@ -33,6 +33,10 @@
*/
class ApiFeedWatchlist extends ApiBase {
+ private $watchlistModule = null;
+ private $linkToDiffs = false;
+ private $linkToSections = false;
+
/**
* This module uses a custom feed wrapper printer.
*
@@ -42,8 +46,6 @@ 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.
@@ -54,11 +56,11 @@ class ApiFeedWatchlist extends ApiBase {
try {
$params = $this->extractRequestParams();
- if( !$wgFeed ) {
+ if ( !$wgFeed ) {
$this->dieUsage( 'Syndication feeds are not available', 'feed-unavailable' );
}
- if( !isset( $wgFeedClasses[$params['feedformat']] ) ) {
+ if ( !isset( $wgFeedClasses[$params['feedformat']] ) ) {
$this->dieUsage( 'Invalid subscription feed type', 'feed-invalid' );
}
@@ -74,7 +76,7 @@ class ApiFeedWatchlist extends ApiBase {
'wlprop' => 'title|user|comment|timestamp',
'wldir' => 'older', // reverse order - from newest to oldest
'wlend' => $endTime, // stop at this time
- 'wllimit' => ( 50 > $wgFeedLimit ) ? $wgFeedLimit : 50
+ 'wllimit' => min( 50, $wgFeedLimit )
);
if ( $params['wlowner'] !== null ) {
@@ -86,6 +88,12 @@ class ApiFeedWatchlist extends ApiBase {
if ( $params['wlexcludeuser'] !== null ) {
$fauxReqArr['wlexcludeuser'] = $params['wlexcludeuser'];
}
+ if ( $params['wlshow'] !== null ) {
+ $fauxReqArr['wlshow'] = $params['wlshow'];
+ }
+ if ( $params['wltype'] !== null ) {
+ $fauxReqArr['wltype'] = $params['wltype'];
+ }
// Support linking to diffs instead of article
if ( $params['linktodiffs'] ) {
@@ -93,6 +101,12 @@ class ApiFeedWatchlist extends ApiBase {
$fauxReqArr['wlprop'] .= '|ids';
}
+ // Support linking directly to sections when possible
+ // (possible only if section name is present in comment)
+ if ( $params['linktosections'] ) {
+ $this->linkToSections = true;
+ }
+
// Check for 'allrev' parameter, and if found, show all revisions to each page on wl.
if ( $params['allrev'] ) {
$fauxReqArr['wlallrev'] = '';
@@ -160,6 +174,18 @@ class ApiFeedWatchlist extends ApiBase {
$titleUrl = $title->getFullURL();
}
$comment = isset( $info['comment'] ) ? $info['comment'] : null;
+
+ // Create an anchor to section.
+ // The anchor won't work for sections that have dupes on page
+ // as there's no way to strip that info from ApiWatchlist (apparently?).
+ // RegExp in the line below is equal to Linker::formatAutocomments().
+ if ( $this->linkToSections && $comment !== null && preg_match( '!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment, $matches ) ) {
+ global $wgParser;
+ $sectionTitle = $wgParser->stripSectionName( $matches[2] );
+ $sectionTitle = Sanitizer::normalizeSectionNameWhitespace( $sectionTitle );
+ $titleUrl .= Title::newFromText( '#' . $sectionTitle )->getFragmentForURL();
+ }
+
$timestamp = $info['timestamp'];
$user = $info['user'];
@@ -168,10 +194,18 @@ class ApiFeedWatchlist extends ApiBase {
return new FeedItem( $titleStr, $completeText, $titleUrl, $timestamp, $user );
}
- public function getAllowedParams() {
+ private function getWatchlistModule() {
+ if ( $this->watchlistModule === null ) {
+ $this->watchlistModule = $this->getMain()->getModuleManager()->getModule( 'query' )
+ ->getModuleManager()->getModule( 'watchlist' );
+ }
+ return $this->watchlistModule;
+ }
+
+ public function getAllowedParams( $flags = 0 ) {
global $wgFeedClasses;
$feedFormatNames = array_keys( $wgFeedClasses );
- return array (
+ $ret = array(
'feedformat' => array(
ApiBase::PARAM_DFLT => 'rss',
ApiBase::PARAM_TYPE => $feedFormatNames
@@ -182,29 +216,41 @@ class ApiFeedWatchlist extends ApiBase {
ApiBase::PARAM_MIN => 1,
ApiBase::PARAM_MAX => 72,
),
- 'allrev' => false,
- 'wlowner' => array(
- ApiBase::PARAM_TYPE => 'user'
- ),
- 'wltoken' => array(
- ApiBase::PARAM_TYPE => 'string'
- ),
- 'wlexcludeuser' => array(
- ApiBase::PARAM_TYPE => 'user'
- ),
'linktodiffs' => false,
+ 'linktosections' => false,
);
+ if ( $flags ) {
+ $wlparams = $this->getWatchlistModule()->getAllowedParams( $flags );
+ $ret['allrev'] = $wlparams['allrev'];
+ $ret['wlowner'] = $wlparams['owner'];
+ $ret['wltoken'] = $wlparams['token'];
+ $ret['wlshow'] = $wlparams['show'];
+ $ret['wltype'] = $wlparams['type'];
+ $ret['wlexcludeuser'] = $wlparams['excludeuser'];
+ } else {
+ $ret['allrev'] = null;
+ $ret['wlowner'] = null;
+ $ret['wltoken'] = null;
+ $ret['wlshow'] = null;
+ $ret['wltype'] = null;
+ $ret['wlexcludeuser'] = null;
+ }
+ return $ret;
}
public function getParamDescription() {
+ $wldescr = $this->getWatchlistModule()->getParamDescription();
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 {$this->getModulePrefix()}wltoken if it's not you)",
- 'wltoken' => 'Security token that requested user set in their preferences',
- 'wlexcludeuser' => 'A user whose edits should not be shown in the watchlist',
+ 'hours' => 'List pages modified within this many hours from now',
'linktodiffs' => 'Link to change differences instead of article pages',
+ 'linktosections' => 'Link directly to changed sections if possible',
+ 'allrev' => $wldescr['allrev'],
+ 'wlowner' => $wldescr['owner'],
+ 'wltoken' => $wldescr['token'],
+ 'wlshow' => $wldescr['show'],
+ 'wltype' => $wldescr['type'],
+ 'wlexcludeuser' => $wldescr['excludeuser'],
);
}
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index d8aa1634..b89fb3a7 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -85,7 +85,7 @@ abstract class ApiFormatBase extends ApiBase {
*
* @param bool $b Whether or not ampersands should be escaped.
*/
- public function setUnescapeAmps ( $b ) {
+ public function setUnescapeAmps( $b ) {
$this->mUnescapeAmps = $b;
}
@@ -170,12 +170,12 @@ abstract class ApiFormatBase extends ApiBase {
?>
<br />
<small>
-You are looking at the HTML representation of the <?php echo( $this->mFormat ); ?> format.<br />
+You are looking at the HTML representation of the <?php echo $this->mFormat; ?> format.<br />
HTML is good for debugging, but is unsuitable for application use.<br />
Specify the format parameter to change the output format.<br />
-To see the non HTML representation of the <?php echo( $this->mFormat ); ?> format, set format=<?php echo( strtolower( $this->mFormat ) ); ?>.<br />
+To see the non HTML representation of the <?php echo $this->mFormat; ?> format, set format=<?php echo strtolower( $this->mFormat ); ?>.<br />
See the <a href='https://www.mediawiki.org/wiki/API'>complete documentation</a>, or
-<a href='<?php echo( $script ); ?>'>API help</a> for more information.
+<a href='<?php echo $script; ?>'>API help</a> for more information.
</small>
<pre style='white-space: pre-wrap;'>
<?php
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index abb63480..342a580f 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -56,30 +56,32 @@ class ApiFormatJson extends ApiFormatBase {
}
public function execute() {
- $prefix = $suffix = '';
-
$params = $this->extractRequestParams();
+ $json = FormatJson::encode(
+ $this->getResultData(),
+ $this->getIsHtml(),
+ $params['utf8'] ? FormatJson::ALL_OK : FormatJson::XMLMETA_OK
+ );
$callback = $params['callback'];
- if ( !is_null( $callback ) ) {
- $prefix = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", '', $callback ) . '(';
- $suffix = ')';
+ if ( $callback !== null ) {
+ $callback = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", '', $callback );
+ $this->printText( "$callback($json)" );
+ } else {
+ $this->printText( $json );
}
- $this->printText(
- $prefix .
- FormatJson::encode( $this->getResultData(), $this->getIsHtml() ) .
- $suffix
- );
}
public function getAllowedParams() {
return array(
- 'callback' => null,
+ 'callback' => null,
+ 'utf8' => false,
);
}
public function getParamDescription() {
return array(
'callback' => 'If specified, wraps the output into a given function call. For safety, all user-specific data will be restricted.',
+ 'utf8' => 'If specified, encodes most (but not all) non-ASCII characters as UTF-8 instead of replacing them with hexadecimal escape sequences.',
);
}
diff --git a/includes/api/ApiFormatWddx.php b/includes/api/ApiFormatWddx.php
index 62b69bb6..5685d937 100644
--- a/includes/api/ApiFormatWddx.php
+++ b/includes/api/ApiFormatWddx.php
@@ -46,7 +46,7 @@ class ApiFormatWddx extends ApiFormatBase {
} else {
// Don't do newlines and indentation if we weren't asked
// for pretty output
- $nl = ( $this->getIsHtml() ? '' : "\n" );
+ $nl = ( $this->getIsHtml() ? "\n" : '' );
$indstr = ' ';
$this->printText( "<?xml version=\"1.0\"?>$nl" );
$this->printText( "<wddxPacket version=\"1.0\">$nl" );
@@ -64,44 +64,43 @@ class ApiFormatWddx extends ApiFormatBase {
* @param $indent int
*/
function slowWddxPrinter( $elemValue, $indent = 0 ) {
- $indstr = ( $this->getIsHtml() ? '' : str_repeat( ' ', $indent ) );
- $indstr2 = ( $this->getIsHtml() ? '' : str_repeat( ' ', $indent + 2 ) );
- $nl = ( $this->getIsHtml() ? '' : "\n" );
- switch ( gettype( $elemValue ) ) {
- case 'array':
- // Check whether we've got an associative array (<struct>)
- // or a regular array (<array>)
- $cnt = count( $elemValue );
- if ( $cnt == 0 || array_keys( $elemValue ) === range( 0, $cnt - 1 ) ) {
- // Regular array
- $this->printText( $indstr . Xml::element( 'array', array(
- 'length' => $cnt ), null ) . $nl );
- foreach ( $elemValue as $subElemValue ) {
- $this->slowWddxPrinter( $subElemValue, $indent + 2 );
- }
- $this->printText( "$indstr</array>$nl" );
- } else {
- // Associative array (<struct>)
- $this->printText( "$indstr<struct>$nl" );
- foreach ( $elemValue as $subElemName => $subElemValue ) {
- $this->printText( $indstr2 . Xml::element( 'var', array(
- 'name' => $subElemName
- ), null ) . $nl );
- $this->slowWddxPrinter( $subElemValue, $indent + 4 );
- $this->printText( "$indstr2</var>$nl" );
- }
- $this->printText( "$indstr</struct>$nl" );
+ $indstr = ( $this->getIsHtml() ? str_repeat( ' ', $indent ) : '' );
+ $indstr2 = ( $this->getIsHtml() ? str_repeat( ' ', $indent + 2 ) : '' );
+ $nl = ( $this->getIsHtml() ? "\n" : '' );
+ if ( is_array( $elemValue ) ) {
+ // Check whether we've got an associative array (<struct>)
+ // or a regular array (<array>)
+ $cnt = count( $elemValue );
+ if ( $cnt == 0 || array_keys( $elemValue ) === range( 0, $cnt - 1 ) ) {
+ // Regular array
+ $this->printText( $indstr . Xml::element( 'array', array(
+ 'length' => $cnt ), null ) . $nl );
+ foreach ( $elemValue as $subElemValue ) {
+ $this->slowWddxPrinter( $subElemValue, $indent + 2 );
}
- break;
- case 'integer':
- case 'double':
- $this->printText( $indstr . Xml::element( 'number', null, $elemValue ) . $nl );
- break;
- case 'string':
- $this->printText( $indstr . Xml::element( 'string', null, $elemValue ) . $nl );
- break;
- default:
- ApiBase::dieDebug( __METHOD__, 'Unknown type ' . gettype( $elemValue ) );
+ $this->printText( "$indstr</array>$nl" );
+ } else {
+ // Associative array (<struct>)
+ $this->printText( "$indstr<struct>$nl" );
+ foreach ( $elemValue as $subElemName => $subElemValue ) {
+ $this->printText( $indstr2 . Xml::element( 'var', array(
+ 'name' => $subElemName
+ ), null ) . $nl );
+ $this->slowWddxPrinter( $subElemValue, $indent + 4 );
+ $this->printText( "$indstr2</var>$nl" );
+ }
+ $this->printText( "$indstr</struct>$nl" );
+ }
+ } elseif ( is_int( $elemValue ) || is_float( $elemValue ) ) {
+ $this->printText( $indstr . Xml::element( 'number', null, $elemValue ) . $nl );
+ } elseif ( is_string( $elemValue ) ) {
+ $this->printText( $indstr . Xml::element( 'string', null, $elemValue ) . $nl );
+ } elseif ( is_bool( $elemValue ) ) {
+ $this->printText( $indstr . Xml::element( 'boolean',
+ array( 'value' => $elemValue ? 'true' : 'false' ) ) . $nl
+ );
+ } else {
+ ApiBase::dieDebug( __METHOD__, 'Unknown type ' . gettype( $elemValue ) );
}
}
diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php
index b4e8e330..4ec149c0 100644
--- a/includes/api/ApiFormatXml.php
+++ b/includes/api/ApiFormatXml.php
@@ -32,7 +32,6 @@ class ApiFormatXml extends ApiFormatBase {
private $mRootElemName = 'api';
public static $namespace = 'http://www.mediawiki.org/xml/api/';
- private $mDoubleQuote = false;
private $mIncludeNamespace = false;
private $mXslt = null;
@@ -50,7 +49,6 @@ class ApiFormatXml extends ApiFormatBase {
public function execute() {
$params = $this->extractRequestParams();
- $this->mDoubleQuote = $params['xmldoublequote'];
$this->mIncludeNamespace = $params['includexmlnamespace'];
$this->mXslt = $params['xslt'];
@@ -71,8 +69,7 @@ class ApiFormatXml extends ApiFormatBase {
$this->printText(
self::recXmlPrint( $this->mRootElemName,
$data,
- $this->getIsHtml() ? - 2 : null,
- $this->mDoubleQuote
+ $this->getIsHtml() ? - 2 : null
)
);
}
@@ -117,11 +114,10 @@ class ApiFormatXml extends ApiFormatBase {
* @param $elemName
* @param $elemValue
* @param $indent
- * @param $doublequote bool
*
* @return string
*/
- public static function recXmlPrint( $elemName, $elemValue, $indent, $doublequote = false ) {
+ public static function recXmlPrint( $elemName, $elemValue, $indent ) {
$retval = '';
if ( !is_null( $indent ) ) {
$indent += 2;
@@ -131,84 +127,71 @@ class ApiFormatXml extends ApiFormatBase {
}
$elemName = str_replace( ' ', '_', $elemName );
- switch ( gettype( $elemValue ) ) {
- case 'array':
- if ( isset( $elemValue['*'] ) ) {
- $subElemContent = $elemValue['*'];
- if ( $doublequote ) {
- $subElemContent = Sanitizer::encodeAttribute( $subElemContent );
- }
- unset( $elemValue['*'] );
-
- // Add xml:space="preserve" to the
- // element so XML parsers will leave
- // whitespace in the content alone
- $elemValue['xml:space'] = 'preserve';
- } else {
- $subElemContent = null;
+ if ( is_array( $elemValue ) ) {
+ if ( isset( $elemValue['*'] ) ) {
+ $subElemContent = $elemValue['*'];
+ unset( $elemValue['*'] );
+
+ // Add xml:space="preserve" to the
+ // element so XML parsers will leave
+ // whitespace in the content alone
+ $elemValue['xml:space'] = 'preserve';
+ } else {
+ $subElemContent = null;
+ }
+
+ if ( isset( $elemValue['_element'] ) ) {
+ $subElemIndName = $elemValue['_element'];
+ unset( $elemValue['_element'] );
+ } else {
+ $subElemIndName = null;
+ }
+
+ $indElements = array();
+ $subElements = array();
+ foreach ( $elemValue as $subElemId => & $subElemValue ) {
+ if ( is_int( $subElemId ) ) {
+ $indElements[] = $subElemValue;
+ unset( $elemValue[$subElemId] );
+ } elseif ( is_array( $subElemValue ) ) {
+ $subElements[$subElemId] = $subElemValue;
+ unset( $elemValue[$subElemId] );
}
+ }
- if ( isset( $elemValue['_element'] ) ) {
- $subElemIndName = $elemValue['_element'];
- unset( $elemValue['_element'] );
- } else {
- $subElemIndName = null;
- }
+ if ( is_null( $subElemIndName ) && count( $indElements ) ) {
+ ApiBase::dieDebug( __METHOD__, "($elemName, ...) has integer keys without _element value. Use ApiResult::setIndexedTagName()." );
+ }
- $indElements = array();
- $subElements = array();
- foreach ( $elemValue as $subElemId => & $subElemValue ) {
- if ( is_string( $subElemValue ) && $doublequote ) {
- $subElemValue = Sanitizer::encodeAttribute( $subElemValue );
- }
-
- if ( gettype( $subElemId ) === 'integer' ) {
- $indElements[] = $subElemValue;
- unset( $elemValue[$subElemId] );
- } elseif ( is_array( $subElemValue ) ) {
- $subElements[$subElemId] = $subElemValue;
- unset ( $elemValue[$subElemId] );
- }
- }
+ if ( count( $subElements ) && count( $indElements ) && !is_null( $subElemContent ) ) {
+ ApiBase::dieDebug( __METHOD__, "($elemName, ...) has content and subelements" );
+ }
- if ( is_null( $subElemIndName ) && count( $indElements ) ) {
- ApiBase::dieDebug( __METHOD__, "($elemName, ...) has integer keys without _element value. Use ApiResult::setIndexedTagName()." );
- }
+ if ( !is_null( $subElemContent ) ) {
+ $retval .= $indstr . Xml::element( $elemName, $elemValue, $subElemContent );
+ } elseif ( !count( $indElements ) && !count( $subElements ) ) {
+ $retval .= $indstr . Xml::element( $elemName, $elemValue );
+ } else {
+ $retval .= $indstr . Xml::element( $elemName, $elemValue, null );
- if ( count( $subElements ) && count( $indElements ) && !is_null( $subElemContent ) ) {
- ApiBase::dieDebug( __METHOD__, "($elemName, ...) has content and subelements" );
+ foreach ( $subElements as $subElemId => & $subElemValue ) {
+ $retval .= self::recXmlPrint( $subElemId, $subElemValue, $indent );
}
- if ( !is_null( $subElemContent ) ) {
- $retval .= $indstr . Xml::element( $elemName, $elemValue, $subElemContent );
- } elseif ( !count( $indElements ) && !count( $subElements ) ) {
- $retval .= $indstr . Xml::element( $elemName, $elemValue );
- } else {
- $retval .= $indstr . Xml::element( $elemName, $elemValue, null );
-
- foreach ( $subElements as $subElemId => & $subElemValue ) {
- $retval .= self::recXmlPrint( $subElemId, $subElemValue, $indent );
- }
-
- foreach ( $indElements as &$subElemValue ) {
- $retval .= self::recXmlPrint( $subElemIndName, $subElemValue, $indent );
- }
-
- $retval .= $indstr . Xml::closeElement( $elemName );
+ foreach ( $indElements as &$subElemValue ) {
+ $retval .= self::recXmlPrint( $subElemIndName, $subElemValue, $indent );
}
- break;
- case 'object':
- // ignore
- break;
- default:
- // to make sure null value doesn't produce unclosed element,
- // which is what Xml::element( $elemName, null, null ) returns
- if ( $elemValue === null ) {
- $retval .= $indstr . Xml::element( $elemName );
- } else {
- $retval .= $indstr . Xml::element( $elemName, null, $elemValue );
- }
- break;
+
+ $retval .= $indstr . Xml::closeElement( $elemName );
+ }
+ } elseif ( !is_object( $elemValue ) ) {
+ // to make sure null value doesn't produce unclosed element,
+ // which is what Xml::element( $elemName, null, null ) returns
+ if ( $elemValue === null ) {
+ $retval .= $indstr . Xml::element( $elemName );
+ } else {
+ $retval .= $indstr . Xml::element( $elemName, null, $elemValue );
+ }
}
return $retval;
}
@@ -232,7 +215,6 @@ class ApiFormatXml extends ApiFormatBase {
public function getAllowedParams() {
return array(
- 'xmldoublequote' => false,
'xslt' => null,
'includexmlnamespace' => false,
);
@@ -240,7 +222,6 @@ class ApiFormatXml extends ApiFormatBase {
public function getParamDescription() {
return array(
- 'xmldoublequote' => 'If specified, double quotes all attributes and content',
'xslt' => 'If specified, adds <xslt> as stylesheet. This should be a wiki page '
. 'in the MediaWiki namespace whose page name ends with ".xsl"',
'includexmlnamespace' => 'If specified, adds an XML namespace'
diff --git a/includes/api/ApiImageRotate.php b/includes/api/ApiImageRotate.php
index b2d75825..7a60e831 100644
--- a/includes/api/ApiImageRotate.php
+++ b/includes/api/ApiImageRotate.php
@@ -22,7 +22,6 @@
*/
class ApiImageRotate extends ApiBase {
-
private $mPageSet = null;
public function __construct( $main, $action ) {
@@ -38,32 +37,29 @@ class ApiImageRotate extends ApiBase {
*/
private static function addValues( array &$result, $values, $flag = null, $name = null ) {
foreach ( $values as $val ) {
- if( $val instanceof Title ) {
+ if ( $val instanceof Title ) {
$v = array();
ApiQueryBase::addTitleInfo( $v, $val );
- } elseif( $name !== null ) {
+ } elseif ( $name !== null ) {
$v = array( $name => $val );
} else {
$v = $val;
}
- if( $flag !== null ) {
+ if ( $flag !== null ) {
$v[$flag] = '';
}
$result[] = $v;
}
}
-
public function execute() {
$params = $this->extractRequestParams();
- $rotation = $params[ 'rotation' ];
- $user = $this->getUser();
+ $rotation = $params['rotation'];
$pageSet = $this->getPageSet();
$pageSet->execute();
$result = array();
- $result = array();
self::addValues( $result, $pageSet->getInvalidTitles(), 'invalid', 'title' );
self::addValues( $result, $pageSet->getSpecialTitles(), 'special', 'title' );
@@ -111,15 +107,17 @@ class ApiImageRotate extends ApiBase {
continue;
}
$ext = strtolower( pathinfo( "$srcPath", PATHINFO_EXTENSION ) );
- $tmpFile = TempFSFile::factory( 'rotate_', $ext);
+ $tmpFile = TempFSFile::factory( 'rotate_', $ext );
$dstPath = $tmpFile->getPath();
$err = $handler->rotate( $file, array(
"srcPath" => $srcPath,
"dstPath" => $dstPath,
- "rotation"=> $rotation
+ "rotation" => $rotation
) );
if ( !$err ) {
- $comment = wfMessage( 'rotate-comment' )->numParams( $rotation )->text();
+ $comment = wfMessage(
+ 'rotate-comment'
+ )->numParams( $rotation )->inContentLanguage()->text();
$status = $file->upload( $dstPath,
$comment, $comment, 0, false, false, $this->getUser() );
if ( $status->isGood() ) {
@@ -152,13 +150,14 @@ class ApiImageRotate extends ApiBase {
/**
* Checks that the user has permissions to perform rotations.
- * @param $user User The user to check.
+ * @param User $user The user to check
+ * @param Title $title
* @return string|null Permission error message, or null if there is no error
*/
protected function checkPermissions( $user, $title ) {
$permissionErrors = array_merge(
- $title->getUserPermissionsErrors( 'edit' , $user ),
- $title->getUserPermissionsErrors( 'upload' , $user )
+ $title->getUserPermissionsErrors( 'edit', $user ),
+ $title->getUserPermissionsErrors( 'upload', $user )
);
if ( $permissionErrors ) {
@@ -179,7 +178,6 @@ class ApiImageRotate extends ApiBase {
}
public function getAllowedParams( $flags = 0 ) {
- $pageSet = $this->getPageSet();
$result = array(
'rotation' => array(
ApiBase::PARAM_TYPE => array( '90', '180', '270' ),
@@ -198,7 +196,7 @@ class ApiImageRotate extends ApiBase {
public function getParamDescription() {
$pageSet = $this->getPageSet();
- return $pageSet->getParamDescription() + array(
+ return $pageSet->getFinalParamDescription() + array(
'rotation' => 'Degrees to rotate image clockwise',
'token' => 'Edit token. You can get one of these through action=tokens',
);
@@ -220,7 +218,7 @@ class ApiImageRotate extends ApiBase {
$pageSet = $this->getPageSet();
return array_merge(
parent::getPossibleErrors(),
- $pageSet->getPossibleErrors()
+ $pageSet->getFinalPossibleErrors()
);
}
diff --git a/includes/api/ApiImport.php b/includes/api/ApiImport.php
index 1f0a5fab..f48a822e 100644
--- a/includes/api/ApiImport.php
+++ b/includes/api/ApiImport.php
@@ -57,7 +57,7 @@ class ApiImport extends ApiBase {
$source = ImportStreamSource::newFromUpload( 'xml' );
}
if ( !$source->isOK() ) {
- $this->dieUsageMsg( $source->getErrorsArray() );
+ $this->dieStatus( $source );
}
$importer = new WikiImporter( $source->value );
@@ -66,8 +66,8 @@ class ApiImport extends ApiBase {
}
if ( isset( $params['rootpage'] ) ) {
$statusRootPage = $importer->setTargetRootPage( $params['rootpage'] );
- if( !$statusRootPage->isGood() ) {
- $this->dieUsageMsg( $statusRootPage->getErrorsArray() );
+ if ( !$statusRootPage->isGood() ) {
+ $this->dieStatus( $statusRootPage );
}
}
$reporter = new ApiImportReporter(
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index 7b2fd914..c11f16cb 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -83,7 +83,7 @@ class ApiMain extends ApiBase {
'import' => 'ApiImport',
'userrights' => 'ApiUserrights',
'options' => 'ApiOptions',
- 'imagerotate' =>'ApiImageRotate',
+ 'imagerotate' => 'ApiImageRotate',
);
/**
@@ -274,7 +274,7 @@ class ApiMain extends ApiBase {
return;
}
- if ( !User::groupHasPermission( '*', 'read' ) ) {
+ if ( !User::isEveryoneAllowed( 'read' ) ) {
// Private wiki, only private headers
if ( $mode !== 'private' ) {
wfDebug( __METHOD__ . ": ignoring request for $mode cache mode, private wiki\n" );
@@ -383,13 +383,8 @@ class ApiMain extends ApiBase {
wfRunHooks( 'ApiMain::onException', array( $this, $e ) );
// Log it
- if ( $e instanceof MWException && !( $e instanceof UsageException ) ) {
- global $wgLogExceptionBacktrace;
- if ( $wgLogExceptionBacktrace ) {
- wfDebugLog( 'exception', $e->getLogMessage() . "\n" . $e->getTraceAsString() . "\n" );
- } else {
- wfDebugLog( 'exception', $e->getLogMessage() );
- }
+ if ( !( $e instanceof UsageException ) ) {
+ MWExceptionHandler::logException( $e );
}
// Handle any kind of exception by outputting properly formatted error message.
@@ -418,7 +413,7 @@ class ApiMain extends ApiBase {
}
// Log the request whether or not there was an error
- $this->logRequest( microtime( true ) - $t);
+ $this->logRequest( microtime( true ) - $t );
// Send cache headers after any code which might generate an error, to
// avoid sending public cache headers for errors.
@@ -609,7 +604,7 @@ class ApiMain extends ApiBase {
$result = $this->getResult();
// Printer may not be initialized if the extractRequestParams() fails for the main module
- if ( !isset ( $this->mPrinter ) ) {
+ 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 ( !$this->mModuleMgr->isDefined( $value, 'format' ) ) {
@@ -763,7 +758,7 @@ class ApiMain extends ApiBase {
*/
protected function checkExecutePermissions( $module ) {
$user = $this->getUser();
- if ( $module->isReadMode() && !User::groupHasPermission( '*', 'read' ) &&
+ if ( $module->isReadMode() && !User::isEveryoneAllowed( 'read' ) &&
!$user->isAllowed( 'read' ) )
{
$this->dieUsageMsg( 'readrequired' );
@@ -782,7 +777,7 @@ class ApiMain extends ApiBase {
// Allow extensions to stop execution for arbitrary reasons.
$message = false;
- if( !wfRunHooks( 'ApiCheckCanExecute', array( $module, $user, &$message ) ) ) {
+ if ( !wfRunHooks( 'ApiCheckCanExecute', array( $module, $user, &$message ) ) ) {
$this->dieUsageMsg( $message );
}
}
@@ -857,7 +852,7 @@ class ApiMain extends ApiBase {
' ' . $request->getMethod() .
' ' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) .
' ' . $request->getIP() .
- ' T=' . $milliseconds .'ms';
+ ' T=' . $milliseconds . 'ms';
foreach ( $this->getParamsUsed() as $name ) {
$value = $request->getVal( $name );
if ( $value === null ) {
@@ -944,7 +939,7 @@ class ApiMain extends ApiBase {
$unusedParams = array_diff( $allParams, $paramsUsed );
}
- if( count( $unusedParams ) ) {
+ if ( count( $unusedParams ) ) {
$s = count( $unusedParams ) > 1 ? 's' : '';
$this->setWarning( "Unrecognized parameter$s: '" . implode( $unusedParams, "', '" ) . "'" );
}
@@ -957,7 +952,7 @@ class ApiMain extends ApiBase {
*/
protected function printResult( $isError ) {
global $wgDebugAPI;
- if( $wgDebugAPI !== false ) {
+ if ( $wgDebugAPI !== false ) {
$this->setWarning( 'SECURITY WARNING: $wgDebugAPI is enabled' );
}
@@ -1002,7 +997,7 @@ class ApiMain extends ApiBase {
ApiBase::PARAM_DFLT => 'help',
ApiBase::PARAM_TYPE => $this->mModuleMgr->getNames( 'action' )
),
- 'maxlag' => array(
+ 'maxlag' => array(
ApiBase::PARAM_TYPE => 'integer'
),
'smaxage' => array(
@@ -1014,7 +1009,7 @@ class ApiMain extends ApiBase {
ApiBase::PARAM_DFLT => 0
),
'requestid' => null,
- 'servedby' => false,
+ 'servedby' => false,
'origin' => null,
);
}
@@ -1042,6 +1037,7 @@ class ApiMain extends ApiBase {
'servedby' => 'Include the hostname that served the request in the results. Unconditionally shown on error',
'origin' => array(
'When accessing the API using a cross-domain AJAX request (CORS), set this to the originating domain.',
+ 'This must be included in any pre-flight request, and therefore must be part of the request URI (not the POST body).',
'This must match one of the origins in the Origin: header exactly, so it has to be set to something like http://en.wikipedia.org or https://meta.wikimedia.org .',
'If this parameter does not match the Origin: header, a 403 response will be returned.',
'If this parameter matches the Origin: header and the origin is whitelisted, an Access-Control-Allow-Origin header will be set.',
@@ -1114,7 +1110,7 @@ class ApiMain extends ApiBase {
return array(
'API developers:',
' Roan Kattouw "<Firstname>.<Lastname>@gmail.com" (lead developer Sep 2007-2009)',
- ' Victor Vasiliev - vasilvv at gee mail dot com',
+ ' Victor Vasiliev - vasilvv @ gmail . com',
' Bryan Tong Minh - bryan . tongminh @ gmail . com',
' Sam Reed - sam @ reedyboy . net',
' Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007, 2012-present)',
@@ -1143,7 +1139,7 @@ class ApiMain extends ApiBase {
$this->setHelp();
// Get help text from cache if present
$key = wfMemcKey( 'apihelp', $this->getModuleName(),
- SpecialVersion::getVersion( 'nodb' ) );
+ str_replace( ' ', '_', SpecialVersion::getVersion( 'nodb' ) ) );
if ( $wgAPICacheHelpTimeout > 0 ) {
$cached = $wgMemc->get( $key );
if ( $cached ) {
diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php
index 3e846e3b..c18036cf 100644
--- a/includes/api/ApiMove.php
+++ b/includes/api/ApiMove.php
@@ -88,7 +88,7 @@ class ApiMove extends ApiBase {
$r['redirectcreated'] = '';
}
- if( $toTitleExists ) {
+ if ( $toTitleExists ) {
$r['moveoverredirect'] = '';
}
@@ -99,7 +99,7 @@ class ApiMove extends ApiBase {
if ( $retval === true ) {
$r['talkfrom'] = $fromTalk->getPrefixedText();
$r['talkto'] = $toTalk->getPrefixedText();
- if( $toTalkExists ) {
+ if ( $toTalkExists ) {
$r['talkmoveoverredirect'] = '';
}
} else {
diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php
index caf361ac..315ace37 100644
--- a/includes/api/ApiOpenSearch.php
+++ b/includes/api/ApiOpenSearch.php
@@ -1,7 +1,5 @@
<?php
/**
- *
- *
* Created on Oct 13, 2006
*
* Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
@@ -29,8 +27,20 @@
*/
class ApiOpenSearch extends ApiBase {
+ /**
+ * Override built-in handling of format parameter.
+ * Only JSON is supported.
+ *
+ * @return ApiFormatBase
+ */
public function getCustomPrinter() {
- return $this->getMain()->createPrinterByName( 'json' );
+ $params = $this->extractRequestParams();
+ $format = $params['format'];
+ $allowed = array( 'json', 'jsonfm' );
+ if ( in_array( $format, $allowed ) ) {
+ return $this->getMain()->createPrinterByName( $format );
+ }
+ return $this->getMain()->createPrinterByName( $allowed[0] );
}
public function execute() {
@@ -94,6 +104,10 @@ class ApiOpenSearch extends ApiBase {
ApiBase::PARAM_ISMULTI => true
),
'suggest' => false,
+ 'format' => array(
+ ApiBase::PARAM_DFLT => 'json',
+ ApiBase::PARAM_TYPE => array( 'json', 'jsonfm' ),
+ )
);
}
@@ -103,6 +117,7 @@ class ApiOpenSearch extends ApiBase {
'limit' => 'Maximum amount of results to return',
'namespace' => 'Namespaces to search',
'suggest' => 'Do nothing if $wgEnableOpenSearchSuggest is false',
+ 'format' => 'The format of the output',
);
}
diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php
index 8c996a26..7256066d 100644
--- a/includes/api/ApiOptions.php
+++ b/includes/api/ApiOptions.php
@@ -42,6 +42,10 @@ class ApiOptions extends ApiBase {
$this->dieUsage( 'Anonymous users cannot change preferences', 'notloggedin' );
}
+ if ( !$user->isAllowed( 'editmyoptions' ) ) {
+ $this->dieUsage( 'You don\'t have permission to edit your options', 'permissiondenied' );
+ }
+
$params = $this->extractRequestParams();
$changed = false;
@@ -50,7 +54,7 @@ class ApiOptions extends ApiBase {
}
if ( $params['reset'] ) {
- $user->resetOptions( $params['resetkinds'] );
+ $user->resetOptions( $params['resetkinds'], $this->getContext() );
$changed = true;
}
@@ -83,7 +87,7 @@ class ApiOptions extends ApiBase {
case 'registered-checkmatrix':
// A key for a multiselect or checkmatrix option.
$validation = true;
- $value = $value !== null ? (bool) $value : null;
+ $value = $value !== null ? (bool)$value : null;
break;
case 'userjs':
// Allow non-default preferences prefixed with 'userjs-', to be set by user scripts
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index 074efe4b..b05cb2b6 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -69,6 +69,9 @@ class ApiPageSet extends ApiBase {
private $mFakePageId = -1;
private $mCacheMode = 'public';
private $mRequestedPageFields = array();
+ /**
+ * @var int
+ */
private $mDefaultNamespace = NS_MAIN;
/**
@@ -149,7 +152,6 @@ class ApiPageSet extends ApiBase {
if ( !$isDryRun ) {
$generator->executeGenerator( $this );
wfRunHooks( 'APIQueryGeneratorAfterExecute', array( &$generator, &$this ) );
- $this->resolvePendingRedirects();
} else {
// Prevent warnings from being reported on these parameters
$main = $this->getMain();
@@ -160,6 +162,10 @@ class ApiPageSet extends ApiBase {
$generator->profileOut();
$this->profileIn();
+ if ( !$isDryRun ) {
+ $this->resolvePendingRedirects();
+ }
+
if ( !$isQuery ) {
// If this pageset is not part of the query, we called profileIn() above
$dbSource->profileOut();
@@ -185,7 +191,7 @@ class ApiPageSet extends ApiBase {
if ( !$isDryRun ) {
// Populate page information with the original user input
- switch( $dataSource ) {
+ switch ( $dataSource ) {
case 'titles':
$this->initFromTitles( $this->mParams['titles'] );
break;
@@ -404,7 +410,7 @@ class ApiPageSet extends ApiBase {
* @return array of raw_prefixed_title (string) => prefixed_title (string)
* @since 1.21
*/
- public function getNormalizedTitlesAsResult( $result = null ) {
+ public function getNormalizedTitlesAsResult( $result = null ) {
$values = array();
foreach ( $this->getNormalizedTitles() as $rawTitleStr => $titleStr ) {
$values[] = array(
@@ -595,13 +601,13 @@ class ApiPageSet extends ApiBase {
}
foreach ( $this->mRequestedPageFields as $fieldName => &$fieldValues ) {
- $fieldValues[$pageId] = $row-> $fieldName;
+ $fieldValues[$pageId] = $row->$fieldName;
}
}
/**
* Do not use, does nothing, will be removed
- * @deprecated 1.21
+ * @deprecated since 1.21
*/
public function finishPageSetGeneration() {
wfDeprecated( __METHOD__, '1.21' );
@@ -859,7 +865,7 @@ class ApiPageSet extends ApiBase {
$from = $this->mPendingRedirectIDs[$rdfrom]->getPrefixedText();
$to = Title::makeTitle( $row->rd_namespace, $row->rd_title, $row->rd_fragment, $row->rd_interwiki );
unset( $this->mPendingRedirectIDs[$rdfrom] );
- if ( !isset( $this->mAllPages[$row->rd_namespace][$row->rd_title] ) ) {
+ if ( !$to->isExternal() && !isset( $this->mAllPages[$row->rd_namespace][$row->rd_title] ) ) {
$lb->add( $row->rd_namespace, $row->rd_title );
}
$this->mRedirectTitles[$from] = $to;
diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php
index 27f8cefd..3e1a7531 100644
--- a/includes/api/ApiParamInfo.php
+++ b/includes/api/ApiParamInfo.php
@@ -149,7 +149,7 @@ class ApiParamInfo extends ApiBase {
$item = array();
if ( is_numeric( $k ) ) {
$retval['examples'] .= $v;
- $result->setContent( $item, $v );
+ ApiResult::setContent( $item, $v );
} else {
if ( !is_array( $v ) ) {
$item['description'] = $v;
@@ -157,7 +157,7 @@ class ApiParamInfo extends ApiBase {
$item['description'] = implode( $v, "\n" );
}
$retval['examples'] .= $item['description'] . ' ' . $k;
- $result->setContent( $item, $k );
+ ApiResult::setContent( $item, $k );
}
$retval['allexamples'][] = $item;
}
@@ -300,7 +300,7 @@ class ApiParamInfo extends ApiBase {
}
// Errors
- $retval['errors'] = $this->parseErrors( $obj->getPossibleErrors() );
+ $retval['errors'] = $this->parseErrors( $obj->getFinalPossibleErrors() );
$result->setIndexedTagName( $retval['errors'], 'error' );
return $retval;
diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php
index 09b7a882..a369994b 100644
--- a/includes/api/ApiParse.php
+++ b/includes/api/ApiParse.php
@@ -44,6 +44,14 @@ class ApiParse extends ApiBase {
$params = $this->extractRequestParams();
$text = $params['text'];
$title = $params['title'];
+ if ( $title === null ) {
+ $titleProvided = false;
+ // A title is needed for parsing, so arbitrarily choose one
+ $title = 'API';
+ } else {
+ $titleProvided = true;
+ }
+
$page = $params['page'];
$pageid = $params['pageid'];
$oldid = $params['oldid'];
@@ -51,7 +59,7 @@ class ApiParse extends ApiBase {
$model = $params['contentmodel'];
$format = $params['contentformat'];
- if ( !is_null( $page ) && ( !is_null( $text ) || $title != 'API' ) ) {
+ if ( !is_null( $page ) && ( !is_null( $text ) || $titleProvided ) ) {
$this->dieUsage( 'The page parameter cannot be used together with the text and title parameters', 'params' );
}
@@ -94,8 +102,7 @@ class ApiParse extends ApiBase {
$titleObj = $rev->getTitle();
$wgTitle = $titleObj;
$pageObj = WikiPage::factory( $titleObj );
- $popts = $pageObj->makeParserOptions( $this->getContext() );
- $popts->enableLimitReport( !$params['disablepp'] );
+ $popts = $this->makeParserOptions( $pageObj, $params );
// If for some reason the "oldid" is actually the current revision, it may be cached
if ( $rev->isCurrent() ) {
@@ -152,8 +159,7 @@ class ApiParse extends ApiBase {
$oldid = $pageObj->getLatest();
}
- $popts = $pageObj->makeParserOptions( $this->getContext() );
- $popts->enableLimitReport( !$params['disablepp'] );
+ $popts = $this->makeParserOptions( $pageObj, $params );
// Potentially cached
$p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
@@ -170,11 +176,24 @@ class ApiParse extends ApiBase {
$wgTitle = $titleObj;
$pageObj = WikiPage::factory( $titleObj );
- $popts = $pageObj->makeParserOptions( $this->getContext() );
- $popts->enableLimitReport( !$params['disablepp'] );
+ $popts = $this->makeParserOptions( $pageObj, $params );
if ( is_null( $text ) ) {
- $this->dieUsage( 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?', 'params' );
+ if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
+ $this->setWarning(
+ "'title' used without 'text', and parsed page properties were requested " .
+ "(did you mean to use 'page' instead of 'title'?)"
+ );
+ }
+ // Prevent warning from ContentHandler::makeContent()
+ $text = '';
+ }
+
+ // If we are parsing text, do not use the content model of the default
+ // API title, but default to wikitext to keep BC.
+ if ( !$titleProvided && is_null( $model ) ) {
+ $model = CONTENT_MODEL_WIKITEXT;
+ $this->setWarning( "No 'title' or 'contentmodel' was given, assuming $model." );
}
try {
@@ -194,10 +213,10 @@ class ApiParse extends ApiBase {
// Build a result and bail out
$result_array = array();
$result_array['text'] = array();
- $result->setContent( $result_array['text'], $this->pstContent->serialize( $format ) );
+ ApiResult::setContent( $result_array['text'], $this->pstContent->serialize( $format ) );
if ( isset( $prop['wikitext'] ) ) {
$result_array['wikitext'] = array();
- $result->setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
+ ApiResult::setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
}
$result->addValue( null, $this->getModuleName(), $result_array );
return;
@@ -225,21 +244,35 @@ class ApiParse extends ApiBase {
if ( isset( $prop['text'] ) ) {
$result_array['text'] = array();
- $result->setContent( $result_array['text'], $p_result->getText() );
+ ApiResult::setContent( $result_array['text'], $p_result->getText() );
}
if ( !is_null( $params['summary'] ) ) {
$result_array['parsedsummary'] = array();
- $result->setContent( $result_array['parsedsummary'], Linker::formatComment( $params['summary'], $titleObj ) );
+ ApiResult::setContent( $result_array['parsedsummary'], Linker::formatComment( $params['summary'], $titleObj ) );
+ }
+
+ if ( isset( $prop['langlinks'] ) || isset( $prop['languageshtml'] ) ) {
+ $langlinks = $p_result->getLanguageLinks();
+
+ if ( $params['effectivelanglinks'] ) {
+ // Link flags are ignored for now, but may in the future be
+ // included in the result.
+ $linkFlags = array();
+ wfRunHooks( 'LanguageLinks', array( $titleObj, &$langlinks, &$linkFlags ) );
+ }
+ } else {
+ $langlinks = false;
}
if ( isset( $prop['langlinks'] ) ) {
- $result_array['langlinks'] = $this->formatLangLinks( $p_result->getLanguageLinks() );
+ $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
}
if ( isset( $prop['languageshtml'] ) ) {
- $languagesHtml = $this->languagesHtml( $p_result->getLanguageLinks() );
+ $languagesHtml = $this->languagesHtml( $langlinks );
+
$result_array['languageshtml'] = array();
- $result->setContent( $result_array['languageshtml'], $languagesHtml );
+ ApiResult::setContent( $result_array['languageshtml'], $languagesHtml );
}
if ( isset( $prop['categories'] ) ) {
$result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
@@ -247,7 +280,7 @@ class ApiParse extends ApiBase {
if ( isset( $prop['categorieshtml'] ) ) {
$categoriesHtml = $this->categoriesHtml( $p_result->getCategories() );
$result_array['categorieshtml'] = array();
- $result->setContent( $result_array['categorieshtml'], $categoriesHtml );
+ ApiResult::setContent( $result_array['categorieshtml'], $categoriesHtml );
}
if ( isset( $prop['links'] ) ) {
$result_array['links'] = $this->formatLinks( $p_result->getLinks() );
@@ -288,7 +321,7 @@ class ApiParse extends ApiBase {
if ( isset( $prop['headhtml'] ) ) {
$result_array['headhtml'] = array();
- $result->setContent( $result_array['headhtml'], $context->getOutput()->headElement( $context->getSkin() ) );
+ ApiResult::setContent( $result_array['headhtml'], $context->getOutput()->headElement( $context->getSkin() ) );
}
}
@@ -298,10 +331,10 @@ class ApiParse extends ApiBase {
if ( isset( $prop['wikitext'] ) ) {
$result_array['wikitext'] = array();
- $result->setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
+ ApiResult::setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
if ( !is_null( $this->pstContent ) ) {
$result_array['psttext'] = array();
- $result->setContent( $result_array['psttext'], $this->pstContent->serialize( $format ) );
+ ApiResult::setContent( $result_array['psttext'], $this->pstContent->serialize( $format ) );
}
}
if ( isset( $prop['properties'] ) ) {
@@ -321,7 +354,7 @@ class ApiParse extends ApiBase {
$xml = $dom->__toString();
}
$result_array['parsetree'] = array();
- $result->setContent( $result_array['parsetree'], $xml );
+ ApiResult::setContent( $result_array['parsetree'], $xml );
}
$result_mapping = array(
@@ -346,6 +379,26 @@ class ApiParse extends ApiBase {
}
/**
+ * Constructs a ParserOptions object
+ *
+ * @param WikiPage $pageObj
+ * @param array $params
+ *
+ * @return ParserOptions
+ */
+ protected function makeParserOptions( WikiPage $pageObj, array $params ) {
+ wfProfileIn( __METHOD__ );
+
+ $popts = $pageObj->makeParserOptions( $this->getContext() );
+ $popts->enableLimitReport( !$params['disablepp'] );
+ $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
+ $popts->setIsSectionPreview( $params['sectionpreview'] );
+
+ wfProfileOut( __METHOD__ );
+ return $popts;
+ }
+
+ /**
* @param $page WikiPage
* @param $popts ParserOptions
* @param $pageId Int
@@ -400,7 +453,7 @@ class ApiParse extends ApiBase {
if ( $title ) {
$entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
}
- $this->getResult()->setContent( $entry, $bits[1] );
+ ApiResult::setContent( $entry, $bits[1] );
$result[] = $entry;
}
return $result;
@@ -411,7 +464,7 @@ class ApiParse extends ApiBase {
foreach ( $links as $link => $sortkey ) {
$entry = array();
$entry['sortkey'] = $sortkey;
- $this->getResult()->setContent( $entry, $link );
+ ApiResult::setContent( $entry, $link );
$result[] = $entry;
}
return $result;
@@ -465,7 +518,7 @@ class ApiParse extends ApiBase {
foreach ( $nslinks as $title => $id ) {
$entry = array();
$entry['ns'] = $ns;
- $this->getResult()->setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() );
+ ApiResult::setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() );
if ( $id != 0 ) {
$entry['exists'] = '';
}
@@ -487,7 +540,7 @@ class ApiParse extends ApiBase {
$entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
}
- $this->getResult()->setContent( $entry, $title->getFullText() );
+ ApiResult::setContent( $entry, $title->getFullText() );
$result[] = $entry;
}
}
@@ -499,7 +552,7 @@ class ApiParse extends ApiBase {
foreach ( $headItems as $tag => $content ) {
$entry = array();
$entry['tag'] = $tag;
- $this->getResult()->setContent( $entry, $content );
+ ApiResult::setContent( $entry, $content );
$result[] = $entry;
}
return $result;
@@ -510,7 +563,7 @@ class ApiParse extends ApiBase {
foreach ( $properties as $name => $value ) {
$entry = array();
$entry['name'] = $name;
- $this->getResult()->setContent( $entry, $value );
+ ApiResult::setContent( $entry, $value );
$result[] = $entry;
}
return $result;
@@ -521,7 +574,7 @@ class ApiParse extends ApiBase {
foreach ( $css as $file => $link ) {
$entry = array();
$entry['file'] = $file;
- $this->getResult()->setContent( $entry, $link );
+ ApiResult::setContent( $entry, $link );
$result[] = $entry;
}
return $result;
@@ -537,9 +590,7 @@ class ApiParse extends ApiBase {
public function getAllowedParams() {
return array(
- 'title' => array(
- ApiBase::PARAM_DFLT => 'API',
- ),
+ 'title' => null,
'text' => null,
'summary' => null,
'page' => null,
@@ -575,10 +626,13 @@ class ApiParse extends ApiBase {
),
'pst' => false,
'onlypst' => false,
+ 'effectivelanglinks' => false,
'uselang' => null,
'section' => null,
'disablepp' => false,
'generatexml' => false,
+ 'preview' => false,
+ 'sectionpreview' => false,
'contentformat' => array(
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
),
@@ -590,11 +644,13 @@ class ApiParse extends ApiBase {
public function getParamDescription() {
$p = $this->getModulePrefix();
+ $wikitext = CONTENT_MODEL_WIKITEXT;
return array(
- 'text' => 'Wikitext to parse',
+ 'text' => "Text to parse. Use {$p}title or {$p}contentmodel to control the content model",
'summary' => 'Summary to parse',
'redirects' => "If the {$p}page or the {$p}pageid parameter is set to a redirect, resolve it",
- 'title' => 'Title of page the text belongs to',
+ 'title' => "Title of page the text belongs to. " .
+ "If omitted, \"API\" is used as the title with content model $wikitext",
'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",
@@ -618,34 +674,52 @@ class ApiParse extends ApiBase {
' wikitext - Gives the original wikitext that was parsed',
' properties - Gives various properties defined in the parsed wikitext',
),
+ 'effectivelanglinks' => array(
+ 'Includes language links supplied by extensions',
+ '(for use with prop=langlinks|languageshtml)',
+ ),
'pst' => array(
'Do a pre-save transform on the input before parsing it',
- 'Ignored if page, pageid or oldid is used'
+ "Only valid when used with {$p}text",
),
'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'
+ 'Returns the same wikitext, after a PST has been applied.',
+ "Only valid when used with {$p}text",
),
'uselang' => 'Which language to parse the request in',
'section' => 'Only retrieve the content of this section number',
'disablepp' => 'Disable the PP Report from the parser output',
- 'generatexml' => 'Generate XML parse tree (requires prop=wikitext)',
- 'contentformat' => 'Content serialization format used for the input text',
- 'contentmodel' => 'Content model of the new content',
+ 'generatexml' => "Generate XML parse tree (requires contentmodel=$wikitext)",
+ 'preview' => 'Parse in preview mode',
+ 'sectionpreview' => 'Parse in section preview mode (enables preview mode too)',
+ 'contentformat' => array(
+ 'Content serialization format used for the input text',
+ "Only valid when used with {$p}text",
+ ),
+ 'contentmodel' => array(
+ "Content model of the input text. Default is the model of the " .
+ "specified ${p}title, or $wikitext if ${p}title is not specified",
+ "Only valid when used with {$p}text",
+ ),
);
}
public function getDescription() {
+ $p = $this->getModulePrefix();
return array(
- 'Parses wikitext and returns parser output',
+ 'Parses content and returns parser output',
'See the various prop-Modules of action=query to get information from the current version of a page',
+ 'There are several ways to specify the text to parse:',
+ "1) Specify a page or revision, using {$p}page, {$p}pageid, or {$p}oldid.",
+ "2) Specify content explicitly, using {$p}text, {$p}title, and {$p}contentmodel.",
+ "3) Specify only a summary to parse. {$p}prop should be given an empty value.",
);
}
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' => 'params', 'info' => 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?' ),
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' ),
@@ -660,7 +734,10 @@ class ApiParse extends ApiBase {
public function getExamples() {
return array(
- 'api.php?action=parse&text={{Project:Sandbox}}'
+ 'api.php?action=parse&page=Project:Sandbox' => 'Parse a page',
+ 'api.php?action=parse&text={{Project:Sandbox}}' => 'Parse wikitext',
+ 'api.php?action=parse&text={{PAGENAME}}&title=Test' => 'Parse wikitext, specifying the page title',
+ 'api.php?action=parse&summary=Some+[[link]]&prop=' => 'Parse a summary',
);
}
diff --git a/includes/api/ApiPatrol.php b/includes/api/ApiPatrol.php
index 4d4fbba9..bd2fde2b 100644
--- a/includes/api/ApiPatrol.php
+++ b/includes/api/ApiPatrol.php
@@ -35,11 +35,27 @@ class ApiPatrol extends ApiBase {
*/
public function execute() {
$params = $this->extractRequestParams();
-
- $rc = RecentChange::newFromID( $params['rcid'] );
- if ( !$rc instanceof RecentChange ) {
- $this->dieUsageMsg( array( 'nosuchrcid', $params['rcid'] ) );
+ $this->requireOnlyOneParameter( $params, 'rcid', 'revid' );
+
+ if ( isset( $params['rcid'] ) ) {
+ $rc = RecentChange::newFromID( $params['rcid'] );
+ if ( !$rc ) {
+ $this->dieUsageMsg( array( 'nosuchrcid', $params['rcid'] ) );
+ }
+ } else {
+ $rev = Revision::newFromId( $params['revid'] );
+ if ( !$rev ) {
+ $this->dieUsageMsg( array( 'nosuchrevid', $params['revid'] ) );
+ }
+ $rc = $rev->getRecentChange();
+ if ( !$rc ) {
+ $this->dieUsage(
+ 'The revision ' . $params['revid'] . " can't be patrolled as it's too old",
+ 'notpatrollable'
+ );
+ }
}
+
$retval = $rc->doMarkPatrolled( $this->getUser() );
if ( $retval ) {
@@ -66,8 +82,10 @@ class ApiPatrol extends ApiBase {
ApiBase::PARAM_REQUIRED => true
),
'rcid' => array(
- ApiBase::PARAM_TYPE => 'integer',
- ApiBase::PARAM_REQUIRED => true
+ ApiBase::PARAM_TYPE => 'integer'
+ ),
+ 'revid' => array(
+ ApiBase::PARAM_TYPE => 'integer'
),
);
}
@@ -76,6 +94,7 @@ class ApiPatrol extends ApiBase {
return array(
'token' => 'Patrol token obtained from list=recentchanges',
'rcid' => 'Recentchanges ID to patrol',
+ 'revid' => 'Revision ID to patrol',
);
}
@@ -94,8 +113,16 @@ class ApiPatrol extends ApiBase {
}
public function getPossibleErrors() {
- return array_merge( parent::getPossibleErrors(), array(
- array( 'nosuchrcid', 'rcid' ),
+ return array_merge(
+ parent::getPossibleErrors(),
+ parent::getRequireOnlyOneParameterErrorMessages( array( 'rcid', 'revid' ) ),
+ array(
+ array( 'nosuchrcid', 'rcid' ),
+ array( 'nosuchrevid', 'revid' ),
+ array(
+ 'code' => 'notpatrollable',
+ 'info' => "The revision can't be patrolled as it's too old"
+ )
) );
}
@@ -109,7 +136,8 @@ class ApiPatrol extends ApiBase {
public function getExamples() {
return array(
- 'api.php?action=patrol&token=123abc&rcid=230672766'
+ 'api.php?action=patrol&token=123abc&rcid=230672766',
+ 'api.php?action=patrol&token=123abc&revid=230672766'
);
}
diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php
index 503c6920..7830c8b4 100644
--- a/includes/api/ApiProtect.php
+++ b/includes/api/ApiProtect.php
@@ -103,8 +103,7 @@ class ApiProtect extends ApiBase {
$status = $pageObj->doUpdateRestrictions( $protections, $expiryarray, $cascade, $params['reason'], $this->getUser() );
if ( !$status->isOK() ) {
- $errors = $status->getErrorsArray();
- $this->dieUsageMsg( $errors[0] );
+ $this->dieStatus( $status );
}
$res = array(
'title' => $titleObj->getPrefixedText(),
diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php
index 134f4a0d..0812ba51 100644
--- a/includes/api/ApiPurge.php
+++ b/includes/api/ApiPurge.php
@@ -42,15 +42,15 @@ class ApiPurge extends ApiBase {
*/
private static function addValues( array &$result, $values, $flag = null, $name = null ) {
foreach ( $values as $val ) {
- if( $val instanceof Title ) {
+ if ( $val instanceof Title ) {
$v = array();
ApiQueryBase::addTitleInfo( $v, $val );
- } elseif( $name !== null ) {
+ } elseif ( $name !== null ) {
$v = array( $name => $val );
} else {
$v = $val;
}
- if( $flag !== null ) {
+ if ( $flag !== null ) {
$v[$flag] = '';
}
$result[] = $v;
@@ -64,6 +64,7 @@ class ApiPurge extends ApiBase {
$params = $this->extractRequestParams();
$forceLinkUpdate = $params['forcelinkupdate'];
+ $forceRecursiveLinkUpdate = $params['forcerecursivelinkupdate'];
$pageSet = $this->getPageSet();
$pageSet->execute();
@@ -82,8 +83,8 @@ class ApiPurge extends ApiBase {
$page->doPurge(); // Directly purge and skip the UI part of purge().
$r['purged'] = '';
- if ( $forceLinkUpdate ) {
- if ( !$this->getUser()->pingLimiter() ) {
+ if ( $forceLinkUpdate || $forceRecursiveLinkUpdate ) {
+ if ( !$this->getUser()->pingLimiter( 'linkpurge' ) ) {
global $wgEnableParserCache;
$popts = $page->makeParserOptions( 'canonical' );
@@ -93,7 +94,8 @@ class ApiPurge extends ApiBase {
$p_result = $content->getParserOutput( $title, $page->getLatest(), $popts, $wgEnableParserCache );
# Update the links tables
- $updates = $content->getSecondaryDataUpdates( $title, null, true, $p_result );
+ $updates = $content->getSecondaryDataUpdates(
+ $title, null, $forceRecursiveLinkUpdate, $p_result );
DataUpdate::runUpdates( $updates );
$r['linkupdate'] = '';
@@ -150,7 +152,10 @@ class ApiPurge extends ApiBase {
}
public function getAllowedParams( $flags = 0 ) {
- $result = array( 'forcelinkupdate' => false );
+ $result = array(
+ 'forcelinkupdate' => false,
+ 'forcerecursivelinkupdate' => false
+ );
if ( $flags ) {
$result += $this->getPageSet()->getFinalParams( $flags );
}
@@ -158,8 +163,12 @@ class ApiPurge extends ApiBase {
}
public function getParamDescription() {
- return $this->getPageSet()->getParamDescription()
- + array( 'forcelinkupdate' => 'Update the links tables' );
+ return $this->getPageSet()->getFinalParamDescription()
+ + array(
+ 'forcelinkupdate' => 'Update the links tables',
+ 'forcerecursivelinkupdate' => 'Update the links table, and update ' .
+ 'the links tables for any page that uses this page as a template',
+ );
}
public function getResultProperties() {
@@ -204,7 +213,7 @@ class ApiPurge extends ApiBase {
public function getPossibleErrors() {
return array_merge(
parent::getPossibleErrors(),
- $this->getPageSet()->getPossibleErrors()
+ $this->getPageSet()->getFinalPossibleErrors()
);
}
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index f69ad234..e03837fc 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -64,6 +64,7 @@ class ApiQuery extends ApiBase {
*/
private static $QueryListModules = array(
'allcategories' => 'ApiQueryAllCategories',
+ 'allfileusages' => 'ApiQueryAllLinks',
'allimages' => 'ApiQueryAllImages',
'alllinks' => 'ApiQueryAllLinks',
'allpages' => 'ApiQueryAllPages',
@@ -102,6 +103,7 @@ class ApiQuery extends ApiBase {
'allmessages' => 'ApiQueryAllMessages',
'siteinfo' => 'ApiQuerySiteinfo',
'userinfo' => 'ApiQueryUserInfo',
+ 'filerepoinfo' => 'ApiQueryFileRepoInfo',
);
/**
@@ -382,7 +384,6 @@ class ApiQuery extends ApiBase {
$modules = $allModules;
$tmp = $completeModules;
$wasPosted = $this->getRequest()->wasPosted();
- $main = $this->getMain();
/** @var $module ApiQueryBase */
foreach ( $allModules as $moduleName => $module ) {
@@ -513,7 +514,7 @@ class ApiQuery extends ApiBase {
ApiQueryBase::addTitleInfo( $vals, $title );
$vals['special'] = '';
if ( $title->isSpecialPage() &&
- !SpecialPageFactory::exists( $title->getDbKey() ) ) {
+ !SpecialPageFactory::exists( $title->getDBkey() ) ) {
$vals['missing'] = '';
} elseif ( $title->getNamespace() == NS_MEDIA &&
!wfFindFile( $title ) ) {
@@ -697,7 +698,7 @@ class ApiQuery extends ApiBase {
}
public function getParamDescription() {
- return $this->getPageSet()->getParamDescription() + array(
+ return $this->getPageSet()->getFinalParamDescription() + 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',
@@ -723,7 +724,7 @@ class ApiQuery extends ApiBase {
public function getPossibleErrors() {
return array_merge(
parent::getPossibleErrors(),
- $this->getPageSet()->getPossibleErrors()
+ $this->getPageSet()->getFinalPossibleErrors()
);
}
@@ -736,6 +737,7 @@ class ApiQuery extends ApiBase {
public function getHelpUrls() {
return array(
+ 'https://www.mediawiki.org/wiki/API:Query',
'https://www.mediawiki.org/wiki/API:Meta',
'https://www.mediawiki.org/wiki/API:Properties',
'https://www.mediawiki.org/wiki/API:Lists',
diff --git a/includes/api/ApiQueryAllCategories.php b/includes/api/ApiQueryAllCategories.php
index 496a0eb8..3f5c6ee7 100644
--- a/includes/api/ApiQueryAllCategories.php
+++ b/includes/api/ApiQueryAllCategories.php
@@ -76,7 +76,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
if ( $dir == 'newer' ) {
$this->addWhereRange( 'cat_pages', 'newer', $min, $max );
} else {
- $this->addWhereRange( 'cat_pages', 'older', $max, $min);
+ $this->addWhereRange( 'cat_pages', 'older', $max, $min );
}
if ( isset( $params['prefix'] ) ) {
@@ -121,7 +121,7 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
$pages[] = $titleObj;
} else {
$item = array();
- $result->setContent( $item, $titleObj->getText() );
+ ApiResult::setContent( $item, $titleObj->getText() );
if ( isset( $prop['size'] ) ) {
$item['size'] = intval( $row->cat_pages );
$item['pages'] = $row->cat_pages - $row->cat_subcats - $row->cat_files;
diff --git a/includes/api/ApiQueryAllImages.php b/includes/api/ApiQueryAllImages.php
index e24b162c..ccc7a3a2 100644
--- a/includes/api/ApiQueryAllImages.php
+++ b/includes/api/ApiQueryAllImages.php
@@ -189,7 +189,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
if ( !is_null( $params['mime'] ) ) {
global $wgMiserMode;
- if ( $wgMiserMode ) {
+ if ( $wgMiserMode ) {
$this->dieUsage( 'MIME search disabled in Miser Mode', 'mimesearchdisabled' );
}
@@ -260,7 +260,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
}
public function getAllowedParams() {
- return array (
+ return array(
'sort' => array(
ApiBase::PARAM_DFLT => 'name',
ApiBase::PARAM_TYPE => array(
diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php
index e355f8b0..47d1bcef 100644
--- a/includes/api/ApiQueryAllLinks.php
+++ b/includes/api/ApiQueryAllLinks.php
@@ -37,24 +37,41 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$prefix = 'al';
$this->table = 'pagelinks';
$this->tablePrefix = 'pl_';
+ $this->fieldTitle = 'title';
$this->dfltNamespace = NS_MAIN;
+ $this->hasNamespace = true;
$this->indexTag = 'l';
$this->description = 'Enumerate all links that point to a given namespace';
- $this->descriptionLink = 'link';
- $this->descriptionLinked = 'linked';
+ $this->descriptionWhat = 'link';
+ $this->descriptionTargets = 'linked titles';
$this->descriptionLinking = 'linking';
break;
case 'alltransclusions':
$prefix = 'at';
$this->table = 'templatelinks';
$this->tablePrefix = 'tl_';
+ $this->fieldTitle = 'title';
$this->dfltNamespace = NS_TEMPLATE;
+ $this->hasNamespace = true;
$this->indexTag = 't';
$this->description = 'List all transclusions (pages embedded using {{x}}), including non-existing';
- $this->descriptionLink = 'transclusion';
- $this->descriptionLinked = 'transcluded';
+ $this->descriptionWhat = 'transclusion';
+ $this->descriptionTargets = 'transcluded titles';
$this->descriptionLinking = 'transcluding';
break;
+ case 'allfileusages':
+ $prefix = 'af';
+ $this->table = 'imagelinks';
+ $this->tablePrefix = 'il_';
+ $this->fieldTitle = 'to';
+ $this->dfltNamespace = NS_FILE;
+ $this->hasNamespace = false;
+ $this->indexTag = 'f';
+ $this->description = 'List all file usages, including non-existing';
+ $this->descriptionWhat = 'file';
+ $this->descriptionTargets = 'file titles';
+ $this->descriptionLinking = 'using';
+ break;
default:
ApiBase::dieDebug( __METHOD__, 'Unknown module name' );
}
@@ -83,21 +100,29 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
$pfx = $this->tablePrefix;
+ $fieldTitle = $this->fieldTitle;
$prop = array_flip( $params['prop'] );
$fld_ids = isset( $prop['ids'] );
$fld_title = isset( $prop['title'] );
+ if ( $this->hasNamespace ) {
+ $namespace = $params['namespace'];
+ } else {
+ $namespace = $this->dfltNamespace;
+ }
if ( $params['unique'] ) {
if ( $fld_ids ) {
$this->dieUsage(
- "{$this->getModuleName()} cannot return corresponding page ids in unique {$this->descriptionLink}s mode",
+ "{$this->getModuleName()} cannot return corresponding page ids in unique {$this->descriptionWhat}s mode",
'params' );
}
$this->addOption( 'DISTINCT' );
}
$this->addTables( $this->table );
- $this->addWhereFld( $pfx . 'namespace', $params['namespace'] );
+ if ( $this->hasNamespace ) {
+ $this->addWhereFld( $pfx . 'namespace', $namespace );
+ }
$continue = !is_null( $params['continue'] );
if ( $continue ) {
@@ -106,14 +131,14 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
if ( $params['unique'] ) {
$this->dieContinueUsageIf( count( $continueArr ) != 1 );
$continueTitle = $db->addQuotes( $continueArr[0] );
- $this->addWhere( "{$pfx}title $op= $continueTitle" );
+ $this->addWhere( "{$pfx}{$fieldTitle} $op= $continueTitle" );
} else {
$this->dieContinueUsageIf( count( $continueArr ) != 2 );
$continueTitle = $db->addQuotes( $continueArr[0] );
$continueFrom = intval( $continueArr[1] );
$this->addWhere(
- "{$pfx}title $op $continueTitle OR " .
- "({$pfx}title = $continueTitle AND " .
+ "{$pfx}{$fieldTitle} $op $continueTitle OR " .
+ "({$pfx}{$fieldTitle} = $continueTitle AND " .
"{$pfx}from $op= $continueFrom)"
);
}
@@ -122,22 +147,24 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
// 'continue' always overrides 'from'
$from = ( $continue || is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
$to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
- $this->addWhereRange( $pfx . 'title', 'newer', $from, $to );
+ $this->addWhereRange( $pfx . $fieldTitle, 'newer', $from, $to );
if ( isset( $params['prefix'] ) ) {
- $this->addWhere( $pfx . 'title' . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) );
+ $this->addWhere( $pfx . $fieldTitle . $db->buildLike( $this->titlePartToKey( $params['prefix'] ), $db->anyString() ) );
}
- $this->addFields( array( 'pl_title' => $pfx . 'title' ) );
+ $this->addFields( array( 'pl_title' => $pfx . $fieldTitle ) );
$this->addFieldsIf( array( 'pl_from' => $pfx . 'from' ), !$params['unique'] );
- $this->addOption( 'USE INDEX', $pfx . 'namespace' );
+ if ( $this->hasNamespace ) {
+ $this->addOption( 'USE INDEX', $pfx . 'namespace' );
+ }
$limit = $params['limit'];
$this->addOption( 'LIMIT', $limit + 1 );
$sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
$orderBy = array();
- $orderBy[] = $pfx . 'title' . $sort;
+ $orderBy[] = $pfx . $fieldTitle . $sort;
if ( !$params['unique'] ) {
$orderBy[] = $pfx . 'from' . $sort;
}
@@ -166,7 +193,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
$vals['fromid'] = intval( $row->pl_from );
}
if ( $fld_title ) {
- $title = Title::makeTitle( $params['namespace'], $row->pl_title );
+ $title = Title::makeTitle( $namespace, $row->pl_title );
ApiQueryBase::addTitleInfo( $vals, $title );
}
$fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
@@ -179,7 +206,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
break;
}
} elseif ( $params['unique'] ) {
- $titles[] = Title::makeTitle( $params['namespace'], $row->pl_title );
+ $titles[] = Title::makeTitle( $namespace, $row->pl_title );
} else {
$pageids[] = $row->pl_from;
}
@@ -195,7 +222,7 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
}
public function getAllowedParams() {
- return array(
+ $allowedParams = array(
'continue' => null,
'from' => null,
'to' => null,
@@ -228,30 +255,39 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
)
),
);
+ if ( !$this->hasNamespace ) {
+ unset( $allowedParams['namespace'] );
+ }
+ return $allowedParams;
}
public function getParamDescription() {
$p = $this->getModulePrefix();
- $link = $this->descriptionLink;
+ $what = $this->descriptionWhat;
+ $targets = $this->descriptionTargets;
$linking = $this->descriptionLinking;
- return array(
- 'from' => "The title of the $link to start enumerating from",
- 'to' => "The title of the $link to stop enumerating at",
- 'prefix' => "Search for all $link titles that begin with this value",
+ $paramDescription = array(
+ 'from' => "The title of the $what to start enumerating from",
+ 'to' => "The title of the $what to stop enumerating at",
+ 'prefix' => "Search for all $targets that begin with this value",
'unique' => array(
- "Only show distinct $link titles. Cannot be used with {$p}prop=ids.",
- 'When used as a generator, yields target pages instead of source pages.',
+ "Only show distinct $targets. Cannot be used with {$p}prop=ids.",
+ 'When used as a generator, yields target pages instead of source pages.',
),
'prop' => array(
'What pieces of information to include',
" ids - Adds the pageid of the $linking page (Cannot be used with {$p}unique)",
- " title - Adds the title of the $link",
+ " title - Adds the title of the $what",
),
'namespace' => 'The namespace to enumerate',
- 'limit' => "How many total items to return",
+ 'limit' => 'How many total items to return',
'continue' => 'When more results are available, use this to continue',
'dir' => 'The direction in which to list',
);
+ if ( !$this->hasNamespace ) {
+ unset( $paramDescription['namespace'] );
+ }
+ return $paramDescription;
}
public function getResultProperties() {
@@ -272,29 +308,31 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
public function getPossibleErrors() {
$m = $this->getModuleName();
- $link = $this->descriptionLink;
+ $what = $this->descriptionWhat;
return array_merge( parent::getPossibleErrors(), array(
- array( 'code' => 'params', 'info' => "{$m} cannot return corresponding page ids in unique {$link}s mode" ),
+ array( 'code' => 'params', 'info' => "{$m} cannot return corresponding page ids in unique {$what}s mode" ),
) );
}
public function getExamples() {
$p = $this->getModulePrefix();
- $link = $this->descriptionLink;
- $linked = $this->descriptionLinked;
+ $name = $this->getModuleName();
+ $what = $this->descriptionWhat;
+ $targets = $this->descriptionTargets;
return array(
- "api.php?action=query&list=all{$link}s&{$p}from=B&{$p}prop=ids|title"
- => "List $linked titles with page ids they are from, including missing ones. Start at B",
- "api.php?action=query&list=all{$link}s&{$p}unique=&{$p}from=B"
- => "List unique $linked titles",
- "api.php?action=query&generator=all{$link}s&g{$p}unique=&g{$p}from=B"
- => "Gets all $link targets, marking the missing ones",
- "api.php?action=query&generator=all{$link}s&g{$p}from=B"
- => "Gets pages containing the {$link}s",
+ "api.php?action=query&list={$name}&{$p}from=B&{$p}prop=ids|title"
+ => "List $targets with page ids they are from, including missing ones. Start at B",
+ "api.php?action=query&list={$name}&{$p}unique=&{$p}from=B"
+ => "List unique $targets",
+ "api.php?action=query&generator={$name}&g{$p}unique=&g{$p}from=B"
+ => "Gets all $targets, marking the missing ones",
+ "api.php?action=query&generator={$name}&g{$p}from=B"
+ => "Gets pages containing the {$what}s",
);
}
public function getHelpUrls() {
- return "https://www.mediawiki.org/wiki/API:All{$this->descriptionLink}s";
+ $name = ucfirst( $this->getModuleName() );
+ return "https://www.mediawiki.org/wiki/API:{$name}";
}
}
diff --git a/includes/api/ApiQueryAllMessages.php b/includes/api/ApiQueryAllMessages.php
index c9811b0d..d47c7b76 100644
--- a/includes/api/ApiQueryAllMessages.php
+++ b/includes/api/ApiQueryAllMessages.php
@@ -87,7 +87,7 @@ class ApiQueryAllMessages extends ApiQueryBase {
foreach ( $messages_target as $message ) {
// === 0: must be at beginning of string (position 0)
if ( strpos( $message, $params['prefix'] ) === 0 ) {
- if( !$skip ) {
+ if ( !$skip ) {
$skip = true;
}
$messages_filtered[] = $message;
diff --git a/includes/api/ApiQueryAllPages.php b/includes/api/ApiQueryAllPages.php
index d718b967..d95980c2 100644
--- a/includes/api/ApiQueryAllPages.php
+++ b/includes/api/ApiQueryAllPages.php
@@ -174,7 +174,7 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
$res = $this->select( __METHOD__ );
//Get gender information
- if( MWNamespace::hasGenderDistinction( $params['namespace'] ) ) {
+ if ( MWNamespace::hasGenderDistinction( $params['namespace'] ) ) {
$users = array();
foreach ( $res as $row ) {
$users[] = $row->page_title;
@@ -304,7 +304,10 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
'prtype' => 'Limit to protected pages only',
'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',
+ 'filterlanglinks' => array(
+ 'Filter based on whether a page has langlinks',
+ 'Note that this may not consider langlinks added by extensions.',
+ ),
'limit' => 'How many total pages to return.',
'prexpiry' => array(
'Which protection expiry to filter the page on',
diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php
index 7283aa00..1948a51a 100644
--- a/includes/api/ApiQueryAllUsers.php
+++ b/includes/api/ApiQueryAllUsers.php
@@ -83,12 +83,12 @@ class ApiQueryAllUsers extends ApiQueryBase {
if ( !is_null( $params['rights'] ) && count( $params['rights'] ) ) {
$groups = array();
- foreach( $params['rights'] as $r ) {
+ foreach ( $params['rights'] as $r ) {
$groups = array_merge( $groups, User::getGroupsWithPermission( $r ) );
}
// no group with the given right(s) exists, no need for a query
- if( !count( $groups ) ) {
+ if ( !count( $groups ) ) {
$this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), '' );
return;
}
diff --git a/includes/api/ApiQueryBacklinks.php b/includes/api/ApiQueryBacklinks.php
index 3ef6b840..2d1089a7 100644
--- a/includes/api/ApiQueryBacklinks.php
+++ b/includes/api/ApiQueryBacklinks.php
@@ -229,10 +229,10 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
$orderBy = array();
$sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
// Don't order by namespace/title if it's constant in the WHERE clause
- if( $this->hasNS && count( array_unique( $allRedirNs ) ) != 1 ) {
+ if ( $this->hasNS && count( array_unique( $allRedirNs ) ) != 1 ) {
$orderBy[] = $this->bl_ns . $sort;
}
- if( count( array_unique( $allRedirDBkey ) ) != 1 ) {
+ if ( count( array_unique( $allRedirDBkey ) ) != 1 ) {
$orderBy[] = $this->bl_title . $sort;
}
$orderBy[] = $this->bl_from . $sort;
@@ -255,6 +255,9 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if ( $this->params['limit'] == 'max' ) {
$this->params['limit'] = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
$result->setParsedLimit( $this->getModuleName(), $this->params['limit'] );
+ } else {
+ $this->params['limit'] = intval( $this->params['limit'] );
+ $this->validateLimit( 'limit', $this->params['limit'], 1, $userMax, $botMax );
}
$this->processContinue();
@@ -294,9 +297,9 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
// 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 } ];
+ $parentID = $this->pageMap[$row->{$this->bl_ns}][$row->{$this->bl_title}];
} else {
- $parentID = $this->pageMap[NS_FILE][$row-> { $this->bl_title } ];
+ $parentID = $this->pageMap[NS_FILE][$row->{$this->bl_title}];
}
$this->continueStr = $this->getContinueRedirStr( $parentID, $row->page_id );
break;
@@ -377,8 +380,8 @@ class ApiQueryBacklinks extends ApiQueryGeneratorBase {
if ( $row->page_is_redirect ) {
$a['redirect'] = '';
}
- $ns = $this->hasNS ? $row-> { $this->bl_ns } : NS_FILE;
- $parentID = $this->pageMap[$ns][$row-> { $this->bl_title } ];
+ $ns = $this->hasNS ? $row->{$this->bl_ns} : NS_FILE;
+ $parentID = $this->pageMap[$ns][$row->{$this->bl_title}];
// Put all the results in an array first
$this->resultArr[$parentID]['redirlinks'][] = $a;
$this->getResult()->setIndexedTagName( $this->resultArr[$parentID]['redirlinks'], $this->bl_code );
diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php
index 7819ead4..8668e04b 100644
--- a/includes/api/ApiQueryBase.php
+++ b/includes/api/ApiQueryBase.php
@@ -432,7 +432,7 @@ abstract class ApiQueryBase extends ApiBase {
if ( trim( $key ) == '' ) {
return '';
}
- $t = Title::newFromDbKey( $key );
+ $t = Title::newFromDBkey( $key );
// This really shouldn't happen but we gotta check anyway
if ( !$t ) {
$this->dieUsageMsg( array( 'invalidtitle', $key ) );
@@ -478,7 +478,7 @@ abstract class ApiQueryBase extends ApiBase {
* @param $protocol String
* @return null|string
*/
- public function prepareUrlQuerySearchString( $query = null, $protocol = null) {
+ public function prepareUrlQuerySearchString( $query = null, $protocol = null ) {
$db = $this->getDb();
if ( !is_null( $query ) || $query != '' ) {
if ( is_null( $protocol ) ) {
diff --git a/includes/api/ApiQueryBlocks.php b/includes/api/ApiQueryBlocks.php
index d9be9f28..e3c27f5e 100644
--- a/includes/api/ApiQueryBlocks.php
+++ b/includes/api/ApiQueryBlocks.php
@@ -63,7 +63,7 @@ class ApiQueryBlocks extends ApiQueryBase {
$this->addTables( 'ipblocks' );
$this->addFields( 'ipb_auto' );
- $this->addFieldsIf ( 'ipb_id', $fld_id );
+ $this->addFieldsIf( 'ipb_id', $fld_id );
$this->addFieldsIf( array( 'ipb_address', 'ipb_user' ), $fld_user || $fld_userid );
$this->addFieldsIf( 'ipb_by_text', $fld_by );
$this->addFieldsIf( 'ipb_by', $fld_byid );
@@ -91,17 +91,30 @@ class ApiQueryBlocks extends ApiQueryBase {
$this->addWhereFld( 'ipb_auto', 0 );
}
if ( isset( $params['ip'] ) ) {
- list( $ip, $range ) = IP::parseCIDR( $params['ip'] );
- if ( $ip && $range ) {
- // We got a CIDR range
- if ( $range < 16 )
- $this->dieUsage( 'CIDR ranges broader than /16 are not accepted', 'cidrtoobroad' );
- $lower = wfBaseConvert( $ip, 10, 16, 8, false );
- $upper = wfBaseConvert( $ip + pow( 2, 32 - $range ) - 1, 10, 16, 8, false );
+ global $wgBlockCIDRLimit;
+ if ( IP::isIPv4( $params['ip'] ) ) {
+ $type = 'IPv4';
+ $cidrLimit = $wgBlockCIDRLimit['IPv4'];
+ $prefixLen = 0;
+ } elseif ( IP::isIPv6( $params['ip'] ) ) {
+ $type = 'IPv6';
+ $cidrLimit = $wgBlockCIDRLimit['IPv6'];
+ $prefixLen = 3; // IP::toHex output is prefixed with "v6-"
} else {
- $lower = $upper = IP::toHex( $params['ip'] );
+ $this->dieUsage( 'IP parameter is not valid', 'param_ip' );
}
- $prefix = substr( $lower, 0, 4 );
+
+ # Check range validity, if it's a CIDR
+ list( $ip, $range ) = IP::parseCIDR( $params['ip'] );
+ if ( $ip !== false && $range !== false && $range < $cidrLimit ) {
+ $this->dieUsage( "$type CIDR ranges broader than /$cidrLimit are not accepted", 'cidrtoobroad' );
+ }
+
+ # Let IP::parseRange handle calculating $upper, instead of duplicating the logic here.
+ list( $lower, $upper ) = IP::parseRange( $params['ip'] );
+
+ # Extract the common prefix to any rangeblock affecting this IP/CIDR
+ $prefix = substr( $lower, 0, $prefixLen + floor( $cidrLimit / 4 ) );
# Fairly hard to make a malicious SQL statement out of hex characters,
# but it is good practice to add quotes
@@ -120,10 +133,10 @@ class ApiQueryBlocks extends ApiQueryBase {
$show = array_flip( $params['show'] );
/* Check for conflicting parameters. */
- if ( ( isset ( $show['account'] ) && isset ( $show['!account'] ) )
- || ( isset ( $show['ip'] ) && isset ( $show['!ip'] ) )
- || ( isset ( $show['range'] ) && isset ( $show['!range'] ) )
- || ( isset ( $show['temp'] ) && isset ( $show['!temp'] ) )
+ if ( ( isset( $show['account'] ) && isset( $show['!account'] ) )
+ || ( isset( $show['ip'] ) && isset( $show['!ip'] ) )
+ || ( isset( $show['range'] ) && isset( $show['!range'] ) )
+ || ( isset( $show['temp'] ) && isset( $show['!temp'] ) )
) {
$this->dieUsageMsg( 'show' );
}
@@ -294,6 +307,7 @@ class ApiQueryBlocks extends ApiQueryBase {
}
public function getParamDescription() {
+ global $wgBlockCIDRLimit;
$p = $this->getModulePrefix();
return array(
'start' => 'The timestamp to start enumerating from',
@@ -301,8 +315,12 @@ class ApiQueryBlocks extends ApiQueryBase {
'dir' => $this->getDirectionDescription( $p ),
'ids' => 'List of block IDs to list (optional)',
'users' => 'List of users to search for (optional)',
- 'ip' => array( 'Get all blocks applying to this IP or CIDR range, including range blocks.',
- 'Cannot be used together with bkusers. CIDR ranges broader than /16 are not accepted' ),
+ '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 " .
+ "IPv4/{$wgBlockCIDRLimit['IPv4']} or IPv6/{$wgBlockCIDRLimit['IPv6']} " .
+ "are not accepted"
+ ),
'limit' => 'The maximum amount of blocks to list',
'prop' => array(
'Which properties to get',
@@ -383,10 +401,19 @@ class ApiQueryBlocks extends ApiQueryBase {
}
public function getPossibleErrors() {
+ global $wgBlockCIDRLimit;
return array_merge( parent::getPossibleErrors(),
$this->getRequireOnlyOneParameterErrorMessages( array( 'users', 'ip' ) ),
array(
- array( 'code' => 'cidrtoobroad', 'info' => 'CIDR ranges broader than /16 are not accepted' ),
+ array(
+ 'code' => 'cidrtoobroad',
+ 'info' => "IPv4 CIDR ranges broader than /{$wgBlockCIDRLimit['IPv4']} are not accepted"
+ ),
+ array(
+ 'code' => 'cidrtoobroad',
+ 'info' => "IPv6 CIDR ranges broader than /{$wgBlockCIDRLimit['IPv6']} are not accepted"
+ ),
+ array( 'code' => 'param_ip', 'info' => 'IP parameter is not valid' ),
array( 'code' => 'param_user', 'info' => 'User parameter may not be empty' ),
array( 'code' => 'param_user', 'info' => 'User name user is not valid' ),
array( 'show' ),
diff --git a/includes/api/ApiQueryCategories.php b/includes/api/ApiQueryCategories.php
index 69a64415..5d714f57 100644
--- a/includes/api/ApiQueryCategories.php
+++ b/includes/api/ApiQueryCategories.php
@@ -49,7 +49,6 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
/**
* @param $resultPageSet ApiPageSet
- * @return
*/
private function run( $resultPageSet = null ) {
if ( $this->getPageSet()->getGoodTitleCount() == 0 ) {
@@ -174,7 +173,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
break;
}
- $titles[] = Title :: makeTitle( NS_CATEGORY, $row->cl_to );
+ $titles[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
}
$resultPageSet->populateFromTitles( $titles );
}
@@ -184,7 +183,7 @@ class ApiQueryCategories extends ApiQueryGeneratorBase {
return array(
'prop' => array(
ApiBase::PARAM_ISMULTI => true,
- ApiBase::PARAM_TYPE => array (
+ ApiBase::PARAM_TYPE => array(
'sortkey',
'timestamp',
'hidden',
diff --git a/includes/api/ApiQueryCategoryMembers.php b/includes/api/ApiQueryCategoryMembers.php
index 9dbd8593..704d108a 100644
--- a/includes/api/ApiQueryCategoryMembers.php
+++ b/includes/api/ApiQueryCategoryMembers.php
@@ -217,7 +217,7 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
if ( $fld_sortkeyprefix ) {
$vals['sortkeyprefix'] = $row->cl_sortkey_prefix;
}
- if ( $fld_type ) {
+ if ( $fld_type ) {
$vals['type'] = $row->cl_type;
}
if ( $fld_timestamp ) {
@@ -258,7 +258,7 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
'prop' => array(
ApiBase::PARAM_DFLT => 'ids|title',
ApiBase::PARAM_ISMULTI => true,
- ApiBase::PARAM_TYPE => array (
+ ApiBase::PARAM_TYPE => array(
'ids',
'title',
'sortkey',
@@ -267,7 +267,7 @@ class ApiQueryCategoryMembers extends ApiQueryGeneratorBase {
'timestamp',
)
),
- 'namespace' => array (
+ 'namespace' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => 'namespace',
),
diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php
index 890e4ecf..82733133 100644
--- a/includes/api/ApiQueryDeletedrevs.php
+++ b/includes/api/ApiQueryDeletedrevs.php
@@ -50,7 +50,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$fld_user = isset( $prop['user'] );
$fld_userid = isset( $prop['userid'] );
$fld_comment = isset( $prop['comment'] );
- $fld_parsedcomment = isset ( $prop['parsedcomment'] );
+ $fld_parsedcomment = isset( $prop['parsedcomment'] );
$fld_minor = isset( $prop['minor'] );
$fld_len = isset( $prop['len'] );
$fld_sha1 = isset( $prop['sha1'] );
@@ -79,13 +79,13 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
if ( $mode == 'revs' || $mode == 'user' ) {
// Ignore namespace and unique due to inability to know whether they were purposely set
- foreach( array( 'from', 'to', 'prefix', /*'namespace', 'unique'*/ ) as $p ) {
+ foreach ( array( 'from', 'to', 'prefix', /*'namespace', 'unique'*/ ) as $p ) {
if ( !is_null( $params[$p] ) ) {
$this->dieUsage( "The '{$p}' parameter cannot be used in modes 1 or 2", 'badparams' );
}
}
} else {
- foreach( array( 'start', 'end' ) as $p ) {
+ foreach ( array( 'start', 'end' ) as $p ) {
if ( !is_null( $params[$p] ) ) {
$this->dieUsage( "The {$p} parameter cannot be used in mode 3", 'badparams' );
}
diff --git a/includes/api/ApiQueryDuplicateFiles.php b/includes/api/ApiQueryDuplicateFiles.php
index 18dcba85..0311fa7f 100644
--- a/includes/api/ApiQueryDuplicateFiles.php
+++ b/includes/api/ApiQueryDuplicateFiles.php
@@ -48,8 +48,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
}
/**
- * @param $resultPageSet ApiPageSet
- * @return
+ * @param ApiPageSet $resultPageSet
*/
private function run( $resultPageSet = null ) {
$params = $this->extractRequestParams();
@@ -59,7 +58,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
}
$images = $namespaces[NS_FILE];
- if( $params['dir'] == 'descending' ) {
+ if ( $params['dir'] == 'descending' ) {
$images = array_reverse( $images );
}
@@ -80,7 +79,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
}
$filesToFind = array_keys( $images );
- if( $params['localonly'] ) {
+ if ( $params['localonly'] ) {
$files = RepoGroup::singleton()->getLocalRepo()->findFiles( $filesToFind );
} else {
$files = RepoGroup::singleton()->findFiles( $filesToFind );
@@ -98,29 +97,29 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
// find all files with the hashes, result format is: array( hash => array( dup1, dup2 ), hash1 => ... )
$filesToFindBySha1s = array_unique( array_values( $sha1s ) );
- if( $params['localonly'] ) {
+ if ( $params['localonly'] ) {
$filesBySha1s = RepoGroup::singleton()->getLocalRepo()->findBySha1s( $filesToFindBySha1s );
} else {
$filesBySha1s = RepoGroup::singleton()->findBySha1s( $filesToFindBySha1s );
}
// iterate over $images to handle continue param correct
- foreach( $images as $image => $pageId ) {
- if( !isset( $sha1s[$image] ) ) {
+ foreach ( $images as $image => $pageId ) {
+ if ( !isset( $sha1s[$image] ) ) {
continue; //file does not exist
}
$sha1 = $sha1s[$image];
$dupFiles = $filesBySha1s[$sha1];
- if( $params['dir'] == 'descending' ) {
+ if ( $params['dir'] == 'descending' ) {
$dupFiles = array_reverse( $dupFiles );
}
/** @var $dupFile File */
foreach ( $dupFiles as $dupFile ) {
$dupName = $dupFile->getName();
- if( $image == $dupName && $dupFile->isLocal() ) {
+ if ( $image == $dupName && $dupFile->isLocal() ) {
continue; //ignore the local file itself
}
- if( $skipUntilThisDup !== false && $dupName < $skipUntilThisDup ) {
+ if ( $skipUntilThisDup !== false && $dupName < $skipUntilThisDup ) {
continue; //skip to pos after the image from continue param
}
$skipUntilThisDup = false;
@@ -139,7 +138,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
'user' => $dupFile->getUser( 'text' ),
'timestamp' => wfTimestamp( TS_ISO_8601, $dupFile->getTimestamp() )
);
- if( !$dupFile->isLocal() ) {
+ if ( !$dupFile->isLocal() ) {
$r['shared'] = '';
}
$fit = $this->addPageSubItem( $pageId, $r );
@@ -149,7 +148,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
}
}
}
- if( !$fit ) {
+ if ( !$fit ) {
break;
}
}
diff --git a/includes/api/ApiQueryExtLinksUsage.php b/includes/api/ApiQueryExtLinksUsage.php
index eb9cdf9e..456e87ba 100644
--- a/includes/api/ApiQueryExtLinksUsage.php
+++ b/includes/api/ApiQueryExtLinksUsage.php
@@ -123,7 +123,7 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
if ( $fld_url ) {
$to = $row->el_to;
// expand protocol-relative urls
- if( $params['expandurl'] ) {
+ if ( $params['expandurl'] ) {
$to = wfExpandUrl( $to, PROTO_CANONICAL );
}
$vals['url'] = $to;
@@ -218,13 +218,13 @@ class ApiQueryExtLinksUsage extends ApiQueryGeneratorBase {
),
'offset' => 'Used for paging. Use the value returned for "continue"',
'protocol' => array(
- "Protocol of the url. If empty and {$p}query set, the protocol is http.",
+ "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.',
- 'expandurl' => 'Expand protocol-relative urls with the canonical protocol',
+ 'expandurl' => 'Expand protocol-relative URLs with the canonical protocol',
);
if ( $wgMiserMode ) {
diff --git a/includes/api/ApiQueryExternalLinks.php b/includes/api/ApiQueryExternalLinks.php
index 761b49ea..583ef697 100644
--- a/includes/api/ApiQueryExternalLinks.php
+++ b/includes/api/ApiQueryExternalLinks.php
@@ -88,7 +88,7 @@ class ApiQueryExternalLinks extends ApiQueryBase {
$entry = array();
$to = $row->el_to;
// expand protocol-relative urls
- if( $params['expandurl'] ) {
+ if ( $params['expandurl'] ) {
$to = wfExpandUrl( $to, PROTO_CANONICAL );
}
ApiResult::setContent( $entry, $to );
@@ -131,11 +131,11 @@ class ApiQueryExternalLinks extends ApiQueryBase {
'limit' => 'How many links to return',
'offset' => 'When more results are available, use this to continue',
'protocol' => array(
- "Protocol of the url. If empty and {$p}query set, the protocol is http.",
+ "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. Useful for checking whether a certain page contains a certain external url',
- 'expandurl' => 'Expand protocol-relative urls with the canonical protocol',
+ 'expandurl' => 'Expand protocol-relative URLs with the canonical protocol',
);
}
@@ -148,7 +148,7 @@ class ApiQueryExternalLinks extends ApiQueryBase {
}
public function getDescription() {
- return 'Returns all external urls (not interwikis) from the given page(s)';
+ return 'Returns all external URLs (not interwikis) from the given page(s)';
}
public function getPossibleErrors() {
diff --git a/includes/api/ApiQueryFileRepoInfo.php b/includes/api/ApiQueryFileRepoInfo.php
new file mode 100644
index 00000000..3a353533
--- /dev/null
+++ b/includes/api/ApiQueryFileRepoInfo.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Copyright © 2013 Mark Holmquist <mtraceur@member.fsf.org>
+ *
+ * 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
+ * @since 1.22
+ */
+
+/**
+ * A query action to return meta information about the foreign file repos
+ * configured on the wiki.
+ *
+ * @ingroup API
+ */
+class ApiQueryFileRepoInfo extends ApiQueryBase {
+
+ public function __construct( $query, $moduleName ) {
+ parent::__construct( $query, $moduleName, 'fri' );
+ }
+
+ protected function getInitialisedRepoGroup() {
+ $repoGroup = RepoGroup::singleton();
+
+ if ( !$repoGroup->reposInitialised ) {
+ $repoGroup->initialiseRepos();
+ }
+
+ return $repoGroup;
+ }
+
+ public function execute() {
+ $params = $this->extractRequestParams();
+ $props = array_flip( $params['prop'] );
+
+ $repos = array();
+
+ $repoGroup = $this->getInitialisedRepoGroup();
+
+ $repoGroup->forEachForeignRepo( function ( $repo ) use ( &$repos, $props ) {
+ $repos[] = array_intersect_key( $repo->getInfo(), $props );
+ } );
+
+ $repos[] = array_intersect_key( $repoGroup->localRepo->getInfo(), $props );
+
+ $result = $this->getResult();
+ $result->setIndexedTagName( $repos, 'repo' );
+ $result->addValue( array( 'query' ), 'repos', $repos );
+ }
+
+ public function getCacheMode( $params ) {
+ return 'public';
+ }
+
+ public function getAllowedParams() {
+ $props = $this->getProps();
+
+ return array(
+ 'prop' => array(
+ ApiBase::PARAM_DFLT => join( '|', $props ),
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_TYPE => $props,
+ ),
+ );
+ }
+
+ public function getProps() {
+ $props = array();
+ $repoGroup = $this->getInitialisedRepoGroup();
+
+ $repoGroup->forEachForeignRepo( function ( $repo ) use ( &$props ) {
+ $props = array_merge( $props, array_keys( $repo->getInfo() ) );
+ } );
+
+ return array_values( array_unique( array_merge( $props, array_keys( $repoGroup->localRepo->getInfo() ) ) ) );
+ }
+
+ public function getParamDescription() {
+ $p = $this->getModulePrefix();
+ return array(
+ 'prop' => array(
+ 'Which repository properties to get (there may be more available on some wikis):',
+ ' apiurl - URL to the repository API - helpful for getting image info from the host.',
+ ' name - The key of the repository - used in e.g. $wgForeignFileRepos and imageinfo return values.',
+ ' displayname - The human-readable name of the repository wiki.',
+ ' rooturl - Root URL for image paths.',
+ ' local - Whether that repository is the local one or not.',
+ ),
+ );
+ }
+
+ public function getDescription() {
+ return 'Return meta information about image repositories configured on the wiki.';
+ }
+
+ public function getExamples() {
+ return array(
+ 'api.php?action=query&meta=filerepoinfo&friprop=apiurl|name|displayname',
+ );
+ }
+}
diff --git a/includes/api/ApiQueryFilearchive.php b/includes/api/ApiQueryFilearchive.php
index 021074a9..f53cd386 100644
--- a/includes/api/ApiQueryFilearchive.php
+++ b/includes/api/ApiQueryFilearchive.php
@@ -218,7 +218,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
}
public function getAllowedParams() {
- return array (
+ return array(
'from' => null,
'continue' => null,
'to' => null,
@@ -281,7 +281,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
' parseddescription - Parse the description on the version',
' mime - Adds MIME of the image',
' mediatype - Adds the media type of the image',
- ' metadata - Lists EXIF metadata for the version of the image',
+ ' metadata - Lists Exif metadata for the version of the image',
' bitdepth - Adds the bit depth of the version',
' archivename - Adds the file name of the archive version for non-latest versions'
),
@@ -373,4 +373,8 @@ class ApiQueryFilearchive extends ApiQueryBase {
),
);
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Filearchive';
+ }
}
diff --git a/includes/api/ApiQueryIWBacklinks.php b/includes/api/ApiQueryIWBacklinks.php
index b47d31f2..ebae3e76 100644
--- a/includes/api/ApiQueryIWBacklinks.php
+++ b/includes/api/ApiQueryIWBacklinks.php
@@ -239,4 +239,8 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
'api.php?action=query&generator=iwbacklinks&giwbltitle=Test&giwblprefix=wikibooks&prop=info'
);
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Iwbacklinks';
+ }
}
diff --git a/includes/api/ApiQueryIWLinks.php b/includes/api/ApiQueryIWLinks.php
index fc77b4e6..be539311 100644
--- a/includes/api/ApiQueryIWLinks.php
+++ b/includes/api/ApiQueryIWLinks.php
@@ -81,8 +81,8 @@ class ApiQueryIWLinks extends ApiQueryBase {
$this->addOption( 'ORDER BY', 'iwl_from' . $sort );
} else {
$this->addOption( 'ORDER BY', array(
- 'iwl_title' . $sort,
- 'iwl_from' . $sort
+ 'iwl_from' . $sort,
+ 'iwl_title' . $sort
));
}
} else {
@@ -90,9 +90,10 @@ class ApiQueryIWLinks extends ApiQueryBase {
if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) {
$this->addOption( 'ORDER BY', 'iwl_prefix' . $sort );
} else {
- $this->addOption( 'ORDER BY', array (
+ $this->addOption( 'ORDER BY', array(
'iwl_from' . $sort,
- 'iwl_prefix' . $sort
+ 'iwl_prefix' . $sort,
+ 'iwl_title' . $sort
));
}
}
@@ -192,4 +193,8 @@ class ApiQueryIWLinks extends ApiQueryBase {
'api.php?action=query&prop=iwlinks&titles=Main%20Page' => 'Get interwiki links from the [[Main Page]]',
);
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Iwlinks';
+ }
}
diff --git a/includes/api/ApiQueryImageInfo.php b/includes/api/ApiQueryImageInfo.php
index 95c2745a..0ea28684 100644
--- a/includes/api/ApiQueryImageInfo.php
+++ b/includes/api/ApiQueryImageInfo.php
@@ -72,7 +72,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
$result = $this->getResult();
//search only inside the local repo
- if( $params['localonly'] ) {
+ if ( $params['localonly'] ) {
$images = RepoGroup::singleton()->getLocalRepo()->findFiles( $titles );
} else {
$images = RepoGroup::singleton()->findFiles( $titles );
@@ -82,12 +82,17 @@ class ApiQueryImageInfo extends ApiQueryBase {
$start = $title === $fromTitle ? $fromTimestamp : $params['start'];
if ( !isset( $images[$title] ) ) {
- $result->addValue(
- array( 'query', 'pages', intval( $pageId ) ),
- 'imagerepository', ''
- );
- // The above can't fail because it doesn't increase the result size
- continue;
+ if ( isset( $prop['uploadwarning'] ) ) {
+ // Uploadwarning needs info about non-existing files
+ $images[$title] = wfLocalFile( $title );
+ } else {
+ $result->addValue(
+ array( 'query', 'pages', intval( $pageId ) ),
+ 'imagerepository', ''
+ );
+ // The above can't fail because it doesn't increase the result size
+ continue;
+ }
}
/** @var $img File */
@@ -170,9 +175,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
break;
}
- $fit = $this->addPageSubItem( $pageId,
- self::getInfo( $oldie, $prop, $result,
- $finalThumbParams, $params['metadataversion'] ) );
+ $fit = self::getTransformCount() < self::TRANSFORM_LIMIT &&
+ $this->addPageSubItem( $pageId,
+ self::getInfo( $oldie, $prop, $result,
+ $finalThumbParams, $params['metadataversion']
+ )
+ );
if ( !$fit ) {
if ( count( $pageIds[NS_FILE] ) == 1 ) {
$this->setContinueEnumParameter( 'start',
@@ -199,21 +207,20 @@ class ApiQueryImageInfo extends ApiQueryBase {
public function getScale( $params ) {
$p = $this->getModulePrefix();
- // Height and width.
- 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'];
+ } elseif ( $params['urlheight'] != -1 ) {
+ // Height is specified but width isn't
+ // Don't set $scale['width']; this signals mergeThumbParams() to fill it with the image's width
+ $scale = array();
+ $scale['height'] = $params['urlheight'];
} else {
$scale = null;
if ( $params['urlparam'] ) {
$this->dieUsage( "{$p}urlparam requires {$p}urlwidth", "urlparam_no_width" );
}
- return $scale;
}
return $scale;
@@ -228,7 +235,21 @@ class ApiQueryImageInfo extends ApiQueryBase {
* @param string $otherParams of otherParams (iiurlparam).
* @return Array of parameters for transform.
*/
- protected function mergeThumbParams ( $image, $thumbParams, $otherParams ) {
+ protected function mergeThumbParams( $image, $thumbParams, $otherParams ) {
+ global $wgThumbLimits;
+
+ if ( !isset( $thumbParams['width'] ) && isset( $thumbParams['height'] ) ) {
+ // We want to limit only by height in this situation, so pass the
+ // image's full width as the limiting width. But some file types
+ // don't have a width of their own, so pick something arbitrary so
+ // thumbnailing the default icon works.
+ if ( $image->getWidth() <= 0 ) {
+ $thumbParams['width'] = max( $wgThumbLimits );
+ } else {
+ $thumbParams['width'] = $image->getWidth();
+ }
+ }
+
if ( !$otherParams ) {
return $thumbParams;
}
@@ -254,8 +275,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
if ( isset( $paramList['width'] ) ) {
if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) {
- $this->dieUsage( "{$p}urlparam had width of {$paramList['width']} but "
- . "{$p}urlwidth was {$thumbParams['width']}", "urlparam_urlwidth_mismatch" );
+ $this->setWarning( "Ignoring width value set in {$p}urlparam ({$paramList['width']}) "
+ . "in favor of width value derived from {$p}urlwidth/{$p}urlheight ({$thumbParams['width']})" );
}
}
@@ -342,6 +363,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
$mediatype = isset( $prop['mediatype'] );
$archive = isset( $prop['archivename'] );
$bitdepth = isset( $prop['bitdepth'] );
+ $uploadwarning = isset( $prop['uploadwarning'] );
if ( ( $url || $sha1 || $meta || $mime || $mediatype || $archive || $bitdepth )
&& $file->isDeleted( File::DELETED_FILE ) ) {
@@ -411,6 +433,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
$vals['bitdepth'] = $file->getBitDepth();
}
+ if ( $uploadwarning ) {
+ $vals['html'] = SpecialUpload::getExistsWarning( UploadBase::getExistsWarning( $file ) );
+ }
+
return $vals;
}
@@ -461,7 +487,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
if ( $start === null ) {
$start = $img->getTimestamp();
}
- return $img->getOriginalTitle()->getText() . '|' . $start;
+ return $img->getOriginalTitle()->getDBkey() . '|' . $start;
}
public function getAllowedParams() {
@@ -537,9 +563,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail' .
' (requires url and param ' . $modulePrefix . 'urlwidth)',
'mediatype' => ' mediatype - Adds the media type of the image',
- 'metadata' => ' metadata - Lists EXIF metadata for the version of the image',
+ 'metadata' => ' metadata - Lists Exif metadata for the version of the image',
'archivename' => ' archivename - Adds the file name of the archive version for non-latest versions',
'bitdepth' => ' bitdepth - Adds the bit depth of the version',
+ 'uploadwarning' => ' uploadwarning - Used by the Special:Upload page to get information about an existing file. Not intended for use outside MediaWiki core',
);
}
@@ -547,7 +574,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
* Returns the descriptions for the properties provided by getPropertyNames()
*
* @param array $filter List of properties to filter out
- *
+ * @param string $modulePrefix
* @return array
*/
public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) {
@@ -566,11 +593,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
return array(
'prop' => self::getPropertyDescriptions( array(), $p ),
'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
- 'Only the current version of the image can be scaled' ),
- 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth",
+ 'For performance reasons if this option is used, ' .
+ 'no more than ' . self::TRANSFORM_LIMIT . ' scaled images will be returned.' ),
+ 'urlheight' => "Similar to {$p}urlwidth.",
'urlparam' => array( "A handler specific parameter string. For example, pdf's ",
"might use 'page15-100px'. {$p}urlwidth must be used and be consistent with {$p}urlparam" ),
- 'limit' => 'How many image revisions to return',
+ 'limit' => 'How many image revisions to return per image',
'start' => 'Timestamp to start listing from',
'end' => 'Timestamp to stop listing at',
'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
@@ -714,8 +742,6 @@ class ApiQueryImageInfo extends ApiQueryBase {
array( 'code' => "{$p}urlwidth", 'info' => "{$p}urlheight cannot be used without {$p}urlwidth" ),
array( 'code' => 'urlparam', 'info' => "Invalid value for {$p}urlparam" ),
array( 'code' => 'urlparam_no_width', 'info' => "{$p}urlparam requires {$p}urlwidth" ),
- array( 'code' => 'urlparam_urlwidth_mismatch', 'info' => "The width set in {$p}urlparm doesn't " .
- "match the one in {$p}urlwidth" ),
) );
}
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index 37cd9159..017684ed 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -56,11 +56,11 @@ class ApiQueryInfo extends ApiQueryBase {
* @return void
*/
public function requestExtraData( $pageSet ) {
- global $wgDisableCounters;
+ global $wgDisableCounters, $wgContentHandlerUseDB;
$pageSet->requestField( 'page_restrictions' );
// when resolving redirects, no page will have this field
- if( !$pageSet->isResolvingRedirects() ) {
+ if ( !$pageSet->isResolvingRedirects() ) {
$pageSet->requestField( 'page_is_redirect' );
}
$pageSet->requestField( 'page_is_new' );
@@ -70,6 +70,9 @@ class ApiQueryInfo extends ApiQueryBase {
$pageSet->requestField( 'page_touched' );
$pageSet->requestField( 'page_latest' );
$pageSet->requestField( 'page_len' );
+ if ( $wgContentHandlerUseDB ) {
+ $pageSet->requestField( 'page_content_model' );
+ }
}
/**
@@ -348,6 +351,10 @@ class ApiQueryInfo extends ApiQueryBase {
$titleExists = $pageid > 0; //$title->exists() needs pageid, which is not set for all title objects
$ns = $title->getNamespace();
$dbkey = $title->getDBkey();
+
+ $pageInfo['contentmodel'] = $title->getContentModel();
+ $pageInfo['pagelanguage'] = $title->getPageLanguage()->getCode();
+
if ( $titleExists ) {
global $wgDisableCounters;
@@ -476,7 +483,7 @@ class ApiQueryInfo extends ApiQueryBase {
$this->protections[$title->getNamespace()][$title->getDBkey()][] = $a;
}
// Also check old restrictions
- foreach( $this->titles as $pageId => $title ) {
+ foreach ( $this->titles as $pageId => $title ) {
if ( $this->pageRestrictions[$pageId] ) {
$namespace = $title->getNamespace();
$dbKey = $title->getDBkey();
@@ -662,7 +669,9 @@ class ApiQueryInfo extends ApiQueryBase {
private function getWatchedInfo() {
$user = $this->getUser();
- if ( $user->isAnon() || count( $this->everything ) == 0 ) {
+ if ( $user->isAnon() || count( $this->everything ) == 0
+ || !$user->isAllowed( 'viewmywatchlist' )
+ ) {
return;
}
@@ -819,7 +828,8 @@ class ApiQueryInfo extends ApiQueryBase {
'starttimestamp' => array(
ApiBase::PROP_TYPE => 'timestamp',
ApiBase::PROP_NULLABLE => true
- )
+ ),
+ 'contentmodel' => 'string',
),
'watched' => array(
'watched' => 'boolean'
diff --git a/includes/api/ApiQueryLangBacklinks.php b/includes/api/ApiQueryLangBacklinks.php
index 7a4880a4..5bd451b6 100644
--- a/includes/api/ApiQueryLangBacklinks.php
+++ b/includes/api/ApiQueryLangBacklinks.php
@@ -195,7 +195,7 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
'prop' => array(
'Which properties to get',
' lllang - Adds the language code of the language link',
- ' lltitle - Adds the title of the language ink',
+ ' lltitle - Adds the title of the language link',
),
'limit' => 'How many total pages to return',
'dir' => 'The direction in which to list',
@@ -223,7 +223,8 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
return array( 'Find all pages that link to the given language link.',
'Can be used to find all links with a language code, or',
'all links to a title (with a given language).',
- 'Using neither parameter is effectively "All Language Links"',
+ 'Using neither parameter is effectively "All Language Links".',
+ 'Note that this may not consider language links added by extensions.',
);
}
@@ -239,4 +240,8 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
'api.php?action=query&generator=langbacklinks&glbltitle=Test&glbllang=fr&prop=info'
);
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Langbacklinks';
+ }
}
diff --git a/includes/api/ApiQueryLangLinks.php b/includes/api/ApiQueryLangLinks.php
index ac65d2d2..aa796e31 100644
--- a/includes/api/ApiQueryLangLinks.php
+++ b/includes/api/ApiQueryLangLinks.php
@@ -67,18 +67,19 @@ class ApiQueryLangLinks extends ApiQueryBase {
);
}
- $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
- if ( isset( $params['lang'] ) ) {
+ //FIXME: (follow-up) To allow extensions to add to the language links, we need
+ // to load them all, add the extra links, then apply paging.
+ // Should not be terrible, it's not going to be more than a few hundred links.
+
+ // Note that, since (ll_from, ll_lang) is a unique key, we don't need
+ // to sort by ll_title to ensure deterministic ordering.
+ $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
+ if ( isset( $params['lang'] ) ) {
$this->addWhereFld( 'll_lang', $params['lang'] );
if ( isset( $params['title'] ) ) {
$this->addWhereFld( 'll_title', $params['title'] );
- $this->addOption( 'ORDER BY', 'll_from' . $sort );
- } else {
- $this->addOption( 'ORDER BY', array(
- 'll_title' . $sort,
- 'll_from' . $sort
- ));
}
+ $this->addOption( 'ORDER BY', 'll_from' . $sort );
} else {
// Don't order by ll_from if it's constant in the WHERE clause
if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) {
diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php
index 73dcea49..1a2719ed 100644
--- a/includes/api/ApiQueryLogEvents.php
+++ b/includes/api/ApiQueryLogEvents.php
@@ -49,12 +49,12 @@ class ApiQueryLogEvents extends ApiQueryBase {
$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_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_parsedcomment = isset( $prop['parsedcomment'] );
$this->fld_details = isset( $prop['details'] );
$this->fld_tags = isset( $prop['tags'] );
@@ -67,7 +67,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
$this->addTables( array( 'logging', 'user', 'page' ) );
$this->addOption( 'STRAIGHT_JOIN' );
$this->addJoinConds( array(
- 'user' => array( 'JOIN',
+ 'user' => array( 'LEFT JOIN',
'user_id=log_user' ),
'page' => array( 'LEFT JOIN',
array( 'log_namespace=page_namespace',
@@ -82,8 +82,8 @@ class ApiQueryLogEvents extends ApiQueryBase {
) );
$this->addFieldsIf( array( 'log_id', 'page_id' ), $this->fld_ids );
- $this->addFieldsIf( array( 'log_user', 'user_name' ), $this->fld_user );
- $this->addFieldsIf( 'user_id', $this->fld_userid );
+ $this->addFieldsIf( array( 'log_user', 'log_user_text', 'user_name' ), $this->fld_user );
+ $this->addFieldsIf( 'log_user', $this->fld_userid );
$this->addFieldsIf( array( 'log_namespace', '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 );
@@ -98,8 +98,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
$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'] = 'change_tag_tag_id';
}
if ( !is_null( $params['action'] ) ) {
@@ -241,7 +240,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
break;
case 'rights':
$vals2 = array();
- if( $legacy ) {
+ if ( $legacy ) {
list( $vals2['old'], $vals2['new'] ) = $params;
} else {
$vals2['new'] = implode( ', ', $params['5::newgroups'] );
@@ -265,6 +264,11 @@ class ApiQueryLogEvents extends ApiQueryBase {
$vals[$type] = $vals2;
$params = null;
break;
+ case 'upload':
+ if ( isset( $params['img_timestamp'] ) ) {
+ $params['img_timestamp'] = wfTimestamp( TS_ISO_8601, $params['img_timestamp'] );
+ }
+ break;
}
if ( !is_null( $params ) ) {
$logParams = array();
@@ -331,10 +335,10 @@ class ApiQueryLogEvents extends ApiQueryBase {
$vals['userhidden'] = '';
} else {
if ( $this->fld_user ) {
- $vals['user'] = $row->user_name;
+ $vals['user'] = $row->user_name === null ? $row->log_user_text : $row->user_name;
}
if ( $this->fld_userid ) {
- $vals['userid'] = $row->user_id;
+ $vals['userid'] = $row->log_user;
}
if ( !$row->log_user ) {
diff --git a/includes/api/ApiQueryORM.php b/includes/api/ApiQueryORM.php
index 41d8f11c..a23ff06b 100644
--- a/includes/api/ApiQueryORM.php
+++ b/includes/api/ApiQueryORM.php
@@ -228,7 +228,7 @@ abstract class ApiQueryORM extends ApiQueryBase {
* @return array
*/
public function getAllowedParams() {
- $params = array (
+ $params = array(
'props' => array(
ApiBase::PARAM_TYPE => $this->getTable()->getFieldNames(),
ApiBase::PARAM_ISMULTI => true,
@@ -252,7 +252,7 @@ abstract class ApiQueryORM extends ApiQueryBase {
* @return array
*/
public function getParamDescription() {
- $descriptions = array (
+ $descriptions = array(
'props' => 'Fields to query',
'continue' => 'Offset number from where to continue the query',
'limit' => 'Max amount of rows to return',
diff --git a/includes/api/ApiQueryPagesWithProp.php b/includes/api/ApiQueryPagesWithProp.php
index 0132fc3e..6f2f02e4 100644
--- a/includes/api/ApiQueryPagesWithProp.php
+++ b/includes/api/ApiQueryPagesWithProp.php
@@ -133,7 +133,7 @@ class ApiQueryPagesWithProp extends ApiQueryGeneratorBase {
'prop' => array(
ApiBase::PARAM_DFLT => 'ids|title',
ApiBase::PARAM_ISMULTI => true,
- ApiBase::PARAM_TYPE => array (
+ ApiBase::PARAM_TYPE => array(
'ids',
'title',
'value',
diff --git a/includes/api/ApiQueryProtectedTitles.php b/includes/api/ApiQueryProtectedTitles.php
index 4aa00007..222ad074 100644
--- a/includes/api/ApiQueryProtectedTitles.php
+++ b/includes/api/ApiQueryProtectedTitles.php
@@ -157,7 +157,7 @@ class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => array_diff( $wgRestrictionLevels, array( '' ) )
),
- 'limit' => array (
+ 'limit' => array(
ApiBase::PARAM_DFLT => 10,
ApiBase::PARAM_TYPE => 'limit',
ApiBase::PARAM_MIN => 1,
diff --git a/includes/api/ApiQueryQueryPage.php b/includes/api/ApiQueryQueryPage.php
index b03bdfb8..79fe0498 100644
--- a/includes/api/ApiQueryQueryPage.php
+++ b/includes/api/ApiQueryQueryPage.php
@@ -32,27 +32,18 @@
class ApiQueryQueryPage extends ApiQueryGeneratorBase {
private $qpMap;
- /**
- * Some query pages are useless because they're available elsewhere in the API
- */
- private $uselessQueryPages = array(
- 'MIMEsearch', // aiprop=mime
- 'LinkSearch', // list=exturlusage
- 'FileDuplicateSearch', // prop=duplicatefiles
- );
-
public function __construct( $query, $moduleName ) {
parent::__construct( $query, $moduleName, 'qp' );
// We need to do this to make sure $wgQueryPages is set up
// This SUCKS
global $IP;
- require_once( "$IP/includes/QueryPage.php" );
+ require_once "$IP/includes/QueryPage.php";
// Build mapping from special page names to QueryPage classes
- global $wgQueryPages;
+ global $wgQueryPages, $wgAPIUselessQueryPages;
$this->qpMap = array();
foreach ( $wgQueryPages as $page ) {
- if( !in_array( $page[1], $this->uselessQueryPages ) ) {
+ if ( !in_array( $page[1], $wgAPIUselessQueryPages ) ) {
$this->qpMap[$page[1]] = $page[0];
}
}
@@ -222,4 +213,8 @@ class ApiQueryQueryPage extends ApiQueryGeneratorBase {
'api.php?action=query&list=querypage&qppage=Ancientpages'
);
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Querypage';
+ }
}
diff --git a/includes/api/ApiQueryRandom.php b/includes/api/ApiQueryRandom.php
index ae3bb893..2754bdae 100644
--- a/includes/api/ApiQueryRandom.php
+++ b/includes/api/ApiQueryRandom.php
@@ -185,4 +185,8 @@ class ApiQueryRandom extends ApiQueryGeneratorBase {
public function getExamples() {
return 'api.php?action=query&list=random&rnnamespace=0&rnlimit=2';
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Random';
+ }
}
diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php
index 8aceab22..6b10bdc6 100644
--- a/includes/api/ApiQueryRecentChanges.php
+++ b/includes/api/ApiQueryRecentChanges.php
@@ -39,7 +39,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
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, $token = array();
+ $fld_tags = false, $fld_sha1 = false, $token = array();
private $tokenFunctions;
@@ -121,6 +121,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$this->fld_patrolled = isset( $prop['patrolled'] );
$this->fld_loginfo = isset( $prop['loginfo'] );
$this->fld_tags = isset( $prop['tags'] );
+ $this->fld_sha1 = isset( $prop['sha1'] );
}
public function execute() {
@@ -273,6 +274,12 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$this->addFields( 'ts_tags' );
}
+ if ( $this->fld_sha1 ) {
+ $this->addTables( 'revision' );
+ $this->addJoinConds( array( 'revision' => array( 'LEFT JOIN', array( 'rc_this_oldid=rev_id' ) ) ) );
+ $this->addFields( array( 'rev_sha1', 'rev_deleted' ) );
+ }
+
if ( $params['toponly'] || $showRedirects ) {
$this->addTables( 'page' );
$this->addJoinConds( array( 'page' => array( 'LEFT JOIN', array( 'rc_namespace=page_namespace', 'rc_title=page_title' ) ) ) );
@@ -287,8 +294,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$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'] = 'change_tag_tag_id';
}
$this->token = $params['token'];
@@ -475,6 +481,19 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
}
}
+ if ( $this->fld_sha1 && $row->rev_sha1 !== null ) {
+ // The RevDel check should currently never pass due to the
+ // rc_deleted = 0 condition in the WHERE clause, but in case that
+ // ever changes we check it here too.
+ if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
+ $vals['sha1hidden'] = '';
+ } elseif ( $row->rev_sha1 !== '' ) {
+ $vals['sha1'] = wfBaseConvert( $row->rev_sha1, 36, 16, 40 );
+ } else {
+ $vals['sha1'] = '';
+ }
+ }
+
if ( !is_null( $this->token ) ) {
$tokenFunctions = $this->getTokenFunctions();
foreach ( $this->token as $t ) {
@@ -499,7 +518,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
}
return $retval;
}
- switch( $type ) {
+ switch ( $type ) {
case 'edit':
return RC_EDIT;
case 'new':
@@ -571,7 +590,8 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
'redirect',
'patrolled',
'loginfo',
- 'tags'
+ 'tags',
+ 'sha1',
)
),
'token' => array(
@@ -638,6 +658,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
' patrolled - Tags edits that have been patrolled',
' loginfo - Adds log information (logid, logtype, etc) to log entries',
' tags - Lists tags for the entry',
+ ' sha1 - Adds the content checksum for entries associated with a revision',
),
'token' => 'Which tokens to obtain for each change',
'show' => array(
@@ -735,7 +756,17 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
ApiBase::PROP_TYPE => 'string',
ApiBase::PROP_NULLABLE => true
)
- )
+ ),
+ 'sha1' => array(
+ 'sha1' => array(
+ ApiBase::PROP_TYPE => 'string',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ 'sha1hidden' => array(
+ ApiBase::PROP_TYPE => 'boolean',
+ ApiBase::PROP_NULLABLE => true
+ ),
+ ),
);
self::addTokenProperties( $props, $this->getTokenFunctions() );
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php
index 192fe873..415288ef 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -146,17 +146,17 @@ class ApiQueryRevisions extends ApiQueryBase {
$prop = array_flip( $params['prop'] );
// Optional fields
- $this->fld_ids = isset ( $prop['ids'] );
+ $this->fld_ids = isset( $prop['ids'] );
// $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed?
- $this->fld_flags = isset ( $prop['flags'] );
- $this->fld_timestamp = isset ( $prop['timestamp'] );
- $this->fld_comment = isset ( $prop['comment'] );
- $this->fld_parsedcomment = isset ( $prop['parsedcomment'] );
- $this->fld_size = isset ( $prop['size'] );
- $this->fld_sha1 = isset ( $prop['sha1'] );
- $this->fld_contentmodel = isset ( $prop['contentmodel'] );
+ $this->fld_flags = isset( $prop['flags'] );
+ $this->fld_timestamp = isset( $prop['timestamp'] );
+ $this->fld_comment = isset( $prop['comment'] );
+ $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
+ $this->fld_size = isset( $prop['size'] );
+ $this->fld_sha1 = isset( $prop['sha1'] );
+ $this->fld_contentmodel = isset( $prop['contentmodel'] );
$this->fld_userid = isset( $prop['userid'] );
- $this->fld_user = isset ( $prop['user'] );
+ $this->fld_user = isset( $prop['user'] );
$this->token = $params['token'];
if ( !empty( $params['contentformat'] ) ) {
@@ -189,8 +189,7 @@ class ApiQueryRevisions extends ApiQueryBase {
$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'] = 'change_tag_tag_id';
}
if ( isset( $prop['content'] ) || !is_null( $this->difftotext ) ) {
@@ -520,7 +519,7 @@ class ApiQueryRevisions extends ApiQueryBase {
} else {
$this->setWarning( "Conversion to XML is supported for wikitext only, " .
$title->getPrefixedDBkey() .
- " uses content model " . $content->getModel() . ")" );
+ " uses content model " . $content->getModel() );
}
}
@@ -533,7 +532,7 @@ class ApiQueryRevisions extends ApiQueryBase {
} else {
$this->setWarning( "Template expansion is supported for wikitext only, " .
$title->getPrefixedDBkey() .
- " uses content model " . $content->getModel() . ")" );
+ " uses content model " . $content->getModel() );
$text = false;
}
@@ -550,7 +549,7 @@ class ApiQueryRevisions extends ApiQueryBase {
if ( !$content->isSupportedFormat( $format ) ) {
$name = $title->getPrefixedDBkey();
- $this->dieUsage( "The requested format {$this->contentFormat} is not supported ".
+ $this->dieUsage( "The requested format {$this->contentFormat} is not supported " .
"for content model $model used by $name", 'badformat' );
}
@@ -593,7 +592,7 @@ class ApiQueryRevisions extends ApiQueryBase {
$name = $title->getPrefixedDBkey();
- $this->dieUsage( "The requested format {$this->contentFormat} is not supported for ".
+ $this->dieUsage( "The requested format {$this->contentFormat} is not supported for " .
"content model $model used by $name", 'badformat' );
}
diff --git a/includes/api/ApiQuerySearch.php b/includes/api/ApiQuerySearch.php
index 86183391..36b55979 100644
--- a/includes/api/ApiQuerySearch.php
+++ b/includes/api/ApiQuerySearch.php
@@ -31,6 +31,14 @@
*/
class ApiQuerySearch extends ApiQueryGeneratorBase {
+ /**
+ * When $wgSearchType is null, $wgSearchAlternatives[0] is null. Null isn't
+ * a valid option for an array for PARAM_TYPE, so we'll use a fake name
+ * that can't possibly be a class name and describes what the null behavior
+ * does
+ */
+ const BACKEND_NULL_PARAM = 'database-backed';
+
public function __construct( $query, $moduleName ) {
parent::__construct( $query, $moduleName, 'sr' );
}
@@ -59,7 +67,8 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
$prop = array_flip( $params['prop'] );
// Create search engine instance and set options
- $search = SearchEngine::create();
+ $search = isset( $params['backend'] ) && $params['backend'] != self::BACKEND_NULL_PARAM ?
+ SearchEngine::create( $params['backend'] ) : SearchEngine::create();
$search->setLimitOffset( $limit + 1, $params['offset'] );
$search->setNamespaces( $params['namespace'] );
$search->showRedirects = $params['redirects'];
@@ -93,6 +102,8 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
}
if ( is_null( $matches ) ) {
$this->dieUsage( "{$what} search is disabled", "search-{$what}-disabled" );
+ } elseif ( $matches instanceof Status && !$matches->isGood() ) {
+ $this->dieUsage( $matches->getWikiText(), 'search-error' );
}
$apiResult = $this->getResult();
@@ -199,7 +210,9 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
}
public function getAllowedParams() {
- return array(
+ global $wgSearchType;
+
+ $params = array(
'search' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
@@ -252,10 +265,23 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
ApiBase::PARAM_MAX2 => ApiBase::LIMIT_SML2
)
);
+
+ $alternatives = SearchEngine::getSearchTypes();
+ if ( count( $alternatives ) > 1 ) {
+ if ( $alternatives[0] === null ) {
+ $alternatives[0] = self::BACKEND_NULL_PARAM;
+ }
+ $params['backend'] = array(
+ ApiBase::PARAM_DFLT => $wgSearchType,
+ ApiBase::PARAM_TYPE => $alternatives,
+ );
+ }
+
+ return $params;
}
public function getParamDescription() {
- return array(
+ $descriptions = 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',
@@ -278,6 +304,12 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
'offset' => 'Use this value to continue paging (return by query)',
'limit' => 'How many total pages to return'
);
+
+ if ( count( SearchEngine::getSearchTypes() ) > 1 ) {
+ $descriptions['backend'] = 'Which search backend to use, if not the default';
+ }
+
+ return $descriptions;
}
public function getResultProperties() {
@@ -345,6 +377,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
return array_merge( parent::getPossibleErrors(), array(
array( 'code' => 'search-text-disabled', 'info' => 'text search is disabled' ),
array( 'code' => 'search-title-disabled', 'info' => 'title search is disabled' ),
+ array( 'code' => 'search-error', 'info' => 'search error has occurred' ),
) );
}
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php
index 810e1d6b..a7767062 100644
--- a/includes/api/ApiQuerySiteinfo.php
+++ b/includes/api/ApiQuerySiteinfo.php
@@ -121,14 +121,29 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data = array();
$mainPage = Title::newMainPage();
$data['mainpage'] = $mainPage->getPrefixedText();
- $data['base'] = wfExpandUrl( $mainPage->getFullUrl(), PROTO_CURRENT );
+ $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
$data['sitename'] = $GLOBALS['wgSitename'];
+ $data['logo'] = $GLOBALS['wgLogo'];
$data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}";
$data['phpversion'] = phpversion();
$data['phpsapi'] = PHP_SAPI;
$data['dbtype'] = $GLOBALS['wgDBtype'];
$data['dbversion'] = $this->getDB()->getServerVersion();
+ $allowFrom = array( '' );
+ $allowException = true;
+ if ( !$GLOBALS['wgAllowExternalImages'] ) {
+ if ( $GLOBALS['wgEnableImageWhitelist'] ) {
+ $data['imagewhitelistenabled'] = '';
+ }
+ $allowFrom = $GLOBALS['wgAllowExternalImagesFrom'];
+ $allowException = !empty( $allowFrom );
+ }
+ if ( $allowException ) {
+ $data['externalimages'] = (array)$allowFrom;
+ $this->getResult()->setIndexedTagName( $data['externalimages'], 'prefix' );
+ }
+
if ( !$wgDisableLangConversion ) {
$data['langconversion'] = '';
}
@@ -170,15 +185,15 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data['lang'] = $GLOBALS['wgLanguageCode'];
$fallbacks = array();
- foreach( $wgContLang->getFallbackLanguages() as $code ) {
+ foreach ( $wgContLang->getFallbackLanguages() as $code ) {
$fallbacks[] = array( 'code' => $code );
}
$data['fallback'] = $fallbacks;
$this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
- if( $wgContLang->hasVariants() ) {
+ if ( $wgContLang->hasVariants() ) {
$variants = array();
- foreach( $wgContLang->getVariants() as $code ) {
+ foreach ( $wgContLang->getVariants() as $code ) {
$variants[] = array( 'code' => $code );
}
$data['variants'] = $variants;
@@ -281,6 +296,8 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data[] = $item;
}
+ sort( $data );
+
$this->getResult()->setIndexedTagName( $data, 'ns' );
return $this->getResult()->addValue( 'query', $property, $data );
}
@@ -345,10 +362,10 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$val['language'] = $langNames[$prefix];
}
$val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
- if( isset( $row['iw_wikiid'] ) ) {
+ if ( isset( $row['iw_wikiid'] ) ) {
$val['wikiid'] = $row['iw_wikiid'];
}
- if( isset( $row['iw_api'] ) ) {
+ if ( isset( $row['iw_api'] ) ) {
$val['api'] = $row['iw_api'];
}
@@ -404,11 +421,15 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data['activeusers'] = intval( SiteStats::activeUsers() );
$data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
$data['jobs'] = intval( SiteStats::jobs() );
+
+ wfRunHooks( 'APIQuerySiteInfoStatisticsInfo', array( &$data ) );
+
return $this->getResult()->addValue( 'query', $property, $data );
}
protected function appendUserGroups( $property, $numberInGroup ) {
- global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
+ global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups;
+ global $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
$data = array();
$result = $this->getResult();
@@ -456,7 +477,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
global $wgFileExtensions;
$data = array();
- foreach ( $wgFileExtensions as $ext ) {
+ foreach ( array_unique( $wgFileExtensions ) as $ext ) {
$data[] = array( 'ext' => $ext );
}
$this->getResult()->setIndexedTagName( $data, 'fe' );
@@ -520,7 +541,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
$data = array(
'url' => $url ? $url : '',
- 'text' => $text ? $text : ''
+ 'text' => $text ? $text : ''
);
return $this->getResult()->addValue( 'query', $property, $data );
@@ -544,9 +565,17 @@ class ApiQuerySiteinfo extends ApiQueryBase {
public function appendSkins( $property ) {
$data = array();
+ $usable = Skin::getUsableSkins();
+ $default = Skin::normalizeKey( 'default' );
foreach ( Skin::getSkinNames() as $name => $displayName ) {
$skin = array( 'code' => $name );
ApiResult::setContent( $skin, $displayName );
+ if ( !isset( $usable[$name] ) ) {
+ $skin['unusable'] = '';
+ }
+ if ( $name === $default ) {
+ $skin['default'] = '';
+ }
$data[] = $skin;
}
$this->getResult()->setIndexedTagName( $data, 'skin' );
@@ -661,13 +690,15 @@ class ApiQuerySiteinfo extends ApiQueryBase {
' specialpagealiases - List of special page aliases',
' magicwords - List of magic words and their aliases',
' statistics - Returns site statistics',
- " interwikimap - Returns interwiki map (optionally filtered, (optionally localised by using {$p}inlanguagecode))",
+ " interwikimap - Returns interwiki map " .
+ "(optionally filtered, (optionally localised by using {$p}inlanguagecode))",
' 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 (optionally localised by using {$p}inlanguagecode)",
+ " languages - Returns a list of languages MediaWiki supports" .
+ "(optionally localised by using {$p}inlanguagecode)",
' skins - Returns a list of all enabled skins',
' extensiontags - Returns a list of parser extension tags',
' functionhooks - Returns a list of parser function hooks',
@@ -675,7 +706,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
' variables - Returns a list of variable IDs',
' protocols - Returns a list of protocols that are allowed in external links.',
),
- 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
+ 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
'showalldb' => 'List all database servers, not just the one lagging the most',
'numberingroup' => 'Lists the number of users in user groups',
'inlanguagecode' => 'Language code for localised language names (best effort, use CLDR extension)',
@@ -687,9 +718,10 @@ class ApiQuerySiteinfo extends ApiQueryBase {
}
public function getPossibleErrors() {
- return array_merge( parent::getPossibleErrors(), array(
- array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
- ) );
+ return array_merge( parent::getPossibleErrors(), array( array(
+ 'code' => 'includeAllDenied',
+ 'info' => 'Cannot view all servers info unless $wgShowHostnames is true'
+ ), ) );
}
public function getExamples() {
diff --git a/includes/api/ApiQueryTags.php b/includes/api/ApiQueryTags.php
index e0637ff7..732df9a4 100644
--- a/includes/api/ApiQueryTags.php
+++ b/includes/api/ApiQueryTags.php
@@ -133,8 +133,7 @@ class ApiQueryTags extends ApiQueryBase {
public function getAllowedParams() {
return array(
- 'continue' => array(
- ),
+ 'continue' => null,
'limit' => array(
ApiBase::PARAM_DFLT => 10,
ApiBase::PARAM_TYPE => 'limit',
@@ -195,4 +194,8 @@ class ApiQueryTags extends ApiQueryBase {
'api.php?action=query&list=tags&tgprop=displayname|description|hitcount'
);
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Tags';
+ }
}
diff --git a/includes/api/ApiQueryUserContributions.php b/includes/api/ApiQueryUserContributions.php
index 597c412d..9a9be7b2 100644
--- a/includes/api/ApiQueryUserContributions.php
+++ b/includes/api/ApiQueryUserContributions.php
@@ -48,7 +48,7 @@ class ApiQueryContributions extends ApiQueryBase {
$this->fld_ids = isset( $prop['ids'] );
$this->fld_title = isset( $prop['title'] );
$this->fld_comment = isset( $prop['comment'] );
- $this->fld_parsedcomment = isset ( $prop['parsedcomment'] );
+ $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
$this->fld_size = isset( $prop['size'] );
$this->fld_sizediff = isset( $prop['sizediff'] );
$this->fld_flags = isset( $prop['flags'] );
@@ -83,10 +83,10 @@ class ApiQueryContributions extends ApiQueryBase {
// Do the actual query.
$res = $this->select( __METHOD__ );
- if( $this->fld_sizediff ) {
+ if ( $this->fld_sizediff ) {
$revIds = array();
foreach ( $res as $row ) {
- if( $row->rev_parent_id ) {
+ if ( $row->rev_parent_id ) {
$revIds[] = $row->rev_parent_id;
}
}
@@ -255,7 +255,7 @@ class ApiQueryContributions extends ApiQueryBase {
$this->addFieldsIf( 'rev_comment', $this->fld_comment || $this->fld_parsedcomment );
$this->addFieldsIf( 'rev_len', $this->fld_size || $this->fld_sizediff );
$this->addFieldsIf( 'rev_minor_edit', $this->fld_flags );
- $this->addFieldsIf( 'rev_parent_id', $this->fld_flags || $this->fld_sizediff );
+ $this->addFieldsIf( 'rev_parent_id', $this->fld_flags || $this->fld_sizediff || $this->fld_ids );
$this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
if ( $this->fld_tags ) {
@@ -268,8 +268,7 @@ class ApiQueryContributions extends ApiQueryBase {
$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'] = 'change_tag_tag_id';
}
if ( $this->params['toponly'] ) {
@@ -297,6 +296,10 @@ class ApiQueryContributions extends ApiQueryBase {
$vals['pageid'] = intval( $row->rev_page );
$vals['revid'] = intval( $row->rev_id );
// $vals['textid'] = intval( $row->rev_text_id ); // todo: Should this field be exposed?
+
+ if ( !is_null( $row->rev_parent_id ) ) {
+ $vals['parentid'] = intval( $row->rev_parent_id );
+ }
}
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
@@ -474,7 +477,11 @@ class ApiQueryContributions extends ApiQueryBase {
),
'ids' => array(
'pageid' => 'integer',
- 'revid' => 'integer'
+ 'revid' => 'integer',
+ 'parentid' => array(
+ ApiBase::PROP_TYPE => 'integer',
+ ApiBase::PROP_NULLABLE => true
+ )
),
'title' => array(
'ns' => 'namespace',
diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php
index 1a491eca..3c85ea69 100644
--- a/includes/api/ApiQueryUserInfo.php
+++ b/includes/api/ApiQueryUserInfo.php
@@ -104,12 +104,15 @@ class ApiQueryUserInfo extends ApiQueryBase {
}
if ( isset( $this->prop['preferencestoken'] ) &&
- is_null( $this->getMain()->getRequest()->getVal( 'callback' ) )
+ is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) &&
+ $user->isAllowed( 'editmyoptions' )
) {
$vals['preferencestoken'] = $user->getEditToken( '', $this->getMain()->getRequest() );
}
if ( isset( $this->prop['editcount'] ) ) {
+ // use intval to prevent null if a non-logged-in user calls
+ // api.php?format=jsonfm&action=query&meta=userinfo&uiprop=editcount
$vals['editcount'] = intval( $user->getEditCount() );
}
@@ -121,11 +124,13 @@ class ApiQueryUserInfo extends ApiQueryBase {
$vals['realname'] = $user->getRealName();
}
- if ( isset( $this->prop['email'] ) ) {
- $vals['email'] = $user->getEmail();
- $auth = $user->getEmailAuthenticationTimestamp();
- if ( !is_null( $auth ) ) {
- $vals['emailauthenticated'] = wfTimestamp( TS_ISO_8601, $auth );
+ if ( $user->isAllowed( 'viewmyprivateinfo' ) ) {
+ if ( isset( $this->prop['email'] ) ) {
+ $vals['email'] = $user->getEmail();
+ $auth = $user->getEmailAuthenticationTimestamp();
+ if ( !is_null( $auth ) ) {
+ $vals['emailauthenticated'] = wfTimestamp( TS_ISO_8601, $auth );
+ }
}
}
@@ -167,8 +172,9 @@ class ApiQueryUserInfo extends ApiQueryBase {
if ( $user->isNewbie() ) {
$categories[] = 'ip';
$categories[] = 'subnet';
- if ( !$user->isAnon() )
+ if ( !$user->isAnon() ) {
$categories[] = 'newbie';
+ }
}
$categories = array_merge( $categories, $user->getGroups() );
diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php
index 72ab7866..dccfee67 100644
--- a/includes/api/ApiQueryUsers.php
+++ b/includes/api/ApiQueryUsers.php
@@ -127,7 +127,7 @@ class ApiQueryUsers extends ApiQueryBase {
$this->addFields( array( 'user_name', 'ug_group' ) );
$userGroupsRes = $this->select( __METHOD__ );
- foreach( $userGroupsRes as $row ) {
+ foreach ( $userGroupsRes as $row ) {
$userGroups[$row->user_name][] = $row->ug_group;
}
}
@@ -149,7 +149,7 @@ class ApiQueryUsers extends ApiQueryBase {
$data[$name]['name'] = $name;
if ( isset( $this->prop['editcount'] ) ) {
- $data[$name]['editcount'] = intval( $user->getEditCount() );
+ $data[$name]['editcount'] = $user->getEditCount();
}
if ( isset( $this->prop['registration'] ) ) {
@@ -204,11 +204,13 @@ class ApiQueryUsers extends ApiQueryBase {
}
}
+ $context = $this->getContext();
// Second pass: add result data to $retval
foreach ( $goodNames as $u ) {
if ( !isset( $data[$u] ) ) {
$data[$u] = array( 'name' => $u );
$urPage = new UserrightsPage;
+ $urPage->setContext( $context );
$iwUser = $urPage->fetchUser( $u );
if ( $iwUser instanceof UserRightsProxy ) {
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index 90b12c14..22843f50 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -68,7 +68,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$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_parsedcomment = isset( $prop['parsedcomment'] );
$this->fld_timestamp = isset( $prop['timestamp'] );
$this->fld_sizes = isset( $prop['sizes'] );
$this->fld_patrol = isset( $prop['patrol'] );
@@ -135,7 +135,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
if ( !$params['allrev'] ) {
$this->addTables( 'page' );
- $this->addJoinConds( array( 'page' => array( 'LEFT JOIN','rc_cur_id=page_id' ) ) );
+ $this->addJoinConds( array( 'page' => array( 'LEFT JOIN', 'rc_cur_id=page_id' ) ) );
$this->addWhere( 'rc_this_oldid=page_latest OR rc_type=' . RC_LOG );
}
@@ -143,12 +143,11 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$show = array_flip( $params['show'] );
/* Check for conflicting parameters. */
- if ( ( isset ( $show['minor'] ) && isset ( $show['!minor'] ) )
- || ( isset ( $show['bot'] ) && isset ( $show['!bot'] ) )
- || ( isset ( $show['anon'] ) && isset ( $show['!anon'] ) )
- || ( isset ( $show['patrolled'] ) && isset ( $show['!patrolled'] ) )
- )
- {
+ if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) )
+ || ( isset( $show['bot'] ) && isset( $show['!bot'] ) )
+ || ( isset( $show['anon'] ) && isset( $show['!anon'] ) )
+ || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) )
+ ) {
$this->dieUsageMsg( 'show' );
}
@@ -171,6 +170,10 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) );
}
+ if ( !is_null( $params['type'] ) ) {
+ $this->addWhereFld( 'rc_type', $this->parseRCType( $params['type'] ) );
+ }
+
if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) {
$this->dieUsage( 'user and excludeuser cannot be used together', 'user-excludeuser' );
}
@@ -226,6 +229,32 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
private function extractRowInfo( $row ) {
$vals = array();
+ $type = intval( $row->rc_type );
+
+ /* Determine what kind of change this was. */
+ switch ( $type ) {
+ case RC_EDIT:
+ $vals['type'] = 'edit';
+ break;
+ case RC_NEW:
+ $vals['type'] = 'new';
+ break;
+ case RC_MOVE:
+ $vals['type'] = 'move';
+ break;
+ case RC_LOG:
+ $vals['type'] = 'log';
+ break;
+ case RC_EXTERNAL:
+ $vals['type'] = 'external';
+ break;
+ case RC_MOVE_OVER_REDIRECT:
+ $vals['type'] = 'move over redirect';
+ break;
+ default:
+ $vals['type'] = $type;
+ }
+
if ( $this->fld_ids ) {
$vals['pageid'] = intval( $row->rc_cur_id );
$vals['revid'] = intval( $row->rc_this_oldid );
@@ -312,6 +341,27 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
return $vals;
}
+ /* Copied from ApiQueryRecentChanges. */
+ 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;
+ case 'external':
+ return RC_EXTERNAL;
+ }
+ }
+
public function getAllowedParams() {
return array(
'allrev' => false,
@@ -321,7 +371,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'end' => array(
ApiBase::PARAM_TYPE => 'timestamp'
),
- 'namespace' => array (
+ 'namespace' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => 'namespace'
),
@@ -376,6 +426,15 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'!patrolled',
)
),
+ 'type' => array(
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_TYPE => array(
+ 'edit',
+ 'external',
+ 'new',
+ 'log',
+ )
+ ),
'owner' => array(
ApiBase::PARAM_TYPE => 'user'
),
@@ -415,6 +474,13 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'Show only items that meet this criteria.',
"For example, to see only minor edits done by logged-in users, set {$p}show=minor|!anon"
),
+ 'type' => array(
+ 'Which types of changes to show',
+ ' edit - Regular page edits',
+ ' external - External changes',
+ ' new - Page creations',
+ ' log - Log entries',
+ ),
'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'
);
@@ -423,6 +489,17 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
public function getResultProperties() {
global $wgLogTypes;
return array(
+ '' => array(
+ 'type' => array(
+ ApiBase::PROP_TYPE => array(
+ 'edit',
+ 'new',
+ 'move',
+ 'log',
+ 'move over redirect'
+ )
+ )
+ ),
'ids' => array(
'pageid' => 'integer',
'revid' => 'integer',
diff --git a/includes/api/ApiQueryWatchlistRaw.php b/includes/api/ApiQueryWatchlistRaw.php
index 2cb4d9eb..ea4e724a 100644
--- a/includes/api/ApiQueryWatchlistRaw.php
+++ b/includes/api/ApiQueryWatchlistRaw.php
@@ -222,4 +222,8 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
'api.php?action=query&generator=watchlistraw&gwrshow=changed&prop=revisions',
);
}
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Watchlistraw';
+ }
}
diff --git a/includes/api/ApiRsd.php b/includes/api/ApiRsd.php
index c4a1328c..d219c91c 100644
--- a/includes/api/ApiRsd.php
+++ b/includes/api/ApiRsd.php
@@ -40,7 +40,7 @@ class ApiRsd extends ApiBase {
$service = array( 'apis' => $this->formatRsdApiList() );
ApiResult::setContent( $service, 'MediaWiki', 'engineName' );
ApiResult::setContent( $service, 'https://www.mediawiki.org/', 'engineLink' );
- ApiResult::setContent( $service, Title::newMainPage()->getCanonicalUrl(), 'homePageLink' );
+ ApiResult::setContent( $service, Title::newMainPage()->getCanonicalURL(), 'homePageLink' );
$result->setIndexedTagName( $service['apis'], 'api' );
diff --git a/includes/api/ApiSetNotificationTimestamp.php b/includes/api/ApiSetNotificationTimestamp.php
index 58d5d9ab..53a68fde 100644
--- a/includes/api/ApiSetNotificationTimestamp.php
+++ b/includes/api/ApiSetNotificationTimestamp.php
@@ -39,6 +39,9 @@ class ApiSetNotificationTimestamp extends ApiBase {
if ( $user->isAnon() ) {
$this->dieUsage( 'Anonymous users cannot use watchlist change notifications', 'notloggedin' );
}
+ if ( !$user->isAllowed( 'editmywatchlist' ) ) {
+ $this->dieUsage( 'You don\'t have permission to edit your watchlist', 'permissiondenied' );
+ }
$params = $this->extractRequestParams();
$this->requireMaxOneParameter( $params, 'timestamp', 'torevid', 'newerthanrevid' );
@@ -212,7 +215,7 @@ class ApiSetNotificationTimestamp extends ApiBase {
}
public function getParamDescription() {
- return $this->getPageSet()->getParamDescription() + array(
+ return $this->getPageSet()->getFinalParamDescription() + array(
'entirewatchlist' => 'Work on all watched pages',
'timestamp' => 'Timestamp to which to set the notification timestamp',
'torevid' => 'Revision to set the notification timestamp to (one page only)',
@@ -270,7 +273,7 @@ class ApiSetNotificationTimestamp extends ApiBase {
$ps = $this->getPageSet();
return array_merge(
parent::getPossibleErrors(),
- $ps->getPossibleErrors(),
+ $ps->getFinalPossibleErrors(),
$this->getRequireMaxOneParameterErrorMessages(
array( 'timestamp', 'torevid', 'newerthanrevid' ) ),
$this->getRequireOnlyOneParameterErrorMessages(
diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php
index 7d67aa6e..467eccf8 100644
--- a/includes/api/ApiUpload.php
+++ b/includes/api/ApiUpload.php
@@ -56,15 +56,19 @@ class ApiUpload extends ApiBase {
$this->mParams['chunk'] = $request->getFileName( 'chunk' );
// Copy the session key to the file key, for backward compatibility.
- if( !$this->mParams['filekey'] && $this->mParams['sessionkey'] ) {
+ if ( !$this->mParams['filekey'] && $this->mParams['sessionkey'] ) {
$this->mParams['filekey'] = $this->mParams['sessionkey'];
}
// Select an upload module
- if ( !$this->selectUploadModule() ) {
- return; // not a true upload, but a status request or similar
- } elseif ( !isset( $this->mUpload ) ) {
- $this->dieUsage( 'No upload module set', 'nomodule' );
+ try {
+ if ( !$this->selectUploadModule() ) {
+ return; // not a true upload, but a status request or similar
+ } elseif ( !isset( $this->mUpload ) ) {
+ $this->dieUsage( 'No upload module set', 'nomodule' );
+ }
+ } catch ( UploadStashException $e ) { // XXX: don't spam exception log
+ $this->dieUsage( get_class( $e ) . ": " . $e->getMessage(), 'stasherror' );
}
// First check permission to upload
@@ -82,7 +86,7 @@ class ApiUpload extends ApiBase {
// Check if the uploaded file is sane
if ( $this->mParams['chunk'] ) {
$maxSize = $this->mUpload->getMaxUploadSize();
- if( $this->mParams['filesize'] > $maxSize ) {
+ if ( $this->mParams['filesize'] > $maxSize ) {
$this->dieUsage( 'The file you submitted was too large', 'file-too-large' );
}
if ( !$this->mUpload->getTitle() ) {
@@ -106,9 +110,13 @@ class ApiUpload extends ApiBase {
}
// Get the result based on the current upload context:
- $result = $this->getContextResult();
- if ( $result['result'] === 'Success' ) {
- $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
+ try {
+ $result = $this->getContextResult();
+ if ( $result['result'] === 'Success' ) {
+ $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
+ }
+ } catch ( UploadStashException $e ) { // XXX: don't spam exception log
+ $this->dieUsage( get_class( $e ) . ": " . $e->getMessage(), 'stasherror' );
}
$this->getResult()->addValue( null, $this->getModuleName(), $result );
@@ -144,7 +152,7 @@ class ApiUpload extends ApiBase {
* @return array
*/
private function getStashResult( $warnings ) {
- $result = array ();
+ $result = array();
// Some uploads can request they be stashed, so as not to publish them immediately.
// In this case, a failure to stash ought to be fatal
try {
@@ -216,27 +224,27 @@ class ApiUpload extends ApiBase {
// Check we added the last chunk:
if ( $this->mParams['offset'] + $chunkSize == $this->mParams['filesize'] ) {
if ( $this->mParams['async'] ) {
- $progress = UploadBase::getSessionStatus( $this->mParams['filekey'] );
+ $progress = UploadBase::getSessionStatus( $filekey );
if ( $progress && $progress['result'] === 'Poll' ) {
$this->dieUsage( "Chunk assembly already in progress.", 'stashfailed' );
}
UploadBase::setSessionStatus(
- $this->mParams['filekey'],
+ $filekey,
array( 'result' => 'Poll',
'stage' => 'queued', 'status' => Status::newGood() )
);
$ok = JobQueueGroup::singleton()->push( new AssembleUploadChunksJob(
- Title::makeTitle( NS_FILE, $this->mParams['filekey'] ),
+ Title::makeTitle( NS_FILE, $filekey ),
array(
- 'filename' => $this->mParams['filename'],
- 'filekey' => $this->mParams['filekey'],
- 'session' => $this->getContext()->exportSession()
+ 'filename' => $this->mParams['filename'],
+ 'filekey' => $filekey,
+ 'session' => $this->getContext()->exportSession()
)
) );
if ( $ok ) {
$result['result'] = 'Poll';
} else {
- UploadBase::setSessionStatus( $this->mParams['filekey'], false );
+ UploadBase::setSessionStatus( $filekey, false );
$this->dieUsage(
"Failed to start AssembleUploadChunks.php", 'stashfailed' );
}
@@ -358,11 +366,9 @@ class ApiUpload extends ApiBase {
}
if ( $this->mParams['chunk'] ) {
- $this->checkChunkedEnabled();
-
// Chunk upload
$this->mUpload = new UploadFromChunks();
- if( isset( $this->mParams['filekey'] ) ) {
+ if ( isset( $this->mParams['filekey'] ) ) {
// handle new chunk
$this->mUpload->continueChunks(
$this->mParams['filename'],
@@ -404,6 +410,10 @@ class ApiUpload extends ApiBase {
$this->dieUsageMsg( 'copyuploadbaddomain' );
}
+ if ( !UploadFromUrl::isAllowedUrl( $this->mParams['url'] ) ) {
+ $this->dieUsageMsg( 'copyuploadbadurl' );
+ }
+
$async = false;
if ( $this->mParams['asyncdownload'] ) {
$this->checkAsyncDownloadEnabled();
@@ -452,9 +462,9 @@ class ApiUpload extends ApiBase {
$verification = $this->mUpload->verifyUpload();
if ( $verification['status'] === UploadBase::OK ) {
return;
- } else {
- return $this->checkVerification( $verification );
}
+
+ $this->checkVerification( $verification );
}
/**
@@ -463,8 +473,8 @@ class ApiUpload extends ApiBase {
protected function checkVerification( array $verification ) {
global $wgFileExtensions;
- // TODO: Move them to ApiBase's message map
- switch( $verification['status'] ) {
+ // @todo Move them to ApiBase's message map
+ switch ( $verification['status'] ) {
// Recoverable errors
case UploadBase::MIN_LENGTH_PARTNAME:
$this->dieRecoverableError( 'filename-tooshort', 'filename' );
@@ -494,7 +504,7 @@ class ApiUpload extends ApiBase {
case UploadBase::FILETYPE_BADTYPE:
$extradata = array(
'filetype' => $verification['finalExt'],
- 'allowed' => $wgFileExtensions
+ 'allowed' => array_values( array_unique( $wgFileExtensions ) )
);
$this->getResult()->setIndexedTagName( $extradata['allowed'], 'ext' );
@@ -555,7 +565,8 @@ class ApiUpload extends ApiBase {
if ( isset( $warnings['exists'] ) ) {
$warning = $warnings['exists'];
unset( $warnings['exists'] );
- $warnings[$warning['warning']] = $warning['file']->getName();
+ $localFile = isset( $warning['normalizedFile'] ) ? $warning['normalizedFile'] : $warning['file'];
+ $warnings[$warning['warning']] = $localFile->getName();
}
}
return $warnings;
@@ -596,12 +607,12 @@ class ApiUpload extends ApiBase {
$ok = JobQueueGroup::singleton()->push( new PublishStashedFileJob(
Title::makeTitle( NS_FILE, $this->mParams['filename'] ),
array(
- 'filename' => $this->mParams['filename'],
- 'filekey' => $this->mParams['filekey'],
- 'comment' => $this->mParams['comment'],
- 'text' => $this->mParams['text'],
- 'watch' => $watch,
- 'session' => $this->getContext()->exportSession()
+ 'filename' => $this->mParams['filename'],
+ 'filekey' => $this->mParams['filekey'],
+ 'comment' => $this->mParams['comment'],
+ 'text' => $this->mParams['text'],
+ 'watch' => $watch,
+ 'session' => $this->getContext()->exportSession()
)
) );
if ( $ok ) {
@@ -653,13 +664,6 @@ class ApiUpload extends ApiBase {
}
}
- protected function checkChunkedEnabled() {
- global $wgAllowChunkedUploads;
- if ( !$wgAllowChunkedUploads ) {
- $this->dieUsage( 'Chunked uploads disabled', 'chunkeduploaddisabled' );
- }
- }
-
public function mustBePosted() {
return true;
}
@@ -816,7 +820,7 @@ class ApiUpload extends ApiBase {
array( 'code' => 'publishfailed', 'info' => 'Publishing of stashed file failed' ),
array( 'code' => 'internal-error', 'info' => 'An internal error occurred' ),
array( 'code' => 'asynccopyuploaddisabled', 'info' => 'Asynchronous copy uploads disabled' ),
- array( 'code' => 'chunkeduploaddisabled', 'info' => 'Chunked uploads disabled' ),
+ array( 'code' => 'stasherror', 'info' => 'An upload stash error occurred' ),
array( 'fileexists-forbidden' ),
array( 'fileexists-shared-forbidden' ),
)
diff --git a/includes/api/ApiUserrights.php b/includes/api/ApiUserrights.php
index b9b1eeda..7d308285 100644
--- a/includes/api/ApiUserrights.php
+++ b/includes/api/ApiUserrights.php
@@ -38,6 +38,7 @@ class ApiUserrights extends ApiBase {
$user = $this->getUrUser();
$form = new UserrightsPage;
+ $form->setContext( $this->getContext() );
$r['user'] = $user->getName();
$r['userid'] = $user->getId();
list( $r['added'], $r['removed'] ) =
@@ -62,10 +63,10 @@ class ApiUserrights extends ApiBase {
$params = $this->extractRequestParams();
$form = new UserrightsPage;
+ $form->setContext( $this->getContext() );
$status = $form->fetchUser( $params['user'] );
if ( !$status->isOK() ) {
- $errors = $status->getErrorsArray();
- $this->dieUsageMsg( $errors[0] );
+ $this->dieStatus( $status );
} else {
$user = $status->value;
}
@@ -83,7 +84,7 @@ class ApiUserrights extends ApiBase {
}
public function getAllowedParams() {
- return array (
+ return array(
'user' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php
index 3e51299f..c7d636a1 100644
--- a/includes/api/ApiWatch.php
+++ b/includes/api/ApiWatch.php
@@ -36,6 +36,9 @@ class ApiWatch extends ApiBase {
if ( !$user->isLoggedIn() ) {
$this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' );
}
+ if ( !$user->isAllowed( 'editmywatchlist' ) ) {
+ $this->dieUsage( 'You don\'t have permission to edit your watchlist', 'permissiondenied' );
+ }
$params = $this->extractRequestParams();
$title = Title::newFromText( $params['title'] );
@@ -57,19 +60,19 @@ class ApiWatch extends ApiBase {
if ( $params['unwatch'] ) {
$res['unwatched'] = '';
$res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )->title( $title )->parseAsBlock();
- $success = UnwatchAction::doUnwatch( $title, $user );
+ $status = UnwatchAction::doUnwatch( $title, $user );
} else {
$res['watched'] = '';
$res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )->title( $title )->parseAsBlock();
- $success = WatchAction::doWatch( $title, $user );
+ $status = WatchAction::doWatch( $title, $user );
}
if ( !is_null( $oldLang ) ) {
$this->getContext()->setLanguage( $oldLang ); // Reset language to $oldLang
}
- if ( !$success ) {
- $this->dieUsageMsg( 'hookaborted' );
+ if ( !$status->isOK() ) {
+ $this->dieStatus( $status );
}
$this->getResult()->addValue( null, $this->getModuleName(), $res );
}