Skip to content
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

User invitation: Mail the newly added user with WPCom invite #35234

Merged
merged 14 commits into from
Jan 29, 2024
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/improve_invite_form
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

SSO: When creating a new users, mail the users with an invitation to WPCom.
175 changes: 120 additions & 55 deletions projects/plugins/jetpack/modules/sso.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @package automattic/jetpack
*/

use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Roles;
use Automattic\Jetpack\Status;
Expand Down Expand Up @@ -54,6 +55,12 @@ private function __construct() {
add_action( 'jetpack_site_before_disconnected', array( static::class, 'disconnect' ) );
add_action( 'wp_login', array( 'Jetpack_SSO', 'clear_cookies_after_login' ) );

// If the user has no errors on creation, send an invite to WordPress.com.
add_filter( 'user_profile_update_errors', array( $this, 'send_wpcom_mail_user_invite' ), 10, 3 );
// Don't send core invitation email when adding a new user via the admin.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Don't send core invitation email when adding a new user via the admin.
// Don't send core invitation email when SSO is activated. They will get an email from WP.com.

add_filter( 'wp_send_new_user_notification_to_user', '__return_false' );
add_action( 'user_new_form', array( $this, 'render_invitation_email_message' ) );

// Adding this action so that on login_init, the action won't be sanitized out of the $action global.
add_action( 'login_form_jetpack-sso', '__return_true' );

Expand Down Expand Up @@ -83,6 +90,64 @@ private function __construct() {
}
}

/**
* Render the invitation email message.
*/
public function render_invitation_email_message() {
wp_admin_notice(
__( 'New users will receive an invite to join WordPress.com.', 'jetpack' ),
array(
'id' => 'invitation_message',
'type' => 'info',
'dismissible' => false,
)
);
}

/**
* Send user invitation to WordPress.com if user has no errors.
*
* @param WP_Error $errors The WP_Error object.
* @param bool $update Whether the user is being updated or not.
* @param stdClass $user The User object about to be created.
* @return WP_Error The modified or not WP_Error object.
*/
public function send_wpcom_mail_user_invite( $errors, $update, $user ) {

if ( $errors->has_errors() ) {
return $errors;
}

$email = $user->user_email;
$role = $user->role;
$locale = get_user_locale( $user->ID );
$blog_id = Jetpack_Options::get_option( 'id' );
$url = '/sites/' . $blog_id . '/invites/new';
$url = add_query_arg( 'locale', $locale, $url );

$response = Client::wpcom_json_api_request_as_user(
$url,
'2', // Api version
array(
'method' => 'POST',
),
array(
'invitees' => array(
array(
'email_or_username' => $email,
'role' => $role,
),
),
)
);

if ( 200 !== $response['response']['code'] ) {
$errors->add( 'invitation_not_sent', __( '<strong>Error</strong>: "The user invitation email could not be sent, the user account was not created.', 'jetpack' ) );
}

return $errors;
}

/**
* Returns the single instance of the Jetpack_SSO object
*
Expand Down Expand Up @@ -119,7 +184,7 @@ public function sso_reminder_logout_wpcom( $errors ) {
if ( ! empty( $errors->errors['loggedout'] ) ) {
$logout_message = wp_kses(
sprintf(
/* translators: %1$s is a link to the WordPress.com account settings page. */
/* translators: %1$s is a link to the WordPress.com account settings page. */
__( 'If you are on a shared computer, remember to also <a href="%1$s">log out of WordPress.com</a>.', 'jetpack' ),
'https://wordpress.com/me'
),
Expand Down Expand Up @@ -320,10 +385,10 @@ public function render_require_two_step() {
<input
type="checkbox"
name="jetpack_sso_require_two_step"
<?php checked( Jetpack_SSO_Helpers::is_two_step_required() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_require_two_step_checkbox_disabled() ); ?>
<?php checked( Jetpack_SSO_Helpers::is_two_step_required() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_require_two_step_checkbox_disabled() ); ?>
>
<?php esc_html_e( 'Require Two-Step Authentication', 'jetpack' ); ?>
<?php esc_html_e( 'Require Two-Step Authentication', 'jetpack' ); ?>
renancarvalho marked this conversation as resolved.
Show resolved Hide resolved
</label>
<?php
}
Expand Down Expand Up @@ -352,10 +417,10 @@ public function render_match_by_email() {
<input
type="checkbox"
name="jetpack_sso_match_by_email"
<?php checked( Jetpack_SSO_Helpers::match_by_email() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_match_by_email_checkbox_disabled() ); ?>
<?php checked( Jetpack_SSO_Helpers::match_by_email() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_match_by_email_checkbox_disabled() ); ?>
>
<?php esc_html_e( 'Match by Email', 'jetpack' ); ?>
<?php esc_html_e( 'Match by Email', 'jetpack' ); ?>
</label>
<?php
}
Expand Down Expand Up @@ -510,7 +575,7 @@ public static function save_cookies() {

setcookie(
'jetpack_sso_original_request',
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the wrapping esc_url_raw().
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the wrapping esc_url_raw().
esc_url_raw( set_url_scheme( ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) . ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' ) ) ),
time() + HOUR_IN_SECONDS,
COOKIEPATH,
Expand Down Expand Up @@ -540,47 +605,47 @@ public function login_form() {
}

$display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] )
? sanitize_text_field( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) )
: false;
? sanitize_text_field( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) )
: false;
$gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] )
? esc_url_raw( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) )
: false;
? esc_url_raw( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) )
: false;

?>
<div id="jetpack-sso-wrap">
<?php
/**
* Allow extension above Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_above_wpcom' );

if ( $display_name && $gravatar ) :
?>
<?php
/**
* Allow extension above Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_above_wpcom' );

if ( $display_name && $gravatar ) :
?>
<div id="jetpack-sso-wrap__user">
<img width="72" height="72" src="<?php echo esc_html( $gravatar ); ?>" />

<h2>
<?php
echo wp_kses(
/* translators: %s a user display name. */
sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
array( 'span' => true )
);
?>
<?php
echo wp_kses(
/* translators: %s a user display name. */
sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
array( 'span' => true )
);
?>
</h2>
</div>

<?php endif; ?>
<?php endif; ?>


<div id="jetpack-sso-wrap__action">
<?php echo $this->build_sso_button( array(), 'is_primary' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping done in build_sso_button() ?>
<?php echo $this->build_sso_button( array(), 'is_primary' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping done in build_sso_button() ?>

<?php if ( $display_name && $gravatar ) : ?>
<?php if ( $display_name && $gravatar ) : ?>
<a rel="nofollow" class="jetpack-sso-wrap__reauth" href="<?php echo esc_url( $this->build_sso_button_url( array( 'force_reauth' => '1' ) ) ); ?>">
<?php esc_html_e( 'Log in as a different WordPress.com user', 'jetpack' ); ?>
</a>
Expand Down Expand Up @@ -610,18 +675,18 @@ public function login_form() {
<?php endif; ?>
</div>

<?php
/**
* Allow extension below Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_below_wpcom' );

if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) :
?>
<?php
/**
* Allow extension below Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_below_wpcom' );

if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) :
?>
<div class="jetpack-sso-or">
<span><?php esc_html_e( 'Or', 'jetpack' ); ?></span>
</div>
Expand All @@ -637,9 +702,9 @@ public function login_form() {
esc_html_e( 'Log in with WordPress.com', 'jetpack' )
?>
</a>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
<?php
}

/**
Expand Down Expand Up @@ -762,8 +827,8 @@ public static function delete_connection_for_user( $user_id ) {
*/
public static function request_initial_nonce() {
$nonce = ! empty( $_COOKIE['jetpack_sso_nonce'] )
? sanitize_key( wp_unslash( $_COOKIE['jetpack_sso_nonce'] ) )
: false;
? sanitize_key( wp_unslash( $_COOKIE['jetpack_sso_nonce'] ) )
: false;

if ( ! $nonce ) {
$xml = new Jetpack_IXR_Client();
Expand Down Expand Up @@ -904,8 +969,8 @@ public function handle_login() {
}

$user_found_with = $new_user_override_role
? 'user_created_new_user_override'
: 'user_created_users_can_register';
? 'user_created_new_user_override'
: 'user_created_users_can_register';
} else {
$tracking->record_user_event(
'sso_login_failed',
Expand Down Expand Up @@ -991,7 +1056,7 @@ public function handle_login() {

add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) );
wp_safe_redirect(
/** This filter is documented in core/src/wp-login.php */
/** This filter is documented in core/src/wp-login.php */
apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user )
);
exit;
Expand Down Expand Up @@ -1032,8 +1097,8 @@ public static function profile_page_url() {
public function build_sso_button( $args = array(), $is_primary = false ) {
$url = $this->build_sso_button_url( $args );
$classes = $is_primary
? 'jetpack-sso button button-primary'
: 'jetpack-sso button';
? 'jetpack-sso button button-primary'
: 'jetpack-sso button';

return sprintf(
'<a rel="nofollow" href="%1$s" class="%2$s">%3$s %4$s</a>',
Expand Down
Loading