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