39 'anonedits' =>
'anonymous',
41 'revertededits' =>
'reverted'
96 $this->user = RequestContext::getMain()->getUser();
100 return self::DEPRECATED_COUNT_TYPES[
$type] ??
$type;
115 if ( $params[
'from'] || $params[
'to'] ) {
116 if (
$type ===
'edits' ||
$type ===
'editors' ) {
117 if ( !$params[
'from'] || !$params[
'to'] ) {
119 new MessageValue(
'rest-pagehistorycount-parameters-invalid' ),
125 new MessageValue(
'rest-pagehistorycount-parameters-invalid' ),
142 if ( !$titleObj || !$titleObj->getArticleID() ) {
151 if ( !$this->permissionManager->userCan(
'read', $this->user, $titleObj ) ) {
160 $count = $this->
getCount( $normalizedType );
161 $countLimit = self::COUNT_LIMITS[$normalizedType];
163 'count' => $count > $countLimit ? $countLimit : $count,
164 'limit' => $count > $countLimit
166 $response->setHeader(
'Cache-Control',
'max-age=' . self::MAX_AGE_200 );
169 if ( isset( self::DEPRECATED_COUNT_TYPES[
$type] ) ) {
170 $docs =
'<https://www.mediawiki.org/wiki/API:REST/History_API' .
171 '#Get_page_history_counts>; rel="deprecation"';
172 $response->setHeader(
'Deprecation',
'version="v1"' );
173 $response->setHeader(
'Link', $docs );
185 $pageId = $this->
getTitle()->getArticleID();
204 if ( $from || $to ) {
220 if ( $from || $to ) {
249 if ( $editsCount > self::COUNT_LIMITS[
$type] * 2 ) {
251 new MessageValue(
'rest-pagehistorycount-too-many-revisions' ),
264 new MessageValue(
'rest-pagehistorycount-type-unrecognized',
276 if ( $this->revision ===
null ) {
279 $this->revision = $this->revisionStore->getKnownCurrentRevision(
$title );
281 $this->revision =
false;
291 if ( $this->titleObject ===
null ) {
318 if ( !$currentRev ) {
321 if ( $this->lastModifiedTimes ===
null ) {
322 $currentRevTime = (int)
wfTimestampOrNull( TS_UNIX, $currentRev->getTimestamp() );
324 $this->lastModifiedTimes = [
325 'currentRevTS' => $currentRevTime,
326 'dependencyModTS' => $loggingTableTime
338 $res = $this->loadBalancer->getConnectionRef(
DB_REPLICA )->selectField(
340 'MAX(log_timestamp)',
341 [
'log_page' => $pageId ],
370 $pageId = $titleObj->getArticleID();
371 return $this->cache->getWithSetCallback(
372 $this->cache->makeKey(
'rest',
'pagehistorycount', $pageId,
$type ),
373 WANObjectCache::TTL_WEEK,
374 function ( $oldValue ) use ( $fetchCount ) {
378 $doIncrementalUpdate = (
381 if ( $doIncrementalUpdate ) {
382 $rev = $this->revisionStore->getRevisionById( $oldValue[
'revision'] );
384 $additionalCount = $fetchCount( $rev );
386 'revision' => $currentRev->getId(),
387 'count' => $oldValue[
'count'] + $additionalCount,
396 'revision' => $currentRev->getId(),
397 'count' => $fetchCount(),
402 'touchedCallback' =>
function (){
406 'lockTSE' => WANObjectCache::TTL_MINUTE * 5
420 'rev_page' => $pageId,
421 'actor_user IS NULL',
422 $dbr->bitAnd(
'rev_deleted',
423 RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_USER ) .
" = 0"
427 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
428 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
429 "OR rev_timestamp > {$oldTs}";
432 $edits =
$dbr->selectRowCount(
434 'revision_actor_temp',
441 [
'LIMIT' => self::COUNT_LIMITS[
'anonymous'] + 1 ],
445 'revactor_rev = rev_id AND revactor_page = rev_page'
449 'revactor_actor = actor_id'
465 'rev_page=' . intval( $pageId ),
466 $dbr->bitAnd(
'rev_deleted',
467 RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_USER ) .
" = 0",
473 'actor.actor_user = ug_user',
474 'ug_group' => $this->permissionManager->getGroupsWithPermission(
'bot' ),
475 'ug_expiry IS NULL OR ug_expiry >= ' .
$dbr->addQuotes(
$dbr->timestamp() )
482 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
483 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
484 "OR rev_timestamp > {$oldTs}";
487 $edits =
$dbr->selectRowCount(
489 'revision_actor_temp',
496 [
'LIMIT' => self::COUNT_LIMITS[
'bot'] + 1 ],
500 'revactor_rev = rev_id AND revactor_page = rev_page'
504 'revactor_actor = actor_id'
521 list( $fromRev, $toRev ) = $this->
orderRevisions( $fromRev, $toRev );
522 return $this->revisionStore->countAuthorsBetween( $pageId, $fromRev,
523 $toRev, $this->user, self::COUNT_LIMITS[
'editors'] );
534 foreach ( self::REVERTED_TAG_NAMES as $tagName ) {
536 $tagIds[] = $this->changeTagDefStore->getId( $tagName );
548 'rev_page' => $pageId,
549 $dbr->bitAnd(
'rev_deleted', RevisionRecord::DELETED_TEXT ) .
" = 0"
552 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
553 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
554 "OR rev_timestamp > {$oldTs}";
556 $edits =
$dbr->selectRowCount(
562 [
'rev_page' => $pageId ],
565 'LIMIT' => self::COUNT_LIMITS[
'reverted'] + 1,
566 'GROUP BY' =>
'rev_id'
572 'ct_rev_id = rev_id',
573 'ct_tag_id' => $tagIds,
589 'rev_page' => $pageId,
590 'rev_minor_edit != 0',
591 $dbr->bitAnd(
'rev_deleted', RevisionRecord::DELETED_TEXT ) .
" = 0"
594 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
595 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
596 "OR rev_timestamp > {$oldTs}";
598 $edits =
$dbr->selectRowCount(
'revision',
'1',
601 [
'LIMIT' => self::COUNT_LIMITS[
'minor'] + 1 ]
618 list( $fromRev, $toRev ) = $this->
orderRevisions( $fromRev, $toRev );
619 return $this->revisionStore->countRevisionsBetween(
623 self::COUNT_LIMITS[
'edits']
633 $rev = $this->revisionStore->getRevisionById( $revId );
636 new MessageValue(
'rest-nonexistent-revision', [ $revId ] ),
654 if ( $fromRev && $toRev && ( $fromRev->getTimestamp() > $toRev->getTimestamp() ||
655 ( $fromRev->getTimestamp() === $toRev->getTimestamp()
656 && $fromRev->getId() > $toRev->getId() ) )
658 return [ $toRev, $fromRev ];
660 return [ $fromRev, $toRev ];
670 self::PARAM_SOURCE =>
'path',
671 ParamValidator::PARAM_TYPE =>
'string',
672 ParamValidator::PARAM_REQUIRED =>
true,
675 self::PARAM_SOURCE =>
'path',
676 ParamValidator::PARAM_TYPE => array_merge(
677 array_keys( self::COUNT_LIMITS ),
678 array_keys( self::DEPRECATED_COUNT_TYPES )
680 ParamValidator::PARAM_REQUIRED =>
true,
683 self::PARAM_SOURCE =>
'query',
684 ParamValidator::PARAM_TYPE =>
'integer',
685 ParamValidator::PARAM_REQUIRED => false
688 self::PARAM_SOURCE =>
'query',
689 ParamValidator::PARAM_TYPE =>
'integer',
690 ParamValidator::PARAM_REQUIRED => false
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Handler class for Core REST API endpoints that perform operations on revisions.
RevisionRecord bool $revision
const DEPRECATED_COUNT_TYPES
validateParameterCombination( $type)
Validates that the provided parameter combination is supported.
RevisionStore $revisionStore
getAnonCount( $pageId, RevisionRecord $fromRev=null)
getCachedCount( $type, callable $fetchCount)
loggingTableTime( $pageId)
Return timestamp of latest entry in logging table for given page id.
getMinorCount( $pageId, RevisionRecord $fromRev=null)
getLastModifiedTimes()
Returns array with 2 timestamps:
getEtag()
Choosing to not implement etags in this handler.
getRevisionOrThrow( $revId)
__construct(RevisionStore $revisionStore, NameTableStoreFactory $nameTableStoreFactory, PermissionManager $permissionManager, ILoadBalancer $loadBalancer, WANObjectCache $cache)
orderRevisions(RevisionRecord $fromRev=null, RevisionRecord $toRev=null)
Reorders revisions if they are present.
const COUNT_LIMITS
The maximum number of counts to return per type of revision.
getBotCount( $pageId, RevisionRecord $fromRev=null)
needsWriteAccess()
Indicates whether this route requires write access.
getRevertedCount( $pageId, RevisionRecord $fromRev=null)
getEditorsCount( $pageId, RevisionRecord $fromRev=null, RevisionRecord $toRev=null)
getParamSettings()
Fetch ParamValidator settings for parameters.
PermissionManager $permissionManager
ILoadBalancer $loadBalancer
getLastModified()
Returns latest of 2 timestamps:
NameTableStore $changeTagDefStore
getEditsCount( $pageId, RevisionRecord $fromRev=null, RevisionRecord $toRev=null)
Group all the pieces relevant to the context of a request into one instance @newable.
Represents a title within MediaWiki.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Multi-datacenter aware caching interface.