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:
parent
99cb12b973
commit
48de797fbd
10 changed files with 565 additions and 221 deletions
|
|
@ -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 ===
|
||||
|
||||
|
|
|
|||
|
|
@ -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' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue