API: Update query modules for MCR
authorBrad Jorsch <bjorsch@wikimedia.org>
Fri, 16 Feb 2018 18:23:45 +0000 (13:23 -0500)
committerCicalese <ccicalese@wikimedia.org>
Fri, 27 Jul 2018 23:33:45 +0000 (23:33 +0000)
MCR deprecated the Revision class in favor of the broadly similar
RevisionRecord, and more interestingly added the concept of multiple
content "slots" to revisions.

Thus, prop=revisions, prop=deletedrevisions, and so on gain a parameter
to specify which slots are wanted. When this new parameter is not
specified (and any content-related props are specified), a warning about
the legacy format will be issued.

The rest of the modules just needed to call methods or use constants on
RevisionRecord instead of Revision. ApiQueryDeletedrevs wasn't touched,
since it has been deprecated since 1.25 anyway.

This also updates a few non-query modules that don't depend on details
of editing, diffing, or viewing MCR revisions that haven't been figured
out yet.

Bug: T200568
Change-Id: I1327d1784f5cedb006cd74df834cf9a560a77a5d

21 files changed:
RELEASE-NOTES-1.32
includes/Storage/RevisionRecord.php
includes/api/ApiExpandTemplates.php
includes/api/ApiFeedContributions.php
includes/api/ApiPatrol.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryAllRevisions.php
includes/api/ApiQueryContributors.php
includes/api/ApiQueryDeletedRevisions.php
includes/api/ApiQueryFilearchive.php
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQueryRevisions.php
includes/api/ApiQueryRevisionsBase.php
includes/api/ApiQueryUserContribs.php
includes/api/ApiQueryWatchlist.php
includes/api/ApiRevisionDelete.php
includes/api/ApiSetNotificationTimestamp.php
includes/api/ApiTag.php
includes/api/i18n/en.json
includes/api/i18n/qqq.json
tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php

index e24ab83..ce0b5c5 100644 (file)
@@ -100,12 +100,32 @@ production.
 * 'missingparam' errors will now use the prefixed parameter name in the code
   and error text, e.g. "noxxfoo" and "The 'xxfoo' parameter must be set" rather
   than "nofoo" and "The 'foo' parameter must be set".
+* action=query&prop=revisions now takes a 'rvslots' parameter to indicate the
+  multi-content revision slots for which content should be returned. It also
+  has a new rvprop, 'roles', to indicate which roles have slots. A deprecation
+  warning will be issued if rvprop=content or rvprop=contentmodel are used
+  without rvslots.
+* The rvcontentformat parameter to action=query&prop=revisions has been
+  deprecated. Clients should be prepared to deal with the default format for
+  relevant models.
+* Use of the deprecated parameters rvexpandtemplates, rvgeneratexml, rvparse,
+  rvdiffto, rvdifftotext, rvdifftotextpst, rvcontentformat, or the deprecated
+  rvprop=parsetree is forbidden with the new 'rvslots' parameter.
+* action=query&prop=deletedrevisions, action=query&list=allrevisions, and
+  action=query&list=alldeletedrevisions are changed similarly to
+  &prop=revisions (see the three previous items).
 
 === Action API internal changes in 1.32 ===
 * Added 'ApiParseMakeOutputPage' hook.
 * Parameter names may no longer contain '{' or '}', as these are now used for
   templated parameters.
 * (T194950) Added 'ApiMaxLagInfo' hook.
+* Added 'ApiParseMakeOutputPage' hook.
+* The following methods now take a RevisionRecord rather than a Revision. No
+  external callers are known.
+  * ApiFeedContributions::feedItemAuthor()
+  * ApiFeedContributions::feedItemDesc()
+  * ApiQueryRevisionsBase::extractRevisionInfo()
 
 === Languages updated in 1.32 ===
 MediaWiki supports over 350 languages. Many localisations are updated regularly.
index 7d1b477..17c56ea 100644 (file)
@@ -453,7 +453,7 @@ abstract class RevisionRecord {
         *
         * @return bool
         */
-       protected function audienceCan( $field, $audience, User $user = null ) {
+       public function audienceCan( $field, $audience, User $user = null ) {
                if ( $audience == self::FOR_PUBLIC && $this->isDeleted( $field ) ) {
                        return false;
                } elseif ( $audience == self::FOR_THIS_USER ) {
index fe49b25..562bcdf 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * API module that functions as a shortcut to the wikitext preprocessor. Expands
  * any templates in a provided string, and returns the result of this expansion
@@ -48,7 +50,7 @@ class ApiExpandTemplates extends ApiBase {
 
                if ( $params['prop'] === null ) {
                        $this->addDeprecation(
-                               'apiwarn-deprecation-expandtemplates-prop', 'action=expandtemplates&!prop'
+                               [ 'apiwarn-deprecation-missingparam', 'prop' ], 'action=expandtemplates&!prop'
                        );
                        $prop = [];
                } else {
@@ -63,12 +65,12 @@ class ApiExpandTemplates extends ApiBase {
                // Get title and revision ID for parser
                $revid = $params['revid'];
                if ( $revid !== null ) {
-                       $rev = Revision::newFromId( $revid );
+                       $rev = MediaWikiServices::getInstance()->getRevisionStore()->getRevisionById( $revid );
                        if ( !$rev ) {
                                $this->dieWithError( [ 'apierror-nosuchrevid', $revid ] );
                        }
                        $pTitleObj = $titleObj;
-                       $titleObj = $rev->getTitle();
+                       $titleObj = Title::newFromLinkTarget( $rev->getPageAsLinkTarget() );
                        if ( $titleProvided ) {
                                if ( !$titleObj->equals( $pTitleObj ) ) {
                                        $this->addWarning( [ 'apierror-revwrongpage', $rev->getId(),
index 61a9035..92d504e 100644 (file)
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionAccessException;
+use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Storage\RevisionStore;
+
 /**
  * @ingroup API
  */
 class ApiFeedContributions extends ApiBase {
 
+       /** @var RevisionStore */
+       private $revisionStore;
+
        /**
         * This module uses a custom feed wrapper printer.
         *
@@ -35,6 +43,8 @@ class ApiFeedContributions extends ApiBase {
        }
 
        public function execute() {
+               $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
+
                $params = $this->extractRequestParams();
 
                $config = $this->getConfig();
@@ -130,7 +140,7 @@ class ApiFeedContributions extends ApiBase {
                if ( $title && $title->userCan( 'read', $this->getUser() ) ) {
                        $date = $row->rev_timestamp;
                        $comments = $title->getTalkPage()->getFullURL();
-                       $revision = Revision::newFromRow( $row );
+                       $revision = $this->revisionStore->newRevisionFromRow( $row );
 
                        return new FeedItem(
                                $title->getPrefixedText(),
@@ -146,21 +156,28 @@ class ApiFeedContributions extends ApiBase {
        }
 
        /**
-        * @param Revision $revision
+        * @since 1.32, takes a RevisionRecord instead of a Revision
+        * @param RevisionRecord $revision
         * @return string
         */
-       protected function feedItemAuthor( $revision ) {
-               return $revision->getUserText();
+       protected function feedItemAuthor( RevisionRecord $revision ) {
+               $user = $revision->getUser();
+               return $user ? $user->getName() : '';
        }
 
        /**
-        * @param Revision $revision
+        * @since 1.32, takes a RevisionRecord instead of a Revision
+        * @param RevisionRecord $revision
         * @return string
         */
-       protected function feedItemDesc( $revision ) {
+       protected function feedItemDesc( RevisionRecord $revision ) {
                if ( $revision ) {
                        $msg = wfMessage( 'colon-separator' )->inContentLanguage()->text();
-                       $content = $revision->getContent();
+                       try {
+                               $content = $revision->getContent( 'main' );
+                       } catch ( RevisionAccessException $e ) {
+                               $content = null;
+                       }
 
                        if ( $content instanceof TextContent ) {
                                // only textual content has a "source view".
@@ -173,8 +190,10 @@ class ApiFeedContributions extends ApiBase {
                                $html = '';
                        }
 
-                       return '<p>' . htmlspecialchars( $revision->getUserText() ) . $msg .
-                               htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) .
+                       $comment = $revision->getComment();
+
+                       return '<p>' . htmlspecialchars( $this->feedItemAuthor( $revision ) ) . $msg .
+                               htmlspecialchars( FeedItem::stripComment( $comment ? $comment->text : '' ) ) .
                                "</p>\n<hr />\n<div>" . $html . '</div>';
                }
 
index a20aca4..2b65f95 100644 (file)
@@ -22,6 +22,8 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * Allows user to patrol pages
  * @ingroup API
@@ -41,11 +43,12 @@ class ApiPatrol extends ApiBase {
                                $this->dieWithError( [ 'apierror-nosuchrcid', $params['rcid'] ] );
                        }
                } else {
-                       $rev = Revision::newFromId( $params['revid'] );
+                       $store = MediaWikiServices::getInstance()->getRevisionStore();
+                       $rev = $store->getRevisionById( $params['revid'] );
                        if ( !$rev ) {
                                $this->dieWithError( [ 'apierror-nosuchrevid', $params['revid'] ] );
                        }
-                       $rc = $rev->getRecentChange();
+                       $rc = $store->getRecentChange( $rev );
                        if ( !$rc ) {
                                $this->dieWithError( [ 'apierror-notpatrollable', $params['revid'] ] );
                        }
index 87f99dd..50afc7d 100644 (file)
@@ -23,6 +23,9 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Query module to enumerate all deleted revisions.
  *
@@ -45,6 +48,7 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                $user = $this->getUser();
                $db = $this->getDB();
                $params = $this->extractRequestParams( false );
+               $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
 
                $result = $this->getResult();
 
@@ -103,7 +107,7 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
 
                if ( $resultPageSet === null ) {
                        $this->parseParameters( $params );
-                       $arQuery = Revision::getArchiveQueryInfo();
+                       $arQuery = $revisionStore->getArchiveQueryInfo();
                        $this->addTables( $arQuery['tables'] );
                        $this->addJoinConds( $arQuery['joins'] );
                        $this->addFields( $arQuery['fields'] );
@@ -235,9 +239,9 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                        // (shouldn't be able to get here without 'deletedhistory', but
                        // check it again just in case)
                        if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                               $bitmask = Revision::DELETED_USER;
+                               $bitmask = RevisionRecord::DELETED_USER;
                        } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                               $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                        } else {
                                $bitmask = 0;
                        }
@@ -343,13 +347,13 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                                        $generated[] = $row->ar_rev_id;
                                }
                        } else {
-                               $revision = Revision::newFromArchiveRow( $row );
+                               $revision = $revisionStore->newRevisionFromArchiveRow( $row );
                                $rev = $this->extractRevisionInfo( $revision, $row );
 
                                if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) {
                                        $index = $nextIndex++;
                                        $pageMap[$row->ar_namespace][$row->ar_title] = $index;
-                                       $title = $revision->getTitle();
+                                       $title = Title::newFromLinkTarget( $revision->getPageAsLinkTarget() );
                                        $a = [
                                                'pageid' => $title->getArticleID(),
                                                'revisions' => [ $rev ],
index a0e71a5..833e2e4 100644 (file)
@@ -20,6 +20,9 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Query module to enumerate all revisions.
  *
@@ -39,6 +42,7 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
        protected function run( ApiPageSet $resultPageSet = null ) {
                $db = $this->getDB();
                $params = $this->extractRequestParams( false );
+               $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
 
                $result = $this->getResult();
 
@@ -63,7 +67,7 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
 
                if ( $resultPageSet === null ) {
                        $this->parseParameters( $params );
-                       $revQuery = Revision::getQueryInfo(
+                       $revQuery = $revisionStore->getQueryInfo(
                                $this->fetchContent ? [ 'page', 'text' ] : [ 'page' ]
                        );
                        $this->addTables( $revQuery['tables'] );
@@ -120,9 +124,9 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
                if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
                        // Paranoia: avoid brute force searches (T19342)
                        if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
-                               $bitmask = Revision::DELETED_USER;
+                               $bitmask = RevisionRecord::DELETED_USER;
                        } elseif ( !$this->getUser()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                               $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                        } else {
                                $bitmask = 0;
                        }
@@ -185,13 +189,13 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
                                        $generated[] = $row->rev_id;
                                }
                        } else {
-                               $revision = Revision::newFromRow( $row );
+                               $revision = $revisionStore->newRevisionFromRow( $row );
                                $rev = $this->extractRevisionInfo( $revision, $row );
 
                                if ( !isset( $pageMap[$row->rev_page] ) ) {
                                        $index = $nextIndex++;
                                        $pageMap[$row->rev_page] = $index;
-                                       $title = $revision->getTitle();
+                                       $title = Title::newFromLinkTarget( $revision->getPageAsLinkTarget() );
                                        $a = [
                                                'pageid' => $title->getArticleID(),
                                                'revisions' => [ $rev ],
index 6848fcb..e39afac 100644 (file)
@@ -23,6 +23,9 @@
  * @since 1.23
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * A query module to show contributors to a page
  *
@@ -75,7 +78,7 @@ class ApiQueryContributors extends ApiQueryBase {
                }
 
                $result = $this->getResult();
-               $revQuery = Revision::getQueryInfo();
+               $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo();
 
                // For MIGRATION_NEW, target indexes on the revision_actor_temp table.
                // Otherwise, revision is fine because it'll have to check all revision rows anyway.
@@ -94,7 +97,7 @@ class ApiQueryContributors extends ApiQueryBase {
                ] );
                $this->addWhereFld( $pageField, $pages );
                $this->addWhere( ActorMigration::newMigration()->isAnon( $revQuery['fields']['rev_user'] ) );
-               $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
+               $this->addWhere( $db->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER ) . ' = 0' );
                $this->addOption( 'GROUP BY', $pageField );
                $res = $this->select( __METHOD__ );
                foreach ( $res as $row ) {
@@ -126,7 +129,7 @@ class ApiQueryContributors extends ApiQueryBase {
                ] );
                $this->addWhereFld( $pageField, $pages );
                $this->addWhere( ActorMigration::newMigration()->isNotAnon( $revQuery['fields']['rev_user'] ) );
-               $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
+               $this->addWhere( $db->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER ) . ' = 0' );
                $this->addOption( 'GROUP BY', [ $pageField, $idField ] );
                $this->addOption( 'LIMIT', $params['limit'] + 1 );
 
index 1a1e8f7..c3af71b 100644 (file)
@@ -23,6 +23,9 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Query module to enumerate deleted revisions for pages.
  *
@@ -55,12 +58,13 @@ class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
                $params = $this->extractRequestParams( false );
 
                $db = $this->getDB();
+               $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
 
                $this->requireMaxOneParameter( $params, 'user', 'excludeuser' );
 
                if ( $resultPageSet === null ) {
                        $this->parseParameters( $params );
-                       $arQuery = Revision::getArchiveQueryInfo();
+                       $arQuery = $revisionStore->getArchiveQueryInfo();
                        $this->addTables( $arQuery['tables'] );
                        $this->addFields( $arQuery['fields'] );
                        $this->addJoinConds( $arQuery['joins'] );
@@ -132,9 +136,9 @@ class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
                        // (shouldn't be able to get here without 'deletedhistory', but
                        // check it again just in case)
                        if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                               $bitmask = Revision::DELETED_USER;
+                               $bitmask = RevisionRecord::DELETED_USER;
                        } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                               $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                        } else {
                                $bitmask = 0;
                        }
@@ -234,7 +238,7 @@ class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
 
                                $fit = $this->addPageSubItem(
                                        $pageMap[$row->ar_namespace][$row->ar_title],
-                                       $this->extractRevisionInfo( Revision::newFromArchiveRow( $row ), $row ),
+                                       $this->extractRevisionInfo( $revisionStore->newRevisionFromArchiveRow( $row ), $row ),
                                        'rev'
                                );
                                if ( !$fit ) {
index ebffb15..a6a3251 100644 (file)
@@ -24,6 +24,8 @@
  * @file
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Query module to enumerate all deleted files.
  *
@@ -153,7 +155,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
                        self::addTitleInfo( $file, $title );
 
                        if ( $fld_description &&
-                               Revision::userCanBitfield( $row->fa_deleted, File::DELETED_COMMENT, $user )
+                               RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_COMMENT, $user )
                        ) {
                                $file['description'] = $commentStore->getComment( 'fa_description', $row )->text;
                                if ( isset( $prop['parseddescription'] ) ) {
@@ -162,7 +164,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
                                }
                        }
                        if ( $fld_user &&
-                               Revision::userCanBitfield( $row->fa_deleted, File::DELETED_USER, $user )
+                               RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_USER, $user )
                        ) {
                                $file['userid'] = (int)$row->fa_user;
                                $file['user'] = $row->fa_user_text;
index f870d45..a5be58b 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * A query action to enumerate the recent changes that were done to the wiki.
  * Various filters are supported.
@@ -365,9 +367,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                // Paranoia: avoid brute force searches (T19342)
                if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
                        if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                               $bitmask = Revision::DELETED_USER;
+                               $bitmask = RevisionRecord::DELETED_USER;
                        } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                               $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                        } else {
                                $bitmask = 0;
                        }
@@ -507,11 +509,11 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                /* Add user data and 'anon' flag, if user is anonymous. */
                if ( $this->fld_user || $this->fld_userid ) {
-                       if ( $row->rc_deleted & Revision::DELETED_USER ) {
+                       if ( $row->rc_deleted & RevisionRecord::DELETED_USER ) {
                                $vals['userhidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_USER, $user ) ) {
+                       if ( RevisionRecord::userCanBitfield( $row->rc_deleted, RevisionRecord::DELETED_USER, $user ) ) {
                                if ( $this->fld_user ) {
                                        $vals['user'] = $row->rc_user_text;
                                }
@@ -546,11 +548,13 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                /* Add edit summary / log summary. */
                if ( $this->fld_comment || $this->fld_parsedcomment ) {
-                       if ( $row->rc_deleted & Revision::DELETED_COMMENT ) {
+                       if ( $row->rc_deleted & RevisionRecord::DELETED_COMMENT ) {
                                $vals['commenthidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_COMMENT, $user ) ) {
+                       if ( RevisionRecord::userCanBitfield(
+                               $row->rc_deleted, RevisionRecord::DELETED_COMMENT, $user
+                       ) ) {
                                $comment = $this->commentStore->getComment( 'rc_comment', $row )->text;
                                if ( $this->fld_comment ) {
                                        $vals['comment'] = $comment;
@@ -597,11 +601,13 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                }
 
                if ( $this->fld_sha1 && $row->rev_sha1 !== null ) {
-                       if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
+                       if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT ) {
                                $vals['sha1hidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( Revision::userCanBitfield( $row->rev_deleted, Revision::DELETED_TEXT, $user ) ) {
+                       if ( RevisionRecord::userCanBitfield(
+                               $row->rev_deleted, RevisionRecord::DELETED_TEXT, $user
+                       ) ) {
                                if ( $row->rev_sha1 !== '' ) {
                                        $vals['sha1'] = Wikimedia\base_convert( $row->rev_sha1, 36, 16, 40 );
                                } else {
@@ -623,7 +629,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        }
                }
 
-               if ( $anyHidden && ( $row->rc_deleted & Revision::DELETED_RESTRICTED ) ) {
+               if ( $anyHidden && ( $row->rc_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
                        $vals['suppressed'] = true;
                }
 
index 5858bc7..5e7b864 100644 (file)
@@ -20,6 +20,9 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * A query action to enumerate revisions of a given page, or show top revisions
  * of multiple pages. Various pieces of information may be shown - flags,
@@ -81,6 +84,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
 
        protected function run( ApiPageSet $resultPageSet = null ) {
                $params = $this->extractRequestParams( false );
+               $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
 
                // If any of those parameters are used, work in 'enumeration' mode.
                // Enum mode can only be used when exactly one page is provided.
@@ -139,7 +143,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        if ( $this->fld_user ) {
                                $opts[] = 'user';
                        }
-                       $revQuery = Revision::getQueryInfo( $opts );
+                       $revQuery = $revisionStore->getQueryInfo( $opts );
                        $this->addTables( $revQuery['tables'] );
                        $this->addFields( $revQuery['fields'] );
                        $this->addJoinConds( $revQuery['joins'] );
@@ -301,9 +305,9 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
                                // Paranoia: avoid brute force searches (T19342)
                                if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
-                                       $bitmask = Revision::DELETED_USER;
+                                       $bitmask = RevisionRecord::DELETED_USER;
                                } elseif ( !$this->getUser()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                                       $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                                       $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                                } else {
                                        $bitmask = 0;
                                }
@@ -382,14 +386,15 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        if ( $resultPageSet !== null ) {
                                $generated[] = $row->rev_id;
                        } else {
-                               $revision = new Revision( $row );
+                               $revision = $revisionStore->newRevisionFromRow( $row );
                                $rev = $this->extractRevisionInfo( $revision, $row );
 
                                if ( $this->token !== null ) {
-                                       $title = $revision->getTitle();
+                                       $title = Title::newFromLinkTarget( $revision->getPageAsLinkTarget() );
+                                       $revisionCompat = new Revision( $revision );
                                        $tokenFunctions = $this->getTokenFunctions();
                                        foreach ( $this->token as $t ) {
-                                               $val = call_user_func( $tokenFunctions[$t], $title->getArticleID(), $title, $revision );
+                                               $val = call_user_func( $tokenFunctions[$t], $title->getArticleID(), $title, $revisionCompat );
                                                if ( $val === false ) {
                                                        $this->addWarning( [ 'apiwarn-tokennotallowed', $t ] );
                                                } else {
index 87c6f9d..600c89e 100644 (file)
  * @file
  */
 
+use MediaWiki\Storage\RevisionAccessException;
+use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Storage\SlotRecord;
+use MediaWiki\MediaWikiServices;
+
 /**
  * A base class for functions common to producing a list of revisions.
  *
  */
 abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
 
+       /**
+        * @name Constants for internal use. Don't use externally.
+        * @{
+        */
+
+       // Bits to indicate the results of the revdel permission check on a revision,
+       // see self::checkRevDel()
+       const IS_DELETED = 1; // Whether the field is revision-deleted
+       const CANNOT_VIEW = 2; // Whether the user cannot view the field due to revdel
+
+       /**@}*/
+
        protected $limit, $diffto, $difftotext, $difftotextpst, $expandTemplates, $generateXML,
-               $section, $parseContent, $fetchContent, $contentFormat, $setParsedLimit = true;
+               $section, $parseContent, $fetchContent, $contentFormat, $setParsedLimit = true,
+               $slotRoles = null, $needSlots;
 
        protected $fld_ids = false, $fld_flags = false, $fld_timestamp = false,
-               $fld_size = false, $fld_sha1 = false, $fld_comment = false,
-               $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
-               $fld_content = false, $fld_tags = false, $fld_contentmodel = false, $fld_parsetree = false;
+               $fld_size = false, $fld_slotsize = false, $fld_sha1 = false, $fld_slotsha1 = false,
+               $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
+               $fld_content = false, $fld_tags = false, $fld_contentmodel = false, $fld_roles = false,
+               $fld_parsetree = false;
 
        public function execute() {
                $this->run();
@@ -55,6 +74,55 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
         * @param array $params
         */
        protected function parseParameters( $params ) {
+               $prop = array_flip( $params['prop'] );
+
+               $this->fld_ids = isset( $prop['ids'] );
+               $this->fld_flags = isset( $prop['flags'] );
+               $this->fld_timestamp = isset( $prop['timestamp'] );
+               $this->fld_comment = isset( $prop['comment'] );
+               $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
+               $this->fld_size = isset( $prop['size'] );
+               $this->fld_slotsize = isset( $prop['slotsize'] );
+               $this->fld_sha1 = isset( $prop['sha1'] );
+               $this->fld_slotsha1 = isset( $prop['slotsha1'] );
+               $this->fld_content = isset( $prop['content'] );
+               $this->fld_contentmodel = isset( $prop['contentmodel'] );
+               $this->fld_userid = isset( $prop['userid'] );
+               $this->fld_user = isset( $prop['user'] );
+               $this->fld_tags = isset( $prop['tags'] );
+               $this->fld_roles = isset( $prop['roles'] );
+               $this->fld_parsetree = isset( $prop['parsetree'] );
+
+               $this->slotRoles = $params['slots'];
+
+               if ( $this->slotRoles !== null ) {
+                       if ( $this->fld_parsetree ) {
+                               $this->dieWithError( [
+                                       'apierror-invalidparammix-cannotusewith',
+                                       $this->encodeParamName( 'prop=parsetree' ),
+                                       $this->encodeParamName( 'slots' ),
+                               ], 'invalidparammix' );
+                       }
+                       foreach ( [
+                               'expandtemplates', 'generatexml', 'parse', 'diffto', 'difftotext', 'difftotextpst',
+                               'contentformat'
+                       ] as $p ) {
+                               if ( $params[$p] !== null && $params[$p] !== false ) {
+                                       $this->dieWithError( [
+                                               'apierror-invalidparammix-cannotusewith',
+                                               $this->encodeParamName( $p ),
+                                               $this->encodeParamName( 'slots' ),
+                                       ], 'invalidparammix' );
+                               }
+                       }
+               }
+
+               if ( !empty( $params['contentformat'] ) ) {
+                       $this->contentFormat = $params['contentformat'];
+               }
+
+               $this->limit = $params['limit'];
+
                if ( !is_null( $params['difftotext'] ) ) {
                        $this->difftotext = $params['difftotext'];
                        $this->difftotextpst = $params['difftotextpst'];
@@ -72,11 +140,13 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        // DifferenceEngine returns a rather ambiguous empty
                        // string if that's not the case
                        if ( $params['diffto'] != 0 ) {
-                               $difftoRev = Revision::newFromId( $params['diffto'] );
+                               $difftoRev = MediaWikiServices::getInstance()->getRevisionStore()
+                                       ->getRevisionById( $params['diffto'] );
                                if ( !$difftoRev ) {
                                        $this->dieWithError( [ 'apierror-nosuchrevid', $params['diffto'] ] );
                                }
-                               if ( !$difftoRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+                               $revDel = $this->checkRevDel( $difftoRev, RevisionRecord::DELETED_TEXT );
+                               if ( $revDel & self::CANNOT_VIEW ) {
                                        $this->addWarning( [ 'apiwarn-difftohidden', $difftoRev->getId() ] );
                                        $params['diffto'] = null;
                                }
@@ -84,39 +154,6 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        $this->diffto = $params['diffto'];
                }
 
-               $prop = array_flip( $params['prop'] );
-
-               $this->fld_ids = isset( $prop['ids'] );
-               $this->fld_flags = isset( $prop['flags'] );
-               $this->fld_timestamp = isset( $prop['timestamp'] );
-               $this->fld_comment = isset( $prop['comment'] );
-               $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
-               $this->fld_size = isset( $prop['size'] );
-               $this->fld_sha1 = isset( $prop['sha1'] );
-               $this->fld_content = isset( $prop['content'] );
-               $this->fld_contentmodel = isset( $prop['contentmodel'] );
-               $this->fld_userid = isset( $prop['userid'] );
-               $this->fld_user = isset( $prop['user'] );
-               $this->fld_tags = isset( $prop['tags'] );
-               $this->fld_parsetree = isset( $prop['parsetree'] );
-
-               if ( $this->fld_parsetree ) {
-                       $encParam = $this->encodeParamName( 'prop' );
-                       $name = $this->getModuleName();
-                       $parent = $this->getParent();
-                       $parentParam = $parent->encodeParamName( $parent->getModuleManager()->getModuleGroup( $name ) );
-                       $this->addDeprecation(
-                               [ 'apiwarn-deprecation-parameter', "{$encParam}=parsetree" ],
-                               "action=query&{$parentParam}={$name}&{$encParam}=parsetree"
-                       );
-               }
-
-               if ( !empty( $params['contentformat'] ) ) {
-                       $this->contentFormat = $params['contentformat'];
-               }
-
-               $this->limit = $params['limit'];
-
                $this->fetchContent = $this->fld_content || !is_null( $this->diffto )
                        || !is_null( $this->difftotext ) || $this->fld_parsetree;
 
@@ -152,18 +189,46 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        $this->limit = 10;
                }
                $this->validateLimit( 'limit', $this->limit, 1, $userMax, $botMax );
+
+               $this->needSlots = $this->fetchContent || $this->fld_contentmodel ||
+                       $this->fld_slotsize || $this->fld_slotsha1;
+               if ( $this->needSlots && $this->slotRoles === null ) {
+                       $encParam = $this->encodeParamName( 'slots' );
+                       $name = $this->getModuleName();
+                       $parent = $this->getParent();
+                       $parentParam = $parent->encodeParamName( $parent->getModuleManager()->getModuleGroup( $name ) );
+                       $this->addDeprecation(
+                               [ 'apiwarn-deprecation-missingparam', $encParam ],
+                               "action=query&{$parentParam}={$name}&!{$encParam}"
+                       );
+               }
        }
 
        /**
-        * Extract information from the Revision
+        * Test revision deletion status
+        * @param RevisionRecord $revision Revision to check
+        * @param int $field One of the RevisionRecord::DELETED_* constants
+        * @return int Revision deletion status flags. Bitwise OR of
+        *  self::IS_DELETED and self::CANNOT_VIEW, as appropriate.
+        */
+       private function checkRevDel( RevisionRecord $revision, $field ) {
+               $ret = $revision->isDeleted( $field ) ? self::IS_DELETED : 0;
+               if ( $ret ) {
+                       $canSee = $revision->audienceCan( $field, RevisionRecord::FOR_THIS_USER, $this->getUser() );
+                       $ret = $ret | ( $canSee ? 0 : self::CANNOT_VIEW );
+               }
+               return $ret;
+       }
+
+       /**
+        * Extract information from the RevisionRecord
         *
-        * @param Revision $revision
+        * @since 1.32, takes a RevisionRecord instead of a Revision
+        * @param RevisionRecord $revision Revision
         * @param object $row Should have a field 'ts_tags' if $this->fld_tags is set
         * @return array
         */
-       protected function extractRevisionInfo( Revision $revision, $row ) {
-               $title = $revision->getTitle();
-               $user = $this->getUser();
+       protected function extractRevisionInfo( RevisionRecord $revision, $row ) {
                $vals = [];
                $anyHidden = false;
 
@@ -179,15 +244,17 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                }
 
                if ( $this->fld_user || $this->fld_userid ) {
-                       if ( $revision->isDeleted( Revision::DELETED_USER ) ) {
+                       $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_USER );
+                       if ( ( $revDel & self::IS_DELETED ) ) {
                                $vals['userhidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( $revision->userCan( Revision::DELETED_USER, $user ) ) {
+                       if ( !( $revDel & self::CANNOT_VIEW ) ) {
+                               $u = $revision->getUser( RevisionRecord::RAW );
                                if ( $this->fld_user ) {
-                                       $vals['user'] = $revision->getUserText( Revision::RAW );
+                                       $vals['user'] = $u->getName();
                                }
-                               $userid = $revision->getUser( Revision::RAW );
+                               $userid = $u->getId();
                                if ( !$userid ) {
                                        $vals['anon'] = true;
                                }
@@ -203,45 +270,115 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                }
 
                if ( $this->fld_size ) {
-                       if ( !is_null( $revision->getSize() ) ) {
+                       try {
                                $vals['size'] = intval( $revision->getSize() );
-                       } else {
+                       } catch ( RevisionAccessException $e ) {
+                               // Back compat: If there's no size, return 0.
+                               // @todo: GergÅ‘ says to mention T198099 as a "todo" here.
                                $vals['size'] = 0;
                        }
                }
 
                if ( $this->fld_sha1 ) {
-                       if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
+                       $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
+                       if ( ( $revDel & self::IS_DELETED ) ) {
                                $vals['sha1hidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( $revision->userCan( Revision::DELETED_TEXT, $user ) ) {
-                               if ( $revision->getSha1() != '' ) {
+                       if ( !( $revDel & self::CANNOT_VIEW ) ) {
+                               try {
                                        $vals['sha1'] = Wikimedia\base_convert( $revision->getSha1(), 36, 16, 40 );
-                               } else {
+                               } catch ( RevisionAccessException $e ) {
+                                       // Back compat: If there's no sha1, return emtpy string.
+                                       // @todo: GergÅ‘ says to mention T198099 as a "todo" here.
                                        $vals['sha1'] = '';
                                }
                        }
                }
 
-               if ( $this->fld_contentmodel ) {
-                       $vals['contentmodel'] = $revision->getContentModel();
+               if ( $this->fld_roles ) {
+                       $vals['roles'] = $revision->getSlotRoles();
+               }
+
+               if ( $this->needSlots ) {
+                       $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
+                       if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) {
+                               $anyHidden = true;
+                       }
+                       if ( $this->slotRoles === null ) {
+                               try {
+                                       $slot = $revision->getSlot( 'main', RevisionRecord::RAW );
+                               } catch ( RevisionAccessException $e ) {
+                                       // Back compat: If there's no slot, there's no content, so set 'textmissing'
+                                       // @todo: GergÅ‘ says to mention T198099 as a "todo" here.
+                                       $vals['textmissing'] = true;
+                                       $slot = null;
+                               }
+
+                               if ( $slot ) {
+                                       $content = null;
+                                       $vals += $this->extractSlotInfo( $slot, $revDel, $content );
+                                       if ( !empty( $vals['nosuchsection'] ) ) {
+                                               $this->dieWithError(
+                                                       [
+                                                               'apierror-nosuchsection-what',
+                                                               wfEscapeWikiText( $this->section ),
+                                                               $this->msg( 'revid', $revision->getId() )
+                                                       ],
+                                                       'nosuchsection'
+                                               );
+                                       }
+                                       if ( $content ) {
+                                               $vals += $this->extractDeprecatedContent( $content, $revision );
+                                       }
+                               }
+                       } else {
+                               $roles = array_intersect( $this->slotRoles, $revision->getSlotRoles() );
+                               $vals['slots'] = [
+                                       ApiResult::META_KVP_MERGE => true,
+                               ];
+                               foreach ( $roles as $role ) {
+                                       try {
+                                               $slot = $revision->getSlot( $role, RevisionRecord::RAW );
+                                       } catch ( RevisionAccessException $e ) {
+                                               // Don't error out here so the client can still process other slots/revisions.
+                                               // @todo: GergÅ‘ says to mention T198099 as a "todo" here.
+                                               $vals['slots'][$role]['missing'] = true;
+                                               continue;
+                                       }
+                                       $content = null;
+                                       $vals['slots'][$role] = $this->extractSlotInfo( $slot, $revDel, $content );
+                                       // @todo Move this into extractSlotInfo() (and remove its $content parameter)
+                                       // when extractDeprecatedContent() is no more.
+                                       if ( $content ) {
+                                               $vals['slots'][$role]['contentmodel'] = $content->getModel();
+                                               $vals['slots'][$role]['contentformat'] = $content->getDefaultFormat();
+                                               ApiResult::setContentValue( $vals['slots'][$role], 'content', $content->serialize() );
+                                       }
+                               }
+                               ApiResult::setArrayType( $vals['slots'], 'kvp', 'role' );
+                               ApiResult::setIndexedTagName( $vals['slots'], 'slot' );
+                       }
                }
 
                if ( $this->fld_comment || $this->fld_parsedcomment ) {
-                       if ( $revision->isDeleted( Revision::DELETED_COMMENT ) ) {
+                       $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_COMMENT );
+                       if ( ( $revDel & self::IS_DELETED ) ) {
                                $vals['commenthidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( $revision->userCan( Revision::DELETED_COMMENT, $user ) ) {
-                               $comment = $revision->getComment( Revision::RAW );
+                       if ( !( $revDel & self::CANNOT_VIEW ) ) {
+                               $comment = $revision->getComment( RevisionRecord::RAW );
+                               $comment = $comment ? $comment->text : '';
 
                                if ( $this->fld_comment ) {
                                        $vals['comment'] = $comment;
                                }
 
                                if ( $this->fld_parsedcomment ) {
-                                       $vals['parsedcomment'] = Linker::formatComment( $comment, $title );
+                                       $vals['parsedcomment'] = Linker::formatComment(
+                                               $comment, Title::newFromLinkTarget( $revision->getPageAsLinkTarget() )
+                                       );
                                }
                        }
                }
@@ -256,69 +393,119 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        }
                }
 
-               $content = null;
-               global $wgParser;
-               if ( $this->fetchContent ) {
-                       $content = $revision->getContent( Revision::FOR_THIS_USER, $this->getUser() );
-                       // Expand templates after getting section content because
-                       // template-added sections don't count and Parser::preprocess()
-                       // will have less input
-                       if ( $content && $this->section !== false ) {
-                               $content = $content->getSection( $this->section, false );
-                               if ( !$content ) {
-                                       $this->dieWithError(
-                                               [
-                                                       'apierror-nosuchsection-what',
-                                                       wfEscapeWikiText( $this->section ),
-                                                       $this->msg( 'revid', $revision->getId() )
-                                               ],
-                                               'nosuchsection'
-                                       );
+               if ( $anyHidden && $revision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
+                       $vals['suppressed'] = true;
+               }
+
+               return $vals;
+       }
+
+       /**
+        * Extract information from the SlotRecord
+        *
+        * @param SlotRecord $slot
+        * @param int $revDel Revdel status flags, from self::checkRevDel()
+        * @param Content|null &$content Set to the slot's content, if available
+        *  and $this->fetchContent is true
+        * @return array
+        */
+       private function extractSlotInfo( SlotRecord $slot, $revDel, &$content = null ) {
+               $vals = [];
+               ApiResult::setArrayType( $vals, 'assoc' );
+
+               if ( $this->fld_slotsize ) {
+                       $vals['size'] = intval( $slot->getSize() );
+               }
+
+               if ( $this->fld_slotsha1 ) {
+                       if ( ( $revDel & self::IS_DELETED ) ) {
+                               $vals['sha1hidden'] = true;
+                       }
+                       if ( !( $revDel & self::CANNOT_VIEW ) ) {
+                               if ( $slot->getSha1() != '' ) {
+                                       $vals['sha1'] = Wikimedia\base_convert( $slot->getSha1(), 36, 16, 40 );
+                               } else {
+                                       $vals['sha1'] = '';
                                }
                        }
-                       if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
+               }
+
+               if ( $this->fld_contentmodel ) {
+                       $vals['contentmodel'] = $slot->getModel();
+               }
+
+               $content = null;
+               if ( $this->fetchContent ) {
+                       if ( ( $revDel & self::IS_DELETED ) ) {
                                $vals['texthidden'] = true;
-                               $anyHidden = true;
-                       } elseif ( !$content ) {
-                               $vals['textmissing'] = true;
+                       }
+                       if ( !( $revDel & self::CANNOT_VIEW ) ) {
+                               try {
+                                       $content = $slot->getContent();
+                               } catch ( RevisionAccessException $e ) {
+                                       // @todo: GergÅ‘ says to mention T198099 as a "todo" here.
+                                       $vals['textmissing'] = true;
+                               }
+                               // Expand templates after getting section content because
+                               // template-added sections don't count and Parser::preprocess()
+                               // will have less input
+                               if ( $content && $this->section !== false ) {
+                                       $content = $content->getSection( $this->section, false );
+                                       if ( !$content ) {
+                                               $vals['nosuchsection'] = true;
+                                       }
+                               }
                        }
                }
+
+               return $vals;
+       }
+
+       /**
+        * Format a Content using deprecated options
+        * @param Content $content Content to format
+        * @param RevisionRecord $revision Revision being processed
+        * @return array
+        */
+       private function extractDeprecatedContent( Content $content, RevisionRecord $revision ) {
+               global $wgParser;
+
+               $vals = [];
+               $title = Title::newFromLinkTarget( $revision->getPageAsLinkTarget() );
+
                if ( $this->fld_parsetree || ( $this->fld_content && $this->generateXML ) ) {
-                       if ( $content ) {
-                               if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
-                                       $t = $content->getNativeData(); # note: don't set $text
+                       if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
+                               $t = $content->getNativeData(); # note: don't set $text
 
-                                       $wgParser->startExternalParse(
-                                               $title,
-                                               ParserOptions::newFromContext( $this->getContext() ),
-                                               Parser::OT_PREPROCESS
-                                       );
-                                       $dom = $wgParser->preprocessToDom( $t );
-                                       if ( is_callable( [ $dom, 'saveXML' ] ) ) {
-                                               $xml = $dom->saveXML();
-                                       } else {
-                                               $xml = $dom->__toString();
-                                       }
-                                       $vals['parsetree'] = $xml;
+                               $wgParser->startExternalParse(
+                                       $title,
+                                       ParserOptions::newFromContext( $this->getContext() ),
+                                       Parser::OT_PREPROCESS
+                               );
+                               $dom = $wgParser->preprocessToDom( $t );
+                               if ( is_callable( [ $dom, 'saveXML' ] ) ) {
+                                       $xml = $dom->saveXML();
                                } else {
-                                       $vals['badcontentformatforparsetree'] = true;
-                                       $this->addWarning(
-                                               [
-                                                       'apierror-parsetree-notwikitext-title',
-                                                       wfEscapeWikiText( $title->getPrefixedText() ),
-                                                       $content->getModel()
-                                               ],
-                                               'parsetree-notwikitext'
-                                       );
+                                       $xml = $dom->__toString();
                                }
+                               $vals['parsetree'] = $xml;
+                       } else {
+                               $vals['badcontentformatforparsetree'] = true;
+                               $this->addWarning(
+                                       [
+                                               'apierror-parsetree-notwikitext-title',
+                                               wfEscapeWikiText( $title->getPrefixedText() ),
+                                               $content->getModel()
+                                       ],
+                                       'parsetree-notwikitext'
+                               );
                        }
                }
 
-               if ( $this->fld_content && $content ) {
+               if ( $this->fld_content ) {
                        $text = null;
 
                        if ( $this->expandTemplates && !$this->parseContent ) {
-                               # XXX: implement template expansion for all content types in ContentHandler?
                                if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
                                        $text = $content->getNativeData();
 
@@ -376,7 +563,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                                $vals['diff'] = [];
                                $context = new DerivativeContext( $this->getContext() );
                                $context->setTitle( $title );
-                               $handler = $revision->getContentHandler();
+                               $handler = $content->getContentHandler();
 
                                if ( !is_null( $this->difftotext ) ) {
                                        $model = $title->getContentModel();
@@ -398,7 +585,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
 
                                                if ( $this->difftotextpst ) {
                                                        $popts = ParserOptions::newFromContext( $this->getContext() );
-                                                       $difftocontent = $difftocontent->preSaveTransform( $title, $user, $popts );
+                                                       $difftocontent = $difftocontent->preSaveTransform( $title, $this->getUser(), $popts );
                                                }
 
                                                $engine = $handler->createDifferenceEngine( $context );
@@ -421,10 +608,6 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        }
                }
 
-               if ( $anyHidden && $revision->isDeleted( Revision::DELETED_RESTRICTED ) ) {
-                       $vals['suppressed'] = true;
-               }
-
                return $vals;
        }
 
@@ -437,6 +620,12 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
        }
 
        public function getAllowedParams() {
+               $slotRoles = MediaWikiServices::getInstance()->getSlotRoleStore()->getMap();
+               if ( !in_array( 'main', $slotRoles, true ) ) {
+                       $slotRoles[] = 'main';
+               }
+               sort( $slotRoles, SORT_STRING );
+
                return [
                        'prop' => [
                                ApiBase::PARAM_ISMULTI => true,
@@ -448,12 +637,15 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                                        'user',
                                        'userid',
                                        'size',
+                                       'slotsize',
                                        'sha1',
+                                       'slotsha1',
                                        'contentmodel',
                                        'comment',
                                        'parsedcomment',
                                        'content',
                                        'tags',
+                                       'roles',
                                        'parsetree',
                                ],
                                ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-prop',
@@ -464,15 +656,27 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                                        'user' => 'apihelp-query+revisions+base-paramvalue-prop-user',
                                        'userid' => 'apihelp-query+revisions+base-paramvalue-prop-userid',
                                        'size' => 'apihelp-query+revisions+base-paramvalue-prop-size',
+                                       'slotsize' => 'apihelp-query+revisions+base-paramvalue-prop-slotsize',
                                        'sha1' => 'apihelp-query+revisions+base-paramvalue-prop-sha1',
+                                       'slotsha1' => 'apihelp-query+revisions+base-paramvalue-prop-slotsha1',
                                        'contentmodel' => 'apihelp-query+revisions+base-paramvalue-prop-contentmodel',
                                        'comment' => 'apihelp-query+revisions+base-paramvalue-prop-comment',
                                        'parsedcomment' => 'apihelp-query+revisions+base-paramvalue-prop-parsedcomment',
                                        'content' => 'apihelp-query+revisions+base-paramvalue-prop-content',
                                        'tags' => 'apihelp-query+revisions+base-paramvalue-prop-tags',
+                                       'roles' => 'apihelp-query+revisions+base-paramvalue-prop-roles',
                                        'parsetree' => [ 'apihelp-query+revisions+base-paramvalue-prop-parsetree',
                                                CONTENT_MODEL_WIKITEXT ],
                                ],
+                               ApiBase::PARAM_DEPRECATED_VALUES => [
+                                       'parsetree' => true,
+                               ],
+                       ],
+                       'slots' => [
+                               ApiBase::PARAM_TYPE => $slotRoles,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-slots',
+                               ApiBase::PARAM_ISMULTI => true,
+                               ApiBase::PARAM_ALL => true,
                        ],
                        'limit' => [
                                ApiBase::PARAM_TYPE => 'limit',
@@ -515,6 +719,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
                        'contentformat' => [
                                ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
                                ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
+                               ApiBase::PARAM_DEPRECATED => true,
                        ],
                ];
        }
index fdcaa76..3aa6183 100644 (file)
@@ -20,6 +20,9 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * This query action adds a list of a specified user's contributions to the output.
  *
@@ -399,7 +402,8 @@ class ApiQueryUserContribs extends ApiQueryBase {
                                                $revIds[] = $data[0]->rev_parent_id;
                                        }
                                }
-                               $this->parentLens = Revision::getParentLengths( $dbSecondary, $revIds );
+                               $this->parentLens = MediaWikiServices::getInstance()->getRevisionStore()
+                                       ->listRevisionSizes( $dbSecondary, $revIds );
                        }
 
                        foreach ( $merged as $data ) {
@@ -438,7 +442,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                $this->resetQueryParams();
                $db = $this->getDB();
 
-               $revQuery = Revision::getQueryInfo( [ 'page' ] );
+               $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo( [ 'page' ] );
                $this->addTables( $revQuery['tables'] );
                $this->addJoinConds( $revQuery['joins'] );
                $this->addFields( $revQuery['fields'] );
@@ -500,9 +504,9 @@ class ApiQueryUserContribs extends ApiQueryBase {
                // see the username.
                $user = $this->getUser();
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                       $bitmask = Revision::DELETED_USER;
+                       $bitmask = RevisionRecord::DELETED_USER;
                } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                       $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                       $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                } else {
                        $bitmask = 0;
                }
@@ -619,7 +623,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                $vals = [];
                $anyHidden = false;
 
-               if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
+               if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT ) {
                        $vals['texthidden'] = true;
                        $anyHidden = true;
                }
@@ -627,7 +631,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                // Any rows where we can't view the user were filtered out in the query.
                $vals['userid'] = (int)$row->rev_user;
                $vals['user'] = $row->rev_user_text;
-               if ( $row->rev_deleted & Revision::DELETED_USER ) {
+               if ( $row->rev_deleted & RevisionRecord::DELETED_USER ) {
                        $vals['userhidden'] = true;
                        $anyHidden = true;
                }
@@ -658,14 +662,14 @@ class ApiQueryUserContribs extends ApiQueryBase {
                }
 
                if ( $this->fld_comment || $this->fld_parsedcomment ) {
-                       if ( $row->rev_deleted & Revision::DELETED_COMMENT ) {
+                       if ( $row->rev_deleted & RevisionRecord::DELETED_COMMENT ) {
                                $vals['commenthidden'] = true;
                                $anyHidden = true;
                        }
 
-                       $userCanView = Revision::userCanBitfield(
+                       $userCanView = RevisionRecord::userCanBitfield(
                                $row->rev_deleted,
-                               Revision::DELETED_COMMENT, $this->getUser()
+                               RevisionRecord::DELETED_COMMENT, $this->getUser()
                        );
 
                        if ( $userCanView ) {
@@ -707,7 +711,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        }
                }
 
-               if ( $anyHidden && $row->rev_deleted & Revision::DELETED_RESTRICTED ) {
+               if ( $anyHidden && ( $row->rev_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
                        $vals['suppressed'] = true;
                }
 
index bb09838..5dd247a 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * This query action allows clients to retrieve a list of recently modified pages
@@ -302,13 +303,13 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
 
                /* Add user data and 'anon' flag, if user is anonymous. */
                if ( $this->fld_user || $this->fld_userid ) {
-                       if ( $recentChangeInfo['rc_deleted'] & Revision::DELETED_USER ) {
+                       if ( $recentChangeInfo['rc_deleted'] & RevisionRecord::DELETED_USER ) {
                                $vals['userhidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( Revision::userCanBitfield(
+                       if ( RevisionRecord::userCanBitfield(
                                $recentChangeInfo['rc_deleted'],
-                               Revision::DELETED_USER,
+                               RevisionRecord::DELETED_USER,
                                $user
                        ) ) {
                                if ( $this->fld_userid ) {
@@ -353,13 +354,13 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
 
                /* Add edit summary / log summary. */
                if ( $this->fld_comment || $this->fld_parsedcomment ) {
-                       if ( $recentChangeInfo['rc_deleted'] & Revision::DELETED_COMMENT ) {
+                       if ( $recentChangeInfo['rc_deleted'] & RevisionRecord::DELETED_COMMENT ) {
                                $vals['commenthidden'] = true;
                                $anyHidden = true;
                        }
-                       if ( Revision::userCanBitfield(
+                       if ( RevisionRecord::userCanBitfield(
                                $recentChangeInfo['rc_deleted'],
-                               Revision::DELETED_COMMENT,
+                               RevisionRecord::DELETED_COMMENT,
                                $user
                        ) ) {
                                $comment = $this->commentStore->getComment( 'rc_comment', $recentChangeInfo )->text;
@@ -407,7 +408,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
                        }
                }
 
-               if ( $anyHidden && ( $recentChangeInfo['rc_deleted'] & Revision::DELETED_RESTRICTED ) ) {
+               if ( $anyHidden && ( $recentChangeInfo['rc_deleted'] & RevisionRecord::DELETED_RESTRICTED ) ) {
                        $vals['suppressed'] = true;
                }
 
index 9a793e2..6121c3d 100644 (file)
@@ -21,6 +21,8 @@
  * @since 1.23
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * API interface to RevDel. The API equivalent of Special:RevisionDelete.
  * Requires API write mode to be enabled.
@@ -61,8 +63,8 @@ class ApiRevisionDelete extends ApiBase {
                }
                $bits = [
                        'content' => RevisionDeleter::getRevdelConstant( $params['type'] ),
-                       'comment' => Revision::DELETED_COMMENT,
-                       'user' => Revision::DELETED_USER,
+                       'comment' => RevisionRecord::DELETED_COMMENT,
+                       'user' => RevisionRecord::DELETED_USER,
                ];
                $bitfield = [];
                foreach ( $bits as $key => $bit ) {
@@ -77,11 +79,11 @@ class ApiRevisionDelete extends ApiBase {
 
                if ( $params['suppress'] === 'yes' ) {
                        $this->checkUserRightsAny( 'suppressrevision' );
-                       $bitfield[Revision::DELETED_RESTRICTED] = 1;
+                       $bitfield[RevisionRecord::DELETED_RESTRICTED] = 1;
                } elseif ( $params['suppress'] === 'no' ) {
-                       $bitfield[Revision::DELETED_RESTRICTED] = 0;
+                       $bitfield[RevisionRecord::DELETED_RESTRICTED] = 0;
                } else {
-                       $bitfield[Revision::DELETED_RESTRICTED] = -1;
+                       $bitfield[RevisionRecord::DELETED_RESTRICTED] = -1;
                }
 
                $targetObj = null;
index f7dc4a7..b81c5bf 100644 (file)
@@ -22,6 +22,7 @@
  *
  * @file
  */
+
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -73,10 +74,11 @@ class ApiSetNotificationTimestamp extends ApiBase {
                        if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
                                $this->dieWithError( [ 'apierror-multpages', $this->encodeParamName( 'torevid' ) ] );
                        }
-                       $title = reset( $pageSet->getGoodTitles() );
+                       $titles = $pageSet->getGoodTitles();
+                       $title = reset( $titles );
                        if ( $title ) {
-                               $timestamp = Revision::getTimestampFromId(
-                                       $title, $params['torevid'], Revision::READ_LATEST );
+                               $timestamp = MediaWikiServices::getInstance()->getRevisionStore()
+                                       ->getTimestampFromId( $title, $params['torevid'], IDBAccessObject::READ_LATEST );
                                if ( $timestamp ) {
                                        $timestamp = $dbw->timestamp( $timestamp );
                                } else {
@@ -87,12 +89,14 @@ class ApiSetNotificationTimestamp extends ApiBase {
                        if ( $params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1 ) {
                                $this->dieWithError( [ 'apierror-multpages', $this->encodeParamName( 'newerthanrevid' ) ] );
                        }
-                       $title = reset( $pageSet->getGoodTitles() );
+                       $titles = $pageSet->getGoodTitles();
+                       $title = reset( $titles );
                        if ( $title ) {
-                               $revid = $title->getNextRevisionID(
-                                       $params['newerthanrevid'], Title::GAID_FOR_UPDATE );
+                               $revid = $title->getNextRevisionID( $params['newerthanrevid'], Title::GAID_FOR_UPDATE );
                                if ( $revid ) {
-                                       $timestamp = $dbw->timestamp( Revision::getTimestampFromId( $title, $revid ) );
+                                       $timestamp = $dbw->timestamp(
+                                               MediaWikiServices::getInstance()->getRevisionStore()->getTimestampFromId( $title, $revid )
+                                       );
                                } else {
                                        $timestamp = null;
                                }
index c9f6db3..e0c7a28 100644 (file)
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionStore;
+
 /**
  * @ingroup API
  * @since 1.25
  */
 class ApiTag extends ApiBase {
 
+       /** @var RevisionStore */
+       private $revisionStore;
+
        public function execute() {
+               $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
+
                $params = $this->extractRequestParams();
                $user = $this->getUser();
 
@@ -84,7 +92,7 @@ class ApiTag extends ApiBase {
                                $valid = RecentChange::newFromId( $id );
                                break;
                        case 'revid':
-                               $valid = Revision::newFromId( $id );
+                               $valid = $this->revisionStore->getRevisionById( $id );
                                break;
                        case 'logid':
                                $valid = self::validateLogId( $id );
index d7cdc6c..44b4dfd 100644 (file)
        "apihelp-query+revisions+base-paramvalue-prop-user": "User that made the revision.",
        "apihelp-query+revisions+base-paramvalue-prop-userid": "User ID of the revision creator.",
        "apihelp-query+revisions+base-paramvalue-prop-size": "Length (bytes) of the revision.",
+       "apihelp-query+revisions+base-paramvalue-prop-slotsize": "Length (bytes) of each revision slot.",
        "apihelp-query+revisions+base-paramvalue-prop-sha1": "SHA-1 (base 16) of the revision.",
-       "apihelp-query+revisions+base-paramvalue-prop-contentmodel": "Content model ID of the revision.",
+       "apihelp-query+revisions+base-paramvalue-prop-slotsha1": "SHA-1 (base 16) of each revision slot.",
+       "apihelp-query+revisions+base-paramvalue-prop-contentmodel": "Content model ID of each revision slot.",
        "apihelp-query+revisions+base-paramvalue-prop-comment": "Comment by the user for the revision.",
        "apihelp-query+revisions+base-paramvalue-prop-parsedcomment": "Parsed comment by the user for the revision.",
-       "apihelp-query+revisions+base-paramvalue-prop-content": "Text of the revision.",
+       "apihelp-query+revisions+base-paramvalue-prop-content": "Content of each revision slot.",
        "apihelp-query+revisions+base-paramvalue-prop-tags": "Tags for the revision.",
-       "apihelp-query+revisions+base-paramvalue-prop-parsetree": "<span class=\"apihelp-deprecated\">Deprecated.</span> Use <kbd>[[Special:ApiHelp/expandtemplates|action=expandtemplates]]</kbd> or <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd> instead. The XML parse tree of revision content (requires content model <code>$1</code>).",
+       "apihelp-query+revisions+base-paramvalue-prop-roles": "List content slot roles that exist in the revision.",
+       "apihelp-query+revisions+base-paramvalue-prop-parsetree": "Use <kbd>[[Special:ApiHelp/expandtemplates|action=expandtemplates]]</kbd> or <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd> instead. The XML parse tree of revision content (requires content model <code>$1</code>).",
+       "apihelp-query+revisions+base-param-slots": "Which revision slots to return data for, when slot-related properties are included in <var>$1props</var>. If omitted, data from the <kbd>main</kbd> slot will be returned in a backwards-compatible format.",
        "apihelp-query+revisions+base-param-limit": "Limit how many revisions will be returned.",
        "apihelp-query+revisions+base-param-expandtemplates": "Use <kbd>[[Special:ApiHelp/expandtemplates|action=expandtemplates]]</kbd> instead. Expand templates in revision content (requires $1prop=content).",
        "apihelp-query+revisions+base-param-generatexml": "Use <kbd>[[Special:ApiHelp/expandtemplates|action=expandtemplates]]</kbd> or <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd> instead. Generate XML parse tree for revision content (requires $1prop=content).",
        "apiwarn-checktoken-percentencoding": "Check that symbols such as \"+\" in the token are properly percent-encoded in the URL.",
        "apiwarn-compare-nocontentmodel": "No content model could be determined, assuming $1.",
        "apiwarn-deprecation-deletedrevs": "<kbd>list=deletedrevs</kbd> has been deprecated. Please use <kbd>prop=deletedrevisions</kbd> or <kbd>list=alldeletedrevisions</kbd> instead.",
-       "apiwarn-deprecation-expandtemplates-prop": "Because no values have been specified for the <var>prop</var> parameter, a legacy format has been used for the output. This format is deprecated, and in the future, a default value will be set for the <var>prop</var> parameter, causing the new format to always be used.",
        "apiwarn-deprecation-httpsexpected": "HTTP used when HTTPS was expected.",
        "apiwarn-deprecation-login-botpw": "Main-account login via <kbd>action=login</kbd> is deprecated and may stop working without warning. To continue login with <kbd>action=login</kbd>, see [[Special:BotPasswords]]. To safely continue using main-account login, see <kbd>action=clientlogin</kbd>.",
        "apiwarn-deprecation-login-nobotpw": "Main-account login via <kbd>action=login</kbd> is deprecated and may stop working without warning. To safely log in, see <kbd>action=clientlogin</kbd>.",
        "apiwarn-deprecation-login-token": "Fetching a token via <kbd>action=login</kbd> is deprecated. Use <kbd>action=query&meta=tokens&type=login</kbd> instead.",
+       "apiwarn-deprecation-missingparam": "Because <var>$1</var> was not specified, a legacy format has been used for the output. This format is deprecated, and in the future the new format will always be used.",
        "apiwarn-deprecation-parameter": "The parameter <var>$1</var> has been deprecated.",
        "apiwarn-deprecation-parse-headitems": "<kbd>prop=headitems</kbd> is deprecated since MediaWiki 1.28. Use <kbd>prop=headhtml</kbd> when creating new HTML documents, or <kbd>prop=modules|jsconfigvars</kbd> when updating a document client-side.",
        "apiwarn-deprecation-purge-get": "Use of <kbd>action=purge</kbd> via GET is deprecated. Use POST instead.",
index dd8e529..f158f27 100644 (file)
        "apihelp-query+revisions+base-paramvalue-prop-user": "{{doc-apihelp-paramvalue|query+revisions+base|prop|user}}",
        "apihelp-query+revisions+base-paramvalue-prop-userid": "{{doc-apihelp-paramvalue|query+revisions+base|prop|userid}}",
        "apihelp-query+revisions+base-paramvalue-prop-size": "{{doc-apihelp-paramvalue|query+revisions+base|prop|size}}",
+       "apihelp-query+revisions+base-paramvalue-prop-slotsize": "{{doc-apihelp-paramvalue|query+revisions+base|prop|slotsize}}",
        "apihelp-query+revisions+base-paramvalue-prop-sha1": "{{doc-apihelp-paramvalue|query+revisions+base|prop|sha1}}",
+       "apihelp-query+revisions+base-paramvalue-prop-slotsha1": "{{doc-apihelp-paramvalue|query+revisions+base|prop|slotsha1}}",
        "apihelp-query+revisions+base-paramvalue-prop-contentmodel": "{{doc-apihelp-paramvalue|query+revisions+base|prop|contentmodel}}",
        "apihelp-query+revisions+base-paramvalue-prop-comment": "{{doc-apihelp-paramvalue|query+revisions+base|prop|comment}}",
        "apihelp-query+revisions+base-paramvalue-prop-parsedcomment": "{{doc-apihelp-paramvalue|query+revisions+base|prop|parsedcomment}}",
        "apihelp-query+revisions+base-paramvalue-prop-content": "{{doc-apihelp-paramvalue|query+revisions+base|prop|content}}",
        "apihelp-query+revisions+base-paramvalue-prop-tags": "{{doc-apihelp-paramvalue|query+revisions+base|prop|tags}}",
+       "apihelp-query+revisions+base-paramvalue-prop-roles": "{{doc-apihelp-paramvalue|query+revisions+base|prop|roles}}",
        "apihelp-query+revisions+base-paramvalue-prop-parsetree": "{{doc-apihelp-paramvalue|query+revisions+base|prop|parsetree|params=* $1 - Value of the constant CONTENT_MODEL_WIKITEXT|paramstart=2}}",
+       "apihelp-query+revisions+base-param-slots": "{{doc-apihelp-param|query+revisions+base|slots|description=the \"slots\" parameter to revision querying modules|noseealso=1}}",
        "apihelp-query+revisions+base-param-limit": "{{doc-apihelp-param|query+revisions+base|limit|description=the \"limit\" parameter to revision querying modules|noseealso=1}}",
        "apihelp-query+revisions+base-param-expandtemplates": "{{doc-apihelp-param|query+revisions+base|expandtemplates|description=the \"expandtemplates\" parameter to revision querying modules|noseealso=1}}",
        "apihelp-query+revisions+base-param-generatexml": "{{doc-apihelp-param|query+revisions+base|generatexml|description=the \"generatexml\" parameter to revision querying modules|noseealso=1}}",
        "apiwarn-checktoken-percentencoding": "{{doc-apierror}}",
        "apiwarn-compare-nocontentmodel": "{{doc-apierror}}\n\nParameters:\n* $1 - Content model being assumed.",
        "apiwarn-deprecation-deletedrevs": "{{doc-apierror}}",
-       "apiwarn-deprecation-expandtemplates-prop": "{{doc-apierror}}",
        "apiwarn-deprecation-httpsexpected": "{{doc-apierror}}",
        "apiwarn-deprecation-login-botpw": "{{doc-apierror}}",
        "apiwarn-deprecation-login-nobotpw": "{{doc-apierror}}",
        "apiwarn-deprecation-login-token": "{{doc-apierror}}",
+       "apiwarn-deprecation-missingparam": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.",
        "apiwarn-deprecation-parameter": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.",
        "apiwarn-deprecation-parse-headitems": "{{doc-apierror}}",
        "apiwarn-deprecation-purge-get": "{{doc-apierror}}",
index 38a1d68..1ed5e95 100644 (file)
@@ -26,16 +26,19 @@ class ApiQueryRevisionsTest extends ApiTestCase {
                        'prop' => 'revisions',
                        'titles' => $pageName,
                        'rvprop' => 'content',
+                       'rvslots' => 'main',
                ] );
                $this->assertArrayHasKey( 'query', $apiResult[0] );
                $this->assertArrayHasKey( 'pages', $apiResult[0]['query'] );
                foreach ( $apiResult[0]['query']['pages'] as $page ) {
                        $this->assertArrayHasKey( 'revisions', $page );
                        foreach ( $page['revisions'] as $revision ) {
-                               $this->assertArrayHasKey( 'contentformat', $revision,
+                               $this->assertArrayHasKey( 'slots', $revision );
+                               $this->assertArrayHasKey( 'main', $revision['slots'] );
+                               $this->assertArrayHasKey( 'contentformat', $revision['slots']['main'],
                                        'contentformat should be included when asking content so client knows how to interpret it'
                                );
-                               $this->assertArrayHasKey( 'contentmodel', $revision,
+                               $this->assertArrayHasKey( 'contentmodel', $revision['slots']['main'],
                                        'contentmodel should be included when asking content so client knows how to interpret it'
                                );
                        }