MediaWiki  master
ApiQueryFilearchive.php
Go to the documentation of this file.
1 <?php
30 
37 
39  private $commentStore;
40 
43 
50  public function __construct(
51  ApiQuery $query,
52  $moduleName,
55  ) {
56  parent::__construct( $query, $moduleName, 'fa' );
57  $this->commentStore = $commentStore;
58  $this->commentFormatter = $commentFormatter;
59  }
60 
61  public function execute() {
62  $user = $this->getUser();
63  $db = $this->getDB();
64 
65  $params = $this->extractRequestParams();
66 
67  $prop = array_fill_keys( $params['prop'], true );
68  $fld_sha1 = isset( $prop['sha1'] );
69  $fld_timestamp = isset( $prop['timestamp'] );
70  $fld_user = isset( $prop['user'] );
71  $fld_size = isset( $prop['size'] );
72  $fld_dimensions = isset( $prop['dimensions'] );
73  $fld_description = isset( $prop['description'] ) || isset( $prop['parseddescription'] );
74  $fld_parseddescription = isset( $prop['parseddescription'] );
75  $fld_mime = isset( $prop['mime'] );
76  $fld_mediatype = isset( $prop['mediatype'] );
77  $fld_metadata = isset( $prop['metadata'] );
78  $fld_bitdepth = isset( $prop['bitdepth'] );
79  $fld_archivename = isset( $prop['archivename'] );
80 
81  if ( $fld_description && !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
82  $this->dieWithError( 'apierror-cantview-deleted-description', 'permissiondenied' );
83  }
84  if ( $fld_metadata && !$this->getAuthority()->isAllowedAny( 'deletedtext', 'undelete' ) ) {
85  $this->dieWithError( 'apierror-cantview-deleted-metadata', 'permissiondenied' );
86  }
87 
88  $fileQuery = ArchivedFile::getQueryInfo();
89  $this->addTables( $fileQuery['tables'] );
90  $this->addFields( $fileQuery['fields'] );
91  $this->addJoinConds( $fileQuery['joins'] );
92 
93  if ( $params['continue'] !== null ) {
94  $cont = explode( '|', $params['continue'] );
95  $this->dieContinueUsageIf( count( $cont ) != 3 );
96  $op = $params['dir'] == 'descending' ? '<' : '>';
97  $cont_from = $db->addQuotes( $cont[0] );
98  $cont_timestamp = $db->addQuotes( $db->timestamp( $cont[1] ) );
99  $cont_id = (int)$cont[2];
100  $this->dieContinueUsageIf( $cont[2] !== (string)$cont_id );
101  $this->addWhere( "fa_name $op $cont_from OR " .
102  "(fa_name = $cont_from AND " .
103  "(fa_timestamp $op $cont_timestamp OR " .
104  "(fa_timestamp = $cont_timestamp AND " .
105  "fa_id $op= $cont_id )))"
106  );
107  }
108 
109  // Image filters
110  $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
111  $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_FILE ) );
112  $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_FILE ) );
113  $this->addWhereRange( 'fa_name', $dir, $from, $to );
114  if ( isset( $params['prefix'] ) ) {
115  $this->addWhere( 'fa_name' . $db->buildLike(
116  $this->titlePartToKey( $params['prefix'], NS_FILE ),
117  $db->anyString() ) );
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' ) ) {
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  $canViewFile = RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_FILE, $user );
192 
193  $file = [];
194  $file['id'] = (int)$row->fa_id;
195  $file['name'] = $row->fa_name;
196  $title = Title::makeTitle( NS_FILE, $row->fa_name );
198 
199  if ( $fld_description &&
200  RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_COMMENT, $user )
201  ) {
202  if ( isset( $prop['parseddescription'] ) ) {
203  $file['parseddescription'] = $formattedDescriptions[$row->fa_id];
204  $file['description'] = $descriptions[$row->fa_id];
205  } else {
206  $file['description'] = $this->commentStore->getComment( 'fa_description', $row )->text;
207  }
208  }
209  if ( $fld_user &&
210  RevisionRecord::userCanBitfield( $row->fa_deleted, File::DELETED_USER, $user )
211  ) {
212  $file['userid'] = (int)$row->fa_user;
213  $file['user'] = $row->fa_user_text;
214  }
215  if ( $fld_sha1 && $canViewFile ) {
216  $file['sha1'] = Wikimedia\base_convert( $row->fa_sha1, 36, 16, 40 );
217  }
218  if ( $fld_timestamp ) {
219  $file['timestamp'] = wfTimestamp( TS_ISO_8601, $row->fa_timestamp );
220  }
221  if ( ( $fld_size || $fld_dimensions ) && $canViewFile ) {
222  $file['size'] = $row->fa_size;
223 
224  $pageCount = ArchivedFile::newFromRow( $row )->pageCount();
225  if ( $pageCount !== false ) {
226  $file['pagecount'] = $pageCount;
227  }
228 
229  $file['height'] = $row->fa_height;
230  $file['width'] = $row->fa_width;
231  }
232  if ( $fld_mediatype && $canViewFile ) {
233  $file['mediatype'] = $row->fa_media_type;
234  }
235  if ( $fld_metadata && $canViewFile ) {
236  $file['metadata'] = $row->fa_metadata
237  ? ApiQueryImageInfo::processMetaData( unserialize( $row->fa_metadata ), $result )
238  : null;
239  }
240  if ( $fld_bitdepth && $canViewFile ) {
241  $file['bitdepth'] = $row->fa_bits;
242  }
243  if ( $fld_mime && $canViewFile ) {
244  $file['mime'] = "$row->fa_major_mime/$row->fa_minor_mime";
245  }
246  if ( $fld_archivename && $row->fa_archive_name !== null ) {
247  $file['archivename'] = $row->fa_archive_name;
248  }
249 
250  if ( $row->fa_deleted & File::DELETED_FILE ) {
251  $file['filehidden'] = true;
252  }
253  if ( $row->fa_deleted & File::DELETED_COMMENT ) {
254  $file['commenthidden'] = true;
255  }
256  if ( $row->fa_deleted & File::DELETED_USER ) {
257  $file['userhidden'] = true;
258  }
259  if ( $row->fa_deleted & File::DELETED_RESTRICTED ) {
260  // This file is deleted for normal admins
261  $file['suppressed'] = true;
262  }
263 
264  $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $file );
265  if ( !$fit ) {
267  'continue', "$row->fa_name|$row->fa_timestamp|$row->fa_id"
268  );
269  break;
270  }
271  }
272 
273  $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'fa' );
274  }
275 
276  public function getAllowedParams() {
277  return [
278  'from' => null,
279  'to' => null,
280  'prefix' => null,
281  'dir' => [
282  ApiBase::PARAM_DFLT => 'ascending',
284  'ascending',
285  'descending'
286  ]
287  ],
288  'sha1' => null,
289  'sha1base36' => null,
290  'prop' => [
291  ApiBase::PARAM_DFLT => 'timestamp',
292  ApiBase::PARAM_ISMULTI => true,
294  'sha1',
295  'timestamp',
296  'user',
297  'size',
298  'dimensions',
299  'description',
300  'parseddescription',
301  'mime',
302  'mediatype',
303  'metadata',
304  'bitdepth',
305  'archivename',
306  ],
308  ],
309  'limit' => [
310  ApiBase::PARAM_DFLT => 10,
311  ApiBase::PARAM_TYPE => 'limit',
312  ApiBase::PARAM_MIN => 1,
315  ],
316  'continue' => [
317  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
318  ],
319  ];
320  }
321 
322  protected function getExamplesMessages() {
323  return [
324  'action=query&list=filearchive'
325  => 'apihelp-query+filearchive-example-simple',
326  ];
327  }
328 
329  public function getHelpUrls() {
330  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Filearchive';
331  }
332 }
ApiQueryFilearchive\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiQueryFilearchive.php:329
ApiQueryFilearchive
Query module to enumerate all deleted files.
Definition: ApiQueryFilearchive.php:36
ApiQueryBase\validateSha1Base36Hash
validateSha1Base36Hash( $hash)
Definition: ApiQueryBase.php:598
ApiQueryBase\addFields
addFields( $value)
Add a set of fields to select to the internal array.
Definition: ApiQueryBase.php:212
ApiQuery
This is the main query class.
Definition: ApiQuery.php:37
MediaWiki\Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
File\DELETED_USER
const DELETED_USER
Definition: File.php:72
File\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: File.php:73
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1379
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:105
ApiQueryFilearchive\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiQueryFilearchive.php:322
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1668
ApiBase\PARAM_TYPE
const PARAM_TYPE
Definition: ApiBase.php:72
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:571
CommentStore
Handle database storage of comments such as edit summaries and log reasons.
Definition: CommentStore.php:42
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
ArchivedFile\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new archivedfile object.
Definition: ArchivedFile.php:247
ApiQueryBase\addOption
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
Definition: ApiQueryBase.php:378
$res
$res
Definition: testCompression.php:57
ContextSource\getUser
getUser()
Definition: ContextSource.php:136
MediaWiki\CommentFormatter\CommentFormatter
This is the main service interface for converting single-line comments from various DB comment fields...
Definition: CommentFormatter.php:16
MediaWiki\CommentFormatter\CommentItem
An object to represent one of the inputs to a batch formatting operation.
Definition: CommentItem.php:13
ApiBase\PARAM_MIN
const PARAM_MIN
Definition: ApiBase.php:75
File\DELETED_COMMENT
const DELETED_COMMENT
Definition: File.php:71
ApiQueryBase
This is a base class for all Query modules.
Definition: ApiQueryBase.php:37
ApiBase\LIMIT_BIG1
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:163
ApiQueryBase\getDB
getDB()
Get the Query database connection (read-only)
Definition: ApiQueryBase.php:117
ApiQueryFilearchive\__construct
__construct(ApiQuery $query, $moduleName, CommentStore $commentStore, CommentFormatter $commentFormatter)
Definition: ApiQueryFilearchive.php:50
ApiBase\PARAM_MAX
const PARAM_MAX
Definition: ApiBase.php:73
ApiQueryFilearchive\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiQueryFilearchive.php:276
ApiQueryBase\addTables
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
Definition: ApiQueryBase.php:182
ApiQueryBase\select
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
Definition: ApiQueryBase.php:399
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:707
$title
$title
Definition: testCompression.php:38
ApiQueryImageInfo\processMetaData
static processMetaData( $metadata, $result)
Definition: ApiQueryImageInfo.php:662
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:650
ApiQueryFilearchive\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiQueryFilearchive.php:61
ApiQueryBase\addWhereRange
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.
Definition: ApiQueryBase.php:337
ApiBase\dieContinueUsageIf
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:1569
ContextSource\getAuthority
getAuthority()
Definition: ContextSource.php:144
ApiQueryBase\addJoinConds
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
Definition: ApiQueryBase.php:201
ApiQueryBase\addWhereFld
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
Definition: ApiQueryBase.php:282
unserialize
unserialize( $serialized)
Definition: ApiMessageTrait.php:146
ApiQueryFilearchive\$commentStore
CommentStore $commentStore
Definition: ApiQueryFilearchive.php:39
ApiBase\LIMIT_BIG2
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:165
ApiBase\PARAM_DFLT
const PARAM_DFLT
Definition: ApiBase.php:70
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:440
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
Definition: ApiBase.php:71
ApiBase\PARAM_MAX2
const PARAM_MAX2
Definition: ApiBase.php:74
File\DELETED_FILE
const DELETED_FILE
Definition: File.php:70
ApiQueryBase\addWhere
addWhere( $value)
Add a set of WHERE clauses to the internal array.
Definition: ApiQueryBase.php:245
ApiQueryBase\setContinueEnumParameter
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
Definition: ApiQueryBase.php:515
NS_FILE
const NS_FILE
Definition: Defines.php:70
ApiQueryFilearchive\$commentFormatter
CommentFormatter $commentFormatter
Definition: ApiQueryFilearchive.php:42
ApiBase\PARAM_HELP_MSG_PER_VALUE
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:138
ApiQueryBase\titlePartToKey
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
Definition: ApiQueryBase.php:529
ArchivedFile\newFromRow
static newFromRow( $row)
Loads a file object from the filearchive table.
Definition: ArchivedFile.php:225
ApiQueryBase\addTitleInfo
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
Definition: ApiQueryBase.php:466
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:40
ApiQueryBase\validateSha1Hash
validateSha1Hash( $hash)
Definition: ApiQueryBase.php:590