X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fapi%2FApiQueryRecentChanges.php;h=f9025c46097bd05d341eff04943e4c0dc6b7065f;hb=9e41908dc0a3629da35cd8dd6c00abed59098227;hp=1955e4a9e6e0538ab940a6387e503e4cfb51165b;hpb=eba6c94002bdfaf7a5c4ddcf66a4ac2f443994df;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php index 1955e4a9e6..f9025c4609 100644 --- a/includes/api/ApiQueryRecentChanges.php +++ b/includes/api/ApiQueryRecentChanges.php @@ -1,9 +1,8 @@ @gmail.com * @@ -19,35 +18,36 @@ * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file */ -if ( !defined( 'MEDIAWIKI' ) ) { - // Eclipse helper - will be ignored in production - require_once( 'ApiQueryBase.php' ); -} - /** * A query action to enumerate the recent changes that were done to the wiki. * Various filters are supported. * * @ingroup API */ -class ApiQueryRecentChanges extends ApiQueryBase { +class ApiQueryRecentChanges extends ApiQueryGeneratorBase { public function __construct( $query, $moduleName ) { parent::__construct( $query, $moduleName, 'rc' ); } - private $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_flags = false, - $fld_timestamp = false, $fld_title = false, $fld_ids = false, - $fld_sizes = false; + private $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false, + $fld_flags = false, $fld_timestamp = false, $fld_title = false, $fld_ids = false, + $fld_sizes = false, $fld_redirect = false, $fld_patrolled = false, $fld_loginfo = false, + $fld_tags = false, $token = array(); + + private $tokenFunctions; + /** * Get an array mapping token names to their handler functions. * The prototype for a token function is func($pageid, $title, $rc) * it should return a token or false (permission denied) - * @return array(tokenname => function) + * @return array array(tokenname => function) */ protected function getTokenFunctions() { // Don't call the hooks twice @@ -67,32 +67,51 @@ class ApiQueryRecentChanges extends ApiQueryBase { return $this->tokenFunctions; } - public static function getPatrolToken( $pageid, $title, $rc ) { + /** + * @param $pageid + * @param $title + * @param $rc RecentChange (optional) + * @return bool|String + */ + public static function getPatrolToken( $pageid, $title, $rc = null ) { global $wgUser; - if ( !$wgUser->useRCPatrol() && ( !$wgUser->useNPPatrol() || - $rc->getAttribute( 'rc_type' ) != RC_NEW ) ) - { - return false; + + $validTokenUser = false; + + if ( $rc ) { + if ( ( $wgUser->useRCPatrol() && $rc->getAttribute( 'rc_type' ) == RC_EDIT ) || + ( $wgUser->useNPPatrol() && $rc->getAttribute( 'rc_type' ) == RC_NEW ) ) + { + $validTokenUser = true; + } + } else { + if ( $wgUser->useRCPatrol() || $wgUser->useNPPatrol() ) { + $validTokenUser = true; + } } - // The patrol token is always the same, let's exploit that - static $cachedPatrolToken = null; - if ( !is_null( $cachedPatrolToken ) ) { + if ( $validTokenUser ) { + // The patrol token is always the same, let's exploit that + static $cachedPatrolToken = null; + if ( is_null( $cachedPatrolToken ) ) { + $cachedPatrolToken = $wgUser->getEditToken( 'patrol' ); + } return $cachedPatrolToken; + } else { + return false; } - $cachedPatrolToken = $wgUser->editToken(); - return $cachedPatrolToken; } /** * Sets internal state to include the desired properties in the output. - * @param $prop associative array of properties, only keys are used here + * @param $prop Array associative array of properties, only keys are used here */ public function initProperties( $prop ) { $this->fld_comment = isset( $prop['comment'] ); $this->fld_parsedcomment = isset( $prop['parsedcomment'] ); $this->fld_user = isset( $prop['user'] ); + $this->fld_userid = isset( $prop['userid'] ); $this->fld_flags = isset( $prop['flags'] ); $this->fld_timestamp = isset( $prop['timestamp'] ); $this->fld_title = isset( $prop['title'] ); @@ -104,22 +123,32 @@ class ApiQueryRecentChanges extends ApiQueryBase { $this->fld_tags = isset( $prop['tags'] ); } + public function execute() { + $this->run(); + } + + public function executeGenerator( $resultPageSet ) { + $this->run( $resultPageSet ); + } + /** * Generates and outputs the result of this query based upon the provided parameters. + * + * @param $resultPageSet ApiPageSet */ - public function execute() { + public function run( $resultPageSet = null ) { + $user = $this->getUser(); /* Get the parameters of the request. */ $params = $this->extractRequestParams(); /* Build our basic query. Namely, something along the lines of: * SELECT * FROM recentchanges WHERE rc_timestamp > $start * AND rc_timestamp < $end AND rc_namespace = $namespace - * AND rc_deleted = '0' + * AND rc_deleted = 0 */ - $db = $this->getDB(); $this->addTables( 'recentchanges' ); $index = array( 'recentchanges' => 'rc_timestamp' ); // May change - $this->addWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] ); + $this->addTimestampWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] ); $this->addWhereFld( 'rc_namespace', $params['namespace'] ); $this->addWhereFld( 'rc_deleted', 0 ); @@ -136,16 +165,15 @@ class ApiQueryRecentChanges extends ApiQueryBase { || ( isset( $show['anon'] ) && isset( $show['!anon'] ) ) || ( isset( $show['redirect'] ) && isset( $show['!redirect'] ) ) || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) - ) - { - $this->dieUsageMsg( array( 'show' ) ); + ) { + $this->dieUsageMsg( 'show' ); } // Check permissions - global $wgUser; - if ( ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol() ) - { - $this->dieUsage( 'You need the patrol right to request the patrolled flag', 'permissiondenied' ); + if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) { + if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) { + $this->dieUsage( 'You need the patrol right to request the patrolled flag', 'permissiondenied' ); + } } /* Add additional conditions to query depending upon parameters. */ @@ -163,7 +191,7 @@ class ApiQueryRecentChanges extends ApiQueryBase { $this->addWhereIf( 'page_is_redirect = 0 OR page_is_redirect IS NULL', isset( $show['!redirect'] ) ); } - if ( !is_null( $params['user'] ) && !is_null( $param['excludeuser'] ) ) { + if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) { $this->dieUsage( 'user and excludeuser cannot be used together', 'user-excludeuser' ); } @@ -187,9 +215,11 @@ class ApiQueryRecentChanges extends ApiQueryBase { 'rc_cur_id', 'rc_type', 'rc_moved_to_ns', - 'rc_moved_to_title' + 'rc_moved_to_title', + 'rc_deleted' ) ); + $showRedirects = false; /* Determine what properties we need to display. */ if ( !is_null( $params['prop'] ) ) { $prop = array_flip( $params['prop'] ); @@ -197,35 +227,20 @@ class ApiQueryRecentChanges extends ApiQueryBase { /* Set up internal members based upon params. */ $this->initProperties( $prop ); - global $wgUser; - if ( $this->fld_patrolled && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol() ) - { + if ( $this->fld_patrolled && !$user->useRCPatrol() && !$user->useNPPatrol() ) { $this->dieUsage( 'You need the patrol right to request the patrolled flag', 'permissiondenied' ); } /* Add fields to our query if they are specified as a needed parameter. */ - $this->addFieldsIf( 'rc_id', $this->fld_ids ); - $this->addFieldsIf( 'rc_this_oldid', $this->fld_ids ); - $this->addFieldsIf( 'rc_last_oldid', $this->fld_ids ); + $this->addFieldsIf( array( 'rc_id', 'rc_this_oldid', 'rc_last_oldid' ), $this->fld_ids ); $this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment ); $this->addFieldsIf( 'rc_user', $this->fld_user ); - $this->addFieldsIf( 'rc_user_text', $this->fld_user ); - $this->addFieldsIf( 'rc_minor', $this->fld_flags ); - $this->addFieldsIf( 'rc_bot', $this->fld_flags ); - $this->addFieldsIf( 'rc_new', $this->fld_flags ); - $this->addFieldsIf( 'rc_old_len', $this->fld_sizes ); - $this->addFieldsIf( 'rc_new_len', $this->fld_sizes ); + $this->addFieldsIf( 'rc_user_text', $this->fld_user || $this->fld_userid ); + $this->addFieldsIf( array( 'rc_minor', 'rc_new', 'rc_bot' ) , $this->fld_flags ); + $this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes ); $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled ); - $this->addFieldsIf( 'rc_logid', $this->fld_loginfo ); - $this->addFieldsIf( 'rc_log_type', $this->fld_loginfo ); - $this->addFieldsIf( 'rc_log_action', $this->fld_loginfo ); - $this->addFieldsIf( 'rc_params', $this->fld_loginfo ); - if ( $this->fld_redirect || isset( $show['redirect'] ) || isset( $show['!redirect'] ) ) - { - $this->addTables( 'page' ); - $this->addJoinConds( array( 'page' => array( 'LEFT JOIN', array( 'rc_namespace=page_namespace', 'rc_title=page_title' ) ) ) ); - $this->addFields( 'page_is_redirect' ); - } + $this->addFieldsIf( array( 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ), $this->fld_loginfo ); + $showRedirects = $this->fld_redirect || isset( $show['redirect'] ) || isset( $show['!redirect'] ); } if ( $this->fld_tags ) { @@ -234,6 +249,16 @@ class ApiQueryRecentChanges extends ApiQueryBase { $this->addFields( 'ts_tags' ); } + if ( $params['toponly'] || $showRedirects ) { + $this->addTables( 'page' ); + $this->addJoinConds( array( 'page' => array( 'LEFT JOIN', array( 'rc_namespace=page_namespace', 'rc_title=page_title' ) ) ) ); + $this->addFields( 'page_is_redirect' ); + + if ( $params['toponly'] ) { + $this->addWhere( 'rc_this_oldid = page_latest' ); + } + } + if ( !is_null( $params['tag'] ) ) { $this->addTables( 'change_tag' ); $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'rc_id=ct_rc_id' ) ) ) ); @@ -248,49 +273,57 @@ class ApiQueryRecentChanges extends ApiQueryBase { $count = 0; /* Perform the actual query. */ - $db = $this->getDB(); $res = $this->select( __METHOD__ ); + $titles = array(); + + $result = $this->getResult(); + /* Iterate through the rows, adding data extracted from them to our query result. */ - while ( $row = $db->fetchObject( $res ) ) { + 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 ) ); break; } - /* Extract the data from a single row. */ - $vals = $this->extractRowInfo( $row ); + if ( is_null( $resultPageSet ) ) { + /* Extract the data from a single row. */ + $vals = $this->extractRowInfo( $row ); - /* Add that row's data to our final output. */ - if ( !$vals ) { - continue; - } - $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals ); - if ( !$fit ) { - $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); - break; + /* Add that row's data to our final output. */ + if ( !$vals ) { + continue; + } + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rc_timestamp ) ); + break; + } + } else { + $titles[] = Title::makeTitle( $row->rc_namespace, $row->rc_title ); } } - $db->freeResult( $res ); - - /* Format the result */ - $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'rc' ); + if ( is_null( $resultPageSet ) ) { + /* Format the result */ + $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'rc' ); + } else { + $resultPageSet->populateFromTitles( $titles ); + } } /** * Extracts from a single sql row the data needed to describe one recent change. * * @param $row The row from which to extract the data. - * @return An array mapping strings (descriptors) to their respective string values. + * @return array An array mapping strings (descriptors) to their respective string values. * @access public */ public function extractRowInfo( $row ) { /* If page was moved somewhere, get the title of the move target. */ $movedToTitle = false; - if ( isset( $row->rc_moved_to_title ) && $row->rc_moved_to_title !== '' ) - { + if ( isset( $row->rc_moved_to_title ) && $row->rc_moved_to_title !== '' ) { $movedToTitle = Title::makeTitle( $row->rc_moved_to_ns, $row->rc_moved_to_title ); } @@ -340,8 +373,16 @@ class ApiQueryRecentChanges extends ApiQueryBase { } /* Add user data and 'anon' flag, if use is anonymous. */ - if ( $this->fld_user ) { - $vals['user'] = $row->rc_user_text; + if ( $this->fld_user || $this->fld_userid ) { + + if ( $this->fld_user ) { + $vals['user'] = $row->rc_user_text; + } + + if ( $this->fld_userid ) { + $vals['userid'] = $row->rc_user; + } + if ( !$row->rc_user ) { $vals['anon'] = ''; } @@ -377,8 +418,7 @@ class ApiQueryRecentChanges extends ApiQueryBase { } if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) { - global $wgUser; - $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->rc_comment, $title ); + $vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title ); } if ( $this->fld_redirect ) { @@ -396,10 +436,14 @@ class ApiQueryRecentChanges extends ApiQueryBase { $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, $row->rc_params, - $row->rc_log_type, $row->rc_timestamp + $vals, + $logEntry->getParameters(), + $logEntry->getType(), + $logEntry->getSubtype(), + $logEntry->getTimestamp() ); } @@ -447,6 +491,24 @@ class ApiQueryRecentChanges extends ApiQueryBase { } } + public function getCacheMode( $params ) { + if ( isset( $params['show'] ) ) { + foreach ( $params['show'] as $show ) { + if ( $show === 'patrolled' || $show === '!patrolled' ) { + return 'private'; + } + } + } + if ( isset( $params['token'] ) ) { + return 'private'; + } + if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { + // formatComment() calls wfMsg() among other things + return 'anon-public-user-private'; + } + return 'public'; + } + public function getAllowedParams() { return array( 'start' => array( @@ -478,6 +540,7 @@ class ApiQueryRecentChanges extends ApiQueryBase { ApiBase::PARAM_DFLT => 'title|timestamp|ids', ApiBase::PARAM_TYPE => array( 'user', + 'userid', 'comment', 'parsedcomment', 'flags', @@ -524,30 +587,139 @@ class ApiQueryRecentChanges extends ApiQueryBase { 'new', 'log' ) - ) + ), + 'toponly' => false, ); } public function getParamDescription() { + $p = $this->getModulePrefix(); return array( - 'start' => 'The timestamp to start enumerating from.', - 'end' => 'The timestamp to end enumerating.', - 'dir' => 'In which direction to enumerate.', + 'start' => 'The timestamp to start enumerating from', + 'end' => 'The timestamp to end enumerating', + 'dir' => $this->getDirectionDescription( $p ), 'namespace' => 'Filter log entries to only this namespace(s)', 'user' => 'Only list changes by this user', 'excludeuser' => 'Don\'t list changes by this user', - 'prop' => 'Include additional pieces of information', + 'prop' => array( + 'Include additional pieces of information', + ' user - Adds the user responsible for the edit and tags if they are an IP', + ' userid - Adds the user id responsible for the edit', + ' comment - Adds the comment for the edit', + ' parsedcomment - Adds the parsed comment for the edit', + ' flags - Adds flags for the edit', + ' timestamp - Adds timestamp of the edit', + ' title - Adds the page title of the edit', + ' ids - Adds the page ID, recent changes ID and the new and old revision ID', + ' sizes - Adds the new and old page length in bytes', + ' redirect - Tags edit if page is a redirect', + ' patrolled - Tags edits that have been patrolled', + ' loginfo - Adds log information (logid, logtype, etc) to log entries', + ' tags - Lists tags for the entry', + ), 'token' => 'Which tokens to obtain for each change', 'show' => array( 'Show only items that meet this criteria.', - 'For example, to see only minor edits done by logged-in users, set show=minor|!anon' + "For example, to see only minor edits done by logged-in users, set {$p}show=minor|!anon" ), - 'type' => 'Which types of changes to show.', - 'limit' => 'How many total changes to return.', - 'tag' => 'Only list changes tagged with this tag.', + 'type' => 'Which types of changes to show', + 'limit' => 'How many total changes to return', + 'tag' => 'Only list changes tagged with this tag', + 'toponly' => 'Only list changes which are the latest revision', ); } + public function getResultProperties() { + global $wgLogTypes; + $props = array( + '' => array( + 'type' => array( + ApiBase::PROP_TYPE => array( + 'edit', + 'new', + 'move', + 'log', + 'move over redirect' + ) + ) + ), + 'title' => array( + 'ns' => 'namespace', + 'title' => 'string', + 'new_ns' => array( + ApiBase::PROP_TYPE => 'namespace', + Apibase::PROP_NULLABLE => true + ), + 'new_title' => array( + ApiBase::PROP_TYPE => 'string', + Apibase::PROP_NULLABLE => true + ) + ), + 'ids' => array( + 'rcid' => 'integer', + 'pageid' => 'integer', + 'revid' => 'integer', + 'old_revid' => 'integer' + ), + 'user' => array( + 'user' => 'string', + 'anon' => 'boolean' + ), + 'userid' => array( + 'userid' => 'integer', + 'anon' => 'boolean' + ), + 'flags' => array( + 'bot' => 'boolean', + 'new' => 'boolean', + 'minor' => 'boolean' + ), + 'sizes' => array( + 'oldlen' => 'integer', + 'newlen' => 'integer' + ), + 'timestamp' => array( + 'timestamp' => 'timestamp' + ), + 'comment' => array( + 'comment' => array( + ApiBase::PROP_TYPE => 'string', + Apibase::PROP_NULLABLE => true + ) + ), + 'parsedcomment' => array( + 'parsedcomment' => array( + ApiBase::PROP_TYPE => 'string', + Apibase::PROP_NULLABLE => true + ) + ), + 'redirect' => array( + 'redirect' => 'boolean' + ), + 'patrolled' => array( + 'patrolled' => 'boolean' + ), + '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 + ) + ) + ); + + self::addTokenProperties( $props, $this->getTokenFunctions() ); + + return $props; + } + public function getDescription() { return 'Enumerate recent changes'; } @@ -560,12 +732,16 @@ class ApiQueryRecentChanges extends ApiQueryBase { ) ); } - protected function getExamples() { + public function getExamples() { return array( 'api.php?action=query&list=recentchanges' ); } + public function getHelpUrls() { + return 'https://www.mediawiki.org/wiki/API:Recentchanges'; + } + public function getVersion() { return __CLASS__ . ': $Id$'; }