MediaWiki 1.43.1
ApiQueryAllRevisions.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Api;
24
42
50
51 private RevisionStore $revisionStore;
52 private ActorMigration $actorMigration;
53 private NamespaceInfo $namespaceInfo;
54
55 public function __construct(
56 ApiQuery $query,
57 string $moduleName,
58 RevisionStore $revisionStore,
59 IContentHandlerFactory $contentHandlerFactory,
60 ParserFactory $parserFactory,
61 SlotRoleRegistry $slotRoleRegistry,
62 ActorMigration $actorMigration,
63 NamespaceInfo $namespaceInfo,
64 ContentRenderer $contentRenderer,
65 ContentTransformer $contentTransformer,
66 CommentFormatter $commentFormatter,
67 TempUserCreator $tempUserCreator,
68 UserFactory $userFactory
69 ) {
70 parent::__construct(
71 $query,
72 $moduleName,
73 'arv',
74 $revisionStore,
75 $contentHandlerFactory,
76 $parserFactory,
77 $slotRoleRegistry,
78 $contentRenderer,
79 $contentTransformer,
80 $commentFormatter,
81 $tempUserCreator,
82 $userFactory
83 );
84 $this->revisionStore = $revisionStore;
85 $this->actorMigration = $actorMigration;
86 $this->namespaceInfo = $namespaceInfo;
87 }
88
93 protected function run( ?ApiPageSet $resultPageSet = null ) {
94 $db = $this->getDB();
95 $params = $this->extractRequestParams( false );
96
97 $result = $this->getResult();
98
99 $this->requireMaxOneParameter( $params, 'user', 'excludeuser' );
100
101 $tsField = 'rev_timestamp';
102 $idField = 'rev_id';
103 $pageField = 'rev_page';
104
105 // Namespace check is likely to be desired, but can't be done
106 // efficiently in SQL.
107 $miser_ns = null;
108 $needPageTable = false;
109 if ( $params['namespace'] !== null ) {
110 $params['namespace'] = array_unique( $params['namespace'] );
111 sort( $params['namespace'] );
112 if ( $params['namespace'] != $this->namespaceInfo->getValidNamespaces() ) {
113 $needPageTable = true;
114 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
115 $miser_ns = $params['namespace'];
116 } else {
117 $this->addWhere( [ 'page_namespace' => $params['namespace'] ] );
118 }
119 }
120 }
121
122 if ( $resultPageSet === null ) {
123 $this->parseParameters( $params );
124 $queryBuilder = $this->revisionStore->newSelectQueryBuilder( $db )
125 ->joinComment()
126 ->joinPage();
127 $this->getQueryBuilder()->merge( $queryBuilder );
128 } else {
129 $this->limit = $this->getParameter( 'limit' ) ?: 10;
130 $this->addTables( [ 'revision' ] );
131 $this->addFields( [ 'rev_timestamp', 'rev_id' ] );
132
133 if ( $params['generatetitles'] ) {
134 $this->addFields( [ 'rev_page' ] );
135 }
136
137 if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
138 $this->getQueryBuilder()->join( 'actor', 'actor_rev_user', 'actor_rev_user.actor_id = rev_actor' );
139 }
140
141 if ( $needPageTable ) {
142 $this->getQueryBuilder()->join( 'page', null, [ "$pageField = page_id" ] );
143 if ( (bool)$miser_ns ) {
144 $this->addFields( [ 'page_namespace' ] );
145 }
146 }
147 }
148
149 // Seems to be needed to avoid a planner bug (T113901)
150 $this->addOption( 'STRAIGHT_JOIN' );
151
152 $dir = $params['dir'];
153 $this->addTimestampWhereRange( $tsField, $dir, $params['start'], $params['end'] );
154
155 if ( $this->fld_tags ) {
156 $this->addFields( [
157 'ts_tags' => MediaWikiServices::getInstance()->getChangeTagsStore()
158 ->makeTagSummarySubquery( 'revision' )
159 ] );
160 }
161
162 if ( $params['user'] !== null ) {
163 $actorQuery = $this->actorMigration->getWhere( $db, 'rev_user', $params['user'] );
164 $this->addWhere( $actorQuery['conds'] );
165 } elseif ( $params['excludeuser'] !== null ) {
166 $actorQuery = $this->actorMigration->getWhere( $db, 'rev_user', $params['excludeuser'] );
167 $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' );
168 }
169
170 if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
171 // Paranoia: avoid brute force searches (T19342)
172 if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
173 $bitmask = RevisionRecord::DELETED_USER;
174 } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
175 $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
176 } else {
177 $bitmask = 0;
178 }
179 if ( $bitmask ) {
180 $this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
181 }
182 }
183
184 if ( $params['continue'] !== null ) {
185 $op = ( $dir == 'newer' ? '>=' : '<=' );
186 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'timestamp', 'int' ] );
187 $this->addWhere( $db->buildComparison( $op, [
188 $tsField => $db->timestamp( $cont[0] ),
189 $idField => $cont[1],
190 ] ) );
191 }
192
193 $this->addOption( 'LIMIT', $this->limit + 1 );
194
195 $sort = ( $dir == 'newer' ? '' : ' DESC' );
196 $orderby = [];
197 // Targeting index rev_timestamp, user_timestamp, usertext_timestamp, or actor_timestamp.
198 // But 'user' is always constant for the latter three, so it doesn't matter here.
199 $orderby[] = "rev_timestamp $sort";
200 $orderby[] = "rev_id $sort";
201 $this->addOption( 'ORDER BY', $orderby );
202
203 $hookData = [];
204 $res = $this->select( __METHOD__, [], $hookData );
205
206 if ( $resultPageSet === null ) {
207 $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
208 }
209
210 $pageMap = []; // Maps rev_page to array index
211 $count = 0;
212 $nextIndex = 0;
213 $generated = [];
214 foreach ( $res as $row ) {
215 if ( $count === 0 && $resultPageSet !== null ) {
216 // Set the non-continue since the list of all revisions is
217 // prone to having entries added at the start frequently.
218 $this->getContinuationManager()->addGeneratorNonContinueParam(
219 $this, 'continue', "$row->rev_timestamp|$row->rev_id"
220 );
221 }
222 if ( ++$count > $this->limit ) {
223 // We've had enough
224 $this->setContinueEnumParameter( 'continue', "$row->rev_timestamp|$row->rev_id" );
225 break;
226 }
227
228 // Miser mode namespace check
229 if ( $miser_ns !== null && !in_array( $row->page_namespace, $miser_ns ) ) {
230 continue;
231 }
232
233 if ( $resultPageSet !== null ) {
234 if ( $params['generatetitles'] ) {
235 $generated[$row->rev_page] = $row->rev_page;
236 } else {
237 $generated[] = $row->rev_id;
238 }
239 } else {
240 $revision = $this->revisionStore->newRevisionFromRow( $row, 0, Title::newFromRow( $row ) );
241 $rev = $this->extractRevisionInfo( $revision, $row );
242
243 if ( !isset( $pageMap[$row->rev_page] ) ) {
244 $index = $nextIndex++;
245 $pageMap[$row->rev_page] = $index;
246 $title = Title::newFromLinkTarget( $revision->getPageAsLinkTarget() );
247 $a = [
248 'pageid' => $title->getArticleID(),
249 'revisions' => [ $rev ],
250 ];
251 ApiResult::setIndexedTagName( $a['revisions'], 'rev' );
252 ApiQueryBase::addTitleInfo( $a, $title );
253 $fit = $this->processRow( $row, $a['revisions'][0], $hookData ) &&
254 $result->addValue( [ 'query', $this->getModuleName() ], $index, $a );
255 } else {
256 $index = $pageMap[$row->rev_page];
257 $fit = $this->processRow( $row, $rev, $hookData ) &&
258 $result->addValue( [ 'query', $this->getModuleName(), $index, 'revisions' ], null, $rev );
259 }
260 if ( !$fit ) {
261 $this->setContinueEnumParameter( 'continue', "$row->rev_timestamp|$row->rev_id" );
262 break;
263 }
264 }
265 }
266
267 if ( $resultPageSet !== null ) {
268 if ( $params['generatetitles'] ) {
269 $resultPageSet->populateFromPageIDs( $generated );
270 } else {
271 $resultPageSet->populateFromRevisionIDs( $generated );
272 }
273 } else {
274 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'page' );
275 }
276 }
277
278 public function getAllowedParams() {
279 $ret = parent::getAllowedParams() + [
280 'user' => [
281 ParamValidator::PARAM_TYPE => 'user',
282 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ],
283 UserDef::PARAM_RETURN_OBJECT => true,
284 ],
285 'namespace' => [
286 ParamValidator::PARAM_ISMULTI => true,
287 ParamValidator::PARAM_TYPE => 'namespace',
288 ParamValidator::PARAM_DEFAULT => null,
289 ],
290 'start' => [
291 ParamValidator::PARAM_TYPE => 'timestamp',
292 ],
293 'end' => [
294 ParamValidator::PARAM_TYPE => 'timestamp',
295 ],
296 'dir' => [
297 ParamValidator::PARAM_TYPE => [
298 'newer',
299 'older'
300 ],
301 ParamValidator::PARAM_DEFAULT => 'older',
302 ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
304 'newer' => 'api-help-paramvalue-direction-newer',
305 'older' => 'api-help-paramvalue-direction-older',
306 ],
307 ],
308 'excludeuser' => [
309 ParamValidator::PARAM_TYPE => 'user',
310 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ],
311 UserDef::PARAM_RETURN_OBJECT => true,
312 ],
313 'continue' => [
314 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
315 ],
316 'generatetitles' => [
317 ParamValidator::PARAM_DEFAULT => false,
318 ],
319 ];
320
321 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
322 $ret['namespace'][ApiBase::PARAM_HELP_MSG_APPEND] = [
323 'api-help-param-limited-in-miser-mode',
324 ];
325 }
326
327 return $ret;
328 }
329
330 protected function getExamplesMessages() {
331 return [
332 'action=query&list=allrevisions&arvuser=Example&arvlimit=50'
333 => 'apihelp-query+allrevisions-example-user',
334 'action=query&list=allrevisions&arvdir=newer&arvlimit=50'
335 => 'apihelp-query+allrevisions-example-ns-any',
336 ];
337 }
338
339 public function getHelpUrls() {
340 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allrevisions';
341 }
342}
343
345class_alias( ApiQueryAllRevisions::class, 'ApiQueryAllRevisions' );
array $params
The job parameters.
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:571
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition ApiBase.php:1768
getResult()
Get the result object.
Definition ApiBase.php:710
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:224
requireMaxOneParameter( $params,... $required)
Dies if more than one parameter from a certain set of parameters are set and not false.
Definition ApiBase.php:1025
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition ApiBase.php:192
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:184
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:851
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition ApiBase.php:973
This class contains a list of pages that the client has requested.
Query module to enumerate all revisions.
run(?ApiPageSet $resultPageSet=null)
getExamplesMessages()
Returns usage examples for this module.
getHelpUrls()
Return links to more detailed help pages about the module.
__construct(ApiQuery $query, string $moduleName, RevisionStore $revisionStore, IContentHandlerFactory $contentHandlerFactory, ParserFactory $parserFactory, SlotRoleRegistry $slotRoleRegistry, ActorMigration $actorMigration, NamespaceInfo $namespaceInfo, ContentRenderer $contentRenderer, ContentTransformer $contentTransformer, CommentFormatter $commentFormatter, TempUserCreator $tempUserCreator, UserFactory $userFactory)
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
select( $method, $extraQuery=[], ?array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
executeGenderCacheFromResultWrapper(IResultWrapper $res, $fname=__METHOD__, $fieldPrefix='page')
Preprocess the result set to fill the GenderCache with the necessary information before using self::a...
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
processRow( $row, array &$data, array &$hookData)
Call the ApiQueryBaseProcessRow hook.
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
A base class for functions common to producing a list of revisions.
extractRevisionInfo(RevisionRecord $revision, $row)
Extract information from the RevisionRecord.
parseParameters( $params)
Parse the parameters into the various instance fields.
This is the main query class.
Definition ApiQuery.php:48
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
This is the main service interface for converting single-line comments from various DB comment fields...
A class containing constants representing the names of configuration variables.
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Type definition for user types.
Definition UserDef.php:27
Page revision base class.
Service for looking up page revisions.
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Represents a title within MediaWiki.
Definition Title.php:78
This is not intended to be a long-term part of MediaWiki; it will be deprecated and removed once acto...
Service for temporary user creation.
Creates User objects.
Service for formatting and validating API parameters.
addTables( $tables, $alias=null)
addWhere( $conds)
addFields( $fields)