Skip to content

Commit

Permalink
Enforce login when accessing My Courses page (#2592)
Browse files Browse the repository at this point in the history
* Redirect logged out requests for the My Courses page to login, and back

* Open-source login/registration changes.

* Fix lint

---------

Co-authored-by: Dion Hulse <[email protected]>
  • Loading branch information
adamwoodnz and dd32 authored Jul 8, 2024
1 parent 8a28466 commit b7898be
Showing 1 changed file with 121 additions and 0 deletions.
121 changes: 121 additions & 0 deletions wp-content/plugins/wporg-learn/inc/sensei.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
add_action( 'sensei_before_main_content', __NAMESPACE__ . '\theme_wrapper_start' );
add_action( 'sensei_after_main_content', __NAMESPACE__ . '\theme_wrapper_end' );
add_action( 'init', __NAMESPACE__ . '\wporg_correct_sensei_slugs', 9 );
add_action( 'template_redirect', __NAMESPACE__ . '\restrict_my_courses_page_access' );
add_filter( 'sensei_login_url', __NAMESPACE__ . '\sensei_login_url', 20, 2 );
add_filter( 'sensei_registration_url', __NAMESPACE__ . '\sensei_registration_url', 20, 2 );
// Disable the Sensei user register page, use the WordPress registration page, see 'sensei_registration_url' filter.
add_filter( 'sensei_use_wp_register_link', '__return_true' );
// Repalce the Sensei login/register form contents.
add_action( 'sensei_login_form_before', __NAMESPACE__ . '\sensei_login_form_before' );
add_action( 'sensei_register_form_start', __NAMESPACE__ . '\sensei_register_form_start' );
// Disable Sensei user login & creation.
add_filter( 'init', __NAMESPACE__ . '\block_login_register_actions', 1 );

/**
* Slugs in Sensei are translatable, which won't work for our site and the language switcher.
Expand Down Expand Up @@ -258,3 +268,114 @@ function get_my_courses_page_url() {

return get_permalink( $page_id );
}

/**
* Redirect requests for the "My Courses" page to the login page and back, if logged out.
*/
function restrict_my_courses_page_access() {
if ( ! is_user_logged_in() && is_page( Sensei()->settings->get_my_courses_page_id() ) ) {
$redirect_to = wp_unslash( $_GET['redirect_to'] ?? '' ) ?: sensei_get_current_page_url();

wp_redirect( wp_login_url( $redirect_to ) );
exit;
}
}

/**
* Don't use the Sensei My Courses page as the login page.
*/
function sensei_login_url( $url, $redirect = '' ) {
return wp_login_url( $redirect );
}

/**
* Don't use the Sensei My Courses page as the registration page, but equally don't use the register page.
*
* Sensei uses the registration page for all logged out users, and the login page for all logged in users.
* This is a poor user experience, as it means that the 'Take Course' links will direct to the registration page, rather than the login page.
* For that reason, we're filtering the registration location to the login page.
*/
function sensei_registration_url( $url, $redirect = '' ) {
return wp_login_url( $redirect );
}

/**
* Replace the Sensei My Courses login form with a call to action to WordPress.org.
*/
function sensei_login_form_before() {
// Start an output buffer, we'll remove the form content in the post-login-form filter.
ob_start();

add_action( 'sensei_login_form_after', function() {
$html = ob_get_clean();

/*
* Use the provided redirect_to, or the current page failing that.
* This differs from Sensei which doesn't respect the redirect_to parameter.
* Validation will occur by the login redirection code.
*/
$redirect_to = wp_unslash( $_GET['redirect_to'] ?? '' ) ?: sensei_get_current_page_url();

// Replace the form with a call to action to WordPress.org.
$html = preg_replace(
'!<form.+</form>!is',
sprintf(
'<div class="wp-block-button"><a href="%s" class="wp-block-button__link wp-element-button button button-primary">%s</a></div>',
esc_url( wp_login_url( $redirect_to ) ),
__( 'Log In', 'wporg-learn' ),
),
$html
);

echo wp_kses_post( $html );
} );
}

/**
* Replace the Sensei registration form with a call to action to WordPress.org.
*/
function sensei_register_form_start() {
// Start an output buffer, we'll replace the form content in the post-login-form filter.
ob_start();

add_action( 'sensei_register_form_end', function() {
// We don't need any of the output buffer contents, since we're just in the <form> tag.
ob_end_clean();

// Output a registration button.
echo sprintf(
'<div class="wp-block-button"><a href="%s" class="wp-block-button__link wp-element-button button button-secondary">%s</a></div>',
esc_url( wp_registration_url() ),
esc_html__( 'Register', 'wporg-learn' ),
);

/*
* Add some custom styles for the My Courses page to remove the registation form border.
*
* This styles it to match the login section beside it.
*/
echo '<style>#my-courses #customer_login form { border: unset; margin: unset; padding: unset; } </style>';
} );
}

/**
* Forcibly disable Sensei user login & creation.
*
* Even if registrations are disabled, sensei still processes the form, for security we don't want this.
*/
function block_login_register_actions() {
if ( function_exists( 'Sensei' ) ) {
remove_action( 'init', array( Sensei()->frontend ?? false, 'sensei_process_registration' ), 2 );
remove_filter( 'init', array( Sensei()->frontend ?? false, 'sensei_handle_login_request' ), 10 ); // Yes, Sensei calls it a filter.
}

// We're also going to forcefully disable the POST'd fields, incase the above action names change.

// By unsetting this, it forces Sensei not to be able to create a user.
unset( $_REQUEST['sensei_reg_password'], $_POST['sensei_reg_password'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing

// By unsetting these, sensei can't process a login.
if ( 'sensei-login' == ( $_REQUEST['form'] ?? '' ) ) {
unset( $_REQUEST['_wpnonce'], $_REQUEST['log'], $_REQUEST['pwd'], $_POST['log'], $_POST['pwd'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
}

0 comments on commit b7898be

Please sign in to comment.