MediaWiki master
LocalFile.php
Go to the documentation of this file.
1<?php
8
9use InvalidArgumentException;
10use MediaHandler;
38use MWFileProps;
39use RuntimeException;
40use stdClass;
41use UnexpectedValueException;
52use Wikimedia\Timestamp\ConvertibleTimestamp;
53use Wikimedia\Timestamp\TimestampFormat as TS;
54
81class LocalFile extends File {
82 private const VERSION = 13; // cache version
83
84 private const CACHE_FIELD_MAX_LEN = 1000;
85
87 private const MDS_EMPTY = 'empty';
88
90 private const MDS_LEGACY = 'legacy';
91
93 private const MDS_PHP = 'php';
94
96 private const MDS_JSON = 'json';
97
99 private const MAX_PAGE_RENDER_JOBS = 50;
100
102 protected $fileExists;
103
105 private $fileId;
106
108 private $fileTypeId;
109
111 protected $width;
112
114 protected $height;
115
117 protected $bits;
118
120 protected $media_type;
121
123 protected $mime;
124
126 protected $size;
127
129 protected $metadataArray = [];
130
138
140 protected $metadataBlobs = [];
141
149
151 protected $sha1;
152
154 protected $dataLoaded = false;
155
157 protected $extraDataLoaded = false;
158
160 protected $deleted;
161
163 protected $file_id;
164
167
169 protected $repoClass = LocalRepo::class;
170
172 private $historyLine = 0;
173
175 private $historyRes = null;
176
178 private $major_mime;
179
181 private $minor_mime;
182
184 private $timestamp;
185
187 private $user;
188
190 private $description;
191
193 private $descriptionTouched;
194
196 private $upgraded = false;
197
199 private $upgrading;
200
202 private $locked;
203
205 private $lockedOwnTrx;
206
208 private $missing;
209
211 private $metadataStorageHelper;
212
214 private $migrationStage = SCHEMA_COMPAT_OLD;
215
216 // @note: higher than IDBAccessObject constants
217 private const LOAD_ALL = 16; // integer; load all the lazy fields too (like metadata)
218
219 private const ATOMIC_SECTION_LOCK = 'LocalFile::lockingTransaction';
220
233 public static function newFromTitle( $title, $repo, $unused = null ): static {
234 return new static( $title, $repo );
235 }
236
246 public static function newFromRow( $row, $repo ): static {
247 $title = Title::makeTitle( NS_FILE, $row->img_name );
248 $file = new static( $title, $repo );
249 $file->loadFromRow( $row );
250
251 return $file;
252 }
253
265 public static function newFromKey( $sha1, $repo, $timestamp = false ): static|false {
266 $dbr = $repo->getReplicaDB();
267 $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr );
268
269 $queryBuilder->where( [ 'img_sha1' => $sha1 ] );
270
271 if ( $timestamp ) {
272 $queryBuilder->andWhere( [ 'img_timestamp' => $dbr->timestamp( $timestamp ) ] );
273 }
274
275 $row = $queryBuilder->caller( __METHOD__ )->fetchRow();
276 if ( $row ) {
277 return static::newFromRow( $row, $repo );
278 } else {
279 return false;
280 }
281 }
282
303 public static function getQueryInfo( array $options = [] ) {
304 wfDeprecated( __METHOD__, '1.41' );
305 $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
306 $queryInfo = FileSelectQueryBuilder::newForFile( $dbr, $options )->getQueryInfo();
307 // needs remapping...
308 return [
309 'tables' => $queryInfo['tables'],
310 'fields' => $queryInfo['fields'],
311 'joins' => $queryInfo['join_conds'],
312 ];
313 }
314
322 public function __construct( $title, $repo ) {
323 parent::__construct( $title, $repo );
324 $this->metadataStorageHelper = new MetadataStorageHelper( $repo );
325 $this->migrationStage = MediaWikiServices::getInstance()->getMainConfig()->get(
326 MainConfigNames::FileSchemaMigrationStage
327 );
328
329 $this->assertRepoDefined();
330 $this->assertTitleDefined();
331 }
332
336 public function getRepo() {
337 return $this->repo;
338 }
339
346 protected function getCacheKey() {
347 return $this->repo->getSharedCacheKey( 'file', sha1( $this->getName() ) );
348 }
349
353 private function loadFromCache() {
354 $this->dataLoaded = false;
355 $this->extraDataLoaded = false;
356
357 $key = $this->getCacheKey();
358 if ( !$key ) {
359 $this->loadFromDB( IDBAccessObject::READ_NORMAL );
360
361 return;
362 }
363
364 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
365 $cachedValues = $cache->getWithSetCallback(
366 $key,
367 $cache::TTL_WEEK,
368 function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
369 $setOpts += Database::getCacheSetOptions( $this->repo->getReplicaDB() );
370
371 $this->loadFromDB( IDBAccessObject::READ_NORMAL );
372
373 $fields = $this->getCacheFields( '' );
374 $cacheVal = [];
375 $cacheVal['fileExists'] = $this->fileExists;
376 if ( $this->fileExists ) {
377 foreach ( $fields as $field ) {
378 $cacheVal[$field] = $this->$field;
379 }
380 }
381 if ( $this->user ) {
382 $cacheVal['user'] = $this->user->getId();
383 $cacheVal['user_text'] = $this->user->getName();
384 }
385
386 // Don't cache metadata items stored as blobs, since they tend to be large
387 if ( $this->metadataBlobs ) {
388 $cacheVal['metadata'] = array_diff_key(
389 $this->metadataArray, $this->metadataBlobs );
390 // Save the blob addresses
391 $cacheVal['metadataBlobs'] = $this->metadataBlobs;
392 } else {
393 $cacheVal['metadata'] = $this->metadataArray;
394 }
395
396 // Strip off excessive entries from the subset of fields that can become large.
397 // If the cache value gets too large and might not fit in the cache,
398 // causing repeat database queries for each access to the file.
399 foreach ( $this->getLazyCacheFields( '' ) as $field ) {
400 if ( isset( $cacheVal[$field] )
401 && strlen( serialize( $cacheVal[$field] ) ) > 100 * 1024
402 ) {
403 unset( $cacheVal[$field] ); // don't let the value get too big
404 if ( $field === 'metadata' ) {
405 unset( $cacheVal['metadataBlobs'] );
406 }
407 }
408 }
409
410 if ( $this->fileExists ) {
411 $ttl = $cache->adaptiveTTL( (int)wfTimestamp( TS::UNIX, $this->timestamp ), $ttl );
412 } else {
413 $ttl = $cache::TTL_DAY;
414 }
415
416 return $cacheVal;
417 },
418 [ 'version' => self::VERSION ]
419 );
420
421 $this->fileExists = $cachedValues['fileExists'];
422 if ( $this->fileExists ) {
423 $this->setProps( $cachedValues );
424 }
425
426 $this->dataLoaded = true;
427 $this->extraDataLoaded = true;
428 foreach ( $this->getLazyCacheFields( '' ) as $field ) {
429 $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
430 }
431 }
432
436 public function invalidateCache() {
437 $key = $this->getCacheKey();
438 if ( !$key ) {
439 return;
440 }
441
442 $this->repo->getPrimaryDB()->onTransactionPreCommitOrIdle(
443 static function () use ( $key ) {
444 MediaWikiServices::getInstance()->getMainWANObjectCache()->delete( $key );
445 },
446 __METHOD__
447 );
448 }
449
457 public function loadFromFile( $path = null ) {
458 $props = $this->repo->getFileProps( $path ?? $this->getVirtualUrl() );
459 $this->setProps( $props );
460 }
461
469 protected function getCacheFields( $prefix = 'img_' ) {
470 if ( $prefix !== '' ) {
471 throw new InvalidArgumentException(
472 __METHOD__ . ' with a non-empty prefix is no longer supported.'
473 );
474 }
475
476 // See self::getQueryInfo() for the fetching of the data from the DB,
477 // self::loadFromRow() for the loading of the object from the DB row,
478 // and self::loadFromCache() for the caching, and self::setProps() for
479 // populating the object from an array of data.
480 return [ 'size', 'width', 'height', 'bits', 'media_type',
481 'major_mime', 'minor_mime', 'timestamp', 'sha1', 'description' ];
482 }
483
491 protected function getLazyCacheFields( $prefix = 'img_' ) {
492 if ( $prefix !== '' ) {
493 throw new InvalidArgumentException(
494 __METHOD__ . ' with a non-empty prefix is no longer supported.'
495 );
496 }
497
498 // Keep this in sync with the omit-lazy option in self::getQueryInfo().
499 return [ 'metadata' ];
500 }
501
507 protected function loadFromDB( $flags = 0 ) {
508 $fname = static::class . '::' . __FUNCTION__;
509
510 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
511 $this->dataLoaded = true;
512 $this->extraDataLoaded = true;
513
514 $dbr = ( $flags & IDBAccessObject::READ_LATEST )
515 ? $this->repo->getPrimaryDB()
516 : $this->repo->getReplicaDB();
517 $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr );
518
519 $queryBuilder->where( [ 'img_name' => $this->getName() ] );
520 $row = $queryBuilder->caller( $fname )->fetchRow();
521
522 if ( $row ) {
523 $this->loadFromRow( $row );
524 } else {
525 $this->fileExists = false;
526 }
527 }
528
534 protected function loadExtraFromDB() {
535 if ( !$this->title ) {
536 return; // Avoid hard failure when the file does not exist. T221812
537 }
538
539 $fname = static::class . '::' . __FUNCTION__;
540
541 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
542 $this->extraDataLoaded = true;
543
544 $db = $this->repo->getReplicaDB();
545 $fieldMap = $this->loadExtraFieldsWithTimestamp( $db, $fname );
546 if ( !$fieldMap ) {
547 $db = $this->repo->getPrimaryDB();
548 $fieldMap = $this->loadExtraFieldsWithTimestamp( $db, $fname );
549 }
550
551 if ( $fieldMap ) {
552 if ( isset( $fieldMap['metadata'] ) ) {
553 $this->loadMetadataFromDbFieldValue( $db, $fieldMap['metadata'] );
554 }
555 } else {
556 throw new RuntimeException( "Could not find data for image '{$this->getName()}'." );
557 }
558 }
559
565 private function loadExtraFieldsWithTimestamp( IReadableDatabase $dbr, $fname ) {
566 $fieldMap = false;
567
568 $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr, [ 'omit-nonlazy' ] );
569 $queryBuilder->where( [ 'img_name' => $this->getName() ] )
570 ->andWhere( [ 'img_timestamp' => $dbr->timestamp( $this->getTimestamp() ) ] );
571 $row = $queryBuilder->caller( $fname )->fetchRow();
572 if ( $row ) {
573 $fieldMap = $this->unprefixRow( $row, 'img_' );
574 } else {
575 # File may have been uploaded over in the meantime; check the old versions
576 $queryBuilder = FileSelectQueryBuilder::newForOldFile( $dbr, [ 'omit-nonlazy' ] );
577 $row = $queryBuilder->where( [ 'oi_name' => $this->getName() ] )
578 ->andWhere( [ 'oi_timestamp' => $dbr->timestamp( $this->getTimestamp() ) ] )
579 ->caller( __METHOD__ )->fetchRow();
580 if ( $row ) {
581 $fieldMap = $this->unprefixRow( $row, 'oi_' );
582 }
583 }
584
585 return $fieldMap;
586 }
587
593 protected function unprefixRow( $row, $prefix = 'img_' ) {
594 $array = (array)$row;
595 $prefixLength = strlen( $prefix );
596
597 // Double check prefix once
598 if ( !str_starts_with( array_key_first( $array ), $prefix ) ) {
599 throw new InvalidArgumentException( __METHOD__ . ': incorrect $prefix parameter' );
600 }
601
602 $decoded = [];
603 foreach ( $array as $name => $value ) {
604 $decoded[substr( $name, $prefixLength )] = $value;
605 }
606
607 return $decoded;
608 }
609
625 public function loadFromRow( $row, $prefix = 'img_' ) {
626 $this->dataLoaded = true;
627
628 $unprefixed = $this->unprefixRow( $row, $prefix );
629
630 $this->name = $unprefixed['name'];
631 $this->media_type = $unprefixed['media_type'];
632
633 $services = MediaWikiServices::getInstance();
634 $this->description = $services->getCommentStore()
635 ->getComment( "{$prefix}description", $row )->text;
636
637 $this->user = $services->getUserFactory()->newFromAnyId(
638 $unprefixed['user'] ?? null,
639 $unprefixed['user_text'] ?? null,
640 $unprefixed['actor'] ?? null
641 );
642
643 $this->timestamp = wfTimestamp( TS::MW, $unprefixed['timestamp'] );
644
645 $this->loadMetadataFromDbFieldValue(
646 $this->repo->getReplicaDB(), $unprefixed['metadata'] );
647
648 if ( empty( $unprefixed['major_mime'] ) ) {
649 $this->major_mime = 'unknown';
650 $this->minor_mime = 'unknown';
651 $this->mime = 'unknown/unknown';
652 } else {
653 if ( !$unprefixed['minor_mime'] ) {
654 $unprefixed['minor_mime'] = 'unknown';
655 }
656 $this->major_mime = $unprefixed['major_mime'];
657 $this->minor_mime = $unprefixed['minor_mime'];
658 $this->mime = $unprefixed['major_mime'] . '/' . $unprefixed['minor_mime'];
659 }
660
661 // Trim zero padding from char/binary field
662 $this->sha1 = rtrim( $unprefixed['sha1'], "\0" );
663
664 // Normalize some fields to integer type, per their database definition.
665 // Use unary + so that overflows will be upgraded to double instead of
666 // being truncated as with intval(). This is important to allow > 2 GiB
667 // files on 32-bit systems.
668 $this->size = +$unprefixed['size'];
669 $this->width = +$unprefixed['width'];
670 $this->height = +$unprefixed['height'];
671 $this->bits = +$unprefixed['bits'];
672
673 // Check for extra fields (deprecated since MW 1.37)
674 $extraFields = array_diff(
675 array_keys( $unprefixed ),
676 [
677 'name', 'media_type', 'description_text', 'description_data',
678 'description_cid', 'user', 'user_text', 'actor', 'timestamp',
679 'metadata', 'major_mime', 'minor_mime', 'sha1', 'size', 'width',
680 'height', 'bits', 'file_id', 'filerevision_id'
681 ]
682 );
683 if ( $extraFields ) {
685 'Passing extra fields (' .
686 implode( ', ', $extraFields )
687 . ') to ' . __METHOD__ . ' was deprecated in MediaWiki 1.37. ' .
688 'Property assignment will be removed in a later version.',
689 '1.37' );
690 foreach ( $extraFields as $field ) {
691 $this->$field = $unprefixed[$field];
692 }
693 }
694
695 $this->fileExists = true;
696 }
697
703 public function load( $flags = 0 ) {
704 if ( !$this->dataLoaded ) {
705 if ( $flags & IDBAccessObject::READ_LATEST ) {
706 $this->loadFromDB( $flags );
707 } else {
708 $this->loadFromCache();
709 }
710 }
711
712 if ( ( $flags & self::LOAD_ALL ) && !$this->extraDataLoaded ) {
713 // @note: loads on name/timestamp to reduce race condition problems
714 $this->loadExtraFromDB();
715 }
716 }
717
722 public function maybeUpgradeRow() {
723 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() || $this->upgrading ) {
724 return;
725 }
726
727 $upgrade = false;
728 $reserialize = false;
729 if ( $this->media_type === null || $this->mime == 'image/svg' ) {
730 $upgrade = true;
731 } else {
732 $handler = $this->getHandler();
733 if ( $handler ) {
734 $validity = $handler->isFileMetadataValid( $this );
735 if ( $validity === MediaHandler::METADATA_BAD ) {
736 $upgrade = true;
737 } elseif ( $validity === MediaHandler::METADATA_COMPATIBLE
738 && $this->repo->isMetadataUpdateEnabled()
739 ) {
740 $upgrade = true;
741 } elseif ( $this->repo->isJsonMetadataEnabled()
742 && $this->repo->isMetadataReserializeEnabled()
743 ) {
744 if ( $this->repo->isSplitMetadataEnabled() && $this->isMetadataOversize() ) {
745 $reserialize = true;
746 } elseif ( $this->metadataSerializationFormat !== self::MDS_EMPTY &&
747 $this->metadataSerializationFormat !== self::MDS_JSON ) {
748 $reserialize = true;
749 }
750 }
751 }
752 }
753
754 if ( $upgrade || $reserialize ) {
755 $this->upgrading = true;
756 // Defer updates unless in auto-commit CLI mode
757 DeferredUpdates::addCallableUpdate( function () use ( $upgrade ) {
758 $this->upgrading = false; // avoid duplicate updates
759 try {
760 if ( $upgrade ) {
761 $this->upgradeRow();
762 } else {
763 $this->reserializeMetadata();
764 }
765 } catch ( LocalFileLockError ) {
766 // let the other process handle it (or do it next time)
767 }
768 } );
769 }
770 }
771
775 public function getUpgraded() {
776 return $this->upgraded;
777 }
778
785 public function getFileIdFromName() {
786 if ( !$this->fileId ) {
787 $dbw = $this->repo->getPrimaryDB();
788 $id = $dbw->newSelectQueryBuilder()
789 ->select( 'file_id' )
790 ->from( 'file' )
791 ->where( [
792 'file_name' => $this->getName(),
793 'file_deleted' => 0
794 ] )
795 ->caller( __METHOD__ )
796 ->fetchField();
797 $this->fileId = $id;
798 }
799
800 return $this->fileId;
801 }
802
809 public function acquireFileIdFromName() {
810 $dbw = $this->repo->getPrimaryDB();
811 $id = $this->getFileIdFromName();
812 if ( $id ) {
813 return $id;
814 }
815 $id = $dbw->newSelectQueryBuilder()
816 ->select( 'file_id' )
817 ->from( 'file' )
818 ->where( [
819 'file_name' => $this->getName(),
820 ] )
821 ->caller( __METHOD__ )
822 ->fetchField();
823 if ( !$id ) {
824 $dbw->newInsertQueryBuilder()
825 ->insertInto( 'file' )
826 ->row( [
827 'file_name' => $this->getName(),
828 // The value will be updated later
829 'file_latest' => 0,
830 'file_deleted' => 0,
831 'file_type' => $this->getFileTypeId(),
832 ] )
833 ->caller( __METHOD__ )->execute();
834 $insertId = $dbw->insertId();
835 if ( !$insertId ) {
836 throw new RuntimeException( 'File entry could not be inserted' );
837 }
838 return $insertId;
839 } else {
840 // Undelete
841 $dbw->newUpdateQueryBuilder()
842 ->update( 'file' )
843 ->set( [ 'file_deleted' => 0 ] )
844 ->where( [ 'file_id' => $id ] )
845 ->caller( __METHOD__ )->execute();
846 return $id;
847 }
848 }
849
850 protected function getFileTypeId(): int {
851 if ( $this->fileTypeId ) {
852 return $this->fileTypeId;
853 }
854 [ $major, $minor ] = self::splitMime( $this->mime );
855 $dbw = $this->repo->getPrimaryDB();
856 $id = $dbw->newSelectQueryBuilder()
857 ->select( 'ft_id' )
858 ->from( 'filetypes' )
859 ->where( [
860 'ft_media_type' => $this->getMediaType(),
861 'ft_major_mime' => $major,
862 'ft_minor_mime' => $minor,
863 ] )
864 ->caller( __METHOD__ )
865 ->fetchField();
866 if ( $id ) {
867 $this->fileTypeId = $id;
868 return $id;
869 }
870 $dbw->newInsertQueryBuilder()
871 ->insertInto( 'filetypes' )
872 ->row( [
873 'ft_media_type' => $this->getMediaType(),
874 'ft_major_mime' => $major,
875 'ft_minor_mime' => $minor,
876 ] )
877 ->caller( __METHOD__ )->execute();
878
879 $id = $dbw->insertId();
880 if ( !$id ) {
881 throw new RuntimeException( 'File entry could not be inserted' );
882 }
883
884 $this->fileTypeId = $id;
885 return $id;
886 }
887
892 public function upgradeRow() {
893 $dbw = $this->repo->getPrimaryDB();
894
895 // Make a DB query condition that will fail to match the image row if the
896 // image was reuploaded while the upgrade was in process.
897 $freshnessCondition = [ 'img_timestamp' => $dbw->timestamp( $this->getTimestamp() ) ];
898
899 $this->loadFromFile();
900
901 # Don't destroy file info of missing files
902 if ( !$this->fileExists ) {
903 wfDebug( __METHOD__ . ": file does not exist, aborting" );
904
905 return;
906 }
907
908 [ $major, $minor ] = self::splitMime( $this->mime );
909
910 wfDebug( __METHOD__ . ': upgrading ' . $this->getName() . " to the current schema" );
911
912 $metadata = $this->getMetadataForDb( $dbw );
913 $dbw->newUpdateQueryBuilder()
914 ->update( 'image' )
915 ->set( [
916 'img_size' => $this->size,
917 'img_width' => $this->width,
918 'img_height' => $this->height,
919 'img_bits' => $this->bits,
920 'img_media_type' => $this->media_type,
921 'img_major_mime' => $major,
922 'img_minor_mime' => $minor,
923 'img_metadata' => $metadata,
924 'img_sha1' => $this->sha1,
925 ] )
926 ->where( [ 'img_name' => $this->getName() ] )
927 ->andWhere( $freshnessCondition )
928 ->caller( __METHOD__ )->execute();
929
930 if ( $this->migrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
931 $dbw->newUpdateQueryBuilder()
932 ->update( 'filerevision' )
933 ->set( [
934 'fr_size' => $this->size,
935 'fr_width' => $this->width,
936 'fr_height' => $this->height,
937 'fr_bits' => $this->bits,
938 'fr_metadata' => $metadata,
939 'fr_sha1' => $this->sha1,
940 ] )
941 ->where( [ 'fr_file' => $this->acquireFileIdFromName() ] )
942 ->andWhere( [ 'fr_timestamp' => $dbw->timestamp( $this->getTimestamp() ) ] )
943 ->caller( __METHOD__ )->execute();
944 }
945
946 $this->invalidateCache();
947
948 $this->upgraded = true; // avoid rework/retries
949 }
950
955 protected function reserializeMetadata() {
956 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
957 return;
958 }
959 $dbw = $this->repo->getPrimaryDB();
960 $metadata = $this->getMetadataForDb( $dbw );
961 $dbw->newUpdateQueryBuilder()
962 ->update( 'image' )
963 ->set( [ 'img_metadata' => $metadata ] )
964 ->where( [
965 'img_name' => $this->name,
966 'img_timestamp' => $dbw->timestamp( $this->timestamp ),
967 ] )
968 ->caller( __METHOD__ )->execute();
969 if ( $this->migrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
970 $dbw->newUpdateQueryBuilder()
971 ->update( 'filerevision' )
972 ->set( [ 'fr_metadata' => $metadata ] )
973 ->where( [ 'fr_file' => $this->acquireFileIdFromName() ] )
974 ->andWhere( [ 'fr_timestamp' => $dbw->timestamp( $this->getTimestamp() ) ] )
975 ->caller( __METHOD__ )->execute();
976 }
977 $this->upgraded = true;
978 }
979
992 public function setProps( $info ) {
993 $this->dataLoaded = true;
994 $fields = $this->getCacheFields( '' );
995 $fields[] = 'fileExists';
996
997 foreach ( $fields as $field ) {
998 if ( isset( $info[$field] ) ) {
999 $this->$field = $info[$field];
1000 }
1001 }
1002
1003 // Only our own cache sets these properties, so they both should be present.
1004 if ( isset( $info['user'] ) &&
1005 isset( $info['user_text'] ) &&
1006 $info['user_text'] !== ''
1007 ) {
1008 $this->user = new UserIdentityValue( $info['user'], $info['user_text'] );
1009 }
1010
1011 // Fix up mime fields
1012 if ( isset( $info['major_mime'] ) ) {
1013 $this->mime = "{$info['major_mime']}/{$info['minor_mime']}";
1014 } elseif ( isset( $info['mime'] ) ) {
1015 $this->mime = $info['mime'];
1016 [ $this->major_mime, $this->minor_mime ] = self::splitMime( $this->mime );
1017 }
1018
1019 if ( isset( $info['metadata'] ) ) {
1020 if ( is_string( $info['metadata'] ) ) {
1021 $this->loadMetadataFromString( $info['metadata'] );
1022 } elseif ( is_array( $info['metadata'] ) ) {
1023 $this->metadataArray = $info['metadata'];
1024 if ( isset( $info['metadataBlobs'] ) ) {
1025 $this->metadataBlobs = $info['metadataBlobs'];
1026 $this->unloadedMetadataBlobs = array_diff_key(
1027 $this->metadataBlobs,
1028 $this->metadataArray
1029 );
1030 } else {
1031 $this->metadataBlobs = [];
1032 $this->unloadedMetadataBlobs = [];
1033 }
1034 } else {
1035 $logger = LoggerFactory::getInstance( 'LocalFile' );
1036 $logger->warning( __METHOD__ . ' given invalid metadata of type ' .
1037 get_debug_type( $info['metadata'] ) );
1038 $this->metadataArray = [];
1039 }
1040 $this->extraDataLoaded = true;
1041 }
1042 }
1043
1059 public function isMissing() {
1060 if ( $this->missing === null ) {
1061 $fileExists = $this->repo->fileExists( $this->getVirtualUrl() );
1062 $this->missing = !$fileExists;
1063 }
1064
1065 return $this->missing;
1066 }
1067
1075 public function getWidth( $page = 1 ) {
1076 $page = (int)$page;
1077 if ( $page < 1 ) {
1078 $page = 1;
1079 }
1080
1081 $this->load();
1082
1083 if ( $this->isMultipage() ) {
1084 $handler = $this->getHandler();
1085 if ( !$handler ) {
1086 return 0;
1087 }
1088 $dim = $handler->getPageDimensions( $this, $page );
1089 if ( $dim ) {
1090 return $dim['width'];
1091 } else {
1092 // For non-paged media, the false goes through an
1093 // intval, turning failure into 0, so do same here.
1094 return 0;
1095 }
1096 } else {
1097 return $this->width;
1098 }
1099 }
1100
1108 public function getHeight( $page = 1 ) {
1109 $page = (int)$page;
1110 if ( $page < 1 ) {
1111 $page = 1;
1112 }
1113
1114 $this->load();
1115
1116 if ( $this->isMultipage() ) {
1117 $handler = $this->getHandler();
1118 if ( !$handler ) {
1119 return 0;
1120 }
1121 $dim = $handler->getPageDimensions( $this, $page );
1122 if ( $dim ) {
1123 return $dim['height'];
1124 } else {
1125 // For non-paged media, the false goes through an
1126 // intval, turning failure into 0, so do same here.
1127 return 0;
1128 }
1129 } else {
1130 return $this->height;
1131 }
1132 }
1133
1141 public function getDescriptionShortUrl() {
1142 if ( !$this->title ) {
1143 return null; // Avoid hard failure when the file does not exist. T221812
1144 }
1145
1146 $pageId = $this->title->getArticleID();
1147
1148 if ( $pageId ) {
1149 $url = $this->repo->makeUrl( [ 'curid' => $pageId ] );
1150 if ( $url !== false ) {
1151 return $url;
1152 }
1153 }
1154 return null;
1155 }
1156
1163 public function getMetadata() {
1164 $data = $this->getMetadataArray();
1165 if ( !$data ) {
1166 return '';
1167 } elseif ( array_keys( $data ) === [ '_error' ] ) {
1168 // Legacy error encoding
1169 return $data['_error'];
1170 } else {
1171 return serialize( $this->getMetadataArray() );
1172 }
1173 }
1174
1181 public function getMetadataArray(): array {
1182 $this->load( self::LOAD_ALL );
1183 if ( $this->unloadedMetadataBlobs ) {
1184 return $this->getMetadataItems(
1185 array_unique( array_merge(
1186 array_keys( $this->metadataArray ),
1187 array_keys( $this->unloadedMetadataBlobs )
1188 ) )
1189 );
1190 }
1191 return $this->metadataArray;
1192 }
1193
1194 public function getMetadataItems( array $itemNames ): array {
1195 $this->load( self::LOAD_ALL );
1196 $result = [];
1197 $addresses = [];
1198 foreach ( $itemNames as $itemName ) {
1199 if ( array_key_exists( $itemName, $this->metadataArray ) ) {
1200 $result[$itemName] = $this->metadataArray[$itemName];
1201 } elseif ( isset( $this->unloadedMetadataBlobs[$itemName] ) ) {
1202 $addresses[$itemName] = $this->unloadedMetadataBlobs[$itemName];
1203 }
1204 }
1205
1206 if ( $addresses ) {
1207 $resultFromBlob = $this->metadataStorageHelper->getMetadataFromBlobStore( $addresses );
1208 foreach ( $addresses as $itemName => $address ) {
1209 unset( $this->unloadedMetadataBlobs[$itemName] );
1210 $value = $resultFromBlob[$itemName] ?? null;
1211 if ( $value !== null ) {
1212 $result[$itemName] = $value;
1213 $this->metadataArray[$itemName] = $value;
1214 }
1215 }
1216 }
1217 return $result;
1218 }
1219
1231 public function getMetadataForDb( IReadableDatabase $db ) {
1232 $this->load( self::LOAD_ALL );
1233 if ( !$this->metadataArray && !$this->metadataBlobs ) {
1234 $s = '';
1235 } elseif ( $this->repo->isJsonMetadataEnabled() ) {
1236 $s = $this->getJsonMetadata();
1237 } else {
1238 $s = serialize( $this->getMetadataArray() );
1239 }
1240 if ( !is_string( $s ) ) {
1241 throw new RuntimeException( 'Could not serialize image metadata value for DB' );
1242 }
1243 return $db->encodeBlob( $s );
1244 }
1245
1252 private function getJsonMetadata() {
1253 // Directly store data that is not already in BlobStore
1254 $envelope = [
1255 'data' => array_diff_key( $this->metadataArray, $this->metadataBlobs )
1256 ];
1257
1258 // Also store the blob addresses
1259 if ( $this->metadataBlobs ) {
1260 $envelope['blobs'] = $this->metadataBlobs;
1261 }
1262
1263 [ $s, $blobAddresses ] = $this->metadataStorageHelper->getJsonMetadata( $this, $envelope );
1264
1265 // Repeated calls to this function should not keep inserting more blobs
1266 $this->metadataBlobs += $blobAddresses;
1267
1268 return $s;
1269 }
1270
1277 private function isMetadataOversize() {
1278 if ( !$this->repo->isSplitMetadataEnabled() ) {
1279 return false;
1280 }
1281 $threshold = $this->repo->getSplitMetadataThreshold();
1282 $directItems = array_diff_key( $this->metadataArray, $this->metadataBlobs );
1283 foreach ( $directItems as $value ) {
1284 if ( strlen( $this->metadataStorageHelper->jsonEncode( $value ) ) > $threshold ) {
1285 return true;
1286 }
1287 }
1288 return false;
1289 }
1290
1299 protected function loadMetadataFromDbFieldValue( IReadableDatabase $db, $metadataBlob ) {
1300 $this->loadMetadataFromString( $db->decodeBlob( $metadataBlob ) );
1301 }
1302
1310 protected function loadMetadataFromString( $metadataString ) {
1311 $this->extraDataLoaded = true;
1312 $this->metadataArray = [];
1313 $this->metadataBlobs = [];
1314 $this->unloadedMetadataBlobs = [];
1315 $metadataString = (string)$metadataString;
1316 if ( $metadataString === '' ) {
1317 $this->metadataSerializationFormat = self::MDS_EMPTY;
1318 return;
1319 }
1320 if ( $metadataString[0] === '{' ) {
1321 $envelope = $this->metadataStorageHelper->jsonDecode( $metadataString );
1322 if ( !$envelope ) {
1323 // Legacy error encoding
1324 $this->metadataArray = [ '_error' => $metadataString ];
1325 $this->metadataSerializationFormat = self::MDS_LEGACY;
1326 } else {
1327 $this->metadataSerializationFormat = self::MDS_JSON;
1328 if ( isset( $envelope['data'] ) ) {
1329 $this->metadataArray = $envelope['data'];
1330 }
1331 if ( isset( $envelope['blobs'] ) ) {
1332 $this->metadataBlobs = $this->unloadedMetadataBlobs = $envelope['blobs'];
1333 }
1334 }
1335 } else {
1336 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1337 $data = @unserialize( $metadataString );
1338 if ( !is_array( $data ) ) {
1339 // Legacy error encoding
1340 $data = [ '_error' => $metadataString ];
1341 $this->metadataSerializationFormat = self::MDS_LEGACY;
1342 } else {
1343 $this->metadataSerializationFormat = self::MDS_PHP;
1344 }
1345 $this->metadataArray = $data;
1346 }
1347 }
1348
1353 public function getBitDepth() {
1354 $this->load();
1355
1356 return (int)$this->bits;
1357 }
1358
1364 public function getSize() {
1365 $this->load();
1366
1367 return $this->size;
1368 }
1369
1375 public function getMimeType() {
1376 $this->load();
1377
1378 return $this->mime;
1379 }
1380
1387 public function getMediaType() {
1388 $this->load();
1389
1390 return $this->media_type;
1391 }
1392
1404 public function exists() {
1405 $this->load();
1406
1407 return $this->fileExists;
1408 }
1409
1431 protected function getThumbnails( $archiveName = false ) {
1432 if ( $archiveName ) {
1433 $dir = $this->getArchiveThumbPath( $archiveName );
1434 } else {
1435 $dir = $this->getThumbPath();
1436 }
1437
1438 $backend = $this->repo->getBackend();
1439 $files = [ $dir ];
1440 try {
1441 $iterator = $backend->getFileList( [ 'dir' => $dir, 'forWrite' => true ] );
1442 if ( $iterator !== null ) {
1443 foreach ( $iterator as $file ) {
1444 $files[] = $file;
1445 }
1446 }
1447 } catch ( FileBackendError ) {
1448 } // suppress (T56674)
1449
1450 return $files;
1451 }
1452
1461 public function purgeCache( $options = [] ) {
1462 // Refresh metadata in memcached, but don't touch thumbnails or CDN
1463 $this->maybeUpgradeRow();
1464 $this->invalidateCache();
1465
1466 // Delete thumbnails
1467 $this->purgeThumbnails( $options );
1468
1469 // Purge CDN cache for this file
1470 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
1471 $hcu->purgeUrls(
1472 $this->getUrl(),
1473 !empty( $options['forThumbRefresh'] )
1474 ? $hcu::PURGE_PRESEND // just a manual purge
1475 : $hcu::PURGE_INTENT_TXROUND_REFLECTED
1476 );
1477 }
1478
1484 public function purgeOldThumbnails( $archiveName ) {
1485 // Get a list of old thumbnails
1486 $thumbs = $this->getThumbnails( $archiveName );
1487
1488 // Delete thumbnails from storage, and prevent the directory itself from being purged
1489 $dir = array_shift( $thumbs );
1490 $this->purgeThumbList( $dir, $thumbs );
1491
1492 $urls = [];
1493 foreach ( $thumbs as $thumb ) {
1494 $urls[] = $this->getArchiveThumbUrl( $archiveName, $thumb );
1495 }
1496
1497 // Purge any custom thumbnail caches
1498 $this->getHookRunner()->onLocalFilePurgeThumbnails( $this, $archiveName, $urls );
1499
1500 // Purge the CDN
1501 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
1502 $hcu->purgeUrls( $urls, $hcu::PURGE_PRESEND );
1503 }
1504
1511 public function purgeThumbnails( $options = [] ) {
1512 $thumbs = $this->getThumbnails();
1513
1514 // Delete thumbnails from storage, and prevent the directory itself from being purged
1515 $dir = array_shift( $thumbs );
1516 $this->purgeThumbList( $dir, $thumbs );
1517
1518 // Always purge all files from CDN regardless of handler filters
1519 $urls = [];
1520 foreach ( $thumbs as $thumb ) {
1521 $urls[] = $this->getThumbUrl( $thumb );
1522 }
1523
1524 // Give the media handler a chance to filter the file purge list
1525 if ( !empty( $options['forThumbRefresh'] ) ) {
1526 $handler = $this->getHandler();
1527 if ( $handler ) {
1528 $handler->filterThumbnailPurgeList( $thumbs, $options );
1529 }
1530 }
1531
1532 // Purge any custom thumbnail caches
1533 $this->getHookRunner()->onLocalFilePurgeThumbnails( $this, false, $urls );
1534
1535 // Purge the CDN
1536 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
1537 $hcu->purgeUrls(
1538 $urls,
1539 !empty( $options['forThumbRefresh'] )
1540 ? $hcu::PURGE_PRESEND // just a manual purge
1541 : $hcu::PURGE_INTENT_TXROUND_REFLECTED
1542 );
1543 }
1544
1551 public function prerenderThumbnails() {
1552 $uploadThumbnailRenderMap = MediaWikiServices::getInstance()
1553 ->getMainConfig()->get( MainConfigNames::UploadThumbnailRenderMap );
1554
1555 $jobs = [];
1556
1557 $sizes = $uploadThumbnailRenderMap;
1558 rsort( $sizes );
1559
1560 foreach ( $sizes as $size ) {
1561 if ( $this->isMultipage() ) {
1562 // (T309114) Only trigger render jobs up to MAX_PAGE_RENDER_JOBS to avoid
1563 // a flood of jobs for huge files.
1564 $pageLimit = min( $this->pageCount(), self::MAX_PAGE_RENDER_JOBS );
1565
1566 $jobs[] = new ThumbnailRenderJob(
1567 $this->getTitle(),
1568 [
1569 'transformParams' => [ 'width' => $size, 'page' => 1 ],
1570 'enqueueNextPage' => true,
1571 'pageLimit' => $pageLimit
1572 ]
1573 );
1574 } elseif ( $this->isVectorized() || $this->getWidth() > $size ) {
1575 $jobs[] = new ThumbnailRenderJob(
1576 $this->getTitle(),
1577 [ 'transformParams' => [ 'width' => $size ] ]
1578 );
1579 }
1580 }
1581
1582 if ( $jobs ) {
1583 MediaWikiServices::getInstance()->getJobQueueGroup()->lazyPush( $jobs );
1584 }
1585 }
1586
1593 protected function purgeThumbList( $dir, $files ) {
1594 $fileListDebug = strtr(
1595 var_export( $files, true ),
1596 [ "\n" => '' ]
1597 );
1598 wfDebug( __METHOD__ . ": $fileListDebug" );
1599
1600 if ( $this->repo->supportsSha1URLs() ) {
1601 $reference = $this->getSha1();
1602 } else {
1603 $reference = $this->getName();
1604 }
1605
1606 $purgeList = [];
1607 foreach ( $files as $file ) {
1608 # Check that the reference (filename or sha1) is part of the thumb name
1609 # This is a basic check to avoid erasing unrelated directories
1610 if ( str_contains( $file, $reference )
1611 || str_contains( $file, "-thumbnail" ) // "short" thumb name
1612 ) {
1613 $purgeList[] = "{$dir}/{$file}";
1614 }
1615 }
1616
1617 # Delete the thumbnails
1618 $this->repo->quickPurgeBatch( $purgeList );
1619 # Clear out the thumbnail directory if empty
1620 $this->repo->quickCleanDir( $dir );
1621 }
1622
1634 public function getHistory( $limit = null, $start = null, $end = null, $inc = true ) {
1635 if ( !$this->exists() ) {
1636 return []; // Avoid hard failure when the file does not exist. T221812
1637 }
1638
1639 $dbr = $this->repo->getReplicaDB();
1640 $oldFileQuery = FileSelectQueryBuilder::newForOldFile( $dbr )->getQueryInfo();
1641
1642 $tables = $oldFileQuery['tables'];
1643 $fields = $oldFileQuery['fields'];
1644 $join_conds = $oldFileQuery['join_conds'];
1645 $conds = $opts = [];
1646 $eq = $inc ? '=' : '';
1647 $conds[] = $dbr->expr( 'oi_name', '=', $this->title->getDBkey() );
1648
1649 if ( $start ) {
1650 $conds[] = $dbr->expr( 'oi_timestamp', "<$eq", $dbr->timestamp( $start ) );
1651 }
1652
1653 if ( $end ) {
1654 $conds[] = $dbr->expr( 'oi_timestamp', ">$eq", $dbr->timestamp( $end ) );
1655 }
1656
1657 if ( $limit ) {
1658 $opts['LIMIT'] = $limit;
1659 }
1660
1661 // Search backwards for time > x queries
1662 $order = ( !$start && $end !== null ) ? 'ASC' : 'DESC';
1663 $opts['ORDER BY'] = "oi_timestamp $order";
1664 $opts['USE INDEX'] = [ 'oldimage' => 'oi_name_timestamp' ];
1665
1666 $this->getHookRunner()->onLocalFile__getHistory( $this, $tables, $fields,
1667 $conds, $opts, $join_conds );
1668
1669 $res = $dbr->newSelectQueryBuilder()
1670 ->tables( $tables )
1671 ->fields( $fields )
1672 ->conds( $conds )
1673 ->caller( __METHOD__ )
1674 ->options( $opts )
1675 ->joinConds( $join_conds )
1676 ->fetchResultSet();
1677 $r = [];
1678
1679 foreach ( $res as $row ) {
1680 $r[] = $this->repo->newFileFromRow( $row );
1681 }
1682
1683 if ( $order == 'ASC' ) {
1684 $r = array_reverse( $r ); // make sure it ends up descending
1685 }
1686
1687 return $r;
1688 }
1689
1700 public function nextHistoryLine() {
1701 if ( !$this->exists() ) {
1702 return false; // Avoid hard failure when the file does not exist. T221812
1703 }
1704
1705 # Polymorphic function name to distinguish foreign and local fetches
1706 $fname = static::class . '::' . __FUNCTION__;
1707
1708 $dbr = $this->repo->getReplicaDB();
1709
1710 if ( $this->historyLine == 0 ) { // called for the first time, return line from cur
1711 $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr );
1712
1713 $queryBuilder->fields( [ 'oi_archive_name' => $dbr->addQuotes( '' ), 'oi_deleted' => '0' ] )
1714 ->where( [ 'img_name' => $this->title->getDBkey() ] );
1715 $this->historyRes = $queryBuilder->caller( $fname )->fetchResultSet();
1716
1717 if ( $this->historyRes->numRows() == 0 ) {
1718 $this->historyRes = null;
1719
1720 return false;
1721 }
1722 } elseif ( $this->historyLine == 1 ) {
1723 $queryBuilder = FileSelectQueryBuilder::newForOldFile( $dbr );
1724
1725 $this->historyRes = $queryBuilder->where( [ 'oi_name' => $this->title->getDBkey() ] )
1726 ->orderBy( 'oi_timestamp', SelectQueryBuilder::SORT_DESC )
1727 ->caller( $fname )->fetchResultSet();
1728 }
1729 $this->historyLine++;
1730
1731 return $this->historyRes->fetchObject();
1732 }
1733
1738 public function resetHistory() {
1739 $this->historyLine = 0;
1740
1741 if ( $this->historyRes !== null ) {
1742 $this->historyRes = null;
1743 }
1744 }
1745
1779 public function upload( $src, $comment, $pageText, $flags = 0, $props = false,
1780 $timestamp = false, ?Authority $uploader = null, $tags = [],
1781 $createDummyRevision = true, $revert = false
1782 ) {
1783 if ( $this->getRepo()->getReadOnlyReason() !== false ) {
1784 return $this->readOnlyFatalStatus();
1785 } elseif ( MediaWikiServices::getInstance()->getRevisionStore()->isReadOnly() ) {
1786 // Check this in advance to avoid writing to FileBackend and the file tables,
1787 // only to fail on insert the revision due to the text store being unavailable.
1788 return $this->readOnlyFatalStatus();
1789 }
1790
1791 $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
1792 if ( !$props ) {
1793 if ( FileRepo::isVirtualUrl( $srcPath )
1794 || FileBackend::isStoragePath( $srcPath )
1795 ) {
1796 $props = $this->repo->getFileProps( $srcPath );
1797 } else {
1798 $mwProps = new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
1799 $props = $mwProps->getPropsFromPath( $srcPath, true );
1800 }
1801 }
1802
1803 $options = [];
1804 $handler = MediaHandler::getHandler( $props['mime'] );
1805 if ( $handler ) {
1806 if ( is_string( $props['metadata'] ) ) {
1807 // This supports callers directly fabricating a metadata
1808 // property using serialize(). Normally the metadata property
1809 // comes from MWFileProps, in which case it won't be a string.
1810 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1811 $metadata = @unserialize( $props['metadata'] );
1812 } else {
1813 $metadata = $props['metadata'];
1814 }
1815
1816 if ( is_array( $metadata ) ) {
1817 $options['headers'] = $handler->getContentHeaders( $metadata );
1818 }
1819 } else {
1820 $options['headers'] = [];
1821 }
1822
1823 // Trim spaces on user supplied text
1824 $comment = trim( $comment );
1825
1826 $status = $this->publish( $src, $flags, $options );
1827
1828 if ( $status->successCount >= 2 ) {
1829 // There will be a copy+(one of move,copy,store).
1830 // The first succeeding does not commit us to updating the DB
1831 // since it simply copied the current version to a timestamped file name.
1832 // It is only *preferable* to avoid leaving such files orphaned.
1833 // Once the second operation goes through, then the current version was
1834 // updated and we must therefore update the DB too.
1835 $oldver = $status->value;
1836
1837 $uploadStatus = $this->recordUpload3(
1838 $oldver,
1839 $comment,
1840 $pageText,
1841 $uploader ?? RequestContext::getMain()->getAuthority(),
1842 $props,
1843 $timestamp,
1844 $tags,
1845 $createDummyRevision,
1846 $revert
1847 );
1848 if ( !$uploadStatus->isOK() ) {
1849 if ( $uploadStatus->hasMessage( 'filenotfound' ) ) {
1850 // update filenotfound error with more specific path
1851 $status->fatal( 'filenotfound', $srcPath );
1852 } else {
1853 $status->merge( $uploadStatus );
1854 }
1855 }
1856 }
1857
1858 return $status;
1859 }
1860
1877 public function recordUpload3(
1878 string $oldver,
1879 string $comment,
1880 string $pageText,
1881 Authority $performer,
1882 $props = false,
1883 $timestamp = false,
1884 $tags = [],
1885 bool $createDummyRevision = true,
1886 bool $revert = false
1887 ): Status {
1888 $dbw = $this->repo->getPrimaryDB();
1889
1890 # Imports or such might force a certain timestamp; otherwise we generate
1891 # it and can fudge it slightly to keep (name,timestamp) unique on re-upload.
1892 if ( $timestamp === false ) {
1893 $timestamp = $dbw->timestamp();
1894 $allowTimeKludge = true;
1895 } else {
1896 $allowTimeKludge = false;
1897 }
1898
1899 $props = $props ?: $this->repo->getFileProps( $this->getVirtualUrl() );
1900 $props['description'] = $comment;
1901 $props['timestamp'] = wfTimestamp( TS::MW, $timestamp ); // DB -> TS::MW
1902 $this->setProps( $props );
1903
1904 # Fail now if the file isn't there
1905 if ( !$this->fileExists ) {
1906 wfDebug( __METHOD__ . ": File " . $this->getRel() . " went missing!" );
1907
1908 return Status::newFatal( 'filenotfound', $this->getRel() );
1909 }
1910
1911 $mimeAnalyzer = MediaWikiServices::getInstance()->getMimeAnalyzer();
1912 if ( !$mimeAnalyzer->isValidMajorMimeType( $this->major_mime ) ) {
1913 $this->major_mime = 'unknown';
1914 }
1915
1916 $actorNormalizaton = MediaWikiServices::getInstance()->getActorNormalization();
1917
1918 // T391473: File uploads can involve moving a lot of bytes around. Sometimes in
1919 // that time the DB connection can timeout. Normally this is automatically
1920 // reconnected, but reconnection does not work inside atomic sections.
1921 // Ping the DB to ensure it is still there prior to entering the atomic
1922 // section. TODO: Refactor upload jobs to be smarter about implicit transactions.
1923 $dbw->ping();
1924 $dbw->startAtomic( __METHOD__ );
1925
1926 $actorId = $actorNormalizaton->acquireActorId( $performer->getUser(), $dbw );
1927 $this->user = $performer->getUser();
1928
1929 # Test to see if the row exists using INSERT IGNORE
1930 # This avoids race conditions by locking the row until the commit, and also
1931 # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
1932 $commentStore = MediaWikiServices::getInstance()->getCommentStore();
1933 $commentFields = $commentStore->insert( $dbw, 'img_description', $comment );
1934 $actorFields = [ 'img_actor' => $actorId ];
1935 $dbw->newInsertQueryBuilder()
1936 ->insertInto( 'image' )
1937 ->ignore()
1938 ->row( [
1939 'img_name' => $this->getName(),
1940 'img_size' => $this->size,
1941 'img_width' => intval( $this->width ),
1942 'img_height' => intval( $this->height ),
1943 'img_bits' => $this->bits,
1944 'img_media_type' => $this->media_type,
1945 'img_major_mime' => $this->major_mime,
1946 'img_minor_mime' => $this->minor_mime,
1947 'img_timestamp' => $dbw->timestamp( $timestamp ),
1948 'img_metadata' => $this->getMetadataForDb( $dbw ),
1949 'img_sha1' => $this->sha1
1950 ] + $commentFields + $actorFields )
1951 ->caller( __METHOD__ )->execute();
1952 $reupload = ( $dbw->affectedRows() == 0 );
1953
1954 $latestFileRevId = null;
1955 if ( $this->migrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
1956 if ( $reupload ) {
1957 $latestFileRevId = $dbw->newSelectQueryBuilder()
1958 ->select( 'fr_id' )
1959 ->from( 'filerevision' )
1960 ->where( [ 'fr_file' => $this->acquireFileIdFromName() ] )
1961 ->orderBy( 'fr_timestamp', 'DESC' )
1962 ->caller( __METHOD__ )
1963 ->fetchField();
1964 }
1965 $commentFieldsNew = $commentStore->insert( $dbw, 'fr_description', $comment );
1966 $dbw->newInsertQueryBuilder()
1967 ->insertInto( 'filerevision' )
1968 ->row( [
1969 'fr_file' => $this->acquireFileIdFromName(),
1970 'fr_size' => $this->size,
1971 'fr_width' => intval( $this->width ),
1972 'fr_height' => intval( $this->height ),
1973 'fr_bits' => $this->bits,
1974 'fr_actor' => $actorId,
1975 'fr_deleted' => 0,
1976 'fr_timestamp' => $dbw->timestamp( $timestamp ),
1977 'fr_metadata' => $this->getMetadataForDb( $dbw ),
1978 'fr_sha1' => $this->sha1
1979 ] + $commentFieldsNew )
1980 ->caller( __METHOD__ )->execute();
1981 $dbw->newUpdateQueryBuilder()
1982 ->update( 'file' )
1983 ->set( [ 'file_latest' => $dbw->insertId() ] )
1984 ->where( [ 'file_id' => $this->getFileIdFromName() ] )
1985 ->caller( __METHOD__ )->execute();
1986 }
1987
1988 if ( $reupload ) {
1989 $row = $dbw->newSelectQueryBuilder()
1990 ->select( [ 'img_timestamp', 'img_sha1' ] )
1991 ->from( 'image' )
1992 ->where( [ 'img_name' => $this->getName() ] )
1993 ->caller( __METHOD__ )->fetchRow();
1994
1995 if ( $row && $row->img_sha1 === $this->sha1 ) {
1996 $dbw->endAtomic( __METHOD__ );
1997 wfDebug( __METHOD__ . ": File " . $this->getRel() . " already exists!" );
1998 $title = Title::newFromText( $this->getName(), NS_FILE );
1999 return Status::newFatal( 'fileexists-no-change', $title->getPrefixedText() );
2000 }
2001
2002 if ( $allowTimeKludge ) {
2003 # Use LOCK IN SHARE MODE to ignore any transaction snapshotting
2004 $lUnixtime = $row ? (int)wfTimestamp( TS::UNIX, $row->img_timestamp ) : false;
2005 # Avoid a timestamp that is not newer than the last version
2006 # TODO: the image/oldimage tables should be like page/revision with an ID field
2007 if ( $lUnixtime && (int)wfTimestamp( TS::UNIX, $timestamp ) <= $lUnixtime ) {
2008 sleep( 1 ); // fast enough re-uploads would go far in the future otherwise
2009 $timestamp = $dbw->timestamp( $lUnixtime + 1 );
2010 $this->timestamp = wfTimestamp( TS::MW, $timestamp ); // DB -> TS::MW
2011 }
2012 }
2013
2014 $tables = [ 'image' ];
2015 $fields = [
2016 'oi_name' => 'img_name',
2017 'oi_archive_name' => $dbw->addQuotes( $oldver ),
2018 'oi_size' => 'img_size',
2019 'oi_width' => 'img_width',
2020 'oi_height' => 'img_height',
2021 'oi_bits' => 'img_bits',
2022 'oi_description_id' => 'img_description_id',
2023 'oi_timestamp' => 'img_timestamp',
2024 'oi_metadata' => 'img_metadata',
2025 'oi_media_type' => 'img_media_type',
2026 'oi_major_mime' => 'img_major_mime',
2027 'oi_minor_mime' => 'img_minor_mime',
2028 'oi_sha1' => 'img_sha1',
2029 'oi_actor' => 'img_actor',
2030 ];
2031
2032 if ( ( $this->migrationStage & SCHEMA_COMPAT_WRITE_NEW ) && $latestFileRevId && $oldver ) {
2033 $dbw->newUpdateQueryBuilder()
2034 ->update( 'filerevision' )
2035 ->set( [ 'fr_archive_name' => $oldver ] )
2036 ->where( [ 'fr_id' => $latestFileRevId ] )
2037 ->caller( __METHOD__ )->execute();
2038 }
2039
2040 $joins = [];
2041 # (T36993) Note: $oldver can be empty here, if the previous
2042 # version of the file was broken. Allow registration of the new
2043 # version to continue anyway, because that's better than having
2044 # an image that's not fixable by user operations.
2045 # Collision, this is an update of a file
2046 # Insert previous contents into oldimage
2047 $dbw->insertSelect( 'oldimage', $tables, $fields,
2048 [ 'img_name' => $this->getName() ], __METHOD__, [], [], $joins );
2049
2050 # Update the current image row
2051 $dbw->newUpdateQueryBuilder()
2052 ->update( 'image' )
2053 ->set( [
2054 'img_size' => $this->size,
2055 'img_width' => intval( $this->width ),
2056 'img_height' => intval( $this->height ),
2057 'img_bits' => $this->bits,
2058 'img_media_type' => $this->media_type,
2059 'img_major_mime' => $this->major_mime,
2060 'img_minor_mime' => $this->minor_mime,
2061 'img_timestamp' => $dbw->timestamp( $timestamp ),
2062 'img_metadata' => $this->getMetadataForDb( $dbw ),
2063 'img_sha1' => $this->sha1
2064 ] + $commentFields + $actorFields )
2065 ->where( [ 'img_name' => $this->getName() ] )
2066 ->caller( __METHOD__ )->execute();
2067 }
2068
2069 $descTitle = $this->getTitle();
2070 $descId = $descTitle->getArticleID();
2071 $wikiPage = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $descTitle );
2072 if ( !$wikiPage instanceof WikiFilePage ) {
2073 throw new UnexpectedValueException( 'Cannot obtain instance of WikiFilePage for ' . $this->getName()
2074 . ', got instance of ' . get_class( $wikiPage ) );
2075 }
2076 $wikiPage->setFile( $this );
2077
2078 // Determine log action. If reupload is done by reverting, use a special log_action.
2079 if ( $revert ) {
2080 $logAction = 'revert';
2081 } elseif ( $reupload ) {
2082 $logAction = 'overwrite';
2083 } else {
2084 $logAction = 'upload';
2085 }
2086 // Add the log entry...
2087 $logEntry = new ManualLogEntry( 'upload', $logAction );
2088 $logEntry->setTimestamp( $this->timestamp );
2089 $logEntry->setPerformer( $performer->getUser() );
2090 $logEntry->setComment( $comment );
2091 $logEntry->setTarget( $descTitle );
2092 // Allow people using the api to associate log entries with the upload.
2093 // Log has a timestamp, but sometimes different from upload timestamp.
2094 $logEntry->setParameters(
2095 [
2096 'img_sha1' => $this->sha1,
2097 'img_timestamp' => $timestamp,
2098 ]
2099 );
2100 // Note we keep $logId around since during new image
2101 // creation, page doesn't exist yet, so log_page = 0
2102 // but we want it to point to the page we're making,
2103 // so we later modify the log entry.
2104 // For a similar reason, we avoid making an RC entry
2105 // now and wait until the page exists.
2106 $logId = $logEntry->insert();
2107
2108 if ( $descTitle->exists() ) {
2109 if ( $createDummyRevision ) {
2110 $services = MediaWikiServices::getInstance();
2111 // Use own context to get the action text in content language
2112 $formatter = $services->getLogFormatterFactory()->newFromEntry( $logEntry );
2113 $formatter->setContext( RequestContext::newExtraneousContext( $descTitle ) );
2114 $editSummary = $formatter->getPlainActionText();
2115
2116 $dummyRevRecord = $wikiPage->newPageUpdater( $performer->getUser() )
2117 ->setCause( PageUpdater::CAUSE_UPLOAD )
2118 ->saveDummyRevision( $editSummary, EDIT_SILENT );
2119
2120 // Associate dummy revision id
2121 $logEntry->setAssociatedRevId( $dummyRevRecord->getId() );
2122 }
2123
2124 $newPageContent = null;
2125 } else {
2126 // Make the description page and RC log entry post-commit
2127 $newPageContent = ContentHandler::makeContent( $pageText, $descTitle );
2128 }
2129
2130 // NOTE: Even after ending this atomic section, we are probably still in the implicit
2131 // transaction started by any prior master query in the request. We cannot yet safely
2132 // schedule jobs, see T263301.
2133 $dbw->endAtomic( __METHOD__ );
2134 $fname = __METHOD__;
2135
2136 # Do some cache purges after final commit so that:
2137 # a) Changes are more likely to be seen post-purge
2138 # b) They won't cause rollback of the log publish/update above
2139 $purgeUpdate = new AutoCommitUpdate(
2140 $dbw,
2141 __METHOD__,
2142 function () use (
2143 $reupload, $wikiPage, $newPageContent, $comment, $performer,
2144 $logEntry, $logId, $descId, $tags, $fname
2145 ) {
2146 # Update memcache after the commit
2147 $this->invalidateCache();
2148
2149 $updateLogPage = false;
2150 if ( $newPageContent ) {
2151 # New file page; create the description page.
2152 # There's already a log entry, so don't make a second RC entry
2153 # CDN and file cache for the description page are purged by doUserEditContent.
2154 $revRecord = $wikiPage->newPageUpdater( $performer )
2155 ->setCause( PageUpdater::CAUSE_UPLOAD )
2156 ->setContent( SlotRecord::MAIN, $newPageContent )
2157 ->saveRevision( $comment, EDIT_NEW | EDIT_SUPPRESS_RC );
2158
2159 if ( $revRecord ) {
2160 // Associate new page revision id
2161 $logEntry->setAssociatedRevId( $revRecord->getId() );
2162
2163 // This relies on the resetArticleID() call in WikiPage::insertOn(),
2164 // which is triggered on $descTitle by doUserEditContent() above.
2165 $updateLogPage = $revRecord->getPageId();
2166 }
2167 } else {
2168 # Existing file page: invalidate description page cache
2169 $title = $wikiPage->getTitle();
2170 $title->invalidateCache();
2171 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2172 $hcu->purgeTitleUrls( $title, $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2173 # Allow the new file version to be patrolled from the page footer
2174 Article::purgePatrolFooterCache( $descId );
2175 }
2176
2177 # Update associated rev id. This should be done by $logEntry->insert() earlier,
2178 # but setAssociatedRevId() wasn't called at that point yet...
2179 $logParams = $logEntry->getParameters();
2180 $logParams['associated_rev_id'] = $logEntry->getAssociatedRevId();
2181 $update = [ 'log_params' => LogEntryBase::makeParamBlob( $logParams ) ];
2182 if ( $updateLogPage ) {
2183 # Also log page, in case where we just created it above
2184 $update['log_page'] = $updateLogPage;
2185 }
2186 $this->getRepo()->getPrimaryDB()->newUpdateQueryBuilder()
2187 ->update( 'logging' )
2188 ->set( $update )
2189 ->where( [ 'log_id' => $logId ] )
2190 ->caller( $fname )->execute();
2191
2192 $this->getRepo()->getPrimaryDB()->newInsertQueryBuilder()
2193 ->insertInto( 'log_search' )
2194 ->row( [
2195 'ls_field' => 'associated_rev_id',
2196 'ls_value' => (string)$logEntry->getAssociatedRevId(),
2197 'ls_log_id' => $logId,
2198 ] )
2199 ->caller( $fname )->execute();
2200
2201 # Add change tags, if any
2202 if ( $tags ) {
2203 $logEntry->addTags( $tags );
2204 }
2205
2206 # Uploads can be patrolled
2207 $logEntry->setIsPatrollable( true );
2208
2209 # Now that the log entry is up-to-date, make an RC entry.
2210 $logEntry->publish( $logId );
2211
2212 # Run hook for other updates (typically more cache purging)
2213 $this->getHookRunner()->onFileUpload( $this, $reupload, !$newPageContent );
2214
2215 if ( $reupload ) {
2216 # Delete old thumbnails
2217 $this->purgeThumbnails();
2218 # Remove the old file from the CDN cache
2219 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2220 $hcu->purgeUrls( $this->getUrl(), $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2221 } else {
2222 # Update backlink pages pointing to this title if created
2223 $blcFactory = MediaWikiServices::getInstance()->getBacklinkCacheFactory();
2224 LinksUpdate::queueRecursiveJobsForTable(
2225 $this->getTitle(),
2226 'imagelinks',
2227 'upload-image',
2228 $performer->getUser()->getName(),
2229 $blcFactory->getBacklinkCache( $this->getTitle() )
2230 );
2231 }
2232
2233 $this->prerenderThumbnails();
2234 }
2235 );
2236
2237 # Invalidate cache for all pages using this file
2238 $cacheUpdateJob = HTMLCacheUpdateJob::newForBacklinks(
2239 $this->getTitle(),
2240 'imagelinks',
2241 [ 'causeAction' => 'file-upload', 'causeAgent' => $performer->getUser()->getName() ]
2242 );
2243
2244 // NOTE: We are probably still in the implicit transaction started by DBO_TRX. We should
2245 // only schedule jobs after that transaction was committed, so a job queue failure
2246 // doesn't cause the upload to fail (T263301). Also, we should generally not schedule any
2247 // Jobs or the DeferredUpdates that assume the update is complete until after the
2248 // transaction has been committed and we are sure that the upload was indeed successful.
2249 $dbw->onTransactionCommitOrIdle( static function () use ( $reupload, $purgeUpdate, $cacheUpdateJob ) {
2250 DeferredUpdates::addUpdate( $purgeUpdate, DeferredUpdates::PRESEND );
2251
2252 if ( !$reupload ) {
2253 // This is a new file, so update the image count
2254 DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [ 'images' => 1 ] ) );
2255 }
2256
2257 MediaWikiServices::getInstance()->getJobQueueGroup()->lazyPush( $cacheUpdateJob );
2258 }, __METHOD__ );
2259
2260 return Status::newGood();
2261 }
2262
2279 public function publish( $src, $flags = 0, array $options = [] ) {
2280 return $this->publishTo( $src, $this->getRel(), $flags, $options );
2281 }
2282
2299 protected function publishTo( $src, $dstRel, $flags = 0, array $options = [] ) {
2300 $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
2301
2302 $repo = $this->getRepo();
2303 if ( $repo->getReadOnlyReason() !== false ) {
2304 return $this->readOnlyFatalStatus();
2305 }
2306
2307 $status = $this->acquireFileLock();
2308 if ( !$status->isOK() ) {
2309 return $status;
2310 }
2311
2312 if ( $this->isOld() ) {
2313 $archiveRel = $dstRel;
2314 $archiveName = basename( $archiveRel );
2315 } else {
2316 $archiveName = ConvertibleTimestamp::now( TS::MW ) . '!' . $this->getName();
2317 $archiveRel = $this->getArchiveRel( $archiveName );
2318 }
2319
2320 if ( $repo->hasSha1Storage() ) {
2321 $sha1 = FileRepo::isVirtualUrl( $srcPath )
2322 ? $repo->getFileSha1( $srcPath )
2323 : FSFile::getSha1Base36FromPath( $srcPath );
2325 $wrapperBackend = $repo->getBackend();
2326 '@phan-var FileBackendDBRepoWrapper $wrapperBackend';
2327 $dst = $wrapperBackend->getPathForSHA1( $sha1 );
2328 $status = $repo->quickImport( $src, $dst );
2329 if ( $flags & File::DELETE_SOURCE ) {
2330 unlink( $srcPath );
2331 }
2332
2333 if ( $this->exists() ) {
2334 $status->value = $archiveName;
2335 }
2336 } else {
2337 $flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0;
2338 $status = $repo->publish( $srcPath, $dstRel, $archiveRel, $flags, $options );
2339
2340 if ( $status->value == 'new' ) {
2341 $status->value = '';
2342 } else {
2343 $status->value = $archiveName;
2344 }
2345 }
2346
2347 $this->releaseFileLock();
2348 return $status;
2349 }
2350
2369 public function move( $target ) {
2370 $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
2371 if ( $this->getRepo()->getReadOnlyReason() !== false ) {
2372 return $this->readOnlyFatalStatus();
2373 }
2374
2375 wfDebugLog( 'imagemove', "Got request to move {$this->name} to " . $target->getText() );
2376 $batch = new LocalFileMoveBatch( $this, $target );
2377
2378 $status = $batch->addCurrent();
2379 if ( !$status->isOK() ) {
2380 return $status;
2381 }
2382 $archiveNames = $batch->addOlds();
2383 $status = $batch->execute();
2384
2385 wfDebugLog( 'imagemove', "Finished moving {$this->name}" );
2386
2387 // Purge the source and target files outside the transaction...
2388 $oldTitleFile = $localRepo->newFile( $this->title );
2389 $newTitleFile = $localRepo->newFile( $target );
2390 DeferredUpdates::addUpdate(
2391 new AutoCommitUpdate(
2392 $this->getRepo()->getPrimaryDB(),
2393 __METHOD__,
2394 static function () use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
2395 $oldTitleFile->purgeEverything();
2396 foreach ( $archiveNames as $archiveName ) {
2398 '@phan-var OldLocalFile $oldTitleFile';
2399 $oldTitleFile->purgeOldThumbnails( $archiveName );
2400 }
2401 $newTitleFile->purgeEverything();
2402 }
2403 ),
2404 DeferredUpdates::PRESEND
2405 );
2406
2407 if ( $status->isOK() ) {
2408 // Now switch the object
2409 $this->title = $target;
2410 // Force regeneration of the name and hashpath
2411 $this->name = null;
2412 $this->hashPath = null;
2413 }
2414
2415 return $status;
2416 }
2417
2434 public function deleteFile( $reason, UserIdentity $user, $suppress = false ) {
2435 if ( $this->getRepo()->getReadOnlyReason() !== false ) {
2436 return $this->readOnlyFatalStatus();
2437 }
2438
2439 $batch = new LocalFileDeleteBatch( $this, $user, $reason, $suppress );
2440
2441 $batch->addCurrent();
2442 // Get old version relative paths
2443 $archiveNames = $batch->addOlds();
2444 $status = $batch->execute();
2445
2446 if ( $status->isOK() ) {
2447 DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [ 'images' => -1 ] ) );
2448 }
2449
2450 // To avoid slow purges in the transaction, move them outside...
2451 DeferredUpdates::addUpdate(
2452 new AutoCommitUpdate(
2453 $this->getRepo()->getPrimaryDB(),
2454 __METHOD__,
2455 function () use ( $archiveNames ) {
2456 $this->purgeEverything();
2457 foreach ( $archiveNames as $archiveName ) {
2458 $this->purgeOldThumbnails( $archiveName );
2459 }
2460 }
2461 ),
2462 DeferredUpdates::PRESEND
2463 );
2464
2465 // Purge the CDN
2466 $purgeUrls = [];
2467 foreach ( $archiveNames as $archiveName ) {
2468 $purgeUrls[] = $this->getArchiveUrl( $archiveName );
2469 }
2470
2471 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2472 $hcu->purgeUrls( $purgeUrls, $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2473
2474 return $status;
2475 }
2476
2494 public function deleteOldFile( $archiveName, $reason, UserIdentity $user, $suppress = false ) {
2495 if ( $this->getRepo()->getReadOnlyReason() !== false ) {
2496 return $this->readOnlyFatalStatus();
2497 }
2498
2499 $batch = new LocalFileDeleteBatch( $this, $user, $reason, $suppress );
2500
2501 $batch->addOld( $archiveName );
2502 $status = $batch->execute();
2503
2504 $this->purgeOldThumbnails( $archiveName );
2505 if ( $status->isOK() ) {
2506 $this->purgeDescription();
2507 }
2508
2509 $url = $this->getArchiveUrl( $archiveName );
2510 $hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2511 $hcu->purgeUrls( $url, $hcu::PURGE_INTENT_TXROUND_REFLECTED );
2512
2513 return $status;
2514 }
2515
2528 public function restore( $versions = [], $unsuppress = false ) {
2529 if ( $this->getRepo()->getReadOnlyReason() !== false ) {
2530 return $this->readOnlyFatalStatus();
2531 }
2532
2533 $batch = new LocalFileRestoreBatch( $this, $unsuppress );
2534
2535 if ( !$versions ) {
2536 $batch->addAll();
2537 } else {
2538 $batch->addIds( $versions );
2539 }
2540 $status = $batch->execute();
2541 if ( $status->isGood() ) {
2542 $cleanupStatus = $batch->cleanup();
2543 $cleanupStatus->successCount = 0;
2544 $cleanupStatus->failCount = 0;
2545 $status->merge( $cleanupStatus );
2546 }
2547
2548 return $status;
2549 }
2550
2561 public function getDescriptionUrl() {
2562 // Avoid hard failure when the file does not exist. T221812
2563 return $this->title ? $this->title->getLocalURL() : false;
2564 }
2565
2575 public function getDescriptionText( ?Language $lang = null ) {
2576 if ( !$this->title ) {
2577 return false; // Avoid hard failure when the file does not exist. T221812
2578 }
2579
2580 $services = MediaWikiServices::getInstance();
2581 $page = $services->getPageStore()->getPageByReference( $this->getTitle() );
2582 if ( !$page ) {
2583 return false;
2584 }
2585
2586 if ( $lang ) {
2587 $parserOptions = ParserOptions::newFromUserAndLang(
2588 RequestContext::getMain()->getUser(),
2589 $lang
2590 );
2591 } else {
2592 $parserOptions = ParserOptions::newFromContext( RequestContext::getMain() );
2593 }
2594
2595 $parseStatus = $services->getParserOutputAccess()
2596 ->getParserOutput( $page, $parserOptions );
2597
2598 if ( !$parseStatus->isGood() ) {
2599 // Rendering failed.
2600 return false;
2601 }
2602 // TODO T371004 move runOutputPipeline out of $parserOutput
2603 return $parseStatus->getValue()->runOutputPipeline( $parserOptions, [] )->getContentHolderText();
2604 }
2605
2613 public function getUploader( int $audience = self::FOR_PUBLIC, ?Authority $performer = null ): ?UserIdentity {
2614 $this->load();
2615 if ( $audience === self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
2616 return null;
2617 } elseif ( $audience === self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $performer ) ) {
2618 return null;
2619 } else {
2620 return $this->user;
2621 }
2622 }
2623
2630 public function getDescription( $audience = self::FOR_PUBLIC, ?Authority $performer = null ) {
2631 $this->load();
2632 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) {
2633 return '';
2634 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_COMMENT, $performer ) ) {
2635 return '';
2636 } else {
2637 return $this->description;
2638 }
2639 }
2640
2645 public function getTimestamp() {
2646 $this->load();
2647
2648 return $this->timestamp;
2649 }
2650
2655 public function getDescriptionTouched() {
2656 if ( !$this->exists() ) {
2657 return false; // Avoid hard failure when the file does not exist. T221812
2658 }
2659
2660 // The DB lookup might return false, e.g. if the file was just deleted, or the shared DB repo
2661 // itself gets it from elsewhere. To avoid repeating the DB lookups in such a case, we
2662 // need to differentiate between null (uninitialized) and false (failed to load).
2663 if ( $this->descriptionTouched === null ) {
2664 $touched = $this->repo->getReplicaDB()->newSelectQueryBuilder()
2665 ->select( 'page_touched' )
2666 ->from( 'page' )
2667 ->where( [ 'page_namespace' => $this->title->getNamespace() ] )
2668 ->andWhere( [ 'page_title' => $this->title->getDBkey() ] )
2669 ->caller( __METHOD__ )->fetchField();
2670 $this->descriptionTouched = $touched ? wfTimestamp( TS::MW, $touched ) : false;
2671 }
2672
2673 return $this->descriptionTouched;
2674 }
2675
2680 public function getSha1() {
2681 $this->load();
2682 return $this->sha1;
2683 }
2684
2688 public function isCacheable() {
2689 $this->load();
2690
2691 // If extra data (metadata) was not loaded then it must have been large
2692 return $this->extraDataLoaded
2693 && strlen( serialize( $this->metadataArray ) ) <= self::CACHE_FIELD_MAX_LEN;
2694 }
2695
2704 public function acquireFileLock( $timeout = 0 ) {
2705 return Status::wrap( $this->getRepo()->getBackend()->lockFiles(
2706 [ $this->getPath() ], LockManager::LOCK_EX, $timeout
2707 ) );
2708 }
2709
2716 public function releaseFileLock() {
2717 return Status::wrap( $this->getRepo()->getBackend()->unlockFiles(
2718 [ $this->getPath() ], LockManager::LOCK_EX
2719 ) );
2720 }
2721
2732 public function lock() {
2733 if ( !$this->locked ) {
2734 $logger = LoggerFactory::getInstance( 'LocalFile' );
2735
2736 $dbw = $this->repo->getPrimaryDB();
2737 $makesTransaction = !$dbw->trxLevel();
2738 $dbw->startAtomic( self::ATOMIC_SECTION_LOCK );
2739 // T56736: use simple lock to handle when the file does not exist.
2740 // SELECT FOR UPDATE prevents changes, not other SELECTs with FOR UPDATE.
2741 // Also, that would cause contention on INSERT of similarly named rows.
2742 $status = $this->acquireFileLock( 10 ); // represents all versions of the file
2743 if ( !$status->isGood() ) {
2744 $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
2745 $logger->warning( "Failed to lock '{file}'", [ 'file' => $this->name ] );
2746
2747 throw new LocalFileLockError( $status );
2748 }
2749 // Release the lock *after* commit to avoid row-level contention.
2750 // Make sure it triggers on rollback() as well as commit() (T132921).
2751 $dbw->onTransactionResolution(
2752 function () use ( $logger ) {
2753 $status = $this->releaseFileLock();
2754 if ( !$status->isGood() ) {
2755 $logger->error( "Failed to unlock '{file}'", [ 'file' => $this->name ] );
2756 }
2757 },
2758 __METHOD__
2759 );
2760 // Callers might care if the SELECT snapshot is safely fresh
2761 $this->lockedOwnTrx = $makesTransaction;
2762 }
2763
2764 $this->locked++;
2765
2766 return $this->lockedOwnTrx;
2767 }
2768
2779 public function unlock() {
2780 if ( $this->locked ) {
2781 --$this->locked;
2782 if ( !$this->locked ) {
2783 $dbw = $this->repo->getPrimaryDB();
2784 $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
2785 $this->lockedOwnTrx = false;
2786 }
2787 }
2788 }
2789
2793 protected function readOnlyFatalStatus() {
2794 return $this->getRepo()->newFatal( 'filereadonlyerror', $this->getName(),
2795 $this->getRepo()->getName(), $this->getRepo()->getReadOnlyReason() );
2796 }
2797
2801 public function __destruct() {
2802 $this->unlock();
2803 }
2804}
2805
2807class_alias( LocalFile::class, 'LocalFile' );
const SCHEMA_COMPAT_OLD
Definition Defines.php:305
const NS_FILE
Definition Defines.php:57
const EDIT_SUPPRESS_RC
Definition Defines.php:126
const SCHEMA_COMPAT_WRITE_NEW
Definition Defines.php:297
const EDIT_SILENT
Do not notify other users (e.g.
Definition Defines.php:123
const EDIT_NEW
Article is assumed to be non-existent, fail if it exists.
Definition Defines.php:114
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:68
MimeMagic helper wrapper.
Base media handler class.
Base class for content handling.
Group all the pieces relevant to the context of a request into one instance.
Deferrable Update for closure/callback updates that should use auto-commit mode.
Defer callable updates to run later in the PHP process.
Class the manages updates of *_link tables as well as similar extension-managed tables.
Class for handling updates to the site_stats table.
Proxy backend that manages file layout rewriting for FileRepo.
Base class for file repositories.
Definition FileRepo.php:52
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:79
FileRepo LocalRepo ForeignAPIRepo false $repo
Some member variables can be lazy-initialised using __get().
Definition File.php:126
Title string false $title
Definition File.php:129
Local file in the wiki's own database.
Definition LocalFile.php:81
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new localfile object.
isMissing()
splitMime inherited
invalidateCache()
Purge the file object/metadata cache.
loadFromRow( $row, $prefix='img_')
Load file metadata from a DB result row.
unprefixRow( $row, $prefix='img_')
getDescriptionShortUrl()
Get short description URL for a file based on the page ID.
getMetadataForDb(IReadableDatabase $db)
Serialize the metadata array for insertion into img_metadata, oi_metadata or fa_metadata.
getWidth( $page=1)
Return the width of the image.
publishTo( $src, $dstRel, $flags=0, array $options=[])
Move or copy a file to a specified location.
static newFromTitle( $title, $repo, $unused=null)
Create a LocalFile from a title Do not call this except from inside a repo class.
getMetadataItems(array $itemNames)
Get multiple elements of the unserialized handler-specific metadata.
int $filerevision_id
id in filerevision table, null on read old
int $file_id
id in file table, null on read old
string $sha1
SHA-1 base 36 content hash.
bool $fileExists
Does the file exist on disk? (loadFromXxx)
getMimeType()
Returns the MIME type of the file.
string $mime
MIME type, determined by MimeAnalyzer::guessMimeType.
int $bits
Returned by getimagesize (loadFromXxx)
getCacheFields( $prefix='img_')
Returns the list of object properties that are included as-is in the cache.
string[] $unloadedMetadataBlobs
Map of metadata item name to blob address for items that exist but have not yet been loaded into $thi...
deleteFile( $reason, UserIdentity $user, $suppress=false)
Delete all versions of the file.
getLazyCacheFields( $prefix='img_')
Returns the list of object properties that are included as-is in the cache, only when they're not too...
getHeight( $page=1)
Return the height of the image.
setProps( $info)
Set properties in this object to be equal to those given in the associative array $info.
array $metadataArray
Unserialized metadata.
loadMetadataFromString( $metadataString)
Unserialize a metadata string which came from some non-DB source, or is the return value of IReadable...
string[] $metadataBlobs
Map of metadata item name to blob address.
loadMetadataFromDbFieldValue(IReadableDatabase $db, $metadataBlob)
Unserialize a metadata blob which came from the database and store it in $this.
load( $flags=0)
Load file metadata from cache or DB, unless already loaded.
getDescriptionText(?Language $lang=null)
Get the HTML text of the description page This is not used by ImagePage for local files,...
getMetadata()
Get handler-specific metadata as a serialized string.
reserializeMetadata()
Write the metadata back to the database with the current serialization format.
static newFromRow( $row, $repo)
Create a LocalFile from a title Do not call this except from inside a repo class.
deleteOldFile( $archiveName, $reason, UserIdentity $user, $suppress=false)
Delete an old version of the file.
unlock()
Decrement the lock reference count and end the atomic section if it reaches zero.
acquireFileIdFromName()
This is mostly for the migration period.
int $size
Size in bytes (loadFromXxx)
loadFromFile( $path=null)
Load metadata from the file itself.
string $media_type
MEDIATYPE_xxx (bitmap, drawing, audio...)
getDescriptionUrl()
isMultipage inherited
getSize()
Returns the size of the image file, in bytes.
publish( $src, $flags=0, array $options=[])
Move or copy a file to its public location.
nextHistoryLine()
Returns the history of this file, line by line.
string null $metadataSerializationFormat
One of the MDS_* constants, giving the format of the metadata as stored in the DB,...
bool $dataLoaded
Whether or not core data has been loaded from the database (loadFromXxx)
lock()
Start an atomic DB section and lock the image for update or increments a reference counter if the loc...
acquireFileLock( $timeout=0)
Acquire an exclusive lock on the file, indicating an intention to write to the file backend.
bool $extraDataLoaded
Whether or not lazy-loaded data has been loaded from the database.
getThumbnails( $archiveName=false)
getTransformScript inherited
prerenderThumbnails()
Prerenders a configurable set of thumbnails.
getUploader(int $audience=self::FOR_PUBLIC, ?Authority $performer=null)
getFileIdFromName()
This is mostly for the migration period.
__construct( $title, $repo)
Do not call this except from inside a repo class.
getHistory( $limit=null, $start=null, $end=null, $inc=true)
purgeDescription inherited
getDescription( $audience=self::FOR_PUBLIC, ?Authority $performer=null)
__destruct()
Clean up any dangling locks.
move( $target)
getLinksTo inherited
recordUpload3(string $oldver, string $comment, string $pageText, Authority $performer, $props=false, $timestamp=false, $tags=[], bool $createDummyRevision=true, bool $revert=false)
Record a file upload in the upload log and the image table (version 3)
loadFromDB( $flags=0)
Load file metadata from the DB.
resetHistory()
Reset the history pointer to the first element of the history.
int $deleted
Bitfield akin to rev_deleted.
getMediaType()
Returns the type of the media in the file.
loadExtraFromDB()
Load lazy file metadata from the DB.
static newFromKey( $sha1, $repo, $timestamp=false)
Create a LocalFile from a SHA-1 key Do not call this except from inside a repo class.
purgeThumbnails( $options=[])
Delete cached transformed files for the current version only.
restore( $versions=[], $unsuppress=false)
Restore all or specified deleted revisions to the given file.
maybeUpgradeRow()
Upgrade a row if it needs it.
getCacheKey()
Get the memcached key for the main data for this file, or false if there is no access to the shared c...
releaseFileLock()
Release a lock acquired with acquireFileLock().
purgeCache( $options=[])
Delete all previously generated thumbnails, refresh metadata in memcached and purge the CDN.
purgeOldThumbnails( $archiveName)
Delete cached transformed files for an archived version only.
upload( $src, $comment, $pageText, $flags=0, $props=false, $timestamp=false, ?Authority $uploader=null, $tags=[], $createDummyRevision=true, $revert=false)
getHashPath inherited
purgeThumbList( $dir, $files)
Delete a list of thumbnails visible at urls.
upgradeRow()
Fix assorted version-related problems with the image row by reloading it from the file.
getMetadataArray()
Get unserialized handler-specific metadata.
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:45
Job to purge the HTML/file cache for all pages that link to or use another page or file.
Job for asynchronous rendering of thumbnails, e.g.
Base class for language-specific code.
Definition Language.php:70
Create PSR-3 logger objects.
Extends the LogEntry Interface with some basic functionality.
Class for creating new log entries and inserting them into the database.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Legacy class representing an editable page and handling UI for some page actions.
Definition Article.php:64
Special handling for representing file pages.
Set options of the Parser.
Value object representing a content slot associated with a page revision.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Controller-like object for creating and updating pages by creating new revisions.
Represents a title within MediaWiki.
Definition Title.php:70
loadFromRow( $row)
Load Title object fields from a DB row.
Definition Title.php:562
Value object representing a user's identity.
Class representing a non-directory file on the file system.
Definition FSFile.php:21
File backend exception for checked exceptions (e.g.
Base class for all file backend classes (including multi-write backends).
Resource locking handling.
Build SELECT queries with a fluent interface.
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'sodipodi'=> ' $path/sodipodi -z -w $width -f $input -e $output', 'inkscape'=> ' $path/inkscape -z -w $width -f $input -e $output', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'imgserv'=> ' $path/imgserv-wrapper -i svg -o png -w$width $input $output', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> false, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 250, 300,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailStepsRatio'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'EnableSpecialMute'=> false, 'EnableUserEmailMuteList'=> false, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'UseLegacyMediaStyles' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'BlockTargetMigrationStage' => 768, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCache' => false, ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'BlockTargetMigrationStage' => 'integer', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'RCEngines' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCache' => 'boolean', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], 'required' => [ 'url', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
This interface represents the authority associated with the current execution context,...
Definition Authority.php:23
getUser()
Returns the performer of the actions associated with this authority.
Interface for objects representing user identity.
addQuotes( $s)
Escape and quote a raw value string for use in a SQL query.
Interface for database access objects.
A database connection without write operations.
newSelectQueryBuilder()
Create an empty SelectQueryBuilder which can be used to run queries against this connection.
encodeBlob( $b)
Some DBMSs have a special format for inserting into blob fields, they don't allow simple quoted strin...
decodeBlob( $b)
Some DBMSs return a special placeholder object representing blob fields in result objects.
expr(string $field, string $op, $value)
See Expression::__construct()
Result wrapper for grabbing data queried from an IDatabase object.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...