66 private const MDS_EMPTY =
'empty';
69 private const MDS_LEGACY =
'legacy';
72 private const MDS_PHP =
'php';
75 private const MDS_JSON =
'json';
78 private const MAX_PAGE_RENDER_JOBS = 50;
217 $file->loadFromRow( $row );
236 $conds = [
'img_sha1' =>
$sha1 ];
241 $fileQuery = static::getQueryInfo();
242 $row =
$dbr->selectRow(
243 $fileQuery[
'tables'], $fileQuery[
'fields'], $conds, __METHOD__, [], $fileQuery[
'joins']
246 return static::newFromRow( $row,
$repo );
271 $commentQuery = MediaWikiServices::getInstance()->getCommentStore()->getJoin(
'img_description' );
275 'image_actor' =>
'actor'
276 ] + $commentQuery[
'tables'],
290 'img_user' =>
'image_actor.actor_user',
291 'img_user_text' =>
'image_actor.actor_name',
292 ] + $commentQuery[
'fields'],
294 'image_actor' => [
'JOIN',
'actor_id=img_actor' ]
295 ] + $commentQuery[
'joins'],
298 if ( in_array(
'omit-nonlazy', $options,
true ) ) {
302 if ( !in_array(
'omit-lazy', $options,
true ) ) {
305 $ret[
'fields'][] =
'img_metadata';
340 return $this->repo->getSharedCacheKey(
'file', sha1( $this->
getName() ) );
356 $this->dataLoaded =
false;
357 $this->extraDataLoaded =
false;
366 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
367 $cachedValues =
$cache->getWithSetCallback(
370 function ( $oldValue, &$ttl, array &$setOpts ) use (
$cache ) {
371 $setOpts += Database::getCacheSetOptions( $this->repo->getReplicaDB() );
378 if ( $this->fileExists ) {
379 foreach ( $fields as $field ) {
380 $cacheVal[$field] = $this->$field;
384 $cacheVal[
'user'] = $this->user->getId();
385 $cacheVal[
'user_text'] = $this->user->getName();
389 if ( $this->metadataBlobs ) {
390 $cacheVal[
'metadata'] = array_diff_key(
391 $this->metadataArray, $this->metadataBlobs );
402 if ( isset( $cacheVal[$field] )
403 && strlen(
serialize( $cacheVal[$field] ) ) > 100 * 1024
405 unset( $cacheVal[$field] );
406 if ( $field ===
'metadata' ) {
407 unset( $cacheVal[
'metadataBlobs'] );
412 if ( $this->fileExists ) {
413 $ttl =
$cache->adaptiveTTL( (
int)
wfTimestamp( TS_UNIX, $this->timestamp ), $ttl );
415 $ttl = $cache::TTL_DAY;
423 $this->fileExists = $cachedValues[
'fileExists'];
424 if ( $this->fileExists ) {
428 $this->dataLoaded =
true;
429 $this->extraDataLoaded =
true;
431 $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
444 $this->repo->getPrimaryDB()->onTransactionPreCommitOrIdle(
445 static function () use ( $key ) {
446 MediaWikiServices::getInstance()->getMainWANObjectCache()->delete( $key );
472 if ( $prefix !==
'' ) {
473 throw new InvalidArgumentException(
474 __METHOD__ .
' with a non-empty prefix is no longer supported.'
482 return [
'size',
'width',
'height',
'bits',
'media_type',
483 'major_mime',
'minor_mime',
'timestamp',
'sha1',
'description' ];
494 if ( $prefix !==
'' ) {
495 throw new InvalidArgumentException(
496 __METHOD__ .
' with a non-empty prefix is no longer supported.'
501 return [
'metadata' ];
510 $fname = static::class .
'::' . __FUNCTION__;
512 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
513 $this->dataLoaded =
true;
514 $this->extraDataLoaded =
true;
516 $dbr = ( $flags & self::READ_LATEST )
517 ? $this->repo->getPrimaryDB()
518 : $this->repo->getReplicaDB();
520 $fileQuery = static::getQueryInfo();
521 $row =
$dbr->selectRow(
522 $fileQuery[
'tables'],
523 $fileQuery[
'fields'],
524 [
'img_name' => $this->
getName() ],
533 $this->fileExists =
false;
543 if ( !$this->title ) {
547 $fname = static::class .
'::' . __FUNCTION__;
549 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
550 $this->extraDataLoaded =
true;
552 $db = $this->repo->getReplicaDB();
555 $db = $this->repo->getPrimaryDB();
560 if ( isset( $fieldMap[
'metadata'] ) ) {
564 throw new MWException(
"Could not find data for image '{$this->getName()}'." );
577 $row =
$dbr->selectRow(
578 $fileQuery[
'tables'],
579 $fileQuery[
'fields'],
581 'img_name' => $this->
getName(),
582 'img_timestamp' =>
$dbr->timestamp( $this->getTimestamp() ),
591 # File may have been uploaded over in the meantime; check the old versions
593 $row =
$dbr->selectRow(
594 $fileQuery[
'tables'],
595 $fileQuery[
'fields'],
598 'oi_timestamp' =>
$dbr->timestamp( $this->getTimestamp() ),
619 $array = (array)$row;
620 $prefixLength = strlen( $prefix );
623 if ( substr( key( $array ), 0, $prefixLength ) !== $prefix ) {
624 throw new MWException( __METHOD__ .
': incorrect $prefix parameter' );
628 foreach ( $array as
$name => $value ) {
629 $decoded[substr(
$name, $prefixLength )] = $value;
651 $this->dataLoaded =
true;
655 $this->name = $unprefixed[
'name'];
656 $this->media_type = $unprefixed[
'media_type'];
658 $services = MediaWikiServices::getInstance();
659 $this->description = $services->getCommentStore()
660 ->getComment(
"{$prefix}description", $row )->text;
662 $this->user = $services->getUserFactory()->newFromAnyId(
663 $unprefixed[
'user'] ??
null,
664 $unprefixed[
'user_text'] ??
null,
665 $unprefixed[
'actor'] ??
null
668 $this->timestamp =
wfTimestamp( TS_MW, $unprefixed[
'timestamp'] );
671 $this->repo->getReplicaDB(), $unprefixed[
'metadata'] );
673 if ( empty( $unprefixed[
'major_mime'] ) ) {
674 $this->major_mime =
'unknown';
675 $this->minor_mime =
'unknown';
676 $this->mime =
'unknown/unknown';
678 if ( !$unprefixed[
'minor_mime'] ) {
679 $unprefixed[
'minor_mime'] =
'unknown';
681 $this->major_mime = $unprefixed[
'major_mime'];
682 $this->minor_mime = $unprefixed[
'minor_mime'];
683 $this->mime = $unprefixed[
'major_mime'] .
'/' . $unprefixed[
'minor_mime'];
687 $this->sha1 = rtrim( $unprefixed[
'sha1'],
"\0" );
693 $this->size = +$unprefixed[
'size'];
694 $this->width = +$unprefixed[
'width'];
695 $this->height = +$unprefixed[
'height'];
696 $this->bits = +$unprefixed[
'bits'];
699 $extraFields = array_diff(
700 array_keys( $unprefixed ),
702 'name',
'media_type',
'description_text',
'description_data',
703 'description_cid',
'user',
'user_text',
'actor',
'timestamp',
704 'metadata',
'major_mime',
'minor_mime',
'sha1',
'size',
'width',
708 if ( $extraFields ) {
710 'Passing extra fields (' .
711 implode(
', ', $extraFields )
712 .
') to ' . __METHOD__ .
' was deprecated in MediaWiki 1.37. ' .
713 'Property assignment will be removed in a later version.',
715 foreach ( $extraFields as $field ) {
716 $this->$field = $unprefixed[$field];
720 $this->fileExists =
true;
728 public function load( $flags = 0 ) {
729 if ( !$this->dataLoaded ) {
730 if ( $flags & self::READ_LATEST ) {
737 if ( ( $flags & self::LOAD_ALL ) && !$this->extraDataLoaded ) {
748 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() || $this->upgrading ) {
753 $reserialize =
false;
754 if ( $this->media_type ===
null || $this->mime ==
'image/svg' ) {
763 && $this->repo->isMetadataUpdateEnabled()
766 } elseif ( $this->repo->isJsonMetadataEnabled()
767 && $this->repo->isMetadataReserializeEnabled()
769 if ( $this->repo->isSplitMetadataEnabled() && $this->isMetadataOversize() ) {
771 } elseif ( $this->metadataSerializationFormat !== self::MDS_EMPTY &&
772 $this->metadataSerializationFormat !== self::MDS_JSON ) {
779 if ( $upgrade || $reserialize ) {
780 $this->upgrading =
true;
783 $this->upgrading =
false;
809 $dbw = $this->repo->getPrimaryDB();
813 $freshnessCondition = [
'img_timestamp' => $dbw->timestamp( $this->
getTimestamp() ) ];
817 # Don't destroy file info of missing files
818 if ( !$this->fileExists ) {
819 wfDebug( __METHOD__ .
": file does not exist, aborting" );
826 wfDebug( __METHOD__ .
': upgrading ' . $this->
getName() .
" to the current schema" );
828 $dbw->update(
'image',
830 'img_size' => $this->size,
831 'img_width' => $this->width,
832 'img_height' => $this->height,
833 'img_bits' => $this->bits,
834 'img_media_type' => $this->media_type,
835 'img_major_mime' => $major,
836 'img_minor_mime' => $minor,
838 'img_sha1' => $this->sha1,
841 [
'img_name' => $this->
getName() ],
849 $this->upgraded =
true;
857 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
860 $dbw = $this->repo->getPrimaryDB();
865 'img_name' => $this->name,
866 'img_timestamp' => $dbw->timestamp( $this->timestamp ),
870 $this->upgraded =
true;
885 $this->dataLoaded =
true;
887 $fields[] =
'fileExists';
889 foreach ( $fields as $field ) {
890 if ( isset( $info[$field] ) ) {
891 $this->$field = $info[$field];
896 if ( isset( $info[
'user'] ) &&
897 isset( $info[
'user_text'] ) &&
898 $info[
'user_text'] !==
''
904 if ( isset( $info[
'major_mime'] ) ) {
905 $this->mime =
"{$info['major_mime']}/{$info['minor_mime']}";
906 } elseif ( isset( $info[
'mime'] ) ) {
907 $this->mime = $info[
'mime'];
908 list( $this->major_mime, $this->minor_mime ) =
self::splitMime( $this->mime );
911 if ( isset( $info[
'metadata'] ) ) {
912 if ( is_string( $info[
'metadata'] ) ) {
914 } elseif ( is_array( $info[
'metadata'] ) ) {
915 $this->metadataArray = $info[
'metadata'];
916 if ( isset( $info[
'metadataBlobs'] ) ) {
917 $this->metadataBlobs = $info[
'metadataBlobs'];
918 $this->unloadedMetadataBlobs = array_diff_key(
919 $this->metadataBlobs,
923 $this->metadataBlobs = [];
924 $this->unloadedMetadataBlobs = [];
927 $logger = LoggerFactory::getInstance(
'LocalFile' );
928 $logger->warning( __METHOD__ .
' given invalid metadata of type ' .
929 gettype( $info[
'metadata'] ) );
930 $this->metadataArray = [];
932 $this->extraDataLoaded =
true;
952 if ( $this->missing ===
null ) {
982 return $dim[
'width'];
1015 return $dim[
'height'];
1034 if ( !$this->title ) {
1038 $pageId = $this->title->getArticleID();
1041 $url = $this->repo->makeUrl( [
'curid' => $pageId ] );
1042 if (
$url !==
false ) {
1059 } elseif ( array_keys( $data ) === [
'_error' ] ) {
1061 return $data[
'_error'];
1075 if ( $this->unloadedMetadataBlobs ) {
1077 array_unique( array_merge(
1078 array_keys( $this->metadataArray ),
1079 array_keys( $this->unloadedMetadataBlobs )
1087 $this->load( self::LOAD_ALL );
1090 foreach ( $itemNames as $itemName ) {
1091 if ( array_key_exists( $itemName, $this->metadataArray ) ) {
1092 $result[$itemName] = $this->metadataArray[$itemName];
1093 } elseif ( isset( $this->unloadedMetadataBlobs[$itemName] ) ) {
1094 $addresses[$itemName] = $this->unloadedMetadataBlobs[$itemName];
1099 $resultFromBlob = $this->metadataStorageHelper->getMetadataFromBlobStore( $addresses );
1100 foreach ( $addresses as $itemName => $address ) {
1101 unset( $this->unloadedMetadataBlobs[$itemName] );
1102 $value = $resultFromBlob[$itemName] ??
null;
1103 if ( $value !==
null ) {
1104 $result[$itemName] = $value;
1105 $this->metadataArray[$itemName] = $value;
1124 $this->load( self::LOAD_ALL );
1125 if ( !$this->metadataArray && !$this->metadataBlobs ) {
1127 } elseif ( $this->repo->isJsonMetadataEnabled() ) {
1128 $s = $this->getJsonMetadata();
1132 if ( !is_string(
$s ) ) {
1133 throw new MWException(
'Could not serialize image metadata value for DB' );
1147 'data' => array_diff_key( $this->metadataArray, $this->metadataBlobs )
1151 if ( $this->metadataBlobs ) {
1152 $envelope[
'blobs'] = $this->metadataBlobs;
1155 list(
$s, $blobAddresses ) = $this->metadataStorageHelper->getJsonMetadata( $this, $envelope );
1158 $this->metadataBlobs += $blobAddresses;
1170 if ( !$this->repo->isSplitMetadataEnabled() ) {
1173 $threshold = $this->repo->getSplitMetadataThreshold();
1174 $directItems = array_diff_key( $this->metadataArray, $this->metadataBlobs );
1175 foreach ( $directItems as $value ) {
1176 if ( strlen( $this->metadataStorageHelper->jsonEncode( $value ) ) > $threshold ) {
1192 $this->loadMetadataFromString( $db->
decodeBlob( $metadataBlob ) );
1203 $this->extraDataLoaded =
true;
1204 $this->metadataArray = [];
1205 $this->metadataBlobs = [];
1206 $this->unloadedMetadataBlobs = [];
1207 $metadataString = (string)$metadataString;
1208 if ( $metadataString ===
'' ) {
1209 $this->metadataSerializationFormat = self::MDS_EMPTY;
1212 if ( $metadataString[0] ===
'{' ) {
1213 $envelope = $this->metadataStorageHelper->jsonDecode( $metadataString );
1216 $this->metadataArray = [
'_error' => $metadataString ];
1217 $this->metadataSerializationFormat = self::MDS_LEGACY;
1219 $this->metadataSerializationFormat = self::MDS_JSON;
1220 if ( isset( $envelope[
'data'] ) ) {
1221 $this->metadataArray = $envelope[
'data'];
1223 if ( isset( $envelope[
'blobs'] ) ) {
1224 $this->metadataBlobs = $this->unloadedMetadataBlobs = $envelope[
'blobs'];
1230 if ( !is_array( $data ) ) {
1232 $data = [
'_error' => $metadataString ];
1233 $this->metadataSerializationFormat = self::MDS_LEGACY;
1235 $this->metadataSerializationFormat = self::MDS_PHP;
1237 $this->metadataArray = $data;
1248 return (
int)$this->bits;
1282 return $this->media_type;
1299 return $this->fileExists;
1319 if ( $archiveName ) {
1320 $dir = $this->getArchiveThumbPath( $archiveName );
1322 $dir = $this->getThumbPath();
1325 $backend = $this->repo->getBackend();
1328 $iterator = $backend->getFileList( [
'dir' => $dir ] );
1329 if ( $iterator !==
null ) {
1330 foreach ( $iterator as
$file ) {
1344 $this->invalidateCache();
1357 $this->maybeUpgradeRow();
1358 $this->purgeMetadataCache();
1361 $this->purgeThumbnails( $options );
1364 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
1367 !empty( $options[
'forThumbRefresh'] )
1368 ? $hcu::PURGE_PRESEND
1369 : $hcu::PURGE_INTENT_TXROUND_REFLECTED
1380 $thumbs = $this->getThumbnails( $archiveName );
1383 $dir = array_shift( $thumbs );
1384 $this->purgeThumbList( $dir, $thumbs );
1387 foreach ( $thumbs as $thumb ) {
1388 $urls[] = $this->getArchiveThumbUrl( $archiveName, $thumb );
1392 $this->getHookRunner()->onLocalFilePurgeThumbnails( $this, $archiveName, $urls );
1395 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
1396 $hcu->purgeUrls( $urls, $hcu::PURGE_PRESEND );
1406 $thumbs = $this->getThumbnails();
1409 $dir = array_shift( $thumbs );
1410 $this->purgeThumbList( $dir, $thumbs );
1414 foreach ( $thumbs as $thumb ) {
1415 $urls[] = $this->getThumbUrl( $thumb );
1419 if ( !empty( $options[
'forThumbRefresh'] ) ) {
1420 $handler = $this->getHandler();
1422 $handler->filterThumbnailPurgeList( $thumbs, $options );
1427 $this->getHookRunner()->onLocalFilePurgeThumbnails( $this,
false, $urls );
1430 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
1433 !empty( $options[
'forThumbRefresh'] )
1434 ? $hcu::PURGE_PRESEND
1435 : $hcu::PURGE_INTENT_TXROUND_REFLECTED
1446 $uploadThumbnailRenderMap = MediaWikiServices::getInstance()
1447 ->getMainConfig()->get( MainConfigNames::UploadThumbnailRenderMap );
1451 $sizes = $uploadThumbnailRenderMap;
1454 foreach ( $sizes as $size ) {
1455 if ( $this->isMultipage() ) {
1458 $pageLimit = min( $this->pageCount(), self::MAX_PAGE_RENDER_JOBS );
1460 for ( $page = 1; $page <= $pageLimit; $page++ ) {
1463 [
'transformParams' => [
1469 } elseif ( $this->isVectorized() || $this->getWidth() > $size ) {
1472 [
'transformParams' => [
'width' => $size ] ]
1478 MediaWikiServices::getInstance()->getJobQueueGroup()->lazyPush( $jobs );
1489 $fileListDebug = strtr(
1490 var_export( $files,
true ),
1493 wfDebug( __METHOD__ .
": $fileListDebug" );
1495 if ( $this->repo->supportsSha1URLs() ) {
1496 $reference = $this->getSha1();
1498 $reference = $this->getName();
1502 foreach ( $files as
$file ) {
1503 # Check that the reference (filename or sha1) is part of the thumb name
1504 # This is a basic check to avoid erasing unrelated directories
1505 if ( strpos(
$file, $reference ) !==
false
1506 || strpos(
$file,
"-thumbnail" ) !==
false
1508 $purgeList[] =
"{$dir}/{$file}";
1512 # Delete the thumbnails
1513 $this->repo->quickPurgeBatch( $purgeList );
1514 # Clear out the thumbnail directory if empty
1515 $this->repo->quickCleanDir( $dir );
1529 public function getHistory( $limit =
null, $start =
null, $end =
null, $inc =
true ) {
1530 if ( !$this->exists() ) {
1534 $dbr = $this->repo->getReplicaDB();
1537 $tables = $oldFileQuery[
'tables'];
1538 $fields = $oldFileQuery[
'fields'];
1539 $join_conds = $oldFileQuery[
'joins'];
1540 $conds = $opts = [];
1541 $eq = $inc ?
'=' :
'';
1542 $conds[] =
"oi_name = " .
$dbr->addQuotes( $this->title->getDBkey() );
1545 $conds[] =
"oi_timestamp <$eq " .
$dbr->addQuotes(
$dbr->timestamp( $start ) );
1549 $conds[] =
"oi_timestamp >$eq " .
$dbr->addQuotes(
$dbr->timestamp( $end ) );
1553 $opts[
'LIMIT'] = $limit;
1557 $order = ( !$start && $end !== null ) ?
'ASC' :
'DESC';
1558 $opts[
'ORDER BY'] =
"oi_timestamp $order";
1559 $opts[
'USE INDEX'] = [
'oldimage' =>
'oi_name_timestamp' ];
1561 $this->getHookRunner()->onLocalFile__getHistory( $this, $tables, $fields,
1562 $conds, $opts, $join_conds );
1564 $res =
$dbr->select( $tables, $fields, $conds, __METHOD__, $opts, $join_conds );
1567 foreach (
$res as $row ) {
1568 $r[] = $this->repo->newFileFromRow( $row );
1571 if ( $order ==
'ASC' ) {
1572 $r = array_reverse( $r );
1589 if ( !$this->exists() ) {
1593 # Polymorphic function name to distinguish foreign and local fetches
1594 $fname = static::class .
'::' . __FUNCTION__;
1596 $dbr = $this->repo->getReplicaDB();
1598 if ( $this->historyLine == 0 ) {
1599 $fileQuery = self::getQueryInfo();
1600 $this->historyRes =
$dbr->select( $fileQuery[
'tables'],
1601 $fileQuery[
'fields'] + [
1602 'oi_archive_name' =>
$dbr->addQuotes(
'' ),
1605 [
'img_name' => $this->title->getDBkey() ],
1611 if ( $this->historyRes->numRows() == 0 ) {
1612 $this->historyRes =
null;
1616 } elseif ( $this->historyLine == 1 ) {
1618 $this->historyRes =
$dbr->select(
1619 $fileQuery[
'tables'],
1620 $fileQuery[
'fields'],
1621 [
'oi_name' => $this->title->getDBkey() ],
1623 [
'ORDER BY' =>
'oi_timestamp DESC' ],
1627 $this->historyLine++;
1629 return $this->historyRes->fetchObject();
1637 $this->historyLine = 0;
1639 if ( $this->historyRes !==
null ) {
1640 $this->historyRes =
null;
1677 public function upload( $src, $comment, $pageText, $flags = 0, $props =
false,
1678 $timestamp =
false,
Authority $uploader =
null, $tags = [],
1679 $createNullRevision =
true, $revert =
false
1681 if ( $this->getRepo()->getReadOnlyReason() !==
false ) {
1682 return $this->readOnlyFatalStatus();
1683 } elseif ( MediaWikiServices::getInstance()->getRevisionStore()->isReadOnly() ) {
1686 return $this->readOnlyFatalStatus();
1689 $srcPath = ( $src instanceof
FSFile ) ? $src->getPath() : $src;
1694 $props = $this->repo->getFileProps( $srcPath );
1696 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
1697 $props = $mwProps->getPropsFromPath( $srcPath,
true );
1704 if ( is_string( $props[
'metadata'] ) ) {
1711 $metadata = $props[
'metadata'];
1714 if ( is_array( $metadata ) ) {
1715 $options[
'headers'] = $handler->getContentHeaders( $metadata );
1718 $options[
'headers'] = [];
1722 $comment = trim( $comment );
1724 $status = $this->publish( $src, $flags, $options );
1726 if ( $status->successCount >= 2 ) {
1733 $oldver = $status->value;
1735 if ( $uploader ===
null ) {
1740 $uploadStatus = $this->recordUpload3(
1748 $createNullRevision,
1751 if ( !$uploadStatus->isOK() ) {
1752 if ( $uploadStatus->hasMessage(
'filenotfound' ) ) {
1754 $status->fatal(
'filenotfound', $srcPath );
1756 $status->merge( $uploadStatus );
1788 bool $createNullRevision =
true,
1789 bool $revert =
false
1791 $dbw = $this->repo->getPrimaryDB();
1793 # Imports or such might force a certain timestamp; otherwise we generate
1794 # it and can fudge it slightly to keep (name,timestamp) unique on re-upload.
1795 if ( $timestamp ===
false ) {
1796 $timestamp = $dbw->timestamp();
1797 $allowTimeKludge =
true;
1799 $allowTimeKludge =
false;
1802 $props = $props ?: $this->repo->getFileProps( $this->getVirtualUrl() );
1803 $props[
'description'] = $comment;
1804 $props[
'timestamp'] =
wfTimestamp( TS_MW, $timestamp );
1805 $this->setProps( $props );
1807 # Fail now if the file isn't there
1808 if ( !$this->fileExists ) {
1809 wfDebug( __METHOD__ .
": File " . $this->getRel() .
" went missing!" );
1814 $actorNormalizaton = MediaWikiServices::getInstance()->getActorNormalization();
1816 $dbw->startAtomic( __METHOD__ );
1818 $actorId = $actorNormalizaton->acquireActorId( $performer->
getUser(), $dbw );
1819 $this->user = $performer->
getUser();
1821 # Test to see if the row exists using INSERT IGNORE
1822 # This avoids race conditions by locking the row until the commit, and also
1823 # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
1824 $commentStore = MediaWikiServices::getInstance()->getCommentStore();
1825 $commentFields = $commentStore->insert( $dbw,
'img_description', $comment );
1826 $actorFields = [
'img_actor' => $actorId ];
1827 $dbw->insert(
'image',
1829 'img_name' => $this->getName(),
1830 'img_size' => $this->size,
1831 'img_width' => intval( $this->width ),
1832 'img_height' => intval( $this->height ),
1833 'img_bits' => $this->bits,
1834 'img_media_type' => $this->media_type,
1835 'img_major_mime' => $this->major_mime,
1836 'img_minor_mime' => $this->minor_mime,
1837 'img_timestamp' => $timestamp,
1838 'img_metadata' => $this->getMetadataForDb( $dbw ),
1839 'img_sha1' => $this->sha1
1840 ] + $commentFields + $actorFields,
1844 $reupload = ( $dbw->affectedRows() == 0 );
1847 $row = $dbw->selectRow(
1849 [
'img_timestamp',
'img_sha1' ],
1850 [
'img_name' => $this->getName() ],
1852 [
'LOCK IN SHARE MODE' ]
1855 if ( $row && $row->img_sha1 === $this->sha1 ) {
1856 $dbw->endAtomic( __METHOD__ );
1857 wfDebug( __METHOD__ .
": File " . $this->getRel() .
" already exists!" );
1862 if ( $allowTimeKludge ) {
1863 # Use LOCK IN SHARE MODE to ignore any transaction snapshotting
1864 $lUnixtime = $row ? (int)
wfTimestamp( TS_UNIX, $row->img_timestamp ) : false;
1865 # Avoid a timestamp that is not newer than the last version
1866 # TODO: the image/oldimage tables should be like page/revision with an ID field
1867 if ( $lUnixtime && (
int)
wfTimestamp( TS_UNIX, $timestamp ) <= $lUnixtime ) {
1869 $timestamp = $dbw->timestamp( $lUnixtime + 1 );
1870 $this->timestamp =
wfTimestamp( TS_MW, $timestamp );
1874 $tables = [
'image' ];
1876 'oi_name' =>
'img_name',
1877 'oi_archive_name' => $dbw->addQuotes( $oldver ),
1878 'oi_size' =>
'img_size',
1879 'oi_width' =>
'img_width',
1880 'oi_height' =>
'img_height',
1881 'oi_bits' =>
'img_bits',
1882 'oi_description_id' =>
'img_description_id',
1883 'oi_timestamp' =>
'img_timestamp',
1884 'oi_metadata' =>
'img_metadata',
1885 'oi_media_type' =>
'img_media_type',
1886 'oi_major_mime' =>
'img_major_mime',
1887 'oi_minor_mime' =>
'img_minor_mime',
1888 'oi_sha1' =>
'img_sha1',
1889 'oi_actor' =>
'img_actor',
1893 # (T36993) Note: $oldver can be empty here, if the previous
1894 # version of the file was broken. Allow registration of the new
1895 # version to continue anyway, because that's better than having
1896 # an image that's not fixable by user operations.
1897 # Collision, this is an update of a file
1898 # Insert previous contents into oldimage
1899 $dbw->insertSelect(
'oldimage', $tables, $fields,
1900 [
'img_name' => $this->getName() ], __METHOD__, [], [], $joins );
1902 # Update the current image row
1903 $dbw->update(
'image',
1905 'img_size' => $this->size,
1906 'img_width' => intval( $this->width ),
1907 'img_height' => intval( $this->height ),
1908 'img_bits' => $this->bits,
1909 'img_media_type' => $this->media_type,
1910 'img_major_mime' => $this->major_mime,
1911 'img_minor_mime' => $this->minor_mime,
1912 'img_timestamp' => $timestamp,
1913 'img_metadata' => $this->getMetadataForDb( $dbw ),
1914 'img_sha1' => $this->sha1
1915 ] + $commentFields + $actorFields,
1916 [
'img_name' => $this->getName() ],
1922 $descId = $descTitle->getArticleID();
1924 $wikiPage->setFile( $this );
1928 $logAction =
'revert';
1929 } elseif ( $reupload ) {
1930 $logAction =
'overwrite';
1932 $logAction =
'upload';
1936 $logEntry->setTimestamp( $this->timestamp );
1937 $logEntry->setPerformer( $performer->
getUser() );
1938 $logEntry->setComment( $comment );
1939 $logEntry->setTarget( $descTitle );
1942 $logEntry->setParameters(
1944 'img_sha1' => $this->sha1,
1945 'img_timestamp' => $timestamp,
1954 $logId = $logEntry->insert();
1956 if ( $descTitle->exists() ) {
1957 if ( $createNullRevision ) {
1958 $revStore = MediaWikiServices::getInstance()->getRevisionStore();
1962 $editSummary = $formatter->getPlainActionText();
1964 $nullRevRecord =
$revStore->newNullRevision(
1972 if ( $nullRevRecord ) {
1973 $inserted =
$revStore->insertRevisionOn( $nullRevRecord, $dbw );
1975 $this->getHookRunner()->onRevisionFromEditComplete(
1978 $inserted->getParentId(),
1983 $wikiPage->updateRevisionOn( $dbw, $inserted );
1985 $logEntry->setAssociatedRevId( $inserted->getId() );
1989 $newPageContent =
null;
1998 $dbw->endAtomic( __METHOD__ );
1999 $fname = __METHOD__;
2001 # Do some cache purges after final commit so that:
2002 # a) Changes are more likely to be seen post-purge
2003 # b) They won't cause rollback of the log publish/update above
2009 $reupload, $wikiPage, $newPageContent, $comment, $performer,
2010 $logEntry, $logId, $descId, $tags, $fname
2012 # Update memcache after the commit
2013 $this->invalidateCache();
2015 $updateLogPage =
false;
2016 if ( $newPageContent ) {
2017 # New file page; create the description page.
2018 # There's already a log entry, so don't make a second RC entry
2019 # CDN and file cache for the description page are purged by doUserEditContent.
2020 $status = $wikiPage->doUserEditContent(
2027 if ( isset( $status->value[
'revision-record'] ) ) {
2029 $revRecord = $status->value[
'revision-record'];
2031 $logEntry->setAssociatedRevId( $revRecord->getId() );
2035 if ( isset( $status->value[
'revision-record'] ) ) {
2037 $revRecord = $status->value[
'revision-record'];
2038 $updateLogPage = $revRecord->getPageId();
2041 # Existing file page: invalidate description page cache
2042 $title = $wikiPage->getTitle();
2043 $title->invalidateCache();
2044 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2045 $hcu->purgeTitleUrls(
$title, $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2046 # Allow the new file version to be patrolled from the page footer
2050 # Update associated rev id. This should be done by $logEntry->insert() earlier,
2051 # but setAssociatedRevId() wasn't called at that point yet...
2052 $logParams = $logEntry->getParameters();
2053 $logParams[
'associated_rev_id'] = $logEntry->getAssociatedRevId();
2055 if ( $updateLogPage ) {
2056 # Also log page, in case where we just created it above
2057 $update[
'log_page'] = $updateLogPage;
2059 $this->getRepo()->getPrimaryDB()->update(
2062 [
'log_id' => $logId ],
2065 $this->getRepo()->getPrimaryDB()->insert(
2068 'ls_field' =>
'associated_rev_id',
2069 'ls_value' => (
string)$logEntry->getAssociatedRevId(),
2070 'ls_log_id' => $logId,
2075 # Add change tags, if any
2077 $logEntry->addTags( $tags );
2080 # Uploads can be patrolled
2081 $logEntry->setIsPatrollable(
true );
2083 # Now that the log entry is up-to-date, make an RC entry.
2084 $logEntry->publish( $logId );
2086 # Run hook for other updates (typically more cache purging)
2087 $this->getHookRunner()->onFileUpload( $this, $reupload, !$newPageContent );
2090 # Delete old thumbnails
2091 $this->purgeThumbnails();
2092 # Remove the old file from the CDN cache
2093 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2094 $hcu->purgeUrls( $this->getUrl(), $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2096 # Update backlink pages pointing to this title if created
2097 $blcFactory = MediaWikiServices::getInstance()->getBacklinkCacheFactory();
2098 LinksUpdate::queueRecursiveJobsForTable(
2102 $performer->
getUser()->getName(),
2103 $blcFactory->getBacklinkCache( $this->getTitle() )
2107 $this->prerenderThumbnails();
2111 # Invalidate cache for all pages using this file
2115 [
'causeAction' =>
'file-upload',
'causeAgent' => $performer->
getUser()->getName() ]
2123 $dbw->onTransactionCommitOrIdle(
static function () use ( $reupload, $purgeUpdate, $cacheUpdateJob ) {
2131 MediaWikiServices::getInstance()->getJobQueueGroup()->lazyPush( $cacheUpdateJob );
2153 public function publish( $src, $flags = 0, array $options = [] ) {
2154 return $this->publishTo( $src, $this->getRel(), $flags, $options );
2173 protected function publishTo( $src, $dstRel, $flags = 0, array $options = [] ) {
2174 $srcPath = ( $src instanceof
FSFile ) ? $src->getPath() : $src;
2176 $repo = $this->getRepo();
2177 if ( $repo->getReadOnlyReason() !==
false ) {
2178 return $this->readOnlyFatalStatus();
2181 $status = $this->acquireFileLock();
2182 if ( !$status->isOK() ) {
2186 if ( $this->isOld() ) {
2187 $archiveRel = $dstRel;
2188 $archiveName = basename( $archiveRel );
2190 $archiveName =
wfTimestamp( TS_MW ) .
'!' . $this->getName();
2191 $archiveRel = $this->getArchiveRel( $archiveName );
2194 if ( $repo->hasSha1Storage() ) {
2196 ? $repo->getFileSha1( $srcPath )
2199 $wrapperBackend = $repo->getBackend();
2200 '@phan-var FileBackendDBRepoWrapper $wrapperBackend';
2201 $dst = $wrapperBackend->getPathForSHA1( $sha1 );
2202 $status = $repo->quickImport( $src, $dst );
2207 if ( $this->exists() ) {
2208 $status->value = $archiveName;
2212 $status = $repo->publish( $srcPath, $dstRel, $archiveRel, $flags, $options );
2214 if ( $status->value ==
'new' ) {
2215 $status->value =
'';
2217 $status->value = $archiveName;
2221 $this->releaseFileLock();
2244 $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
2245 if ( $this->getRepo()->getReadOnlyReason() !==
false ) {
2246 return $this->readOnlyFatalStatus();
2249 wfDebugLog(
'imagemove',
"Got request to move {$this->name} to " . $target->getText() );
2252 $status = $batch->addCurrent();
2253 if ( !$status->isOK() ) {
2256 $archiveNames = $batch->addOlds();
2257 $status = $batch->execute();
2259 wfDebugLog(
'imagemove',
"Finished moving {$this->name}" );
2262 $oldTitleFile = $localRepo->newFile( $this->title );
2263 $newTitleFile = $localRepo->newFile( $target );
2266 $this->getRepo()->getPrimaryDB(),
2268 static function () use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
2269 $oldTitleFile->purgeEverything();
2270 foreach ( $archiveNames as $archiveName ) {
2272 '@phan-var OldLocalFile $oldTitleFile';
2273 $oldTitleFile->purgeOldThumbnails( $archiveName );
2275 $newTitleFile->purgeEverything();
2278 DeferredUpdates::PRESEND
2281 if ( $status->isOK() ) {
2283 $this->title = $target;
2286 $this->hashPath =
null;
2309 if ( $this->getRepo()->getReadOnlyReason() !==
false ) {
2310 return $this->readOnlyFatalStatus();
2315 $batch->addCurrent();
2317 $archiveNames = $batch->addOlds();
2318 $status = $batch->execute();
2320 if ( $status->isOK() ) {
2327 $this->getRepo()->getPrimaryDB(),
2329 function () use ( $archiveNames ) {
2330 $this->purgeEverything();
2331 foreach ( $archiveNames as $archiveName ) {
2332 $this->purgeOldThumbnails( $archiveName );
2336 DeferredUpdates::PRESEND
2341 foreach ( $archiveNames as $archiveName ) {
2342 $purgeUrls[] = $this->getArchiveUrl( $archiveName );
2345 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2346 $hcu->purgeUrls( $purgeUrls, $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2370 if ( $this->getRepo()->getReadOnlyReason() !==
false ) {
2371 return $this->readOnlyFatalStatus();
2376 $batch->addOld( $archiveName );
2377 $status = $batch->execute();
2379 $this->purgeOldThumbnails( $archiveName );
2380 if ( $status->isOK() ) {
2381 $this->purgeDescription();
2384 $url = $this->getArchiveUrl( $archiveName );
2385 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2386 $hcu->purgeUrls( $url, $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2403 public function restore( $versions = [], $unsuppress =
false ) {
2404 if ( $this->getRepo()->getReadOnlyReason() !==
false ) {
2405 return $this->readOnlyFatalStatus();
2413 $batch->addIds( $versions );
2415 $status = $batch->execute();
2416 if ( $status->isGood() ) {
2417 $cleanupStatus = $batch->cleanup();
2418 $cleanupStatus->successCount = 0;
2419 $cleanupStatus->failCount = 0;
2420 $status->merge( $cleanupStatus );
2437 if ( !$this->title ) {
2441 return $this->title->getLocalURL();
2454 if ( !$this->title ) {
2458 $services = MediaWikiServices::getInstance();
2459 $page = $services->getPageStore()->getPageByReference( $this->
getTitle() );
2473 $parseStatus = $services->getParserOutputAccess()
2474 ->getParserOutput( $page, $parserOptions );
2476 if ( !$parseStatus->isGood() ) {
2480 return $parseStatus->getValue()->getText();
2492 if ( $audience === self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
2494 } elseif ( $audience === self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $performer ) ) {
2509 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) {
2511 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_COMMENT, $performer ) ) {
2514 return $this->description;
2525 return $this->timestamp;
2533 if ( !$this->exists() ) {
2540 if ( $this->descriptionTouched ===
null ) {
2542 'page_namespace' => $this->title->getNamespace(),
2543 'page_title' => $this->title->getDBkey()
2545 $touched = $this->repo->getReplicaDB()->selectField(
'page',
'page_touched', $cond, __METHOD__ );
2546 $this->descriptionTouched = $touched ?
wfTimestamp( TS_MW, $touched ) :
false;
2549 return $this->descriptionTouched;
2568 return $this->extraDataLoaded
2569 && strlen(
serialize( $this->metadataArray ) ) <= self::CACHE_FIELD_MAX_LEN;
2581 return Status::wrap( $this->getRepo()->getBackend()->lockFiles(
2593 return Status::wrap( $this->getRepo()->getBackend()->unlockFiles(
2609 if ( !$this->locked ) {
2610 $logger = LoggerFactory::getInstance(
'LocalFile' );
2612 $dbw = $this->repo->getPrimaryDB();
2613 $makesTransaction = !$dbw->trxLevel();
2614 $dbw->startAtomic( self::ATOMIC_SECTION_LOCK );
2618 $status = $this->acquireFileLock( 10 );
2619 if ( !$status->isGood() ) {
2620 $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
2621 $logger->warning(
"Failed to lock '{file}'", [
'file' => $this->name ] );
2627 $dbw->onTransactionResolution(
2628 function () use ( $logger ) {
2629 $status = $this->releaseFileLock();
2630 if ( !$status->isGood() ) {
2631 $logger->error(
"Failed to unlock '{file}'", [
'file' => $this->name ] );
2637 $this->lockedOwnTrx = $makesTransaction;
2642 return $this->lockedOwnTrx;
2656 if ( $this->locked ) {
2658 if ( !$this->locked ) {
2659 $dbw = $this->repo->getPrimaryDB();
2660 $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
2661 $this->lockedOwnTrx =
false;
2670 return $this->getRepo()->newFatal(
'filereadonlyerror', $this->getName(),
2671 $this->getRepo()->getName(), $this->getRepo()->getReadOnlyReason() );
unserialize( $serialized)
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
static purgePatrolFooterCache( $articleID)
Purge the cache used to check if it is worth showing the patrol footer For example,...
Deferrable Update for closure/callback updates that should use auto-commit mode.
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the pending update queue for execution at the appropriate time.
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add an update to the pending update queue that invokes the specified callback when run.
Class representing a non-directory file on the file system.
static getSha1Base36FromPath( $path)
Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case encoding,...
File backend exception for checked exceptions (e.g.
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
static isVirtualUrl( $url)
Determine if a string is an mwrepo:// URL.
Implements some public methods and some protected utility functions which are required by multiple ch...
string $url
The URL corresponding to one of the four basic zones.
assertRepoDefined()
Assert that $this->repo is set to a valid FileRepo instance.
getName()
Return the name of this file.
getVirtualUrl( $suffix=false)
Get the public zone virtual URL for a current version source file.
assertTitleDefined()
Assert that $this->title is set to a Title.
isMultipage()
Returns 'true' if this file is a type which supports multiple pages, e.g.
FileRepo LocalRepo ForeignAPIRepo bool $repo
Some member variables can be lazy-initialised using __get().
string $path
The storage path corresponding to one of the zones.
static splitMime( $mime)
Split an internet media type into its two components; if not a two-part name, set the minor type to '...
getHandler()
Get a MediaHandler instance for this file.
string null $name
The name of a file from its title object.
static newForBacklinks(PageReference $page, $table, $params=[])
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Helper class for file deletion.
Helper class for file movement.
Helper class for file undeletion.
Local file in the wiki's own database.
exists()
canRender inherited
setProps( $info)
Set properties in this object to be equal to those given in the associative array $info.
string $major_mime
Major MIME type.
maybeUpgradeRow()
Upgrade a row if it needs it.
static newFromKey( $sha1, $repo, $timestamp=false)
Create a LocalFile from a SHA-1 key Do not call this except from inside a repo class.
array $metadataArray
Unserialized metadata.
getMediaType()
Returns the type of the media in the file.
string[] $unloadedMetadataBlobs
Map of metadata item name to blob address for items that exist but have not yet been loaded into $thi...
deleteOldFile( $archiveName, $reason, UserIdentity $user, $suppress=false)
Delete an old version of the file.
move( $target)
getLinksTo inherited
lock()
Start an atomic DB section and lock the image for update or increments a reference counter if the loc...
loadFromRow( $row, $prefix='img_')
Load file metadata from a DB result row.
getCacheKey()
Get the memcached key for the main data for this file, or false if there is no access to the shared c...
getWidth( $page=1)
Return the width of the image @stable to override.
__destruct()
Clean up any dangling locks.
string $mime
MIME type, determined by MimeAnalyzer::guessMimeType.
reserializeMetadata()
Write the metadata back to the database with the current serialization format.
isMissing()
splitMime inherited
isMetadataOversize()
Determine whether the loaded metadata may be a candidate for splitting, by measuring its serialized s...
getDescriptionUrl()
isMultipage inherited
getHistory( $limit=null, $start=null, $end=null, $inc=true)
purgeDescription inherited
getMutableCacheKeys(WANObjectCache $cache)
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new localfile object.
releaseFileLock()
Release a lock acquired with acquireFileLock().
getUploader(int $audience=self::FOR_PUBLIC, Authority $performer=null)
loadMetadataFromDbFieldValue(IDatabase $db, $metadataBlob)
Unserialize a metadata blob which came from the database and store it in $this.
loadFromDB( $flags=0)
Load file metadata from the DB.
load( $flags=0)
Load file metadata from cache or DB, unless already loaded.
loadMetadataFromString( $metadataString)
Unserialize a metadata string which came from some non-DB source, or is the return value of IDatabase...
bool $upgrading
Whether the row was scheduled to upgrade on load.
string $media_type
MEDIATYPE_xxx (bitmap, drawing, audio...)
deleteFile( $reason, UserIdentity $user, $suppress=false)
Delete all versions of the file.
acquireFileLock( $timeout=0)
Acquire an exclusive lock on the file, indicating an intention to write to the file backend.
purgeCache( $options=[])
Delete all previously generated thumbnails, refresh metadata in memcached and purge the CDN.
loadFromFile( $path=null)
Load metadata from the file itself.
getBitDepth()
@stable to override
string null $metadataSerializationFormat
One of the MDS_* constants, giving the format of the metadata as stored in the DB,...
int $size
Size in bytes (loadFromXxx)
string $minor_mime
Minor MIME type.
getDescriptionShortUrl()
Get short description URL for a file based on the page ID.
getThumbnails( $archiveName=false)
getTransformScript inherited
bool $upgraded
Whether the row was upgraded on load.
static newFromTitle( $title, $repo, $unused=null)
Create a LocalFile from a title Do not call this except from inside a repo class.
MetadataStorageHelper $metadataStorageHelper
purgeMetadataCache()
Refresh metadata in memcached, but don't touch thumbnails or CDN.
getMetadataForDb(IDatabase $db)
Serialize the metadata array for insertion into img_metadata, oi_metadata or fa_metadata.
string $timestamp
Upload timestamp.
const ATOMIC_SECTION_LOCK
purgeOldThumbnails( $archiveName)
Delete cached transformed files for an archived version only.
publishTo( $src, $dstRel, $flags=0, array $options=[])
Move or copy a file to a specified location.
UserIdentity null $user
Uploader.
purgeThumbList( $dir, $files)
Delete a list of thumbnails visible at urls.
string $description
Description of current revision of the file.
const CACHE_FIELD_MAX_LEN
unlock()
Decrement the lock reference count and end the atomic section if it reaches zero.
IResultWrapper null $historyRes
Result of the query for the file's history (nextHistoryLine)
getLazyCacheFields( $prefix='img_')
Returns the list of object properties that are included as-is in the cache, only when they're not too...
getSize()
Returns the size of the image file, in bytes @stable to override.
loadExtraFieldsWithTimestamp( $dbr, $fname)
invalidateCache()
Purge the file object/metadata cache.
getMimeType()
Returns the MIME type of the file.
bool $extraDataLoaded
Whether or not lazy-loaded data has been loaded from the database.
int $historyLine
Number of line to return by nextHistoryLine() (constructor)
string $sha1
SHA-1 base 36 content hash.
getDescription( $audience=self::FOR_PUBLIC, Authority $performer=null)
getHeight( $page=1)
Return the height of the image @stable to override.
prerenderThumbnails()
Prerenders a configurable set of thumbnails.
bool $lockedOwnTrx
True if the image row is locked with a lock initiated transaction.
resetHistory()
Reset the history pointer to the first element of the history.
unprefixRow( $row, $prefix='img_')
static newFromRow( $row, $repo)
Create a LocalFile from a title Do not call this except from inside a repo class.
getJsonMetadata()
Get metadata in JSON format ready for DB insertion, optionally splitting items out to BlobStore.
publish( $src, $flags=0, array $options=[])
Move or copy a file to its public location.
restore( $versions=[], $unsuppress=false)
Restore all or specified deleted revisions to the given file.
getCacheFields( $prefix='img_')
Returns the list of object properties that are included as-is in the cache.
int $locked
If >= 1 the image row is locked.
int $bits
Returned by getimagesize (loadFromXxx)
getMetadataItems(array $itemNames)
Get multiple elements of the unserialized handler-specific metadata.
bool $missing
True if file is not present in file system.
getDescriptionText(Language $lang=null)
Get the HTML text of the description page This is not used by ImagePage for local files,...
purgeThumbnails( $options=[])
Delete cached transformed files for the current version only.
loadExtraFromDB()
Load lazy file metadata from the DB.
nextHistoryLine()
Returns the history of this file, line by line.
upgradeRow()
Fix assorted version-related problems with the image row by reloading it from the file.
int $deleted
Bitfield akin to rev_deleted.
loadFromCache()
Try to load file metadata from memcached, falling back to the database.
string $descriptionTouched
TS_MW timestamp of the last change of the file description.
getMetadata()
Get handler-specific metadata as a serialized string.
getMetadataArray()
Get unserialized handler-specific metadata.
__construct( $title, $repo)
Do not call this except from inside a repo class.
bool $dataLoaded
Whether or not core data has been loaded from the database (loadFromXxx)
bool $fileExists
Does the file exist on disk? (loadFromXxx)
upload( $src, $comment, $pageText, $flags=0, $props=false, $timestamp=false, Authority $uploader=null, $tags=[], $createNullRevision=true, $revert=false)
getHashPath inherited
recordUpload3(string $oldver, string $comment, string $pageText, Authority $performer, $props=false, $timestamp=false, $tags=[], bool $createNullRevision=true, bool $revert=false)
Record a file upload in the upload log and the image table (version 3)
string[] $metadataBlobs
Map of metadata item name to blob address.
static makeParamBlob( $params)
Create a blob from a parameter array.
MimeMagic helper wrapper.
Class for creating new log entries and inserting them into the database.
A class containing constants representing the names of configuration variables.
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new oldlocalfile object.
static newFromUserAndLang(UserIdentity $user, Language $lang)
Get a ParserOptions object from a given user and language.
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
static newExtraneousContext(Title $title, $request=[])
Create a new extraneous context.
static getMain()
Get the RequestContext object associated with the main request.
static factory(array $deltas)
static newFatal( $message,... $parameters)
Factory function for fatal errors.
static newGood( $value=null)
Factory function for good results.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
Job for asynchronous rendering of thumbnails.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Multi-datacenter aware caching interface.
Special handling for representing file pages.
foreach( $mmfl['setupFiles'] as $fileName) if( $queue) if(empty( $mmfl['quiet'])) $s
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!isset( $args[0])) $lang