10use InvalidArgumentException;
84 parent::__construct( $info );
86 $this->dbDomain = WikiMap::getCurrentWikiDbDomain()->getId();
87 $this->hasAccessibleSharedCache =
true;
89 $this->
hasSha1Storage = ( $info[
'storageLayout'] ?? null ) ===
'sha1';
94 'backend' => $this->backend,
95 'repoName' => $this->name,
104 'splitMetadataThreshold',
105 'updateCompatibleMetadata',
106 'reserializeMetadata',
109 if ( isset( $info[$option] ) ) {
110 $this->$option = $info[$option];
120 if ( isset( $row->img_name ) ) {
121 return ( $this->fileFromRowFactory )( $row, $this );
122 } elseif ( isset( $row->oi_name ) ) {
123 return ( $this->oldFileFromRowFactory )( $row, $this );
125 throw new InvalidArgumentException( __METHOD__ .
': invalid row' );
135 $title = File::normalizeTitle( $title );
136 return OldLocalFile::newFromArchiveName( $title, $this, $archiveName );
151 wfDebug( __METHOD__ .
": skipped because storage uses sha1 paths" );
152 return Status::newGood();
159 $storageKeys = array_unique( $storageKeys );
160 foreach ( $storageKeys as $key ) {
162 $path =
"$root/$hashPath$key";
163 $dbw->startAtomic( __METHOD__ );
168 if ( !$deleted && !$hidden ) {
169 wfDebug( __METHOD__ .
": deleting $key" );
170 $op = [
'op' =>
'delete',
'src' =>
$path ];
172 $status->error(
'undelete-cleanup-error',
$path );
173 $status->failCount++;
176 wfDebug( __METHOD__ .
": $key still in use" );
177 $status->successCount++;
179 $dbw->endAtomic( __METHOD__ );
193 $queryBuilder = $this->
getPrimaryDB()->newSelectQueryBuilder()
195 ->from(
'filearchive' )
196 ->where( [
'fa_storage_group' =>
'deleted',
'fa_storage_key' => $key ] );
197 if ( $lock ===
'lock' ) {
198 $queryBuilder->forUpdate();
200 return (
bool)$queryBuilder->caller( __METHOD__ )->fetchField();
212 $ext = File::normalizeExtension( substr( $key, strcspn( $key,
'.' ) + 1 ) );
215 $queryBuilder = $dbw->newSelectQueryBuilder()
220 $dbw->expr(
'oi_archive_name', IExpression::LIKE,
new LikeValue( $dbw->anyString(),
".$ext" ) ),
221 $dbw->bitAnd(
'oi_deleted', File::DELETED_FILE ) => File::DELETED_FILE,
223 if ( $lock ===
'lock' ) {
224 $queryBuilder->forUpdate();
227 return (
bool)$queryBuilder->caller( __METHOD__ )->fetchField();
237 $sha1 = strtok( $key,
'.' );
238 if ( is_string( $sha1 ) && strlen( $sha1 ) === 32 && $sha1[0] ===
'0' ) {
239 $sha1 = substr( $sha1, 1 );
251 $title = File::normalizeTitle( $title,
'exception' );
253 $memcKey = $this->
getSharedCacheKey(
'file-redirect', md5( $title->getDBkey() ) );
254 if ( $memcKey ===
false ) {
255 $memcKey = $this->
getLocalCacheKey(
'file-redirect', md5( $title->getDBkey() ) );
261 $method = __METHOD__;
262 $redirDbKey = $this->wanCache->getWithSetCallback(
265 function ( $oldValue, &$ttl, array &$setOpts ) use ( $method, $title ) {
270 $row = $dbr->newSelectQueryBuilder()
271 ->select( [
'rd_namespace',
'rd_title' ] )
273 ->join(
'redirect',
null,
'rd_from = page_id' )
274 ->where( [
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey() ] )
275 ->caller( $method )->fetchRow();
277 return ( $row && $row->rd_namespace ==
NS_FILE )
281 [
'pcTTL' => WANObjectCache::TTL_PROC_LONG ]
285 if ( $redirDbKey !==
' ' && strval( $redirDbKey ) !==
'' ) {
287 return Title::newFromText( $redirDbKey,
NS_FILE );
298 foreach ( $items as $item ) {
299 if ( is_array( $item ) ) {
300 $title = File::normalizeTitle( $item[
'title'] );
302 $searchSet[$title->getDBkey()] = $item;
305 $title = File::normalizeTitle( $item );
307 $searchSet[$title->getDBkey()] = [];
312 $fileMatchesSearch =
static function (
File $file, array $search ) {
318 $contextPerformer = RequestContext::getMain()->getAuthority();
319 $performer = ( !empty( $search[
'private'] ) && $search[
'private'] instanceof
Authority )
326 ( empty( $search[
'time'] ) && !$file->
isOld() ) ||
327 ( !empty( $search[
'time'] ) && $search[
'time'] === $file->
getTimestamp() )
329 ( !empty( $search[
'private'] ) || !$file->
isDeleted( File::DELETED_FILE ) ) &&
330 $file->
userCan( File::DELETED_FILE, $performer )
334 $applyMatchingFiles =
function (
IResultWrapper $res, &$searchSet, &$finalFiles )
335 use ( $fileMatchesSearch, $flags )
339 foreach ( $res as $row ) {
343 $dbKeysLook = [ strtr( $file->
getName(),
' ',
'_' ) ];
344 if ( !empty( $info[
'initialCapital'] ) ) {
346 $dbKeysLook[] = $contLang->lcfirst( $file->
getName() );
348 foreach ( $dbKeysLook as $dbKey ) {
349 if ( isset( $searchSet[$dbKey] )
350 && $fileMatchesSearch( $file, $searchSet[$dbKey] )
353 ? [
'title' => $dbKey,
'timestamp' => $file->
getTimestamp() ]
355 unset( $searchSet[$dbKey] );
365 foreach ( $searchSet as $dbKey => $_ ) {
369 if ( count( $imgNames ) ) {
370 $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr );
371 $res = $queryBuilder->where( [
'img_name' => $imgNames ] )->caller( __METHOD__ )->fetchResultSet();
372 $applyMatchingFiles( $res, $searchSet, $finalFiles );
377 foreach ( $searchSet as $dbKey => $search ) {
378 if ( isset( $search[
'time'] ) ) {
380 ->expr(
'oi_name',
'=', $this->
getNameFromTitle( File::normalizeTitle( $dbKey ) ) )
381 ->and(
'oi_timestamp',
'=', $dbr->timestamp( $search[
'time'] ) );
385 if ( count( $oiConds ) ) {
386 $queryBuilder = FileSelectQueryBuilder::newForOldFile( $dbr );
388 $res = $queryBuilder->where( $dbr->orExpr( $oiConds ) )
389 ->caller( __METHOD__ )->fetchResultSet();
390 $applyMatchingFiles( $res, $searchSet, $finalFiles );
394 foreach ( $searchSet as $dbKey => $search ) {
395 if ( !empty( $search[
'ignoreRedirect'] ) ) {
399 $title = File::normalizeTitle( $dbKey );
402 if ( $redir && $redir->getNamespace() ===
NS_FILE ) {
403 $file = $this->
newFile( $redir );
404 if ( $file && $fileMatchesSearch( $file, $search ) ) {
407 $finalFiles[$dbKey] = [
408 'title' => $file->
getTitle()->getDBkey(),
412 $finalFiles[$dbKey] = $file;
429 $queryBuilder = FileSelectQueryBuilder::newForFile( $this->
getReplicaDB() );
430 $res = $queryBuilder->where( [
'img_sha1' => $hash ] )
431 ->orderBy(
'img_name' )
432 ->caller( __METHOD__ )->fetchResultSet();
435 foreach ( $res as $row ) {
453 if ( $hashes === [] ) {
458 $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr );
460 $queryBuilder->where( [
'img_sha1' => $hashes ] )
461 ->orderBy(
'img_name' );
462 $res = $queryBuilder->caller( __METHOD__ )->fetchResultSet();
465 foreach ( $res as $row ) {
467 $result[$file->getSha1()][] = $file;
483 $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr );
486 ->where( $dbr->expr(
'img_name', IExpression::LIKE,
new LikeValue( $prefix, $dbr->anyString() ) ) )
487 ->orderBy(
'img_name' )
488 ->limit( intval( $limit ) );
489 $res = $queryBuilder->caller( __METHOD__ )->fetchResultSet();
493 foreach ( $res as $row ) {
505 return $this->dbProvider->getReplicaDatabase();
514 return $this->dbProvider->getPrimaryDatabase();
523 return static function ( $index ) {
546 ? $this->wanCache->makeGlobalKey(
547 'filerepo-' . $kClassSuffix,
564 function () use ( $key ) {
565 $this->wanCache->delete( $key );
573 public function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
604 public function delete( $srcRel, $archiveRel ) {
624 wfDebug( __METHOD__ .
": skipped because storage uses sha1 paths" );
625 return Status::newGood();
627 return parent::$function( ...$args );
679 if ( !$this->blobStore ) {
681 ->newBlobStore( $this->dbDomain );
698class_alias( LocalRepo::class,
'LocalRepo' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
if(!defined('MW_SETUP_CALLBACK'))
Group all the pieces relevant to the context of a request into one instance.
Interface for objects (potentially) representing an editable wiki page.