From 75e28dc0182fd4ea04323333258c0cc31a15584a Mon Sep 17 00:00:00 2001 From: eranroz Date: Wed, 29 Jan 2014 20:10:36 +0200 Subject: [PATCH] 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 --- CREDITS | 1 + RELEASE-NOTES-1.23 | 2 + includes/api/ApiBase.php | 23 +++++++-- includes/api/ApiPageSet.php | 61 +++++++++++++++++++++++ includes/api/ApiPurge.php | 32 +----------- includes/api/ApiWatch.php | 99 ++++++++++++++++++++++++++++++------- 6 files changed, 163 insertions(+), 55 deletions(-) diff --git a/CREDITS b/CREDITS index 21db8509b0..d95ec1cf72 100644 --- a/CREDITS +++ b/CREDITS @@ -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 diff --git a/RELEASE-NOTES-1.23 b/RELEASE-NOTES-1.23 index 57080b75e9..dd3b2dba54 100644 --- a/RELEASE-NOTES-1.23 +++ b/RELEASE-NOTES-1.23 @@ -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 === diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index c1a4cd353e..6ad384b4bc 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -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. diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php index 4095fe62f1..36dd726178 100644 --- a/includes/api/ApiPageSet.php +++ b/includes/api/ApiPageSet.php @@ -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) diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php index c0dd808863..e5d6a3c0cd 100644 --- a/includes/api/ApiPurge.php +++ b/includes/api/ApiPurge.php @@ -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(); diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index 759afd72e8..09f9356aa5 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -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' ); + } + + $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 ); + } - if ( !$title || !$title->isWatchable() ) { - $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); + 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"', ); } -- 2.20.1