Support ApiPageSet in ApiWatch
authoreranroz <eranroz89@gmail.com>
Wed, 29 Jan 2014 18:10:36 +0000 (20:10 +0200)
committerBrad Jorsch <bjorsch@wikimedia.org>
Tue, 4 Feb 2014 19:44:22 +0000 (14:44 -0500)
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
RELEASE-NOTES-1.23
includes/api/ApiBase.php
includes/api/ApiPageSet.php
includes/api/ApiPurge.php
includes/api/ApiWatch.php

diff --git a/CREDITS b/CREDITS
index 21db850..d95ec1c 100644 (file)
--- 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
index 57080b7..dd3b2db 100644 (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 ===
 
index c1a4cd3..6ad384b 100644 (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.
index 4095fe6..36dd726 100644 (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)
index c0dd808..e5d6a3c 100644 (file)
 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();
index 759afd7..09f9356 100644 (file)
  * @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"',
                );
        }