Go to the documentation of this file.
24 use Wikimedia\AtEase\AtEase;
172 $file->loadFromRow( $row );
189 $conds = [
'img_sha1' =>
$sha1 ];
194 $fileQuery = static::getQueryInfo();
195 $row =
$dbr->selectRow(
196 $fileQuery[
'tables'], $fileQuery[
'fields'], $conds, __METHOD__, [], $fileQuery[
'joins']
199 return static::newFromRow( $row,
$repo );
217 $commentQuery = MediaWikiServices::getInstance()->getCommentStore()->getJoin(
'img_description' );
220 'tables' => [
'image' ] + $commentQuery[
'tables'] + $actorQuery[
'tables'],
233 ] + $commentQuery[
'fields'] + $actorQuery[
'fields'],
234 'joins' => $commentQuery[
'joins'] + $actorQuery[
'joins'],
237 if ( in_array(
'omit-nonlazy', $options,
true ) ) {
241 if ( !in_array(
'omit-lazy', $options,
true ) ) {
243 $ret[
'fields'][] =
'img_metadata';
257 $this->metadata =
'';
258 $this->historyLine = 0;
259 $this->historyRes =
null;
260 $this->dataLoaded =
false;
261 $this->extraDataLoaded =
false;
273 return $this->repo->getSharedCacheKey(
'file', sha1( $this->
getName() ) );
289 $this->dataLoaded =
false;
290 $this->extraDataLoaded =
false;
299 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
300 $cachedValues =
$cache->getWithSetCallback(
303 function ( $oldValue, &$ttl, array &$setOpts ) use (
$cache ) {
304 $setOpts += Database::getCacheSetOptions( $this->repo->getReplicaDB() );
311 if ( $this->fileExists ) {
312 foreach ( $fields as $field ) {
313 $cacheVal[$field] = $this->$field;
316 $cacheVal[
'user'] = $this->user ? $this->user->getId() : 0;
317 $cacheVal[
'user_text'] = $this->user ? $this->user->getName() :
'';
318 $cacheVal[
'actor'] = $this->user ? $this->user->getActorId() :
null;
324 if ( isset( $cacheVal[$field] )
325 && strlen( $cacheVal[$field] ) > 100 * 1024
327 unset( $cacheVal[$field] );
331 if ( $this->fileExists ) {
334 $ttl = $cache::TTL_DAY;
342 $this->fileExists = $cachedValues[
'fileExists'];
343 if ( $this->fileExists ) {
347 $this->dataLoaded =
true;
348 $this->extraDataLoaded =
true;
350 $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
363 $this->repo->getMasterDB()->onTransactionPreCommitOrIdle(
364 function () use ( $key ) {
365 MediaWikiServices::getInstance()->getMainWANObjectCache()->delete( $key );
375 $props = $this->repo->getFileProps( $this->
getVirtualUrl() );
386 if ( $prefix !==
'' ) {
387 throw new InvalidArgumentException(
388 __METHOD__ .
' with a non-empty prefix is no longer supported.'
396 return [
'size',
'width',
'height',
'bits',
'media_type',
397 'major_mime',
'minor_mime',
'metadata',
'timestamp',
'sha1',
'description' ];
408 if ( $prefix !==
'' ) {
409 throw new InvalidArgumentException(
410 __METHOD__ .
' with a non-empty prefix is no longer supported.'
415 return [
'metadata' ];
423 $fname = static::class .
'::' . __FUNCTION__;
425 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
426 $this->dataLoaded =
true;
427 $this->extraDataLoaded =
true;
429 $dbr = ( $flags & self::READ_LATEST )
430 ? $this->repo->getMasterDB()
431 : $this->repo->getReplicaDB();
433 $fileQuery = static::getQueryInfo();
434 $row =
$dbr->selectRow(
435 $fileQuery[
'tables'],
436 $fileQuery[
'fields'],
437 [
'img_name' => $this->
getName() ],
446 $this->fileExists =
false;
455 if ( !$this->title ) {
459 $fname = static::class .
'::' . __FUNCTION__;
461 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
462 $this->extraDataLoaded =
true;
470 foreach ( $fieldMap as
$name => $value ) {
471 $this->
$name = $value;
474 throw new MWException(
"Could not find data for image '{$this->getName()}'." );
487 $row =
$dbr->selectRow(
488 $fileQuery[
'tables'],
489 $fileQuery[
'fields'],
491 'img_name' => $this->
getName(),
492 'img_timestamp' =>
$dbr->timestamp( $this->getTimestamp() ),
501 # File may have been uploaded over in the meantime; check the old versions
503 $row =
$dbr->selectRow(
504 $fileQuery[
'tables'],
505 $fileQuery[
'fields'],
508 'oi_timestamp' =>
$dbr->timestamp( $this->getTimestamp() ),
519 if ( isset( $fieldMap[
'metadata'] ) ) {
520 $fieldMap[
'metadata'] = $this->repo->getReplicaDB()->decodeBlob( $fieldMap[
'metadata'] );
533 $array = (array)$row;
534 $prefixLength = strlen( $prefix );
537 if ( substr( key( $array ), 0, $prefixLength ) !== $prefix ) {
538 throw new MWException( __METHOD__ .
': incorrect $prefix parameter' );
542 foreach ( $array as
$name => $value ) {
543 $decoded[substr(
$name, $prefixLength )] = $value;
560 $decoded[
'description'] = MediaWikiServices::getInstance()->getCommentStore()
561 ->getComment(
'description', (
object)$decoded )->text;
564 $decoded[
'user'] ??
null,
565 $decoded[
'user_text'] ??
null,
566 $decoded[
'actor'] ??
null
568 unset( $decoded[
'user_text'], $decoded[
'actor'] );
570 $decoded[
'timestamp'] =
wfTimestamp( TS_MW, $decoded[
'timestamp'] );
572 $decoded[
'metadata'] = $this->repo->getReplicaDB()->decodeBlob( $decoded[
'metadata'] );
574 if ( empty( $decoded[
'major_mime'] ) ) {
575 $decoded[
'mime'] =
'unknown/unknown';
577 if ( !$decoded[
'minor_mime'] ) {
578 $decoded[
'minor_mime'] =
'unknown';
580 $decoded[
'mime'] = $decoded[
'major_mime'] .
'/' . $decoded[
'minor_mime'];
584 $decoded[
'sha1'] = rtrim( $decoded[
'sha1'],
"\0" );
590 foreach ( [
'size',
'width',
'height',
'bits' ] as $field ) {
591 $decoded[$field] = +$decoded[$field];
604 $this->dataLoaded =
true;
605 $this->extraDataLoaded =
true;
607 $array = $this->
decodeRow( $row, $prefix );
609 foreach ( $array as
$name => $value ) {
610 $this->
$name = $value;
613 $this->fileExists =
true;
621 if ( !$this->dataLoaded ) {
622 if ( $flags & self::READ_LATEST ) {
629 if ( ( $flags & self::LOAD_ALL ) && !$this->extraDataLoaded ) {
646 if ( is_null( $this->media_type ) || $this->mime ==
'image/svg' ) {
661 $this->upgrading =
true;
664 $this->upgrading =
false;
689 # Don't destroy file info of missing files
690 if ( !$this->fileExists ) {
692 wfDebug( __METHOD__ .
": file does not exist, aborting\n" );
697 $dbw = $this->repo->getMasterDB();
705 wfDebug( __METHOD__ .
': upgrading ' . $this->
getName() .
" to the current schema\n" );
707 $dbw->update(
'image',
709 'img_size' => $this->size,
710 'img_width' => $this->width,
711 'img_height' => $this->height,
712 'img_bits' => $this->bits,
713 'img_media_type' => $this->media_type,
714 'img_major_mime' => $major,
715 'img_minor_mime' => $minor,
716 'img_metadata' => $dbw->encodeBlob( $this->metadata ),
719 [
'img_name' => $this->
getName() ],
726 $this->upgraded =
true;
740 $this->dataLoaded =
true;
742 $fields[] =
'fileExists';
744 foreach ( $fields as $field ) {
745 if ( isset( $info[$field] ) ) {
746 $this->$field = $info[$field];
750 if ( isset( $info[
'user'] ) || isset( $info[
'user_text'] ) || isset( $info[
'actor'] ) ) {
752 $info[
'user'] ??
null,
753 $info[
'user_text'] ??
null,
754 $info[
'actor'] ??
null
759 if ( isset( $info[
'major_mime'] ) ) {
760 $this->mime =
"{$info['major_mime']}/{$info['minor_mime']}";
761 } elseif ( isset( $info[
'mime'] ) ) {
762 $this->mime = $info[
'mime'];
763 list( $this->major_mime, $this->minor_mime ) =
self::splitMime( $this->mime );
782 if ( $this->missing ===
null ) {
811 return $dim[
'width'];
843 return $dim[
'height'];
864 if ( !$this->user ) {
867 if (
$type ===
'object' ) {
869 } elseif (
$type ===
'text' ) {
870 return 'Unknown user';
871 } elseif (
$type ===
'id' ) {
875 if (
$type ===
'object' ) {
877 } elseif (
$type ===
'text' ) {
879 } elseif (
$type ===
'id' ) {
880 return $this->user->getId();
895 if ( !$this->title ) {
899 $pageId = $this->title->getArticleID();
902 $url = $this->repo->makeUrl( [
'curid' => $pageId ] );
903 if (
$url !==
false ) {
915 $this->
load( self::LOAD_ALL );
991 if ( $archiveName ) {
997 $backend = $this->repo->getBackend();
1000 $iterator = $backend->getFileList( [
'dir' => $dir ] );
1001 foreach ( $iterator as
$file ) {
1048 Hooks::run(
'LocalFilePurgeThumbnails', [ $this, $archiveName ] );
1051 $dir = array_shift( $files );
1056 foreach ( $files as
$file ) {
1071 foreach ( $files as
$file ) {
1074 array_shift( $urls );
1077 if ( !empty( $options[
'forThumbRefresh'] ) ) {
1085 Hooks::run(
'LocalFilePurgeThumbnails', [ $this,
false ] );
1088 $dir = array_shift( $files );
1108 foreach ( $sizes as
$size ) {
1112 [
'transformParams' => [
'width' => $size ] ]
1128 $fileListDebug = strtr(
1129 var_export( $files,
true ),
1132 wfDebug( __METHOD__ .
": $fileListDebug\n" );
1135 foreach ( $files as
$file ) {
1136 if ( $this->repo->supportsSha1URLs() ) {
1137 $reference = $this->
getSha1();
1139 $reference = $this->
getName();
1142 # Check that the reference (filename or sha1) is part of the thumb name
1143 # This is a basic sanity check to avoid erasing unrelated directories
1144 if ( strpos(
$file, $reference ) !==
false
1145 || strpos(
$file,
"-thumbnail" ) !==
false
1147 $purgeList[] =
"{$dir}/{$file}";
1151 # Delete the thumbnails
1152 $this->repo->quickPurgeBatch( $purgeList );
1153 # Clear out the thumbnail directory if empty
1154 $this->repo->quickCleanDir( $dir );
1167 function getHistory( $limit =
null, $start =
null, $end =
null, $inc =
true ) {
1168 if ( !$this->
exists() ) {
1172 $dbr = $this->repo->getReplicaDB();
1175 $tables = $oldFileQuery[
'tables'];
1176 $fields = $oldFileQuery[
'fields'];
1177 $join_conds = $oldFileQuery[
'joins'];
1178 $conds = $opts = [];
1179 $eq = $inc ?
'=' :
'';
1180 $conds[] =
"oi_name = " .
$dbr->addQuotes( $this->title->getDBkey() );
1183 $conds[] =
"oi_timestamp <$eq " .
$dbr->addQuotes(
$dbr->timestamp( $start ) );
1187 $conds[] =
"oi_timestamp >$eq " .
$dbr->addQuotes(
$dbr->timestamp( $end ) );
1191 $opts[
'LIMIT'] = $limit;
1195 $order = ( !$start && $end !== null ) ?
'ASC' :
'DESC';
1196 $opts[
'ORDER BY'] =
"oi_timestamp $order";
1197 $opts[
'USE INDEX'] = [
'oldimage' =>
'oi_name_timestamp' ];
1201 Hooks::run(
'LocalFile::getHistory', [ &$localFile, &$tables, &$fields,
1202 &$conds, &$opts, &$join_conds ] );
1204 $res =
$dbr->select( $tables, $fields, $conds, __METHOD__, $opts, $join_conds );
1207 foreach (
$res as $row ) {
1208 $r[] = $this->repo->newFileFromRow( $row );
1211 if ( $order ==
'ASC' ) {
1212 $r = array_reverse( $r );
1228 if ( !$this->
exists() ) {
1232 # Polymorphic function name to distinguish foreign and local fetches
1233 $fname = static::class .
'::' . __FUNCTION__;
1235 $dbr = $this->repo->getReplicaDB();
1237 if ( $this->historyLine == 0 ) {
1239 $this->historyRes =
$dbr->select( $fileQuery[
'tables'],
1240 $fileQuery[
'fields'] + [
1241 'oi_archive_name' =>
$dbr->addQuotes(
'' ),
1244 [
'img_name' => $this->title->getDBkey() ],
1250 if (
$dbr->numRows( $this->historyRes ) == 0 ) {
1251 $this->historyRes =
null;
1255 } elseif ( $this->historyLine == 1 ) {
1257 $this->historyRes =
$dbr->select(
1258 $fileQuery[
'tables'],
1259 $fileQuery[
'fields'],
1260 [
'oi_name' => $this->title->getDBkey() ],
1262 [
'ORDER BY' =>
'oi_timestamp DESC' ],
1266 $this->historyLine++;
1268 return $dbr->fetchObject( $this->historyRes );
1275 $this->historyLine = 0;
1277 if ( !is_null( $this->historyRes ) ) {
1278 $this->historyRes =
null;
1315 function upload( $src, $comment, $pageText, $flags = 0, $props =
false,
1317 $createNullRevision =
true, $revert =
false
1319 if ( $this->
getRepo()->getReadOnlyReason() !==
false ) {
1321 } elseif ( MediaWikiServices::getInstance()->getRevisionStore()->isReadOnly() ) {
1327 $srcPath = ( $src instanceof
FSFile ) ? $src->getPath() : $src;
1332 $props = $this->repo->getFileProps( $srcPath );
1334 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
1335 $props = $mwProps->getPropsFromPath( $srcPath,
true );
1342 $metadata = AtEase::quietCall(
'unserialize', $props[
'metadata'] );
1350 $options[
'headers'] = [];
1354 $comment = trim( $comment );
1359 if (
$status->successCount >= 2 ) {
1375 $createNullRevision,
1378 if ( !$uploadStatus->isOK() ) {
1379 if ( $uploadStatus->hasMessage(
'filenotfound' ) ) {
1381 $status->fatal(
'filenotfound', $srcPath );
1383 $status->merge( $uploadStatus );
1439 $oldver, $comment, $pageText, $props =
false,
$timestamp =
false,
$user =
null, $tags = [],
1440 $createNullRevision =
true, $revert =
false
1442 if ( is_null(
$user ) ) {
1447 $dbw = $this->repo->getMasterDB();
1449 # Imports or such might force a certain timestamp; otherwise we generate
1450 # it and can fudge it slightly to keep (name,timestamp) unique on re-upload.
1453 $allowTimeKludge =
true;
1455 $allowTimeKludge =
false;
1458 $props = $props ?: $this->repo->getFileProps( $this->
getVirtualUrl() );
1459 $props[
'description'] = $comment;
1466 # Fail now if the file isn't there
1467 if ( !$this->fileExists ) {
1468 wfDebug( __METHOD__ .
": File " . $this->
getRel() .
" went missing!\n" );
1473 $dbw->startAtomic( __METHOD__ );
1475 # Test to see if the row exists using INSERT IGNORE
1476 # This avoids race conditions by locking the row until the commit, and also
1477 # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
1478 $commentStore = MediaWikiServices::getInstance()->getCommentStore();
1479 $commentFields = $commentStore->insert( $dbw,
'img_description', $comment );
1481 $actorFields = $actorMigration->getInsertValues( $dbw,
'img_user',
$user );
1482 $dbw->insert(
'image',
1484 'img_name' => $this->
getName(),
1485 'img_size' => $this->size,
1486 'img_width' => intval( $this->width ),
1487 'img_height' => intval( $this->height ),
1488 'img_bits' => $this->bits,
1489 'img_media_type' => $this->media_type,
1490 'img_major_mime' => $this->major_mime,
1491 'img_minor_mime' => $this->minor_mime,
1493 'img_metadata' => $dbw->encodeBlob( $this->metadata ),
1495 ] + $commentFields + $actorFields,
1499 $reupload = ( $dbw->affectedRows() == 0 );
1502 $row = $dbw->selectRow(
1504 [
'img_timestamp',
'img_sha1' ],
1505 [
'img_name' => $this->
getName() ],
1507 [
'LOCK IN SHARE MODE' ]
1510 if ( $row && $row->img_sha1 === $this->sha1 ) {
1511 $dbw->endAtomic( __METHOD__ );
1512 wfDebug( __METHOD__ .
": File " . $this->
getRel() .
" already exists!\n" );
1517 if ( $allowTimeKludge ) {
1518 # Use LOCK IN SHARE MODE to ignore any transaction snapshotting
1519 $lUnixtime = $row ?
wfTimestamp( TS_UNIX, $row->img_timestamp ) :
false;
1520 # Avoid a timestamp that is not newer than the last version
1521 # TODO: the image/oldimage tables should be like page/revision with an ID field
1524 $timestamp = $dbw->timestamp( $lUnixtime + 1 );
1529 $tables = [
'image' ];
1531 'oi_name' =>
'img_name',
1532 'oi_archive_name' => $dbw->addQuotes( $oldver ),
1533 'oi_size' =>
'img_size',
1534 'oi_width' =>
'img_width',
1535 'oi_height' =>
'img_height',
1536 'oi_bits' =>
'img_bits',
1537 'oi_description_id' =>
'img_description_id',
1538 'oi_timestamp' =>
'img_timestamp',
1539 'oi_metadata' =>
'img_metadata',
1540 'oi_media_type' =>
'img_media_type',
1541 'oi_major_mime' =>
'img_major_mime',
1542 'oi_minor_mime' =>
'img_minor_mime',
1543 'oi_sha1' =>
'img_sha1',
1544 'oi_actor' =>
'img_actor',
1548 # (T36993) Note: $oldver can be empty here, if the previous
1549 # version of the file was broken. Allow registration of the new
1550 # version to continue anyway, because that's better than having
1551 # an image that's not fixable by user operations.
1552 # Collision, this is an update of a file
1553 # Insert previous contents into oldimage
1554 $dbw->insertSelect(
'oldimage', $tables, $fields,
1555 [
'img_name' => $this->
getName() ], __METHOD__, [], [], $joins );
1557 # Update the current image row
1558 $dbw->update(
'image',
1560 'img_size' => $this->size,
1561 'img_width' => intval( $this->width ),
1562 'img_height' => intval( $this->height ),
1563 'img_bits' => $this->bits,
1564 'img_media_type' => $this->media_type,
1565 'img_major_mime' => $this->major_mime,
1566 'img_minor_mime' => $this->minor_mime,
1568 'img_metadata' => $dbw->encodeBlob( $this->metadata ),
1570 ] + $commentFields + $actorFields,
1571 [
'img_name' => $this->
getName() ],
1577 $descId = $descTitle->getArticleID();
1579 $wikiPage->setFile( $this );
1582 if ( $revert ===
true ) {
1583 $logAction =
'revert';
1584 } elseif ( $reupload ===
true ) {
1585 $logAction =
'overwrite';
1587 $logAction =
'upload';
1591 $logEntry->setTimestamp( $this->timestamp );
1592 $logEntry->setPerformer(
$user );
1593 $logEntry->setComment( $comment );
1594 $logEntry->setTarget( $descTitle );
1597 $logEntry->setParameters(
1599 'img_sha1' => $this->sha1,
1609 $logId = $logEntry->insert();
1611 if ( $descTitle->exists() ) {
1615 $editSummary = $formatter->getPlainActionText();
1624 if ( $nullRevision ) {
1625 $nullRevision->insertOn( $dbw );
1627 'NewRevisionFromEditComplete',
1628 [ $wikiPage, $nullRevision, $nullRevision->getParentId(),
$user ]
1630 $wikiPage->updateRevisionOn( $dbw, $nullRevision );
1632 $logEntry->setAssociatedRevId( $nullRevision->getId() );
1635 $newPageContent =
null;
1641 # Defer purges, page creation, and link updates in case they error out.
1642 # The most important thing is that files and the DB registry stay synced.
1643 $dbw->endAtomic( __METHOD__ );
1644 $fname = __METHOD__;
1646 # Do some cache purges after final commit so that:
1647 # a) Changes are more likely to be seen post-purge
1648 # b) They won't cause rollback of the log publish/update above
1654 $reupload, $wikiPage, $newPageContent, $comment,
$user,
1655 $logEntry, $logId, $descId, $tags, $fname
1657 # Update memcache after the commit
1660 $updateLogPage =
false;
1661 if ( $newPageContent ) {
1662 # New file page; create the description page.
1663 # There's already a log entry, so don't make a second RC entry
1664 # CDN and file cache for the description page are purged by doEditContent.
1665 $status = $wikiPage->doEditContent(
1673 if ( isset(
$status->value[
'revision'] ) ) {
1675 $rev =
$status->value[
'revision'];
1677 $logEntry->setAssociatedRevId( $rev->getId() );
1681 if ( isset(
$status->value[
'revision'] ) ) {
1683 $rev =
$status->value[
'revision'];
1684 $updateLogPage = $rev->getPage();
1687 # Existing file page: invalidate description page cache
1688 $wikiPage->getTitle()->invalidateCache();
1689 $wikiPage->getTitle()->purgeSquid();
1690 # Allow the new file version to be patrolled from the page footer
1694 # Update associated rev id. This should be done by $logEntry->insert() earlier,
1695 # but setAssociatedRevId() wasn't called at that point yet...
1696 $logParams = $logEntry->getParameters();
1697 $logParams[
'associated_rev_id'] = $logEntry->getAssociatedRevId();
1699 if ( $updateLogPage ) {
1700 # Also log page, in case where we just created it above
1701 $update[
'log_page'] = $updateLogPage;
1703 $this->
getRepo()->getMasterDB()->update(
1706 [
'log_id' => $logId ],
1709 $this->
getRepo()->getMasterDB()->insert(
1712 'ls_field' =>
'associated_rev_id',
1713 'ls_value' => $logEntry->getAssociatedRevId(),
1714 'ls_log_id' => $logId,
1719 # Add change tags, if any
1721 $logEntry->addTags( $tags );
1724 # Uploads can be patrolled
1725 $logEntry->setIsPatrollable(
true );
1727 # Now that the log entry is up-to-date, make an RC entry.
1728 $logEntry->publish( $logId );
1730 # Run hook for other updates (typically more cache purging)
1731 Hooks::run(
'FileUpload', [ $this, $reupload, !$newPageContent ] );
1734 # Delete old thumbnails
1736 # Remove the old file from the CDN cache
1742 # Update backlink pages pointing to this title if created
1758 # This is a new file, so update the image count
1762 # Invalidate cache for all pages using this file
1785 function publish( $src, $flags = 0, array $options = [] ) {
1804 function publishTo( $src, $dstRel, $flags = 0, array $options = [] ) {
1805 $srcPath = ( $src instanceof
FSFile ) ? $src->getPath() : $src;
1814 if ( $this->
isOld() ) {
1815 $archiveRel = $dstRel;
1816 $archiveName = basename( $archiveRel );
1828 '@phan-var FileBackendDBRepoWrapper $wrapperBackend';
1829 $dst = $wrapperBackend->getPathForSHA1(
$sha1 );
1836 $status->value = $archiveName;
1842 if (
$status->value ==
'new' ) {
1845 $status->value = $archiveName;
1871 $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
1872 if ( $this->
getRepo()->getReadOnlyReason() !==
false ) {
1876 wfDebugLog(
'imagemove',
"Got request to move {$this->name} to " . $target->getText() );
1880 $batch->addCurrent();
1881 $archiveNames = $batch->addOlds();
1885 wfDebugLog(
'imagemove',
"Finished moving {$this->name}" );
1888 $oldTitleFile = $localRepo->newFile( $this->title );
1889 $newTitleFile = $localRepo->newFile( $target );
1892 $this->
getRepo()->getMasterDB(),
1894 function () use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
1895 $oldTitleFile->purgeEverything();
1896 foreach ( $archiveNames as $archiveName ) {
1898 '@phan-var OldLocalFile $oldTitleFile';
1899 $oldTitleFile->purgeOldThumbnails( $archiveName );
1901 $newTitleFile->purgeEverything();
1909 $this->title = $target;
1912 $this->hashPath =
null;
1931 function delete( $reason, $suppress =
false,
$user = null ) {
1932 if ( $this->
getRepo()->getReadOnlyReason() !== false ) {
1939 $batch->addCurrent();
1941 $archiveNames = $batch->addOlds();
1952 $this->
getRepo()->getMasterDB(),
1954 function () use ( $archiveNames ) {
1956 foreach ( $archiveNames as $archiveName ) {
1966 foreach ( $archiveNames as $archiveName ) {
1990 if ( $this->
getRepo()->getReadOnlyReason() !==
false ) {
1997 $batch->addOld( $archiveName );
2025 function restore( $versions = [], $unsuppress =
false ) {
2026 if ( $this->
getRepo()->getReadOnlyReason() !==
false ) {
2036 $batch->addIds( $versions );
2040 $cleanupStatus = $batch->cleanup();
2041 $cleanupStatus->successCount = 0;
2042 $cleanupStatus->failCount = 0;
2043 $status->merge( $cleanupStatus );
2060 if ( !$this->title ) {
2064 return $this->title->getLocalURL();
2076 if ( !$this->title ) {
2080 $store = MediaWikiServices::getInstance()->getRevisionStore();
2081 $revision = $store->getRevisionByTitle( $this->title, 0, Revision::READ_NORMAL );
2086 $renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
2087 $rendered = $renderer->getRenderedRevision( $revision,
new ParserOptions(
null,
$lang ) );
2094 $pout = $rendered->getRevisionParserOutput();
2095 return $pout->getText();
2105 if ( $audience == self::FOR_PUBLIC && $this->
isDeleted( self::DELETED_COMMENT ) ) {
2107 } elseif ( $audience == self::FOR_THIS_USER
2129 if ( !$this->
exists() ) {
2136 if ( $this->descriptionTouched ===
null ) {
2138 'page_namespace' => $this->title->getNamespace(),
2139 'page_title' => $this->title->getDBkey()
2141 $touched = $this->repo->getReplicaDB()->selectField(
'page',
'page_touched', $cond, __METHOD__ );
2142 $this->descriptionTouched = $touched ?
wfTimestamp( TS_MW, $touched ) :
false;
2154 if ( $this->sha1 ==
'' && $this->fileExists ) {
2157 $this->sha1 = $this->repo->getFileSha1( $this->
getPath() );
2158 if ( !
wfReadOnly() && strval( $this->sha1 ) !=
'' ) {
2159 $dbw = $this->repo->getMasterDB();
2160 $dbw->update(
'image',
2161 [
'img_sha1' => $this->sha1 ],
2162 [
'img_name' => $this->
getName() ],
2180 return $this->extraDataLoaded
2214 if ( !$this->locked ) {
2215 $logger = LoggerFactory::getInstance(
'LocalFile' );
2217 $dbw = $this->repo->getMasterDB();
2218 $makesTransaction = !$dbw->trxLevel();
2219 $dbw->startAtomic( self::ATOMIC_SECTION_LOCK );
2225 $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
2226 $logger->warning(
"Failed to lock '{file}'", [
'file' => $this->name ] );
2232 $dbw->onTransactionResolution(
2233 function () use ( $logger ) {
2236 $logger->error(
"Failed to unlock '{file}'", [
'file' => $this->name ] );
2242 $this->lockedOwnTrx = $makesTransaction;
2259 if ( $this->locked ) {
2261 if ( !$this->locked ) {
2262 $dbw = $this->repo->getMasterDB();
2263 $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
2264 $this->lockedOwnTrx =
false;
2273 return $this->
getRepo()->newFatal(
'filereadonlyerror', $this->
getName(),
string $media_type
MEDIATYPE_xxx (bitmap, drawing, audio...)
Set options of the Parser.
const ATOMIC_SECTION_LOCK
bool $fileExists
Does the file exist on disk? (loadFromXxx)
getPath()
Return the storage path to the file.
$wgUpdateCompatibleMetadata
If to automatically update the img_metadata field if the metadata field is outdated but compatible wi...
static getInitialPageText( $comment='', $license='', $copyStatus='', $source='', Config $config=null)
Get the initial image page text based on a comment and optional file status information.
maybeUpgradeRow()
Upgrade a row if it needs it.
Helper class for file undeletion.
getMutableCacheKeys(WANObjectCache $cache)
getReadOnlyReason()
Get an explanatory message if this repo is read-only.
unprefixRow( $row, $prefix='img_')
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
FileRepo LocalRepo ForeignAPIRepo bool $repo
Some member variables can be lazy-initialised using __get().
getArchiveThumbPath( $archiveName, $suffix=false)
Get the path of an archived file's thumbs, or a particular thumb if $suffix is specified.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
__construct( $title, $repo)
Do not call this except from inside a repo class.
unlock()
Decrement the lock reference count and end the atomic section if it reaches zero.
getId()
Get the user's ID.
isMultipage()
Returns 'true' if this file is a type which supports multiple pages, e.g.
getUser( $type='text')
Returns user who uploaded the file.
getActorId(IDatabase $dbw=null)
Get the user's actor ID.
loadFromDB( $flags=0)
Load file metadata from the DB.
if(!isset( $args[0])) $lang
purgeThumbList( $dir, $files)
Delete a list of thumbnails visible at urls.
getRel()
Get the path of the file relative to the public zone root.
Deferrable Update for closure/callback updates that should use auto-commit mode.
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new oldlocalfile object.
bool $missing
True if file is not present in file system.
Helper class for file deletion.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
File backend exception for checked exceptions (e.g.
upload( $src, $comment, $pageText, $flags=0, $props=false, $timestamp=false, $user=null, $tags=[], $createNullRevision=true, $revert=false)
getHashPath inherited
bool $upgraded
Whether the row was upgraded on load.
getDescriptionShortUrl()
Get short description URL for a file based on the page ID.
getCacheFields( $prefix='img_')
Returns the list of object properties that are included as-is in the cache.
getPrefixedText()
Get the prefixed title with spaces.
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
getHistory( $limit=null, $start=null, $end=null, $inc=true)
purgeDescription inherited
getMediaType()
Returns the type of the media in the file.
getUrl()
Return the URL of the file.
bool $dataLoaded
Whether or not core data has been loaded from the database (loadFromXxx)
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
static newFromTitle( $title, $repo, $unused=null)
Create a LocalFile from a title Do not call this except from inside a repo class.
wfReadOnly()
Check whether the wiki is in read-only mode.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
static newExtraneousContext(Title $title, $request=[])
Create a new extraneous context.
bool $upgrading
Whether the row was scheduled to upgrade on load.
getSize()
Returns the size of the image file, in bytes.
purgeOldThumbnails( $archiveName)
Delete cached transformed files for an archived version only.
getThumbnails( $archiveName=false)
getTransformScript inherited
string $sha1
SHA-1 base 36 content hash.
static splitMime( $mime)
Split an internet media type into its two components; if not a two-part name, set the minor type to '...
string $minor_mime
Minor MIME type.
getArchiveRel( $suffix=false)
Get the path of an archived file relative to the public zone root.
isDeleted( $field)
Is this file a "deleted" file in a private archive? STUB.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
static newMigration()
Static constructor.
publish( $src, $dstRel, $archiveRel, $flags=0, array $options=[])
Copy or move a file either from a storage path, virtual URL, or file system path, into this repositor...
decodeRow( $row, $prefix='img_')
Decode a row from the database (either object or array) to an array with timestamps and MIME types de...
int $bits
Returned by getimagesize (loadFromXxx)
purgeThumbnails( $options=[])
Delete cached transformed files for the current version only.
hasSha1Storage()
Returns whether or not storage is SHA-1 based.
Implements some public methods and some protected utility functions which are required by multiple ch...
loadExtraFromDB()
Load lazy file metadata from the DB.
publishTo( $src, $dstRel, $flags=0, array $options=[])
Move or copy a file to a specified location.
string $url
The URL corresponding to one of the four basic zones.
static newFromRow( $row, $repo)
Create a LocalFile from a title Do not call this except from inside a repo class.
purgeMetadataCache()
Refresh metadata in memcached, but don't touch thumbnails or CDN.
getThumbPath( $suffix=false)
Get the path of the thumbnail directory, or a particular file if $suffix is specified.
quickImport( $src, $dst, $options=null)
Import a file from the local file system into the repo.
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
deleteOld( $archiveName, $reason, $suppress=false, $user=null)
Delete an old version of the file.
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
const CACHE_FIELD_MAX_LEN
loadFromRow( $row, $prefix='img_')
Load file metadata from a DB result row.
__destruct()
Clean up any dangling locks.
int $deleted
Bitfield akin to rev_deleted.
$wgUploadThumbnailRenderMap
When defined, is an array of thumbnail widths to be rendered at upload time.
static getSha1Base36FromPath( $path)
Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case encoding,...
IResultWrapper null $historyRes
Result of the query for the file's history (nextHistoryLine)
getDescriptionText(Language $lang=null)
Get the HTML text of the description page This is not used by ImagePage for local files,...
static factory(array $deltas)
MimeMagic helper wrapper.
prerenderThumbnails()
Prerenders a configurable set of thumbnails.
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
getCacheKey()
Get the memcached key for the main data for this file, or false if there is no access to the shared c...
addWatch( $title, $checkRights=self::CHECK_USER_RIGHTS)
Watch an article.
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
string $mime
MIME type, determined by MimeAnalyzer::guessMimeType.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
setProps( $info)
Set properties in this object to be equal to those given in the associative array $info.
getFileSha1( $virtualUrl)
Get the sha1 (base 36) of a file with a given virtual URL/storage path.
Class to represent a local file in the wiki's own database.
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
getMimeType()
Returns the MIME type of the file.
getDescription( $audience=self::FOR_PUBLIC, User $user=null)
bool $extraDataLoaded
Whether or not lazy-loaded data has been loaded from the database.
Job for asynchronous rendering of thumbnails.
int $size
Size in bytes (loadFromXxx)
Class to invalidate the HTML/file cache of all the pages linking to a given title.
purgeDescription()
Purge the file description page, but don't go after pages using the file.
lock()
Start an atomic DB section and lock the image for update or increments a reference counter if the loc...
isOld()
Returns true if the image is an old version STUB.
Handles purging the appropriate CDN objects given a list of URLs or Title instances.
assertTitleDefined()
Assert that $this->title is set to a Title.
static newGood( $value=null)
Factory function for good results.
int $historyLine
Number of line to return by nextHistoryLine() (constructor)
upgradeRow()
Fix assorted version-related problems with the image row by reloading it from the file.
load( $flags=0)
Load file metadata from cache or DB, unless already loaded.
Multi-datacenter aware caching interface.
static newFromKey( $sha1, $repo, $timestamp=false)
Create a LocalFile from a SHA-1 key Do not call this except from inside a repo class.
nextHistoryLine()
Returns the history of this file, line by line.
string $metadata
Handler-specific metadata.
getArchiveThumbUrl( $archiveName, $suffix=false)
Get the URL of the archived file's thumbs, or a particular thumb if $suffix is specified.
getName()
Return the name of this file.
getArchiveUrl( $suffix=false)
Get the URL of the archive directory, or a particular file if $suffix is specified.
Class representing a non-directory file on the file system.
string $timestamp
Upload timestamp.
getBackend()
Get the file backend instance.
string $descriptionTouched
TS_MW timestamp of the last change of the file description.
recordUpload( $oldver, $desc, $license='', $copyStatus='', $source='', $watch=false, $timestamp=false, User $user=null)
Record a file upload in the upload log and the image table.
bool $locked
True if the image row is locked.
string $description
Description of current revision of the file.
getLazyCacheFields( $prefix='img_')
Returns the list of object properties that are included as-is in the cache, only when they're not too...
getTitle()
Return the associated title object.
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new localfile object.
getMetadata()
Get handler-specific metadata.
static singleton( $domain=false)
loadFromFile()
Load metadata from the file itself.
assertRepoDefined()
Assert that $this->repo is set to a valid FileRepo instance.
recordUpload2( $oldver, $comment, $pageText, $props=false, $timestamp=false, $user=null, $tags=[], $createNullRevision=true, $revert=false)
Record a file upload in the upload log and the image table.
resetHistory()
Reset the history pointer to the first element of the history.
static makeParamBlob( $params)
Create a blob from a parameter array.
static queueRecursiveJobsForTable(Title $title, $table, $action='unknown', $userName='unknown')
Queue a RefreshLinks job for any table.
static isVirtualUrl( $url)
Determine if a string is an mwrepo:// URL.
loadExtraFieldsWithTimestamp( $dbr, $fname)
Helper class for file movement.
Class for creating new log entries and inserting them into the database.
static newNullRevision( $dbw, $pageId, $summary, $minor, $user=null)
Create a new null-revision for insertion into a page's history.
Special handling for file pages.
string $name
The name of a file from its title object.
publish( $src, $flags=0, array $options=[])
Move or copy a file to its public location.
getRepo()
Returns the repository.
getWidth( $page=1)
Return the width of the image.
getThumbUrl( $suffix=false)
Get the URL of the thumbnail directory, or a particular file if $suffix is specified.
string $major_mime
Major MIME type.
isVectorized()
Return true if the file is vectorized.
getHandler()
Get a MediaHandler instance for this file.
getHeight( $page=1)
Return the height of the image.
isMissing()
splitMime inherited
invalidateCache()
Purge the file object/metadata cache.
userCan( $field, User $user=null)
Determine if the current user is allowed to view a particular field of this file, if it's marked as d...
bool $lockedOwnTrx
True if the image row is locked with a lock initiated transaction.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add a callable update.
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
getDescriptionUrl()
isMultipage inherited
move( $target)
getLinksTo inherited
getName()
Get the user name, or the IP of an anonymous user.
restore( $versions=[], $unsuppress=false)
Restore all or specified deleted revisions to the given file.
Internationalisation code.
purgeEverything()
Purge metadata and all affected pages when the file is created, deleted, or majorly updated.
exists()
canRender inherited
getThumbnails()
Get all thumbnail names previously generated for this file STUB Overridden by LocalFile.
loadFromCache()
Try to load file metadata from memcached, falling back to the database.
purgeCache( $options=[])
Delete all previously generated thumbnails, refresh metadata in memcached and purge the CDN.
getVirtualUrl( $suffix=false)
Get the public zone virtual URL for a current version source file.
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...