diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:12:12 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:12:12 -0400 |
commit | c9aa36da061816dee256a979c2ff8d2ee41824d9 (patch) | |
tree | 29f7002b80ee984b488bd047dbbd80b36bf892e9 /includes/api/ApiQueryWatchlist.php | |
parent | b4274e0e33eafb5e9ead9d949ebf031a9fb8363b (diff) | |
parent | d1ba966140d7a60cd5ae4e8667ceb27c1a138592 (diff) |
Merge branch 'archwiki'
# Conflicts:
# skins/ArchLinux.php
# skins/ArchLinux/archlogo.gif
Diffstat (limited to 'includes/api/ApiQueryWatchlist.php')
-rw-r--r-- | includes/api/ApiQueryWatchlist.php | 410 |
1 files changed, 196 insertions, 214 deletions
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php index 22843f50..efbe05ee 100644 --- a/includes/api/ApiQueryWatchlist.php +++ b/includes/api/ApiQueryWatchlist.php @@ -32,7 +32,7 @@ */ class ApiQueryWatchlist extends ApiQueryGeneratorBase { - public function __construct( $query, $moduleName ) { + public function __construct( ApiQuery $query, $moduleName ) { parent::__construct( $query, $moduleName, 'wl' ); } @@ -44,12 +44,14 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->run( $resultPageSet ); } - private $fld_ids = false, $fld_title = false, $fld_patrol = false, $fld_flags = false, - $fld_timestamp = false, $fld_user = false, $fld_comment = false, $fld_parsedcomment = false, $fld_sizes = false, - $fld_notificationtimestamp = false, $fld_userid = false, $fld_loginfo = false; + private $fld_ids = false, $fld_title = false, $fld_patrol = false, + $fld_flags = false, $fld_timestamp = false, $fld_user = false, + $fld_comment = false, $fld_parsedcomment = false, $fld_sizes = false, + $fld_notificationtimestamp = false, $fld_userid = false, + $fld_loginfo = false; /** - * @param $resultPageSet ApiPageSet + * @param ApiPageSet $resultPageSet * @return void */ private function run( $resultPageSet = null ) { @@ -57,7 +59,8 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $params = $this->extractRequestParams(); - $user = $this->getWatchlistUser( $params ); + $user = $this->getUser(); + $wlowner = $this->getWatchlistUser( $params ); if ( !is_null( $params['prop'] ) && is_null( $resultPageSet ) ) { $prop = array_flip( $params['prop'] ); @@ -83,10 +86,12 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } $this->addFields( array( + 'rc_id', 'rc_namespace', 'rc_title', 'rc_timestamp', 'rc_type', + 'rc_deleted', ) ); if ( is_null( $resultPageSet ) ) { @@ -103,7 +108,10 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->addFieldsIf( 'rc_patrolled', $this->fld_patrol ); $this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes ); $this->addFieldsIf( 'wl_notificationtimestamp', $this->fld_notificationtimestamp ); - $this->addFieldsIf( array( 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ), $this->fld_loginfo ); + $this->addFieldsIf( + array( 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ), + $this->fld_loginfo + ); } elseif ( $params['allrev'] ) { $this->addFields( 'rc_this_oldid' ); } else { @@ -115,22 +123,35 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { 'watchlist', ) ); - $userId = $user->getId(); + $userId = $wlowner->getId(); $this->addJoinConds( array( 'watchlist' => array( 'INNER JOIN', array( 'wl_user' => $userId, 'wl_namespace=rc_namespace', 'wl_title=rc_title' - ) ) ) ); - - $this->addWhere( array( - 'rc_deleted' => 0, - ) ); + ) + ) ) ); $db = $this->getDB(); $this->addTimestampWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] ); + // Include in ORDER BY for uniqueness + $this->addWhereRange( 'rc_id', $params['dir'], null, null ); + + if ( !is_null( $params['continue'] ) ) { + $cont = explode( '|', $params['continue'] ); + $this->dieContinueUsageIf( count( $cont ) != 2 ); + $op = ( $params['dir'] === 'newer' ? '>' : '<' ); + $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) ); + $continueId = (int)$cont[1]; + $this->dieContinueUsageIf( $continueId != $cont[1] ); + $this->addWhere( "rc_timestamp $op $continueTimestamp OR " . + "(rc_timestamp = $continueTimestamp AND " . + "rc_id $op= $continueId)" + ); + } + $this->addWhereFld( 'wl_namespace', $params['namespace'] ); if ( !$params['allrev'] ) { @@ -144,18 +165,21 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { /* 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'] ) ) + || ( isset( $show['bot'] ) && isset( $show['!bot'] ) ) + || ( isset( $show['anon'] ) && isset( $show['!anon'] ) ) + || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) + || ( isset( $show['unread'] ) && isset( $show['!unread'] ) ) ) { $this->dieUsageMsg( 'show' ); } // Check permissions. if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) { - $user = $this->getUser(); if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) { - $this->dieUsage( 'You need the patrol right to request the patrolled flag', 'permissiondenied' ); + $this->dieUsage( + 'You need the patrol right to request the patrolled flag', + 'permissiondenied' + ); } } @@ -168,10 +192,16 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $this->addWhereIf( 'rc_user != 0', isset( $show['!anon'] ) ); $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) ); $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) ); + $this->addWhereIf( 'wl_notificationtimestamp IS NOT NULL', isset( $show['unread'] ) ); + $this->addWhereIf( 'wl_notificationtimestamp IS NULL', isset( $show['!unread'] ) ); } if ( !is_null( $params['type'] ) ) { - $this->addWhereFld( 'rc_type', $this->parseRCType( $params['type'] ) ); + try { + $this->addWhereFld( 'rc_type', RecentChange::parseToRCType( $params['type'] ) ); + } catch ( MWException $e ) { + ApiBase::dieDebug( __METHOD__, $e->getMessage() ); + } } if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) { @@ -185,7 +215,40 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } // This is an index optimization for mysql, as done in the Special:Watchlist page - $this->addWhereIf( "rc_timestamp > ''", !isset( $params['start'] ) && !isset( $params['end'] ) && $db->getType() == 'mysql' ); + $this->addWhereIf( + "rc_timestamp > ''", + !isset( $params['start'] ) && !isset( $params['end'] ) && $db->getType() == 'mysql' + ); + + // Paranoia: avoid brute force searches (bug 17342) + if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) { + if ( !$user->isAllowed( 'deletedhistory' ) ) { + $bitmask = Revision::DELETED_USER; + } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { + $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED; + } else { + $bitmask = 0; + } + if ( $bitmask ) { + $this->addWhere( $this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask" ); + } + } + + // LogPage::DELETED_ACTION hides the affected page, too. So hide those + // entirely from the watchlist, or someone could guess the title. + if ( !$user->isAllowed( 'deletedhistory' ) ) { + $bitmask = LogPage::DELETED_ACTION; + } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { + $bitmask = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED; + } else { + $bitmask = 0; + } + if ( $bitmask ) { + $this->addWhere( $this->getDB()->makeList( array( + 'rc_type != ' . RC_LOG, + $this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask", + ), LIST_OR ) ); + } $this->addOption( 'LIMIT', $params['limit'] + 1 ); @@ -194,9 +257,10 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $res = $this->select( __METHOD__ ); foreach ( $res as $row ) { - if ( ++ $count > $params['limit'] ) { - // We've reached the one extra which shows that there are additional pages to be had. Stop here... - $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); + if ( ++$count > $params['limit'] ) { + // We've reached the one extra which shows that there are + // additional pages to be had. Stop here... + $this->setContinueEnumParameter( 'continue', "$row->rc_timestamp|$row->rc_id" ); break; } @@ -204,8 +268,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $vals = $this->extractRowInfo( $row ); $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); if ( !$fit ) { - $this->setContinueEnumParameter( 'start', - wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); + $this->setContinueEnumParameter( 'continue', "$row->rc_timestamp|$row->rc_id" ); break; } } else { @@ -218,7 +281,10 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } if ( is_null( $resultPageSet ) ) { - $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' ); + $this->getResult()->setIndexedTagName_internal( + array( 'query', $this->getModuleName() ), + 'item' + ); } elseif ( $params['allrev'] ) { $resultPageSet->populateFromRevisionIDs( $ids ); } else { @@ -227,139 +293,142 @@ 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 ); - $vals['old_revid'] = intval( $row->rc_last_oldid ); - } - + /* Determine the title of the page that has been changed. */ $title = Title::makeTitle( $row->rc_namespace, $row->rc_title ); + $user = $this->getUser(); - if ( $this->fld_title ) { - ApiQueryBase::addTitleInfo( $vals, $title ); + /* Our output data. */ + $vals = array(); + $type = intval( $row->rc_type ); + $vals['type'] = RecentChange::parseFromRCType( $type ); + $anyHidden = false; + + /* Create a new entry in the result for the title. */ + if ( $this->fld_title || $this->fld_ids ) { + // These should already have been filtered out of the query, but just in case. + if ( $type === RC_LOG && ( $row->rc_deleted & LogPage::DELETED_ACTION ) ) { + $vals['actionhidden'] = ''; + $anyHidden = true; + } + if ( $type !== RC_LOG || + LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user ) + ) { + if ( $this->fld_title ) { + ApiQueryBase::addTitleInfo( $vals, $title ); + } + if ( $this->fld_ids ) { + $vals['pageid'] = intval( $row->rc_cur_id ); + $vals['revid'] = intval( $row->rc_this_oldid ); + $vals['old_revid'] = intval( $row->rc_last_oldid ); + } + } } + /* Add user data and 'anon' flag, if user is anonymous. */ if ( $this->fld_user || $this->fld_userid ) { - - if ( $this->fld_userid ) { - $vals['userid'] = $row->rc_user; - // for backwards compatibility - $vals['user'] = $row->rc_user; + if ( $row->rc_deleted & Revision::DELETED_USER ) { + $vals['userhidden'] = ''; + $anyHidden = true; } + if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_USER, $user ) ) { + if ( $this->fld_userid ) { + $vals['userid'] = $row->rc_user; + // for backwards compatibility + $vals['user'] = $row->rc_user; + } - if ( $this->fld_user ) { - $vals['user'] = $row->rc_user_text; - } + if ( $this->fld_user ) { + $vals['user'] = $row->rc_user_text; + } - if ( !$row->rc_user ) { - $vals['anon'] = ''; + if ( !$row->rc_user ) { + $vals['anon'] = ''; + } } } + /* Add flags, such as new, minor, bot. */ if ( $this->fld_flags ) { + if ( $row->rc_bot ) { + $vals['bot'] = ''; + } if ( $row->rc_type == RC_NEW ) { $vals['new'] = ''; } if ( $row->rc_minor ) { $vals['minor'] = ''; } - if ( $row->rc_bot ) { - $vals['bot'] = ''; - } } - if ( $this->fld_patrol && isset( $row->rc_patrolled ) ) { - $vals['patrolled'] = ''; + /* Add sizes of each revision. (Only available on 1.10+) */ + if ( $this->fld_sizes ) { + $vals['oldlen'] = intval( $row->rc_old_len ); + $vals['newlen'] = intval( $row->rc_new_len ); } + /* Add the timestamp. */ if ( $this->fld_timestamp ) { $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp ); } - if ( $this->fld_sizes ) { - $vals['oldlen'] = intval( $row->rc_old_len ); - $vals['newlen'] = intval( $row->rc_new_len ); - } - if ( $this->fld_notificationtimestamp ) { $vals['notificationtimestamp'] = ( $row->wl_notificationtimestamp == null ) ? '' : wfTimestamp( TS_ISO_8601, $row->wl_notificationtimestamp ); } - if ( $this->fld_comment && isset( $row->rc_comment ) ) { - $vals['comment'] = $row->rc_comment; - } + /* Add edit summary / log summary. */ + if ( $this->fld_comment || $this->fld_parsedcomment ) { + if ( $row->rc_deleted & Revision::DELETED_COMMENT ) { + $vals['commenthidden'] = ''; + $anyHidden = true; + } + if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_COMMENT, $user ) ) { + if ( $this->fld_comment && isset( $row->rc_comment ) ) { + $vals['comment'] = $row->rc_comment; + } - if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) { - $vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title ); + if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) { + $vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title ); + } + } } - if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) { - $vals['logid'] = intval( $row->rc_logid ); - $vals['logtype'] = $row->rc_log_type; - $vals['logaction'] = $row->rc_log_action; - $logEntry = DatabaseLogEntry::newFromRow( (array)$row ); - ApiQueryLogEvents::addLogParams( - $this->getResult(), - $vals, - $logEntry->getParameters(), - $logEntry->getType(), - $logEntry->getSubtype(), - $logEntry->getTimestamp() - ); + /* Add the patrolled flag */ + if ( $this->fld_patrol && $row->rc_patrolled == 1 ) { + $vals['patrolled'] = ''; } - return $vals; - } + if ( $this->fld_patrol && ChangesList::isUnpatrolled( $row, $user ) ) { + $vals['unpatrolled'] = ''; + } - /* Copied from ApiQueryRecentChanges. */ - private function parseRCType( $type ) { - if ( is_array( $type ) ) { - $retval = array(); - foreach ( $type as $t ) { - $retval[] = $this->parseRCType( $t ); + if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) { + if ( $row->rc_deleted & LogPage::DELETED_ACTION ) { + $vals['actionhidden'] = ''; + $anyHidden = true; + } + if ( LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user ) ) { + $vals['logid'] = intval( $row->rc_logid ); + $vals['logtype'] = $row->rc_log_type; + $vals['logaction'] = $row->rc_log_action; + $logEntry = DatabaseLogEntry::newFromRow( (array)$row ); + ApiQueryLogEvents::addLogParams( + $this->getResult(), + $vals, + $logEntry->getParameters(), + $logEntry->getType(), + $logEntry->getSubtype(), + $logEntry->getTimestamp() + ); } - return $retval; } - switch ( $type ) { - case 'edit': - return RC_EDIT; - case 'new': - return RC_NEW; - case 'log': - return RC_LOG; - case 'external': - return RC_EXTERNAL; + + if ( $anyHidden && ( $row->rc_deleted & Revision::DELETED_RESTRICTED ) ) { + $vals['suppressed'] = ''; } + + return $vals; } public function getAllowedParams() { @@ -424,6 +493,8 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { '!anon', 'patrolled', '!patrolled', + 'unread', + '!unread', ) ), 'type' => array( @@ -440,12 +511,14 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { ), 'token' => array( ApiBase::PARAM_TYPE => 'string' - ) + ), + 'continue' => null, ); } public function getParamDescription() { $p = $this->getModulePrefix(); + return array( 'allrev' => 'Include multiple revisions of the same page within given timeframe', 'start' => 'The timestamp to start enumerating from', @@ -482,105 +555,14 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { ' 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' - ); - } - - 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', - 'old_revid' => 'integer' - ), - 'title' => array( - 'ns' => 'namespace', - 'title' => 'string' - ), - 'user' => array( - 'user' => 'string', - 'anon' => 'boolean' - ), - 'userid' => array( - 'userid' => 'integer', - 'anon' => 'boolean' - ), - 'flags' => array( - 'new' => 'boolean', - 'minor' => 'boolean', - 'bot' => 'boolean' - ), - 'patrol' => array( - 'patrolled' => 'boolean' - ), - 'timestamp' => array( - 'timestamp' => 'timestamp' - ), - 'sizes' => array( - 'oldlen' => 'integer', - 'newlen' => 'integer' - ), - 'notificationtimestamp' => array( - 'notificationtimestamp' => array( - ApiBase::PROP_TYPE => 'timestamp', - ApiBase::PROP_NULLABLE => true - ) - ), - 'comment' => array( - 'comment' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ) - ), - 'parsedcomment' => array( - 'parsedcomment' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ) - ), - 'loginfo' => array( - 'logid' => array( - ApiBase::PROP_TYPE => 'integer', - ApiBase::PROP_NULLABLE => true - ), - 'logtype' => array( - ApiBase::PROP_TYPE => $wgLogTypes, - ApiBase::PROP_NULLABLE => true - ), - 'logaction' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ) - ) + 'token' => 'Give a security token (settable in preferences) to ' . + 'allow access to another user\'s watchlist', + 'continue' => 'When more results are available, use this to continue', ); } public function getDescription() { - return "Get all recent changes to pages in the logged in user's watchlist"; - } - - public function getPossibleErrors() { - return array_merge( parent::getPossibleErrors(), array( - array( 'code' => 'bad_wlowner', 'info' => 'Specified user does not exist' ), - array( 'code' => 'bad_wltoken', 'info' => 'Incorrect watchlist token provided -- please set a correct token in Special:Preferences' ), - array( 'code' => 'notloggedin', 'info' => 'You must be logged-in to have a watchlist' ), - array( 'code' => 'patrol', 'info' => 'patrol property is not available' ), - array( 'show' ), - array( 'code' => 'permissiondenied', 'info' => 'You need the patrol right to request the patrolled flag' ), - array( 'code' => 'user-excludeuser', 'info' => 'user and excludeuser cannot be used together' ), - ) ); + return "Get all recent changes to pages in the logged in user's watchlist."; } public function getExamples() { |