3 * Deleted file in the 'filearchive' table.
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.
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.
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
21 * @ingroup FileAbstraction
24 use MediaWiki\MediaWikiServices
;
27 * Class representing a row of the 'filearchive' table
29 * @ingroup FileAbstraction
32 /** @var int Filearchive row ID */
35 /** @var string File name */
38 /** @var string FileStore storage group */
41 /** @var string FileStore SHA-1 key */
44 /** @var int File size in bytes */
47 /** @var int Size in bytes */
53 /** @var int Height */
56 /** @var string Metadata string */
59 /** @var string MIME type */
62 /** @var string Media type */
65 /** @var string Upload description */
68 /** @var User|null Uploader */
71 /** @var string Time of upload */
74 /** @var bool Whether or not all this has been loaded from the database (loadFromXxx) */
77 /** @var int Bitfield akin to rev_deleted */
80 /** @var string SHA-1 hash of file content */
83 /** @var int|false Number of pages of a multipage document, or false for
84 * documents which aren't multipage documents
88 /** @var string Original base filename */
89 private $archive_name;
91 /** @var MediaHandler */
95 protected $title; # image title
102 * @param string $sha1
104 function __construct( $title, $id = 0, $key = '', $sha1 = '' ) {
106 $this->title
= false;
108 $this->group
= 'deleted'; // needed for direct use of constructor
114 $this->metadata
= '';
115 $this->mime
= "unknown/unknown";
116 $this->media_type
= '';
117 $this->description
= '';
119 $this->timestamp
= null;
121 $this->dataLoaded
= false;
122 $this->exists
= false;
125 if ( $title instanceof Title
) {
126 $this->title
= File
::normalizeTitle( $title, 'exception' );
127 $this->name
= $title->getDBkey();
142 if ( !$id && !$key && !( $title instanceof Title
) && !$sha1 ) {
143 throw new MWException( "No specifications provided to ArchivedFile constructor." );
148 * Loads a file object from the filearchive table
149 * @throws MWException
150 * @return bool|null True on success or null
152 public function load() {
153 if ( $this->dataLoaded
) {
158 if ( $this->id
> 0 ) {
159 $conds['fa_id'] = $this->id
;
162 $conds['fa_storage_group'] = $this->group
;
163 $conds['fa_storage_key'] = $this->key
;
165 if ( $this->title
) {
166 $conds['fa_name'] = $this->title
->getDBkey();
169 $conds['fa_sha1'] = $this->sha1
;
172 if ( !count( $conds ) ) {
173 throw new MWException( "No specific information for retrieving archived file" );
176 if ( !$this->title ||
$this->title
->getNamespace() == NS_FILE
) {
177 $this->dataLoaded
= true; // set it here, to have also true on miss
178 $dbr = wfGetDB( DB_REPLICA
);
179 $fileQuery = self
::getQueryInfo();
180 $row = $dbr->selectRow(
181 $fileQuery['tables'],
182 $fileQuery['fields'],
185 [ 'ORDER BY' => 'fa_timestamp DESC' ],
189 // this revision does not exist?
193 // initialize fields for filestore image object
194 $this->loadFromRow( $row );
196 throw new MWException( 'This title does not correspond to an image page.' );
198 $this->exists
= true;
204 * Loads a file object from the filearchive table
206 * @param stdClass $row
207 * @return ArchivedFile
209 public static function newFromRow( $row ) {
210 $file = new ArchivedFile( Title
::makeTitle( NS_FILE
, $row->fa_name
) );
211 $file->loadFromRow( $row );
217 * Fields in the filearchive table
218 * @deprecated since 1.31, use self::getQueryInfo() instead.
221 static function selectFields() {
222 global $wgActorTableSchemaMigrationStage;
224 if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH
) {
225 // If code is using this instead of self::getQueryInfo(), there's a
226 // decent chance it's going to try to directly access
227 // $row->fa_user or $row->fa_user_text and we can't give it
228 // useful values here once those aren't being written anymore.
229 throw new BadMethodCallException(
230 'Cannot use ' . __METHOD__
. ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
234 wfDeprecated( __METHOD__
, '1.31' );
251 'fa_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ?
'fa_actor' : null,
254 'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
256 ] + MediaWikiServices
::getInstance()->getCommentStore()->getFields( 'fa_description' );
260 * Return the tables, fields, and join conditions to be selected to create
261 * a new archivedfile object.
263 * @return array[] With three keys:
264 * - tables: (string[]) to include in the `$table` to `IDatabase->select()`
265 * - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
266 * - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
268 public static function getQueryInfo() {
269 $commentQuery = MediaWikiServices
::getInstance()->getCommentStore()->getJoin( 'fa_description' );
270 $actorQuery = ActorMigration
::newMigration()->getJoin( 'fa_user' );
272 'tables' => [ 'filearchive' ] +
$commentQuery['tables'] +
$actorQuery['tables'],
289 'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
291 ] +
$commentQuery['fields'] +
$actorQuery['fields'],
292 'joins' => $commentQuery['joins'] +
$actorQuery['joins'],
297 * Load ArchivedFile object fields from a DB row.
299 * @param stdClass $row Object database row
302 public function loadFromRow( $row ) {
303 $this->id
= intval( $row->fa_id
);
304 $this->name
= $row->fa_name
;
305 $this->archive_name
= $row->fa_archive_name
;
306 $this->group
= $row->fa_storage_group
;
307 $this->key
= $row->fa_storage_key
;
308 $this->size
= $row->fa_size
;
309 $this->bits
= $row->fa_bits
;
310 $this->width
= $row->fa_width
;
311 $this->height
= $row->fa_height
;
312 $this->metadata
= $row->fa_metadata
;
313 $this->mime
= "$row->fa_major_mime/$row->fa_minor_mime";
314 $this->media_type
= $row->fa_media_type
;
315 $this->description
= MediaWikiServices
::getInstance()->getCommentStore()
316 // Legacy because $row may have come from self::selectFields()
317 ->getCommentLegacy( wfGetDB( DB_REPLICA
), 'fa_description', $row )->text
;
318 $this->user
= User
::newFromAnyId( $row->fa_user
, $row->fa_user_text
, $row->fa_actor
);
319 $this->timestamp
= $row->fa_timestamp
;
320 $this->deleted
= $row->fa_deleted
;
321 if ( isset( $row->fa_sha1
) ) {
322 $this->sha1
= $row->fa_sha1
;
324 // old row, populate from key
325 $this->sha1
= LocalRepo
::getHashFromKey( $this->key
);
327 if ( !$this->title
) {
328 $this->title
= Title
::makeTitleSafe( NS_FILE
, $row->fa_name
);
333 * Return the associated title object
337 public function getTitle() {
338 if ( !$this->title
) {
345 * Return the file name
349 public function getName() {
350 if ( $this->name
=== false ) {
360 public function getID() {
369 public function exists() {
372 return $this->exists
;
376 * Return the FileStore key
379 public function getKey() {
386 * Return the FileStore key (overriding base File class)
389 public function getStorageKey() {
390 return $this->getKey();
394 * Return the FileStore storage group
397 public function getGroup() {
402 * Return the width of the image
405 public function getWidth() {
412 * Return the height of the image
415 public function getHeight() {
418 return $this->height
;
422 * Get handler-specific metadata
425 public function getMetadata() {
428 return $this->metadata
;
432 * Return the size of the image file, in bytes
435 public function getSize() {
442 * Return the bits of the image file, in bytes
445 public function getBits() {
452 * Returns the MIME type of the file.
455 public function getMimeType() {
462 * Get a MediaHandler instance for this file
463 * @return MediaHandler
465 function getHandler() {
466 if ( !isset( $this->handler
) ) {
467 $this->handler
= MediaHandler
::getHandler( $this->getMimeType() );
470 return $this->handler
;
474 * Returns the number of pages of a multipage document, or false for
475 * documents which aren't multipage documents
478 function pageCount() {
479 if ( !isset( $this->pageCount
) ) {
480 // @FIXME: callers expect File objects
481 if ( $this->getHandler() && $this->handler
->isMultiPage( $this ) ) {
482 $this->pageCount
= $this->handler
->pageCount( $this );
484 $this->pageCount
= false;
488 return $this->pageCount
;
492 * Return the type of the media in the file.
493 * Use the value returned by this function with the MEDIATYPE_xxx constants.
496 public function getMediaType() {
499 return $this->media_type
;
503 * Return upload timestamp.
507 public function getTimestamp() {
510 return wfTimestamp( TS_MW
, $this->timestamp
);
514 * Get the SHA-1 base 36 hash of the file
526 * Returns ID or name of user who uploaded the file
528 * @note Prior to MediaWiki 1.23, this method always
529 * returned the user id, and was inconsistent with
530 * the rest of the file classes.
531 * @param string $type 'text', 'id', or 'object'
532 * @return int|string|User|null
533 * @throws MWException
534 * @since 1.31 added 'object'
536 public function getUser( $type = 'text' ) {
539 if ( $type === 'object' ) {
541 } elseif ( $type === 'text' ) {
542 return $this->user ?
$this->user
->getName() : '';
543 } elseif ( $type === 'id' ) {
544 return $this->user ?
$this->user
->getId() : 0;
547 throw new MWException( "Unknown type '$type'." );
551 * Return upload description.
555 public function getDescription() {
557 if ( $this->isDeleted( File
::DELETED_COMMENT
) ) {
560 return $this->description
;
565 * Return the user ID of the uploader.
569 public function getRawUser() {
570 return $this->getUser( 'id' );
574 * Return the user name of the uploader.
578 public function getRawUserText() {
579 return $this->getUser( 'text' );
583 * Return upload description.
587 public function getRawDescription() {
590 return $this->description
;
594 * Returns the deletion bitfield
597 public function getVisibility() {
600 return $this->deleted
;
604 * for file or revision rows
606 * @param int $field One of DELETED_* bitfield constants
609 public function isDeleted( $field ) {
612 return ( $this->deleted
& $field ) == $field;
616 * Determine if the current user is allowed to view a particular
617 * field of this FileStore image file, if it's marked as deleted.
619 * @param null|User $user User object to check, or null to use $wgUser
622 public function userCan( $field, User
$user = null ) {
625 $title = $this->getTitle();
626 return Revision
::userCanBitfield( $this->deleted
, $field, $user, $title ?
: null );