79 parent::__construct( $info );
81 $this->dbDomain = WikiMap::getCurrentWikiDbDomain();
82 $this->hasAccessibleSharedCache =
true;
84 $this->
hasSha1Storage = ( $info[
'storageLayout'] ?? null ) ===
'sha1';
88 'backend' => $this->backend,
89 'repoName' => $this->name,
98 'splitMetadataThreshold',
99 'updateCompatibleMetadata',
100 'reserializeMetadata',
103 if ( isset( $info[$option] ) ) {
104 $this->$option = $info[$option];
115 if ( isset( $row->img_name ) ) {
116 return call_user_func( $this->fileFromRowFactory, $row, $this );
117 } elseif ( isset( $row->oi_name ) ) {
118 return call_user_func( $this->oldFileFromRowFactory, $row, $this );
120 throw new MWException( __METHOD__ .
': invalid row' );
131 return OldLocalFile::newFromArchiveName(
$title, $this, $archiveName );
146 wfDebug( __METHOD__ .
": skipped because storage uses sha1 paths" );
147 return Status::newGood();
154 $storageKeys = array_unique( $storageKeys );
155 foreach ( $storageKeys as $key ) {
157 $path =
"$root/$hashPath$key";
158 $dbw->startAtomic( __METHOD__ );
163 if ( !$deleted && !$hidden ) {
164 wfDebug( __METHOD__ .
": deleting $key" );
165 $op = [
'op' =>
'delete',
'src' =>
$path ];
167 $status->error(
'undelete-cleanup-error',
$path );
168 $status->failCount++;
171 wfDebug( __METHOD__ .
": $key still in use" );
172 $status->successCount++;
174 $dbw->endAtomic( __METHOD__ );
189 return (
bool)$dbw->selectField(
'filearchive',
'1',
190 [
'fa_storage_group' =>
'deleted',
'fa_storage_key' => $key ],
192 $lock ===
'lock' ? [
'FOR UPDATE' ] : []
204 $sha1 = self::getHashFromKey( $key );
205 $ext = File::normalizeExtension( substr( $key, strcspn( $key,
'.' ) + 1 ) );
208 return (
bool)$dbw->selectField(
'oldimage',
'1',
211 'oi_archive_name ' . $dbw->buildLike( $dbw->anyString(),
".$ext" ),
212 $dbw->bitAnd(
'oi_deleted', File::DELETED_FILE ) => File::DELETED_FILE,
215 $lock ===
'lock' ? [
'FOR UPDATE' ] : []
226 $sha1 = strtok( $key,
'.' );
227 if ( is_string( $sha1 ) && strlen( $sha1 ) === 32 && $sha1[0] ===
'0' ) {
228 $sha1 = substr( $sha1, 1 );
243 if ( $memcKey ===
false ) {
250 $method = __METHOD__;
251 $redirDbKey = $this->wanCache->getWithSetCallback(
254 function ( $oldValue, &$ttl, array &$setOpts ) use ( $method,
$title ) {
257 $setOpts += Database::getCacheSetOptions(
$dbr );
259 $row =
$dbr->selectRow(
260 [
'page',
'redirect' ],
261 [
'rd_namespace',
'rd_title' ],
263 'page_namespace' =>
$title->getNamespace(),
264 'page_title' =>
$title->getDBkey(),
270 return ( $row && $row->rd_namespace ==
NS_FILE )
271 ? Title::makeTitle( $row->rd_namespace, $row->rd_title )->getDBkey()
274 [
'pcTTL' => WANObjectCache::TTL_PROC_LONG ]
278 if ( $redirDbKey !==
' ' && strval( $redirDbKey ) !==
'' ) {
280 return Title::newFromText( $redirDbKey,
NS_FILE );
290 foreach ( $items as $item ) {
291 if ( is_array( $item ) ) {
292 $title = File::normalizeTitle( $item[
'title'] );
294 $searchSet[
$title->getDBkey()] = $item;
297 $title = File::normalizeTitle( $item );
299 $searchSet[
$title->getDBkey()] = [];
304 $fileMatchesSearch =
static function (
File $file, array $search ) {
310 $contextPerformer = RequestContext::getMain()->getAuthority();
311 $performer = ( !empty( $search[
'private'] ) && $search[
'private'] instanceof
Authority )
318 ( empty( $search[
'time'] ) && !
$file->isOld() ) ||
319 ( !empty( $search[
'time'] ) && $search[
'time'] ===
$file->getTimestamp() )
321 ( !empty( $search[
'private'] ) || !
$file->isDeleted( File::DELETED_FILE ) ) &&
322 $file->userCan( File::DELETED_FILE, $performer )
327 use ( $fileMatchesSearch, $flags )
329 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
331 foreach (
$res as $row ) {
335 $dbKeysLook = [ strtr(
$file->getName(),
' ',
'_' ) ];
336 if ( !empty( $info[
'initialCapital'] ) ) {
338 $dbKeysLook[] = $contLang->lcfirst(
$file->getName() );
340 foreach ( $dbKeysLook as $dbKey ) {
341 if ( isset( $searchSet[$dbKey] )
342 && $fileMatchesSearch(
$file, $searchSet[$dbKey] )
345 ? [
'title' => $dbKey,
'timestamp' =>
$file->getTimestamp() ]
347 unset( $searchSet[$dbKey] );
357 foreach ( array_keys( $searchSet ) as $dbKey ) {
361 if ( count( $imgNames ) ) {
362 $fileQuery = LocalFile::getQueryInfo();
363 $res =
$dbr->select( $fileQuery[
'tables'], $fileQuery[
'fields'], [
'img_name' => $imgNames ],
364 __METHOD__, [], $fileQuery[
'joins'] );
365 $applyMatchingFiles(
$res, $searchSet, $finalFiles );
370 foreach ( $searchSet as $dbKey => $search ) {
371 if ( isset( $search[
'time'] ) ) {
372 $oiConds[] =
$dbr->makeList(
375 'oi_timestamp' =>
$dbr->timestamp( $search[
'time'] )
382 if ( count( $oiConds ) ) {
383 $fileQuery = OldLocalFile::getQueryInfo();
384 $res =
$dbr->select( $fileQuery[
'tables'], $fileQuery[
'fields'],
386 __METHOD__, [], $fileQuery[
'joins'] );
387 $applyMatchingFiles(
$res, $searchSet, $finalFiles );
391 foreach ( $searchSet as $dbKey => $search ) {
392 if ( !empty( $search[
'ignoreRedirect'] ) ) {
396 $title = File::normalizeTitle( $dbKey );
399 if ( $redir && $redir->getNamespace() ===
NS_FILE ) {
401 if (
$file && $fileMatchesSearch(
$file, $search ) ) {
404 $finalFiles[$dbKey] = [
405 'title' =>
$file->getTitle()->getDBkey(),
406 'timestamp' =>
$file->getTimestamp()
409 $finalFiles[$dbKey] =
$file;
427 $fileQuery = LocalFile::getQueryInfo();
429 $fileQuery[
'tables'],
430 $fileQuery[
'fields'],
431 [
'img_sha1' => $hash ],
433 [
'ORDER BY' =>
'img_name' ],
438 foreach (
$res as $row ) {
461 $fileQuery = LocalFile::getQueryInfo();
463 $fileQuery[
'tables'],
464 $fileQuery[
'fields'],
467 [
'ORDER BY' =>
'img_name' ],
472 foreach (
$res as $row ) {
489 $selectOptions = [
'ORDER BY' =>
'img_name',
'LIMIT' => intval( $limit ) ];
493 $fileQuery = LocalFile::getQueryInfo();
495 $fileQuery[
'tables'],
496 $fileQuery[
'fields'],
497 'img_name ' .
$dbr->buildLike( $prefix,
$dbr->anyString() ),
505 foreach (
$res as $row ) {
534 return static function ( $index ) {
546 return $this->hasAccessibleSharedCache;
552 ? $this->wanCache->makeGlobalKey(
553 'filerepo-' . $kClassSuffix,
570 function () use ( $key ) {
571 $this->wanCache->delete( $key );
578 public function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
604 public function delete( $srcRel, $archiveRel ) {
623 wfDebug( __METHOD__ .
": skipped because storage uses sha1 paths" );
624 return Status::newGood();
626 return parent::$function( ...$args );
640 return $this->useJsonMetadata;
660 return $this->splitMetadataThreshold;
664 return $this->updateCompatibleMetadata;
668 return $this->reserializeMetadata;
678 if ( !$this->blobStore ) {
679 $this->blobStore = MediaWikiServices::getInstance()->getBlobStoreFactory()
680 ->newBlobStore( $this->dbDomain );
682 return $this->blobStore;
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Proxy backend that manages file layout rewriting for FileRepo.
doOperation(array $op, array $opts=[])
Same as doOperations() except it takes a single operation.
Base class for file repositories.
assertWritableRepo()
Throw an exception if this repo is read-only by design.
newGood( $value=null)
Create a new good result.
getLocalCacheKey( $kClassSuffix,... $components)
Get a site-local, repository-qualified, WAN cache key.
hasSha1Storage()
Returns whether or not storage is SHA-1 based.
getZonePath( $zone)
Get the storage path corresponding to one of the zones.
getDeletedHashPath( $key)
Get a relative path for a deletion archive key, e.g.
getNameFromTitle( $title)
Get the name of a file from its title.
newFile( $title, $time=false)
Create a new File object from the local repository.
getInfo()
Return information about the repository.
Implements some public methods and some protected utility functions which are required by multiple ch...
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
skipWriteOperationIfSha1( $function, array $args)
Skips the write operation if storage is sha1-based, executes it normally otherwise.
int null $splitMetadataThreshold
getDBFactory()
Get a callback to get a DB handle given an index (DB_REPLICA/DB_PRIMARY)
getSharedCacheKey( $kClassSuffix,... $components)
Get a global, repository-qualified, WAN cache key.
isMetadataUpdateEnabled()
isSplitMetadataEnabled()
Returns true if files should split up large metadata, storing parts of it in the BlobStore.
deletedFileHasKey( $key, $lock=null)
Check if a deleted (filearchive) file has this sha1 key.
callable $oldFileFactoryKey
isJsonMetadataEnabled()
Returns true if files should store metadata in JSON format.
cleanupBatch(array $files, $flags=0)
Deletes a batch of files.
publishBatch(array $ntuples, $flags=0)
Publish a batch of files.
findFiles(array $items, $flags=0)
Find many files at once.
findFilesByPrefix( $prefix, $limit)
Return an array of files where the name starts with $prefix.
findBySha1s(array $hashes)
Get an array of arrays or iterators of file objects for files that have the given SHA-1 content hashe...
getBlobStore()
Get a BlobStore for storing and retrieving large metadata, or null if that can't be done.
callable $oldFileFromRowFactory
string $dbDomain
DB domain of the repo wiki.
invalidateImageRedirect( $title)
Invalidates image redirect cache related to that image.
cleanupDeletedBatch(array $storageKeys)
Delete files in the deleted directory if they are not referenced in the filearchive table.
bool $updateCompatibleMetadata
getPrimaryDB()
Get a connection to the primary DB.
checkRedirect( $title)
Checks if there is a redirect named as $title.
hasAcessibleSharedCache()
Check whether the repo has a shared cache, accessible from the current site context.
bool $hasAccessibleSharedCache
Whether shared cache keys are exposed/accessible.
getReplicaDB()
Get a connection to the replica DB.
store( $srcPath, $dstZone, $dstRel, $flags=0)
Store a file to a given destination.
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...
storeBatch(array $triplets, $flags=0)
Store a batch of files.
getSplitMetadataThreshold()
Get the threshold above which metadata items should be split into separate storage,...
callable $fileFromRowFactory
__construct(array $info=null)
deleteBatch(array $sourceDestPairs)
Move a group of files to the deletion archive.
hiddenFileHasKey( $key, $lock=null)
Check if a hidden (revision delete) file has this sha1 key.
static getHashFromKey( $key)
Gets the SHA1 hash from a storage key.
newFromArchiveName( $title, $archiveName)
bool $reserializeMetadata
isMetadataReserializeEnabled()
findBySha1( $hash)
Get an array or iterator of file objects for files that have a given SHA-1 content hash.
Interface for objects (potentially) representing an editable wiki page.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!is_readable( $file)) $ext