MediaWiki  REL1_22
ApiQueryWatchlist.php
Go to the documentation of this file.
00001 <?php
00033 class ApiQueryWatchlist extends ApiQueryGeneratorBase {
00034 
00035     public function __construct( $query, $moduleName ) {
00036         parent::__construct( $query, $moduleName, 'wl' );
00037     }
00038 
00039     public function execute() {
00040         $this->run();
00041     }
00042 
00043     public function executeGenerator( $resultPageSet ) {
00044         $this->run( $resultPageSet );
00045     }
00046 
00047     private $fld_ids = false, $fld_title = false, $fld_patrol = false, $fld_flags = false,
00048             $fld_timestamp = false, $fld_user = false, $fld_comment = false, $fld_parsedcomment = false, $fld_sizes = false,
00049             $fld_notificationtimestamp = false, $fld_userid = false, $fld_loginfo = false;
00050 
00055     private function run( $resultPageSet = null ) {
00056         $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' );
00057 
00058         $params = $this->extractRequestParams();
00059 
00060         $user = $this->getWatchlistUser( $params );
00061 
00062         if ( !is_null( $params['prop'] ) && is_null( $resultPageSet ) ) {
00063             $prop = array_flip( $params['prop'] );
00064 
00065             $this->fld_ids = isset( $prop['ids'] );
00066             $this->fld_title = isset( $prop['title'] );
00067             $this->fld_flags = isset( $prop['flags'] );
00068             $this->fld_user = isset( $prop['user'] );
00069             $this->fld_userid = isset( $prop['userid'] );
00070             $this->fld_comment = isset( $prop['comment'] );
00071             $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
00072             $this->fld_timestamp = isset( $prop['timestamp'] );
00073             $this->fld_sizes = isset( $prop['sizes'] );
00074             $this->fld_patrol = isset( $prop['patrol'] );
00075             $this->fld_notificationtimestamp = isset( $prop['notificationtimestamp'] );
00076             $this->fld_loginfo = isset( $prop['loginfo'] );
00077 
00078             if ( $this->fld_patrol ) {
00079                 if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
00080                     $this->dieUsage( 'patrol property is not available', 'patrol' );
00081                 }
00082             }
00083         }
00084 
00085         $this->addFields( array(
00086             'rc_namespace',
00087             'rc_title',
00088             'rc_timestamp',
00089             'rc_type',
00090         ) );
00091 
00092         if ( is_null( $resultPageSet ) ) {
00093             $this->addFields( array(
00094                 'rc_cur_id',
00095                 'rc_this_oldid',
00096                 'rc_last_oldid',
00097             ) );
00098 
00099             $this->addFieldsIf( array( 'rc_type', 'rc_minor', 'rc_bot' ), $this->fld_flags );
00100             $this->addFieldsIf( 'rc_user', $this->fld_user || $this->fld_userid );
00101             $this->addFieldsIf( 'rc_user_text', $this->fld_user );
00102             $this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
00103             $this->addFieldsIf( 'rc_patrolled', $this->fld_patrol );
00104             $this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes );
00105             $this->addFieldsIf( 'wl_notificationtimestamp', $this->fld_notificationtimestamp );
00106             $this->addFieldsIf( array( 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ), $this->fld_loginfo );
00107         } elseif ( $params['allrev'] ) {
00108             $this->addFields( 'rc_this_oldid' );
00109         } else {
00110             $this->addFields( 'rc_cur_id' );
00111         }
00112 
00113         $this->addTables( array(
00114             'recentchanges',
00115             'watchlist',
00116         ) );
00117 
00118         $userId = $user->getId();
00119         $this->addJoinConds( array( 'watchlist' => array( 'INNER JOIN',
00120             array(
00121                 'wl_user' => $userId,
00122                 'wl_namespace=rc_namespace',
00123                 'wl_title=rc_title'
00124         ) ) ) );
00125 
00126         $this->addWhere( array(
00127             'rc_deleted' => 0,
00128         ) );
00129 
00130         $db = $this->getDB();
00131 
00132         $this->addTimestampWhereRange( 'rc_timestamp', $params['dir'],
00133             $params['start'], $params['end'] );
00134         $this->addWhereFld( 'wl_namespace', $params['namespace'] );
00135 
00136         if ( !$params['allrev'] ) {
00137             $this->addTables( 'page' );
00138             $this->addJoinConds( array( 'page' => array( 'LEFT JOIN', 'rc_cur_id=page_id' ) ) );
00139             $this->addWhere( 'rc_this_oldid=page_latest OR rc_type=' . RC_LOG );
00140         }
00141 
00142         if ( !is_null( $params['show'] ) ) {
00143             $show = array_flip( $params['show'] );
00144 
00145             /* Check for conflicting parameters. */
00146             if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) )
00147                     || ( isset( $show['bot'] ) && isset( $show['!bot'] ) )
00148                     || ( isset( $show['anon'] ) && isset( $show['!anon'] ) )
00149                     || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) )
00150             ) {
00151                 $this->dieUsageMsg( 'show' );
00152             }
00153 
00154             // Check permissions.
00155             if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) {
00156                 $user = $this->getUser();
00157                 if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
00158                     $this->dieUsage( 'You need the patrol right to request the patrolled flag', 'permissiondenied' );
00159                 }
00160             }
00161 
00162             /* Add additional conditions to query depending upon parameters. */
00163             $this->addWhereIf( 'rc_minor = 0', isset( $show['!minor'] ) );
00164             $this->addWhereIf( 'rc_minor != 0', isset( $show['minor'] ) );
00165             $this->addWhereIf( 'rc_bot = 0', isset( $show['!bot'] ) );
00166             $this->addWhereIf( 'rc_bot != 0', isset( $show['bot'] ) );
00167             $this->addWhereIf( 'rc_user = 0', isset( $show['anon'] ) );
00168             $this->addWhereIf( 'rc_user != 0', isset( $show['!anon'] ) );
00169             $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) );
00170             $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) );
00171         }
00172 
00173         if ( !is_null( $params['type'] ) ) {
00174             $this->addWhereFld( 'rc_type', $this->parseRCType( $params['type'] ) );
00175         }
00176 
00177         if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) {
00178             $this->dieUsage( 'user and excludeuser cannot be used together', 'user-excludeuser' );
00179         }
00180         if ( !is_null( $params['user'] ) ) {
00181             $this->addWhereFld( 'rc_user_text', $params['user'] );
00182         }
00183         if ( !is_null( $params['excludeuser'] ) ) {
00184             $this->addWhere( 'rc_user_text != ' . $db->addQuotes( $params['excludeuser'] ) );
00185         }
00186 
00187         // This is an index optimization for mysql, as done in the Special:Watchlist page
00188         $this->addWhereIf( "rc_timestamp > ''", !isset( $params['start'] ) && !isset( $params['end'] ) && $db->getType() == 'mysql' );
00189 
00190         $this->addOption( 'LIMIT', $params['limit'] + 1 );
00191 
00192         $ids = array();
00193         $count = 0;
00194         $res = $this->select( __METHOD__ );
00195 
00196         foreach ( $res as $row ) {
00197             if ( ++ $count > $params['limit'] ) {
00198                 // We've reached the one extra which shows that there are additional pages to be had. Stop here...
00199                 $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) );
00200                 break;
00201             }
00202 
00203             if ( is_null( $resultPageSet ) ) {
00204                 $vals = $this->extractRowInfo( $row );
00205                 $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals );
00206                 if ( !$fit ) {
00207                     $this->setContinueEnumParameter( 'start',
00208                             wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) );
00209                     break;
00210                 }
00211             } else {
00212                 if ( $params['allrev'] ) {
00213                     $ids[] = intval( $row->rc_this_oldid );
00214                 } else {
00215                     $ids[] = intval( $row->rc_cur_id );
00216                 }
00217             }
00218         }
00219 
00220         if ( is_null( $resultPageSet ) ) {
00221             $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' );
00222         } elseif ( $params['allrev'] ) {
00223             $resultPageSet->populateFromRevisionIDs( $ids );
00224         } else {
00225             $resultPageSet->populateFromPageIDs( $ids );
00226         }
00227     }
00228 
00229     private function extractRowInfo( $row ) {
00230         $vals = array();
00231 
00232         $type = intval( $row->rc_type );
00233 
00234         /* Determine what kind of change this was. */
00235         switch ( $type ) {
00236             case RC_EDIT:
00237                 $vals['type'] = 'edit';
00238                 break;
00239             case RC_NEW:
00240                 $vals['type'] = 'new';
00241                 break;
00242             case RC_MOVE:
00243                 $vals['type'] = 'move';
00244                 break;
00245             case RC_LOG:
00246                 $vals['type'] = 'log';
00247                 break;
00248             case RC_EXTERNAL:
00249                 $vals['type'] = 'external';
00250                 break;
00251             case RC_MOVE_OVER_REDIRECT:
00252                 $vals['type'] = 'move over redirect';
00253                 break;
00254             default:
00255                 $vals['type'] = $type;
00256         }
00257 
00258         if ( $this->fld_ids ) {
00259             $vals['pageid'] = intval( $row->rc_cur_id );
00260             $vals['revid'] = intval( $row->rc_this_oldid );
00261             $vals['old_revid'] = intval( $row->rc_last_oldid );
00262         }
00263 
00264         $title = Title::makeTitle( $row->rc_namespace, $row->rc_title );
00265 
00266         if ( $this->fld_title ) {
00267             ApiQueryBase::addTitleInfo( $vals, $title );
00268         }
00269 
00270         if ( $this->fld_user || $this->fld_userid ) {
00271 
00272             if ( $this->fld_userid ) {
00273                 $vals['userid'] = $row->rc_user;
00274                 // for backwards compatibility
00275                 $vals['user'] = $row->rc_user;
00276             }
00277 
00278             if ( $this->fld_user ) {
00279                 $vals['user'] = $row->rc_user_text;
00280             }
00281 
00282             if ( !$row->rc_user ) {
00283                 $vals['anon'] = '';
00284             }
00285         }
00286 
00287         if ( $this->fld_flags ) {
00288             if ( $row->rc_type == RC_NEW ) {
00289                 $vals['new'] = '';
00290             }
00291             if ( $row->rc_minor ) {
00292                 $vals['minor'] = '';
00293             }
00294             if ( $row->rc_bot ) {
00295                 $vals['bot'] = '';
00296             }
00297         }
00298 
00299         if ( $this->fld_patrol && isset( $row->rc_patrolled ) ) {
00300             $vals['patrolled'] = '';
00301         }
00302 
00303         if ( $this->fld_timestamp ) {
00304             $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp );
00305         }
00306 
00307         if ( $this->fld_sizes ) {
00308             $vals['oldlen'] = intval( $row->rc_old_len );
00309             $vals['newlen'] = intval( $row->rc_new_len );
00310         }
00311 
00312         if ( $this->fld_notificationtimestamp ) {
00313             $vals['notificationtimestamp'] = ( $row->wl_notificationtimestamp == null )
00314                 ? ''
00315                 : wfTimestamp( TS_ISO_8601, $row->wl_notificationtimestamp );
00316         }
00317 
00318         if ( $this->fld_comment && isset( $row->rc_comment ) ) {
00319             $vals['comment'] = $row->rc_comment;
00320         }
00321 
00322         if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
00323             $vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
00324         }
00325 
00326         if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) {
00327             $vals['logid'] = intval( $row->rc_logid );
00328             $vals['logtype'] = $row->rc_log_type;
00329             $vals['logaction'] = $row->rc_log_action;
00330             $logEntry = DatabaseLogEntry::newFromRow( (array)$row );
00331             ApiQueryLogEvents::addLogParams(
00332                 $this->getResult(),
00333                 $vals,
00334                 $logEntry->getParameters(),
00335                 $logEntry->getType(),
00336                 $logEntry->getSubtype(),
00337                 $logEntry->getTimestamp()
00338             );
00339         }
00340 
00341         return $vals;
00342     }
00343 
00344     /* Copied from ApiQueryRecentChanges. */
00345     private function parseRCType( $type ) {
00346         if ( is_array( $type ) ) {
00347             $retval = array();
00348             foreach ( $type as $t ) {
00349                 $retval[] = $this->parseRCType( $t );
00350             }
00351             return $retval;
00352         }
00353         switch ( $type ) {
00354             case 'edit':
00355                 return RC_EDIT;
00356             case 'new':
00357                 return RC_NEW;
00358             case 'log':
00359                 return RC_LOG;
00360             case 'external':
00361                 return RC_EXTERNAL;
00362         }
00363     }
00364 
00365     public function getAllowedParams() {
00366         return array(
00367             'allrev' => false,
00368             'start' => array(
00369                 ApiBase::PARAM_TYPE => 'timestamp'
00370             ),
00371             'end' => array(
00372                 ApiBase::PARAM_TYPE => 'timestamp'
00373             ),
00374             'namespace' => array(
00375                 ApiBase::PARAM_ISMULTI => true,
00376                 ApiBase::PARAM_TYPE => 'namespace'
00377             ),
00378             'user' => array(
00379                 ApiBase::PARAM_TYPE => 'user',
00380             ),
00381             'excludeuser' => array(
00382                 ApiBase::PARAM_TYPE => 'user',
00383             ),
00384             'dir' => array(
00385                 ApiBase::PARAM_DFLT => 'older',
00386                 ApiBase::PARAM_TYPE => array(
00387                     'newer',
00388                     'older'
00389                 )
00390             ),
00391             'limit' => array(
00392                 ApiBase::PARAM_DFLT => 10,
00393                 ApiBase::PARAM_TYPE => 'limit',
00394                 ApiBase::PARAM_MIN => 1,
00395                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00396                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00397             ),
00398             'prop' => array(
00399                 ApiBase::PARAM_ISMULTI => true,
00400                 ApiBase::PARAM_DFLT => 'ids|title|flags',
00401                 ApiBase::PARAM_TYPE => array(
00402                     'ids',
00403                     'title',
00404                     'flags',
00405                     'user',
00406                     'userid',
00407                     'comment',
00408                     'parsedcomment',
00409                     'timestamp',
00410                     'patrol',
00411                     'sizes',
00412                     'notificationtimestamp',
00413                     'loginfo',
00414                 )
00415             ),
00416             'show' => array(
00417                 ApiBase::PARAM_ISMULTI => true,
00418                 ApiBase::PARAM_TYPE => array(
00419                     'minor',
00420                     '!minor',
00421                     'bot',
00422                     '!bot',
00423                     'anon',
00424                     '!anon',
00425                     'patrolled',
00426                     '!patrolled',
00427                 )
00428             ),
00429             'type' => array(
00430                 ApiBase::PARAM_ISMULTI => true,
00431                 ApiBase::PARAM_TYPE => array(
00432                     'edit',
00433                     'external',
00434                     'new',
00435                     'log',
00436                 )
00437             ),
00438             'owner' => array(
00439                 ApiBase::PARAM_TYPE => 'user'
00440             ),
00441             'token' => array(
00442                 ApiBase::PARAM_TYPE => 'string'
00443             )
00444         );
00445     }
00446 
00447     public function getParamDescription() {
00448         $p = $this->getModulePrefix();
00449         return array(
00450             'allrev' => 'Include multiple revisions of the same page within given timeframe',
00451             'start' => 'The timestamp to start enumerating from',
00452             'end' => 'The timestamp to end enumerating',
00453             'namespace' => 'Filter changes to only the given namespace(s)',
00454             'user' => 'Only list changes by this user',
00455             'excludeuser' => 'Don\'t list changes by this user',
00456             'dir' => $this->getDirectionDescription( $p ),
00457             'limit' => 'How many total results to return per request',
00458             'prop' => array(
00459                 'Which additional items to get (non-generator mode only).',
00460                 ' ids                    - Adds revision ids and page ids',
00461                 ' title                  - Adds title of the page',
00462                 ' flags                  - Adds flags for the edit',
00463                 ' user                   - Adds the user who made the edit',
00464                 ' userid                 - Adds user id of whom made the edit',
00465                 ' comment                - Adds comment of the edit',
00466                 ' parsedcomment          - Adds parsed comment of the edit',
00467                 ' timestamp              - Adds timestamp of the edit',
00468                 ' patrol                 - Tags edits that are patrolled',
00469                 ' sizes                  - Adds the old and new lengths of the page',
00470                 ' notificationtimestamp  - Adds timestamp of when the user was last notified about the edit',
00471                 ' loginfo                - Adds log information where appropriate',
00472             ),
00473             'show' => array(
00474                 'Show only items that meet this criteria.',
00475                 "For example, to see only minor edits done by logged-in users, set {$p}show=minor|!anon"
00476             ),
00477             'type' => array(
00478                 'Which types of changes to show',
00479                 ' edit           - Regular page edits',
00480                 ' external       - External changes',
00481                 ' new            - Page creations',
00482                 ' log            - Log entries',
00483             ),
00484             'owner' => 'The name of the user whose watchlist you\'d like to access',
00485             'token' => 'Give a security token (settable in preferences) to allow access to another user\'s watchlist'
00486         );
00487     }
00488 
00489     public function getResultProperties() {
00490         global $wgLogTypes;
00491         return array(
00492             '' => array(
00493                 'type' => array(
00494                     ApiBase::PROP_TYPE => array(
00495                         'edit',
00496                         'new',
00497                         'move',
00498                         'log',
00499                         'move over redirect'
00500                     )
00501                 )
00502             ),
00503             'ids' => array(
00504                 'pageid' => 'integer',
00505                 'revid' => 'integer',
00506                 'old_revid' => 'integer'
00507             ),
00508             'title' => array(
00509                 'ns' => 'namespace',
00510                 'title' => 'string'
00511             ),
00512             'user' => array(
00513                 'user' => 'string',
00514                 'anon' => 'boolean'
00515             ),
00516             'userid' => array(
00517                 'userid' => 'integer',
00518                 'anon' => 'boolean'
00519             ),
00520             'flags' => array(
00521                 'new' => 'boolean',
00522                 'minor' => 'boolean',
00523                 'bot' => 'boolean'
00524             ),
00525             'patrol' => array(
00526                 'patrolled' => 'boolean'
00527             ),
00528             'timestamp' => array(
00529                 'timestamp' => 'timestamp'
00530             ),
00531             'sizes' => array(
00532                 'oldlen' => 'integer',
00533                 'newlen' => 'integer'
00534             ),
00535             'notificationtimestamp' => array(
00536                 'notificationtimestamp' => array(
00537                     ApiBase::PROP_TYPE => 'timestamp',
00538                     ApiBase::PROP_NULLABLE => true
00539                 )
00540             ),
00541             'comment' => array(
00542                 'comment' => array(
00543                     ApiBase::PROP_TYPE => 'string',
00544                     ApiBase::PROP_NULLABLE => true
00545                 )
00546             ),
00547             'parsedcomment' => array(
00548                 'parsedcomment' => array(
00549                     ApiBase::PROP_TYPE => 'string',
00550                     ApiBase::PROP_NULLABLE => true
00551                 )
00552             ),
00553             'loginfo' => array(
00554                 'logid' => array(
00555                     ApiBase::PROP_TYPE => 'integer',
00556                     ApiBase::PROP_NULLABLE => true
00557                 ),
00558                 'logtype' => array(
00559                     ApiBase::PROP_TYPE => $wgLogTypes,
00560                     ApiBase::PROP_NULLABLE => true
00561                 ),
00562                 'logaction' => array(
00563                     ApiBase::PROP_TYPE => 'string',
00564                     ApiBase::PROP_NULLABLE => true
00565                 )
00566             )
00567         );
00568     }
00569 
00570     public function getDescription() {
00571         return "Get all recent changes to pages in the logged in user's watchlist";
00572     }
00573 
00574     public function getPossibleErrors() {
00575         return array_merge( parent::getPossibleErrors(), array(
00576             array( 'code' => 'bad_wlowner', 'info' => 'Specified user does not exist' ),
00577             array( 'code' => 'bad_wltoken', 'info' => 'Incorrect watchlist token provided -- please set a correct token in Special:Preferences' ),
00578             array( 'code' => 'notloggedin', 'info' => 'You must be logged-in to have a watchlist' ),
00579             array( 'code' => 'patrol', 'info' => 'patrol property is not available' ),
00580             array( 'show' ),
00581             array( 'code' => 'permissiondenied', 'info' => 'You need the patrol right to request the patrolled flag' ),
00582             array( 'code' => 'user-excludeuser', 'info' => 'user and excludeuser cannot be used together' ),
00583         ) );
00584     }
00585 
00586     public function getExamples() {
00587         return array(
00588             'api.php?action=query&list=watchlist',
00589             'api.php?action=query&list=watchlist&wlprop=ids|title|timestamp|user|comment',
00590             'api.php?action=query&list=watchlist&wlallrev=&wlprop=ids|title|timestamp|user|comment',
00591             'api.php?action=query&generator=watchlist&prop=info',
00592             'api.php?action=query&generator=watchlist&gwlallrev=&prop=revisions&rvprop=timestamp|user',
00593             'api.php?action=query&list=watchlist&wlowner=Bob_Smith&wltoken=123ABC'
00594         );
00595     }
00596 
00597     public function getHelpUrls() {
00598         return 'https://www.mediawiki.org/wiki/API:Watchlist';
00599     }
00600 }