30 private const REVISIONS_RETURN_LIMIT = 20;
31 private const ALLOWED_FILTER_TYPES = [
'anonymous',
'bot',
'reverted',
'minor' ];
34 private $revisionStore;
37 private $changeTagDefStore;
40 private $groupPermissionsLookup;
43 private $loadBalancer;
49 private $titleFormatter;
54 private $page =
false;
74 $this->revisionStore = $revisionStore;
76 $this->groupPermissionsLookup = $groupPermissionsLookup;
77 $this->loadBalancer = $loadBalancer;
78 $this->pageLookup = $pageLookup;
79 $this->titleFormatter = $titleFormatter;
86 if ( $this->page === false ) {
87 $this->page = $this->pageLookup->getExistingPageByText(
105 if ( $params[
'older_than'] !==
null && $params[
'newer_than'] !==
null ) {
107 new MessageValue(
'rest-pagehistory-incompatible-params' ), 400 );
110 if ( ( $params[
'older_than'] !==
null && $params[
'older_than'] < 1 ) ||
111 ( $params[
'newer_than'] !==
null && $params[
'newer_than'] < 1 )
114 new MessageValue(
'rest-pagehistory-param-range-error' ), 400 );
118 if ( $params[
'filter'] ===
'reverted' ) {
121 $tagIds[] = $this->changeTagDefStore->getId( $tagName );
128 $page = $this->getPage();
137 if ( !$this->
getAuthority()->authorizeRead(
'read', $page ) ) {
145 $relativeRevId = $params[
'older_than'] ?? $params[
'newer_than'] ?? 0;
146 if ( $relativeRevId ) {
148 $rev = $this->revisionStore->getRevisionByPageId(
160 $ts = $rev->getTimestamp();
161 if ( $ts ===
null ) {
173 $res = $this->getDbResults( $page, $params, $relativeRevId, $ts, $tagIds );
174 $response = $this->processDbResults(
$res, $page, $params );
186 private function getDbResults(
ExistingPageRecord $page, array $params, $relativeRevId, $ts, $tagIds ) {
188 $revQuery = $this->revisionStore->getQueryInfo();
190 'rev_page' => $page->
getId()
193 if ( $params[
'filter'] ) {
196 if ( isset(
$revQuery[
'tables'][
'temp_rev_user'] ) ) {
198 "temp_rev_user.revactor_rev = rev_id AND revactor_page = rev_page";
202 switch ( $params[
'filter'] ) {
204 $cond[] =
'EXISTS(' .
$dbr->selectSQLText(
208 'actor_rev_user.actor_user = ug_user',
209 'ug_group' => $this->groupPermissionsLookup->getGroupsWithPermission(
'bot' ),
210 'ug_expiry IS NULL OR ug_expiry >= ' .
$dbr->addQuotes(
$dbr->timestamp() )
214 $bitmask = $this->getBitmask();
216 $cond[] =
$dbr->bitAnd(
'rev_deleted', $bitmask ) .
" != $bitmask";
221 $cond[] =
"actor_user IS NULL";
222 $bitmask = $this->getBitmask();
224 $cond[] =
$dbr->bitAnd(
'rev_deleted', $bitmask ) .
" != $bitmask";
232 $cond[] =
'EXISTS(' .
$dbr->selectSQLText(
235 [
'ct_rev_id = rev_id',
'ct_tag_id' => $tagIds ],
241 $cond[] =
'rev_minor_edit != 0';
246 if ( $relativeRevId ) {
247 $op = $params[
'older_than'] ?
'<' :
'>';
248 $sort = $params[
'older_than'] ?
'DESC' :
'ASC';
249 $ts =
$dbr->addQuotes(
$dbr->timestamp( $ts ) );
250 $cond[] =
"rev_timestamp $op $ts OR " .
251 "(rev_timestamp = $ts AND rev_id $op $relativeRevId)";
252 $orderBy =
"rev_timestamp $sort, rev_id $sort";
254 $orderBy =
"rev_timestamp DESC, rev_id DESC";
258 $limit = self::REVISIONS_RETURN_LIMIT + 1;
267 'ORDER BY' => $orderBy,
283 private function getBitmask() {
284 if ( !$this->
getAuthority()->isAllowed(
'deletedhistory' ) ) {
285 $bitmask = RevisionRecord::DELETED_USER;
286 } elseif ( !$this->
getAuthority()->isAllowedAny(
'suppressrevision',
'viewsuppressed' ) ) {
287 $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
300 private function processDbResults(
$res, $page, $params ) {
305 foreach (
$res as $row ) {
306 $rev = $this->revisionStore->newRevisionFromRow(
308 IDBAccessObject::READ_NORMAL,
312 $firstRevId = $row->rev_id;
314 $lastRevId = $row->rev_id;
317 'id' => $rev->getId(),
318 'timestamp' =>
wfTimestamp( TS_ISO_8601, $rev->getTimestamp() ),
319 'minor' => $rev->isMinor(),
320 'size' => $rev->getSize()
325 $sizes[$rev->getId()] = $rev->getSize();
326 $parentId = $rev->getParentId();
328 $revision[
'parent_id'] = $parentId;
331 $comment = $rev->getComment( RevisionRecord::FOR_THIS_USER, $this->
getAuthority() );
332 $revision[
'comment'] = $comment ? $comment->text :
null;
334 $revUser = $rev->getUser( RevisionRecord::FOR_THIS_USER, $this->
getAuthority() );
336 $revision[
'user'] = [
337 'id' => $revUser->isRegistered() ? $revUser->getId() :
null,
338 'name' => $revUser->getName()
341 $revision[
'user'] =
null;
344 $revisions[] = $revision;
347 if ( count( $revisions ) == self::REVISIONS_RETURN_LIMIT ) {
354 foreach ( $revisions as $revision ) {
355 if ( isset( $revision[
'parent_id'] ) && !isset( $sizes[$revision[
'parent_id']] ) ) {
356 $unknownSizes[] = $revision[
'parent_id'];
359 if ( $unknownSizes ) {
360 $sizes += $this->revisionStore->getRevisionSizes( $unknownSizes );
362 foreach ( $revisions as &$revision ) {
363 $revision[
'delta'] =
null;
364 if ( isset( $revision[
'parent_id'] ) ) {
365 if ( isset( $sizes[$revision[
'parent_id']] ) ) {
366 $revision[
'delta'] = $revision[
'size'] - $sizes[$revision[
'parent_id']];
370 unset( $revision[
'parent_id'] );
374 if ( $revisions && $params[
'newer_than'] ) {
375 $revisions = array_reverse( $revisions );
381 $lastRevId = $firstRevId;
387 'revisions' => $revisions
393 if ( $params[
'newer_than'] ||
$res->numRows() > self::REVISIONS_RETURN_LIMIT ) {
398 if ( $params[
'older_than'] ||
399 ( $params[
'newer_than'] &&
$res->numRows() > self::REVISIONS_RETURN_LIMIT )
403 $newer = $firstRevId;
409 if ( isset( $params[
'filter'] ) ) {
410 $queryParts[
'filter'] = $params[
'filter'];
413 $pathParams = [
'title' => $this->titleFormatter->getPrefixedDBkey( $page ) ];
415 $response[
'latest'] = $this->
getRouteUrl( $pathParams, $queryParts );
417 if ( isset( $older ) ) {
419 $this->
getRouteUrl( $pathParams, $queryParts + [
'older_than' => $older ] );
421 if ( isset( $newer ) ) {
423 $this->
getRouteUrl( $pathParams, $queryParts + [
'newer_than' => $newer ] );
436 self::PARAM_SOURCE =>
'path',
437 ParamValidator::PARAM_TYPE =>
'string',
438 ParamValidator::PARAM_REQUIRED =>
true,
441 self::PARAM_SOURCE =>
'query',
442 ParamValidator::PARAM_TYPE =>
'integer',
443 ParamValidator::PARAM_REQUIRED =>
false,
446 self::PARAM_SOURCE =>
'query',
447 ParamValidator::PARAM_TYPE =>
'integer',
448 ParamValidator::PARAM_REQUIRED =>
false,
451 self::PARAM_SOURCE =>
'query',
452 ParamValidator::PARAM_TYPE => self::ALLOWED_FILTER_TYPES,
453 ParamValidator::PARAM_REQUIRED =>
false,
464 $page = $this->getPage();
478 $page = $this->getPage();
483 $rev = $this->revisionStore->getKnownCurrentRevision( $page );
484 return $rev->getTimestamp();
491 return (
bool)$this->getPage();
__construct(RevisionStore $revisionStore, NameTableStoreFactory $nameTableStoreFactory, GroupPermissionsLookup $groupPermissionsLookup, ILoadBalancer $loadBalancer, PageLookup $pageLookup, TitleFormatter $titleFormatter)
RevisionStore $revisionStore.