40 'anonedits' =>
'anonymous',
42 'revertededits' =>
'reverted'
105 return self::DEPRECATED_COUNT_TYPES[
$type] ??
$type;
120 if ( $params[
'from'] || $params[
'to'] ) {
121 if (
$type ===
'edits' ||
$type ===
'editors' ) {
122 if ( !$params[
'from'] || !$params[
'to'] ) {
124 new MessageValue(
'rest-pagehistorycount-parameters-invalid' ),
130 new MessageValue(
'rest-pagehistorycount-parameters-invalid' ),
165 $count = $this->
getCount( $normalizedType );
166 $countLimit = self::COUNT_LIMITS[$normalizedType];
168 'count' => $count > $countLimit ? $countLimit : $count,
169 'limit' => $count > $countLimit
171 $response->setHeader(
'Cache-Control',
'max-age=' . self::MAX_AGE_200 );
174 if ( isset( self::DEPRECATED_COUNT_TYPES[
$type] ) ) {
175 $docs =
'<https://www.mediawiki.org/wiki/API:REST/History_API' .
176 '#Get_page_history_counts>; rel="deprecation"';
177 $response->setHeader(
'Deprecation',
'version="v1"' );
178 $response->setHeader(
'Link', $docs );
190 $pageId = $this->
getPage()->getId();
209 if ( $from || $to ) {
225 if ( $from || $to ) {
254 if ( $editsCount > self::COUNT_LIMITS[
$type] * 2 ) {
256 new MessageValue(
'rest-pagehistorycount-too-many-revisions' ),
269 new MessageValue(
'rest-pagehistorycount-type-unrecognized',
281 if ( $this->revision === false ) {
284 $this->revision = $this->revisionStore->getKnownCurrentRevision(
$page ) ?:
null;
286 $this->revision =
null;
296 if ( $this->page === false ) {
297 $this->page = $this->pageLookup->getExistingPageByText(
311 $lastModifiedTimes = $this->getLastModifiedTimes();
312 if ( $lastModifiedTimes ) {
313 return max( array_values( $lastModifiedTimes ) );
325 $currentRev = $this->getCurrentRevision();
326 if ( !$currentRev ) {
329 if ( $this->lastModifiedTimes ===
null ) {
330 $currentRevTime = (int)
wfTimestampOrNull( TS_UNIX, $currentRev->getTimestamp() );
331 $loggingTableTime = $this->loggingTableTime( $currentRev->getPageId() );
332 $this->lastModifiedTimes = [
333 'currentRevTS' => $currentRevTime,
334 'dependencyModTS' => $loggingTableTime
337 return $this->lastModifiedTimes;
346 $res = $this->loadBalancer->getConnectionRef(
DB_REPLICA )->selectField(
348 'MAX(log_timestamp)',
349 [
'log_page' => $pageId ],
377 $pageId = $this->getPage()->getId();
378 return $this->cache->getWithSetCallback(
379 $this->cache->makeKey(
'rest',
'pagehistorycount', $pageId,
$type ),
380 WANObjectCache::TTL_WEEK,
381 function ( $oldValue ) use ( $fetchCount ) {
382 $currentRev = $this->getCurrentRevision();
385 $doIncrementalUpdate = (
386 $this->getLastModified() != $this->getLastModifiedTimes()[
'dependencyModTS']
388 if ( $doIncrementalUpdate ) {
389 $rev = $this->revisionStore->getRevisionById( $oldValue[
'revision'] );
391 $additionalCount = $fetchCount( $rev );
393 'revision' => $currentRev->getId(),
394 'count' => $oldValue[
'count'] + $additionalCount,
395 'dependencyModTS' => $this->getLastModifiedTimes()[
'dependencyModTS']
403 'revision' => $currentRev->getId(),
404 'count' => $fetchCount(),
405 'dependencyModTS' => $this->getLastModifiedTimes()[
'dependencyModTS']
409 'touchedCallback' =>
function (){
410 return $this->getLastModified();
413 'lockTSE' => WANObjectCache::TTL_MINUTE * 5
426 $revQuery = $this->actorMigration->getJoin(
'rev_user' );
429 'rev_page' => $pageId,
430 'actor_user IS NULL',
431 $dbr->bitAnd(
'rev_deleted',
432 RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_USER ) .
" = 0"
436 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
437 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
438 "OR rev_timestamp > {$oldTs}";
443 if ( isset(
$revQuery[
'tables'][
'temp_rev_user'] ) ) {
445 "temp_rev_user.revactor_rev = rev_id AND revactor_page = rev_page";
448 $edits =
$dbr->selectRowCount(
455 [
'LIMIT' => self::COUNT_LIMITS[
'anonymous'] + 1 ],
469 $revQuery = $this->actorMigration->getJoin(
'rev_user' );
473 if ( isset(
$revQuery[
'tables'][
'temp_rev_user'] ) ) {
475 "temp_rev_user.revactor_rev = rev_id AND revactor_page = rev_page";
479 'rev_page=' . intval( $pageId ),
480 $dbr->bitAnd(
'rev_deleted',
481 RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_USER ) .
" = 0",
487 $revQuery[
'fields'][
'rev_user'] .
' = ug_user',
488 'ug_group' => $this->groupPermissionsLookup->getGroupsWithPermission(
'bot' ),
489 'ug_expiry IS NULL OR ug_expiry >= ' .
$dbr->addQuotes(
$dbr->timestamp() )
496 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
497 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
498 "OR rev_timestamp > {$oldTs}";
501 $edits =
$dbr->selectRowCount(
508 [
'LIMIT' => self::COUNT_LIMITS[
'bot'] + 1 ],
524 list( $fromRev, $toRev ) = $this->orderRevisions( $fromRev, $toRev );
525 return $this->revisionStore->countAuthorsBetween( $pageId, $fromRev,
526 $toRev, $this->
getAuthority(), self::COUNT_LIMITS[
'editors'] );
539 $tagIds[] = $this->changeTagDefStore->getId( $tagName );
551 'rev_page' => $pageId,
552 $dbr->bitAnd(
'rev_deleted', RevisionRecord::DELETED_TEXT ) .
" = 0"
555 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
556 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
557 "OR rev_timestamp > {$oldTs}";
559 $edits =
$dbr->selectRowCount(
565 [
'rev_page' => $pageId ],
568 'LIMIT' => self::COUNT_LIMITS[
'reverted'] + 1,
569 'GROUP BY' =>
'rev_id'
575 'ct_rev_id = rev_id',
576 'ct_tag_id' => $tagIds,
592 'rev_page' => $pageId,
593 'rev_minor_edit != 0',
594 $dbr->bitAnd(
'rev_deleted', RevisionRecord::DELETED_TEXT ) .
" = 0"
597 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
598 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
599 "OR rev_timestamp > {$oldTs}";
601 $edits =
$dbr->selectRowCount(
'revision',
'1',
604 [
'LIMIT' => self::COUNT_LIMITS[
'minor'] + 1 ]
621 list( $fromRev, $toRev ) = $this->orderRevisions( $fromRev, $toRev );
622 return $this->revisionStore->countRevisionsBetween(
626 self::COUNT_LIMITS[
'edits']
636 $rev = $this->revisionStore->getRevisionById( $revId );
639 new MessageValue(
'rest-nonexistent-revision', [ $revId ] ),
657 if ( $fromRev && $toRev && ( $fromRev->getTimestamp() > $toRev->getTimestamp() ||
658 ( $fromRev->getTimestamp() === $toRev->getTimestamp()
659 && $fromRev->getId() > $toRev->getId() ) )
661 return [ $toRev, $fromRev ];
663 return [ $fromRev, $toRev ];
673 self::PARAM_SOURCE =>
'path',
674 ParamValidator::PARAM_TYPE =>
'string',
675 ParamValidator::PARAM_REQUIRED =>
true,
678 self::PARAM_SOURCE =>
'path',
679 ParamValidator::PARAM_TYPE => array_merge(
680 array_keys( self::COUNT_LIMITS ),
681 array_keys( self::DEPRECATED_COUNT_TYPES )
683 ParamValidator::PARAM_REQUIRED =>
true,
686 self::PARAM_SOURCE =>
'query',
687 ParamValidator::PARAM_TYPE =>
'integer',
688 ParamValidator::PARAM_REQUIRED => false
691 self::PARAM_SOURCE =>
'query',
692 ParamValidator::PARAM_TYPE =>
'integer',
693 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.
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
This is not intended to be a long-term part of MediaWiki; it will be deprecated and removed once acto...
Handler class for Core REST API endpoints that perform operations on revisions.
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)
__construct(RevisionStore $revisionStore, NameTableStoreFactory $nameTableStoreFactory, GroupPermissionsLookup $groupPermissionsLookup, ILoadBalancer $loadBalancer, WANObjectCache $cache, PageLookup $pageLookup, ActorMigration $actorMigration)
RevisionRecord false null $revision
getLastModifiedTimes()
Returns array with 2 timestamps:
getEtag()
Choosing to not implement etags in this handler.
getRevisionOrThrow( $revId)
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.
GroupPermissionsLookup $groupPermissionsLookup
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.
ExistingPageRecord false null $page
ILoadBalancer $loadBalancer
getLastModified()
Returns latest of 2 timestamps:
ActorMigration $actorMigration
NameTableStore $changeTagDefStore
getEditsCount( $pageId, RevisionRecord $fromRev=null, RevisionRecord $toRev=null)
Multi-datacenter aware caching interface.
Service interface for looking up infermation about wiki pages.