-
Notifications
You must be signed in to change notification settings - Fork 820
Connection: protected owner class #43601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+779
−0
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
fa64f42
Protected owner class
bindlegirl f6a2f92
Hook into user creation
bindlegirl dae7e8a
phan and renamed email param
bindlegirl ff5363d
Add tests
bindlegirl 397552e
Minor error string change
bindlegirl 2eb32f9
Update test
bindlegirl 09a4e1c
Try fixing tests
bindlegirl dd6289d
Slimming the class
bindlegirl e02f21d
Update tests
bindlegirl a0ecb00
Everything goes to error_handler
bindlegirl 2d2cac7
Set user id to0
bindlegirl e91d3eb
Unset invalid_connection_owner on WoA
bindlegirl 8000b3a
Remove all errors
bindlegirl 6e6d8a4
Update tests
bindlegirl 9c3a65f
Update tests vol 2
bindlegirl a24762e
Adding support for pre-populating emails
bindlegirl 09bc440
Fix test
bindlegirl 86d76ad
Prepopulate role
bindlegirl 2f0aa11
Fix populating logic to depend on url
bindlegirl bb74fae
Simplify
bindlegirl ee213d8
Improve messaging
bindlegirl 0585965
Fix test
bindlegirl a7d3868
Disable chackbox
bindlegirl 5d3923d
Remove invitations
bindlegirl 5cfd8bd
jquery to vanilla js
bindlegirl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
projects/plugins/wpcomsh/changelog/add-protected_owner_error_wpcomsh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: added | ||
|
||
Adding support for protected connection owner. |
275 changes: 275 additions & 0 deletions
275
projects/plugins/wpcomsh/connection/class-protected-owner-error-handler.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,275 @@ | ||
<?php | ||
/** | ||
* The Jetpack Connection Protected Owner Error Handler class file. | ||
* | ||
* @package wpcomsh | ||
*/ | ||
|
||
namespace Automattic\WPComSH\Connection; | ||
|
||
/** | ||
* The Jetpack Connection Protected Owner Error Handler class. | ||
* | ||
* This class handles errors related to protected owner accounts in the Jetpack Connection. | ||
* It retrieves owner account errors stored in WordPress options and displays them in the UI. | ||
* | ||
* The class automatically clears errors when the required local account is created, | ||
* allowing external healing code to establish the proper Jetpack connection. | ||
* | ||
* Additionally, this class provides email prepopulation functionality for the WordPress | ||
* user creation form when creating missing protected owner accounts. It overrides the | ||
* default User_Admin class behavior to ensure the WP.com invitation checkbox is not | ||
* pre-checked when creating protected owner accounts. | ||
* | ||
* @since $$next-version$$ | ||
*/ | ||
class Protected_Owner_Error_Handler { | ||
|
||
/** | ||
* The name of the option that stores the error | ||
* | ||
* @var string | ||
*/ | ||
const STORED_ERRORS_OPTION = 'jetpack_connection_protected_owner_error'; | ||
|
||
/** | ||
* Holds the instance of this singleton class | ||
* | ||
* @var Protected_Owner_Error_Handler $instance | ||
*/ | ||
private static $instance = null; | ||
|
||
/** | ||
* Initialize instance and register hooks | ||
*/ | ||
private function __construct() { | ||
// Inject protected owner errors into the connection error system | ||
add_filter( 'jetpack_connection_get_verified_errors', array( $this, 'handle_error' ) ); | ||
|
||
// Clear errors when the missing user is created or updated (allows external healing code to work) | ||
add_action( 'user_register', array( $this, 'check_and_clear_error_for_user' ) ); | ||
add_action( 'profile_update', array( $this, 'check_and_clear_error_for_user' ) ); | ||
|
||
// Add form prepopulation functionality | ||
add_action( 'user_new_form', array( $this, 'prepopulate_user_form' ) ); | ||
|
||
// Disable WordPress.com invitations when creating protected owner accounts | ||
add_filter( 'jetpack_sso_invite_new_users_wpcom', array( $this, 'disable_wpcom_invite_for_protected_owner' ) ); | ||
} | ||
|
||
/** | ||
* Gets the instance of this singleton class | ||
* | ||
* @return Protected_Owner_Error_Handler $instance | ||
*/ | ||
public static function get_instance() { | ||
if ( self::$instance === null ) { | ||
self::$instance = new self(); | ||
} | ||
return self::$instance; | ||
} | ||
|
||
/** | ||
* Check if there's an active protected owner error | ||
* | ||
* @return array|false Raw error data if there's an active error, false otherwise. | ||
*/ | ||
private function get_active_error() { | ||
// Check if option is populated | ||
$raw_error = get_option( self::STORED_ERRORS_OPTION, false ); | ||
|
||
// Return early if no error is stored | ||
if ( ! $raw_error || ! is_array( $raw_error ) ) { | ||
return false; | ||
} | ||
|
||
// Validate the minimal required fields | ||
if ( ! isset( $raw_error['error_type'] ) || ! isset( $raw_error['email'] ) ) { | ||
return false; | ||
} | ||
|
||
// Check if user exists with the required email | ||
$user = get_user_by( 'email', $raw_error['email'] ); | ||
if ( $user ) { | ||
// User exists, delete the option and return false (no active error) | ||
$this->delete_error(); | ||
return false; | ||
} | ||
|
||
// User doesn't exist, we have an active error | ||
return $raw_error; | ||
} | ||
|
||
/** | ||
* Handle protected owner errors in the connection error system | ||
* | ||
* @param array $verified_errors Current verified errors. | ||
* @return array Updated verified errors including protected owner errors. | ||
*/ | ||
public function handle_error( $verified_errors ) { | ||
// Clear all existing errors first | ||
$verified_errors = array(); | ||
|
||
$raw_error = $this->get_active_error(); | ||
|
||
// Return early if no active error | ||
if ( ! $raw_error ) { | ||
return $verified_errors; | ||
} | ||
|
||
// Use a consistent error code for all protected owner errors | ||
$error_code = 'protected_owner_missing'; | ||
|
||
// Prepare error data for the connection error system | ||
$user_id = '0'; | ||
$timestamp = $raw_error['timestamp'] ?? time(); | ||
|
||
$error_details = array( | ||
'error_code' => $error_code, | ||
'user_id' => $user_id, | ||
'error_message' => $this->get_error_message( $raw_error['email'] ), | ||
'error_data' => array( | ||
'email' => $raw_error['email'], | ||
'error_type' => $raw_error['error_type'], | ||
'action' => 'create_missing_account', | ||
'support_url' => admin_url( 'user-new.php' ), | ||
), | ||
'timestamp' => $timestamp, | ||
'nonce' => wp_generate_password( 10, false ), | ||
'error_type' => 'protected_owner', | ||
); | ||
|
||
// Return only the protected owner error - it takes priority over other connection errors | ||
// since it's typically the root cause and other errors may be symptoms | ||
return array( | ||
$error_code => array( | ||
$user_id => $error_details, | ||
), | ||
); | ||
} | ||
|
||
/** | ||
* Get a user-friendly error message for protected owner errors | ||
* | ||
* @param string $email The WordPress.com email address of the protected owner. | ||
* @return string The error message. | ||
*/ | ||
private function get_error_message( $email ) { | ||
return sprintf( | ||
/* translators: %s is the WordPress.com email address */ | ||
__( 'This site needs to be connected to WordPress.com by the plan owner account with email %s. Please create a local user account with this email address to resolve this issue.', 'wpcomsh' ), | ||
esc_html( $email ) | ||
); | ||
} | ||
|
||
/** | ||
* Delete the stored error | ||
*/ | ||
public function delete_error() { | ||
delete_option( self::STORED_ERRORS_OPTION ); | ||
} | ||
|
||
/** | ||
* Check if the user matches the protected owner error and clear it if so | ||
* This allows external healing code to automatically establish the connection | ||
* | ||
* @param int $user_id The ID of the user to check. | ||
*/ | ||
public function check_and_clear_error_for_user( $user_id ) { | ||
// Get the raw error data to check the email | ||
$raw_error = get_option( self::STORED_ERRORS_OPTION, false ); | ||
|
||
// Return early if no error is stored | ||
if ( ! $raw_error || ! is_array( $raw_error ) || ! isset( $raw_error['email'] ) ) { | ||
return; | ||
} | ||
|
||
// Get the user | ||
$user = get_user_by( 'id', $user_id ); | ||
if ( ! $user ) { | ||
return; | ||
} | ||
|
||
// Check if the user's email matches the required email | ||
if ( strtolower( $user->user_email ) === strtolower( $raw_error['email'] ) ) { | ||
// The user with the required email has been created/updated | ||
// Clear the error so external healing code can establish the connection | ||
$this->delete_error(); | ||
} | ||
} | ||
|
||
/** | ||
* Add form prepopulation functionality | ||
*/ | ||
public function prepopulate_user_form() { | ||
$email = $this->get_prepopulation_email(); | ||
|
||
if ( ! $email ) { | ||
return; | ||
} | ||
|
||
// Output hidden field and JavaScript to prepopulate the form | ||
?> | ||
<input type="hidden" id="jetpack_prepopulate_email" value="<?php echo esc_attr( $email ); ?>" /> | ||
<input type="hidden" name="jetpack_create_missing_account" value="1" /> | ||
|
||
<script type="text/javascript"> | ||
(function() { | ||
document.addEventListener('DOMContentLoaded', function() { | ||
// Prepopulate the email field and role | ||
var emailInput = document.getElementById('jetpack_prepopulate_email'); | ||
if (emailInput && emailInput.value) { | ||
var emailField = document.getElementById('email'); | ||
var roleField = document.getElementById('role'); | ||
|
||
if (emailField) { | ||
emailField.value = emailInput.value; | ||
} | ||
if (roleField) { | ||
roleField.value = 'administrator'; | ||
} | ||
} | ||
}); | ||
})(); | ||
</script> | ||
<?php | ||
} | ||
|
||
/** | ||
* Get the email address for prepopulation from various sources | ||
* | ||
* @return string|false Email address if available, false otherwise | ||
*/ | ||
private function get_prepopulation_email() { | ||
// Check URL parameters first (from React component) | ||
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- URL parameters are read-only for prepopulation, no sensitive actions performed | ||
if ( isset( $_GET['jetpack_protected_owner_email'] ) && | ||
isset( $_GET['jetpack_create_missing_account'] ) ) { | ||
$email = sanitize_email( wp_unslash( $_GET['jetpack_protected_owner_email'] ) ); | ||
if ( is_email( $email ) ) { | ||
return $email; | ||
} | ||
} | ||
// phpcs:enable WordPress.Security.NonceVerification.Recommended | ||
|
||
// Only prepopulate when explicitly triggered from dashboard | ||
return false; | ||
} | ||
|
||
/** | ||
* Disable WordPress.com invitations when creating protected owner accounts | ||
* | ||
* @param bool $invite_new_users_wpcom Whether to invite new users to WordPress.com. | ||
* @return bool Updated value indicating whether to invite new users to WordPress.com. | ||
*/ | ||
public function disable_wpcom_invite_for_protected_owner( $invite_new_users_wpcom ) { | ||
// Check if we're in a protected owner creation context | ||
$email = $this->get_prepopulation_email(); | ||
if ( ! $email ) { | ||
return $invite_new_users_wpcom; // Not a protected owner creation, let the default behavior proceed | ||
} | ||
|
||
// Disable invitations for protected owner creation | ||
return false; | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
projects/plugins/wpcomsh/connection/protected-owner-handlers.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
/** | ||
* Protected Owner error handling initialization for wpcomsh. | ||
* | ||
* This file loads and initializes the Protected Owner error handling | ||
* that integrates with the Jetpack connection error system. | ||
* | ||
* @package wpcomsh | ||
*/ | ||
|
||
// Require the Protected Owner Error Handler class | ||
require_once __DIR__ . '/class-protected-owner-error-handler.php'; | ||
|
||
// Initialize the Protected Owner error handler | ||
add_action( | ||
'plugins_loaded', | ||
function () { | ||
// Initialize the Protected Owner Error Handler singleton | ||
\Automattic\WPComSH\Connection\Protected_Owner_Error_Handler::get_instance(); | ||
}, | ||
5 | ||
); |
454 changes: 454 additions & 0 deletions
454
projects/plugins/wpcomsh/tests/ProtectedOwnerErrorHandlerTest.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,454 @@ | ||
<?php | ||
/** | ||
* Protected Owner Error Handler Test file. | ||
* | ||
* @package wpcomsh | ||
*/ | ||
|
||
use Automattic\WPComSH\Connection\Protected_Owner_Error_Handler; | ||
|
||
/** | ||
* Class ProtectedOwnerErrorHandlerTest. | ||
*/ | ||
class ProtectedOwnerErrorHandlerTest extends WP_UnitTestCase { | ||
use \Automattic\Jetpack\PHPUnit\WP_UnitTestCase_Fix; | ||
|
||
/** | ||
* The Protected_Owner_Error_Handler instance being tested. | ||
* | ||
* @var Protected_Owner_Error_Handler | ||
*/ | ||
private $handler; | ||
|
||
/** | ||
* Set up test environment before each test. | ||
*/ | ||
public function setUp(): void { | ||
parent::setUp(); | ||
$this->handler = Protected_Owner_Error_Handler::get_instance(); | ||
|
||
// Clean up any existing error data | ||
delete_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ); | ||
delete_option( 'jetpack_connection_xmlrpc_verified_errors' ); | ||
} | ||
|
||
/** | ||
* Clean up after each test. | ||
*/ | ||
public function tearDown(): void { | ||
delete_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ); | ||
delete_option( 'jetpack_connection_xmlrpc_verified_errors' ); | ||
parent::tearDown(); | ||
} | ||
|
||
/** | ||
* Test that the class implements singleton pattern correctly. | ||
*/ | ||
public function test_singleton_pattern() { | ||
$instance1 = Protected_Owner_Error_Handler::get_instance(); | ||
$instance2 = Protected_Owner_Error_Handler::get_instance(); | ||
|
||
$this->assertSame( $instance1, $instance2 ); | ||
$this->assertInstanceOf( Protected_Owner_Error_Handler::class, $instance1 ); | ||
} | ||
|
||
/** | ||
* Test handle_error returns original errors when no error is stored. | ||
*/ | ||
public function test_handle_error_returns_original_errors_when_no_error_stored() { | ||
$original_errors = array( 'some_error' => array( '1' => array( 'data' => 'test' ) ) ); | ||
$result = $this->handler->handle_error( $original_errors ); | ||
$this->assertEquals( array(), $result ); | ||
} | ||
|
||
/** | ||
* Test handle_error returns original errors for invalid data. | ||
*/ | ||
public function test_handle_error_returns_original_errors_for_invalid_data() { | ||
$original_errors = array( 'some_error' => array( '1' => array( 'data' => 'test' ) ) ); | ||
|
||
// Test with non-array data | ||
update_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, 'invalid_data' ); | ||
$result = $this->handler->handle_error( $original_errors ); | ||
$this->assertEquals( array(), $result ); | ||
|
||
// Test with missing error_type | ||
update_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, array( 'email' => 'test@example.com' ) ); | ||
$result = $this->handler->handle_error( $original_errors ); | ||
$this->assertEquals( array(), $result ); | ||
|
||
// Test with missing email | ||
update_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, array( 'error_type' => 'missing_owner' ) ); | ||
$result = $this->handler->handle_error( $original_errors ); | ||
$this->assertEquals( array(), $result ); | ||
} | ||
|
||
/** | ||
* Test handle_error returns original errors when user exists. | ||
*/ | ||
public function test_handle_error_returns_original_errors_when_user_exists() { | ||
$test_email = 'test@example.com'; | ||
$original_errors = array( 'some_error' => array( '1' => array( 'data' => 'test' ) ) ); | ||
|
||
// Create a user with the required email | ||
$this->factory()->user->create( array( 'user_email' => $test_email ) ); | ||
|
||
// Set up an error | ||
update_option( | ||
Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, | ||
array( | ||
'error_type' => 'missing_owner', | ||
'email' => $test_email, | ||
) | ||
); | ||
|
||
$result = $this->handler->handle_error( $original_errors ); | ||
|
||
// Should return empty array and delete the stored error | ||
$this->assertEquals( array(), $result ); | ||
$this->assertFalse( get_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ) ); | ||
} | ||
|
||
/** | ||
* Test handle_error returns protected owner error when user doesn't exist. | ||
*/ | ||
public function test_handle_error_returns_protected_owner_error() { | ||
$test_email = 'test@example.com'; | ||
$test_timestamp = time(); | ||
$original_errors = array( 'some_error' => array( '1' => array( 'data' => 'test' ) ) ); | ||
|
||
update_option( | ||
Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, | ||
array( | ||
'error_type' => 'missing_owner', | ||
'email' => $test_email, | ||
'timestamp' => $test_timestamp, | ||
) | ||
); | ||
|
||
$result = $this->handler->handle_error( $original_errors ); | ||
|
||
// Should return only the protected owner error (takes priority) | ||
$this->assertIsArray( $result ); | ||
$this->assertArrayHasKey( 'protected_owner_missing', $result ); | ||
$this->assertArrayNotHasKey( 'some_error', $result ); | ||
|
||
$error_data = $result['protected_owner_missing']['0']; | ||
$this->assertEquals( 'protected_owner_missing', $error_data['error_code'] ); | ||
$this->assertSame( '0', $error_data['user_id'] ); | ||
$this->assertEquals( 'protected_owner', $error_data['error_type'] ); | ||
$this->assertEquals( $test_timestamp, $error_data['timestamp'] ); | ||
$this->assertArrayHasKey( 'error_message', $error_data ); | ||
$this->assertStringContainsString( $test_email, $error_data['error_message'] ); | ||
$this->assertArrayHasKey( 'error_data', $error_data ); | ||
$this->assertEquals( $test_email, $error_data['error_data']['email'] ); | ||
$this->assertEquals( 'missing_owner', $error_data['error_data']['error_type'] ); | ||
$this->assertEquals( 'create_missing_account', $error_data['error_data']['action'] ); | ||
$this->assertStringContainsString( 'user-new.php', $error_data['error_data']['support_url'] ); | ||
} | ||
|
||
/** | ||
* Test delete_error method. | ||
*/ | ||
public function test_delete_error() { | ||
// Set an error first | ||
update_option( | ||
Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, | ||
array( | ||
'error_type' => 'missing_owner', | ||
'email' => 'test@example.com', | ||
) | ||
); | ||
|
||
// Verify error exists | ||
$this->assertNotFalse( get_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ) ); | ||
|
||
// Delete the error | ||
$this->handler->delete_error(); | ||
|
||
// Verify our error is gone | ||
$this->assertFalse( get_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ) ); | ||
} | ||
|
||
/** | ||
* Test check_and_clear_error_for_user method with matching email. | ||
*/ | ||
public function test_check_and_clear_error_for_user_matching_email() { | ||
$test_email = 'test@example.com'; | ||
|
||
// Set an error | ||
update_option( | ||
Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, | ||
array( | ||
'error_type' => 'missing_owner', | ||
'email' => $test_email, | ||
) | ||
); | ||
|
||
// Create a user with matching email | ||
$user_id = $this->factory()->user->create( array( 'user_email' => $test_email ) ); | ||
|
||
// Simulate user creation/update | ||
$this->handler->check_and_clear_error_for_user( $user_id ); | ||
|
||
// Error should be cleared | ||
$this->assertFalse( get_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ) ); | ||
} | ||
|
||
/** | ||
* Test check_and_clear_error_for_user method with non-matching email. | ||
*/ | ||
public function test_check_and_clear_error_for_user_non_matching_email() { | ||
$test_email = 'test@example.com'; | ||
|
||
// Set an error | ||
update_option( | ||
Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, | ||
array( | ||
'error_type' => 'missing_owner', | ||
'email' => $test_email, | ||
) | ||
); | ||
|
||
// Create a user with different email | ||
$user_id = $this->factory()->user->create( array( 'user_email' => 'different@example.com' ) ); | ||
|
||
// Simulate user creation/update | ||
$this->handler->check_and_clear_error_for_user( $user_id ); | ||
|
||
// Error should remain | ||
$this->assertNotFalse( get_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ) ); | ||
} | ||
|
||
/** | ||
* Test check_and_clear_error_for_user method with no error stored. | ||
*/ | ||
public function test_check_and_clear_error_for_user_no_error_stored() { | ||
// Create a user | ||
$user_id = $this->factory()->user->create( array( 'user_email' => 'test@example.com' ) ); | ||
|
||
// This should not cause any errors | ||
$this->handler->check_and_clear_error_for_user( $user_id ); | ||
|
||
// No error should be created | ||
$this->assertFalse( get_option( Protected_Owner_Error_Handler::STORED_ERRORS_OPTION ) ); | ||
} | ||
|
||
/** | ||
* Test get_prepopulation_email from URL parameters. | ||
*/ | ||
public function test_get_prepopulation_email_from_url_parameters() { | ||
$test_email = 'test@example.com'; | ||
|
||
// Set up URL parameters | ||
$_GET['jetpack_protected_owner_email'] = $test_email; | ||
$_GET['jetpack_create_missing_account'] = '1'; | ||
|
||
// Use reflection to access private method | ||
$reflection = new ReflectionClass( $this->handler ); | ||
$method = $reflection->getMethod( 'get_prepopulation_email' ); | ||
$method->setAccessible( true ); | ||
|
||
$result = $method->invoke( $this->handler ); | ||
|
||
$this->assertEquals( $test_email, $result ); | ||
|
||
// Clean up | ||
unset( $_GET['jetpack_protected_owner_email'] ); | ||
unset( $_GET['jetpack_create_missing_account'] ); | ||
} | ||
|
||
/** | ||
* Test get_prepopulation_email from URL parameters with invalid email. | ||
*/ | ||
public function test_get_prepopulation_email_from_url_parameters_invalid_email() { | ||
// Set up URL parameters with invalid email | ||
$_GET['jetpack_protected_owner_email'] = 'invalid-email'; | ||
$_GET['jetpack_create_missing_account'] = '1'; | ||
|
||
// Use reflection to access private method | ||
$reflection = new ReflectionClass( $this->handler ); | ||
$method = $reflection->getMethod( 'get_prepopulation_email' ); | ||
$method->setAccessible( true ); | ||
|
||
$result = $method->invoke( $this->handler ); | ||
|
||
$this->assertFalse( $result ); | ||
|
||
// Clean up | ||
unset( $_GET['jetpack_protected_owner_email'] ); | ||
unset( $_GET['jetpack_create_missing_account'] ); | ||
} | ||
|
||
/** | ||
* Test get_prepopulation_email from URL parameters missing create_missing_account. | ||
*/ | ||
public function test_get_prepopulation_email_from_url_parameters_missing_create_flag() { | ||
$test_email = 'test@example.com'; | ||
|
||
// Set up URL parameters missing the create flag | ||
$_GET['jetpack_protected_owner_email'] = $test_email; | ||
|
||
// Use reflection to access private method | ||
$reflection = new ReflectionClass( $this->handler ); | ||
$method = $reflection->getMethod( 'get_prepopulation_email' ); | ||
$method->setAccessible( true ); | ||
|
||
$result = $method->invoke( $this->handler ); | ||
|
||
$this->assertFalse( $result ); | ||
|
||
// Clean up | ||
unset( $_GET['jetpack_protected_owner_email'] ); | ||
} | ||
|
||
/** | ||
* Test get_prepopulation_email from stored error data fallback. | ||
*/ | ||
public function test_get_prepopulation_email_from_stored_error() { | ||
$test_email = 'test@example.com'; | ||
|
||
// Set up an error | ||
update_option( | ||
Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, | ||
array( | ||
'error_type' => 'missing_owner', | ||
'email' => $test_email, | ||
) | ||
); | ||
|
||
// Use reflection to access private method | ||
$reflection = new ReflectionClass( $this->handler ); | ||
$method = $reflection->getMethod( 'get_prepopulation_email' ); | ||
$method->setAccessible( true ); | ||
|
||
$result = $method->invoke( $this->handler ); | ||
|
||
// Should now return false since stored error fallback was removed | ||
$this->assertFalse( $result ); | ||
} | ||
|
||
/** | ||
* Test get_prepopulation_email returns false when no email available. | ||
*/ | ||
public function test_get_prepopulation_email_returns_false_when_no_email() { | ||
// Use reflection to access private method | ||
$reflection = new ReflectionClass( $this->handler ); | ||
$method = $reflection->getMethod( 'get_prepopulation_email' ); | ||
$method->setAccessible( true ); | ||
|
||
$result = $method->invoke( $this->handler ); | ||
|
||
$this->assertFalse( $result ); | ||
} | ||
|
||
/** | ||
* Test get_prepopulation_email URL parameters take priority over stored error. | ||
*/ | ||
public function test_get_prepopulation_email_url_parameters_take_priority() { | ||
$url_email = 'url@example.com'; | ||
$stored_email = 'stored@example.com'; | ||
|
||
// Set up stored error | ||
update_option( | ||
Protected_Owner_Error_Handler::STORED_ERRORS_OPTION, | ||
array( | ||
'error_type' => 'missing_owner', | ||
'email' => $stored_email, | ||
) | ||
); | ||
|
||
// Set up URL parameters (should work since only URL parameters are used) | ||
$_GET['jetpack_protected_owner_email'] = $url_email; | ||
$_GET['jetpack_create_missing_account'] = '1'; | ||
|
||
// Use reflection to access private method | ||
$reflection = new ReflectionClass( $this->handler ); | ||
$method = $reflection->getMethod( 'get_prepopulation_email' ); | ||
$method->setAccessible( true ); | ||
|
||
$result = $method->invoke( $this->handler ); | ||
|
||
$this->assertEquals( $url_email, $result ); | ||
|
||
// Clean up | ||
unset( $_GET['jetpack_protected_owner_email'] ); | ||
unset( $_GET['jetpack_create_missing_account'] ); | ||
} | ||
|
||
/** | ||
* Test prepopulate_user_form outputs expected HTML when email is available. | ||
*/ | ||
public function test_prepopulate_user_form_with_email() { | ||
$test_email = 'test@example.com'; | ||
|
||
// Set up URL parameters to ensure we have an email to prepopulate | ||
$_GET['jetpack_protected_owner_email'] = $test_email; | ||
$_GET['jetpack_create_missing_account'] = '1'; | ||
|
||
// Capture output | ||
ob_start(); | ||
$this->handler->prepopulate_user_form(); | ||
$output = ob_get_clean(); | ||
|
||
// Verify output contains expected elements | ||
$this->assertStringContainsString( $test_email, $output ); | ||
$this->assertStringContainsString( 'jetpack_prepopulate_email', $output ); | ||
$this->assertStringContainsString( 'jetpack_create_missing_account', $output ); | ||
$this->assertStringContainsString( 'text/javascript', $output ); | ||
$this->assertStringContainsString( 'getElementById', $output ); | ||
$this->assertStringContainsString( 'administrator', $output ); | ||
|
||
// Clean up | ||
unset( $_GET['jetpack_protected_owner_email'] ); | ||
unset( $_GET['jetpack_create_missing_account'] ); | ||
} | ||
|
||
/** | ||
* Test prepopulate_user_form outputs nothing when no email is available. | ||
*/ | ||
public function test_prepopulate_user_form_without_email() { | ||
// Capture output | ||
ob_start(); | ||
$this->handler->prepopulate_user_form(); | ||
$output = ob_get_clean(); | ||
|
||
// Should be empty | ||
$this->assertEmpty( $output ); | ||
} | ||
|
||
/** | ||
* Test disable_wpcom_invite_for_protected_owner filter with protected owner context. | ||
*/ | ||
public function test_disable_wpcom_invite_for_protected_owner_with_email() { | ||
$test_email = 'test@example.com'; | ||
|
||
// Set up URL parameters to create protected owner context | ||
$_GET['jetpack_protected_owner_email'] = $test_email; | ||
$_GET['jetpack_create_missing_account'] = '1'; | ||
|
||
// Test that the filter disables invitations | ||
$result = $this->handler->disable_wpcom_invite_for_protected_owner( true ); | ||
$this->assertFalse( $result ); | ||
|
||
// Test that it also works when the original value is false | ||
$result = $this->handler->disable_wpcom_invite_for_protected_owner( false ); | ||
$this->assertFalse( $result ); | ||
|
||
// Clean up | ||
unset( $_GET['jetpack_protected_owner_email'] ); | ||
unset( $_GET['jetpack_create_missing_account'] ); | ||
} | ||
|
||
/** | ||
* Test disable_wpcom_invite_for_protected_owner filter without protected owner context. | ||
*/ | ||
public function test_disable_wpcom_invite_for_protected_owner_without_email() { | ||
// Test that without protected owner context, original value is preserved | ||
$result = $this->handler->disable_wpcom_invite_for_protected_owner( true ); | ||
$this->assertTrue( $result ); | ||
|
||
$result = $this->handler->disable_wpcom_invite_for_protected_owner( false ); | ||
$this->assertFalse( $result ); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.