MediaWiki master
ApiQueryFilearchive.php
Go to the documentation of this file.
1<?php
13namespace MediaWiki\Api;
14
27use Wikimedia\Timestamp\TimestampFormat as TS;
28
35
36 public function __construct(
37 ApiQuery $query,
38 string $moduleName,
39 private readonly CommentStore $commentStore,
40 private readonly CommentFormatter $commentFormatter,
41 ) {
42 parent::__construct( $query, $moduleName, 'fa' );
43 }
44
45 public function execute() {
46 $user = $this->getUser();
47 $db = $this->getDB();
48
49 $params = $this->extractRequestParams();
50
51 $prop = array_fill_keys( $params['prop'], true );
52 $fld_sha1 = isset( $prop['sha1'] );
53 $fld_timestamp = isset( $prop['timestamp'] );
54 $fld_user = isset( $prop['user'] );
55 $fld_size = isset( $prop['size'] );
56 $fld_dimensions = isset( $prop['dimensions'] );
57 $fld_description = isset( $prop['description'] ) || isset( $prop['parseddescription'] );
58 $fld_parseddescription = isset( $prop['parseddescription'] );
59 $fld_mime = isset( $prop['mime'] );
60 $fld_mediatype = isset( $prop['mediatype'] );
61 $fld_metadata = isset( $prop['metadata'] );
62 $fld_bitdepth = isset( $prop['bitdepth'] );
63 $fld_archivename = isset( $prop['archivename'] );
64
65 if ( $fld_description && !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
66 $this->dieWithError( 'apierror-cantview-deleted-description', 'permissiondenied' );
67 }
68 if ( $fld_metadata && !$this->getAuthority()->isAllowedAny( 'deletedtext', 'undelete' ) ) {
69 $this->dieWithError( 'apierror-cantview-deleted-metadata', 'permissiondenied' );
70 }
71
72 $fileQuery = ArchivedFile::getQueryInfo();
73 $this->addTables( $fileQuery['tables'] );
74 $this->addFields( $fileQuery['fields'] );
75 $this->addJoinConds( $fileQuery['joins'] );
76
77 if ( $params['continue'] !== null ) {
78 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'string', 'timestamp', 'int' ] );
79 $op = $params['dir'] == 'descending' ? '<=' : '>=';
80 $this->addWhere( $db->buildComparison( $op, [
81 'fa_name' => $cont[0],
82 'fa_timestamp' => $db->timestamp( $cont[1] ),
83 'fa_id' => $cont[2],
84 ] ) );
85 }
86
87 // Image filters
88 $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
89 $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_FILE ) );
90 $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_FILE ) );
91 $this->addWhereRange( 'fa_name', $dir, $from, $to );
92 if ( isset( $params['prefix'] ) ) {
93 $this->addWhere(
94 $db->expr(
95 'fa_name',
96 IExpression::LIKE,
97 new LikeValue( $this->titlePartToKey( $params['prefix'], NS_FILE ), $db->anyString() )
98 )
99 );
100 }
101
102 $sha1Set = isset( $params['sha1'] );
103 $sha1base36Set = isset( $params['sha1base36'] );
104 if ( $sha1Set || $sha1base36Set ) {
105 $sha1 = false;
106 if ( $sha1Set ) {
107 $sha1 = strtolower( $params['sha1'] );
108 if ( !$this->validateSha1Hash( $sha1 ) ) {
109 $this->dieWithError( 'apierror-invalidsha1hash' );
110 }
111 $sha1 = \Wikimedia\base_convert( $sha1, 16, 36, 31 );
112 } elseif ( $sha1base36Set ) {
113 $sha1 = strtolower( $params['sha1base36'] );
114 if ( !$this->validateSha1Base36Hash( $sha1 ) ) {
115 $this->dieWithError( 'apierror-invalidsha1base36hash' );
116 }
117 }
118 if ( $sha1 ) {
119 $this->addWhereFld( 'fa_sha1', $sha1 );
120 // Paranoia: avoid brute force searches (T19342)
121 if ( !$this->getAuthority()->isAllowed( 'deletedtext' ) ) {
122 $bitmask = File::DELETED_FILE;
123 } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
124 $bitmask = File::DELETED_FILE | File::DELETED_RESTRICTED;
125 } else {
126 $bitmask = 0;
127 }
128 if ( $bitmask ) {
129 $this->addWhere( $this->getDB()->bitAnd( 'fa_deleted', $bitmask ) . " != $bitmask" );
130 }
131 }
132 }
133
134 $limit = $params['limit'];
135 $this->addOption( 'LIMIT', $limit + 1 );
136 $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
137 $this->addOption( 'ORDER BY', [
138 'fa_name' . $sort,
139 'fa_timestamp' . $sort,
140 'fa_id' . $sort,
141 ] );
142
143 $res = $this->select( __METHOD__ );
144
145 // Format descriptions in a batch
146 $formattedDescriptions = [];
147 $descriptions = [];
148 if ( $fld_parseddescription ) {
149 $commentItems = [];
150 foreach ( $res as $row ) {
151 $desc = $this->commentStore->getComment( 'fa_description', $row )->text;
152 $descriptions[$row->fa_id] = $desc;
153 $commentItems[$row->fa_id] = ( new CommentItem( $desc ) )
154 ->selfLinkTarget( new TitleValue( NS_FILE, $row->fa_name ) );
155 }
156 $formattedDescriptions = $this->commentFormatter->createBatch()
157 ->comments( $commentItems )
158 ->execute();
159 }
160
161 $count = 0;
162 $result = $this->getResult();
163 foreach ( $res as $row ) {
164 if ( ++$count > $limit ) {
165 // We've reached the one extra which shows that there are
166 // additional pages to be had. Stop here...
168 'continue', "$row->fa_name|$row->fa_timestamp|$row->fa_id"
169 );
170 break;
171 }
172
173 $exists = $row->fa_archive_name !== '';
174 $canViewFile = RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_FILE, $user );
175
176 $file = [];
177 $file['id'] = (int)$row->fa_id;
178 $file['name'] = $row->fa_name;
179 $title = Title::makeTitle( NS_FILE, $row->fa_name );
180 self::addTitleInfo( $file, $title );
181
182 if ( $fld_description &&
183 RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_COMMENT, $user )
184 ) {
185 if ( isset( $prop['parseddescription'] ) ) {
186 $file['parseddescription'] = $formattedDescriptions[$row->fa_id];
187 $file['description'] = $descriptions[$row->fa_id];
188 } else {
189 $file['description'] = $this->commentStore->getComment( 'fa_description', $row )->text;
190 }
191 }
192 if ( $fld_user &&
193 RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_USER, $user )
194 ) {
195 $file['userid'] = (int)$row->fa_user;
196 $file['user'] = $row->fa_user_text;
197 }
198 if ( !$exists ) {
199 $file['filemissing'] = true;
200 }
201 if ( $fld_sha1 && $canViewFile && $exists ) {
202 $file['sha1'] = \Wikimedia\base_convert( $row->fa_sha1, 36, 16, 40 );
203 }
204 if ( $fld_timestamp ) {
205 $file['timestamp'] = wfTimestamp( TS::ISO_8601, $row->fa_timestamp );
206 }
207 if ( ( $fld_size || $fld_dimensions ) && $canViewFile && $exists ) {
208 $file['size'] = $row->fa_size;
209
210 $pageCount = ArchivedFile::newFromRow( $row )->pageCount();
211 if ( $pageCount !== false ) {
212 $file['pagecount'] = $pageCount;
213 }
214
215 $file['height'] = $row->fa_height;
216 $file['width'] = $row->fa_width;
217 }
218 if ( $fld_mediatype && $canViewFile && $exists ) {
219 $file['mediatype'] = $row->fa_media_type;
220 }
221 if ( $fld_metadata && $canViewFile && $exists ) {
222 $metadataArray = ArchivedFile::newFromRow( $row )->getMetadataArray();
223 $file['metadata'] = $row->fa_metadata
224 ? ApiQueryImageInfo::processMetaData( $metadataArray, $result )
225 : null;
226 }
227 if ( $fld_bitdepth && $canViewFile && $exists ) {
228 $file['bitdepth'] = $row->fa_bits;
229 }
230 if ( $fld_mime && $canViewFile && $exists ) {
231 $file['mime'] = "$row->fa_major_mime/$row->fa_minor_mime";
232 }
233 if ( $fld_archivename && $row->fa_archive_name !== null ) {
234 $file['archivename'] = $row->fa_archive_name;
235 }
236
237 if ( $row->fa_deleted & File::DELETED_FILE ) {
238 $file['filehidden'] = true;
239 }
240 if ( $row->fa_deleted & File::DELETED_COMMENT ) {
241 $file['commenthidden'] = true;
242 }
243 if ( $row->fa_deleted & File::DELETED_USER ) {
244 $file['userhidden'] = true;
245 }
246 if ( $row->fa_deleted & File::DELETED_RESTRICTED ) {
247 // This file is deleted for normal admins
248 $file['suppressed'] = true;
249 }
250
251 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $file );
252 if ( !$fit ) {
254 'continue', "$row->fa_name|$row->fa_timestamp|$row->fa_id"
255 );
256 break;
257 }
258 }
259
260 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'fa' );
261 }
262
264 public function getAllowedParams() {
265 return [
266 'from' => null,
267 'to' => null,
268 'prefix' => null,
269 'dir' => [
270 ParamValidator::PARAM_DEFAULT => 'ascending',
271 ParamValidator::PARAM_TYPE => [
272 'ascending',
273 'descending'
274 ]
275 ],
276 'sha1' => null,
277 'sha1base36' => null,
278 'prop' => [
279 ParamValidator::PARAM_DEFAULT => 'timestamp',
280 ParamValidator::PARAM_ISMULTI => true,
281 ParamValidator::PARAM_TYPE => [
282 'sha1',
283 'timestamp',
284 'user',
285 'size',
286 'dimensions',
287 'description',
288 'parseddescription',
289 'mime',
290 'mediatype',
291 'metadata',
292 'bitdepth',
293 'archivename',
294 ],
296 ],
297 'limit' => [
298 ParamValidator::PARAM_DEFAULT => 10,
299 ParamValidator::PARAM_TYPE => 'limit',
300 IntegerDef::PARAM_MIN => 1,
301 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
302 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
303 ],
304 'continue' => [
305 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
306 ],
307 ];
308 }
309
311 protected function getExamplesMessages() {
312 return [
313 'action=query&list=filearchive'
314 => 'apihelp-query+filearchive-example-simple',
315 ];
316 }
317
319 public function getHelpUrls() {
320 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Filearchive';
321 }
322}
323
325class_alias( ApiQueryFilearchive::class, 'ApiQueryFilearchive' );
const NS_FILE
Definition Defines.php:57
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1506
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:542
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition ApiBase.php:1691
getResult()
Get the result object.
Definition ApiBase.php:681
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:206
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:166
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:233
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:822
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:231
This is a base class for all Query modules.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
getDB()
Get the Query database connection (read-only).
select( $method, $extraQuery=[], ?array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
addWhere( $value)
Add a set of WHERE clauses to the internal array.
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addFields( $value)
Add a set of fields to select to the internal array.
addWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, and an ORDER BY clause to sort in the right direction.
Query module to enumerate all deleted files.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
__construct(ApiQuery $query, string $moduleName, private readonly CommentStore $commentStore, private readonly CommentFormatter $commentFormatter,)
getHelpUrls()
Return links to more detailed help pages about the module.1.25, returning boolean false is deprecated...
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
static processMetaData( $metadata, $result)
This is the main query class.
Definition ApiQuery.php:36
This is the main service interface for converting single-line comments from various DB comment fields...
An object to represent one of the inputs to a batch formatting operation.
Handle database storage of comments such as edit summaries and log reasons.
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
Deleted file in the 'filearchive' table.
static newFromRow( $row)
Loads a file object from the filearchive table.
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new archivedfile object.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:80
Page revision base class.
Represents the target of a wiki link.
Represents a title within MediaWiki.
Definition Title.php:69
Service for formatting and validating API parameters.
Type definition for integer types.
Content of like value.
Definition LikeValue.php:14