Support ApiPageSet in ApiWatch

Add support for ApiPageSet capabilities in ApiWatch to support batch
operation on titles, pageids etc. The old 'title' parameter (for a
single page) is still supported to keep backwards compatibility.

Moved a function from ApiPurge to ApiPageSet: getInvalidTitlesAndRevisions() gathers
warnings about invalid/missing titles/ids in the requested page set.

Split from If993f6e8.

Change-Id: I820dcb64d469616b10741df013911197cc5bde29
This commit is contained in:
eranroz 2014-01-29 20:10:36 +02:00 committed by Brad Jorsch
parent 9d0b230b65
commit 75e28dc018
6 changed files with 163 additions and 55 deletions

View file

@ -129,6 +129,7 @@ following names for their contribution to the product.
* Ebrahim Byagowi
* Edward Z. Yang
* Elvis Stansvik
* Eranroz
* Erwin Dokter
* Federico Leva
* FunPika

View file

@ -150,6 +150,8 @@ production.
list=usercontribs.
* list=watchlist now uses the querying user's rights rather than the wlowner's
rights when checking whether wlprop=patrol is allowed.
* (bug 32151) ApiWatch now has pageset capabilities (titles/pageids/generators).
Title parameter is now deprecated.
=== Languages updated in 1.23 ===

View file

@ -1362,13 +1362,13 @@ abstract class ApiBase extends ContextSource {
}
/**
* Throw a UsageException based on the errors in the Status object.
* Get error (as code, string) from a Status object.
*
* @since 1.22
* @since 1.23
* @param Status $status Status object
* @throws MWException
* @return array of code and error string
*/
public function dieStatus( $status ) {
public function getErrorFromStatus( $status ) {
if ( $status->isGood() ) {
throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
}
@ -1396,7 +1396,20 @@ abstract class ApiBase extends ContextSource {
// Translate message to code, for backwards compatability
$code = ApiBase::$messageMap[$code]['code'];
}
$this->dieUsage( $msg->inLanguage( 'en' )->useDatabase( false )->plain(), $code );
return array( $code, $msg->inLanguage( 'en' )->useDatabase( false )->plain() );
}
/**
* Throw a UsageException based on the errors in the Status object.
*
* @since 1.22
* @param Status $status Status object
* @throws MWException
*/
public function dieStatus( $status ) {
list( $code, $msg ) = $this->getErrorFromStatus( $status );
$this->dieUsage( $msg, $code );
}
// @codingStandardsIgnoreStart Allow long lines. Cannot split these.

View file

@ -74,6 +74,30 @@ class ApiPageSet extends ApiBase {
*/
private $mDefaultNamespace = NS_MAIN;
/**
* Add all items from $values into the result
* @param array $result output
* @param array $values values to add
* @param string $flag the name of the boolean flag to mark this element
* @param string $name if given, name of the value
*/
private static function addValues( array &$result, $values, $flag = null, $name = null ) {
foreach ( $values as $val ) {
if ( $val instanceof Title ) {
$v = array();
ApiQueryBase::addTitleInfo( $v, $val );
} elseif ( $name !== null ) {
$v = array( $name => $val );
} else {
$v = $val;
}
if ( $flag !== null ) {
$v[$flag] = '';
}
$result[] = $v;
}
}
/**
* Constructor
* @param $dbSource ApiBase Module implementing getDB().
@ -498,6 +522,43 @@ class ApiPageSet extends ApiBase {
return $values;
}
/**
* Get an array of invalid/special/missing titles.
*
* @param $invalidChecks List of types of invalid titles to include.
* Recognized values are:
* - invalidTitles: Titles from $this->getInvalidTitles()
* - special: Titles from $this->getSpecialTitles()
* - missingIds: ids from $this->getMissingPageIDs()
* - missingRevIds: ids from $this->getMissingRevisionIDs()
* - missingTitles: Titles from $this->getMissingTitles()
* - interwikiTitles: Titles from $this->getInterwikiTitlesAsResult()
* @return array Array suitable for inclusion in the response
* @since 1.23
*/
public function getInvalidTitlesAndRevisions( $invalidChecks = array( 'invalidTitles', 'special', 'missingIds', 'missingRevIds', 'missingTitles', 'interwikiTitles' ) ) {
$result = array();
if ( in_array( "invalidTitles", $invalidChecks ) ) {
self::addValues( $result, $this->getInvalidTitles(), 'invalid', 'title' );
}
if ( in_array( "special", $invalidChecks ) ) {
self::addValues( $result, $this->getSpecialTitles(), 'special', 'title' );
}
if ( in_array( "missingIds", $invalidChecks ) ) {
self::addValues( $result, $this->getMissingPageIDs(), 'missing', 'pageid' );
}
if ( in_array( "missingRevIds", $invalidChecks ) ) {
self::addValues( $result, $this->getMissingRevisionIDs(), 'missing', 'revid' );
}
if ( in_array( "missingTitles", $invalidChecks ) ) {
self::addValues( $result, $this->getMissingTitles(), 'missing' );
}
if ( in_array( "interwikiTitles", $invalidChecks ) ) {
self::addValues( $result, $this->getInterwikiTitlesAsResult() );
}
return $result;
}
/**
* Get the list of revision IDs (requested with the revids= parameter)
* @return array revID (int) => pageID (int)

View file

@ -32,30 +32,6 @@
class ApiPurge extends ApiBase {
private $mPageSet;
/**
* Add all items from $values into the result
* @param array $result output
* @param array $values values to add
* @param string $flag the name of the boolean flag to mark this element
* @param string $name if given, name of the value
*/
private static function addValues( array &$result, $values, $flag = null, $name = null ) {
foreach ( $values as $val ) {
if ( $val instanceof Title ) {
$v = array();
ApiQueryBase::addTitleInfo( $v, $val );
} elseif ( $name !== null ) {
$v = array( $name => $val );
} else {
$v = $val;
}
if ( $flag !== null ) {
$v[$flag] = '';
}
$result[] = $v;
}
}
/**
* Purges the cache of a page
*/
@ -67,13 +43,7 @@ class ApiPurge extends ApiBase {
$pageSet = $this->getPageSet();
$pageSet->execute();
$result = array();
self::addValues( $result, $pageSet->getInvalidTitles(), 'invalid', 'title' );
self::addValues( $result, $pageSet->getSpecialTitles(), 'special', 'title' );
self::addValues( $result, $pageSet->getMissingPageIDs(), 'missing', 'pageid' );
self::addValues( $result, $pageSet->getMissingRevisionIDs(), 'missing', 'revid' );
self::addValues( $result, $pageSet->getMissingTitles(), 'missing' );
self::addValues( $result, $pageSet->getInterwikiTitlesAsResult() );
$result = $pageSet->getInvalidTitlesAndRevisions();
foreach ( $pageSet->getGoodTitles() as $title ) {
$r = array();

View file

@ -30,21 +30,57 @@
* @ingroup API
*/
class ApiWatch extends ApiBase {
private $mPageSet = null;
public function execute() {
$user = $this->getUser();
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'] );
$pageSet = $this->getPageSet();
// by default we use pageset to extract the page to work on.
// title is still supported for backward compatibility
if ( !isset( $params['title'] ) ) {
$pageSet->execute();
$res = $pageSet->getInvalidTitlesAndRevisions( array( 'invalidTitles', 'special', 'missingIds', 'missingRevIds', 'interwikiTitles' ) );
foreach ( $pageSet->getMissingTitles() as $title ) {
$r = $this->watchTitle( $title, $user, $params );
$r['missing'] = 1;
$res[] = $r;
}
foreach ( $pageSet->getGoodTitles() as $title ) {
$r = $this->watchTitle( $title, $user, $params );
$res[] = $r;
}
$this->getResult()->setIndexedTagName( $res, 'w' );
} else {
// dont allow use of old title parameter with new pageset parameters.
$extraParams = array_keys( array_filter( $pageSet->extractRequestParams(), function ( $x ) {
return $x !== null && $x !== false;
} ) );
if ( $extraParams ) {
$p = $this->getModulePrefix();
$this->dieUsage( "The parameter {$p}title can not be used with ". implode( ", ", $extraParams ), 'invalidparammix' );
}
if ( !$title || !$title->isWatchable() ) {
$this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
$title = Title::newFromText( $params['title'] );
if ( !$title || !$title->isWatchable() ) {
$this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
}
$res = $this->watchTitle( $title, $user, $params, true );
}
$this->getResult()->addValue( null, $this->getModuleName(), $res );
}
private function watchTitle( Title $title, User $user, array $params, $compatibilityMode = false ) {
if ( !$title->isWatchable() ) {
return array( 'title' => $title->getPrefixedText(), 'watchable' => 0 );
}
$res = array( 'title' => $title->getPrefixedText() );
@ -61,25 +97,45 @@ class ApiWatch extends ApiBase {
}
if ( $params['unwatch'] ) {
$res['unwatched'] = '';
$res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )
->title( $title )->parseAsBlock();
$status = UnwatchAction::doUnwatch( $title, $user );
if ( $status->isOK() ) {
$res['unwatched'] = '';
$res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )
->title( $title )->parseAsBlock();
}
} else {
$res['watched'] = '';
$res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )
->title( $title )->parseAsBlock();
$status = WatchAction::doWatch( $title, $user );
if ( $status->isOK() ) {
$res['watched'] = '';
$res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )
->title( $title )->parseAsBlock();
}
}
if ( !is_null( $oldLang ) ) {
$this->getContext()->setLanguage( $oldLang ); // Reset language to $oldLang
}
if ( !$status->isOK() ) {
$this->dieStatus( $status );
if ( $compatibilityMode ) {
$this->dieStatus( $status );
}
$res['error'] = $this->getErrorFromStatus( $status );
}
$this->getResult()->addValue( null, $this->getModuleName(), $res );
return $res;
}
/**
* Get a cached instance of an ApiPageSet object
* @return ApiPageSet
*/
private function getPageSet() {
if ( $this->mPageSet === null ) {
$this->mPageSet = new ApiPageSet( $this );
}
return $this->mPageSet;
}
public function mustBePosted() {
@ -98,11 +154,11 @@ class ApiWatch extends ApiBase {
return 'watch';
}
public function getAllowedParams() {
return array(
public function getAllowedParams( $flags = 0 ) {
$result = array(
'title' => array(
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_REQUIRED => true
ApiBase::PARAM_DEPRECATED => true
),
'unwatch' => false,
'uselang' => null,
@ -111,11 +167,16 @@ class ApiWatch extends ApiBase {
ApiBase::PARAM_REQUIRED => true
),
);
if ( $flags ) {
$result += $this->getPageSet()->getFinalParams( $flags );
}
return $result;
}
public function getParamDescription() {
return array(
'title' => 'The page to (un)watch',
$psModule = $this->getPageSet();
return $psModule->getParamDescription() + array(
'title' => 'The page to (un)watch. use titles instead',
'unwatch' => 'If set the page will be unwatched rather than watched',
'uselang' => 'Language to show the message in',
'token' => 'A token previously acquired via prop=info',
@ -134,7 +195,7 @@ class ApiWatch extends ApiBase {
}
public function getDescription() {
return 'Add or remove a page from/to the current user\'s watchlist';
return 'Add or remove pages from/to the current user\'s watchlist';
}
public function getPossibleErrors() {
@ -147,8 +208,8 @@ class ApiWatch extends ApiBase {
public function getExamples() {
return array(
'api.php?action=watch&title=Main_Page' => 'Watch the page "Main Page"',
'api.php?action=watch&title=Main_Page&unwatch=' => 'Unwatch the page "Main Page"',
'api.php?action=watch&titles=Main_Page' => 'Watch the page "Main Page"',
'api.php?action=watch&titles=Main_Page&unwatch=' => 'Unwatch the page "Main Page"',
);
}