Introduce a service to represent wfReadOnly() and friends.
It's necessary to have two service instances, one for wfReadOnly() and
one for wfConfiguredReadOnlyReason(), to avoid a circular dependency,
since LoadBalancer needs the configured reason during construction, but
wfReadOnly() needs to query the currently active load balancer.
Not having a cache of the configuration makes it possible to dynamically
change the configuration. Ideally things would not change the
configuration, and I removed such instances in core, but to support
extensions, I added a test ensuring that the configuration can be changed.
Change-Id: I9bbee946c10742526d3423208efd68cb3cc5a7ee
'Config' => __DIR__ . '/includes/config/Config.php',
'ConfigException' => __DIR__ . '/includes/config/ConfigException.php',
'ConfigFactory' => __DIR__ . '/includes/config/ConfigFactory.php',
+ 'ConfiguredReadOnlyMode' => __DIR__ . '/includes/ReadOnlyMode.php',
'ConstantDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
'Content' => __DIR__ . '/includes/content/Content.php',
'ContentHandler' => __DIR__ . '/includes/content/ContentHandler.php',
'RawAction' => __DIR__ . '/includes/actions/RawAction.php',
'RawMessage' => __DIR__ . '/includes/Message.php',
'ReadOnlyError' => __DIR__ . '/includes/exception/ReadOnlyError.php',
+ 'ReadOnlyMode' => __DIR__ . '/includes/ReadOnlyMode.php',
'ReassignEdits' => __DIR__ . '/maintenance/reassignEdits.php',
'RebuildAll' => __DIR__ . '/maintenance/rebuildall.php',
'RebuildFileCache' => __DIR__ . '/maintenance/rebuildFileCache.php',
* @return bool
*/
function wfReadOnly() {
- return wfReadOnlyReason() !== false;
+ return \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
+ ->isReadOnly();
}
/**
* @return string|bool String when in read-only mode; false otherwise
*/
function wfReadOnlyReason() {
- $readOnly = wfConfiguredReadOnlyReason();
- if ( $readOnly !== false ) {
- return $readOnly;
- }
-
- static $lbReadOnly = null;
- if ( $lbReadOnly === null ) {
- // Callers use this method to be aware that data presented to a user
- // may be very stale and thus allowing submissions can be problematic.
- $lbReadOnly = wfGetLB()->getReadOnlyReason();
- }
-
- return $lbReadOnly;
+ return \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
+ ->getReason();
}
/**
* @since 1.27
*/
function wfConfiguredReadOnlyReason() {
- global $wgReadOnly, $wgReadOnlyFile;
-
- if ( $wgReadOnly === null ) {
- // Set $wgReadOnly for faster access next time
- if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
- $wgReadOnly = file_get_contents( $wgReadOnlyFile );
- } else {
- $wgReadOnly = false;
- }
- }
-
- return $wgReadOnly;
+ return \MediaWiki\MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
+ ->getReason();
}
/**
return $this->getService( 'VirtualRESTServiceClient' );
}
+ /**
+ * @since 1.29
+ * @return \ReadOnlyMode
+ */
+ public function getConfiguredReadOnlyMode() {
+ return $this->getService( 'ConfiguredReadOnlyMode' );
+ }
+
+ /**
+ * @since 1.29
+ * @return \ReadOnlyMode
+ */
+ public function getReadOnlyMode() {
+ return $this->getService( 'ReadOnlyMode' );
+ }
+
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service getter here, don't forget to add a test
// case for it in MediaWikiServicesTest::provideGetters() and in
--- /dev/null
+<?php
+
+use Wikimedia\Rdbms\LoadBalancer;
+
+/**
+ * A service class for fetching the wiki's current read-only mode.
+ * To obtain an instance, use MediaWikiServices::getReadOnlyMode().
+ *
+ * @since 1.29
+ */
+class ReadOnlyMode {
+ private $configuredReadOnly;
+ private $loadBalancer;
+
+ public function __construct( ConfiguredReadOnlyMode $cro, LoadBalancer $loadBalancer ) {
+ $this->configuredReadOnly = $cro;
+ $this->loadBalancer = $loadBalancer;
+ }
+
+ /**
+ * Check whether the wiki is in read-only mode.
+ *
+ * @return bool
+ */
+ public function isReadOnly() {
+ return $this->getReason() !== false;
+ }
+
+ /**
+ * Check if the site is in read-only mode and return the message if so
+ *
+ * This checks the configuration and registered DB load balancers for
+ * read-only mode. This may result in DB connection being made.
+ *
+ * @return string|bool String when in read-only mode; false otherwise
+ */
+ public function getReason() {
+ $reason = $this->configuredReadOnly->getReason();
+ if ( $reason !== false ) {
+ return $reason;
+ }
+ $reason = $this->loadBalancer->getReadOnlyReason();
+ if ( $reason !== false && $reason !== null ) {
+ return $reason;
+ }
+ return false;
+ }
+
+ /**
+ * Set the read-only mode, which will apply for the remainder of the
+ * request or until a service reset.
+ */
+ public function setReason( $msg ) {
+ $this->configuredReadOnly->setReason( $msg );
+ }
+
+ /**
+ * Clear the cache of the read only file
+ */
+ public function clearCache() {
+ $this->configuredReadOnly->clearCache();
+ }
+}
+
+/**
+ * A read-only mode service which does not depend on LoadBalancer.
+ * To obtain an instance, use MediaWikiServices::getConfiguredReadOnlyMode().
+ *
+ * @since 1.29
+ */
+class ConfiguredReadOnlyMode {
+ private $config;
+ private $fileReason;
+ private $overrideReason;
+
+ public function __construct( Config $config ) {
+ $this->config = $config;
+ }
+
+ /**
+ * Check whether the wiki is in read-only mode.
+ *
+ * @return bool
+ */
+ public function isReadOnly() {
+ return $this->getReason() !== false;
+ }
+
+ /**
+ * Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
+ *
+ * @return string|bool String when in read-only mode; false otherwise
+ */
+ public function getReason() {
+ if ( $this->overrideReason !== null ) {
+ return $this->overrideReason;
+ }
+ $confReason = $this->config->get( 'ReadOnly' );
+ if ( $confReason !== null ) {
+ return $confReason;
+ }
+ if ( $this->fileReason === null ) {
+ // Cache for faster access next time
+ $readOnlyFile = $this->config->get( 'ReadOnlyFile' );
+ if ( is_file( $readOnlyFile ) && filesize( $readOnlyFile ) > 0 ) {
+ $this->fileReason = file_get_contents( $readOnlyFile );
+ } else {
+ $this->fileReason = false;
+ }
+ }
+ return $this->fileReason;
+ }
+
+ /**
+ * Set the read-only mode, which will apply for the remainder of the
+ * request or until a service reset.
+ */
+ public function setReason( $msg ) {
+ $this->overrideReason = $msg;
+ }
+
+ /**
+ * Clear the cache of the read only file
+ */
+ public function clearCache() {
+ $this->fileReason = null;
+ }
+}
$lbConf = MWLBFactory::applyDefaultConfig(
$mainConfig->get( 'LBFactoryConf' ),
- $mainConfig
+ $mainConfig,
+ $services->getConfiguredReadOnlyMode()
);
$class = MWLBFactory::getLBFactoryClass( $lbConf );
'WatchedItemStore' => function( MediaWikiServices $services ) {
$store = new WatchedItemStore(
$services->getDBLoadBalancer(),
- new HashBagOStuff( [ 'maxKeys' => 100 ] )
+ new HashBagOStuff( [ 'maxKeys' => 100 ] ),
+ $services->getReadOnlyMode()
);
$store->setStatsdDataFactory( $services->getStatsdDataFactory() );
return $store;
return $vrsClient;
},
+ 'ConfiguredReadOnlyMode' => function( MediaWikiServices $services ) {
+ return new ConfiguredReadOnlyMode( $services->getMainConfig() );
+ },
+
+ 'ReadOnlyMode' => function( MediaWikiServices $services ) {
+ return new ReadOnlyMode(
+ $services->getConfiguredReadOnlyMode(),
+ $services->getDBLoadBalancer()
+ );
+ },
+
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service here, don't forget to add a getter function
// in the MediaWikiServices class. The convenience getter should just call
*/
private $loadBalancer;
+ /**
+ * @var ReadOnlyMode
+ */
+ private $readOnlyMode;
+
/**
* @var HashBagOStuff
*/
/**
* @param LoadBalancer $loadBalancer
* @param HashBagOStuff $cache
+ * @param ReadOnlyMode $readOnlyMode
*/
public function __construct(
LoadBalancer $loadBalancer,
- HashBagOStuff $cache
+ HashBagOStuff $cache,
+ ReadOnlyMode $readOnlyMode
) {
$this->loadBalancer = $loadBalancer;
$this->cache = $cache;
+ $this->readOnlyMode = $readOnlyMode;
$this->stats = new NullStatsdDataFactory();
$this->deferredUpdatesAddCallableUpdateCallback = [ 'DeferredUpdates', 'addCallableUpdate' ];
$this->revisionGetTimestampFromIdCallback = [ 'Revision', 'getTimestampFromId' ];
* @return bool success
*/
public function addWatchBatchForUser( User $user, array $targets ) {
- if ( $this->loadBalancer->getReadOnlyReason() !== false ) {
+ if ( $this->readOnlyMode->isReadOnly() ) {
return false;
}
// Only loggedin user can have a watchlist
*/
public function removeWatch( User $user, LinkTarget $target ) {
// Only logged in user can have a watchlist
- if ( $this->loadBalancer->getReadOnlyReason() !== false || $user->isAnon() ) {
+ if ( $this->readOnlyMode->isReadOnly() || $user->isAnon() ) {
return false;
}
*/
public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 ) {
// Only loggedin user can have a watchlist
- if ( $this->loadBalancer->getReadOnlyReason() !== false || $user->isAnon() ) {
+ if ( $this->readOnlyMode->isReadOnly() || $user->isAnon() ) {
return false;
}
/**
* @param array $lbConf Config for LBFactory::__construct()
* @param Config $mainConfig Main config object from MediaWikiServices
+ * @param ConfiguredReadOnlyMode $readOnlyMode
* @return array
*/
- public static function applyDefaultConfig( array $lbConf, Config $mainConfig ) {
+ public static function applyDefaultConfig( array $lbConf, Config $mainConfig,
+ ConfiguredReadOnlyMode $readOnlyMode
+ ) {
global $wgCommandLineMode;
static $typesWithSchema = [ 'postgres', 'msssql' ];
'errorLogger' => [ MWExceptionHandler::class, 'logException' ],
'cliMode' => $wgCommandLineMode,
'hostname' => wfHostname(),
- // TODO: replace the global wfConfiguredReadOnlyReason() with a service.
- 'readOnlyReason' => wfConfiguredReadOnlyReason(),
+ 'readOnlyReason' => $readOnlyMode->getReason(),
];
// When making changes here, remember to also specify MediaWiki-specific options
}
public function finalSetup() {
- global $wgDebugToolbar, $wgUseFileCache, $wgReadOnly;
+ global $wgDebugToolbar, $wgUseFileCache;
$this->enabled = $wgUseFileCache;
// Script will handle capturing output and saving it itself
// Has to be done before Setup.php initialize MWDebug
$wgDebugToolbar = false;
// Avoid DB writes (like enotif/counters)
- $wgReadOnly = 'Building cache'; // avoid DB writes (like enotif/counters)
+ MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
+ ->setReason( 'Building cache' );
parent::finalSetup();
}
$this->dbw = $this->getDB( DB_MASTER );
$this->dryrun = $this->hasOption( 'dry-run' );
if ( $this->dryrun ) {
- $GLOBALS['wgReadOnly'] = 'Dry run mode, image upgrades are suppressed';
+ MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
+ ->setReason( 'Dry run mode, image upgrades are suppressed' );
}
if ( $this->hasOption( 'missing' ) ) {
}
/**
+ * Intended to cover the relevant bits of ServiceWiring.php, as well as GlobalFunctions.php
* @covers ::wfReadOnly
*/
public function testReadOnlyEmpty() {
global $wgReadOnly;
$wgReadOnly = null;
+ MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()->clearCache();
$this->assertFalse( wfReadOnly() );
$this->assertFalse( wfReadOnly() );
}
/**
+ * Intended to cover the relevant bits of ServiceWiring.php, as well as GlobalFunctions.php
* @covers ::wfReadOnly
*/
public function testReadOnlySet() {
global $wgReadOnly, $wgReadOnlyFile;
+ $readOnlyMode = MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
+ $readOnlyMode->clearCache();
+
$f = fopen( $wgReadOnlyFile, "wt" );
fwrite( $f, 'Message' );
fclose( $f );
$this->assertTrue( wfReadOnly() ); # Check cached
unlink( $wgReadOnlyFile );
- $wgReadOnly = null; # Clean cache
-
+ $readOnlyMode->clearCache();
$this->assertFalse( wfReadOnly() );
$this->assertFalse( wfReadOnly() );
}
+ /**
+ * This behaviour could probably be deprecated. Several extensions rely on it as of 1.29.
+ * @covers ::wfReadOnlyReason
+ */
+ public function testReadOnlyGlobalChange() {
+ $this->assertFalse( wfReadOnlyReason() );
+ $this->setMwGlobals( [
+ 'wgReadOnly' => 'reason'
+ ] );
+ $this->assertSame( 'reason', wfReadOnlyReason() );
+ }
+
public static function provideArrayToCGI() {
return [
[ [], '' ], // empty
--- /dev/null
+<?php
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * @group Database
+ *
+ * @covers ReadOnlyMode
+ * @covers ConfiguredReadOnlyMode
+ */
+class ReadOnlyModeTest extends MediaWikiTestCase {
+ public function provider() {
+ $rawTests = [
+ 'None of anything' => [
+ 'confMessage' => null,
+ 'hasFileName' => false,
+ 'fileContents' => false,
+ 'lbMessage' => false,
+ 'expectedState' => false,
+ 'expectedMessage' => false,
+ 'expectedConfState' => false,
+ 'expectedConfMessage' => false
+ ],
+ 'File missing' => [
+ 'confMessage' => null,
+ 'hasFileName' => true,
+ 'fileContents' => false,
+ 'lbMessage' => false,
+ 'expectedState' => false,
+ 'expectedMessage' => false,
+ 'expectedConfState' => false,
+ 'expectedConfMessage' => false
+ ],
+ 'File empty' => [
+ 'confMessage' => null,
+ 'hasFileName' => true,
+ 'fileContents' => '',
+ 'lbMessage' => false,
+ 'expectedState' => false,
+ 'expectedMessage' => false,
+ 'expectedConfState' => false,
+ 'expectedConfMessage' => false
+ ],
+ 'File has message' => [
+ 'confMessage' => null,
+ 'hasFileName' => true,
+ 'fileContents' => 'Message',
+ 'lbMessage' => false,
+ 'expectedState' => true,
+ 'expectedMessage' => 'Message',
+ 'expectedConfState' => true,
+ 'expectedConfMessage' => 'Message',
+ ],
+ 'Conf has message' => [
+ 'confMessage' => 'Message',
+ 'hasFileName' => false,
+ 'fileContents' => false,
+ 'lbMessage' => false,
+ 'expectedState' => true,
+ 'expectedMessage' => 'Message',
+ 'expectedConfState' => true,
+ 'expectedConfMessage' => 'Message'
+ ],
+ "Conf=false means don't check the file" => [
+ 'confMessage' => false,
+ 'hasFileName' => true,
+ 'fileContents' => 'Message',
+ 'lbMessage' => false,
+ 'expectedState' => false,
+ 'expectedMessage' => false,
+ 'expectedConfState' => false,
+ 'expectedConfMessage' => false,
+ ],
+ 'LB has message' => [
+ 'confMessage' => null,
+ 'hasFileName' => false,
+ 'fileContents' => false,
+ 'lbMessage' => 'Message',
+ 'expectedState' => true,
+ 'expectedMessage' => 'Message',
+ 'expectedConfState' => false,
+ 'expectedConfMessage' => false
+ ],
+ 'All three have a message: conf wins' => [
+ 'confMessage' => 'conf',
+ 'hasFileName' => true,
+ 'fileContents' => 'file',
+ 'lbMessage' => 'lb',
+ 'expectedState' => true,
+ 'expectedMessage' => 'conf',
+ 'expectedConfState' => true,
+ 'expectedConfMessage' => 'conf'
+ ]
+ ];
+ $cookedTests = [];
+ foreach ( $rawTests as $desc => $test ) {
+ $cookedTests[$desc] = [ $test ];
+ }
+ return $cookedTests;
+ }
+
+ private function createMode( $params, $makeLB ) {
+ $config = new HashConfig( [
+ 'ReadOnly' => $params['confMessage'],
+ 'ReadOnlyFile' => $this->createFile( $params ),
+ ] );
+
+ $rom = new ConfiguredReadOnlyMode( $config );
+
+ if ( $makeLB ) {
+ $lb = $this->createLB( $params );
+ $rom = new ReadOnlyMode( $rom, $lb );
+ }
+
+ return $rom;
+ }
+
+ private function createLB( $params ) {
+ $lb = $this->getMockBuilder( \Wikimedia\Rdbms\LoadBalancer::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $lb->expects( $this->any() )->method( 'getReadOnlyReason' )
+ ->willReturn( $params['lbMessage'] );
+ return $lb;
+ }
+
+ private function createFile( $params ) {
+ if ( $params['hasFileName'] ) {
+ $fileName = $this->getNewTempFile();
+
+ if ( $params['fileContents'] === false ) {
+ unlink( $fileName );
+ } else {
+ file_put_contents( $fileName, $params['fileContents'] );
+ }
+ } else {
+ $fileName = null;
+ }
+ return $fileName;
+ }
+
+ /**
+ * @dataProvider provider
+ */
+ public function testWithLB( $params ) {
+ $rom = $this->createMode( $params, true );
+ $this->assertSame( $params['expectedMessage'], $rom->getReason() );
+ $this->assertSame( $params['expectedState'], $rom->isReadOnly() );
+ }
+
+ /**
+ * @dataProvider provider
+ */
+ public function testWithoutLB( $params ) {
+ $cro = $this->createMode( $params, false );
+ $this->assertSame( $params['expectedConfMessage'], $cro->getReason() );
+ $this->assertSame( $params['expectedConfState'], $cro->isReadOnly() );
+ }
+
+ public function testSetReadOnlyReason() {
+ $rom = $this->createMode(
+ [
+ 'confMessage' => 'conf',
+ 'hasFileName' => false,
+ 'fileContents' => false,
+ 'lbMessage' => 'lb'
+ ],
+ true );
+ $rom->setReason( 'override' );
+ $this->assertSame( 'override', $rom->getReason() );
+ }
+
+ /**
+ * @covers ReadOnlyMode::clearCache
+ * @covers ConfiguredReadOnlyMode::clearCache
+ */
+ public function testClearCache() {
+ $fileName = $this->getNewTempFile();
+ unlink( $fileName );
+ $config = new HashConfig( [
+ 'ReadOnly' => null,
+ 'ReadOnlyFile' => $fileName,
+ ] );
+ $cro = new ConfiguredReadOnlyMode( $config );
+ $lb = $this->createLB( [ 'lbMessage' => false ] );
+ $rom = new ReadOnlyMode( $cro, $lb );
+
+ $this->assertSame( false, $rom->getReason(), 'initial' );
+
+ file_put_contents( $fileName, 'file' );
+ $this->assertSame( false, $rom->getReason(), 'stale' );
+
+ $rom->clearCache();
+ $this->assertSame( 'file', $rom->getReason(), 'fresh' );
+ }
+}
*/
private function getMockLoadBalancer(
$mockDb,
- $expectedConnectionType = null,
- $readOnlyReason = false
+ $expectedConnectionType = null
) {
$mock = $this->getMockBuilder( LoadBalancer::class )
->disableOriginalConstructor()
->method( 'getConnectionRef' )
->will( $this->returnValue( $mockDb ) );
}
- $mock->expects( $this->any() )
- ->method( 'getReadOnlyReason' )
- ->will( $this->returnValue( $readOnlyReason ) );
return $mock;
}
return $mock;
}
+ /**
+ * @return PHPUnit_Framework_MockObject_MockObject|ReadOnlyMode
+ */
+ private function getMockReadOnlyMode( $readOnly = false ) {
+ $mock = $this->getMockBuilder( ReadOnlyMode::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mock->expects( $this->any() )
+ ->method( 'isReadOnly' )
+ ->will( $this->returnValue( $readOnly ) );
+ return $mock;
+ }
+
/**
* @param int $id
* @return PHPUnit_Framework_MockObject_MockObject|User
return $fakeRow;
}
- private function newWatchedItemStore( LoadBalancer $loadBalancer, HashBagOStuff $cache ) {
+ private function newWatchedItemStore( LoadBalancer $loadBalancer, HashBagOStuff $cache,
+ ReadOnlyMode $readOnlyMode
+ ) {
return new WatchedItemStore(
$loadBalancer,
- $cache
+ $cache,
+ $readOnlyMode
);
}
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals( 12, $store->countWatchedItems( $user ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals( 7, $store->countWatchers( $titleValue ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$expected = [
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$expected = [
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals( 7, $store->countVisitingWatchers( $titleValue, '111' ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$expected = [
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$expected = [
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$expected = [
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals( 9, $store->countUnreadNotifications( $user ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertSame(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $this->getMockCache()
+ $this->getMockCache(),
+ $this->getMockReadOnlyMode()
);
$store->duplicateEntry(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$store->duplicateEntry(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$store->duplicateAllAssociatedEntries(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$store->duplicateAllAssociatedEntries(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$store->addWatch(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$store->addWatch(
public function testAddWatchBatchForUser_readOnlyDBReturnsFalse() {
$store = $this->newWatchedItemStore(
- $this->getMockLoadBalancer( $this->getMockDb(), null, 'Some Reason' ),
- $this->getMockCache()
+ $this->getMockLoadBalancer( $this->getMockDb() ),
+ $this->getMockCache(),
+ $this->getMockReadOnlyMode( true )
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$mockUser = $this->getMockNonAnonUserWithId( 1 );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertTrue(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$watchedItem = $store->loadWatchedItem(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertTrue(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$watchedItem = $store->getWatchedItem(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$user = $this->getMockNonAnonUserWithId( 1 );
$store = $this->newWatchedItemStore(
$mockLoadBalancer,
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$watchedItems = $store->getWatchedItemsForUser(
public function testGetWatchedItemsForUser_badSortOptionThrowsException() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb() ),
- $this->getMockCache()
+ $this->getMockCache(),
+ $this->getMockReadOnlyMode()
);
$this->setExpectedException( 'InvalidArgumentException' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertTrue(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertFalse(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
// Note: This does not actually assert the job is correct
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
// Note: This does not actually assert the job is correct
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$callableCallCounter = 0;
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$addUpdateCallCounter = 0;
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$callableCallCounter = 0;
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$addUpdateCallCounter = 0;
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$addUpdateCallCounter = 0;
public function testSetNotificationTimestampsForUser_anonUser() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb() ),
- $this->getMockCache()
+ $this->getMockCache(),
+ $this->getMockReadOnlyMode()
);
$this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(), '' ) );
}
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $this->getMockCache()
+ $this->getMockCache(),
+ $this->getMockReadOnlyMode()
);
$this->assertTrue(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $this->getMockCache()
+ $this->getMockCache(),
+ $this->getMockReadOnlyMode()
);
$this->assertTrue(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $this->getMockCache()
+ $this->getMockCache(),
+ $this->getMockReadOnlyMode()
);
$this->assertTrue(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$this->assertEquals(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
$watchers = $store->updateNotificationTimestamp(
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
- $mockCache
+ $mockCache,
+ $this->getMockReadOnlyMode()
);
// This will add the item to the cache
$this->manager->checkAccountCreatePermissions( new \User )
);
- $this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
+ $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
+ $readOnlyMode->setReason( 'Because' );
$this->assertEquals(
\Status::newFatal( 'readonlytext', 'Because' ),
$this->manager->checkAccountCreatePermissions( new \User )
);
- $this->setMwGlobals( [ 'wgReadOnly' => false ] );
+ $readOnlyMode->setReason( false );
$wgGroupPermissions['*']['createaccount'] = false;
$status = $this->manager->checkAccountCreatePermissions( new \User );
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->assertSame( 'noname', $ret->message->getKey() );
- $this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
+ $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
+ $readOnlyMode->setReason( 'Because' );
$this->hook( 'LocalUserCreated', $this->never() );
$userReq->username = self::usernameForCreation();
$ret = $this->manager->beginAccountCreation( $creator, [ $userReq ], 'http://localhost/' );
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->assertSame( 'readonlytext', $ret->message->getKey() );
$this->assertSame( [ 'Because' ], $ret->message->getParams() );
- $this->setMwGlobals( [ 'wgReadOnly' => false ] );
+ $readOnlyMode->setReason( false );
$this->hook( 'LocalUserCreated', $this->never() );
$userReq->username = self::usernameForCreation();
$this->request->getSession()->setSecret( 'AuthManager::accountCreationState',
[ 'username' => $creator->getName() ] + $session );
- $this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
+ $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
+ $readOnlyMode->setReason( 'Because' );
$this->hook( 'LocalUserCreated', $this->never() );
$ret = $this->manager->continueAccountCreation( [] );
$this->unhook( 'LocalUserCreated' );
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->assertSame( 'readonlytext', $ret->message->getKey() );
$this->assertSame( [ 'Because' ], $ret->message->getParams() );
- $this->setMwGlobals( [ 'wgReadOnly' => false ] );
+ $readOnlyMode->setReason( false );
$this->request->getSession()->setSecret( 'AuthManager::accountCreationState',
[ 'username' => $creator->getName() ] + $session );
// Wiki is read-only
$session->clear();
- $this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
+ $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
+ $readOnlyMode->setReason( 'Because' );
$user = \User::newFromName( $username );
$this->hook( 'LocalUserCreated', $this->never() );
$ret = $this->manager->autoCreateUser( $user, AuthManager::AUTOCREATE_SOURCE_SESSION, true );
[ LogLevel::DEBUG, 'denied by wfReadOnly(): {reason}' ],
], $logger->getBuffer() );
$logger->clearBuffer();
- $this->setMwGlobals( [ 'wgReadOnly' => false ] );
+ $readOnlyMode->setReason( false );
// Session blacklisted
$session->clear();