40 'anonedits' =>
'anonymous',
42 'revertededits' =>
'reverted'
99 return self::DEPRECATED_COUNT_TYPES[
$type] ??
$type;
114 if ( $params[
'from'] || $params[
'to'] ) {
115 if (
$type ===
'edits' ||
$type ===
'editors' ) {
116 if ( !$params[
'from'] || !$params[
'to'] ) {
118 new MessageValue(
'rest-pagehistorycount-parameters-invalid' ),
124 new MessageValue(
'rest-pagehistorycount-parameters-invalid' ),
141 if ( !$titleObj || !$titleObj->getArticleID() ) {
150 if ( !$this->permissionManager->userCan(
'read', $this->user, $titleObj ) ) {
159 $count = $this->
getCount( $normalizedType );
160 $countLimit = self::COUNT_LIMITS[$normalizedType];
162 'count' => $count > $countLimit ? $countLimit : $count,
163 'limit' => $count > $countLimit
165 $response->setHeader(
'Cache-Control',
'max-age=' . self::MAX_AGE_200 );
168 if ( isset( self::DEPRECATED_COUNT_TYPES[
$type] ) ) {
169 $docs =
'<https://www.mediawiki.org/wiki/API:REST/History_API' .
170 '#Get_page_history_counts>; rel="deprecation"';
171 $response->setHeader(
'Deprecation',
'version="v1"' );
172 $response->setHeader(
'Link', $docs );
184 $pageId = $this->
getTitle()->getArticleID();
203 if ( $from || $to ) {
219 if ( $from || $to ) {
248 if ( $editsCount > self::COUNT_LIMITS[
$type] * 2 ) {
250 new MessageValue(
'rest-pagehistorycount-too-many-revisions' ),
263 new MessageValue(
'rest-pagehistorycount-type-unrecognized',
275 if ( $this->revision ===
null ) {
278 $this->revision = $this->revisionStore->getKnownCurrentRevision(
$title );
280 $this->revision =
false;
290 if ( $this->titleObject ===
null ) {
317 if ( !$currentRev ) {
320 if ( $this->lastModifiedTimes ===
null ) {
321 $currentRevTime = (int)
wfTimestampOrNull( TS_UNIX, $currentRev->getTimestamp() );
323 $this->lastModifiedTimes = [
324 'currentRevTS' => $currentRevTime,
325 'dependencyModTS' => $loggingTableTime
337 $res = $this->loadBalancer->getConnectionRef(
DB_REPLICA )->selectField(
339 'MAX(log_timestamp)',
340 [
'log_page' => $pageId ],
369 $pageId = $titleObj->getArticleID();
370 return $this->cache->getWithSetCallback(
371 $this->cache->makeKey(
'rest',
'pagehistorycount', $pageId,
$type ),
372 WANObjectCache::TTL_WEEK,
373 function ( $oldValue ) use ( $fetchCount ) {
377 $doIncrementalUpdate = (
380 if ( $doIncrementalUpdate ) {
381 $rev = $this->revisionStore->getRevisionById( $oldValue[
'revision'] );
383 $additionalCount = $fetchCount( $rev );
385 'revision' => $currentRev->getId(),
386 'count' => $oldValue[
'count'] + $additionalCount,
395 'revision' => $currentRev->getId(),
396 'count' => $fetchCount(),
401 'touchedCallback' =>
function (){
405 'lockTSE' => WANObjectCache::TTL_MINUTE * 5
419 'rev_page' => $pageId,
420 'actor_user IS NULL',
421 $dbr->bitAnd(
'rev_deleted',
426 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
427 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
428 "OR rev_timestamp > {$oldTs}";
431 $edits =
$dbr->selectRowCount(
433 'revision_actor_temp',
440 [
'LIMIT' => self::COUNT_LIMITS[
'anonymous'] + 1 ],
444 'revactor_rev = rev_id AND revactor_page = rev_page'
448 'revactor_actor = actor_id'
464 'rev_page=' . intval( $pageId ),
465 $dbr->bitAnd(
'rev_deleted',
472 'actor.actor_user = ug_user',
473 'ug_group' => $this->permissionManager->getGroupsWithPermission(
'bot' ),
474 'ug_expiry IS NULL OR ug_expiry >= ' .
$dbr->addQuotes(
$dbr->timestamp() )
481 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
482 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
483 "OR rev_timestamp > {$oldTs}";
486 $edits =
$dbr->selectRowCount(
488 'revision_actor_temp',
495 [
'LIMIT' => self::COUNT_LIMITS[
'bot'] + 1 ],
499 'revactor_rev = rev_id AND revactor_page = rev_page'
503 'revactor_actor = actor_id'
520 list( $fromRev, $toRev ) = $this->
orderRevisions( $fromRev, $toRev );
521 return $this->revisionStore->countAuthorsBetween( $pageId, $fromRev,
522 $toRev, $this->user, self::COUNT_LIMITS[
'editors'] );
535 $tagIds[] = $this->changeTagDefStore->getId( $tagName );
547 'rev_page' => $pageId,
551 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
552 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
553 "OR rev_timestamp > {$oldTs}";
555 $edits =
$dbr->selectRowCount(
561 [
'rev_page' => $pageId ],
564 'LIMIT' => self::COUNT_LIMITS[
'reverted'] + 1,
565 'GROUP BY' =>
'rev_id'
571 'ct_rev_id = rev_id',
572 'ct_tag_id' => $tagIds,
588 'rev_page' => $pageId,
589 'rev_minor_edit != 0',
593 $oldTs =
$dbr->addQuotes(
$dbr->timestamp( $fromRev->getTimestamp() ) );
594 $cond[] =
"(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
595 "OR rev_timestamp > {$oldTs}";
597 $edits =
$dbr->selectRowCount(
'revision',
'1',
600 [
'LIMIT' => self::COUNT_LIMITS[
'minor'] + 1 ]
617 list( $fromRev, $toRev ) = $this->
orderRevisions( $fromRev, $toRev );
618 return $this->revisionStore->countRevisionsBetween(
622 self::COUNT_LIMITS[
'edits']
632 $rev = $this->revisionStore->getRevisionById( $revId );
635 new MessageValue(
'rest-nonexistent-revision', [ $revId ] ),
653 if ( $fromRev && $toRev && ( $fromRev->getTimestamp() > $toRev->getTimestamp() ||
654 ( $fromRev->getTimestamp() === $toRev->getTimestamp()
655 && $fromRev->getId() > $toRev->getId() ) )
657 return [ $toRev, $fromRev ];
659 return [ $fromRev, $toRev ];
669 self::PARAM_SOURCE =>
'path',
670 ParamValidator::PARAM_TYPE =>
'string',
671 ParamValidator::PARAM_REQUIRED =>
true,
674 self::PARAM_SOURCE =>
'path',
675 ParamValidator::PARAM_TYPE => array_merge(
676 array_keys( self::COUNT_LIMITS ),
677 array_keys( self::DEPRECATED_COUNT_TYPES )
679 ParamValidator::PARAM_REQUIRED =>
true,
682 self::PARAM_SOURCE =>
'query',
683 ParamValidator::PARAM_TYPE =>
'integer',
684 ParamValidator::PARAM_REQUIRED => false
687 self::PARAM_SOURCE =>
'query',
688 ParamValidator::PARAM_TYPE =>
'integer',
689 ParamValidator::PARAM_REQUIRED => false