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