Fix ParserOutput::getText 'unwrap' flag for end-of-doc comment
[lhc/web/wiklou.git] / includes / filerepo / file / ArchivedFile.php
1 <?php
2 /**
3 * Deleted file in the 'filearchive' table.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup FileAbstraction
22 */
23
24 /**
25 * Class representing a row of the 'filearchive' table
26 *
27 * @ingroup FileAbstraction
28 */
29 class ArchivedFile {
30 /** @var int Filearchive row ID */
31 private $id;
32
33 /** @var string File name */
34 private $name;
35
36 /** @var string FileStore storage group */
37 private $group;
38
39 /** @var string FileStore SHA-1 key */
40 private $key;
41
42 /** @var int File size in bytes */
43 private $size;
44
45 /** @var int Size in bytes */
46 private $bits;
47
48 /** @var int Width */
49 private $width;
50
51 /** @var int Height */
52 private $height;
53
54 /** @var string Metadata string */
55 private $metadata;
56
57 /** @var string MIME type */
58 private $mime;
59
60 /** @var string Media type */
61 private $media_type;
62
63 /** @var string Upload description */
64 private $description;
65
66 /** @var int User ID of uploader */
67 private $user;
68
69 /** @var string User name of uploader */
70 private $user_text;
71
72 /** @var string Time of upload */
73 private $timestamp;
74
75 /** @var bool Whether or not all this has been loaded from the database (loadFromXxx) */
76 private $dataLoaded;
77
78 /** @var int Bitfield akin to rev_deleted */
79 private $deleted;
80
81 /** @var string SHA-1 hash of file content */
82 private $sha1;
83
84 /** @var int|false Number of pages of a multipage document, or false for
85 * documents which aren't multipage documents
86 */
87 private $pageCount;
88
89 /** @var string Original base filename */
90 private $archive_name;
91
92 /** @var MediaHandler */
93 protected $handler;
94
95 /** @var Title */
96 protected $title; # image title
97
98 /**
99 * @throws MWException
100 * @param Title $title
101 * @param int $id
102 * @param string $key
103 * @param string $sha1
104 */
105 function __construct( $title, $id = 0, $key = '', $sha1 = '' ) {
106 $this->id = -1;
107 $this->title = false;
108 $this->name = false;
109 $this->group = 'deleted'; // needed for direct use of constructor
110 $this->key = '';
111 $this->size = 0;
112 $this->bits = 0;
113 $this->width = 0;
114 $this->height = 0;
115 $this->metadata = '';
116 $this->mime = "unknown/unknown";
117 $this->media_type = '';
118 $this->description = '';
119 $this->user = 0;
120 $this->user_text = '';
121 $this->timestamp = null;
122 $this->deleted = 0;
123 $this->dataLoaded = false;
124 $this->exists = false;
125 $this->sha1 = '';
126
127 if ( $title instanceof Title ) {
128 $this->title = File::normalizeTitle( $title, 'exception' );
129 $this->name = $title->getDBkey();
130 }
131
132 if ( $id ) {
133 $this->id = $id;
134 }
135
136 if ( $key ) {
137 $this->key = $key;
138 }
139
140 if ( $sha1 ) {
141 $this->sha1 = $sha1;
142 }
143
144 if ( !$id && !$key && !( $title instanceof Title ) && !$sha1 ) {
145 throw new MWException( "No specifications provided to ArchivedFile constructor." );
146 }
147 }
148
149 /**
150 * Loads a file object from the filearchive table
151 * @throws MWException
152 * @return bool|null True on success or null
153 */
154 public function load() {
155 if ( $this->dataLoaded ) {
156 return true;
157 }
158 $conds = [];
159
160 if ( $this->id > 0 ) {
161 $conds['fa_id'] = $this->id;
162 }
163 if ( $this->key ) {
164 $conds['fa_storage_group'] = $this->group;
165 $conds['fa_storage_key'] = $this->key;
166 }
167 if ( $this->title ) {
168 $conds['fa_name'] = $this->title->getDBkey();
169 }
170 if ( $this->sha1 ) {
171 $conds['fa_sha1'] = $this->sha1;
172 }
173
174 if ( !count( $conds ) ) {
175 throw new MWException( "No specific information for retrieving archived file" );
176 }
177
178 if ( !$this->title || $this->title->getNamespace() == NS_FILE ) {
179 $this->dataLoaded = true; // set it here, to have also true on miss
180 $dbr = wfGetDB( DB_REPLICA );
181 $fileQuery = self::getQueryInfo();
182 $row = $dbr->selectRow(
183 $fileQuery['tables'],
184 $fileQuery['fields'],
185 $conds,
186 __METHOD__,
187 [ 'ORDER BY' => 'fa_timestamp DESC' ],
188 $fileQuery['joins']
189 );
190 if ( !$row ) {
191 // this revision does not exist?
192 return null;
193 }
194
195 // initialize fields for filestore image object
196 $this->loadFromRow( $row );
197 } else {
198 throw new MWException( 'This title does not correspond to an image page.' );
199 }
200 $this->exists = true;
201
202 return true;
203 }
204
205 /**
206 * Loads a file object from the filearchive table
207 *
208 * @param stdClass $row
209 * @return ArchivedFile
210 */
211 public static function newFromRow( $row ) {
212 $file = new ArchivedFile( Title::makeTitle( NS_FILE, $row->fa_name ) );
213 $file->loadFromRow( $row );
214
215 return $file;
216 }
217
218 /**
219 * Fields in the filearchive table
220 * @deprecated since 1.31, use self::getQueryInfo() instead.
221 * @return array
222 */
223 static function selectFields() {
224 wfDeprecated( __METHOD__, '1.31' );
225 return [
226 'fa_id',
227 'fa_name',
228 'fa_archive_name',
229 'fa_storage_key',
230 'fa_storage_group',
231 'fa_size',
232 'fa_bits',
233 'fa_width',
234 'fa_height',
235 'fa_metadata',
236 'fa_media_type',
237 'fa_major_mime',
238 'fa_minor_mime',
239 'fa_user',
240 'fa_user_text',
241 'fa_timestamp',
242 'fa_deleted',
243 'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
244 'fa_sha1',
245 ] + CommentStore::newKey( 'fa_description' )->getFields();
246 }
247
248 /**
249 * Return the tables, fields, and join conditions to be selected to create
250 * a new archivedfile object.
251 * @since 1.31
252 * @return array With three keys:
253 * - tables: (string[]) to include in the `$table` to `IDatabase->select()`
254 * - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
255 * - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
256 */
257 public static function getQueryInfo() {
258 $commentQuery = CommentStore::newKey( 'fa_description' )->getJoin();
259 return [
260 'tables' => [ 'filearchive' ] + $commentQuery['tables'],
261 'fields' => [
262 'fa_id',
263 'fa_name',
264 'fa_archive_name',
265 'fa_storage_key',
266 'fa_storage_group',
267 'fa_size',
268 'fa_bits',
269 'fa_width',
270 'fa_height',
271 'fa_metadata',
272 'fa_media_type',
273 'fa_major_mime',
274 'fa_minor_mime',
275 'fa_user',
276 'fa_user_text',
277 'fa_timestamp',
278 'fa_deleted',
279 'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
280 'fa_sha1',
281 ] + $commentQuery['fields'],
282 'joins' => $commentQuery['joins'],
283 ];
284 }
285
286 /**
287 * Load ArchivedFile object fields from a DB row.
288 *
289 * @param stdClass $row Object database row
290 * @since 1.21
291 */
292 public function loadFromRow( $row ) {
293 $this->id = intval( $row->fa_id );
294 $this->name = $row->fa_name;
295 $this->archive_name = $row->fa_archive_name;
296 $this->group = $row->fa_storage_group;
297 $this->key = $row->fa_storage_key;
298 $this->size = $row->fa_size;
299 $this->bits = $row->fa_bits;
300 $this->width = $row->fa_width;
301 $this->height = $row->fa_height;
302 $this->metadata = $row->fa_metadata;
303 $this->mime = "$row->fa_major_mime/$row->fa_minor_mime";
304 $this->media_type = $row->fa_media_type;
305 $this->description = CommentStore::newKey( 'fa_description' )
306 // Legacy because $row may have come from self::selectFields()
307 ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row )->text;
308 $this->user = $row->fa_user;
309 $this->user_text = $row->fa_user_text;
310 $this->timestamp = $row->fa_timestamp;
311 $this->deleted = $row->fa_deleted;
312 if ( isset( $row->fa_sha1 ) ) {
313 $this->sha1 = $row->fa_sha1;
314 } else {
315 // old row, populate from key
316 $this->sha1 = LocalRepo::getHashFromKey( $this->key );
317 }
318 if ( !$this->title ) {
319 $this->title = Title::makeTitleSafe( NS_FILE, $row->fa_name );
320 }
321 }
322
323 /**
324 * Return the associated title object
325 *
326 * @return Title
327 */
328 public function getTitle() {
329 if ( !$this->title ) {
330 $this->load();
331 }
332 return $this->title;
333 }
334
335 /**
336 * Return the file name
337 *
338 * @return string
339 */
340 public function getName() {
341 if ( $this->name === false ) {
342 $this->load();
343 }
344
345 return $this->name;
346 }
347
348 /**
349 * @return int
350 */
351 public function getID() {
352 $this->load();
353
354 return $this->id;
355 }
356
357 /**
358 * @return bool
359 */
360 public function exists() {
361 $this->load();
362
363 return $this->exists;
364 }
365
366 /**
367 * Return the FileStore key
368 * @return string
369 */
370 public function getKey() {
371 $this->load();
372
373 return $this->key;
374 }
375
376 /**
377 * Return the FileStore key (overriding base File class)
378 * @return string
379 */
380 public function getStorageKey() {
381 return $this->getKey();
382 }
383
384 /**
385 * Return the FileStore storage group
386 * @return string
387 */
388 public function getGroup() {
389 return $this->group;
390 }
391
392 /**
393 * Return the width of the image
394 * @return int
395 */
396 public function getWidth() {
397 $this->load();
398
399 return $this->width;
400 }
401
402 /**
403 * Return the height of the image
404 * @return int
405 */
406 public function getHeight() {
407 $this->load();
408
409 return $this->height;
410 }
411
412 /**
413 * Get handler-specific metadata
414 * @return string
415 */
416 public function getMetadata() {
417 $this->load();
418
419 return $this->metadata;
420 }
421
422 /**
423 * Return the size of the image file, in bytes
424 * @return int
425 */
426 public function getSize() {
427 $this->load();
428
429 return $this->size;
430 }
431
432 /**
433 * Return the bits of the image file, in bytes
434 * @return int
435 */
436 public function getBits() {
437 $this->load();
438
439 return $this->bits;
440 }
441
442 /**
443 * Returns the MIME type of the file.
444 * @return string
445 */
446 public function getMimeType() {
447 $this->load();
448
449 return $this->mime;
450 }
451
452 /**
453 * Get a MediaHandler instance for this file
454 * @return MediaHandler
455 */
456 function getHandler() {
457 if ( !isset( $this->handler ) ) {
458 $this->handler = MediaHandler::getHandler( $this->getMimeType() );
459 }
460
461 return $this->handler;
462 }
463
464 /**
465 * Returns the number of pages of a multipage document, or false for
466 * documents which aren't multipage documents
467 * @return bool|int
468 */
469 function pageCount() {
470 if ( !isset( $this->pageCount ) ) {
471 // @FIXME: callers expect File objects
472 if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) {
473 $this->pageCount = $this->handler->pageCount( $this );
474 } else {
475 $this->pageCount = false;
476 }
477 }
478
479 return $this->pageCount;
480 }
481
482 /**
483 * Return the type of the media in the file.
484 * Use the value returned by this function with the MEDIATYPE_xxx constants.
485 * @return string
486 */
487 public function getMediaType() {
488 $this->load();
489
490 return $this->media_type;
491 }
492
493 /**
494 * Return upload timestamp.
495 *
496 * @return string
497 */
498 public function getTimestamp() {
499 $this->load();
500
501 return wfTimestamp( TS_MW, $this->timestamp );
502 }
503
504 /**
505 * Get the SHA-1 base 36 hash of the file
506 *
507 * @return string
508 * @since 1.21
509 */
510 function getSha1() {
511 $this->load();
512
513 return $this->sha1;
514 }
515
516 /**
517 * Returns ID or name of user who uploaded the file
518 *
519 * @note Prior to MediaWiki 1.23, this method always
520 * returned the user id, and was inconsistent with
521 * the rest of the file classes.
522 * @param string $type 'text' or 'id'
523 * @return int|string
524 * @throws MWException
525 */
526 public function getUser( $type = 'text' ) {
527 $this->load();
528
529 if ( $type == 'text' ) {
530 return $this->user_text;
531 } elseif ( $type == 'id' ) {
532 return (int)$this->user;
533 }
534
535 throw new MWException( "Unknown type '$type'." );
536 }
537
538 /**
539 * Return upload description.
540 *
541 * @return string|int
542 */
543 public function getDescription() {
544 $this->load();
545 if ( $this->isDeleted( File::DELETED_COMMENT ) ) {
546 return 0;
547 } else {
548 return $this->description;
549 }
550 }
551
552 /**
553 * Return the user ID of the uploader.
554 *
555 * @return int
556 */
557 public function getRawUser() {
558 $this->load();
559
560 return $this->user;
561 }
562
563 /**
564 * Return the user name of the uploader.
565 *
566 * @return string
567 */
568 public function getRawUserText() {
569 $this->load();
570
571 return $this->user_text;
572 }
573
574 /**
575 * Return upload description.
576 *
577 * @return string
578 */
579 public function getRawDescription() {
580 $this->load();
581
582 return $this->description;
583 }
584
585 /**
586 * Returns the deletion bitfield
587 * @return int
588 */
589 public function getVisibility() {
590 $this->load();
591
592 return $this->deleted;
593 }
594
595 /**
596 * for file or revision rows
597 *
598 * @param int $field One of DELETED_* bitfield constants
599 * @return bool
600 */
601 public function isDeleted( $field ) {
602 $this->load();
603
604 return ( $this->deleted & $field ) == $field;
605 }
606
607 /**
608 * Determine if the current user is allowed to view a particular
609 * field of this FileStore image file, if it's marked as deleted.
610 * @param int $field
611 * @param null|User $user User object to check, or null to use $wgUser
612 * @return bool
613 */
614 public function userCan( $field, User $user = null ) {
615 $this->load();
616
617 $title = $this->getTitle();
618 return Revision::userCanBitfield( $this->deleted, $field, $user, $title ?: null );
619 }
620 }