MediaWiki  master
FileRepo.php
Go to the documentation of this file.
1 <?php
11 
39 class FileRepo {
40  const DELETE_SOURCE = 1;
41  const OVERWRITE = 2;
42  const OVERWRITE_SAME = 4;
43  const SKIP_LOCKING = 8;
44 
45  const NAME_AND_TIME_ONLY = 1;
46 
50 
53 
55  protected $hasSha1Storage = false;
56 
58  protected $supportsSha1URLs = false;
59 
61  protected $backend;
62 
64  protected $zones = [];
65 
67  protected $thumbScriptUrl;
68 
71  protected $transformVia404;
72 
76  protected $descBaseUrl;
77 
81  protected $scriptDirUrl;
82 
84  protected $articleUrl;
85 
91  protected $initialCapital;
92 
98  protected $pathDisclosureProtection = 'simple';
99 
101  protected $url;
102 
104  protected $thumbUrl;
105 
107  protected $hashLevels;
108 
111 
116  protected $abbrvThreshold;
117 
119  protected $favicon;
120 
122  protected $isPrivate;
123 
125  protected $fileFactory = [ UnregisteredLocalFile::class, 'newFromTitle' ];
127  protected $oldFileFactory = false;
129  protected $fileFactoryKey = false;
131  protected $oldFileFactoryKey = false;
132 
136  protected $thumbProxyUrl;
138  protected $thumbProxySecret;
139 
141  protected $wanCache;
142 
147  public function __construct( array $info = null ) {
148  // Verify required settings presence
149  if (
150  $info === null
151  || !array_key_exists( 'name', $info )
152  || !array_key_exists( 'backend', $info )
153  ) {
154  throw new MWException( __CLASS__ .
155  " requires an array of options having both 'name' and 'backend' keys.\n" );
156  }
157 
158  // Required settings
159  $this->name = $info['name'];
160  if ( $info['backend'] instanceof FileBackend ) {
161  $this->backend = $info['backend']; // useful for testing
162  } else {
163  $this->backend = FileBackendGroup::singleton()->get( $info['backend'] );
164  }
165 
166  // Optional settings that can have no value
167  $optionalSettings = [
168  'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription',
169  'thumbScriptUrl', 'pathDisclosureProtection', 'descriptionCacheExpiry',
170  'favicon', 'thumbProxyUrl', 'thumbProxySecret',
171  ];
172  foreach ( $optionalSettings as $var ) {
173  if ( isset( $info[$var] ) ) {
174  $this->$var = $info[$var];
175  }
176  }
177 
178  // Optional settings that have a default
179  $this->initialCapital = $info['initialCapital'] ??
180  MediaWikiServices::getInstance()->getNamespaceInfo()->isCapitalized( NS_FILE );
181  $this->url = $info['url'] ?? false; // a subclass may set the URL (e.g. ForeignAPIRepo)
182  if ( isset( $info['thumbUrl'] ) ) {
183  $this->thumbUrl = $info['thumbUrl'];
184  } else {
185  $this->thumbUrl = $this->url ? "{$this->url}/thumb" : false;
186  }
187  $this->hashLevels = $info['hashLevels'] ?? 2;
188  $this->deletedHashLevels = $info['deletedHashLevels'] ?? $this->hashLevels;
189  $this->transformVia404 = !empty( $info['transformVia404'] );
190  $this->abbrvThreshold = $info['abbrvThreshold'] ?? 255;
191  $this->isPrivate = !empty( $info['isPrivate'] );
192  // Give defaults for the basic zones...
193  $this->zones = $info['zones'] ?? [];
194  foreach ( [ 'public', 'thumb', 'transcoded', 'temp', 'deleted' ] as $zone ) {
195  if ( !isset( $this->zones[$zone]['container'] ) ) {
196  $this->zones[$zone]['container'] = "{$this->name}-{$zone}";
197  }
198  if ( !isset( $this->zones[$zone]['directory'] ) ) {
199  $this->zones[$zone]['directory'] = '';
200  }
201  if ( !isset( $this->zones[$zone]['urlsByExt'] ) ) {
202  $this->zones[$zone]['urlsByExt'] = [];
203  }
204  }
205 
206  $this->supportsSha1URLs = !empty( $info['supportsSha1URLs'] );
207 
208  $this->wanCache = $info['wanCache'] ?? WANObjectCache::newEmpty();
209  }
210 
216  public function getBackend() {
217  return $this->backend;
218  }
219 
226  public function getReadOnlyReason() {
227  return $this->backend->getReadOnlyReason();
228  }
229 
237  protected function initZones( $doZones = [] ) {
238  $status = $this->newGood();
239  foreach ( (array)$doZones as $zone ) {
240  $root = $this->getZonePath( $zone );
241  if ( $root === null ) {
242  throw new MWException( "No '$zone' zone defined in the {$this->name} repo." );
243  }
244  }
245 
246  return $status;
247  }
248 
255  public static function isVirtualUrl( $url ) {
256  return substr( $url, 0, 9 ) == 'mwrepo://';
257  }
258 
267  public function getVirtualUrl( $suffix = false ) {
268  $path = 'mwrepo://' . $this->name;
269  if ( $suffix !== false ) {
270  $path .= '/' . rawurlencode( $suffix );
271  }
272 
273  return $path;
274  }
275 
283  public function getZoneUrl( $zone, $ext = null ) {
284  if ( in_array( $zone, [ 'public', 'thumb', 'transcoded' ] ) ) {
285  // standard public zones
286  if ( $ext !== null && isset( $this->zones[$zone]['urlsByExt'][$ext] ) ) {
287  // custom URL for extension/zone
288  return $this->zones[$zone]['urlsByExt'][$ext];
289  } elseif ( isset( $this->zones[$zone]['url'] ) ) {
290  // custom URL for zone
291  return $this->zones[$zone]['url'];
292  }
293  }
294  switch ( $zone ) {
295  case 'public':
296  return $this->url;
297  case 'temp':
298  case 'deleted':
299  return false; // no public URL
300  case 'thumb':
301  return $this->thumbUrl;
302  case 'transcoded':
303  return "{$this->url}/transcoded";
304  default:
305  return false;
306  }
307  }
308 
312  public function backendSupportsUnicodePaths() {
313  return (bool)( $this->getBackend()->getFeatures() & FileBackend::ATTR_UNICODE_PATHS );
314  }
315 
324  public function resolveVirtualUrl( $url ) {
325  if ( substr( $url, 0, 9 ) != 'mwrepo://' ) {
326  throw new MWException( __METHOD__ . ': unknown protocol' );
327  }
328  $bits = explode( '/', substr( $url, 9 ), 3 );
329  if ( count( $bits ) != 3 ) {
330  throw new MWException( __METHOD__ . ": invalid mwrepo URL: $url" );
331  }
332  list( $repo, $zone, $rel ) = $bits;
333  if ( $repo !== $this->name ) {
334  throw new MWException( __METHOD__ . ": fetching from a foreign repo is not supported" );
335  }
336  $base = $this->getZonePath( $zone );
337  if ( !$base ) {
338  throw new MWException( __METHOD__ . ": invalid zone: $zone" );
339  }
340 
341  return $base . '/' . rawurldecode( $rel );
342  }
343 
350  protected function getZoneLocation( $zone ) {
351  if ( !isset( $this->zones[$zone] ) ) {
352  return [ null, null ]; // bogus
353  }
354 
355  return [ $this->zones[$zone]['container'], $this->zones[$zone]['directory'] ];
356  }
357 
364  public function getZonePath( $zone ) {
365  list( $container, $base ) = $this->getZoneLocation( $zone );
366  if ( $container === null || $base === null ) {
367  return null;
368  }
369  $backendName = $this->backend->getName();
370  if ( $base != '' ) { // may not be set
371  $base = "/{$base}";
372  }
373 
374  return "mwstore://$backendName/{$container}{$base}";
375  }
376 
388  public function newFile( $title, $time = false ) {
390  if ( !$title ) {
391  return null;
392  }
393  if ( $time ) {
394  if ( $this->oldFileFactory ) {
395  return call_user_func( $this->oldFileFactory, $title, $this, $time );
396  } else {
397  return null;
398  }
399  } else {
400  return call_user_func( $this->fileFactory, $title, $this );
401  }
402  }
403 
421  public function findFile( $title, $options = [] ) {
423  if ( !$title ) {
424  return false;
425  }
426  if ( isset( $options['bypassCache'] ) ) {
427  $options['latest'] = $options['bypassCache']; // b/c
428  }
429  $time = $options['time'] ?? false;
430  $flags = !empty( $options['latest'] ) ? File::READ_LATEST : 0;
431  # First try the current version of the file to see if it precedes the timestamp
432  $img = $this->newFile( $title );
433  if ( !$img ) {
434  return false;
435  }
436  $img->load( $flags );
437  if ( $img->exists() && ( !$time || $img->getTimestamp() == $time ) ) {
438  return $img;
439  }
440  # Now try an old version of the file
441  if ( $time !== false ) {
442  $img = $this->newFile( $title, $time );
443  if ( $img ) {
444  $img->load( $flags );
445  if ( $img->exists() ) {
446  if ( !$img->isDeleted( File::DELETED_FILE ) ) {
447  return $img; // always OK
448  } elseif ( !empty( $options['private'] ) &&
449  $img->userCan( File::DELETED_FILE,
450  $options['private'] instanceof User ? $options['private'] : null
451  )
452  ) {
453  return $img;
454  }
455  }
456  }
457  }
458 
459  # Now try redirects
460  if ( !empty( $options['ignoreRedirect'] ) ) {
461  return false;
462  }
463  $redir = $this->checkRedirect( $title );
464  if ( $redir && $title->getNamespace() == NS_FILE ) {
465  $img = $this->newFile( $redir );
466  if ( !$img ) {
467  return false;
468  }
469  $img->load( $flags );
470  if ( $img->exists() ) {
471  $img->redirectedFrom( $title->getDBkey() );
472 
473  return $img;
474  }
475  }
476 
477  return false;
478  }
479 
497  public function findFiles( array $items, $flags = 0 ) {
498  $result = [];
499  foreach ( $items as $item ) {
500  if ( is_array( $item ) ) {
501  $title = $item['title'];
502  $options = $item;
503  unset( $options['title'] );
504  } else {
505  $title = $item;
506  $options = [];
507  }
508  $file = $this->findFile( $title, $options );
509  if ( $file ) {
510  $searchName = File::normalizeTitle( $title )->getDBkey(); // must be valid
511  if ( $flags & self::NAME_AND_TIME_ONLY ) {
512  $result[$searchName] = [
513  'title' => $file->getTitle()->getDBkey(),
514  'timestamp' => $file->getTimestamp()
515  ];
516  } else {
517  $result[$searchName] = $file;
518  }
519  }
520  }
521 
522  return $result;
523  }
524 
534  public function findFileFromKey( $sha1, $options = [] ) {
535  $time = $options['time'] ?? false;
536  # First try to find a matching current version of a file...
537  if ( !$this->fileFactoryKey ) {
538  return false; // find-by-sha1 not supported
539  }
540  $img = call_user_func( $this->fileFactoryKey, $sha1, $this, $time );
541  if ( $img && $img->exists() ) {
542  return $img;
543  }
544  # Now try to find a matching old version of a file...
545  if ( $time !== false && $this->oldFileFactoryKey ) { // find-by-sha1 supported?
546  $img = call_user_func( $this->oldFileFactoryKey, $sha1, $this, $time );
547  if ( $img && $img->exists() ) {
548  if ( !$img->isDeleted( File::DELETED_FILE ) ) {
549  return $img; // always OK
550  } elseif ( !empty( $options['private'] ) &&
551  $img->userCan( File::DELETED_FILE,
552  $options['private'] instanceof User ? $options['private'] : null
553  )
554  ) {
555  return $img;
556  }
557  }
558  }
559 
560  return false;
561  }
562 
571  public function findBySha1( $hash ) {
572  return [];
573  }
574 
582  public function findBySha1s( array $hashes ) {
583  $result = [];
584  foreach ( $hashes as $hash ) {
585  $files = $this->findBySha1( $hash );
586  if ( count( $files ) ) {
587  $result[$hash] = $files;
588  }
589  }
590 
591  return $result;
592  }
593 
602  public function findFilesByPrefix( $prefix, $limit ) {
603  return [];
604  }
605 
611  public function getThumbScriptUrl() {
612  return $this->thumbScriptUrl;
613  }
614 
620  public function getThumbProxyUrl() {
621  return $this->thumbProxyUrl;
622  }
623 
629  public function getThumbProxySecret() {
631  }
632 
638  public function canTransformVia404() {
639  return $this->transformVia404;
640  }
641 
648  public function getNameFromTitle( Title $title ) {
649  if (
650  $this->initialCapital !=
651  MediaWikiServices::getInstance()->getNamespaceInfo()->isCapitalized( NS_FILE )
652  ) {
653  $name = $title->getUserCaseDBKey();
654  if ( $this->initialCapital ) {
655  $name = MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $name );
656  }
657  } else {
658  $name = $title->getDBkey();
659  }
660 
661  return $name;
662  }
663 
669  public function getRootDirectory() {
670  return $this->getZonePath( 'public' );
671  }
672 
680  public function getHashPath( $name ) {
681  return self::getHashPathForLevel( $name, $this->hashLevels );
682  }
683 
691  public function getTempHashPath( $suffix ) {
692  $parts = explode( '!', $suffix, 2 ); // format is <timestamp>!<name> or just <name>
693  $name = $parts[1] ?? $suffix; // hash path is not based on timestamp
694  return self::getHashPathForLevel( $name, $this->hashLevels );
695  }
696 
702  protected static function getHashPathForLevel( $name, $levels ) {
703  if ( $levels == 0 ) {
704  return '';
705  } else {
706  $hash = md5( $name );
707  $path = '';
708  for ( $i = 1; $i <= $levels; $i++ ) {
709  $path .= substr( $hash, 0, $i ) . '/';
710  }
711 
712  return $path;
713  }
714  }
715 
721  public function getHashLevels() {
722  return $this->hashLevels;
723  }
724 
730  public function getName() {
731  return $this->name;
732  }
733 
741  public function makeUrl( $query = '', $entry = 'index' ) {
742  if ( isset( $this->scriptDirUrl ) ) {
743  return wfAppendQuery( "{$this->scriptDirUrl}/{$entry}.php", $query );
744  }
745 
746  return false;
747  }
748 
761  public function getDescriptionUrl( $name ) {
762  $encName = wfUrlencode( $name );
763  if ( !is_null( $this->descBaseUrl ) ) {
764  # "http://example.com/wiki/File:"
765  return $this->descBaseUrl . $encName;
766  }
767  if ( !is_null( $this->articleUrl ) ) {
768  # "http://example.com/wiki/$1"
769  # We use "Image:" as the canonical namespace for
770  # compatibility across all MediaWiki versions.
771  return str_replace( '$1',
772  "Image:$encName", $this->articleUrl );
773  }
774  if ( !is_null( $this->scriptDirUrl ) ) {
775  # "http://example.com/w"
776  # We use "Image:" as the canonical namespace for
777  # compatibility across all MediaWiki versions,
778  # and just sort of hope index.php is right. ;)
779  return $this->makeUrl( "title=Image:$encName" );
780  }
781 
782  return false;
783  }
784 
795  public function getDescriptionRenderUrl( $name, $lang = null ) {
796  $query = 'action=render';
797  if ( !is_null( $lang ) ) {
798  $query .= '&uselang=' . urlencode( $lang );
799  }
800  if ( isset( $this->scriptDirUrl ) ) {
801  return $this->makeUrl(
802  'title=' .
803  wfUrlencode( 'Image:' . $name ) .
804  "&$query" );
805  } else {
806  $descUrl = $this->getDescriptionUrl( $name );
807  if ( $descUrl ) {
808  return wfAppendQuery( $descUrl, $query );
809  } else {
810  return false;
811  }
812  }
813  }
814 
820  public function getDescriptionStylesheetUrl() {
821  if ( isset( $this->scriptDirUrl ) ) {
822  // Must match canonical query parameter order for optimum caching
823  // See Title::getCdnUrls
824  return $this->makeUrl( 'title=MediaWiki:Filepage.css&action=raw&ctype=text/css' );
825  }
826 
827  return false;
828  }
829 
843  public function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
844  $this->assertWritableRepo(); // fail out if read-only
845 
846  $status = $this->storeBatch( [ [ $srcPath, $dstZone, $dstRel ] ], $flags );
847  if ( $status->successCount == 0 ) {
848  $status->setOK( false );
849  }
850 
851  return $status;
852  }
853 
866  public function storeBatch( array $triplets, $flags = 0 ) {
867  $this->assertWritableRepo(); // fail out if read-only
868 
869  if ( $flags & self::DELETE_SOURCE ) {
870  throw new InvalidArgumentException( "DELETE_SOURCE not supported in " . __METHOD__ );
871  }
872 
873  $status = $this->newGood();
874  $backend = $this->backend; // convenience
875 
876  $operations = [];
877  // Validate each triplet and get the store operation...
878  foreach ( $triplets as $triplet ) {
879  list( $srcPath, $dstZone, $dstRel ) = $triplet;
880  wfDebug( __METHOD__
881  . "( \$src='$srcPath', \$dstZone='$dstZone', \$dstRel='$dstRel' )\n"
882  );
883 
884  // Resolve destination path
885  $root = $this->getZonePath( $dstZone );
886  if ( !$root ) {
887  throw new MWException( "Invalid zone: $dstZone" );
888  }
889  if ( !$this->validateFilename( $dstRel ) ) {
890  throw new MWException( 'Validation error in $dstRel' );
891  }
892  $dstPath = "$root/$dstRel";
893  $dstDir = dirname( $dstPath );
894  // Create destination directories for this triplet
895  if ( !$this->initDirectory( $dstDir )->isOK() ) {
896  return $this->newFatal( 'directorycreateerror', $dstDir );
897  }
898 
899  // Resolve source to a storage path if virtual
900  $srcPath = $this->resolveToStoragePath( $srcPath );
901 
902  // Get the appropriate file operation
903  if ( FileBackend::isStoragePath( $srcPath ) ) {
904  $opName = 'copy';
905  } else {
906  $opName = 'store';
907  }
908  $operations[] = [
909  'op' => $opName,
910  'src' => $srcPath,
911  'dst' => $dstPath,
912  'overwrite' => $flags & self::OVERWRITE,
913  'overwriteSame' => $flags & self::OVERWRITE_SAME,
914  ];
915  }
916 
917  // Execute the store operation for each triplet
918  $opts = [ 'force' => true ];
919  if ( $flags & self::SKIP_LOCKING ) {
920  $opts['nonLocking'] = true;
921  }
922  $status->merge( $backend->doOperations( $operations, $opts ) );
923 
924  return $status;
925  }
926 
937  public function cleanupBatch( array $files, $flags = 0 ) {
938  $this->assertWritableRepo(); // fail out if read-only
939 
940  $status = $this->newGood();
941 
942  $operations = [];
943  foreach ( $files as $path ) {
944  if ( is_array( $path ) ) {
945  // This is a pair, extract it
946  list( $zone, $rel ) = $path;
947  $path = $this->getZonePath( $zone ) . "/$rel";
948  } else {
949  // Resolve source to a storage path if virtual
950  $path = $this->resolveToStoragePath( $path );
951  }
952  $operations[] = [ 'op' => 'delete', 'src' => $path ];
953  }
954  // Actually delete files from storage...
955  $opts = [ 'force' => true ];
956  if ( $flags & self::SKIP_LOCKING ) {
957  $opts['nonLocking'] = true;
958  }
959  $status->merge( $this->backend->doOperations( $operations, $opts ) );
960 
961  return $status;
962  }
963 
977  final public function quickImport( $src, $dst, $options = null ) {
978  return $this->quickImportBatch( [ [ $src, $dst, $options ] ] );
979  }
980 
989  final public function quickPurge( $path ) {
990  return $this->quickPurgeBatch( [ $path ] );
991  }
992 
1000  public function quickCleanDir( $dir ) {
1001  $status = $this->newGood();
1002  $status->merge( $this->backend->clean(
1003  [ 'dir' => $this->resolveToStoragePath( $dir ) ] ) );
1004 
1005  return $status;
1006  }
1007 
1020  public function quickImportBatch( array $triples ) {
1021  $status = $this->newGood();
1022  $operations = [];
1023  foreach ( $triples as $triple ) {
1024  list( $src, $dst ) = $triple;
1025  if ( $src instanceof FSFile ) {
1026  $op = 'store';
1027  } else {
1028  $src = $this->resolveToStoragePath( $src );
1029  $op = FileBackend::isStoragePath( $src ) ? 'copy' : 'store';
1030  }
1031  $dst = $this->resolveToStoragePath( $dst );
1032 
1033  if ( !isset( $triple[2] ) ) {
1034  $headers = [];
1035  } elseif ( is_string( $triple[2] ) ) {
1036  // back-compat
1037  $headers = [ 'Content-Disposition' => $triple[2] ];
1038  } elseif ( is_array( $triple[2] ) && isset( $triple[2]['headers'] ) ) {
1039  $headers = $triple[2]['headers'];
1040  } else {
1041  $headers = [];
1042  }
1043 
1044  $operations[] = [
1045  'op' => $op,
1046  'src' => $src,
1047  'dst' => $dst,
1048  'headers' => $headers
1049  ];
1050  $status->merge( $this->initDirectory( dirname( $dst ) ) );
1051  }
1052  $status->merge( $this->backend->doQuickOperations( $operations ) );
1053 
1054  return $status;
1055  }
1056 
1065  public function quickPurgeBatch( array $paths ) {
1066  $status = $this->newGood();
1067  $operations = [];
1068  foreach ( $paths as $path ) {
1069  $operations[] = [
1070  'op' => 'delete',
1071  'src' => $this->resolveToStoragePath( $path ),
1072  'ignoreMissingSource' => true
1073  ];
1074  }
1075  $status->merge( $this->backend->doQuickOperations( $operations ) );
1076 
1077  return $status;
1078  }
1079 
1090  public function storeTemp( $originalName, $srcPath ) {
1091  $this->assertWritableRepo(); // fail out if read-only
1092 
1093  $date = MWTimestamp::getInstance()->format( 'YmdHis' );
1094  $hashPath = $this->getHashPath( $originalName );
1095  $dstUrlRel = $hashPath . $date . '!' . rawurlencode( $originalName );
1096  $virtualUrl = $this->getVirtualUrl( 'temp' ) . '/' . $dstUrlRel;
1097 
1098  $result = $this->quickImport( $srcPath, $virtualUrl );
1099  $result->value = $virtualUrl;
1100 
1101  return $result;
1102  }
1103 
1110  public function freeTemp( $virtualUrl ) {
1111  $this->assertWritableRepo(); // fail out if read-only
1112 
1113  $temp = $this->getVirtualUrl( 'temp' );
1114  if ( substr( $virtualUrl, 0, strlen( $temp ) ) != $temp ) {
1115  wfDebug( __METHOD__ . ": Invalid temp virtual URL\n" );
1116 
1117  return false;
1118  }
1119 
1120  return $this->quickPurge( $virtualUrl )->isOK();
1121  }
1122 
1132  public function concatenate( array $srcPaths, $dstPath, $flags = 0 ) {
1133  $this->assertWritableRepo(); // fail out if read-only
1134 
1135  $status = $this->newGood();
1136 
1137  $sources = [];
1138  foreach ( $srcPaths as $srcPath ) {
1139  // Resolve source to a storage path if virtual
1140  $source = $this->resolveToStoragePath( $srcPath );
1141  $sources[] = $source; // chunk to merge
1142  }
1143 
1144  // Concatenate the chunks into one FS file
1145  $params = [ 'srcs' => $sources, 'dst' => $dstPath ];
1146  $status->merge( $this->backend->concatenate( $params ) );
1147  if ( !$status->isOK() ) {
1148  return $status;
1149  }
1150 
1151  // Delete the sources if required
1152  if ( $flags & self::DELETE_SOURCE ) {
1153  $status->merge( $this->quickPurgeBatch( $srcPaths ) );
1154  }
1155 
1156  // Make sure status is OK, despite any quickPurgeBatch() fatals
1157  $status->setResult( true );
1158 
1159  return $status;
1160  }
1161 
1181  public function publish(
1182  $src, $dstRel, $archiveRel, $flags = 0, array $options = []
1183  ) {
1184  $this->assertWritableRepo(); // fail out if read-only
1185 
1186  $status = $this->publishBatch(
1187  [ [ $src, $dstRel, $archiveRel, $options ] ], $flags );
1188  if ( $status->successCount == 0 ) {
1189  $status->setOK( false );
1190  }
1191  $status->value = $status->value[0] ?? false;
1192 
1193  return $status;
1194  }
1195 
1206  public function publishBatch( array $ntuples, $flags = 0 ) {
1207  $this->assertWritableRepo(); // fail out if read-only
1208 
1209  $backend = $this->backend; // convenience
1210  // Try creating directories
1211  $status = $this->initZones( 'public' );
1212  if ( !$status->isOK() ) {
1213  return $status;
1214  }
1215 
1216  $status = $this->newGood( [] );
1217 
1218  $operations = [];
1219  $sourceFSFilesToDelete = []; // cleanup for disk source files
1220  // Validate each triplet and get the store operation...
1221  foreach ( $ntuples as $ntuple ) {
1222  list( $src, $dstRel, $archiveRel ) = $ntuple;
1223  $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
1224 
1225  $options = $ntuple[3] ?? [];
1226  // Resolve source to a storage path if virtual
1227  $srcPath = $this->resolveToStoragePath( $srcPath );
1228  if ( !$this->validateFilename( $dstRel ) ) {
1229  throw new MWException( 'Validation error in $dstRel' );
1230  }
1231  if ( !$this->validateFilename( $archiveRel ) ) {
1232  throw new MWException( 'Validation error in $archiveRel' );
1233  }
1234 
1235  $publicRoot = $this->getZonePath( 'public' );
1236  $dstPath = "$publicRoot/$dstRel";
1237  $archivePath = "$publicRoot/$archiveRel";
1238 
1239  $dstDir = dirname( $dstPath );
1240  $archiveDir = dirname( $archivePath );
1241  // Abort immediately on directory creation errors since they're likely to be repetitive
1242  if ( !$this->initDirectory( $dstDir )->isOK() ) {
1243  return $this->newFatal( 'directorycreateerror', $dstDir );
1244  }
1245  if ( !$this->initDirectory( $archiveDir )->isOK() ) {
1246  return $this->newFatal( 'directorycreateerror', $archiveDir );
1247  }
1248 
1249  // Set any desired headers to be use in GET/HEAD responses
1250  $headers = $options['headers'] ?? [];
1251 
1252  // Archive destination file if it exists.
1253  // This will check if the archive file also exists and fail if does.
1254  // This is a sanity check to avoid data loss. On Windows and Linux,
1255  // copy() will overwrite, so the existence check is vulnerable to
1256  // race conditions unless a functioning LockManager is used.
1257  // LocalFile also uses SELECT FOR UPDATE for synchronization.
1258  $operations[] = [
1259  'op' => 'copy',
1260  'src' => $dstPath,
1261  'dst' => $archivePath,
1262  'ignoreMissingSource' => true
1263  ];
1264 
1265  // Copy (or move) the source file to the destination
1266  if ( FileBackend::isStoragePath( $srcPath ) ) {
1267  if ( $flags & self::DELETE_SOURCE ) {
1268  $operations[] = [
1269  'op' => 'move',
1270  'src' => $srcPath,
1271  'dst' => $dstPath,
1272  'overwrite' => true, // replace current
1273  'headers' => $headers
1274  ];
1275  } else {
1276  $operations[] = [
1277  'op' => 'copy',
1278  'src' => $srcPath,
1279  'dst' => $dstPath,
1280  'overwrite' => true, // replace current
1281  'headers' => $headers
1282  ];
1283  }
1284  } else { // FS source path
1285  $operations[] = [
1286  'op' => 'store',
1287  'src' => $src, // prefer FSFile objects
1288  'dst' => $dstPath,
1289  'overwrite' => true, // replace current
1290  'headers' => $headers
1291  ];
1292  if ( $flags & self::DELETE_SOURCE ) {
1293  $sourceFSFilesToDelete[] = $srcPath;
1294  }
1295  }
1296  }
1297 
1298  // Execute the operations for each triplet
1299  $status->merge( $backend->doOperations( $operations ) );
1300  // Find out which files were archived...
1301  foreach ( $ntuples as $i => $ntuple ) {
1302  list( , , $archiveRel ) = $ntuple;
1303  $archivePath = $this->getZonePath( 'public' ) . "/$archiveRel";
1304  if ( $this->fileExists( $archivePath ) ) {
1305  $status->value[$i] = 'archived';
1306  } else {
1307  $status->value[$i] = 'new';
1308  }
1309  }
1310  // Cleanup for disk source files...
1311  foreach ( $sourceFSFilesToDelete as $file ) {
1312  Wikimedia\suppressWarnings();
1313  unlink( $file ); // FS cleanup
1314  Wikimedia\restoreWarnings();
1315  }
1316 
1317  return $status;
1318  }
1319 
1327  protected function initDirectory( $dir ) {
1328  $path = $this->resolveToStoragePath( $dir );
1329  list( , $container, ) = FileBackend::splitStoragePath( $path );
1330 
1331  $params = [ 'dir' => $path ];
1332  if ( $this->isPrivate
1333  || $container === $this->zones['deleted']['container']
1334  || $container === $this->zones['temp']['container']
1335  ) {
1336  # Take all available measures to prevent web accessibility of new deleted
1337  # directories, in case the user has not configured offline storage
1338  $params = [ 'noAccess' => true, 'noListing' => true ] + $params;
1339  }
1340 
1341  $status = $this->newGood();
1342  $status->merge( $this->backend->prepare( $params ) );
1343 
1344  return $status;
1345  }
1346 
1353  public function cleanDir( $dir ) {
1354  $this->assertWritableRepo(); // fail out if read-only
1355 
1356  $status = $this->newGood();
1357  $status->merge( $this->backend->clean(
1358  [ 'dir' => $this->resolveToStoragePath( $dir ) ] ) );
1359 
1360  return $status;
1361  }
1362 
1369  public function fileExists( $file ) {
1370  $result = $this->fileExistsBatch( [ $file ] );
1371 
1372  return $result[0];
1373  }
1374 
1381  public function fileExistsBatch( array $files ) {
1382  $paths = array_map( [ $this, 'resolveToStoragePath' ], $files );
1383  $this->backend->preloadFileStat( [ 'srcs' => $paths ] );
1384 
1385  $result = [];
1386  foreach ( $files as $key => $file ) {
1387  $path = $this->resolveToStoragePath( $file );
1388  $result[$key] = $this->backend->fileExists( [ 'src' => $path ] );
1389  }
1390 
1391  return $result;
1392  }
1393 
1404  public function delete( $srcRel, $archiveRel ) {
1405  $this->assertWritableRepo(); // fail out if read-only
1406 
1407  return $this->deleteBatch( [ [ $srcRel, $archiveRel ] ] );
1408  }
1409 
1427  public function deleteBatch( array $sourceDestPairs ) {
1428  $this->assertWritableRepo(); // fail out if read-only
1429 
1430  // Try creating directories
1431  $status = $this->initZones( [ 'public', 'deleted' ] );
1432  if ( !$status->isOK() ) {
1433  return $status;
1434  }
1435 
1436  $status = $this->newGood();
1437 
1438  $backend = $this->backend; // convenience
1439  $operations = [];
1440  // Validate filenames and create archive directories
1441  foreach ( $sourceDestPairs as $pair ) {
1442  list( $srcRel, $archiveRel ) = $pair;
1443  if ( !$this->validateFilename( $srcRel ) ) {
1444  throw new MWException( __METHOD__ . ':Validation error in $srcRel' );
1445  } elseif ( !$this->validateFilename( $archiveRel ) ) {
1446  throw new MWException( __METHOD__ . ':Validation error in $archiveRel' );
1447  }
1448 
1449  $publicRoot = $this->getZonePath( 'public' );
1450  $srcPath = "{$publicRoot}/$srcRel";
1451 
1452  $deletedRoot = $this->getZonePath( 'deleted' );
1453  $archivePath = "{$deletedRoot}/{$archiveRel}";
1454  $archiveDir = dirname( $archivePath ); // does not touch FS
1455 
1456  // Create destination directories
1457  if ( !$this->initDirectory( $archiveDir )->isOK() ) {
1458  return $this->newFatal( 'directorycreateerror', $archiveDir );
1459  }
1460 
1461  $operations[] = [
1462  'op' => 'move',
1463  'src' => $srcPath,
1464  'dst' => $archivePath,
1465  // We may have 2+ identical files being deleted,
1466  // all of which will map to the same destination file
1467  'overwriteSame' => true // also see T33792
1468  ];
1469  }
1470 
1471  // Move the files by execute the operations for each pair.
1472  // We're now committed to returning an OK result, which will
1473  // lead to the files being moved in the DB also.
1474  $opts = [ 'force' => true ];
1475  $status->merge( $backend->doOperations( $operations, $opts ) );
1476 
1477  return $status;
1478  }
1479 
1486  public function cleanupDeletedBatch( array $storageKeys ) {
1487  $this->assertWritableRepo();
1488  }
1489 
1498  public function getDeletedHashPath( $key ) {
1499  if ( strlen( $key ) < 31 ) {
1500  throw new MWException( "Invalid storage key '$key'." );
1501  }
1502  $path = '';
1503  for ( $i = 0; $i < $this->deletedHashLevels; $i++ ) {
1504  $path .= $key[$i] . '/';
1505  }
1506 
1507  return $path;
1508  }
1509 
1518  protected function resolveToStoragePath( $path ) {
1519  if ( self::isVirtualUrl( $path ) ) {
1520  return $this->resolveVirtualUrl( $path );
1521  }
1522 
1523  return $path;
1524  }
1525 
1533  public function getLocalCopy( $virtualUrl ) {
1534  $path = $this->resolveToStoragePath( $virtualUrl );
1535 
1536  return $this->backend->getLocalCopy( [ 'src' => $path ] );
1537  }
1538 
1547  public function getLocalReference( $virtualUrl ) {
1548  $path = $this->resolveToStoragePath( $virtualUrl );
1549 
1550  return $this->backend->getLocalReference( [ 'src' => $path ] );
1551  }
1552 
1560  public function getFileProps( $virtualUrl ) {
1561  $fsFile = $this->getLocalReference( $virtualUrl );
1562  $mwProps = new MWFileProps( MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer() );
1563  if ( $fsFile ) {
1564  $props = $mwProps->getPropsFromPath( $fsFile->getPath(), true );
1565  } else {
1566  $props = $mwProps->newPlaceholderProps();
1567  }
1568 
1569  return $props;
1570  }
1571 
1578  public function getFileTimestamp( $virtualUrl ) {
1579  $path = $this->resolveToStoragePath( $virtualUrl );
1580 
1581  return $this->backend->getFileTimestamp( [ 'src' => $path ] );
1582  }
1583 
1590  public function getFileSize( $virtualUrl ) {
1591  $path = $this->resolveToStoragePath( $virtualUrl );
1592 
1593  return $this->backend->getFileSize( [ 'src' => $path ] );
1594  }
1595 
1602  public function getFileSha1( $virtualUrl ) {
1603  $path = $this->resolveToStoragePath( $virtualUrl );
1604 
1605  return $this->backend->getFileSha1Base36( [ 'src' => $path ] );
1606  }
1607 
1617  public function streamFileWithStatus( $virtualUrl, $headers = [], $optHeaders = [] ) {
1618  $path = $this->resolveToStoragePath( $virtualUrl );
1619  $params = [ 'src' => $path, 'headers' => $headers, 'options' => $optHeaders ];
1620 
1621  // T172851: HHVM does not flush the output properly, causing OOM
1622  ob_start( null, 1048576 );
1623  ob_implicit_flush( true );
1624 
1625  $status = $this->newGood();
1626  $status->merge( $this->backend->streamFile( $params ) );
1627 
1628  // T186565: Close the buffer, unless it has already been closed
1629  // in HTTPFileStreamer::resetOutputBuffers().
1630  if ( ob_get_status() ) {
1631  ob_end_flush();
1632  }
1633 
1634  return $status;
1635  }
1636 
1645  public function enumFiles( $callback ) {
1646  $this->enumFilesInStorage( $callback );
1647  }
1648 
1656  protected function enumFilesInStorage( $callback ) {
1657  $publicRoot = $this->getZonePath( 'public' );
1658  $numDirs = 1 << ( $this->hashLevels * 4 );
1659  // Use a priori assumptions about directory structure
1660  // to reduce the tree height of the scanning process.
1661  for ( $flatIndex = 0; $flatIndex < $numDirs; $flatIndex++ ) {
1662  $hexString = sprintf( "%0{$this->hashLevels}x", $flatIndex );
1663  $path = $publicRoot;
1664  for ( $hexPos = 0; $hexPos < $this->hashLevels; $hexPos++ ) {
1665  $path .= '/' . substr( $hexString, 0, $hexPos + 1 );
1666  }
1667  $iterator = $this->backend->getFileList( [ 'dir' => $path ] );
1668  foreach ( $iterator as $name ) {
1669  // Each item returned is a public file
1670  call_user_func( $callback, "{$path}/{$name}" );
1671  }
1672  }
1673  }
1674 
1681  public function validateFilename( $filename ) {
1682  if ( strval( $filename ) == '' ) {
1683  return false;
1684  }
1685 
1686  return FileBackend::isPathTraversalFree( $filename );
1687  }
1688 
1695  switch ( $this->pathDisclosureProtection ) {
1696  case 'none':
1697  case 'simple': // b/c
1698  $callback = [ $this, 'passThrough' ];
1699  break;
1700  default: // 'paranoid'
1701  $callback = [ $this, 'paranoidClean' ];
1702  }
1703  return $callback;
1704  }
1705 
1712  function paranoidClean( $param ) {
1713  return '[hidden]';
1714  }
1715 
1722  function passThrough( $param ) {
1723  return $param;
1724  }
1725 
1732  public function newFatal( $message /*, parameters...*/ ) {
1733  $status = Status::newFatal( ...func_get_args() );
1734  $status->cleanCallback = $this->getErrorCleanupFunction();
1735 
1736  return $status;
1737  }
1738 
1745  public function newGood( $value = null ) {
1747  $status->cleanCallback = $this->getErrorCleanupFunction();
1748 
1749  return $status;
1750  }
1751 
1760  public function checkRedirect( Title $title ) {
1761  return false;
1762  }
1763 
1771  public function invalidateImageRedirect( Title $title ) {
1772  }
1773 
1779  public function getDisplayName() {
1780  global $wgSitename;
1781 
1782  if ( $this->isLocal() ) {
1783  return $wgSitename;
1784  }
1785 
1786  // 'shared-repo-name-wikimediacommons' is used when $wgUseInstantCommons = true
1787  return wfMessageFallback( 'shared-repo-name-' . $this->name, 'shared-repo' )->text();
1788  }
1789 
1797  public function nameForThumb( $name ) {
1798  if ( strlen( $name ) > $this->abbrvThreshold ) {
1800  $name = ( $ext == '' ) ? 'thumbnail' : "thumbnail.$ext";
1801  }
1802 
1803  return $name;
1804  }
1805 
1811  public function isLocal() {
1812  return $this->getName() == 'local';
1813  }
1814 
1823  public function getSharedCacheKey( /*...*/ ) {
1824  return false;
1825  }
1826 
1834  public function getLocalCacheKey( /*...*/ ) {
1835  $args = func_get_args();
1836  array_unshift( $args, 'filerepo', $this->getName() );
1837 
1838  return $this->wanCache->makeKey( ...$args );
1839  }
1840 
1849  public function getTempRepo() {
1850  return new TempFileRepo( [
1851  'name' => "{$this->name}-temp",
1852  'backend' => $this->backend,
1853  'zones' => [
1854  'public' => [
1855  // Same place storeTemp() uses in the base repo, though
1856  // the path hashing is mismatched, which is annoying.
1857  'container' => $this->zones['temp']['container'],
1858  'directory' => $this->zones['temp']['directory']
1859  ],
1860  'thumb' => [
1861  'container' => $this->zones['temp']['container'],
1862  'directory' => $this->zones['temp']['directory'] == ''
1863  ? 'thumb'
1864  : $this->zones['temp']['directory'] . '/thumb'
1865  ],
1866  'transcoded' => [
1867  'container' => $this->zones['temp']['container'],
1868  'directory' => $this->zones['temp']['directory'] == ''
1869  ? 'transcoded'
1870  : $this->zones['temp']['directory'] . '/transcoded'
1871  ]
1872  ],
1873  'hashLevels' => $this->hashLevels, // performance
1874  'isPrivate' => true // all in temp zone
1875  ] );
1876  }
1877 
1884  public function getUploadStash( User $user = null ) {
1885  return new UploadStash( $this, $user );
1886  }
1887 
1895  protected function assertWritableRepo() {
1896  }
1897 
1904  public function getInfo() {
1905  $ret = [
1906  'name' => $this->getName(),
1907  'displayname' => $this->getDisplayName(),
1908  'rootUrl' => $this->getZoneUrl( 'public' ),
1909  'local' => $this->isLocal(),
1910  ];
1911 
1912  $optionalSettings = [
1913  'url', 'thumbUrl', 'initialCapital', 'descBaseUrl', 'scriptDirUrl', 'articleUrl',
1914  'fetchDescription', 'descriptionCacheExpiry', 'favicon'
1915  ];
1916  foreach ( $optionalSettings as $k ) {
1917  if ( isset( $this->$k ) ) {
1918  $ret[$k] = $this->$k;
1919  }
1920  }
1921 
1922  return $ret;
1923  }
1924 
1929  public function hasSha1Storage() {
1930  return $this->hasSha1Storage;
1931  }
1932 
1937  public function supportsSha1URLs() {
1938  return $this->supportsSha1URLs;
1939  }
1940 }
string $favicon
The URL of the repo&#39;s favicon, if any.
Definition: FileRepo.php:119
assertWritableRepo()
Throw an exception if this repo is read-only by design.
Definition: FileRepo.php:1895
static isPathTraversalFree( $path)
Check if a relative path has no directory traversals.
bool $fetchDescription
Whether to fetch commons image description pages and display them on the local wiki.
Definition: FileRepo.php:49
string $scriptDirUrl
URL of the MediaWiki installation, equivalent to $wgScriptPath, e.g.
Definition: FileRepo.php:81
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
int $abbrvThreshold
File names over this size will use the short form of thumbnail names.
Definition: FileRepo.php:116
array $zones
Map of zones to config.
Definition: FileRepo.php:64
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1585
canTransformVia404()
Returns true if the repository can transform files via a 404 handler.
Definition: FileRepo.php:638
getTempRepo()
Get a temporary private FileRepo associated with this repo.
Definition: FileRepo.php:1849
getVirtualUrl( $suffix=false)
Get a URL referring to this repository, with the private mwrepo protocol.
Definition: FileRepo.php:267
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
doOperations(array $ops, array $opts=[])
This is the main entry point into the backend for write operations.
getFileSize( $virtualUrl)
Get the size of a file with a given virtual URL/storage path.
Definition: FileRepo.php:1590
getUserCaseDBKey()
Get the DB key with the initial letter case as specified by the user.
Definition: Title.php:1012
string $pathDisclosureProtection
May be &#39;paranoid&#39; to remove all parameters from error messages, &#39;none&#39; to leave the paths in unchange...
Definition: FileRepo.php:98
paranoidClean( $param)
Path disclosure protection function.
Definition: FileRepo.php:1712
getDescriptionUrl( $name)
Get the URL of an image description page.
Definition: FileRepo.php:761
$wgSitename
Name of the site.
cleanDir( $dir)
Deletes a directory if empty.
Definition: FileRepo.php:1353
getRootDirectory()
Get the public zone root storage directory of the repository.
Definition: FileRepo.php:669
initDirectory( $dir)
Creates a directory with the appropriate zone permissions.
Definition: FileRepo.php:1327
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1982
getUploadStash(User $user=null)
Get an UploadStash associated with this repo.
Definition: FileRepo.php:1884
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
makeUrl( $query='', $entry='index')
Make an url to this repo.
Definition: FileRepo.php:741
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Definition: router.php:42
if(!isset( $args[0])) $lang
string false $url
Public zone URL.
Definition: FileRepo.php:101
nameForThumb( $name)
Get the portion of the file that contains the origin file name.
Definition: FileRepo.php:1797
resolveVirtualUrl( $url)
Get the backend storage path corresponding to a virtual URL.
Definition: FileRepo.php:324
const DELETE_SOURCE
Definition: FileRepo.php:40
WANObjectCache $wanCache
Definition: FileRepo.php:141
checkRedirect(Title $title)
Checks if there is a redirect named as $title.
Definition: FileRepo.php:1760
quickCleanDir( $dir)
Deletes a directory if empty.
Definition: FileRepo.php:1000
$source
$value
getLocalCacheKey()
Get a key for this repo in the local cache domain.
Definition: FileRepo.php:1834
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
getHashLevels()
Get the number of hash directory levels.
Definition: FileRepo.php:721
getZoneUrl( $zone, $ext=null)
Get the URL corresponding to one of the four basic zones.
Definition: FileRepo.php:283
A helper class for throttling authentication attempts.
quickImport( $src, $dst, $options=null)
Import a file from the local file system into the repo.
Definition: FileRepo.php:977
freeTemp( $virtualUrl)
Remove a temporary file or mark it for garbage collection.
Definition: FileRepo.php:1110
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1799
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...
Definition: FileRepo.php:1181
cleanupDeletedBatch(array $storageKeys)
Delete files in the deleted directory if they are not referenced in the filearchive table...
Definition: FileRepo.php:1486
const DELETED_FILE
Definition: File.php:63
getBackend()
Get the file backend instance.
Definition: FileRepo.php:216
fileExists( $file)
Checks existence of a file.
Definition: FileRepo.php:1369
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1980
if( $line===false) $args
Definition: cdb.php:64
and how to run hooks for an and one after Each event has a name
Definition: hooks.txt:6
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
static getInstance( $ts=false)
Get a timestamp instance in GMT.
Definition: MWTimestamp.php:39
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1982
getZoneLocation( $zone)
The the storage container and base path of a zone.
Definition: FileRepo.php:350
array $oldFileFactoryKey
callable|bool Override these in the base class
Definition: FileRepo.php:131
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
enumFiles( $callback)
Call a callback function for every public regular file in the repository.
Definition: FileRepo.php:1645
static newFatal( $message)
Factory function for fatal errors.
Definition: StatusValue.php:68
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action, or null $user:User who performed the tagging when the tagging is subsequent to the action, or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'ContentSecurityPolicyDefaultSource':Modify the allowed CSP load sources. This affects all directives except for the script directive. If you want to add a script source, see ContentSecurityPolicyScriptSource hook. & $defaultSrc:Array of Content-Security-Policy allowed sources $policyConfig:Current configuration for the Content-Security-Policy header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyDirectives':Modify the content security policy directives. Use this only if ContentSecurityPolicyDefaultSource and ContentSecurityPolicyScriptSource do not meet your needs. & $directives:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyScriptSource':Modify the allowed CSP script sources. Note that you also have to use ContentSecurityPolicyDefaultSource if you want non-script sources to be loaded from whatever you add. & $scriptSrc:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1263
static newEmpty()
Get an instance that wraps EmptyBagOStuff.
const OVERWRITE
Definition: FileRepo.php:41
enumFilesInStorage( $callback)
Call a callback function for every public file in the repository.
Definition: FileRepo.php:1656
isLocal()
Returns true if this the local file repository.
Definition: FileRepo.php:1811
getReadOnlyReason()
Get an explanatory message if this repo is read-only.
Definition: FileRepo.php:226
initZones( $doZones=[])
Check if a single zone or list of zones is defined for usage.
Definition: FileRepo.php:237
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness, which urlencode encodes by default.
string $thumbProxyUrl
URL of where to proxy thumb.php requests to.
Definition: FileRepo.php:136
getThumbProxyUrl()
Get the URL thumb.php requests are being proxied to.
Definition: FileRepo.php:620
getInfo()
Return information about the repository.
Definition: FileRepo.php:1904
string $thumbScriptUrl
URL of thumb.php.
Definition: FileRepo.php:67
getDescriptionStylesheetUrl()
Get the URL of the stylesheet to apply to description pages.
Definition: FileRepo.php:820
getDBkey()
Get the main part with underscores.
Definition: Title.php:1002
quickPurgeBatch(array $paths)
Purge a batch of files from the repo.
Definition: FileRepo.php:1065
getErrorCleanupFunction()
Get a callback function to use for cleaning error message parameters.
Definition: FileRepo.php:1694
getFileSha1( $virtualUrl)
Get the sha1 (base 36) of a file with a given virtual URL/storage path.
Definition: FileRepo.php:1602
invalidateImageRedirect(Title $title)
Invalidates image redirect cache related to that image Doesn&#39;t do anything for repositories that don&#39;...
Definition: FileRepo.php:1771
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
concatenate(array $srcPaths, $dstPath, $flags=0)
Concatenate a list of temporary files into a target file location.
Definition: FileRepo.php:1132
deleteBatch(array $sourceDestPairs)
Move a group of files to the deletion archive.
Definition: FileRepo.php:1427
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
getTempHashPath( $suffix)
Get a relative path including trailing slash, e.g.
Definition: FileRepo.php:691
int $hashLevels
The number of directory levels for hash-based division of files.
Definition: FileRepo.php:107
getThumbScriptUrl()
Get the URL of thumb.php.
Definition: FileRepo.php:611
bool $initialCapital
Equivalent to $wgCapitalLinks (or $wgCapitalLinkOverrides[NS_FILE], determines whether filenames impl...
Definition: FileRepo.php:91
wfMessageFallback(... $keys)
This function accepts multiple message keys and returns a message instance for the first message whic...
static isVirtualUrl( $url)
Determine if a string is an mwrepo:// URL.
Definition: FileRepo.php:255
getSharedCacheKey()
Get a key on the primary cache for this repository.
Definition: FileRepo.php:1823
$params
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1982
string $articleUrl
Equivalent to $wgArticlePath, e.g.
Definition: FileRepo.php:84
string $thumbUrl
The base thumbnail URL.
Definition: FileRepo.php:104
const ATTR_UNICODE_PATHS
findFilesByPrefix( $prefix, $limit)
Return an array of files where the name starts with $prefix.
Definition: FileRepo.php:602
newFile( $title, $time=false)
Create a new File object from the local repository.
Definition: FileRepo.php:388
const SKIP_LOCKING
Definition: FileRepo.php:43
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
findFiles(array $items, $flags=0)
Find many files at once.
Definition: FileRepo.php:497
getLocalCopy( $virtualUrl)
Get a local FS copy of a file with a given virtual URL/storage path.
Definition: FileRepo.php:1533
static getHashPathForLevel( $name, $levels)
Definition: FileRepo.php:702
const NS_FILE
Definition: Defines.php:66
bool $hasSha1Storage
Definition: FileRepo.php:55
int $descriptionCacheExpiry
Definition: FileRepo.php:52
getZonePath( $zone)
Get the storage path corresponding to one of the zones.
Definition: FileRepo.php:364
string $descBaseUrl
URL of image description pages, e.g.
Definition: FileRepo.php:76
streamFileWithStatus( $virtualUrl, $headers=[], $optHeaders=[])
Attempt to stream a file with the given virtual URL/storage path.
Definition: FileRepo.php:1617
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
getFileTimestamp( $virtualUrl)
Get the timestamp of a file with a given virtual URL/storage path.
Definition: FileRepo.php:1578
getNameFromTitle(Title $title)
Get the name of a file from its title object.
Definition: FileRepo.php:648
storeTemp( $originalName, $srcPath)
Pick a random name in the temp zone and store a file to it.
Definition: FileRepo.php:1090
const NAME_AND_TIME_ONLY
Definition: FileRepo.php:45
string $thumbProxySecret
Secret key to pass as an X-Swift-Secret header to the proxied thumb service.
Definition: FileRepo.php:138
backendSupportsUnicodePaths()
Definition: FileRepo.php:312
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
UploadStash is intended to accomplish a few things:
Definition: UploadStash.php:53
cleanupBatch(array $files, $flags=0)
Deletes a batch of files.
Definition: FileRepo.php:937
bool $transformVia404
Whether to skip media file transformation on parse and rely on a 404 handler instead.
Definition: FileRepo.php:71
findFile( $title, $options=[])
Find an instance of the named file created at the specified time Returns false if the file does not e...
Definition: FileRepo.php:421
findBySha1s(array $hashes)
Get an array of arrays or iterators of file objects for files that have the given SHA-1 content hashe...
Definition: FileRepo.php:582
Class representing a non-directory file on the file system.
Definition: FSFile.php:29
const OVERWRITE_SAME
Definition: FileRepo.php:42
getFileProps( $virtualUrl)
Get properties of a file with a given virtual URL/storage path.
Definition: FileRepo.php:1560
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
store( $srcPath, $dstZone, $dstRel, $flags=0)
Store a file to a given destination.
Definition: FileRepo.php:843
findBySha1( $hash)
Get an array or iterator of file objects for files that have a given SHA-1 content hash...
Definition: FileRepo.php:571
FileRepo for temporary files created via FileRepo::getTempRepo()
Definition: TempFileRepo.php:5
Base class for all file backend classes (including multi-write backends).
Definition: FileBackend.php:92
findFileFromKey( $sha1, $options=[])
Find an instance of the file with this key, created at the specified time Returns false if the file d...
Definition: FileRepo.php:534
getDisplayName()
Get the human-readable name of the repo.
Definition: FileRepo.php:1779
if(!is_readable( $file)) $ext
Definition: router.php:48
Base class for file repositories.
Definition: FileRepo.php:39
bool $supportsSha1URLs
Definition: FileRepo.php:58
MimeMagic helper wrapper.
Definition: MWFileProps.php:28
array $fileFactoryKey
callable|bool Override these in the base class
Definition: FileRepo.php:129
array $oldFileFactory
callable|bool Override these in the base class
Definition: FileRepo.php:127
getHashPath( $name)
Get a relative path including trailing slash, e.g.
Definition: FileRepo.php:680
array $fileFactory
callable Override these in the base class
Definition: FileRepo.php:125
storeBatch(array $triplets, $flags=0)
Store a batch of files.
Definition: FileRepo.php:866
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
getLocalReference( $virtualUrl)
Get a local FS file with a given virtual URL/storage path.
Definition: FileRepo.php:1547
FileBackend $backend
Definition: FileRepo.php:61
fileExistsBatch(array $files)
Checks existence of an array of files.
Definition: FileRepo.php:1381
quickImportBatch(array $triples)
Import a batch of files from the local file system into the repo.
Definition: FileRepo.php:1020
quickPurge( $path)
Purge a file from the repo.
Definition: FileRepo.php:989
getDeletedHashPath( $key)
Get a relative path for a deletion archive key, e.g.
Definition: FileRepo.php:1498
passThrough( $param)
Path disclosure protection function.
Definition: FileRepo.php:1722
validateFilename( $filename)
Determine if a relative path is valid, i.e.
Definition: FileRepo.php:1681
supportsSha1URLs()
Returns whether or not repo supports having originals SHA-1s in the thumb URLs.
Definition: FileRepo.php:1937
int $deletedHashLevels
The number of directory levels for hash-based division of deleted files.
Definition: FileRepo.php:110
resolveToStoragePath( $path)
If a path is a virtual URL, resolve it to a storage path.
Definition: FileRepo.php:1518
__construct(array $info=null)
Definition: FileRepo.php:147
hasSha1Storage()
Returns whether or not storage is SHA-1 based.
Definition: FileRepo.php:1929
bool $isPrivate
Whether all zones should be private (e.g.
Definition: FileRepo.php:122
static normalizeTitle( $title, $exception=false)
Given a string or Title object return either a valid Title object with namespace NS_FILE or null...
Definition: File.php:194
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1473
getThumbProxySecret()
Get the secret key for the proxied thumb service.
Definition: FileRepo.php:629
getDescriptionRenderUrl( $name, $lang=null)
Get the URL of the content-only fragment of the description page.
Definition: FileRepo.php:795
getName()
Get the name of this repository, as specified by $info[&#39;name]&#39; to the constructor.
Definition: FileRepo.php:730
newGood( $value=null)
Create a new good result.
Definition: FileRepo.php:1745
newFatal( $message)
Create a new fatal error.
Definition: FileRepo.php:1732
publishBatch(array $ntuples, $flags=0)
Publish a batch of files.
Definition: FileRepo.php:1206