Improve API query RevDel handling

* ApiQueryDeletedrevs, ApiQueryFilearchive, ApiQueryRecentChanges, and
  ApiQueryWatchlist will now return entires where fields have been
  revision-deleted. "Hidden" indicators will be provided as appropriate.
* ApiQueryImageInfo, ApiQueryLogEvents, ApiQueryRevisions,
  ApiQueryContributions will now return field values in addition to the
  "hidden" indicators when the requesting user has the necessary rights.
* Modules that return "hidden" indicators will now also return a
  "suppressed" indicator.
* ApiQueryImageInfo will now return info for DELETED_FILE file revisions
  if the requesting user has the 'deletedtext' right.
* ApiQueryLogEvents, when searching by user or title, will now return
  entries where the user or action are revision-deleted if the
  requesting user has the 'deletedhistory' right.
* ApiQueryContributions now uses the correct user rights rather than
  'hideuser' to determine when to show contributions where the username
  was revision-deleted.
* ApiQueryContributions will now indicate when the revision text is
  hidden.
* Fix a bug in ApiQueryDeletedrevs found during testing where specifying
  the "content" prop along with the "tags" prop or "drtag" parameter
  would cause an SQL error.
* Fix various PHP warnings in ApiQueryFilearchive caused by the lack of
  ArchivedFile::selectFields() fields.
* ApiQueryImageInfo::getInfo's $metadataOpts parameter has been renamed
  $opts, and now may have an option to indicate the user to use for
  RevDel visibility checks.
* ApiQueryWatchlist now properly uses the actual user's rights for
  checking whether wlprop=patrol is allowed, rather than using the
  wlowner's rights.

Bug: 27747
Bug: 27748
Bug: 28261
Bug: 34926
Bug: 48966
Change-Id: Idec2199976f460e1c73a26d0717e9fc4ab8042bb
This commit is contained in:
Brad Jorsch 2013-12-18 16:58:39 -05:00
parent 99cb12b973
commit 48de797fbd
10 changed files with 565 additions and 221 deletions

View file

@ -125,6 +125,24 @@ production.
* All API modules now support an assert parameter. See the new features section
for more details.
* Added prop=contributors to fetch the list of contributors to the page.
* The following API modules will now return entries where fields have been
revision-deleted: list=deletedrevs, list=filearchive, list=recentchanges,
list=watchlist. "hidden" indicators will be included, in the same style as is
already done for prop=revisions.
* The following API modules will now return the content of revision-deleted
fields, in addition to the "hidden" indicators, if the querying user has the
necessary rights: list=logevents, list=usercontribs, prop=imageinfo,
prop=revisions.
* The above modules, where applicable, will now return entries filtered by
revision-deleted fields if the querying user has the necessary rights. For
example, prop=revisions with rvuser or rvexcludeuser will no longer skip
revisions where the user was revision-deleted if the current user has the
deletedhistory right.
* The 'hideuser' right, used when blocking, is no longer necessary or
sufficient for seeing contributions with revision-deleted in
list=usercontribs.
* list=watchlist now uses the querying user's rights rather than the wlowner's
rights when checking whether wlprop=patrol is allowed.
=== Languages updated in 1.23 ===

View file

@ -608,6 +608,15 @@ abstract class ApiQueryBase extends ApiBase {
return $errors;
}
/**
* Check whether the current user has permission to view revision-deleted
* fields.
* @return bool
*/
public function userCanSeeRevDel() {
return $this->getUser()->isAllowedAny( 'deletedhistory', 'deletedtext', 'suppressrevision' );
}
}
/**

View file

@ -66,6 +66,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$fld_token = false;
}
// If user can't undelete, no tokens
if ( !$user->isAllowed( 'undelete' ) ) {
$fld_token = false;
}
$result = $this->getResult();
$pageSet = $this->getPageSet();
$titles = $pageSet->getTitles();
@ -101,8 +106,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
}
$this->addTables( 'archive' );
$this->addWhere( 'ar_deleted = 0' );
$this->addFields( array( 'ar_title', 'ar_namespace', 'ar_timestamp' ) );
$this->addFields( array( 'ar_title', 'ar_namespace', 'ar_timestamp', 'ar_deleted' ) );
$this->addFieldsIf( 'ar_parent_id', $fld_parentid );
$this->addFieldsIf( 'ar_rev_id', $fld_revid );
@ -131,11 +135,13 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
if ( $fld_content ) {
$this->addTables( 'text' );
$this->addJoinConds(
array( 'text' => array( 'INNER JOIN', array( 'ar_text_id=old_id' ) ) )
);
$this->addFields( array( 'ar_text', 'ar_text_id', 'old_text', 'old_flags' ) );
$this->addWhere( 'ar_text_id = old_id' );
// This also means stricter restrictions
if ( !$user->isAllowed( 'undelete' ) ) {
if ( !$user->isAllowedAny( 'undelete', 'deletedtext' ) ) {
$this->dieUsage(
'You don\'t have permission to view deleted revision content',
'permissiondenied'
@ -188,6 +194,22 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$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->isAllowed( 'suppressrevision' ) ) {
$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'] ) && ( $mode == 'all' || $mode == 'revs' ) ) {
$cont = explode( '|', $params['continue'] );
$this->dieContinueUsageIf( count( $cont ) != 3 );
@ -243,6 +265,8 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
}
$rev = array();
$anyHidden = false;
$rev['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ar_timestamp );
if ( $fld_revid ) {
$rev['revid'] = intval( $row->ar_rev_id );
@ -250,21 +274,37 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
if ( $fld_parentid && !is_null( $row->ar_parent_id ) ) {
$rev['parentid'] = intval( $row->ar_parent_id );
}
if ( $fld_user ) {
$rev['user'] = $row->ar_user_text;
}
if ( $fld_userid ) {
$rev['userid'] = $row->ar_user;
}
if ( $fld_comment ) {
$rev['comment'] = $row->ar_comment;
if ( $fld_user || $fld_userid ) {
if ( $row->ar_deleted & Revision::DELETED_USER ) {
$rev['userhidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_USER, $user ) ) {
if ( $fld_user ) {
$rev['user'] = $row->ar_user_text;
}
if ( $fld_userid ) {
$rev['userid'] = $row->ar_user;
}
}
}
$title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
if ( $fld_parsedcomment ) {
$rev['parsedcomment'] = Linker::formatComment( $row->ar_comment, $title );
if ( $fld_comment || $fld_parsedcomment ) {
if ( $row->ar_deleted & Revision::DELETED_COMMENT ) {
$rev['commenthidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_COMMENT, $user ) ) {
if ( $fld_comment ) {
$rev['comment'] = $row->ar_comment;
}
if ( $fld_parsedcomment ) {
$title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
$rev['parsedcomment'] = Linker::formatComment( $row->ar_comment, $title );
}
}
}
if ( $fld_minor && $row->ar_minor_edit == 1 ) {
$rev['minor'] = '';
}
@ -272,14 +312,26 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
$rev['len'] = $row->ar_len;
}
if ( $fld_sha1 ) {
if ( $row->ar_sha1 != '' ) {
$rev['sha1'] = wfBaseConvert( $row->ar_sha1, 36, 16, 40 );
} else {
$rev['sha1'] = '';
if ( $row->ar_deleted & Revision::DELETED_TEXT ) {
$rev['sha1hidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_TEXT, $user ) ) {
if ( $row->ar_sha1 != '' ) {
$rev['sha1'] = wfBaseConvert( $row->ar_sha1, 36, 16, 40 );
} else {
$rev['sha1'] = '';
}
}
}
if ( $fld_content ) {
ApiResult::setContent( $rev, Revision::getRevisionText( $row ) );
if ( $row->ar_deleted & Revision::DELETED_TEXT ) {
$rev['texthidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_TEXT, $user ) ) {
ApiResult::setContent( $rev, Revision::getRevisionText( $row ) );
}
}
if ( $fld_tags ) {
@ -292,11 +344,16 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
}
}
if ( $anyHidden && ( $row->ar_deleted & Revision::DELETED_RESTRICTED ) ) {
$rev['suppressed'] = '';
}
if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) {
$pageID = $newPageID++;
$pageMap[$row->ar_namespace][$row->ar_title] = $pageID;
$a['revisions'] = array( $rev );
$result->setIndexedTagName( $a['revisions'], 'rev' );
$title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
ApiQueryBase::addTitleInfo( $a, $title );
if ( $fld_token ) {
$a['token'] = $token;

View file

@ -66,6 +66,7 @@ class ApiQueryFilearchive extends ApiQueryBase {
$this->addTables( 'filearchive' );
$this->addFields( ArchivedFile::selectFields() );
$this->addFields( array( 'fa_name', 'fa_deleted' ) );
$this->addFieldsIf( 'fa_sha1', $fld_sha1 );
$this->addFieldsIf( 'fa_timestamp', $fld_timestamp );
@ -121,14 +122,16 @@ class ApiQueryFilearchive extends ApiQueryBase {
}
}
if ( !$user->isAllowed( 'suppressrevision' ) ) {
// Filter out revisions that the user is not allowed to see. There
// is no way to indicate that we have skipped stuff because the
// continuation parameter is fa_name
// Note that this field is unindexed. This should however not be
// a big problem as files with fa_deleted are rare
$this->addWhereFld( 'fa_deleted', 0 );
// Exclude files this user can't view.
if ( !$user->isAllowed( 'deletedtext' ) ) {
$bitmask = File::DELETED_FILE;
} elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
$bitmask = File::DELETED_FILE | File::DELETED_RESTRICTED;
} else {
$bitmask = 0;
}
if ( $bitmask ) {
$this->addWhere( $this->getDB()->bitAnd( 'fa_deleted', $bitmask ) . " != $bitmask" );
}
$limit = $params['limit'];
@ -153,16 +156,27 @@ class ApiQueryFilearchive extends ApiQueryBase {
$title = Title::makeTitle( NS_FILE, $row->fa_name );
self::addTitleInfo( $file, $title );
if ( $fld_description &&
Revision::userCanBitfield( $row->fa_deleted, File::DELETED_COMMENT, $user )
) {
$file['description'] = $row->fa_description;
if ( isset( $prop['parseddescription'] ) ) {
$file['parseddescription'] = Linker::formatComment(
$row->fa_description, $title );
}
}
if ( $fld_user &&
Revision::userCanBitfield( $row->fa_deleted, File::DELETED_USER, $user )
) {
$file['userid'] = $row->fa_user;
$file['user'] = $row->fa_user_text;
}
if ( $fld_sha1 ) {
$file['sha1'] = wfBaseConvert( $row->fa_sha1, 36, 16, 40 );
}
if ( $fld_timestamp ) {
$file['timestamp'] = wfTimestamp( TS_ISO_8601, $row->fa_timestamp );
}
if ( $fld_user ) {
$file['userid'] = $row->fa_user;
$file['user'] = $row->fa_user_text;
}
if ( $fld_size || $fld_dimensions ) {
$file['size'] = $row->fa_size;
@ -174,13 +188,6 @@ class ApiQueryFilearchive extends ApiQueryBase {
$file['height'] = $row->fa_height;
$file['width'] = $row->fa_width;
}
if ( $fld_description ) {
$file['description'] = $row->fa_description;
if ( isset( $prop['parseddescription'] ) ) {
$file['parseddescription'] = Linker::formatComment(
$row->fa_description, $title );
}
}
if ( $fld_mediatype ) {
$file['mediatype'] = $row->fa_media_type;
}

View file

@ -50,11 +50,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
$scale = $this->getScale( $params );
$metadataOpts = array(
$opts = array(
'version' => $params['metadataversion'],
'language' => $params['extmetadatalanguage'],
'multilang' => $params['extmetadatamultilang'],
'extmetadatafilter' => $params['extmetadatafilter'],
'revdelUser' => $this->getUser(),
);
$pageIds = $this->getPageSet()->getAllTitlesByNamespace();
@ -78,13 +79,21 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
}
$result = $this->getResult();
//search only inside the local repo
$user = $this->getUser();
$findTitles = array_map( function ( $title ) use ( $user ) {
return array(
'title' => $title,
'private' => $user,
);
}, $titles );
if ( $params['localonly'] ) {
$images = RepoGroup::singleton()->getLocalRepo()->findFiles( $titles );
$images = RepoGroup::singleton()->getLocalRepo()->findFiles( $findTitles );
} else {
$images = RepoGroup::singleton()->findFiles( $titles );
$images = RepoGroup::singleton()->findFiles( $findTitles );
}
$result = $this->getResult();
foreach ( $titles as $title ) {
$pageId = $pageIds[NS_FILE][$title];
$start = $title === $fromTitle ? $fromTimestamp : $params['start'];
@ -154,7 +163,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
$fit = $this->addPageSubItem( $pageId,
self::getInfo( $img, $prop, $result,
$finalThumbParams, $metadataOpts
$finalThumbParams, $opts
)
);
if ( !$fit ) {
@ -189,7 +198,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
$fit = self::getTransformCount() < self::TRANSFORM_LIMIT &&
$this->addPageSubItem( $pageId,
self::getInfo( $oldie, $prop, $result,
$finalThumbParams, $metadataOpts
$finalThumbParams, $opts
)
);
if ( !$fit ) {
@ -310,25 +319,29 @@ class ApiQueryImageInfo extends ApiQueryBase {
* @param array $prop of properties to get (in the keys)
* @param $result ApiResult object
* @param array $thumbParams containing 'width' and 'height' items, or null
* @param array|bool|string $metadataOpts Options for metadata fetching.
* @param array|bool|string $opts Options for data fetching.
* This is an array consisting of the keys:
* 'version': The metadata version for the metadata option
* 'language': The language for extmetadata property
* 'multilang': Return all translations in extmetadata property
* 'revdelUser': User to use when checking whether to show revision-deleted fields.
* @return Array: result array
*/
static function getInfo( $file, $prop, $result, $thumbParams = null, $metadataOpts = false ) {
static function getInfo( $file, $prop, $result, $thumbParams = null, $opts = false ) {
global $wgContLang;
if ( !$metadataOpts || is_string( $metadataOpts ) ) {
$metadataOpts = array(
'version' => $metadataOpts ?: 'latest',
$anyHidden = false;
if ( !$opts || is_string( $opts ) ) {
$opts = array(
'version' => $opts ?: 'latest',
'language' => $wgContLang,
'multilang' => false,
'extmetadatafilter' => array(),
'revdelUser' => null,
);
}
$version = $metadataOpts['version'];
$version = $opts['version'];
$vals = array();
// Timestamp is shown even if the file is revdelete'd in interface
// so do same here.
@ -336,13 +349,27 @@ class ApiQueryImageInfo extends ApiQueryBase {
$vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
}
// Handle external callers who don't pass revdelUser
if ( isset( $opts['revdelUser'] ) && $opts['revdelUser'] ) {
$revdelUser = $opts['revdelUser'];
$canShowField = function ( $field ) use ( $file, $revdelUser ) {
return $file->userCan( $field, $revdelUser );
};
} else {
$canShowField = function ( $field ) use ( $file ) {
return !$file->isDeleted( $field );
};
}
$user = isset( $prop['user'] );
$userid = isset( $prop['userid'] );
if ( $user || $userid ) {
if ( $file->isDeleted( File::DELETED_USER ) ) {
$vals['userhidden'] = '';
} else {
$anyHidden = true;
}
if ( $canShowField( File::DELETED_USER ) ) {
if ( $user ) {
$vals['user'] = $file->getUser();
}
@ -374,13 +401,15 @@ class ApiQueryImageInfo extends ApiQueryBase {
if ( $pcomment || $comment ) {
if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
$vals['commenthidden'] = '';
} else {
$anyHidden = true;
}
if ( $canShowField( File::DELETED_COMMENT ) ) {
if ( $pcomment ) {
$vals['parsedcomment'] = Linker::formatComment(
$file->getDescription(), $file->getTitle() );
$file->getDescription( File::RAW ), $file->getTitle() );
}
if ( $comment ) {
$vals['comment'] = $file->getDescription();
$vals['comment'] = $file->getDescription( File::RAW );
}
}
}
@ -396,11 +425,20 @@ class ApiQueryImageInfo extends ApiQueryBase {
$bitdepth = isset( $prop['bitdepth'] );
$uploadwarning = isset( $prop['uploadwarning'] );
if ( ( $canonicaltitle || $url || $sha1 || $meta || $mime || $mediatype || $archive || $bitdepth )
&& $file->isDeleted( File::DELETED_FILE )
) {
$vals['filehidden'] = '';
if ( $uploadwarning ) {
$vals['html'] = SpecialUpload::getExistsWarning( UploadBase::getExistsWarning( $file ) );
}
if ( $file->isDeleted( File::DELETED_FILE ) ) {
$vals['filehidden'] = '';
$anyHidden = true;
}
if ( $anyHidden && $file->isDeleted( File::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = true;
}
if ( !$canShowField( File::DELETED_FILE ) ) {
//Early return, tidier than indenting all following things one level
return $vals;
}
@ -458,12 +496,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
// start with a letter, and all the values are strings.
// Thus there should be no issue with format=xml.
$format = new FormatMetadata;
$format->setSingleLanguage( !$metadataOpts['multilang'] );
$format->getContext()->setLanguage( $metadataOpts['language'] );
$format->setSingleLanguage( !$opts['multilang'] );
$format->getContext()->setLanguage( $opts['language'] );
$extmetaArray = $format->fetchExtendedMetadata( $file );
if ( $metadataOpts['extmetadatafilter'] ) {
if ( $opts['extmetadatafilter'] ) {
$extmetaArray = array_intersect_key(
$extmetaArray, array_flip( $metadataOpts['extmetadatafilter'] )
$extmetaArray, array_flip( $opts['extmetadatafilter'] )
);
}
$vals['extmetadata'] = $extmetaArray;
@ -485,10 +523,6 @@ class ApiQueryImageInfo extends ApiQueryBase {
$vals['bitdepth'] = $file->getBitDepth();
}
if ( $uploadwarning ) {
$vals['html'] = SpecialUpload::getExistsWarning( UploadBase::getExistsWarning( $file ) );
}
return $vals;
}
@ -528,6 +562,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
}
public function getCacheMode( $params ) {
if ( $this->userCanSeeRevDel() ) {
return 'private';
}
return 'public';
}

View file

@ -158,11 +158,23 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
// Paranoia: avoid brute force searches (bug 17342)
if ( !is_null( $title ) ) {
$this->addWhere( $db->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0' );
}
if ( !is_null( $user ) ) {
$this->addWhere( $db->bitAnd( 'log_deleted', LogPage::DELETED_USER ) . ' = 0' );
if ( !is_null( $title ) || !is_null( $user ) ) {
if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
$titleBits = LogPage::DELETED_ACTION;
$userBits = LogPage::DELETED_USER;
} elseif ( !$this->getUser()->isAllowed( 'suppressrevision' ) ) {
$titleBits = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
$userBits = LogPage::DELETED_USER | LogPage::DELETED_RESTRICTED;
} else {
$titleBits = 0;
$userBits = 0;
}
if ( !is_null( $title ) && $titleBits ) {
$this->addWhere( $db->bitAnd( 'log_deleted', $titleBits ) . " != $titleBits" );
}
if ( !is_null( $user ) && $userBits ) {
$this->addWhere( $db->bitAnd( 'log_deleted', $userBits ) . " != $userBits" );
}
}
$count = 0;
@ -296,6 +308,8 @@ class ApiQueryLogEvents extends ApiQueryBase {
private function extractRowInfo( $row ) {
$logEntry = DatabaseLogEntry::newFromRow( $row );
$vals = array();
$anyHidden = false;
$user = $this->getUser();
if ( $this->fld_ids ) {
$vals['logid'] = intval( $row->log_id );
@ -305,16 +319,29 @@ class ApiQueryLogEvents extends ApiQueryBase {
$title = Title::makeTitle( $row->log_namespace, $row->log_title );
}
if ( $this->fld_title || $this->fld_ids ) {
if ( $this->fld_title || $this->fld_ids || $this->fld_details && $row->log_params !== '' ) {
if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) {
$vals['actionhidden'] = '';
} else {
$anyHidden = true;
}
if ( LogEventsList::userCan( $row, LogPage::DELETED_ACTION, $user ) ) {
if ( $this->fld_title ) {
ApiQueryBase::addTitleInfo( $vals, $title );
}
if ( $this->fld_ids ) {
$vals['pageid'] = intval( $row->page_id );
}
if ( $this->fld_details && $row->log_params !== '' ) {
self::addLogParams(
$this->getResult(),
$vals,
$logEntry->getParameters(),
$logEntry->getType(),
$logEntry->getSubtype(),
$logEntry->getTimestamp(),
$logEntry->isLegacy()
);
}
}
}
@ -323,26 +350,12 @@ class ApiQueryLogEvents extends ApiQueryBase {
$vals['action'] = $row->log_action;
}
if ( $this->fld_details && $row->log_params !== '' ) {
if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) {
$vals['actionhidden'] = '';
} else {
self::addLogParams(
$this->getResult(),
$vals,
$logEntry->getParameters(),
$logEntry->getType(),
$logEntry->getSubtype(),
$logEntry->getTimestamp(),
$logEntry->isLegacy()
);
}
}
if ( $this->fld_user || $this->fld_userid ) {
if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) {
$vals['userhidden'] = '';
} else {
$anyHidden = true;
}
if ( LogEventsList::userCan( $row, LogPage::DELETED_USER, $user ) ) {
if ( $this->fld_user ) {
$vals['user'] = $row->user_name === null ? $row->log_user_text : $row->user_name;
}
@ -362,7 +375,9 @@ class ApiQueryLogEvents extends ApiQueryBase {
if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->log_comment ) ) {
if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) {
$vals['commenthidden'] = '';
} else {
$anyHidden = true;
}
if ( LogEventsList::userCan( $row, LogPage::DELETED_COMMENT, $user ) ) {
if ( $this->fld_comment ) {
$vals['comment'] = $row->log_comment;
}
@ -383,10 +398,17 @@ class ApiQueryLogEvents extends ApiQueryBase {
}
}
if ( $anyHidden && LogEventsList::isDeleted( $row, LogPage::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = '';
}
return $vals;
}
public function getCacheMode( $params ) {
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';

View file

@ -145,7 +145,6 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
/* Build our basic query. Namely, something along the lines of:
* SELECT * FROM recentchanges WHERE rc_timestamp > $start
* AND rc_timestamp < $end AND rc_namespace = $namespace
* AND rc_deleted = 0
*/
$this->addTables( 'recentchanges' );
$index = array( 'recentchanges' => 'rc_timestamp' ); // May change
@ -176,7 +175,6 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
) );
$this->addWhereFld( 'rc_namespace', $params['namespace'] );
$this->addWhereFld( 'rc_deleted', 0 );
if ( !is_null( $params['type'] ) ) {
$this->addWhereFld( 'rc_type', $this->parseRCType( $params['type'] ) );
@ -326,6 +324,36 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$this->addWhereFld( 'ct_tag', $params['tag'] );
}
// Paranoia: avoid brute force searches (bug 17342)
if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
if ( !$user->isAllowed( 'deletedhistory' ) ) {
$bitmask = Revision::DELETED_USER;
} elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
$bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
} else {
$bitmask = 0;
}
if ( $bitmask ) {
$this->addWhere( $this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask" );
}
}
if ( $this->getRequest()->getCheck( 'namespace' ) ) {
// LogPage::DELETED_ACTION hides the affected page, too.
if ( !$user->isAllowed( 'deletedhistory' ) ) {
$bitmask = LogPage::DELETED_ACTION;
} elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
$bitmask = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
} else {
$bitmask = 0;
}
if ( $bitmask ) {
$this->addWhere( $this->getDB()->makeList( array(
'rc_type != ' . RC_LOG,
$this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
), LIST_OR ) );
}
}
$this->token = $params['token'];
$this->addOption( 'LIMIT', $params['limit'] + 1 );
$this->addOption( 'USE INDEX', $index );
@ -389,6 +417,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
public function extractRowInfo( $row ) {
/* Determine the title of the page that has been changed. */
$title = Title::makeTitle( $row->rc_namespace, $row->rc_title );
$user = $this->getUser();
/* Our output data. */
$vals = array();
@ -419,32 +448,50 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$vals['type'] = $type;
}
$anyHidden = false;
/* Create a new entry in the result for the title. */
if ( $this->fld_title ) {
ApiQueryBase::addTitleInfo( $vals, $title );
if ( $this->fld_title || $this->fld_ids ) {
if ( $type === RC_LOG && ( $row->rc_deleted & LogPage::DELETED_ACTION ) ) {
$vals['actionhidden'] = '';
$anyHidden = true;
}
if ( $type !== RC_LOG ||
LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user )
) {
if ( $this->fld_title ) {
ApiQueryBase::addTitleInfo( $vals, $title );
}
if ( $this->fld_ids ) {
$vals['pageid'] = intval( $row->rc_cur_id );
$vals['revid'] = intval( $row->rc_this_oldid );
$vals['old_revid'] = intval( $row->rc_last_oldid );
}
}
}
/* Add ids, such as rcid, pageid, revid, and oldid to the change's info. */
if ( $this->fld_ids ) {
$vals['rcid'] = intval( $row->rc_id );
$vals['pageid'] = intval( $row->rc_cur_id );
$vals['revid'] = intval( $row->rc_this_oldid );
$vals['old_revid'] = intval( $row->rc_last_oldid );
}
/* Add user data and 'anon' flag, if use is anonymous. */
/* Add user data and 'anon' flag, if user is anonymous. */
if ( $this->fld_user || $this->fld_userid ) {
if ( $this->fld_user ) {
$vals['user'] = $row->rc_user_text;
if ( $row->rc_deleted & Revision::DELETED_USER ) {
$vals['userhidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_USER, $user ) ) {
if ( $this->fld_user ) {
$vals['user'] = $row->rc_user_text;
}
if ( $this->fld_userid ) {
$vals['userid'] = $row->rc_user;
}
if ( $this->fld_userid ) {
$vals['userid'] = $row->rc_user;
}
if ( !$row->rc_user ) {
$vals['anon'] = '';
if ( !$row->rc_user ) {
$vals['anon'] = '';
}
}
}
@ -473,12 +520,20 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
}
/* Add edit summary / log summary. */
if ( $this->fld_comment && isset( $row->rc_comment ) ) {
$vals['comment'] = $row->rc_comment;
}
if ( $this->fld_comment || $this->fld_parsedcomment ) {
if ( $row->rc_deleted & Revision::DELETED_COMMENT ) {
$vals['commenthidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_COMMENT, $user ) ) {
if ( $this->fld_comment && isset( $row->rc_comment ) ) {
$vals['comment'] = $row->rc_comment;
}
if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
$vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
$vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
}
}
}
if ( $this->fld_redirect ) {
@ -492,23 +547,29 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
$vals['patrolled'] = '';
}
if ( $this->fld_patrolled && ChangesList::isUnpatrolled( $row, $this->getUser() ) ) {
if ( $this->fld_patrolled && ChangesList::isUnpatrolled( $row, $user ) ) {
$vals['unpatrolled'] = '';
}
if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) {
$vals['logid'] = intval( $row->rc_logid );
$vals['logtype'] = $row->rc_log_type;
$vals['logaction'] = $row->rc_log_action;
$logEntry = DatabaseLogEntry::newFromRow( (array)$row );
ApiQueryLogEvents::addLogParams(
$this->getResult(),
$vals,
$logEntry->getParameters(),
$logEntry->getType(),
$logEntry->getSubtype(),
$logEntry->getTimestamp()
);
if ( $row->rc_deleted & LogPage::DELETED_ACTION ) {
$vals['actionhidden'] = '';
$anyHidden = true;
}
if ( LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user ) ) {
$vals['logid'] = intval( $row->rc_logid );
$vals['logtype'] = $row->rc_log_type;
$vals['logaction'] = $row->rc_log_action;
$logEntry = DatabaseLogEntry::newFromRow( (array)$row );
ApiQueryLogEvents::addLogParams(
$this->getResult(),
$vals,
$logEntry->getParameters(),
$logEntry->getType(),
$logEntry->getSubtype(),
$logEntry->getTimestamp()
);
}
}
if ( $this->fld_tags ) {
@ -522,15 +583,16 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
}
if ( $this->fld_sha1 && $row->rev_sha1 !== null ) {
// The RevDel check should currently never pass due to the
// rc_deleted = 0 condition in the WHERE clause, but in case that
// ever changes we check it here too.
if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
$vals['sha1hidden'] = '';
} elseif ( $row->rev_sha1 !== '' ) {
$vals['sha1'] = wfBaseConvert( $row->rev_sha1, 36, 16, 40 );
} else {
$vals['sha1'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->rev_deleted, Revision::DELETED_TEXT, $user ) ) {
if ( $row->rev_sha1 !== '' ) {
$vals['sha1'] = wfBaseConvert( $row->rev_sha1, 36, 16, 40 );
} else {
$vals['sha1'] = '';
}
}
}
@ -547,6 +609,10 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
}
}
if ( $anyHidden && ( $row->rc_deleted & Revision::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = '';
}
return $vals;
}
@ -585,6 +651,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
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';

View file

@ -147,7 +147,7 @@ class ApiQueryRevisions extends ApiQueryBase {
if ( !$difftoRev ) {
$this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) );
}
if ( $difftoRev->isDeleted( Revision::DELETED_TEXT ) ) {
if ( !$diffToRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
$this->setWarning( "Couldn't diff to r{$difftoRev->getID()}: content is hidden" );
$params['diffto'] = null;
}
@ -316,7 +316,16 @@ class ApiQueryRevisions extends ApiQueryBase {
}
if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
// Paranoia: avoid brute force searches (bug 17342)
$this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
$bitmask = Revision::DELETED_USER;
} elseif ( !$this->getUser()->isAllowed( 'suppressrevision' ) ) {
$bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
} else {
$bitmask = 0;
}
if ( $bitmask ) {
$this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
}
}
} elseif ( $revCount > 0 ) {
$max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
@ -408,7 +417,9 @@ class ApiQueryRevisions extends ApiQueryBase {
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() );
@ -425,11 +436,13 @@ class ApiQueryRevisions extends ApiQueryBase {
if ( $this->fld_user || $this->fld_userid ) {
if ( $revision->isDeleted( Revision::DELETED_USER ) ) {
$vals['userhidden'] = '';
} else {
$anyHidden = true;
}
if ( $revision->userCan( Revision::DELETED_USER, $user ) ) {
if ( $this->fld_user ) {
$vals['user'] = $revision->getUserText();
$vals['user'] = $revision->getRawUserText();
}
$userid = $revision->getUser();
$userid = $revision->getRawUser();
if ( !$userid ) {
$vals['anon'] = '';
}
@ -452,14 +465,18 @@ class ApiQueryRevisions extends ApiQueryBase {
}
}
if ( $this->fld_sha1 && !$revision->isDeleted( Revision::DELETED_TEXT ) ) {
if ( $revision->getSha1() != '' ) {
$vals['sha1'] = wfBaseConvert( $revision->getSha1(), 36, 16, 40 );
} else {
$vals['sha1'] = '';
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'] = '';
}
}
} elseif ( $this->fld_sha1 ) {
$vals['sha1hidden'] = '';
}
if ( $this->fld_contentmodel ) {
@ -469,8 +486,10 @@ class ApiQueryRevisions extends ApiQueryBase {
if ( $this->fld_comment || $this->fld_parsedcomment ) {
if ( $revision->isDeleted( Revision::DELETED_COMMENT ) ) {
$vals['commenthidden'] = '';
} else {
$comment = $revision->getComment();
$anyHidden = true;
}
if ( $revision->userCan( Revision::DELETED_COMMENT, $user ) ) {
$comment = $revision->getRawComment();
if ( $this->fld_comment ) {
$vals['comment'] = $comment;
@ -507,7 +526,7 @@ class ApiQueryRevisions extends ApiQueryBase {
$content = null;
global $wgParser;
if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
$content = $revision->getContent();
$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
@ -520,8 +539,14 @@ class ApiQueryRevisions extends ApiQueryBase {
);
}
}
if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
$vals['texthidden'] = '';
$anyHidden = true;
} elseif ( !$content ) {
$vals['textmissing'] = '';
}
}
if ( $this->fld_content && $content && !$revision->isDeleted( Revision::DELETED_TEXT ) ) {
if ( $this->fld_content && $content ) {
$text = null;
if ( $this->generateXML ) {
@ -596,21 +621,13 @@ class ApiQueryRevisions extends ApiQueryBase {
if ( $text !== false ) {
ApiResult::setContent( $vals, $text );
}
} elseif ( $this->fld_content ) {
if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
$vals['texthidden'] = '';
} else {
$vals['textmissing'] = '';
}
}
if ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
if ( $content && ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) ) {
global $wgAPIMaxUncachedDiffs;
static $n = 0; // Number of uncached diffs we've had
if ( is_null( $content ) ) {
$vals['textmissing'] = '';
} elseif ( $n < $wgAPIMaxUncachedDiffs ) {
if ( $n < $wgAPIMaxUncachedDiffs ) {
$vals['diff'] = array();
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $title );
@ -653,6 +670,10 @@ class ApiQueryRevisions extends ApiQueryBase {
}
}
if ( $anyHidden && $revision->isDeleted( Revision::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = '';
}
return $vals;
}
@ -660,6 +681,9 @@ class ApiQueryRevisions extends ApiQueryBase {
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';

View file

@ -176,9 +176,19 @@ class ApiQueryContributions extends ApiQueryBase {
);
}
if ( !$user->isAllowed( 'hideuser' ) ) {
$this->addWhere( $this->getDB()->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
// Don't include any revisions where we're not supposed to be able to
// see the username.
if ( !$user->isAllowed( 'deletedhistory' ) ) {
$bitmask = Revision::DELETED_USER;
} elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
$bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
} else {
$bitmask = 0;
}
if ( $bitmask ) {
$this->addWhere( $this->getDB()->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
}
// We only want pages by the specified users.
if ( $this->prefixMode ) {
$this->addWhere( 'rev_user_text' .
@ -299,11 +309,19 @@ class ApiQueryContributions extends ApiQueryBase {
*/
private function extractRowInfo( $row ) {
$vals = array();
$anyHidden = false;
if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
$vals['texthidden'] = '';
$anyHidden = true;
}
// Any rows where we can't view the user were filtered out in the query.
$vals['userid'] = $row->rev_user;
$vals['user'] = $row->rev_user_text;
if ( $row->rev_deleted & Revision::DELETED_USER ) {
$vals['userhidden'] = '';
$anyHidden = true;
}
if ( $this->fld_ids ) {
$vals['pageid'] = intval( $row->rev_page );
@ -340,7 +358,9 @@ class ApiQueryContributions extends ApiQueryBase {
if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->rev_comment ) ) {
if ( $row->rev_deleted & Revision::DELETED_COMMENT ) {
$vals['commenthidden'] = '';
} else {
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->rev_deleted, Revision::DELETED_COMMENT, $this->getUser() ) ) {
if ( $this->fld_comment ) {
$vals['comment'] = $row->rev_comment;
}
@ -379,6 +399,10 @@ class ApiQueryContributions extends ApiQueryBase {
}
}
if ( $anyHidden && $row->rev_deleted & Revision::DELETED_RESTRICTED ) {
$vals['suppressed'] = '';
}
return $vals;
}

View file

@ -59,7 +59,8 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$params = $this->extractRequestParams();
$user = $this->getWatchlistUser( $params );
$user = $this->getUser();
$wlowner = $this->getWatchlistUser( $params );
if ( !is_null( $params['prop'] ) && is_null( $resultPageSet ) ) {
$prop = array_flip( $params['prop'] );
@ -89,6 +90,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'rc_title',
'rc_timestamp',
'rc_type',
'rc_deleted',
) );
if ( is_null( $resultPageSet ) ) {
@ -120,7 +122,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
'watchlist',
) );
$userId = $user->getId();
$userId = $wlowner->getId();
$this->addJoinConds( array( 'watchlist' => array( 'INNER JOIN',
array(
'wl_user' => $userId,
@ -129,10 +131,6 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
)
) ) );
$this->addWhere( array(
'rc_deleted' => 0,
) );
$db = $this->getDB();
$this->addTimestampWhereRange( 'rc_timestamp', $params['dir'],
@ -159,7 +157,6 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
// Check permissions.
if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) {
$user = $this->getUser();
if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
$this->dieUsage(
'You need the patrol right to request the patrolled flag',
@ -199,6 +196,36 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
!isset( $params['start'] ) && !isset( $params['end'] ) && $db->getType() == 'mysql'
);
// Paranoia: avoid brute force searches (bug 17342)
if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
if ( !$user->isAllowed( 'deletedhistory' ) ) {
$bitmask = Revision::DELETED_USER;
} elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
$bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
} else {
$bitmask = 0;
}
if ( $bitmask ) {
$this->addWhere( $this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask" );
}
}
// LogPage::DELETED_ACTION hides the affected page, too. So hide those
// entirely from the watchlist, or someone could guess the title.
if ( !$user->isAllowed( 'deletedhistory' ) ) {
$bitmask = LogPage::DELETED_ACTION;
} elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
$bitmask = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
} else {
$bitmask = 0;
}
if ( $bitmask ) {
$this->addWhere( $this->getDB()->makeList( array(
'rc_type != ' . RC_LOG,
$this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
), LIST_OR ) );
}
$this->addOption( 'LIMIT', $params['limit'] + 1 );
$ids = array();
@ -246,6 +273,11 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
}
private function extractRowInfo( $row ) {
/* Determine the title of the page that has been changed. */
$title = Title::makeTitle( $row->rc_namespace, $row->rc_title );
$user = $this->getUser();
/* Our output data. */
$vals = array();
$type = intval( $row->rc_type );
@ -274,87 +306,131 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
$vals['type'] = $type;
}
if ( $this->fld_ids ) {
$vals['pageid'] = intval( $row->rc_cur_id );
$vals['revid'] = intval( $row->rc_this_oldid );
$vals['old_revid'] = intval( $row->rc_last_oldid );
}
$title = Title::makeTitle( $row->rc_namespace, $row->rc_title );
if ( $this->fld_title ) {
ApiQueryBase::addTitleInfo( $vals, $title );
$anyHidden = false;
/* Create a new entry in the result for the title. */
if ( $this->fld_title || $this->fld_ids ) {
// These should already have been filtered out of the query, but just in case.
if ( $type === RC_LOG && ( $row->rc_deleted & LogPage::DELETED_ACTION ) ) {
$vals['actionhidden'] = '';
$anyHidden = true;
}
if ( $type !== RC_LOG ||
LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user )
) {
if ( $this->fld_title ) {
ApiQueryBase::addTitleInfo( $vals, $title );
}
if ( $this->fld_ids ) {
$vals['pageid'] = intval( $row->rc_cur_id );
$vals['revid'] = intval( $row->rc_this_oldid );
$vals['old_revid'] = intval( $row->rc_last_oldid );
}
}
}
/* Add user data and 'anon' flag, if user is anonymous. */
if ( $this->fld_user || $this->fld_userid ) {
if ( $this->fld_userid ) {
$vals['userid'] = $row->rc_user;
// for backwards compatibility
$vals['user'] = $row->rc_user;
if ( $row->rc_deleted & Revision::DELETED_USER ) {
$vals['userhidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_USER, $user ) ) {
if ( $this->fld_userid ) {
$vals['userid'] = $row->rc_user;
// for backwards compatibility
$vals['user'] = $row->rc_user;
}
if ( $this->fld_user ) {
$vals['user'] = $row->rc_user_text;
}
if ( $this->fld_user ) {
$vals['user'] = $row->rc_user_text;
}
if ( !$row->rc_user ) {
$vals['anon'] = '';
if ( !$row->rc_user ) {
$vals['anon'] = '';
}
}
}
/* Add flags, such as new, minor, bot. */
if ( $this->fld_flags ) {
if ( $row->rc_bot ) {
$vals['bot'] = '';
}
if ( $row->rc_type == RC_NEW ) {
$vals['new'] = '';
}
if ( $row->rc_minor ) {
$vals['minor'] = '';
}
if ( $row->rc_bot ) {
$vals['bot'] = '';
}
}
if ( $this->fld_patrol && isset( $row->rc_patrolled ) ) {
$vals['patrolled'] = '';
}
if ( $this->fld_timestamp ) {
$vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp );
}
/* Add sizes of each revision. (Only available on 1.10+) */
if ( $this->fld_sizes ) {
$vals['oldlen'] = intval( $row->rc_old_len );
$vals['newlen'] = intval( $row->rc_new_len );
}
/* Add the timestamp. */
if ( $this->fld_timestamp ) {
$vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp );
}
if ( $this->fld_notificationtimestamp ) {
$vals['notificationtimestamp'] = ( $row->wl_notificationtimestamp == null )
? ''
: wfTimestamp( TS_ISO_8601, $row->wl_notificationtimestamp );
}
if ( $this->fld_comment && isset( $row->rc_comment ) ) {
$vals['comment'] = $row->rc_comment;
/* Add edit summary / log summary. */
if ( $this->fld_comment || $this->fld_parsedcomment ) {
if ( $row->rc_deleted & Revision::DELETED_COMMENT ) {
$vals['commenthidden'] = '';
$anyHidden = true;
}
if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_COMMENT, $user ) ) {
if ( $this->fld_comment && isset( $row->rc_comment ) ) {
$vals['comment'] = $row->rc_comment;
}
if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
$vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
}
}
}
if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
$vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
/* Add the patrolled flag */
if ( $this->fld_patrol && $row->rc_patrolled == 1 ) {
$vals['patrolled'] = '';
}
if ( $this->fld_patrol && ChangesList::isUnpatrolled( $row, $user ) ) {
$vals['unpatrolled'] = '';
}
if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) {
$vals['logid'] = intval( $row->rc_logid );
$vals['logtype'] = $row->rc_log_type;
$vals['logaction'] = $row->rc_log_action;
$logEntry = DatabaseLogEntry::newFromRow( (array)$row );
ApiQueryLogEvents::addLogParams(
$this->getResult(),
$vals,
$logEntry->getParameters(),
$logEntry->getType(),
$logEntry->getSubtype(),
$logEntry->getTimestamp()
);
if ( $row->rc_deleted & LogPage::DELETED_ACTION ) {
$vals['actionhidden'] = '';
$anyHidden = true;
}
if ( LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user ) ) {
$vals['logid'] = intval( $row->rc_logid );
$vals['logtype'] = $row->rc_log_type;
$vals['logaction'] = $row->rc_log_action;
$logEntry = DatabaseLogEntry::newFromRow( (array)$row );
ApiQueryLogEvents::addLogParams(
$this->getResult(),
$vals,
$logEntry->getParameters(),
$logEntry->getType(),
$logEntry->getSubtype(),
$logEntry->getTimestamp()
);
}
}
if ( $anyHidden && ( $row->rc_deleted & Revision::DELETED_RESTRICTED ) ) {
$vals['suppressed'] = '';
}
return $vals;
@ -467,7 +543,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
),
'token' => array(
ApiBase::PARAM_TYPE => 'string'
)
),
);
}
@ -511,7 +587,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
),
'owner' => 'The name of the user whose watchlist you\'d like to access',
'token' => 'Give a security token (settable in preferences) to ' .
'allow access to another user\'s watchlist'
'allow access to another user\'s watchlist',
);
}