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