Skip to content

Commit 35faecf

Browse files
Merge pull request #1304 from equalizedigital/steve/PRO-464/activation-redirect
Added: activation redirect to welcome page and add corresponding tests
2 parents 91d01bb + 49258ec commit 35faecf

4 files changed

Lines changed: 370 additions & 0 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
/**
3+
* Handles plugin activation redirect to welcome page.
4+
*
5+
* @package Accessibility_Checker\Admin
6+
*/
7+
8+
namespace EDAC\Admin;
9+
10+
if ( ! defined( 'ABSPATH' ) ) {
11+
exit; // Exit if accessed directly.
12+
}
13+
14+
/**
15+
* Class Activation_Redirect
16+
*
17+
* Handles redirecting to the welcome page after plugin activation.
18+
*
19+
* @since 1.35.0
20+
*/
21+
class Activation_Redirect {
22+
23+
/**
24+
* The page slug for the welcome page.
25+
*
26+
* @since 1.35.0
27+
* @var string
28+
*/
29+
const WELCOME_PAGE_SLUG = 'accessibility_checker';
30+
31+
/**
32+
* Initialize the activation redirect.
33+
*
34+
* @since 1.35.0
35+
*/
36+
public function init(): void {
37+
add_action( 'admin_init', [ $this, 'maybe_redirect_to_welcome' ] );
38+
}
39+
40+
/**
41+
* Get the welcome page URL.
42+
*
43+
* @since 1.35.0
44+
* @return string The URL to the welcome page.
45+
*/
46+
public function get_welcome_page_url(): string {
47+
return admin_url( 'admin.php?page=' . self::WELCOME_PAGE_SLUG );
48+
}
49+
50+
/**
51+
* Redirect to welcome page after activation if conditions are met.
52+
*
53+
* This will only redirect if:
54+
* - The activation redirect transient is set
55+
* - We're not doing an AJAX request
56+
* - We're not in the network admin (multisite)
57+
* - We're not activating multiple plugins at once
58+
* - User has permission to access the welcome page
59+
*
60+
* @since 1.35.0
61+
* @return void
62+
*/
63+
public function maybe_redirect_to_welcome(): void {
64+
// Check if the activation redirect transient exists.
65+
if ( ! get_transient( 'edac_activation_redirect' ) ) {
66+
return;
67+
}
68+
69+
// Don't redirect during AJAX requests.
70+
if ( wp_doing_ajax() ) {
71+
return;
72+
}
73+
74+
// Don't redirect during REST API requests.
75+
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
76+
return;
77+
}
78+
79+
// Don't redirect in network admin (multisite).
80+
if ( is_network_admin() ) {
81+
return;
82+
}
83+
84+
// Don't redirect if multiple plugins are being activated at once.
85+
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We're checking $_GET, not processing form data.
86+
if ( isset( $_GET['activate-multi'] ) ) {
87+
return;
88+
}
89+
90+
// Don't redirect if user doesn't have permission to see the welcome page.
91+
// Uses 'edit_posts' to match the welcome page's capability check (see includes/options-page.php).
92+
// This allows Authors and above to access the welcome page, as intended by the plugin design.
93+
if ( ! current_user_can( 'edit_posts' ) ) {
94+
return;
95+
}
96+
97+
// Don't redirect if we're in a test environment.
98+
if ( defined( 'WP_TESTS_DOMAIN' ) ) {
99+
return;
100+
}
101+
102+
// Delete the transient to prevent redirect loops.
103+
delete_transient( 'edac_activation_redirect' );
104+
105+
// Perform the redirect to the welcome page.
106+
wp_safe_redirect( $this->get_welcome_page_url() );
107+
exit;
108+
}
109+
}

admin/class-admin.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public function init(): void {
7676
$admin_footer_text = new Admin_Footer_Text();
7777
$admin_footer_text->init();
7878

79+
$activation_redirect = new Activation_Redirect();
80+
$activation_redirect->init();
81+
7982
$this->init_ajax();
8083

8184
$this->meta_boxes->init_hooks();

includes/activation.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ function edac_activation() {
2222

2323
// This is an add_option on purpose to not overwrite user settings on update.
2424
add_option( 'edacp_ignore_user_roles', [ 'administrator' ] );
25+
26+
// Set transient to trigger redirect to welcome page.
27+
// This will be checked on admin_init and deleted after redirect.
28+
set_transient( 'edac_activation_redirect', true, 60 );
2529
}
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
<?php
2+
/**
3+
* PHPUnit tests for the Activation_Redirect class.
4+
*
5+
* @package Accessibility_Checker\Tests
6+
*/
7+
8+
use PHPUnit\Framework\TestCase;
9+
use EDAC\Admin\Activation_Redirect;
10+
11+
/**
12+
* Class Activation_Redirect_Test
13+
*
14+
* @covers \EDAC\Admin\Activation_Redirect
15+
*/
16+
class ActivationRedirectTest extends WP_UnitTestCase {
17+
18+
/**
19+
* Instance of the Activation_Redirect class.
20+
*
21+
* @var Activation_Redirect $activation_redirect.
22+
*/
23+
private $activation_redirect;
24+
25+
/**
26+
* Set up the test fixture.
27+
*/
28+
protected function setUp(): void {
29+
parent::setUp();
30+
$this->activation_redirect = new Activation_Redirect();
31+
32+
// Clear any existing transients.
33+
delete_transient( 'edac_activation_redirect' );
34+
35+
// Clear any $_GET parameters.
36+
unset( $_GET['activate-multi'] );
37+
38+
// Intercept redirects to prevent headers being sent during tests.
39+
add_filter(
40+
'wp_redirect',
41+
static function ( $location ) {
42+
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Test exception message.
43+
throw new Exception( 'Redirect to: ' . $location );
44+
}
45+
);
46+
}
47+
48+
/**
49+
* Clean up after each test.
50+
*/
51+
protected function tearDown(): void {
52+
// Clean up transient and global state.
53+
delete_transient( 'edac_activation_redirect' );
54+
unset( $_GET['activate-multi'] );
55+
remove_all_filters( 'wp_doing_ajax' );
56+
remove_all_filters( 'is_network_admin' );
57+
remove_all_filters( 'wp_redirect' );
58+
parent::tearDown();
59+
}
60+
61+
/**
62+
* Test instantiation of Activation_Redirect class.
63+
*/
64+
public function test_can_instantiate_class() {
65+
$this->assertInstanceOf( Activation_Redirect::class, $this->activation_redirect );
66+
}
67+
68+
/**
69+
* Test that init() adds the admin_init action.
70+
*/
71+
public function test_init_adds_action() {
72+
$this->activation_redirect->init();
73+
$this->assertNotFalse( has_action( 'admin_init', [ $this->activation_redirect, 'maybe_redirect_to_welcome' ] ) );
74+
}
75+
76+
/**
77+
* Test that redirect does not happen when transient is not set.
78+
*/
79+
public function test_no_redirect_without_transient() {
80+
// Ensure transient doesn't exist.
81+
delete_transient( 'edac_activation_redirect' );
82+
83+
// Set up admin user.
84+
$admin_user = $this->factory->user->create( [ 'role' => 'administrator' ] );
85+
wp_set_current_user( $admin_user );
86+
87+
// Call the method - it should return early.
88+
$this->activation_redirect->maybe_redirect_to_welcome();
89+
90+
// If we get here without a redirect and no transient, the test passes.
91+
$this->assertFalse( get_transient( 'edac_activation_redirect' ) );
92+
}
93+
94+
/**
95+
* Test that transient is deleted only when redirect happens.
96+
*
97+
* Note: This test expects a redirect exception to be thrown because
98+
* WP_TESTS_DOMAIN is defined in the test environment, preventing the
99+
* actual redirect. In a real scenario without WP_TESTS_DOMAIN, the
100+
* redirect would occur and the transient would be deleted.
101+
*/
102+
public function test_transient_is_deleted_only_on_redirect() {
103+
// Set the transient.
104+
set_transient( 'edac_activation_redirect', true, 60 );
105+
106+
// Set up admin user.
107+
$admin_user = $this->factory->user->create( [ 'role' => 'administrator' ] );
108+
wp_set_current_user( $admin_user );
109+
110+
// Call the method - it will return early due to WP_TESTS_DOMAIN.
111+
$this->activation_redirect->maybe_redirect_to_welcome();
112+
113+
// Transient should still exist because redirect didn't happen.
114+
$this->assertTrue( (bool) get_transient( 'edac_activation_redirect' ) );
115+
}
116+
117+
/**
118+
* Test that redirect does not happen during AJAX requests.
119+
*/
120+
public function test_no_redirect_during_ajax() {
121+
// Set the transient.
122+
set_transient( 'edac_activation_redirect', true, 60 );
123+
124+
// Set up admin user.
125+
$admin_user = $this->factory->user->create( [ 'role' => 'administrator' ] );
126+
wp_set_current_user( $admin_user );
127+
128+
// Simulate AJAX request.
129+
add_filter(
130+
'wp_doing_ajax',
131+
function () {
132+
return true;
133+
}
134+
);
135+
136+
// Call the method - it should return early.
137+
$this->activation_redirect->maybe_redirect_to_welcome();
138+
139+
// Transient should still exist because no redirect happened.
140+
$this->assertTrue( (bool) get_transient( 'edac_activation_redirect' ) );
141+
}
142+
143+
/**
144+
* Test that redirect does not happen during bulk plugin activation.
145+
*/
146+
public function test_no_redirect_during_bulk_activation() {
147+
// Set the transient.
148+
set_transient( 'edac_activation_redirect', true, 60 );
149+
150+
// Set up admin user.
151+
$admin_user = $this->factory->user->create( [ 'role' => 'administrator' ] );
152+
wp_set_current_user( $admin_user );
153+
154+
// Simulate bulk activation.
155+
$_GET['activate-multi'] = 'true';
156+
157+
// Call the method - it should return early.
158+
$this->activation_redirect->maybe_redirect_to_welcome();
159+
160+
// Transient should still exist because no redirect happened.
161+
$this->assertTrue( (bool) get_transient( 'edac_activation_redirect' ) );
162+
}
163+
164+
/**
165+
* Test that redirect does not happen for users without proper capabilities.
166+
*/
167+
public function test_no_redirect_without_proper_capability() {
168+
// Set the transient.
169+
set_transient( 'edac_activation_redirect', true, 60 );
170+
171+
// Set up subscriber user (no edit_posts capability).
172+
$subscriber = $this->factory->user->create( [ 'role' => 'subscriber' ] );
173+
wp_set_current_user( $subscriber );
174+
175+
// Call the method - it should return early.
176+
$this->activation_redirect->maybe_redirect_to_welcome();
177+
178+
// Transient should still exist because no redirect happened.
179+
$this->assertTrue( (bool) get_transient( 'edac_activation_redirect' ) );
180+
}
181+
182+
/**
183+
* Test that redirect does not happen in network admin (multisite).
184+
*
185+
* Note: This test is skipped because is_network_admin() checks the WP_NETWORK_ADMIN
186+
* constant which cannot be easily mocked in unit tests. The network admin check is
187+
* covered by integration testing in actual multisite environments.
188+
*/
189+
public function test_no_redirect_in_network_admin() {
190+
$this->markTestSkipped(
191+
'Cannot reliably test network admin context in unit tests. ' .
192+
'The is_network_admin() function checks WP_NETWORK_ADMIN constant ' .
193+
'which cannot be mocked. This is covered by integration tests.'
194+
);
195+
}
196+
197+
/**
198+
* Test activation function sets the transient.
199+
*/
200+
public function test_activation_sets_transient() {
201+
// Ensure transient doesn't exist initially.
202+
delete_transient( 'edac_activation_redirect' );
203+
204+
// Mock the Accessibility_Statement class since it's required by activation.
205+
if ( ! class_exists( 'EDAC\Admin\Accessibility_Statement' ) ) {
206+
require_once EDAC_PLUGIN_DIR . 'admin/class-accessibility-statement.php';
207+
}
208+
209+
// Run the activation function.
210+
edac_activation();
211+
212+
// Check that the transient was set.
213+
$this->assertTrue( (bool) get_transient( 'edac_activation_redirect' ) );
214+
215+
// Clean up.
216+
delete_transient( 'edac_activation_redirect' );
217+
}
218+
219+
/**
220+
* Test that the correct redirect URL is returned.
221+
*/
222+
public function test_get_welcome_page_url_returns_correct_url() {
223+
$url = $this->activation_redirect->get_welcome_page_url();
224+
225+
// Verify the URL is properly formed and points to the welcome page.
226+
$this->assertStringContainsString( 'admin.php?page=accessibility_checker', $url );
227+
$this->assertStringContainsString( 'wp-admin', $url );
228+
229+
// Verify it's a valid admin URL.
230+
$expected_url = admin_url( 'admin.php?page=accessibility_checker' );
231+
$this->assertEquals( $expected_url, $url );
232+
}
233+
234+
/**
235+
* Test that redirect does not happen in test environment.
236+
*/
237+
public function test_no_redirect_in_test_environment() {
238+
// Set the transient.
239+
set_transient( 'edac_activation_redirect', true, 60 );
240+
241+
// Set up admin user.
242+
$admin_user = $this->factory->user->create( [ 'role' => 'administrator' ] );
243+
wp_set_current_user( $admin_user );
244+
245+
// WP_TESTS_DOMAIN should be defined in test environment.
246+
$this->assertTrue( defined( 'WP_TESTS_DOMAIN' ), 'WP_TESTS_DOMAIN should be defined in test environment' );
247+
248+
// Call the method - it should return early without redirecting.
249+
$this->activation_redirect->maybe_redirect_to_welcome();
250+
251+
// Transient should still exist because no redirect happened.
252+
$this->assertTrue( (bool) get_transient( 'edac_activation_redirect' ) );
253+
}
254+
}

0 commit comments

Comments
 (0)