jenkins-bot has submitted this change and it was merged.
Change subject: API: Split list=deletedrevs into prop=deletedrevisions and
list=alldeletedrevisions
......................................................................
API: Split list=deletedrevs into prop=deletedrevisions and
list=alldeletedrevisions
list=deletedrevs has always been an odd one: it pretends to be a prop
module sometimes in taking titles from ApiPageSet, but when the pageset
supplies no titles it acts like a list module. This causes problems
such as bug 71389, and makes the whole thing unnecessarily confusing.
The solution is to split the "prop" and "list" aspects into separate
modules: prop=deletedrevisions when input should come from ApiPageSet
and list=alldeletedrevisions when not.
At the same time, let's take advantage of the situation to clear up some
other bugs. And let's share the revision-formatting code with
ApiQueryRevisions instead of partially reimplementing it.
Bug: 23489
Bug: 27193
Bug: 44190
Bug: 71396
Bug: 71389
Change-Id: I3e960d5c655bc57885d6d4ee227e67104808add7
---
M RELEASE-NOTES-1.25
M includes/AutoLoader.php
M includes/api/ApiBase.php
M includes/api/ApiHelp.php
M includes/api/ApiPageSet.php
M includes/api/ApiParamInfo.php
M includes/api/ApiQuery.php
A includes/api/ApiQueryAllDeletedRevisions.php
A includes/api/ApiQueryDeletedRevisions.php
M includes/api/ApiQueryDeletedrevs.php
M includes/api/ApiQueryRevisions.php
A includes/api/ApiQueryRevisionsBase.php
M includes/api/i18n/en.json
M includes/api/i18n/qqq.json
14 files changed, 1,411 insertions(+), 446 deletions(-)
Approvals:
Aaron Schulz: Looks good to me, approved
jenkins-bot: Verified
diff --git a/RELEASE-NOTES-1.25 b/RELEASE-NOTES-1.25
index fa870bb..7a088cf 100644
--- a/RELEASE-NOTES-1.25
+++ b/RELEASE-NOTES-1.25
@@ -85,6 +85,15 @@
when a batch of pages is complete.
* Pretty-printed HTML output now has nicer formatting and (if available)
better syntax highlighting.
+* Deprecated list=deletedrevs in favor of newly-added prop=deletedrevisions and
+ list=alldeletedrevisions.
+* prop=revisions will gracefully continue when given too many revids or titles,
+ rather than just ignoring the extras.
+* prop=revisions will no longer die if rvcontentformat doesn't match a
+ revision's content model; it will instead warn and omit the content.
+* If the user has the 'deletedhistory' right, action=query's revids parameter
+ will now recognize deleted revids.
+* prop=revisions may be used as a generator, generating revids.
=== Action API internal changes in 1.25 ===
* ApiHelp has been rewritten to support i18n and paginated HTML output.
@@ -109,6 +118,11 @@
has been removed.
* ApiFormatBase now always buffers. Output is done when
ApiFormatBase::closePrinter is called.
+* Much of the logic in ApiQueryRevisions has been split into
ApiQueryRevisionsBase.
+* The 'revids' parameter supplied by ApiPageSet will now count deleted
+ revisions as "good" if the user has the 'deletedhistory' right. New methods
+ ApiPageSet::getLiveRevisionIDs() and ApiPageSet::getDeletedRevisionIDs() are
+ provided to access just the live or just the deleted revids.
* The following methods have been deprecated and may be removed in a future
release:
* ApiBase::getDescription
@@ -127,6 +141,9 @@
* ApiMain::reallyMakeHelpMsg
* ApiMain::makeHelpMsgHeader
* ApiQueryImageInfo::getPropertyDescriptions
+* The following classes have been deprecated and may be removed in a future
+ release:
+ * ApiQueryDeletedrevs
=== Languages updated in 1.25 ===
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 36a0fcb..f0aa116 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -262,6 +262,7 @@
'ApiPurge' => 'includes/api/ApiPurge.php',
'ApiQuery' => 'includes/api/ApiQuery.php',
'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php',
+ 'ApiQueryAllDeletedRevisions' =>
'includes/api/ApiQueryAllDeletedRevisions.php',
'ApiQueryAllImages' => 'includes/api/ApiQueryAllImages.php',
'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
'ApiQueryAllMessages' => 'includes/api/ApiQueryAllMessages.php',
@@ -276,6 +277,7 @@
'ApiQueryCategoryMembers' => 'includes/api/ApiQueryCategoryMembers.php',
'ApiQueryContributions' => 'includes/api/ApiQueryUserContributions.php',
'ApiQueryContributors' => 'includes/api/ApiQueryContributors.php',
+ 'ApiQueryDeletedRevisions' =>
'includes/api/ApiQueryDeletedRevisions.php',
'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
'ApiQueryDisabled' => 'includes/api/ApiQueryDisabled.php',
'ApiQueryDuplicateFiles' => 'includes/api/ApiQueryDuplicateFiles.php',
@@ -303,6 +305,7 @@
'ApiQueryRecentChanges' => 'includes/api/ApiQueryRecentChanges.php',
'ApiQueryFileRepoInfo' => 'includes/api/ApiQueryFileRepoInfo.php',
'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
+ 'ApiQueryRevisionsBase' => 'includes/api/ApiQueryRevisionsBase.php',
'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
'ApiQueryStashImageInfo' => 'includes/api/ApiQueryStashImageInfo.php',
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index 58bd68d..3f84f2a 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -79,7 +79,7 @@
// of arrays, with the first member being the 'tag' for the info and the
// remaining members being the values. In the help, this is formatted
using
// apihelp-{$path}-paraminfo-{$tag}, which is passed $1 = count, $2 =
- // comma-joined list of values.
+ // comma-joined list of values, $3 = module prefix.
const PARAM_HELP_MSG_INFO = 12;
/// @since 1.25
// When PARAM_DFLT is an array, this may be an array mapping those
values
diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php
index fdde4bd..b33b087 100644
--- a/includes/api/ApiHelp.php
+++ b/includes/api/ApiHelp.php
@@ -376,6 +376,7 @@
$info[] =
$context->msg( "apihelp-{$path}-paraminfo-{$tag}" )
->numParams(
count( $i ) )
->params(
$context->getLanguage()->commaList( $i ) )
+ ->params(
$module->getModulePrefix() )
->parse();
}
}
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index db5eb52..ea85cac 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -68,6 +68,8 @@
private $mPendingRedirectIDs = array();
private $mConvertedTitles = array();
private $mGoodRevIDs = array();
+ private $mLiveRevIDs = array();
+ private $mDeletedRevIDs = array();
private $mMissingRevIDs = array();
private $mFakePageId = -1;
private $mCacheMode = 'public';
@@ -596,11 +598,27 @@
}
/**
- * Get the list of revision IDs (requested with the revids= parameter)
+ * Get the list of valid revision IDs (requested with the revids=
parameter)
* @return array Array of revID (int) => pageID (int)
*/
public function getRevisionIDs() {
return $this->mGoodRevIDs;
+ }
+
+ /**
+ * Get the list of non-deleted revision IDs (requested with the revids=
parameter)
+ * @return array Array of revID (int) => pageID (int)
+ */
+ public function getLiveRevisionIDs() {
+ return $this->mLiveRevIDs;
+ }
+
+ /**
+ * Get the list of revision IDs that were associated with deleted
titles.
+ * @return array Array of revID (int) => pageID (int)
+ */
+ public function getDeletedRevisionIDs() {
+ return $this->mDeletedRevIDs;
}
/**
@@ -901,6 +919,7 @@
$revid = intval( $row->rev_id );
$pageid = intval( $row->rev_page );
$this->mGoodRevIDs[$revid] = $pageid;
+ $this->mLiveRevIDs[$revid] = $pageid;
$pageids[$pageid] = '';
unset( $remaining[$revid] );
}
@@ -911,6 +930,51 @@
// Populate all the page information
$this->initFromPageIds( array_keys( $pageids ) );
+
+ // If the user can see deleted revisions, pull out the
corresponding
+ // titles from the archive table and include them too. We ignore
+ // ar_page_id because deleted revisions are tied by title, not
page_id.
+ if ( !empty( $this->mMissingRevIDs ) &&
$this->getUser()->isAllowed( 'deletedhistory' ) ) {
+ $remaining = array_flip( $this->mMissingRevIDs );
+ $tables = array( 'archive' );
+ $fields = array( 'ar_rev_id', 'ar_namespace',
'ar_title' );
+ $where = array( 'ar_rev_id' => $this->mMissingRevIDs );
+
+ $this->profileDBIn();
+ $res = $db->select( $tables, $fields, $where,
__METHOD__ );
+ $titles = array();
+ foreach ( $res as $row ) {
+ $revid = intval( $row->ar_rev_id );
+ $titles[$revid] = Title::makeTitle(
$row->ar_namespace, $row->ar_title );
+ unset( $remaining[$revid] );
+ }
+ $this->profileDBOut();
+
+ $this->initFromTitles( $titles );
+
+ foreach ( $titles as $revid => $title ) {
+ $ns = $title->getNamespace();
+ $dbkey = $title->getDBkey();
+
+ // Handle converted titles
+ if ( !isset( $this->mAllPages[$ns][$dbkey] ) &&
+ isset(
$this->mConvertedTitles[$title->getPrefixedText()] )
+ ) {
+ $title = Title::newFromText(
$this->mConvertedTitles[$title->getPrefixedText()] );
+ $ns = $title->getNamespace();
+ $dbkey = $title->getDBkey();
+ }
+
+ if ( isset( $this->mAllPages[$ns][$dbkey] ) ) {
+ $this->mGoodRevIDs[$revid] =
$this->mAllPages[$ns][$dbkey];
+ $this->mDeletedRevIDs[$revid] =
$this->mAllPages[$ns][$dbkey];
+ } else {
+ $remaining[$revid] = true;
+ }
+ }
+
+ $this->mMissingRevIDs = array_keys( $remaining );
+ }
}
/**
diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php
index 07670f6..b1c092e 100644
--- a/includes/api/ApiParamInfo.php
+++ b/includes/api/ApiParamInfo.php
@@ -331,6 +331,7 @@
$this->context->msg(
"apihelp-{$path}-paraminfo-{$tag}" )
->numParams( count( $i
) )
->params(
$this->context->getLanguage()->commaList( $i ) )
+ ->params(
$module->getModulePrefix() )
) );
$result->setSubelements( $info, 'text'
);
$item['info'][] = $info;
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index a091663..5a0491a 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -45,6 +45,7 @@
'categories' => 'ApiQueryCategories',
'categoryinfo' => 'ApiQueryCategoryInfo',
'contributors' => 'ApiQueryContributors',
+ 'deletedrevisions' => 'ApiQueryDeletedRevisions',
'duplicatefiles' => 'ApiQueryDuplicateFiles',
'extlinks' => 'ApiQueryExternalLinks',
'fileusage' => 'ApiQueryBacklinksprop',
@@ -69,6 +70,7 @@
*/
private static $QueryListModules = array(
'allcategories' => 'ApiQueryAllCategories',
+ 'alldeletedrevisions' => 'ApiQueryAllDeletedRevisions',
'allfileusages' => 'ApiQueryAllLinks',
'allimages' => 'ApiQueryAllImages',
'alllinks' => 'ApiQueryAllLinks',
diff --git a/includes/api/ApiQueryAllDeletedRevisions.php
b/includes/api/ApiQueryAllDeletedRevisions.php
new file mode 100644
index 0000000..0292e9a
--- /dev/null
+++ b/includes/api/ApiQueryAllDeletedRevisions.php
@@ -0,0 +1,384 @@
+<?php
+/**
+ * Created on Oct 3, 2014
+ *
+ * Copyright © 2014 Brad Jorsch "[email protected]"
+ *
+ * Heavily based on ApiQueryDeletedrevs,
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Query module to enumerate all deleted revisions.
+ *
+ * @ingroup API
+ */
+class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
+
+ public function __construct( ApiQuery $query, $moduleName ) {
+ parent::__construct( $query, $moduleName, 'adr' );
+ }
+
+ /**
+ * @param ApiPageSet $resultPageSet
+ * @return void
+ */
+ protected function run( ApiPageSet $resultPageSet = null ) {
+ $user = $this->getUser();
+ // Before doing anything at all, let's check permissions
+ if ( !$user->isAllowed( 'deletedhistory' ) ) {
+ $this->dieUsage(
+ 'You don\'t have permission to view deleted
revision information',
+ 'permissiondenied'
+ );
+ }
+
+ $db = $this->getDB();
+ $params = $this->extractRequestParams( false );
+
+ $result = $this->getResult();
+ $pageSet = $this->getPageSet();
+ $titles = $pageSet->getTitles();
+
+ // This module operates in two modes:
+ // 'user': List deleted revs by a certain user
+ // 'all': List all deleted revs in NS
+ $mode = 'all';
+ if ( !is_null( $params['user'] ) ) {
+ $mode = 'user';
+ }
+
+ if ( $mode == 'user' ) {
+ foreach ( array( 'from', 'to', 'prefix', 'excludeuser'
) as $param ) {
+ if ( !is_null( $params[$param] ) ) {
+ $p = $this->getModulePrefix();
+ $this->dieUsage( "The '{$p}{$param}'
parameter cannot be used with '{$p}user'",
+ 'badparams' );
+ }
+ }
+ } else {
+ foreach ( array( 'start', 'end' ) as $param ) {
+ if ( !is_null( $params[$param] ) ) {
+ $p = $this->getModulePrefix();
+ $this->dieUsage( "The '{$p}{$param}'
parameter may only be used with '{$p}user'",
+ 'badparams' );
+ }
+ }
+ }
+
+ $this->addTables( 'archive' );
+ if ( $resultPageSet === null ) {
+ $this->parseParameters( $params );
+ $this->addFields( Revision::selectArchiveFields() );
+ $this->addFields( array( 'ar_title', 'ar_namespace' ) );
+ } else {
+ $this->limit = $this->getParameter( 'limit' ) ?: 10;
+ $this->addFields( array( 'ar_title', 'ar_namespace',
'ar_timestamp', 'ar_rev_id', 'ar_id' ) );
+ }
+
+ if ( $this->fld_tags ) {
+ $this->addTables( 'tag_summary' );
+ $this->addJoinConds(
+ array( 'tag_summary' => array( 'LEFT JOIN',
array( 'ar_rev_id=ts_rev_id' ) ) )
+ );
+ $this->addFields( 'ts_tags' );
+ }
+
+ if ( !is_null( $params['tag'] ) ) {
+ $this->addTables( 'change_tag' );
+ $this->addJoinConds(
+ array( 'change_tag' => array( 'INNER JOIN',
array( 'ar_rev_id=ct_rev_id' ) ) )
+ );
+ $this->addWhereFld( 'ct_tag', $params['tag'] );
+ }
+
+ if ( $this->fld_content || !is_null( $this->diffto ) ||
!is_null( $this->difftotext ) ) {
+ // Modern MediaWiki has the content for deleted revs in
the 'text'
+ // table using fields old_text and old_flags. But
revisions deleted
+ // pre-1.5 store the content in the 'archive' table
directly using
+ // fields ar_text and ar_flags, and no corresponding
'text' row. So
+ // we have to LEFT JOIN and fetch all four fields.
+ $this->addTables( 'text' );
+ $this->addJoinConds(
+ array( 'text' => array( 'LEFT JOIN', array(
'ar_text_id=old_id' ) ) )
+ );
+ $this->addFields( array( 'ar_text', 'ar_flags',
'old_text', 'old_flags' ) );
+
+ // This also means stricter restrictions
+ if ( !$user->isAllowedAny( 'undelete', 'deletedtext' )
) {
+ $this->dieUsage(
+ 'You don\'t have permission to view
deleted revision content',
+ 'permissiondenied'
+ );
+ }
+ }
+
+ $dir = $params['dir'];
+ $miser_ns = null;
+
+ if ( $mode == 'all' ) {
+ if ( $params['namespace'] !== null ) {
+ $this->addWhereFld( 'ar_namespace',
$params['namespace'] );
+ }
+
+ $from = $params['from'] === null
+ ? null
+ : $this->titlePartToKey( $params['from'],
$params['namespace'] );
+ $to = $params['to'] === null
+ ? null
+ : $this->titlePartToKey( $params['to'],
$params['namespace'] );
+ $this->addWhereRange( 'ar_title', $dir, $from, $to );
+
+ if ( isset( $params['prefix'] ) ) {
+ $this->addWhere( 'ar_title' . $db->buildLike(
+ $this->titlePartToKey(
$params['prefix'], $params['namespace'] ),
+ $db->anyString() ) );
+ }
+ } else {
+ if ( $this->getConfig()->get( 'MiserMode' ) ) {
+ $miser_ns = $params['namespace'];
+ } else {
+ $this->addWhereFld( 'ar_namespace',
$params['namespace'] );
+ }
+ $this->addTimestampWhereRange( 'ar_timestamp', $dir,
$params['start'], $params['end'] );
+ }
+
+ if ( !is_null( $params['user'] ) ) {
+ $this->addWhereFld( 'ar_user_text', $params['user'] );
+ } elseif ( !is_null( $params['excludeuser'] ) ) {
+ $this->addWhere( 'ar_user_text != ' .
+ $db->addQuotes( $params['excludeuser'] ) );
+ }
+
+ if ( !is_null( $params['user'] ) || !is_null(
$params['excludeuser'] ) ) {
+ // Paranoia: avoid brute force searches (bug 17342)
+ // (shouldn't be able to get here without
'deletedhistory', but
+ // check it again just in case)
+ if ( !$user->isAllowed( 'deletedhistory' ) ) {
+ $bitmask = Revision::DELETED_USER;
+ } elseif ( !$user->isAllowedAny( 'suppressrevision',
'viewsuppressed' ) ) {
+ $bitmask = Revision::DELETED_USER |
Revision::DELETED_RESTRICTED;
+ } else {
+ $bitmask = 0;
+ }
+ if ( $bitmask ) {
+ $this->addWhere( $db->bitAnd( 'ar_deleted',
$bitmask ) . " != $bitmask" );
+ }
+ }
+
+ if ( !is_null( $params['continue'] ) ) {
+ $cont = explode( '|', $params['continue'] );
+ $op = ( $dir == 'newer' ? '>' : '<' );
+ if ( $mode == 'all' ) {
+ $this->dieContinueUsageIf( count( $cont ) != 4
);
+ $ns = intval( $cont[0] );
+ $this->dieContinueUsageIf( strval( $ns ) !==
$cont[0] );
+ $title = $db->addQuotes( $cont[1] );
+ $ts = $db->addQuotes( $db->timestamp( $cont[2]
) );
+ $ar_id = (int)$cont[3];
+ $this->dieContinueUsageIf( strval( $ar_id ) !==
$cont[3] );
+ $this->addWhere( "ar_namespace $op $ns OR " .
+ "(ar_namespace = $ns AND " .
+ "(ar_title $op $title OR " .
+ "(ar_title = $title AND " .
+ "(ar_timestamp $op $ts OR " .
+ "(ar_timestamp = $ts AND " .
+ "ar_id $op= $ar_id)))))" );
+ } else {
+ $this->dieContinueUsageIf( count( $cont ) != 2
);
+ $ts = $db->addQuotes( $db->timestamp( $cont[0]
) );
+ $ar_id = (int)$cont[1];
+ $this->dieContinueUsageIf( strval( $ar_id ) !==
$cont[1] );
+ $this->addWhere( "ar_timestamp $op $ts OR " .
+ "(ar_timestamp = $ts AND " .
+ "ar_id $op= $ar_id)" );
+ }
+ }
+
+ $this->addOption( 'LIMIT', $this->limit + 1 );
+
+ $sort = ( $dir == 'newer' ? '' : ' DESC' );
+ $orderby = array();
+ if ( $mode == 'all' ) {
+ // Targeting index name_title_timestamp
+ if ( $params['namespace'] === null || count(
array_unique( $params['namespace'] ) ) > 1 ) {
+ $orderby[] = "ar_namespace $sort";
+ }
+ $orderby[] = "ar_title $sort";
+ $orderby[] = "ar_timestamp $sort";
+ $orderby[] = "ar_id $sort";
+ } else {
+ // Targeting index usertext_timestamp
+ // 'user' is always constant.
+ $orderby[] = "ar_timestamp $sort";
+ $orderby[] = "ar_id $sort";
+ }
+ $this->addOption( 'ORDER BY', $orderby );
+
+ $res = $this->select( __METHOD__ );
+ $pageMap = array(); // Maps ns&title to array index
+ $count = 0;
+ $nextIndex = 0;
+ $generated = array();
+ foreach ( $res as $row ) {
+ if ( ++$count > $this->limit ) {
+ // We've had enough
+ if ( $mode == 'all' ) {
+ $this->setContinueEnumParameter(
'continue',
+
"$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+ );
+ } else {
+ $this->setContinueEnumParameter(
'continue', "$row->ar_timestamp|$row->ar_id" );
+ }
+ break;
+ }
+
+ // Miser mode namespace check
+ if ( $miser_ns !== null && !in_array(
$row->ar_namespace, $miser_ns ) ) {
+ continue;
+ }
+
+ if ( $resultPageSet !== null ) {
+ if ( $params['generatetitles'] ) {
+ $key =
"{$row->ar_namespace}:{$row->ar_title}";
+ if ( !isset( $generated[$key] ) ) {
+ $generated[$key] =
Title::makeTitle( $row->ar_namespace, $row->ar_title );
+ }
+ } else {
+ $generated[] = $row->ar_rev_id;
+ }
+ } else {
+ $revision = Revision::newFromArchiveRow( $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();
+ $a = array(
+ 'pageid' =>
$title->getArticleID(),
+ 'revisions' => array( $rev ),
+ );
+ $result->setIndexedTagName(
$a['revisions'], 'rev' );
+ ApiQueryBase::addTitleInfo( $a, $title
);
+ $fit = $result->addValue( array(
'query', $this->getModuleName() ), $index, $a );
+ } else {
+ $index =
$pageMap[$row->ar_namespace][$row->ar_title];
+ $fit = $result->addValue(
+ array( 'query',
$this->getModuleName(), $index, 'revisions' ),
+ null, $rev );
+ }
+ if ( !$fit ) {
+ if ( $mode == 'all' ) {
+
$this->setContinueEnumParameter( 'continue',
+
"$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+ );
+ } else {
+
$this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" );
+ }
+ break;
+ }
+ }
+ }
+
+ if ( $resultPageSet !== null ) {
+ if ( $params['generatetitles'] ) {
+ $resultPageSet->populateFromTitles( $generated
);
+ } else {
+ $resultPageSet->populateFromRevisionIDs(
$generated );
+ }
+ } else {
+ $result->setIndexedTagName_internal( array( 'query',
$this->getModuleName() ), 'page' );
+ }
+ }
+
+ public function getAllowedParams() {
+ $ret = parent::getAllowedParams() + array(
+ 'user' => array(
+ ApiBase::PARAM_TYPE => 'user'
+ ),
+ 'namespace' => array(
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_TYPE => 'namespace',
+ ApiBase::PARAM_DFLT => null,
+ ),
+ 'start' => array(
+ ApiBase::PARAM_TYPE => 'timestamp',
+ ApiBase::PARAM_HELP_MSG_INFO => array( array(
'useronly' ) ),
+ ),
+ 'end' => array(
+ ApiBase::PARAM_TYPE => 'timestamp',
+ ApiBase::PARAM_HELP_MSG_INFO => array( array(
'useronly' ) ),
+ ),
+ 'dir' => array(
+ ApiBase::PARAM_TYPE => array(
+ 'newer',
+ 'older'
+ ),
+ ApiBase::PARAM_DFLT => 'older',
+ ApiBase::PARAM_HELP_MSG =>
'api-help-param-direction',
+ ),
+ 'from' => array(
+ ApiBase::PARAM_HELP_MSG_INFO => array( array(
'nonuseronly' ) ),
+ ),
+ 'to' => array(
+ ApiBase::PARAM_HELP_MSG_INFO => array( array(
'nonuseronly' ) ),
+ ),
+ 'prefix' => array(
+ ApiBase::PARAM_HELP_MSG_INFO => array( array(
'nonuseronly' ) ),
+ ),
+ 'excludeuser' => array(
+ ApiBase::PARAM_TYPE => 'user',
+ ApiBase::PARAM_HELP_MSG_INFO => array( array(
'nonuseronly' ) ),
+ ),
+ 'tag' => null,
+ 'continue' => array(
+ ApiBase::PARAM_HELP_MSG =>
'api-help-param-continue',
+ ),
+ 'generatetitles' => array(
+ ApiBase::PARAM_DFLT => false
+ ),
+ );
+
+ if ( $this->getConfig()->get( 'MiserMode' ) ) {
+ $ret['user'][ApiBase::PARAM_HELP_MSG_APPEND] = array(
+
'apihelp-query+alldeletedrevisions-param-miser-user-namespace',
+ );
+ $ret['namespace'][ApiBase::PARAM_HELP_MSG_APPEND] =
array(
+
'apihelp-query+alldeletedrevisions-param-miser-user-namespace',
+ );
+ }
+
+ return $ret;
+ }
+
+ protected function getExamplesMessages() {
+ return array(
+
'action=query&list=alldeletedrevisions&adruser=Example&adrlimit=50'
+ =>
'apihelp-query+alldeletedrevisions-example-user',
+
'action=query&list=alldeletedrevisions&adrdir=newer&adrlimit=50'
+ =>
'apihelp-query+alldeletedrevisions-example-ns-main',
+ );
+ }
+
+ public function getHelpUrls() {
+ return 'https://www.mediawiki.org/wiki/API:Alldeletedrevisions';
+ }
+}
diff --git a/includes/api/ApiQueryDeletedRevisions.php
b/includes/api/ApiQueryDeletedRevisions.php
new file mode 100644
index 0000000..0271037
--- /dev/null
+++ b/includes/api/ApiQueryDeletedRevisions.php
@@ -0,0 +1,304 @@
+<?php
+/**
+ * Created on Oct 3, 2014
+ *
+ * Copyright © 2014 Brad Jorsch "[email protected]"
+ *
+ * Heavily based on ApiQueryDeletedrevs,
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Query module to enumerate deleted revisions for pages.
+ *
+ * @ingroup API
+ */
+class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
+
+ public function __construct( ApiQuery $query, $moduleName ) {
+ parent::__construct( $query, $moduleName, 'drv' );
+ }
+
+ protected function run( ApiPageSet $resultPageSet = null ) {
+ $user = $this->getUser();
+ // Before doing anything at all, let's check permissions
+ if ( !$user->isAllowed( 'deletedhistory' ) ) {
+ $this->dieUsage(
+ 'You don\'t have permission to view deleted
revision information',
+ 'permissiondenied'
+ );
+ }
+
+ $result = $this->getResult();
+ $pageSet = $this->getPageSet();
+ $pageMap = $pageSet->getGoodAndMissingTitlesByNamespace();
+ $pageCount = count( $pageSet->getGoodAndMissingTitles() );
+ $revCount = $pageSet->getRevisionCount();
+ if ( $revCount === 0 && $pageCount === 0 ) {
+ // Nothing to do
+ return;
+ }
+ if ( $revCount !== 0 && count(
$pageSet->getDeletedRevisionIDs() ) === 0 ) {
+ // Nothing to do, revisions were supplied but none are
deleted
+ return;
+ }
+
+ $params = $this->extractRequestParams( false );
+
+ $db = $this->getDB();
+
+ if ( !is_null( $params['user'] ) && !is_null(
$params['excludeuser'] ) ) {
+ $this->dieUsage( 'user and excludeuser cannot be used
together', 'badparams' );
+ }
+
+ $this->addTables( 'archive' );
+ if ( $resultPageSet === null ) {
+ $this->parseParameters( $params );
+ $this->addFields( Revision::selectArchiveFields() );
+ $this->addFields( array( 'ar_title', 'ar_namespace' ) );
+ } else {
+ $this->limit = $this->getParameter( 'limit' ) ?: 10;
+ $this->addFields( array( 'ar_title', 'ar_namespace',
'ar_timestamp', 'ar_rev_id', 'ar_id' ) );
+ }
+
+ if ( $this->fld_tags ) {
+ $this->addTables( 'tag_summary' );
+ $this->addJoinConds(
+ array( 'tag_summary' => array( 'LEFT JOIN',
array( 'ar_rev_id=ts_rev_id' ) ) )
+ );
+ $this->addFields( 'ts_tags' );
+ }
+
+ if ( !is_null( $params['tag'] ) ) {
+ $this->addTables( 'change_tag' );
+ $this->addJoinConds(
+ array( 'change_tag' => array( 'INNER JOIN',
array( 'ar_rev_id=ct_rev_id' ) ) )
+ );
+ $this->addWhereFld( 'ct_tag', $params['tag'] );
+ }
+
+ if ( $this->fld_content || !is_null( $this->diffto ) ||
!is_null( $this->difftotext ) ) {
+ // Modern MediaWiki has the content for deleted revs in
the 'text'
+ // table using fields old_text and old_flags. But
revisions deleted
+ // pre-1.5 store the content in the 'archive' table
directly using
+ // fields ar_text and ar_flags, and no corresponding
'text' row. So
+ // we have to LEFT JOIN and fetch all four fields.
+ $this->addTables( 'text' );
+ $this->addJoinConds(
+ array( 'text' => array( 'LEFT JOIN', array(
'ar_text_id=old_id' ) ) )
+ );
+ $this->addFields( array( 'ar_text', 'ar_flags',
'old_text', 'old_flags' ) );
+
+ // This also means stricter restrictions
+ if ( !$user->isAllowedAny( 'undelete', 'deletedtext' )
) {
+ $this->dieUsage(
+ 'You don\'t have permission to view
deleted revision content',
+ 'permissiondenied'
+ );
+ }
+ }
+
+ $dir = $params['dir'];
+
+ if ( $revCount !== 0 ) {
+ $this->addWhere( array(
+ 'ar_rev_id' => array_keys(
$pageSet->getDeletedRevisionIDs() )
+ ) );
+ } else {
+ // We need a custom WHERE clause that matches all
titles.
+ $lb = new LinkBatch(
$pageSet->getGoodAndMissingTitles() );
+ $where = $lb->constructSet( 'ar', $db );
+ $this->addWhere( $where );
+ }
+
+ if ( !is_null( $params['user'] ) ) {
+ $this->addWhereFld( 'ar_user_text', $params['user'] );
+ } elseif ( !is_null( $params['excludeuser'] ) ) {
+ $this->addWhere( 'ar_user_text != ' .
+ $db->addQuotes( $params['excludeuser'] ) );
+ }
+
+ if ( !is_null( $params['user'] ) || !is_null(
$params['excludeuser'] ) ) {
+ // Paranoia: avoid brute force searches (bug 17342)
+ // (shouldn't be able to get here without
'deletedhistory', but
+ // check it again just in case)
+ if ( !$user->isAllowed( 'deletedhistory' ) ) {
+ $bitmask = Revision::DELETED_USER;
+ } elseif ( !$user->isAllowedAny( 'suppressrevision',
'viewsuppressed' ) ) {
+ $bitmask = Revision::DELETED_USER |
Revision::DELETED_RESTRICTED;
+ } else {
+ $bitmask = 0;
+ }
+ if ( $bitmask ) {
+ $this->addWhere( $db->bitAnd( 'ar_deleted',
$bitmask ) . " != $bitmask" );
+ }
+ }
+
+ if ( !is_null( $params['continue'] ) ) {
+ $cont = explode( '|', $params['continue'] );
+ $op = ( $dir == 'newer' ? '>' : '<' );
+ if ( $revCount !== 0 ) {
+ $this->dieContinueUsageIf( count( $cont ) != 2
);
+ $rev = intval( $cont[0] );
+ $this->dieContinueUsageIf( strval( $rev ) !==
$cont[0] );
+ $ar_id = (int)$cont[1];
+ $this->dieContinueUsageIf( strval( $ar_id ) !==
$cont[1] );
+ $this->addWhere( "ar_rev_id $op $rev OR " .
+ "(ar_rev_id = $rev AND " .
+ "ar_id $op= $ar_id)" );
+ } else {
+ $this->dieContinueUsageIf( count( $cont ) != 4
);
+ $ns = intval( $cont[0] );
+ $this->dieContinueUsageIf( strval( $ns ) !==
$cont[0] );
+ $title = $db->addQuotes( $cont[1] );
+ $ts = $db->addQuotes( $db->timestamp( $cont[2]
) );
+ $ar_id = (int)$cont[3];
+ $this->dieContinueUsageIf( strval( $ar_id ) !==
$cont[3] );
+ $this->addWhere( "ar_namespace $op $ns OR " .
+ "(ar_namespace = $ns AND " .
+ "(ar_title $op $title OR " .
+ "(ar_title = $title AND " .
+ "(ar_timestamp $op $ts OR " .
+ "(ar_timestamp = $ts AND " .
+ "ar_id $op= $ar_id)))))" );
+ }
+ }
+
+ $this->addOption( 'LIMIT', $this->limit + 1 );
+
+ if ( $revCount !== 0 ) {
+ // Sort by ar_rev_id when querying by ar_rev_id
+ $this->addWhereRange( 'ar_rev_id', $dir, null, null );
+ } else {
+ // Sort by ns and title in the same order as timestamp
for efficiency
+ // But only when not already unique in the query
+ if ( count( $pageMap ) > 1 ) {
+ $this->addWhereRange( 'ar_namespace', $dir,
null, null );
+ }
+ $oneTitle = key( reset( $pageMap ) );
+ foreach ( $pageMap as $pages ) {
+ if ( count( $pages ) > 1 || key( $pages ) !==
$oneTitle ) {
+ $this->addWhereRange( 'ar_title', $dir,
null, null );
+ break;
+ }
+ }
+ $this->addTimestampWhereRange( 'ar_timestamp', $dir,
$params['start'], $params['end'] );
+ }
+ // Include in ORDER BY for uniqueness
+ $this->addWhereRange( 'ar_id', $dir, null, null );
+
+ $res = $this->select( __METHOD__ );
+ $count = 0;
+ $generated = array();
+ foreach ( $res as $row ) {
+ if ( ++$count > $this->limit ) {
+ // We've had enough
+ $this->setContinueEnumParameter( 'continue',
+ $revCount
+ ? "$row->ar_rev_id|$row->ar_id"
+ :
"$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+ );
+ break;
+ }
+
+ if ( $resultPageSet !== null ) {
+ $generated[] = $row->ar_rev_id;
+ } else {
+ if ( !isset(
$pageMap[$row->ar_namespace][$row->ar_title] ) ) {
+ // Was it converted?
+ $title = Title::makeTitle(
$row->ar_namespace, $row->ar_title );
+ $converted =
$pageSet->getConvertedTitles();
+ if ( $title && isset(
$converted[$title->getPrefixedText()] ) ) {
+ $title = Title::newFromText(
$converted[$title->getPrefixedText()] );
+ if ( $title && isset(
$pageMap[$title->getNamespace()][$title->getDBkey()] ) ) {
+
$pageMap[$row->ar_namespace][$row->ar_title] =
+
$pageMap[$title->getNamespace()][$title->getDBkey()];
+ }
+ }
+ }
+ if ( !isset(
$pageMap[$row->ar_namespace][$row->ar_title] ) ) {
+ ApiBase::dieDebug( "Found row in
archive (ar_id={$row->ar_id}) that didn't " .
+ "get processed by ApiPageSet" );
+ }
+
+ $fit = $this->addPageSubItem(
+
$pageMap[$row->ar_namespace][$row->ar_title],
+ $this->extractRevisionInfo(
Revision::newFromArchiveRow( $row ), $row ),
+ 'rev'
+ );
+ if ( !$fit ) {
+ $this->setContinueEnumParameter(
'continue',
+ $revCount
+ ?
"$row->ar_rev_id|$row->ar_id"
+ :
"$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+ );
+ break;
+ }
+ }
+ }
+
+ if ( $resultPageSet !== null ) {
+ $resultPageSet->populateFromRevisionIDs( $generated );
+ }
+ }
+
+ public function getAllowedParams() {
+ return parent::getAllowedParams() + array(
+ 'start' => array(
+ ApiBase::PARAM_TYPE => 'timestamp',
+ ),
+ 'end' => array(
+ ApiBase::PARAM_TYPE => 'timestamp',
+ ),
+ 'dir' => array(
+ ApiBase::PARAM_TYPE => array(
+ 'newer',
+ 'older'
+ ),
+ ApiBase::PARAM_DFLT => 'older',
+ ApiBase::PARAM_HELP_MSG =>
'api-help-param-direction',
+ ),
+ 'tag' => null,
+ 'user' => array(
+ ApiBase::PARAM_TYPE => 'user'
+ ),
+ 'excludeuser' => array(
+ ApiBase::PARAM_TYPE => 'user'
+ ),
+ 'continue' => array(
+ ApiBase::PARAM_HELP_MSG =>
'api-help-param-continue',
+ ),
+ );
+ }
+
+ protected function getExamplesMessages() {
+ return array(
+
'action=query&prop=deletedrevisions&titles=Main%20Page|Talk:Main%20Page&' .
+ 'drvprop=user|comment|content'
+ =>
'apihelp-query+deletedrevisions-example-titles',
+ 'action=query&prop=deletedrevisions&revids=123456'
+ =>
'apihelp-query+deletedrevisions-example-revids',
+ );
+ }
+
+ public function getHelpUrls() {
+ return
'https://www.mediawiki.org/wiki/API:Properties#deletedrevisions_.2F_drv';
+ }
+}
diff --git a/includes/api/ApiQueryDeletedrevs.php
b/includes/api/ApiQueryDeletedrevs.php
index 4a5f5fd..f828255 100644
--- a/includes/api/ApiQueryDeletedrevs.php
+++ b/includes/api/ApiQueryDeletedrevs.php
@@ -28,6 +28,7 @@
* Query module to enumerate all deleted revisions.
*
* @ingroup API
+ * @deprecated since 1.25
*/
class ApiQueryDeletedrevs extends ApiQueryBase {
@@ -44,6 +45,12 @@
'permissiondenied'
);
}
+
+ $this->setWarning(
+ 'list=deletedrevs has been deprecated. Please use
prop=deletedrevisions or ' .
+ 'list=alldeletedrevisions instead.'
+ );
+ $this->logFeatureUsage( 'action=query&list=deletedrevs' );
$db = $this->getDB();
$params = $this->extractRequestParams( false );
@@ -420,6 +427,10 @@
$result->setIndexedTagName_internal( array( 'query',
$this->getModuleName() ), 'page' );
}
+ public function isDeprecated() {
+ return true;
+ }
+
public function getAllowedParams() {
return array(
'start' => array(
diff --git a/includes/api/ApiQueryRevisions.php
b/includes/api/ApiQueryRevisions.php
index f5ad9d0..2e980f3 100644
--- a/includes/api/ApiQueryRevisions.php
+++ b/includes/api/ApiQueryRevisions.php
@@ -32,19 +32,13 @@
*
* @ingroup API
*/
-class ApiQueryRevisions extends ApiQueryBase {
+class ApiQueryRevisions extends ApiQueryRevisionsBase {
- private $diffto, $difftotext, $expandTemplates, $generateXML, $section,
- $token, $parseContent, $contentFormat;
+ private $token = null;
public function __construct( ApiQuery $query, $moduleName ) {
parent::__construct( $query, $moduleName, 'rv' );
}
-
- private $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;
private $tokenFunctions;
@@ -89,7 +83,7 @@
array( $title->getPrefixedText(), $rev->getUserText() )
);
}
- public function execute() {
+ protected function run( ApiPageSet $resultPageSet = null ) {
$params = $this->extractRequestParams( false );
// If any of those parameters are used, work in 'enumeration'
mode.
@@ -107,6 +101,11 @@
// Optimization -- nothing to do
if ( $revCount === 0 && $pageCount === 0 ) {
+ // Nothing to do
+ return;
+ }
+ if ( $revCount > 0 && count( $pageSet->getLiveRevisionIDs() )
=== 0 ) {
+ // We're in revisions mode but all given revisions are
deleted
return;
}
@@ -127,75 +126,32 @@
);
}
- if ( !is_null( $params['difftotext'] ) ) {
- $this->difftotext = $params['difftotext'];
- } elseif ( !is_null( $params['diffto'] ) ) {
- if ( $params['diffto'] == 'cur' ) {
- $params['diffto'] = 0;
- }
- if ( ( !ctype_digit( $params['diffto'] ) ||
$params['diffto'] < 0 )
- && $params['diffto'] != 'prev' &&
$params['diffto'] != 'next'
- ) {
- $this->dieUsage(
- 'rvdiffto must be set to a non-negative
number, "prev", "next" or "cur"',
- 'diffto'
- );
- }
- // Check whether the revision exists and is readable,
- // DifferenceEngine returns a rather ambiguous empty
- // string if that's not the case
- if ( $params['diffto'] != 0 ) {
- $difftoRev = Revision::newFromID(
$params['diffto'] );
- if ( !$difftoRev ) {
- $this->dieUsageMsg( array(
'nosuchrevid', $params['diffto'] ) );
- }
- if ( !$difftoRev->userCan(
Revision::DELETED_TEXT, $this->getUser() ) ) {
- $this->setWarning( "Couldn't diff to
r{$difftoRev->getID()}: content is hidden" );
- $params['diffto'] = null;
- }
- }
- $this->diffto = $params['diffto'];
+ // In non-enum mode, rvlimit can't be directly used. Use the
maximum
+ // allowed value.
+ if ( !$enumRevMode ) {
+ $this->setParsedLimit = false;
+ $params['limit'] = 'max';
}
$db = $this->getDB();
- $this->addTables( 'page' );
- $this->addFields( Revision::selectFields() );
- $this->addWhere( 'page_id = rev_page' );
+ $this->addTables( array( 'revision', 'page' ) );
+ $this->addJoinConds(
+ array( 'page' => array( 'INNER JOIN', array( 'page_id =
rev_page' ) ) )
+ );
- $prop = array_flip( $params['prop'] );
-
- // Optional fields
- $this->fld_ids = isset( $prop['ids'] );
- // $this->addFieldsIf('rev_text_id', $this->fld_ids); // should
this be exposed?
- $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_contentmodel = isset( $prop['contentmodel'] );
- $this->fld_userid = isset( $prop['userid'] );
- $this->fld_user = isset( $prop['user'] );
- $this->token = $params['token'];
-
- if ( !empty( $params['contentformat'] ) ) {
- $this->contentFormat = $params['contentformat'];
+ if ( $resultPageSet === null ) {
+ $this->parseParameters( $params );
+ $this->token = $params['token'];
+ $this->addFields( Revision::selectFields() );
+ if ( $this->token !== null || $pageCount > 0 ) {
+ $this->addFields( Revision::selectPageFields()
);
+ }
+ } else {
+ $this->limit = $this->getParameter( 'limit' ) ?: 10;
+ $this->addFields( array( 'rev_id', 'rev_page' ) );
}
- $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 :
ApiBase::LIMIT_BIG1 );
- $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 :
ApiBase::LIMIT_BIG2 );
- $limit = $params['limit'];
- if ( $limit == 'max' ) {
- $limit = $this->getMain()->canApiHighLimits() ? $botMax
: $userMax;
- $this->getResult()->setParsedLimit(
$this->getModuleName(), $limit );
- }
-
- if ( !is_null( $this->token ) || $pageCount > 0 ) {
- $this->addFields( Revision::selectPageFields() );
- }
-
- if ( isset( $prop['tags'] ) ) {
- $this->fld_tags = true;
+ if ( $this->fld_tags ) {
$this->addTables( 'tag_summary' );
$this->addJoinConds(
array( 'tag_summary' => array( 'LEFT JOIN',
array( 'rev_id=ts_rev_id' ) ) )
@@ -211,7 +167,7 @@
$this->addWhereFld( 'ct_tag', $params['tag'] );
}
- if ( isset( $prop['content'] ) || !is_null( $this->diffto ) ||
!is_null( $this->difftotext ) ) {
+ if ( $this->fld_content || !is_null( $this->diffto ) ||
!is_null( $this->difftotext ) ) {
// For each page we will request, the user must have
read rights for that page
$user = $this->getUser();
/** @var $title Title */
@@ -224,28 +180,11 @@
}
$this->addTables( 'text' );
- $this->addWhere( 'rev_text_id=old_id' );
+ $this->addJoinConds(
+ array( 'text' => array( 'INNER JOIN', array(
'rev_text_id=old_id' ) ) )
+ );
$this->addFields( 'old_id' );
$this->addFields( Revision::selectTextFields() );
-
- $this->fld_content = isset( $prop['content'] );
-
- $this->expandTemplates = $params['expandtemplates'];
- $this->generateXML = $params['generatexml'];
- $this->parseContent = $params['parse'];
- if ( $this->parseContent ) {
- // Must manually initialize unset limit
- if ( is_null( $limit ) ) {
- $limit = 1;
- }
- // We are only going to parse 1 revision per
request
- $this->validateLimit( 'limit', $limit, 1, 1, 1
);
- }
- if ( isset( $params['section'] ) ) {
- $this->section = $params['section'];
- } else {
- $this->section = false;
- }
}
// add user name, if needed
@@ -254,9 +193,6 @@
$this->addJoinConds( array( 'user' =>
Revision::userJoinCond() ) );
$this->addFields( Revision::selectUserFields() );
}
-
- // Bug 24166 - API error when using rvprop=tags
- $this->addTables( 'revision' );
if ( $enumRevMode ) {
// This is mostly to prevent parameter errors (and
optimize SQL?)
@@ -300,12 +236,6 @@
$params['start'], $params['end'], false
);
}
- // must manually initialize unset limit
- if ( is_null( $limit ) ) {
- $limit = 10;
- }
- $this->validateLimit( 'limit', $limit, 1, $userMax,
$botMax );
-
// There is only one ID, use it
$ids = array_keys( $pageSet->getGoodTitles() );
$this->addWhereFld( 'rev_page', reset( $ids ) );
@@ -330,11 +260,7 @@
}
}
} elseif ( $revCount > 0 ) {
- $max = $this->getMain()->canApiHighLimits() ? $botMax :
$userMax;
- $revs = $pageSet->getRevisionIDs();
- if ( self::truncateArray( $revs, $max ) ) {
- $this->setWarning( "Too many values supplied
for parameter 'revids': the limit is $max" );
- }
+ $revs = $pageSet->getLiveRevisionIDs();
// Get all revision IDs
$this->addWhereFld( 'rev_id', array_keys( $revs ) );
@@ -343,19 +269,11 @@
$this->addWhere( 'rev_id >= ' . intval(
$params['continue'] ) );
}
$this->addOption( 'ORDER BY', 'rev_id' );
-
- // assumption testing -- we should never get more then
$revCount rows.
- $limit = $revCount;
} elseif ( $pageCount > 0 ) {
- $max = $this->getMain()->canApiHighLimits() ? $botMax :
$userMax;
$titles = $pageSet->getGoodTitles();
- if ( self::truncateArray( $titles, $max ) ) {
- $this->setWarning( "Too many values supplied
for parameter 'titles': the limit is $max" );
- }
// When working in multi-page non-enumeration mode,
// limit to the latest revision only
- $this->addWhere( 'page_id=rev_page' );
$this->addWhere( 'page_latest=rev_id' );
// Get all page IDs
@@ -378,31 +296,20 @@
'rev_page',
'rev_id'
) );
-
- // assumption testing -- we should never get more then
$pageCount rows.
- $limit = $pageCount;
} else {
ApiBase::dieDebug( __METHOD__, 'param validation?' );
}
- $this->addOption( 'LIMIT', $limit + 1 );
+ $this->addOption( 'LIMIT', $this->limit + 1 );
$count = 0;
+ $generated = array();
$res = $this->select( __METHOD__ );
foreach ( $res as $row ) {
- if ( ++$count > $limit ) {
+ if ( ++$count > $this->limit ) {
// We've reached the one extra which shows that
there are
// additional pages to be had. Stop here...
- if ( !$enumRevMode ) {
- ApiBase::dieDebug( __METHOD__, 'Got
more rows then expected' ); // bug report
- }
- $this->setContinueEnumParameter( 'continue',
intval( $row->rev_id ) );
- break;
- }
-
- $fit = $this->addPageSubItem( $row->rev_page,
$this->extractRowInfo( $row ), 'rev' );
- if ( !$fit ) {
if ( $enumRevMode ) {
$this->setContinueEnumParameter(
'continue', intval( $row->rev_id ) );
} elseif ( $revCount > 0 ) {
@@ -413,313 +320,55 @@
}
break;
}
- }
- }
- private function extractRowInfo( $row ) {
- $revision = new Revision( $row );
- $title = $revision->getTitle();
- $user = $this->getUser();
- $vals = array();
- $anyHidden = false;
-
- if ( $this->fld_ids ) {
- $vals['revid'] = intval( $revision->getId() );
- // $vals['oldid'] = intval( $row->rev_text_id ); //
todo: should this be exposed?
- if ( !is_null( $revision->getParentId() ) ) {
- $vals['parentid'] = intval(
$revision->getParentId() );
- }
- }
-
- if ( $this->fld_flags && $revision->isMinor() ) {
- $vals['minor'] = '';
- }
-
- if ( $this->fld_user || $this->fld_userid ) {
- if ( $revision->isDeleted( Revision::DELETED_USER ) ) {
- $vals['userhidden'] = '';
- $anyHidden = true;
- }
- if ( $revision->userCan( Revision::DELETED_USER, $user
) ) {
- if ( $this->fld_user ) {
- $vals['user'] =
$revision->getRawUserText();
- }
- $userid = $revision->getRawUser();
- if ( !$userid ) {
- $vals['anon'] = '';
- }
-
- if ( $this->fld_userid ) {
- $vals['userid'] = $userid;
- }
- }
- }
-
- if ( $this->fld_timestamp ) {
- $vals['timestamp'] = wfTimestamp( TS_ISO_8601,
$revision->getTimestamp() );
- }
-
- if ( $this->fld_size ) {
- if ( !is_null( $revision->getSize() ) ) {
- $vals['size'] = intval( $revision->getSize() );
+ if ( $resultPageSet !== null ) {
+ $generated[] = $row->rev_id;
} else {
- $vals['size'] = 0;
- }
- }
+ $revision = new Revision( $row );
+ $rev = $this->extractRevisionInfo( $revision,
$row );
- if ( $this->fld_sha1 ) {
- if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
- $vals['sha1hidden'] = '';
- $anyHidden = true;
- }
- if ( $revision->userCan( Revision::DELETED_TEXT, $user
) ) {
- if ( $revision->getSha1() != '' ) {
- $vals['sha1'] = wfBaseConvert(
$revision->getSha1(), 36, 16, 40 );
- } else {
- $vals['sha1'] = '';
- }
- }
- }
-
- if ( $this->fld_contentmodel ) {
- $vals['contentmodel'] = $revision->getContentModel();
- }
-
- if ( $this->fld_comment || $this->fld_parsedcomment ) {
- if ( $revision->isDeleted( Revision::DELETED_COMMENT )
) {
- $vals['commenthidden'] = '';
- $anyHidden = true;
- }
- if ( $revision->userCan( Revision::DELETED_COMMENT,
$user ) ) {
- $comment = $revision->getRawComment();
-
- if ( $this->fld_comment ) {
- $vals['comment'] = $comment;
+ if ( $this->token !== null ) {
+ $title = $revision->getTitle();
+ $tokenFunctions =
$this->getTokenFunctions();
+ foreach ( $this->token as $t ) {
+ $val = call_user_func(
$tokenFunctions[$t], $title->getArticleID(), $title, $revision );
+ if ( $val === false ) {
+ $this->setWarning(
"Action '$t' is not allowed for the current user" );
+ } else {
+ $rev[$t . 'token'] =
$val;
+ }
+ }
}
- if ( $this->fld_parsedcomment ) {
- $vals['parsedcomment'] =
Linker::formatComment( $comment, $title );
- }
- }
- }
-
- if ( $this->fld_tags ) {
- if ( $row->ts_tags ) {
- $tags = explode( ',', $row->ts_tags );
- $this->getResult()->setIndexedTagName( $tags,
'tag' );
- $vals['tags'] = $tags;
- } else {
- $vals['tags'] = array();
- }
- }
-
- if ( !is_null( $this->token ) ) {
- $tokenFunctions = $this->getTokenFunctions();
- foreach ( $this->token as $t ) {
- $val = call_user_func( $tokenFunctions[$t],
$title->getArticleID(), $title, $revision );
- if ( $val === false ) {
- $this->setWarning( "Action '$t' is not
allowed for the current user" );
- } else {
- $vals[$t . 'token'] = $val;
- }
- }
- }
-
- $content = null;
- global $wgParser;
- if ( $this->fld_content || !is_null( $this->diffto ) ||
!is_null( $this->difftotext ) ) {
- $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->dieUsage(
- "There is no section
{$this->section} in r" . $revision->getId(),
- 'nosuchsection'
- );
- }
- }
- if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
- $vals['texthidden'] = '';
- $anyHidden = true;
- } elseif ( !$content ) {
- $vals['textmissing'] = '';
- }
- }
- if ( $this->fld_content && $content ) {
- $text = null;
-
- if ( $this->generateXML ) {
- 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( array( $dom,
'saveXML' ) ) ) {
- $xml = $dom->saveXML();
+ $fit = $this->addPageSubItem( $row->rev_page,
$rev, 'rev' );
+ if ( !$fit ) {
+ if ( $enumRevMode ) {
+
$this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
+ } elseif ( $revCount > 0 ) {
+
$this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
} else {
- $xml = $dom->__toString();
+
$this->setContinueEnumParameter( 'continue', intval( $row->rev_page ) .
+ '|' . intval(
$row->rev_id ) );
}
- $vals['parsetree'] = $xml;
- } else {
- $this->setWarning( "Conversion to XML
is supported for wikitext only, " .
- $title->getPrefixedDBkey() .
- " uses content model " .
$content->getModel() );
+ break;
}
- }
-
- if ( $this->expandTemplates && !$this->parseContent ) {
- #XXX: implement template expansion for all
content types in ContentHandler?
- if ( $content->getModel() ===
CONTENT_MODEL_WIKITEXT ) {
- $text = $content->getNativeData();
-
- $text = $wgParser->preprocess(
- $text,
- $title,
- ParserOptions::newFromContext(
$this->getContext() )
- );
- } else {
- $this->setWarning( "Template expansion
is supported for wikitext only, " .
- $title->getPrefixedDBkey() .
- " uses content model " .
$content->getModel() );
-
- $text = false;
- }
- }
- if ( $this->parseContent ) {
- $po = $content->getParserOutput(
- $title,
- $revision->getId(),
- ParserOptions::newFromContext(
$this->getContext() )
- );
- $text = $po->getText();
- }
-
- if ( $text === null ) {
- $format = $this->contentFormat ?
$this->contentFormat : $content->getDefaultFormat();
- $model = $content->getModel();
-
- if ( !$content->isSupportedFormat( $format ) ) {
- $name = $title->getPrefixedDBkey();
-
- $this->dieUsage( "The requested format
{$this->contentFormat} is not supported " .
- "for content model $model used
by $name", 'badformat' );
- }
-
- $text = $content->serialize( $format );
-
- // always include format and model.
- // Format is needed to deserialize, model is
needed to interpret.
- $vals['contentformat'] = $format;
- $vals['contentmodel'] = $model;
- }
-
- if ( $text !== false ) {
- ApiResult::setContent( $vals, $text );
}
}
- if ( $content && ( !is_null( $this->diffto ) || !is_null(
$this->difftotext ) ) ) {
- static $n = 0; // Number of uncached diffs we've had
-
- if ( $n < $this->getConfig()->get(
'APIMaxUncachedDiffs' ) ) {
- $vals['diff'] = array();
- $context = new DerivativeContext(
$this->getContext() );
- $context->setTitle( $title );
- $handler = $revision->getContentHandler();
-
- if ( !is_null( $this->difftotext ) ) {
- $model = $title->getContentModel();
-
- if ( $this->contentFormat
- &&
!ContentHandler::getForModelID( $model )->isSupportedFormat(
$this->contentFormat )
- ) {
-
- $name =
$title->getPrefixedDBkey();
-
- $this->dieUsage( "The requested
format {$this->contentFormat} is not supported for " .
- "content model $model
used by $name", 'badformat' );
- }
-
- $difftocontent =
ContentHandler::makeContent(
- $this->difftotext,
- $title,
- $model,
- $this->contentFormat
- );
-
- $engine =
$handler->createDifferenceEngine( $context );
- $engine->setContent( $content,
$difftocontent );
- } else {
- $engine =
$handler->createDifferenceEngine( $context, $revision->getID(), $this->diffto );
- $vals['diff']['from'] =
$engine->getOldid();
- $vals['diff']['to'] =
$engine->getNewid();
- }
- $difftext = $engine->getDiffBody();
- ApiResult::setContent( $vals['diff'], $difftext
);
- if ( !$engine->wasCacheHit() ) {
- $n++;
- }
- } else {
- $vals['diff']['notcached'] = '';
- }
+ if ( $resultPageSet !== null ) {
+ $resultPageSet->populateFromRevisionIDs( $generated );
}
-
- if ( $anyHidden && $revision->isDeleted(
Revision::DELETED_RESTRICTED ) ) {
- $vals['suppressed'] = '';
- }
-
- return $vals;
}
public function getCacheMode( $params ) {
if ( isset( $params['token'] ) ) {
return 'private';
}
- if ( $this->userCanSeeRevDel() ) {
- return 'private';
- }
- if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment',
$params['prop'] ) ) {
- // formatComment() calls wfMessage() among other things
- return 'anon-public-user-private';
- }
-
- return 'public';
+ return parent::getCacheMode( $params );
}
public function getAllowedParams() {
- return array(
- 'prop' => array(
- ApiBase::PARAM_ISMULTI => true,
- ApiBase::PARAM_DFLT =>
'ids|timestamp|flags|comment|user',
- ApiBase::PARAM_TYPE => array(
- 'ids',
- 'flags',
- 'timestamp',
- 'user',
- 'userid',
- 'size',
- 'sha1',
- 'contentmodel',
- 'comment',
- 'parsedcomment',
- 'content',
- 'tags'
- )
- ),
- 'limit' => array(
- ApiBase::PARAM_TYPE => 'limit',
- ApiBase::PARAM_MIN => 1,
- ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
- ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
- ApiBase::PARAM_HELP_MSG_INFO => array( array(
'singlepageonly' ) ),
- ),
+ $ret = parent::getAllowedParams() + array(
'startid' => array(
ApiBase::PARAM_TYPE => 'integer',
ApiBase::PARAM_HELP_MSG_INFO => array( array(
'singlepageonly' ) ),
@@ -742,7 +391,7 @@
'newer',
'older'
),
- ApiBase::PARAM_HELP_MSG =>
'api-help-param-continue',
+ ApiBase::PARAM_HELP_MSG =>
'api-help-param-direction',
ApiBase::PARAM_HELP_MSG_INFO => array( array(
'singlepageonly' ) ),
),
'user' => array(
@@ -754,10 +403,6 @@
ApiBase::PARAM_HELP_MSG_INFO => array( array(
'singlepageonly' ) ),
),
'tag' => null,
- 'expandtemplates' => false,
- 'generatexml' => false,
- 'parse' => false,
- 'section' => null,
'token' => array(
ApiBase::PARAM_DEPRECATED => true,
ApiBase::PARAM_TYPE => array_keys(
$this->getTokenFunctions() ),
@@ -766,13 +411,11 @@
'continue' => array(
ApiBase::PARAM_HELP_MSG =>
'api-help-param-continue',
),
- 'diffto' => null,
- 'difftotext' => null,
- 'contentformat' => array(
- ApiBase::PARAM_TYPE =>
ContentHandler::getAllContentFormats(),
- ApiBase::PARAM_DFLT => null
- ),
);
+
+ $ret['limit'][ApiBase::PARAM_HELP_MSG_INFO] = array( array(
'singlepageonly' ) );
+
+ return $ret;
}
protected function getExamplesMessages() {
diff --git a/includes/api/ApiQueryRevisionsBase.php
b/includes/api/ApiQueryRevisionsBase.php
new file mode 100644
index 0000000..3879d7b
--- /dev/null
+++ b/includes/api/ApiQueryRevisionsBase.php
@@ -0,0 +1,474 @@
+<?php
+/**
+ *
+ *
+ * Created on Oct 3, 2014 as a split from ApiQueryRevisions
+ *
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * A base class for functions common to producing a list of revisions.
+ *
+ * @ingroup API
+ */
+abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
+
+ protected $limit, $diffto, $difftotext, $expandTemplates, $generateXML,
$section,
+ $parseContent, $contentFormat, $setParsedLimit = true;
+
+ 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;
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator( $resultPageSet ) {
+ $this->run( $resultPageSet );
+ }
+
+ /**
+ * @param ApiPageSet $resultPageSet
+ * @return void
+ */
+ abstract protected function run( ApiPageSet $resultPageSet = null );
+
+ /**
+ * Parse the parameters into the various instance fields.
+ *
+ * @param array $params
+ */
+ protected function parseParameters( $params ) {
+ if ( !is_null( $params['difftotext'] ) ) {
+ $this->difftotext = $params['difftotext'];
+ } elseif ( !is_null( $params['diffto'] ) ) {
+ if ( $params['diffto'] == 'cur' ) {
+ $params['diffto'] = 0;
+ }
+ if ( ( !ctype_digit( $params['diffto'] ) ||
$params['diffto'] < 0 )
+ && $params['diffto'] != 'prev' &&
$params['diffto'] != 'next'
+ ) {
+ $p = $this->getModulePrefix();
+ $this->dieUsage(
+ "{$p}diffto must be set to a
non-negative number, \"prev\", \"next\" or \"cur\"",
+ 'diffto'
+ );
+ }
+ // Check whether the revision exists and is readable,
+ // DifferenceEngine returns a rather ambiguous empty
+ // string if that's not the case
+ if ( $params['diffto'] != 0 ) {
+ $difftoRev = Revision::newFromID(
$params['diffto'] );
+ if ( !$difftoRev ) {
+ $this->dieUsageMsg( array(
'nosuchrevid', $params['diffto'] ) );
+ }
+ if ( !$difftoRev->userCan(
Revision::DELETED_TEXT, $this->getUser() ) ) {
+ $this->setWarning( "Couldn't diff to
r{$difftoRev->getID()}: content is hidden" );
+ $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'] );
+
+ if ( !empty( $params['contentformat'] ) ) {
+ $this->contentFormat = $params['contentformat'];
+ }
+
+ $this->limit = $params['limit'];
+
+ $smallLimit = false;
+ if ( $this->fld_content || !is_null( $this->diffto ) ||
!is_null( $this->difftotext ) ) {
+ $smallLimit = true;
+ $this->expandTemplates = $params['expandtemplates'];
+ $this->generateXML = $params['generatexml'];
+ $this->parseContent = $params['parse'];
+ if ( $this->parseContent ) {
+ // Must manually initialize unset limit
+ if ( is_null( $this->limit ) ) {
+ $this->limit = 1;
+ }
+ }
+ if ( isset( $params['section'] ) ) {
+ $this->section = $params['section'];
+ } else {
+ $this->section = false;
+ }
+ }
+
+ $userMax = $this->parseContent ? 1 : ( $smallLimit ?
ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
+ $botMax = $this->parseContent ? 1 : ( $smallLimit ?
ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
+ if ( $this->limit == 'max' ) {
+ $this->limit = $this->getMain()->canApiHighLimits() ?
$botMax : $userMax;
+ if ( $this->setParsedLimit ) {
+ $this->getResult()->setParsedLimit(
$this->getModuleName(), $this->limit );
+ }
+ }
+
+ if ( is_null( $this->limit ) ) {
+ $this->limit = 10;
+ }
+ $this->validateLimit( 'limit', $this->limit, 1, $userMax,
$botMax );
+ }
+
+ /**
+ * Extract information from the Revision
+ *
+ * @param 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();
+ $vals = array();
+ $anyHidden = false;
+
+ if ( $this->fld_ids ) {
+ $vals['revid'] = intval( $revision->getId() );
+ if ( !is_null( $revision->getParentId() ) ) {
+ $vals['parentid'] = intval(
$revision->getParentId() );
+ }
+ }
+
+ if ( $this->fld_flags && $revision->isMinor() ) {
+ $vals['minor'] = '';
+ }
+
+ if ( $this->fld_user || $this->fld_userid ) {
+ if ( $revision->isDeleted( Revision::DELETED_USER ) ) {
+ $vals['userhidden'] = '';
+ $anyHidden = true;
+ }
+ if ( $revision->userCan( Revision::DELETED_USER, $user
) ) {
+ if ( $this->fld_user ) {
+ $vals['user'] =
$revision->getRawUserText();
+ }
+ $userid = $revision->getRawUser();
+ if ( !$userid ) {
+ $vals['anon'] = '';
+ }
+
+ if ( $this->fld_userid ) {
+ $vals['userid'] = $userid;
+ }
+ }
+ }
+
+ if ( $this->fld_timestamp ) {
+ $vals['timestamp'] = wfTimestamp( TS_ISO_8601,
$revision->getTimestamp() );
+ }
+
+ if ( $this->fld_size ) {
+ if ( !is_null( $revision->getSize() ) ) {
+ $vals['size'] = intval( $revision->getSize() );
+ } else {
+ $vals['size'] = 0;
+ }
+ }
+
+ if ( $this->fld_sha1 ) {
+ if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
+ $vals['sha1hidden'] = '';
+ $anyHidden = true;
+ }
+ if ( $revision->userCan( Revision::DELETED_TEXT, $user
) ) {
+ if ( $revision->getSha1() != '' ) {
+ $vals['sha1'] = wfBaseConvert(
$revision->getSha1(), 36, 16, 40 );
+ } else {
+ $vals['sha1'] = '';
+ }
+ }
+ }
+
+ if ( $this->fld_contentmodel ) {
+ $vals['contentmodel'] = $revision->getContentModel();
+ }
+
+ if ( $this->fld_comment || $this->fld_parsedcomment ) {
+ if ( $revision->isDeleted( Revision::DELETED_COMMENT )
) {
+ $vals['commenthidden'] = '';
+ $anyHidden = true;
+ }
+ if ( $revision->userCan( Revision::DELETED_COMMENT,
$user ) ) {
+ $comment = $revision->getRawComment();
+
+ if ( $this->fld_comment ) {
+ $vals['comment'] = $comment;
+ }
+
+ if ( $this->fld_parsedcomment ) {
+ $vals['parsedcomment'] =
Linker::formatComment( $comment, $title );
+ }
+ }
+ }
+
+ if ( $this->fld_tags ) {
+ if ( $row->ts_tags ) {
+ $tags = explode( ',', $row->ts_tags );
+ $this->getResult()->setIndexedTagName( $tags,
'tag' );
+ $vals['tags'] = $tags;
+ } else {
+ $vals['tags'] = array();
+ }
+ }
+
+ $content = null;
+ global $wgParser;
+ if ( $this->fld_content || !is_null( $this->diffto ) ||
!is_null( $this->difftotext ) ) {
+ $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->dieUsage(
+ "There is no section
{$this->section} in r" . $revision->getId(),
+ 'nosuchsection'
+ );
+ }
+ }
+ if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
+ $vals['texthidden'] = '';
+ $anyHidden = true;
+ } elseif ( !$content ) {
+ $vals['textmissing'] = '';
+ }
+ }
+ if ( $this->fld_content && $content ) {
+ $text = null;
+
+ if ( $this->generateXML ) {
+ 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( array( $dom,
'saveXML' ) ) ) {
+ $xml = $dom->saveXML();
+ } else {
+ $xml = $dom->__toString();
+ }
+ $vals['parsetree'] = $xml;
+ } else {
+ $vals['badcontentformatforparsetree'] =
'';
+ $this->setWarning( "Conversion to XML
is supported for wikitext only, " .
+ $title->getPrefixedDBkey() .
+ " uses content model " .
$content->getModel() );
+ }
+ }
+
+ if ( $this->expandTemplates && !$this->parseContent ) {
+ #XXX: implement template expansion for all
content types in ContentHandler?
+ if ( $content->getModel() ===
CONTENT_MODEL_WIKITEXT ) {
+ $text = $content->getNativeData();
+
+ $text = $wgParser->preprocess(
+ $text,
+ $title,
+ ParserOptions::newFromContext(
$this->getContext() )
+ );
+ } else {
+ $this->setWarning( "Template expansion
is supported for wikitext only, " .
+ $title->getPrefixedDBkey() .
+ " uses content model " .
$content->getModel() );
+ $vals['badcontentformat'] = '';
+ $text = false;
+ }
+ }
+ if ( $this->parseContent ) {
+ $po = $content->getParserOutput(
+ $title,
+ $revision->getId(),
+ ParserOptions::newFromContext(
$this->getContext() )
+ );
+ $text = $po->getText();
+ }
+
+ if ( $text === null ) {
+ $format = $this->contentFormat ?
$this->contentFormat : $content->getDefaultFormat();
+ $model = $content->getModel();
+
+ if ( !$content->isSupportedFormat( $format ) ) {
+ $name = $title->getPrefixedDBkey();
+ $this->setWarning( "The requested
format {$this->contentFormat} is not " .
+ "supported for content model
$model used by $name" );
+ $vals['badcontentformat'] = '';
+ $text = false;
+ } else {
+ $text = $content->serialize( $format );
+ // always include format and model.
+ // Format is needed to deserialize,
model is needed to interpret.
+ $vals['contentformat'] = $format;
+ $vals['contentmodel'] = $model;
+ }
+ }
+
+ if ( $text !== false ) {
+ ApiResult::setContent( $vals, $text );
+ }
+ }
+
+ if ( $content && ( !is_null( $this->diffto ) || !is_null(
$this->difftotext ) ) ) {
+ static $n = 0; // Number of uncached diffs we've had
+
+ if ( $n < $this->getConfig()->get(
'APIMaxUncachedDiffs' ) ) {
+ $vals['diff'] = array();
+ $context = new DerivativeContext(
$this->getContext() );
+ $context->setTitle( $title );
+ $handler = $revision->getContentHandler();
+
+ if ( !is_null( $this->difftotext ) ) {
+ $model = $title->getContentModel();
+
+ if ( $this->contentFormat
+ &&
!ContentHandler::getForModelID( $model )->isSupportedFormat(
$this->contentFormat )
+ ) {
+ $name =
$title->getPrefixedDBkey();
+ $this->setWarning( "The
requested format {$this->contentFormat} is not " .
+ "supported for content
model $model used by $name" );
+
$vals['diff']['badcontentformat'] = '';
+ $engine = null;
+ } else {
+ $difftocontent =
ContentHandler::makeContent(
+ $this->difftotext,
+ $title,
+ $model,
+ $this->contentFormat
+ );
+
+ $engine =
$handler->createDifferenceEngine( $context );
+ $engine->setContent( $content,
$difftocontent );
+ }
+ } else {
+ $engine =
$handler->createDifferenceEngine( $context, $revision->getID(), $this->diffto );
+ $vals['diff']['from'] =
$engine->getOldid();
+ $vals['diff']['to'] =
$engine->getNewid();
+ }
+ if ( $engine ) {
+ $difftext = $engine->getDiffBody();
+ ApiResult::setContent( $vals['diff'],
$difftext );
+ if ( !$engine->wasCacheHit() ) {
+ $n++;
+ }
+ }
+ } else {
+ $vals['diff']['notcached'] = '';
+ }
+ }
+
+ if ( $anyHidden && $revision->isDeleted(
Revision::DELETED_RESTRICTED ) ) {
+ $vals['suppressed'] = '';
+ }
+
+ return $vals;
+ }
+
+ public function getCacheMode( $params ) {
+ if ( $this->userCanSeeRevDel() ) {
+ return 'private';
+ }
+
+ return 'public';
+ }
+
+ public function getAllowedParams() {
+ return array(
+ 'prop' => array(
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_DFLT =>
'ids|timestamp|flags|comment|user',
+ ApiBase::PARAM_TYPE => array(
+ 'ids',
+ 'flags',
+ 'timestamp',
+ 'user',
+ 'userid',
+ 'size',
+ 'sha1',
+ 'contentmodel',
+ 'comment',
+ 'parsedcomment',
+ 'content',
+ 'tags'
+ ),
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-prop',
+ ),
+ 'limit' => array(
+ ApiBase::PARAM_TYPE => 'limit',
+ ApiBase::PARAM_MIN => 1,
+ ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
+ ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-limit',
+ ),
+ 'expandtemplates' => array(
+ ApiBase::PARAM_DFLT => false,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-expandtemplates',
+ ),
+ 'generatexml' => array(
+ ApiBase::PARAM_DFLT => false,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-generatexml',
+ ),
+ 'parse' => array(
+ ApiBase::PARAM_DFLT => false,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-parse',
+ ),
+ 'section' => array(
+ ApiBase::PARAM_DFLT => null,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-section',
+ ),
+ 'diffto' => array(
+ ApiBase::PARAM_DFLT => null,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-diffto',
+ ),
+ 'difftotext' => array(
+ ApiBase::PARAM_DFLT => null,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-difftotext',
+ ),
+ 'contentformat' => array(
+ ApiBase::PARAM_TYPE =>
ContentHandler::getAllContentFormats(),
+ ApiBase::PARAM_DFLT => null,
+ ApiBase::PARAM_HELP_MSG =>
'apihelp-query+revisions+base-param-contentformat',
+ ),
+ );
+ }
+
+}
diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json
index 2bec759..485c776 100644
--- a/includes/api/i18n/en.json
+++ b/includes/api/i18n/en.json
@@ -319,6 +319,23 @@
"apihelp-query+allcategories-example-size": "List categories with
information on the number of pages in each",
"apihelp-query+allcategories-example-generator": "Retrieve info about
the category page itself for categories beginning \"List\"",
+ "apihelp-query+alldeletedrevisions-description": "List all deleted
revisions by a user or in a namespace.",
+ "apihelp-query+alldeletedrevisions-paraminfo-useronly": "May only be
used with $3user.",
+ "apihelp-query+alldeletedrevisions-paraminfo-nonuseronly": "Cannot be
used with $3user.",
+ "apihelp-query+alldeletedrevisions-param-start": "The timestamp to
start enumerating from.",
+ "apihelp-query+alldeletedrevisions-param-end": "The timestamp to stop
enumerating at.",
+ "apihelp-query+alldeletedrevisions-param-from": "Start listing at this
title.",
+ "apihelp-query+alldeletedrevisions-param-to": "Stop listing at this
title.",
+ "apihelp-query+alldeletedrevisions-param-prefix": "Search for all page
titles that begin with this value.",
+ "apihelp-query+alldeletedrevisions-param-tag": "Only list revisions
tagged with this tag.",
+ "apihelp-query+alldeletedrevisions-param-user": "Only list revisions by
this user.",
+ "apihelp-query+alldeletedrevisions-param-excludeuser": "Don't list
revisions by this user.",
+ "apihelp-query+alldeletedrevisions-param-namespace": "Only list pages
in this namespace.",
+ "apihelp-query+alldeletedrevisions-param-miser-user-namespace":
"'''NOTE:''' Due to [https://www.mediawiki.org/wiki/Manual:$wgMiserMode miser
mode], using $1user and $1namespace together may result in fewer than
\"$1limit\" results returned before continuing; in extreme cases, zero results
may be returned.",
+ "apihelp-query+alldeletedrevisions-param-generatetitles": "When being
used as a generator, generate titles rather than revision IDs.",
+ "apihelp-query+alldeletedrevisions-example-user": "List the last 50
deleted contributions by User:Example",
+ "apihelp-query+alldeletedrevisions-example-ns-main": "List the first 50
deleted revisions in the main namespace",
+
"apihelp-query+allfileusages-description": "List all file usages,
including non-existing.",
"apihelp-query+allfileusages-param-from": "The title of the file to
start enumerating from.",
"apihelp-query+allfileusages-param-to": "The title of the file to stop
enumerating at.",
@@ -507,6 +524,17 @@
"apihelp-query+contributors-param-excluderights": "Exclude users having
the given rights. Does not include rights granted by implicit or auto-promoted
groups like *, user, or autoconfirmed.",
"apihelp-query+contributors-param-limit": "How many contributors to
return.",
"apihelp-query+contributors-example-simple": "Show contributors to the
[[Main Page]]",
+
+ "apihelp-query+deletedrevisions-description": "Get deleted revision
information.\n\nMay be used in several ways:\n# Get deleted revisions for a set
of pages, by setting titles or pageids. Ordered by title and timestamp.\n# Get
data about a set of deleted revisions by setting their IDs with revids. Ordered
by revision ID.",
+ "apihelp-query+deletedrevisions-param-start": "The timestamp to start
enumerating from. Ignored when processing a list of revision IDs.",
+ "apihelp-query+deletedrevisions-param-end": "The timestamp to stop
enumerating at. Ignored when processing a list of revision IDs.",
+ "apihelp-query+deletedrevisions-param-tag": "Only list revisions tagged
with this tag.",
+ "apihelp-query+deletedrevisions-param-user": "Only list revisions by
this user.",
+ "apihelp-query+deletedrevisions-param-excludeuser": "Don't list
revisions by this user.",
+ "apihelp-query+deletedrevisions-param-limit": "The maximum amount of
revisions to list.",
+ "apihelp-query+deletedrevisions-param-prop": "Which properties to
get:\n;revid:Adds the revision ID of the deleted revision.\n;parentid:Adds the
revision ID of the previous revision to the page.\n;user:Adds the user who made
the revision.\n;userid:Adds the user ID who made the revision.\n;comment:Adds
the comment of the revision.\n;parsedcomment:Adds the parsed comment of the
revision.\n;minor:Tags if the revision is minor.\n;len:Adds the length (bytes)
of the revision.\n;sha1:Adds the SHA-1 (base 16) of the
revision.\n;content:Adds the content of the revision.\n;tags:Tags for the
revision.",
+ "apihelp-query+deletedrevisions-example-titles": "List the deleted
revisions of [[Main Page]] and [[Talk:Main Page]], with content",
+ "apihelp-query+deletedrevisions-example-revids": "List the information
for deleted revision 123456",
"apihelp-query+deletedrevs-description": "List deleted
revisions.\n\nOperates in three modes:\n# List deleted revisions for the given
titles, sorted by timestamp.\n# List deleted contributions for the given user,
sorted by timestamp (no titles specified).\n# List all deleted revisions in the
given namespace, sorted by title and timestamp (no titles specified, $1user not
set).\n\nCertain parameters only apply to some modes and are ignored in
others.",
"apihelp-query+deletedrevs-paraminfo-modes": "{{PLURAL:$1|Mode|Modes}}:
$2",
@@ -763,8 +791,6 @@
"apihelp-query+revisions-description": "Get revision
information.\n\nMay be used in several ways:\n# Get data about a set of pages
(last revision), by setting titles or pageids.\n# Get revisions for one given
page, by using titles or pageids with start, end, or limit.\n# Get data about a
set of revisions by setting their IDs with revids.",
"apihelp-query+revisions-paraminfo-singlepageonly": "May only be used
with a single page (mode #2).",
- "apihelp-query+revisions-param-prop": "Which properties to get for each
revision:\n;ids:The ID of the revision.\n;flags:Revision flags
(minor).\n;timestamp:The timestamp of the revision.\n;user:User that made the
revision.\n;userid:User ID of revision creator.\n;size:Length (bytes) of the
revision.\n;sha1:SHA-1 (base 16) of the revision.\n;contentmodel:Content model
ID.\n;comment:Comment by the user for revision.\n;parsedcomment:Parsed comment
by the user for the revision.\n;content:Text of the revision.\n;tags:Tags for
the revision.",
- "apihelp-query+revisions-param-limit": "Limit how many revisions will
be returned.",
"apihelp-query+revisions-param-startid": "From which revision ID to
start enumeration.",
"apihelp-query+revisions-param-endid": "Stop revision enumeration on
this revision ID.",
"apihelp-query+revisions-param-start": "From which revision timestamp
to start enumeration.",
@@ -772,14 +798,7 @@
"apihelp-query+revisions-param-user": "Only include revisions made by
user.",
"apihelp-query+revisions-param-excludeuser": "Exclude revisions made by
user.",
"apihelp-query+revisions-param-tag": "Only list revisions tagged with
this tag.",
- "apihelp-query+revisions-param-expandtemplates": "Expand templates in
revision content (requires $1prop=content).",
- "apihelp-query+revisions-param-generatexml": "Generate XML parse tree
for revision content (requires $1prop=content).",
- "apihelp-query+revisions-param-parse": "Parse revision content
(requires $1prop=content). For performance reasons, if this option is used,
$1limit is enforced to 1.",
- "apihelp-query+revisions-param-section": "Only retrieve the content of
this section number.",
"apihelp-query+revisions-param-token": "Which tokens to obtain for each
revision.",
- "apihelp-query+revisions-param-diffto": "Revision ID to diff each
revision to. Use \"prev\", \"next\" and \"cur\" for the previous, next and
current revision respectively.",
- "apihelp-query+revisions-param-difftotext": "Text to diff each revision
to. Only diffs a limited number of revisions. Overrides $1diffto. If $1section
is set, only that section will be diffed against this text.",
- "apihelp-query+revisions-param-contentformat": "Serialization format
used for $1difftotext and expected for output of content.",
"apihelp-query+revisions-example-content": "Get data with content for
the last revision of titles \"API\" and \"Main Page\"",
"apihelp-query+revisions-example-last5": "Get last 5 revisions of the
\"Main Page\"",
"apihelp-query+revisions-example-first5": "Get first 5 revisions of the
\"Main Page\"",
@@ -787,6 +806,16 @@
"apihelp-query+revisions-example-first5-not-localhost": "Get first 5
revisions of the \"Main Page\" that were not made made by anonymous user
\"127.0.0.1\"",
"apihelp-query+revisions-example-first5-user": "Get first 5 revisions
of the \"Main Page\" that were made by the user \"MediaWiki default\"",
+ "apihelp-query+revisions+base-param-prop": "Which properties to get for
each revision:\n;ids:The ID of the revision.\n;flags:Revision flags
(minor).\n;timestamp:The timestamp of the revision.\n;user:User that made the
revision.\n;userid:User ID of the revision creator.\n;size:Length (bytes) of
the revision.\n;sha1:SHA-1 (base 16) of the revision.\n;contentmodel:Content
model ID of the revision.\n;comment:Comment by the user for the
revision.\n;parsedcomment:Parsed comment by the user for the
revision.\n;content:Text of the revision.\n;tags:Tags for the revision.",
+ "apihelp-query+revisions+base-param-limit": "Limit how many revisions
will be returned.",
+ "apihelp-query+revisions+base-param-expandtemplates": "Expand templates
in revision content (requires $1prop=content).",
+ "apihelp-query+revisions+base-param-generatexml": "Generate XML parse
tree for revision content (requires $1prop=content).",
+ "apihelp-query+revisions+base-param-parse": "Parse revision content
(requires $1prop=content). For performance reasons, if this option is used,
$1limit is enforced to 1.",
+ "apihelp-query+revisions+base-param-section": "Only retrieve the
content of this section number.",
+ "apihelp-query+revisions+base-param-diffto": "Revision ID to diff each
revision to. Use \"prev\", \"next\" and \"cur\" for the previous, next and
current revision respectively.",
+ "apihelp-query+revisions+base-param-difftotext": "Text to diff each
revision to. Only diffs a limited number of revisions. Overrides $1diffto. If
$1section is set, only that section will be diffed against this text",
+ "apihelp-query+revisions+base-param-contentformat": "Serialization
format used for $1difftotext and expected for output of content.",
+
"apihelp-query+search-description": "Perform a full text search.",
"apihelp-query+search-param-search": "Search for all page titles (or
content) that have this value.",
"apihelp-query+search-param-namespace": "Search only within these
namespaces.",
diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json
index 25ba0b2..ce058e9 100644
--- a/includes/api/i18n/qqq.json
+++ b/includes/api/i18n/qqq.json
@@ -291,6 +291,24 @@
"apihelp-query+allcategories-param-prop":
"{{doc-apihelp-param|query+allcategories|prop}}",
"apihelp-query+allcategories-example-size":
"{{doc-apihelp-example|query+allcategories}}",
"apihelp-query+allcategories-example-generator":
"{{doc-apihelp-example|query+allcategories}}",
+
+ "apihelp-query+alldeletedrevisions-description":
"{{doc-apihelp-description|query+alldeletedrevisions}}",
+ "apihelp-query+alldeletedrevisions-paraminfo-nonuseronly":
"{{doc-apihelp-paraminfo|query+alldeletedrevisions|nonuseronly}}",
+ "apihelp-query+alldeletedrevisions-paraminfo-useronly":
"{{doc-apihelp-paraminfo|query+alldeletedrevisions|useronly}}",
+ "apihelp-query+alldeletedrevisions-param-end":
"{{doc-apihelp-param|query+alldeletedrevisions|end}}",
+ "apihelp-query+alldeletedrevisions-param-excludeuser":
"{{doc-apihelp-param|query+alldeletedrevisions|excludeuser}}",
+ "apihelp-query+alldeletedrevisions-param-from":
"{{doc-apihelp-param|query+alldeletedrevisions|from}}",
+ "apihelp-query+alldeletedrevisions-param-generatetitles":
"{{doc-apihelp-param|query+alldeletedrevisions|generatetitles}}",
+ "apihelp-query+alldeletedrevisions-param-miser-user-namespace":
"{{doc-apihelp-param|query+alldeletedrevisions|miser-user-namespace}}",
+ "apihelp-query+alldeletedrevisions-param-namespace":
"{{doc-apihelp-param|query+alldeletedrevisions|namespace}}",
+ "apihelp-query+alldeletedrevisions-param-prefix":
"{{doc-apihelp-param|query+alldeletedrevisions|prefix}}",
+ "apihelp-query+alldeletedrevisions-param-start":
"{{doc-apihelp-param|query+alldeletedrevisions|start}}",
+ "apihelp-query+alldeletedrevisions-param-tag":
"{{doc-apihelp-param|query+alldeletedrevisions|tag}}",
+ "apihelp-query+alldeletedrevisions-param-to":
"{{doc-apihelp-param|query+alldeletedrevisions|to}}",
+ "apihelp-query+alldeletedrevisions-param-user":
"{{doc-apihelp-param|query+alldeletedrevisions|user}}",
+ "apihelp-query+alldeletedrevisions-example-ns-main":
"{{doc-apihelp-example|query+alldeletedrevisions}}",
+ "apihelp-query+alldeletedrevisions-example-user":
"{{doc-apihelp-example|query+alldeletedrevisions}}",
+
"apihelp-query+allfileusages-description":
"{{doc-apihelp-description|query+allfileusages}}",
"apihelp-query+allfileusages-param-from":
"{{doc-apihelp-param|query+allfileusages|from}}",
"apihelp-query+allfileusages-param-to":
"{{doc-apihelp-param|query+allfileusages|to}}",
@@ -466,6 +484,18 @@
"apihelp-query+contributors-param-excluderights":
"{{doc-apihelp-param|query+contributors|excluderights}}",
"apihelp-query+contributors-param-limit":
"{{doc-apihelp-param|query+contributors|limit}}",
"apihelp-query+contributors-example-simple":
"{{doc-apihelp-example|query+contributors}}",
+
+ "apihelp-query+deletedrevisions-description":
"{{doc-apihelp-description|query+deletedrevisions}}",
+ "apihelp-query+deletedrevisions-param-end":
"{{doc-apihelp-param|query+deletedrevisions|end}}",
+ "apihelp-query+deletedrevisions-param-excludeuser":
"{{doc-apihelp-param|query+deletedrevisions|excludeuser}}",
+ "apihelp-query+deletedrevisions-param-limit":
"{{doc-apihelp-param|query+deletedrevisions|limit}}",
+ "apihelp-query+deletedrevisions-param-prop":
"{{doc-apihelp-param|query+deletedrevisions|prop}}",
+ "apihelp-query+deletedrevisions-param-start":
"{{doc-apihelp-param|query+deletedrevisions|start}}",
+ "apihelp-query+deletedrevisions-param-tag":
"{{doc-apihelp-param|query+deletedrevisions|tag}}",
+ "apihelp-query+deletedrevisions-param-user":
"{{doc-apihelp-param|query+deletedrevisions|user}}",
+ "apihelp-query+deletedrevisions-example-revids":
"{{doc-apihelp-example|query+deletedrevisions}}",
+ "apihelp-query+deletedrevisions-example-titles":
"{{doc-apihelp-example|query+deletedrevisions}}",
+
"apihelp-query+deletedrevs-description":
"{{doc-apihelp-description|query+deletedrevs}}",
"apihelp-query+deletedrevs-paraminfo-modes":
"{{doc-apihelp-paraminfo|query+deletedrevs|modes}}\n{{Identical|Mode}}",
"apihelp-query+deletedrevs-param-start":
"{{doc-apihelp-param|query+deletedrevs|start}}",
@@ -692,8 +722,6 @@
"apihelp-query+redirects-example-generator":
"{{doc-apihelp-example|query+redirects}}",
"apihelp-query+revisions-description":
"{{doc-apihelp-description|query+revisions}}",
"apihelp-query+revisions-paraminfo-singlepageonly":
"{{doc-apihelp-paraminfo|query+revisions|singlepageonly}}",
- "apihelp-query+revisions-param-prop":
"{{doc-apihelp-param|query+revisions|prop}}",
- "apihelp-query+revisions-param-limit":
"{{doc-apihelp-param|query+revisions|limit}}",
"apihelp-query+revisions-param-startid":
"{{doc-apihelp-param|query+revisions|startid}}",
"apihelp-query+revisions-param-endid":
"{{doc-apihelp-param|query+revisions|endid}}",
"apihelp-query+revisions-param-start":
"{{doc-apihelp-param|query+revisions|start}}",
@@ -701,20 +729,24 @@
"apihelp-query+revisions-param-user":
"{{doc-apihelp-param|query+revisions|user}}",
"apihelp-query+revisions-param-excludeuser":
"{{doc-apihelp-param|query+revisions|excludeuser}}",
"apihelp-query+revisions-param-tag":
"{{doc-apihelp-param|query+revisions|tag}}",
- "apihelp-query+revisions-param-expandtemplates":
"{{doc-apihelp-param|query+revisions|expandtemplates}}",
- "apihelp-query+revisions-param-generatexml":
"{{doc-apihelp-param|query+revisions|generatexml}}",
- "apihelp-query+revisions-param-parse":
"{{doc-apihelp-param|query+revisions|parse}}",
- "apihelp-query+revisions-param-section":
"{{doc-apihelp-param|query+revisions|section}}",
"apihelp-query+revisions-param-token":
"{{doc-apihelp-param|query+revisions|token}}",
- "apihelp-query+revisions-param-diffto":
"{{doc-apihelp-param|query+revisions|diffto}}",
- "apihelp-query+revisions-param-difftotext":
"{{doc-apihelp-param|query+revisions|difftotext}}",
- "apihelp-query+revisions-param-contentformat":
"{{doc-apihelp-param|query+revisions|contentformat}}",
"apihelp-query+revisions-example-content":
"{{doc-apihelp-example|query+revisions}}",
"apihelp-query+revisions-example-last5":
"{{doc-apihelp-example|query+revisions}}",
"apihelp-query+revisions-example-first5":
"{{doc-apihelp-example|query+revisions}}",
"apihelp-query+revisions-example-first5-after":
"{{doc-apihelp-example|query+revisions}}",
"apihelp-query+revisions-example-first5-not-localhost":
"{{doc-apihelp-example|query+revisions}}",
"apihelp-query+revisions-example-first5-user":
"{{doc-apihelp-example|query+revisions}}",
+
+ "apihelp-query+revisions+base-param-contentformat":
"{{doc-apihelp-param|query+revisions+base|contentformat|description=the
\"contentformat\" parameter to revision querying modules|noseealso=1}}",
+ "apihelp-query+revisions+base-param-diffto":
"{{doc-apihelp-param|query+revisions+base|diffto|description=the \"diffto\"
parameter to revision querying modules|noseealso=1}}",
+ "apihelp-query+revisions+base-param-difftotext":
"{{doc-apihelp-param|query+revisions+base|difftotext|description=the
\"difftotext\" 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}}",
+ "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-parse":
"{{doc-apihelp-param|query+revisions+base|parse|description=the \"parse\"
parameter to revision querying modules|noseealso=1}}",
+ "apihelp-query+revisions+base-param-prop":
"{{doc-apihelp-param|query+revisions+base|prop|description=the \"prop\"
parameter to revision querying modules|noseealso=1}}",
+ "apihelp-query+revisions+base-param-section":
"{{doc-apihelp-param|query+revisions+base|section|description=the \"section\"
parameter to revision querying modules|noseealso=1}}",
+
"apihelp-query+search-description":
"{{doc-apihelp-description|query+search}}",
"apihelp-query+search-param-search":
"{{doc-apihelp-param|query+search|search}}",
"apihelp-query+search-param-namespace":
"{{doc-apihelp-param|query+search|namespace}}",
--
To view, visit https://gerrit.wikimedia.org/r/168646
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I3e960d5c655bc57885d6d4ee227e67104808add7
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Anomie <[email protected]>
Gerrit-Reviewer: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Anomie <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: Yurik <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits