MediaWiki master
OldLocalFile.php
Go to the documentation of this file.
1<?php
22
23use InvalidArgumentException;
24use LogicException;
33use MWFileProps;
34use RuntimeException;
35use stdClass;
39
46class OldLocalFile extends LocalFile {
48 protected $requestedTime;
49
51 protected $archive_name;
52
53 public const CACHE_VERSION = 1;
54
62 public static function newFromTitle( $title, $repo, $time = null ) {
63 # The null default value is only here to avoid an E_STRICT
64 if ( $time === null ) {
65 throw new InvalidArgumentException( __METHOD__ . ' got null for $time parameter' );
66 }
67
68 return new static( $title, $repo, $time, null );
69 }
70
79 public static function newFromArchiveName( $title, $repo, $archiveName ) {
80 return new static( $title, $repo, null, $archiveName );
81 }
82
90 public static function newFromRow( $row, $repo ) {
91 $title = Title::makeTitle( NS_FILE, $row->oi_name );
92 $file = new static( $title, $repo, null, $row->oi_archive_name );
93 $file->loadFromRow( $row, 'oi_' );
94
95 return $file;
96 }
97
110 public static function newFromKey( $sha1, $repo, $timestamp = false ) {
111 $dbr = $repo->getReplicaDB();
112 $queryBuilder = FileSelectQueryBuilder::newForOldFile( $dbr );
113
114 $queryBuilder->where( [ 'oi_sha1' => $sha1 ] );
115 if ( $timestamp ) {
116 $queryBuilder->andWhere( [ 'oi_timestamp' => $dbr->timestamp( $timestamp ) ] );
117 }
118
119 $row = $queryBuilder->caller( __METHOD__ )->fetchRow();
120 if ( $row ) {
121 return static::newFromRow( $row, $repo );
122 } else {
123 return false;
124 }
125 }
126
147 public static function getQueryInfo( array $options = [] ) {
148 wfDeprecated( __METHOD__, '1.41' );
149 $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
150 $queryInfo = FileSelectQueryBuilder::newForOldFile( $dbr, $options )->getQueryInfo();
151 return [
152 'tables' => $queryInfo['tables'],
153 'fields' => $queryInfo['fields'],
154 'joins' => $queryInfo['join_conds'],
155 ];
156 }
157
166 public function __construct( $title, $repo, $time, $archiveName ) {
167 parent::__construct( $title, $repo );
168 $this->requestedTime = $time;
169 $this->archive_name = $archiveName;
170 if ( $time === null && $archiveName === null ) {
171 throw new LogicException( __METHOD__ . ': must specify at least one of $time or $archiveName' );
172 }
173 }
174
175 public function loadFromRow( $row, $prefix = 'img_' ) {
176 $this->archive_name = $row->{"{$prefix}archive_name"};
177 $this->deleted = $row->{"{$prefix}deleted"};
178 $row = clone $row;
179 unset( $row->{"{$prefix}archive_name"} );
180 unset( $row->{"{$prefix}deleted"} );
181 parent::loadFromRow( $row, $prefix );
182 }
183
188 protected function getCacheKey() {
189 return false;
190 }
191
196 public function getArchiveName() {
197 if ( $this->archive_name === null ) {
198 $this->load();
199 }
200
201 return $this->archive_name;
202 }
203
207 public function isOld() {
208 return true;
209 }
210
214 public function isVisible() {
215 return $this->exists() && !$this->isDeleted( File::DELETED_FILE );
216 }
217
222 protected function loadFromDB( $flags = 0 ) {
223 $this->dataLoaded = true;
224
225 $dbr = ( $flags & IDBAccessObject::READ_LATEST )
226 ? $this->repo->getPrimaryDB()
227 : $this->repo->getReplicaDB();
228 $queryBuilder = $this->buildQueryBuilderForLoad( $dbr, [] );
229 $row = $queryBuilder->caller( __METHOD__ )->fetchRow();
230 if ( $row ) {
231 $this->loadFromRow( $row, 'oi_' );
232 } else {
233 $this->fileExists = false;
234 }
235 }
236
241 protected function loadExtraFromDB() {
242 $this->extraDataLoaded = true;
243 $dbr = $this->repo->getReplicaDB();
244 $queryBuilder = $this->buildQueryBuilderForLoad( $dbr );
245
246 // In theory the file could have just been renamed/deleted...oh well
247 $row = $queryBuilder->caller( __METHOD__ )->fetchRow();
248
249 if ( !$row ) { // fallback to primary DB
250 $dbr = $this->repo->getPrimaryDB();
251 $queryBuilder = $this->buildQueryBuilderForLoad( $dbr );
252 $row = $queryBuilder->caller( __METHOD__ )->fetchRow();
253 }
254
255 if ( $row ) {
256 foreach ( $this->unprefixRow( $row, 'oi_' ) as $name => $value ) {
257 $this->$name = $value;
258 }
259 } else {
260 throw new RuntimeException( "Could not find data for image '{$this->archive_name}'." );
261 }
262 }
263
264 private function buildQueryBuilderForLoad(
265 IReadableDatabase $dbr, array $options = [ 'omit-nonlazy' ]
267 $queryBuilder = FileSelectQueryBuilder::newForOldFile( $dbr, $options );
268 $queryBuilder->where( [ 'oi_name' => $this->getName() ] )
269 ->orderBy( 'oi_timestamp', SelectQueryBuilder::SORT_DESC );
270 if ( $this->requestedTime === null ) {
271 $queryBuilder->andWhere( [ 'oi_archive_name' => $this->archive_name ] );
272 } else {
273 $queryBuilder->andWhere( [ 'oi_timestamp' => $dbr->timestamp( $this->requestedTime ) ] );
274 }
275 return $queryBuilder;
276 }
277
282 protected function getCacheFields( $prefix = 'img_' ) {
283 $fields = parent::getCacheFields( $prefix );
284 $fields[] = $prefix . 'archive_name';
285 $fields[] = $prefix . 'deleted';
286
287 return $fields;
288 }
289
294 public function getRel() {
295 return $this->getArchiveRel( $this->getArchiveName() );
296 }
297
302 public function getUrlRel() {
303 return $this->getArchiveRel( rawurlencode( $this->getArchiveName() ) );
304 }
305
309 public function upgradeRow() {
310 $this->loadFromFile();
311
312 # Don't destroy file info of missing files
313 if ( !$this->fileExists ) {
314 wfDebug( __METHOD__ . ": file does not exist, aborting" );
315
316 return;
317 }
318
319 $dbw = $this->repo->getPrimaryDB();
320 [ $major, $minor ] = self::splitMime( $this->mime );
321 $metadata = $this->getMetadataForDb( $dbw );
322
323 wfDebug( __METHOD__ . ': upgrading ' . $this->archive_name . " to the current schema" );
324 $dbw->newUpdateQueryBuilder()
325 ->update( 'oldimage' )
326 ->set( [
327 'oi_size' => $this->size,
328 'oi_width' => $this->width,
329 'oi_height' => $this->height,
330 'oi_bits' => $this->bits,
331 'oi_media_type' => $this->media_type,
332 'oi_major_mime' => $major,
333 'oi_minor_mime' => $minor,
334 'oi_metadata' => $metadata,
335 'oi_sha1' => $this->sha1,
336 ] )
337 ->where( [
338 'oi_name' => $this->getName(),
339 'oi_archive_name' => $this->archive_name,
340 ] )
341 ->caller( __METHOD__ )->execute();
342
343 $migrationStage = MediaWikiServices::getInstance()->getMainConfig()->get(
344 MainConfigNames::FileSchemaMigrationStage
345 );
346 if ( $migrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
347 $dbw->newUpdateQueryBuilder()
348 ->update( 'filerevision' )
349 ->set( [
350 'fr_size' => $this->size,
351 'fr_width' => $this->width,
352 'fr_height' => $this->height,
353 'fr_bits' => $this->bits,
354 'fr_metadata' => $metadata,
355 'fr_sha1' => $this->sha1,
356 ] )
357 ->where( [
358 'fr_file' => $this->acquireFileIdFromName(),
359 'fr_archive_name' => $this->archive_name,
360 ] )
361 ->caller( __METHOD__ )->execute();
362 }
363 }
364
365 protected function reserializeMetadata() {
366 // TODO: implement this and make it possible to hit it from refreshImageMetadata.php
367 // It can be hit from action=purge but that's not very useful if the
368 // goal is to reserialize the whole oldimage table.
369 }
370
376 public function isDeleted( $field ) {
377 $this->load();
378
379 return ( $this->deleted & $field ) == $field;
380 }
381
386 public function getVisibility() {
387 $this->load();
388
389 return (int)$this->deleted;
390 }
391
400 public function userCan( $field, Authority $performer ) {
401 $this->load();
402
403 return RevisionRecord::userCanBitfield(
404 $this->deleted,
405 $field,
406 $performer
407 );
408 }
409
419 public function uploadOld( $srcPath, $timestamp, $comment, UserIdentity $user ) {
420 $archiveName = $this->getArchiveName();
421 $dstRel = $this->getArchiveRel( $archiveName );
422 $status = $this->publishTo( $srcPath, $dstRel );
423
424 if ( $status->isGood() &&
425 !$this->recordOldUpload( $srcPath, $archiveName, $timestamp, $comment, $user )
426 ) {
427 $status->fatal( 'filenotfound', $srcPath );
428 }
429
430 return $status;
431 }
432
444 protected function recordOldUpload( $srcPath, $archiveName, $timestamp, $comment, $user ) {
445 $dbw = $this->repo->getPrimaryDB();
446
447 $services = MediaWikiServices::getInstance();
448 $mwProps = new MWFileProps( $services->getMimeAnalyzer() );
449 $props = $mwProps->getPropsFromPath( $srcPath, true );
450 if ( !$props['fileExists'] ) {
451 return false;
452 }
453 $this->setProps( $props );
454
455 $dbw->startAtomic( __METHOD__ );
456
457 $commentFields = $services->getCommentStore()
458 ->insert( $dbw, 'oi_description', $comment );
459 $actorId = $services->getActorNormalization()
460 ->acquireActorId( $user, $dbw );
461 $dbw->newInsertQueryBuilder()
462 ->insertInto( 'oldimage' )
463 ->row( [
464 'oi_name' => $this->getName(),
465 'oi_archive_name' => $archiveName,
466 'oi_size' => $props['size'],
467 'oi_width' => intval( $props['width'] ),
468 'oi_height' => intval( $props['height'] ),
469 'oi_bits' => $props['bits'],
470 'oi_actor' => $actorId,
471 'oi_timestamp' => $dbw->timestamp( $timestamp ),
472 'oi_metadata' => $this->getMetadataForDb( $dbw ),
473 'oi_media_type' => $props['media_type'],
474 'oi_major_mime' => $props['major_mime'],
475 'oi_minor_mime' => $props['minor_mime'],
476 'oi_sha1' => $props['sha1'],
477 ] + $commentFields )
478 ->caller( __METHOD__ )->execute();
479
480 $migrationStage = MediaWikiServices::getInstance()->getMainConfig()->get(
481 MainConfigNames::FileSchemaMigrationStage
482 );
483 if ( $migrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
484 $commentFields = $services->getCommentStore()
485 ->insert( $dbw, 'fr_description', $comment );
486 $dbw->newInsertQueryBuilder()
487 ->insertInto( 'filerevision' )
488 ->ignore()
489 ->row( [
490 'fr_file' => $this->acquireFileIdFromName(),
491 'fr_size' => $this->size,
492 'fr_width' => intval( $this->width ),
493 'fr_height' => intval( $this->height ),
494 'fr_bits' => $this->bits,
495 'fr_actor' => $actorId,
496 'fr_deleted' => 0,
497 'fr_timestamp' => $dbw->timestamp( $timestamp ),
498 'fr_metadata' => $this->getMetadataForDb( $dbw ),
499 'fr_sha1' => $this->sha1
500 ] + $commentFields )
501 ->caller( __METHOD__ )->execute();
502 }
503
504 $dbw->endAtomic( __METHOD__ );
505
506 return true;
507 }
508
516 public function exists() {
517 $archiveName = $this->getArchiveName();
518 if ( $archiveName === '' || !is_string( $archiveName ) ) {
519 return false;
520 }
521 return parent::exists();
522 }
523}
524
526class_alias( OldLocalFile::class, 'OldLocalFile' );
const NS_FILE
Definition Defines.php:71
const SCHEMA_COMPAT_WRITE_NEW
Definition Defines.php:307
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
MimeMagic helper wrapper.
static newForOldFile(IReadableDatabase $db, array $options=[])
FileRepo LocalRepo ForeignAPIRepo false $repo
Some member variables can be lazy-initialised using __get().
Definition File.php:140
Title string false $title
Definition File.php:143
Local file in the wiki's own database.
Definition LocalFile.php:93
string $sha1
SHA-1 base 36 content hash.
Old file in the oldimage table.
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new oldlocalfile object.
getCacheFields( $prefix='img_')
Returns the list of object properties that are included as-is in the cache.to override string[] 1....
loadFromRow( $row, $prefix='img_')
Load file metadata from a DB result row.
static newFromKey( $sha1, $repo, $timestamp=false)
Create a OldLocalFile from a SHA-1 key Do not call this except from inside a repo class.
static newFromArchiveName( $title, $repo, $archiveName)
static newFromTitle( $title, $repo, $time=null)
uploadOld( $srcPath, $timestamp, $comment, UserIdentity $user)
Upload a file directly into archive.
loadExtraFromDB()
Load lazy file metadata from the DB.
string null $archive_name
Archive name.
exists()
If archive name is an empty string, then file does not "exist".
userCan( $field, Authority $performer)
Determine if the current user is allowed to view a particular field of this image file,...
__construct( $title, $repo, $time, $archiveName)
reserializeMetadata()
Write the metadata back to the database with the current serialization format.
recordOldUpload( $srcPath, $archiveName, $timestamp, $comment, $user)
Record a file upload in the oldimage table, without adding log entries.
getVisibility()
Returns bitfield value.
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:57
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Page revision base class.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Represents a title within MediaWiki.
Definition Title.php:78
Build SELECT queries with a fluent interface.
where( $conds)
Add conditions to the query.
This interface represents the authority associated with the current execution context,...
Definition Authority.php:37
Interface for objects representing user identity.
Interface for database access objects.
A database connection without write operations.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...