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