Merge "Move up devunt's name to Developers"
[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 string 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 $row = $dbr->selectRow(
182 'filearchive',
183 self::selectFields(),
184 $conds,
185 __METHOD__,
186 [ 'ORDER BY' => 'fa_timestamp DESC' ]
187 );
188 if ( !$row ) {
189 // this revision does not exist?
190 return null;
191 }
192
193 // initialize fields for filestore image object
194 $this->loadFromRow( $row );
195 } else {
196 throw new MWException( 'This title does not correspond to an image page.' );
197 }
198 $this->exists = true;
199
200 return true;
201 }
202
203 /**
204 * Loads a file object from the filearchive table
205 *
206 * @param stdClass $row
207 * @return ArchivedFile
208 */
209 public static function newFromRow( $row ) {
210 $file = new ArchivedFile( Title::makeTitle( NS_FILE, $row->fa_name ) );
211 $file->loadFromRow( $row );
212
213 return $file;
214 }
215
216 /**
217 * Fields in the filearchive table
218 * @return array
219 */
220 static function selectFields() {
221 return [
222 'fa_id',
223 'fa_name',
224 'fa_archive_name',
225 'fa_storage_key',
226 'fa_storage_group',
227 'fa_size',
228 'fa_bits',
229 'fa_width',
230 'fa_height',
231 'fa_metadata',
232 'fa_media_type',
233 'fa_major_mime',
234 'fa_minor_mime',
235 'fa_description',
236 'fa_user',
237 'fa_user_text',
238 'fa_timestamp',
239 'fa_deleted',
240 'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
241 'fa_sha1',
242 ];
243 }
244
245 /**
246 * Load ArchivedFile object fields from a DB row.
247 *
248 * @param stdClass $row Object database row
249 * @since 1.21
250 */
251 public function loadFromRow( $row ) {
252 $this->id = intval( $row->fa_id );
253 $this->name = $row->fa_name;
254 $this->archive_name = $row->fa_archive_name;
255 $this->group = $row->fa_storage_group;
256 $this->key = $row->fa_storage_key;
257 $this->size = $row->fa_size;
258 $this->bits = $row->fa_bits;
259 $this->width = $row->fa_width;
260 $this->height = $row->fa_height;
261 $this->metadata = $row->fa_metadata;
262 $this->mime = "$row->fa_major_mime/$row->fa_minor_mime";
263 $this->media_type = $row->fa_media_type;
264 $this->description = $row->fa_description;
265 $this->user = $row->fa_user;
266 $this->user_text = $row->fa_user_text;
267 $this->timestamp = $row->fa_timestamp;
268 $this->deleted = $row->fa_deleted;
269 if ( isset( $row->fa_sha1 ) ) {
270 $this->sha1 = $row->fa_sha1;
271 } else {
272 // old row, populate from key
273 $this->sha1 = LocalRepo::getHashFromKey( $this->key );
274 }
275 if ( !$this->title ) {
276 $this->title = Title::makeTitleSafe( NS_FILE, $row->fa_name );
277 }
278 }
279
280 /**
281 * Return the associated title object
282 *
283 * @return Title
284 */
285 public function getTitle() {
286 if ( !$this->title ) {
287 $this->load();
288 }
289 return $this->title;
290 }
291
292 /**
293 * Return the file name
294 *
295 * @return string
296 */
297 public function getName() {
298 if ( $this->name === false ) {
299 $this->load();
300 }
301
302 return $this->name;
303 }
304
305 /**
306 * @return int
307 */
308 public function getID() {
309 $this->load();
310
311 return $this->id;
312 }
313
314 /**
315 * @return bool
316 */
317 public function exists() {
318 $this->load();
319
320 return $this->exists;
321 }
322
323 /**
324 * Return the FileStore key
325 * @return string
326 */
327 public function getKey() {
328 $this->load();
329
330 return $this->key;
331 }
332
333 /**
334 * Return the FileStore key (overriding base File class)
335 * @return string
336 */
337 public function getStorageKey() {
338 return $this->getKey();
339 }
340
341 /**
342 * Return the FileStore storage group
343 * @return string
344 */
345 public function getGroup() {
346 return $this->group;
347 }
348
349 /**
350 * Return the width of the image
351 * @return int
352 */
353 public function getWidth() {
354 $this->load();
355
356 return $this->width;
357 }
358
359 /**
360 * Return the height of the image
361 * @return int
362 */
363 public function getHeight() {
364 $this->load();
365
366 return $this->height;
367 }
368
369 /**
370 * Get handler-specific metadata
371 * @return string
372 */
373 public function getMetadata() {
374 $this->load();
375
376 return $this->metadata;
377 }
378
379 /**
380 * Return the size of the image file, in bytes
381 * @return int
382 */
383 public function getSize() {
384 $this->load();
385
386 return $this->size;
387 }
388
389 /**
390 * Return the bits of the image file, in bytes
391 * @return int
392 */
393 public function getBits() {
394 $this->load();
395
396 return $this->bits;
397 }
398
399 /**
400 * Returns the MIME type of the file.
401 * @return string
402 */
403 public function getMimeType() {
404 $this->load();
405
406 return $this->mime;
407 }
408
409 /**
410 * Get a MediaHandler instance for this file
411 * @return MediaHandler
412 */
413 function getHandler() {
414 if ( !isset( $this->handler ) ) {
415 $this->handler = MediaHandler::getHandler( $this->getMimeType() );
416 }
417
418 return $this->handler;
419 }
420
421 /**
422 * Returns the number of pages of a multipage document, or false for
423 * documents which aren't multipage documents
424 * @return bool|int
425 */
426 function pageCount() {
427 if ( !isset( $this->pageCount ) ) {
428 // @FIXME: callers expect File objects
429 if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) {
430 $this->pageCount = $this->handler->pageCount( $this );
431 } else {
432 $this->pageCount = false;
433 }
434 }
435
436 return $this->pageCount;
437 }
438
439 /**
440 * Return the type of the media in the file.
441 * Use the value returned by this function with the MEDIATYPE_xxx constants.
442 * @return string
443 */
444 public function getMediaType() {
445 $this->load();
446
447 return $this->media_type;
448 }
449
450 /**
451 * Return upload timestamp.
452 *
453 * @return string
454 */
455 public function getTimestamp() {
456 $this->load();
457
458 return wfTimestamp( TS_MW, $this->timestamp );
459 }
460
461 /**
462 * Get the SHA-1 base 36 hash of the file
463 *
464 * @return string
465 * @since 1.21
466 */
467 function getSha1() {
468 $this->load();
469
470 return $this->sha1;
471 }
472
473 /**
474 * Returns ID or name of user who uploaded the file
475 *
476 * @note Prior to MediaWiki 1.23, this method always
477 * returned the user id, and was inconsistent with
478 * the rest of the file classes.
479 * @param string $type 'text' or 'id'
480 * @return int|string
481 * @throws MWException
482 */
483 public function getUser( $type = 'text' ) {
484 $this->load();
485
486 if ( $type == 'text' ) {
487 return $this->user_text;
488 } elseif ( $type == 'id' ) {
489 return (int)$this->user;
490 }
491
492 throw new MWException( "Unknown type '$type'." );
493 }
494
495 /**
496 * Return the user name of the uploader.
497 *
498 * @deprecated since 1.23 Use getUser( 'text' ) instead.
499 * @return string
500 */
501 public function getUserText() {
502 wfDeprecated( __METHOD__, '1.23' );
503 $this->load();
504 if ( $this->isDeleted( File::DELETED_USER ) ) {
505 return 0;
506 } else {
507 return $this->user_text;
508 }
509 }
510
511 /**
512 * Return upload description.
513 *
514 * @return string
515 */
516 public function getDescription() {
517 $this->load();
518 if ( $this->isDeleted( File::DELETED_COMMENT ) ) {
519 return 0;
520 } else {
521 return $this->description;
522 }
523 }
524
525 /**
526 * Return the user ID of the uploader.
527 *
528 * @return int
529 */
530 public function getRawUser() {
531 $this->load();
532
533 return $this->user;
534 }
535
536 /**
537 * Return the user name of the uploader.
538 *
539 * @return string
540 */
541 public function getRawUserText() {
542 $this->load();
543
544 return $this->user_text;
545 }
546
547 /**
548 * Return upload description.
549 *
550 * @return string
551 */
552 public function getRawDescription() {
553 $this->load();
554
555 return $this->description;
556 }
557
558 /**
559 * Returns the deletion bitfield
560 * @return int
561 */
562 public function getVisibility() {
563 $this->load();
564
565 return $this->deleted;
566 }
567
568 /**
569 * for file or revision rows
570 *
571 * @param int $field One of DELETED_* bitfield constants
572 * @return bool
573 */
574 public function isDeleted( $field ) {
575 $this->load();
576
577 return ( $this->deleted & $field ) == $field;
578 }
579
580 /**
581 * Determine if the current user is allowed to view a particular
582 * field of this FileStore image file, if it's marked as deleted.
583 * @param int $field
584 * @param null|User $user User object to check, or null to use $wgUser
585 * @return bool
586 */
587 public function userCan( $field, User $user = null ) {
588 $this->load();
589
590 $title = $this->getTitle();
591 return Revision::userCanBitfield( $this->deleted, $field, $user, $title ?: null );
592 }
593 }