getServiceContainer(); return new SpecialNuke( $services->getJobQueueGroup(), $services->getDBLoadBalancerFactory(), $services->getPermissionManager(), $services->getRepoGroup(), $services->getUserFactory(), $services->getUserOptionsLookup(), $services->getUserNamePrefixSearch(), $services->getUserNameUtils(), $services->getNamespaceInfo(), $services->getContentLanguage(), $services->getService( 'NukeIPLookup' ) ); } /** * Ensure that the prompt prevents a user blocked from deleting * pages from accessing the form. * * @return void */ public function testBlocked() { $user = $this->getTestSysop()->getUser(); $performer = new UltimateAuthority( $user ); // Self-blocks should still prevent the form from being shown $this->getServiceContainer() ->getBlockUserFactory() ->newBlockUser( $user, $performer, 'infinity', 'SpecialNukeTest::testBlocked' ) ->placeBlockUnsafe(); $this->expectException( UserBlockedError::class ); $this->executeSpecialPage( '', null, 'qqx', $performer ); $this->getServiceContainer() ->getUnblockUserFactory() ->newUnblockUser( $user, $performer, 'SpecialNukeTest::testBlocked' ) ->unblockUnsafe(); } public function testProtectedPage() { $pages = []; $pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN )[ 'title' ]; $pages[] = $this->insertPage( 'Page456', 'Test', NS_MAIN )[ 'title' ]; $pages[] = $this->insertPage( 'Page789', 'Test', NS_MAIN )[ 'title' ]; $this->overrideConfigValues( [ "GroupPermissions" => [ "testgroup" => [ "nuke" => true, "delete" => true ] ] ] ); $services = $this->getServiceContainer(); $page = $services->getWikiPageFactory() ->newFromTitle( $pages[2] ); $restrictions = []; foreach ( $services->getRestrictionStore()->listApplicableRestrictionTypes( $page ) as $type ) { $restrictions[$type] = "sysop"; } $cascade = false; $page->doUpdateRestrictions( $restrictions, [], $cascade, "test", $this->getTestSysop()->getUser() ); $testUser = $this->getTestUser( [ "testgroup" ] ); $request = new FauxRequest( [ 'action' => 'delete', 'wpDeleteReasonList' => 'other', 'wpReason' => 'Reason', 'pages' => $pages, 'wpFormIdentifier' => 'nukelist', 'wpEditToken' => $testUser->getUser()->getEditToken(), ], true ); $this->expectException( PermissionsError::class ); $this->executeSpecialPage( '', $request, 'qqx', $testUser->getAuthority() ); foreach ( $pages as $checkedPage ) { $this->assertTrue( $checkedPage->exists() ); } } public function testPrompt() { $admin = $this->getTestSysop()->getUser(); $this->disableAutoCreateTempUser(); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', null, 'qqx', $performer ); $this->assertStringContainsString( '(nuke-summary)', $html ); $this->assertStringContainsString( '(nuke-tools)', $html ); } /** * Ensure that the prompt prevents a nuke user without the checkuser-temporary-account permission * from performing CheckUser IP lookups * * @return void */ public function testPromptCheckUserNoPermission() { $this->expectException( PermissionsError::class ); $this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' ); $this->enableAutoCreateTempUser(); $ip = '1.2.3.4'; $adminUser = $this->getTestSysop()->getUser(); $permissionManager = $this->getServiceContainer()->getPermissionManager(); $permissions = $permissionManager->getUserPermissions( $adminUser ); $permissions = array_diff( $permissions, [ 'checkuser-temporary-account' ] ); $permissionManager->overrideUserRightsForTesting( $adminUser, $permissions ); $performer = new UltimateAuthority( $adminUser ); $request = new FauxRequest( [ 'target' => $ip, 'action' => 'submit', ], true ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); } /** * Ensure that the prompt prevents a nuke user who hasn't accepted the agreement * from performing CheckUser IP lookups * * @return void */ public function testPromptCheckUserNoPreference() { $this->expectException( ErrorPageError::class ); $this->expectExceptionMessage( 'To view temporary account contributions for an IP, please accept' . ' the agreement in [[Special:Preferences|your preferences]].' ); $this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' ); $this->enableAutoCreateTempUser(); $ip = '1.2.3.4'; $this->overrideConfigValues( [ 'GroupPermissions' => [ 'testgroup' => [ 'nuke' => true, 'checkuser-temporary-account' => true ] ] ] ); $adminUser = $this->getTestUser( [ 'testgroup' ] )->getUser(); $request = new FauxRequest( [ 'target' => $ip, 'action' => 'submit', ], true ); $adminPerformer = new UltimateAuthority( $adminUser ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer ); } /** * Ensure that the prompt displays the correct messages when * temp accounts and CheckUser are enabled * * @return void */ public function testPromptWithCheckUser() { $this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' ); $admin = $this->getTestSysop()->getUser(); $this->enableAutoCreateTempUser(); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', null, 'qqx', $performer ); $this->assertStringContainsString( '(nuke-summary)', $html ); $this->assertStringContainsString( '(nuke-tools-tempaccount)', $html ); } public function testPromptTarget() { $testUser = $this->getTestUser(); $performer = $testUser->getAuthority(); $this->editPage( 'Target1', 'test', "", NS_MAIN, $performer ); $this->editPage( 'Target2', 'test', "", NS_MAIN, $performer ); $adminUser = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'target' => $testUser->getUser()->getName() ] ); $adminPerformer = new UltimateAuthority( $adminUser ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer ); $this->assertStringContainsString( 'Target1', $html ); $this->assertStringContainsString( 'Target2', $html ); } /** * Ensure that the prompt works with anon IP searches when * temp accounts are disabled * * @return void */ public function testPromptTargetAnonUser() { $this->disableAutoCreateTempUser( [ 'known' => false ] ); $ip = '127.0.0.1'; $testUser = $this->getServiceContainer()->getUserFactory()->newAnonymous( $ip ); $performer = new UltimateAuthority( $testUser ); $this->editPage( 'Target1', 'test', "", NS_MAIN, $performer ); $this->editPage( 'Target2', 'test', "", NS_MAIN, $performer ); $adminUser = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'target' => $testUser->getUser()->getName() ] ); $adminPerformer = new UltimateAuthority( $adminUser ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer ); $this->assertStringContainsString( '(nuke-list:', $html ); $this->assertStringContainsString( 'Target1', $html ); $this->assertStringContainsString( 'Target2', $html ); $this->assertEquals( 2, substr_count( $html, '(nuke-editby: 127.0.0.1)' ) ); } /** * Ensure that the prompt returns temp accounts from IP lookups when * temp accounts and CheckUser are enabled * * @return void */ public function testPromptTargetCheckUser() { $this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' ); $this->enableAutoCreateTempUser(); $ip = '1.2.3.4'; RequestContext::getMain()->getRequest()->setIP( $ip ); $testUser = $this->getServiceContainer()->getTempUserCreator() ->create( null, new FauxRequest() )->getUser(); $this->editPage( 'Target1', 'test', "", NS_MAIN, $testUser ); $this->editPage( 'Target2', 'test', "", NS_MAIN, $testUser ); $adminUser = $this->getTestSysop()->getUser(); $permissionManager = $this->getServiceContainer()->getPermissionManager(); $permissionManager->overrideUserRightsForTesting( $adminUser, array_merge( $permissionManager->getUserPermissions( $adminUser ), [ 'checkuser-temporary-account-no-preference' ] ) ); $request = new FauxRequest( [ 'target' => $ip, 'action' => 'submit', ], true ); $adminPerformer = new UltimateAuthority( $adminUser ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer ); $this->assertEquals( 2, substr_count( $html, '(nuke-editby: ~2024-1)' ) ); $this->assertStringContainsString( '(nuke-list-tempaccount:', $html ); $this->assertStringContainsString( 'Target1', $html ); $this->assertStringContainsString( 'Target2', $html ); } /** * Ensure that the prompt returns temp accounts and IP accounts from IP lookups when * temp accounts and CheckUser are enabled and Anonymous IP accounts exist * * @return void */ public function testPromptTargetCheckUserMixed() { $this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' ); $this->disableAutoCreateTempUser( [ 'known' => false ] ); $ip = '1.2.3.4'; $testUser = $this->getServiceContainer()->getUserFactory()->newAnonymous( $ip ); $performer = new UltimateAuthority( $testUser ); // create a page as an anonymous IP user $this->editPage( 'Target1', 'test', "", NS_MAIN, $performer ); $this->enableAutoCreateTempUser(); RequestContext::getMain()->getRequest()->setIP( $ip ); $testUser = $this->getServiceContainer()->getTempUserCreator() ->create( null, new FauxRequest() )->getUser(); $performer = new UltimateAuthority( $testUser ); // create a page as a temp user $this->editPage( 'Target2', 'test', "", NS_MAIN, $performer ); $adminUser = $this->getTestSysop()->getUser(); $permissionManager = $this->getServiceContainer()->getPermissionManager(); $permissionManager->overrideUserRightsForTesting( $adminUser, array_merge( $permissionManager->getUserPermissions( $adminUser ), [ 'checkuser-temporary-account-no-preference' ] ) ); $request = new FauxRequest( [ 'target' => $ip, 'action' => 'submit', ], true ); $adminPerformer = new UltimateAuthority( $adminUser ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer ); $this->assertSame( 1, substr_count( $html, ' (nuke-editby: ~2024-1)' ) ); $this->assertSame( 1, substr_count( $html, ' (nuke-editby: 1.2.3.4)' ) ); // They should all show up together $this->assertStringContainsString( '(nuke-list-tempaccount:', $html ); $this->assertStringContainsString( 'Target1', $html ); $this->assertStringContainsString( 'Target2', $html ); } public function testListNoPagesGlobal() { $admin = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'action' => 'submit', 'pattern' => 'ThisPageShouldNotExist-' . rand() ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $this->assertStringContainsString( '(nuke-nopages-global)', $html ); $this->assertStringNotContainsString( '(nuke-nopages)', $html ); } public function testListNoPagesUser() { $admin = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'action' => 'submit', 'target' => 'ThisPageShouldNotExist-' . rand() ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $this->assertStringNotContainsString( '(nuke-nopages-global)', $html ); $this->assertStringContainsString( 'nuke-nopages', $html ); } public function testListNamespace() { $this->editPage( 'NukeUserPageTarget', 'test', '', NS_USER ); $admin = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'action' => 'submit', 'pattern' => 'NukeUserPageTarget', 'namespace' => NS_USER ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $expectedTitle = Title::makeTitle( NS_USER, 'NukeUserPageTarget' ) ->getPrefixedText(); $this->assertStringContainsString( $expectedTitle, $html ); } public function testListTalk() { $this->editPage( 'NukeTalkPageTarget', 'test', '', NS_TALK ); $admin = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'action' => 'submit', 'pattern' => 'NukeTalkPageTarget' ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $expectedTitle = Title::makeTitle( NS_TALK, 'NukeTalkPageTarget' ) ->getPrefixedText(); $this->assertStringContainsString( $expectedTitle, $html ); } public function testListCapitalizedNamespace() { $this->overrideConfigValues( [ 'CapitalLinks' => false, 'CapitalLinkOverrides' => [] ] ); $this->editPage( 'uncapsTarget', 'test' ); $this->editPage( 'UncapsTarget', 'test' ); $admin = $this->getTestSysop()->getUser(); $expectedTitle = Title::makeTitle( NS_MAIN, 'uncapsTarget' ) ->getPrefixedText(); $shouldMatch = [ "%ncapsTarget", "u%", "uncapsTarget" ]; $shouldNotMatch = [ "UncapsTarget" ]; foreach ( $shouldMatch as $match ) { $request = new FauxRequest( [ 'action' => 'submit', 'pattern' => 'uncapsTarget' ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $this->assertStringContainsString( $expectedTitle, $html, "match: $match" ); foreach ( $shouldNotMatch as $noMatch ) { $this->assertStringNotContainsString( $noMatch, $html ); } } } public function testListCapitalizedNamespaceOverrides() { $overriddenNamespaces = [ NS_PROJECT => true, NS_MEDIAWIKI => true, NS_USER => true, ]; $this->overrideConfigValues( [ 'CapitalLinks' => false, 'CapitalLinkOverrides' => $overriddenNamespaces ] ); $overriddenNamespaces[ NS_MAIN ] = false; foreach ( $overriddenNamespaces as $ns => $override ) { $this->editPage( "UncapsTarget" . $ns, 'test', '', $ns ); // If capital links for this ns is `true`, then the existing page should be edited. $this->editPage( "uncapsTarget" . $ns, 'test2', '', $ns ); } $admin = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'action' => 'submit', 'pattern' => 'u%' ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $this->assertStringContainsString( "uncapsTarget" . NS_MAIN, $html ); // Check that the overridden namespaces are included in the search $this->assertStringContainsString( "UncapsTarget" . NS_PROJECT, $html ); $this->assertStringContainsString( "UncapsTarget" . NS_MEDIAWIKI, $html ); $this->assertStringContainsString( "UncapsTarget" . NS_USER, $html ); $this->assertStringNotContainsString( "UncapsTarget" . NS_MAIN, $html ); // Check that the non-overridden namespaces are not included in the search $this->assertStringNotContainsString( "uncapsTarget" . NS_PROJECT, $html ); $this->assertStringNotContainsString( "uncapsTarget" . NS_MEDIAWIKI, $html ); $this->assertStringNotContainsString( "uncapsTarget" . NS_USER, $html ); } /** * Check if Nuke still works with languages with complicated capitalization * or no capitalization. * * Since the first letter of each title should be capitalized, searching for the * lowercase version should still yield the uppercase version, or the same character * if casing does not exist for the language. * * @return void */ public function testListCapitalized() { /** @noinspection SpellCheckingInspection */ $testedLanguages = [ "ee" => [ "Ülemiste", "ülemiste" ], "zh" => "你好", "ja" => "にほんご", ]; foreach ( $testedLanguages as $lang => $testData ) { [ $created, $wanted ] = is_array( $testData ) ? $testData : [ $testData, $testData ]; $this->overrideConfigValue( 'LanguageCode', $lang ); $this->editPage( $created, 'test' ); $admin = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'action' => 'submit', 'pattern' => $wanted ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $this->assertStringContainsString( $created, $html ); } } public function testListLimit() { $this->editPage( 'Page1', 'test' ); $this->editPage( 'Page2', 'test' ); $this->editPage( 'Page3', 'test' ); $admin = $this->getTestSysop()->getUser(); $request = new FauxRequest( [ 'action' => 'submit', 'pattern' => 'Page%', 'limit' => 2 ], true ); $performer = new UltimateAuthority( $admin ); [ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer ); $this->assertEquals( 2, substr_count( $html, '