* '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.
*
* @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 ) {
* @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
if ( $params['prop'] === null ) {
$this->addDeprecation(
- 'apiwarn-deprecation-expandtemplates-prop', 'action=expandtemplates&!prop'
+ [ 'apiwarn-deprecation-missingparam', 'prop' ], 'action=expandtemplates&!prop'
);
$prop = [];
} else {
// 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(),
* @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.
*
}
public function execute() {
+ $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
+
$params = $this->extractRequestParams();
$config = $this->getConfig();
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(),
}
/**
- * @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".
$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>';
}
* @file
*/
+use MediaWiki\MediaWikiServices;
+
/**
* Allows user to patrol pages
* @ingroup API
$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'] ] );
}
* @file
*/
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
/**
* Query module to enumerate all deleted revisions.
*
$user = $this->getUser();
$db = $this->getDB();
$params = $this->extractRequestParams( false );
+ $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
$result = $this->getResult();
if ( $resultPageSet === null ) {
$this->parseParameters( $params );
- $arQuery = Revision::getArchiveQueryInfo();
+ $arQuery = $revisionStore->getArchiveQueryInfo();
$this->addTables( $arQuery['tables'] );
$this->addJoinConds( $arQuery['joins'] );
$this->addFields( $arQuery['fields'] );
// (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;
}
$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 ],
* @file
*/
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
/**
* Query module to enumerate all revisions.
*
protected function run( ApiPageSet $resultPageSet = null ) {
$db = $this->getDB();
$params = $this->extractRequestParams( false );
+ $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
$result = $this->getResult();
if ( $resultPageSet === null ) {
$this->parseParameters( $params );
- $revQuery = Revision::getQueryInfo(
+ $revQuery = $revisionStore->getQueryInfo(
$this->fetchContent ? [ 'page', 'text' ] : [ 'page' ]
);
$this->addTables( $revQuery['tables'] );
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;
}
$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 ],
* @since 1.23
*/
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
/**
* A query module to show contributors to a page
*
}
$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.
] );
$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 ) {
] );
$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 );
* @file
*/
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
/**
* Query module to enumerate deleted revisions for pages.
*
$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'] );
// (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;
}
$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 ) {
* @file
*/
+use MediaWiki\Storage\RevisionRecord;
+
/**
* Query module to enumerate all deleted files.
*
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'] ) ) {
}
}
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;
* @file
*/
+use MediaWiki\Storage\RevisionRecord;
+
/**
* A query action to enumerate the recent changes that were done to the wiki.
* Various filters are supported.
// 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;
}
/* 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;
}
/* 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;
}
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 {
}
}
- if ( $anyHidden && ( $row->rc_deleted & Revision::DELETED_RESTRICTED ) ) {
+ if ( $anyHidden && ( $row->rc_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = true;
}
* @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,
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.
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'] );
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;
}
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 {
* @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();
* @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'];
// 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;
}
$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;
$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;
}
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;
}
}
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() )
+ );
}
}
}
}
}
- $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();
$vals['diff'] = [];
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $title );
- $handler = $revision->getContentHandler();
+ $handler = $content->getContentHandler();
if ( !is_null( $this->difftotext ) ) {
$model = $title->getContentModel();
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 );
}
}
- if ( $anyHidden && $revision->isDeleted( Revision::DELETED_RESTRICTED ) ) {
- $vals['suppressed'] = true;
- }
-
return $vals;
}
}
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,
'user',
'userid',
'size',
+ 'slotsize',
'sha1',
+ 'slotsha1',
'contentmodel',
'comment',
'parsedcomment',
'content',
'tags',
+ 'roles',
'parsetree',
],
ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-prop',
'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',
'contentformat' => [
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
+ ApiBase::PARAM_DEPRECATED => true,
],
];
}
* @file
*/
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+
/**
* This query action adds a list of a specified user's contributions to the output.
*
$revIds[] = $data[0]->rev_parent_id;
}
}
- $this->parentLens = Revision::getParentLengths( $dbSecondary, $revIds );
+ $this->parentLens = MediaWikiServices::getInstance()->getRevisionStore()
+ ->listRevisionSizes( $dbSecondary, $revIds );
}
foreach ( $merged as $data ) {
$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'] );
// 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;
}
$vals = [];
$anyHidden = false;
- if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
+ if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT ) {
$vals['texthidden'] = true;
$anyHidden = true;
}
// 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;
}
}
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 ) {
}
}
- if ( $anyHidden && $row->rev_deleted & Revision::DELETED_RESTRICTED ) {
+ if ( $anyHidden && ( $row->rev_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = true;
}
*/
use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
/**
* This query action allows clients to retrieve a list of recently modified pages
/* 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 ) {
/* 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;
}
}
- if ( $anyHidden && ( $recentChangeInfo['rc_deleted'] & Revision::DELETED_RESTRICTED ) ) {
+ if ( $anyHidden && ( $recentChangeInfo['rc_deleted'] & RevisionRecord::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = true;
}
* @since 1.23
*/
+use MediaWiki\Storage\RevisionRecord;
+
/**
* API interface to RevDel. The API equivalent of Special:RevisionDelete.
* Requires API write mode to be enabled.
}
$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 ) {
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;
*
* @file
*/
+
use MediaWiki\MediaWikiServices;
/**
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 {
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;
}
* @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();
$valid = RecentChange::newFromId( $id );
break;
case 'revid':
- $valid = Revision::newFromId( $id );
+ $valid = $this->revisionStore->getRevisionById( $id );
break;
case 'logid':
$valid = self::validateLogId( $id );
"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.",
"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}}",
'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'
);
}