Thanks to its nearly endless customizability through plugins and themes, WordPress has come a long way from its roots as a blogging platform, today acting as the backbone for all kinds of web based applications from online stores to membership sites and e-book authoring platforms.
While traditionally only the site admins would log in to the WordPress dashboard, many of these new, application-like uses change that: when users need to be authenticated, the WordPress login is extended to visitors and customers.
WordPress provides good tools for managing user roles and capabilities, and they can be further extended with the help of plugins, but for a professional touch, that's not quite enough — you also need to make sure the user experience fits that of your product as a whole. After all, seeing the default (or lightly styled) WordPress login screen when logging in to a web based application can leave an unfinished impression.
In this three-part tutorial series, I will show you how to fix this by customizing the login and new user registration functionality to fit your site's looks, without violating WordPress's best practices.
What You Will Learn in This Tutorial
By default, WordPress uses a good looking, but generic login screen that we are all very familiar with:
The screen does it job, and it doesn't look bad at all. But when building a serious web site, you don't keep the default theme, so why would you keep the default login?
It is possible to do some styling using CSS and a number of actions and filters provided by the login page itself. But while these changes can lead to decent results, I think the approach still has its shortcomings, most importantly the fact that the login page is totally separate from the rest of your site.
That's why, today, we are not going to settle with the simple, cosmetic changes but will instead build a login page of our own. One that feels like a part of the WordPress based web site rather than something added on top of it.
To do this, in this tutorial, we will build a WordPress plugin that replaces the login page with a combination of a custom WordPress page and a shortcode used for rendering the login form. While doing so, you will learn about the WordPress login flow, the points were we can hook into it and modify it to our needs, as well as developing WordPress plugins.
Then, in the next two tutorials in the series, we will continue from there, customizing the new user registration and password reset functionality in the same way.
You can follow the tutorial implementing your own plugin as you go, or you can download the full sample code from the linked GitHub repository.
Let’s get started!
Create the Plugin
While some of the customization is clearly dependent on the theme used, the functionality of making the login page customizable itself is very much independent of themes and therefore best implemented as a plugin.
So, the first thing as we get started is to create a plugin to hold all of the customizations we will build in this tutorial.
First, create a directory to hold the plugin, naming it personalize-login
. Then, inside that directory, create another directory and name it templates
. In this directory, we will add the templates used for rendering the different HTML forms created in this series (we'll get back to this later in the tutorial).
Then, with the directories created, place a PHP file called personalize-login.php
in the plugin directory. This file will contain all the code for this plugin, starting with the plugin initialization:
<?php /** * Plugin Name: Personalize Login * Description: A plugin that replaces the WordPress login flow with a custom page. * Version: 1.0.0 * Author: Jarkko Laine * License: GPL-2.0+ * Text Domain: personalize-login */ class Personalize_Login_Plugin { /** * Initializes the plugin. * * To keep the initialization fast, only add filter and action * hooks in the constructor. */ public function __construct() { } } // Initialize the plugin $personalize_login_pages_plugin = new Personalize_Login_Plugin();
The plugin's constructor is still empty, but not for long: we'll soon start adding content to it, in the form of filter and action hooks.
But first, let's create some WordPress pages.
Create the Custom Pages For Login
By default, when you try to access the WordPress admin, or click on a Log In link on your WordPress site (assuming your theme displays one), WordPress sends you to wp-login.php
, the default version of the WordPress login page. Now, we'll change that and send the user to a custom WordPress page.
To do this, first we need to create a page.
At a later point, it might make sense to add a settings screen for picking the pages to use for each of the different steps in the user management flow. For now, however, I want to keep your focus on the customization and not confuse you with creating settings pages, so we will go with the simple option and just create the pages automatically when the plugin is activated.
In a WordPress plugin, the way to do this is by registering a static function to a plugin activation hook so that it is called when plugin is activated through the Plugins menu.
Notice that the activation hook does not get called when a plugin is updated through the plugin updates. So, if you make changes to the code inside the activation hook and want to have them take action, make sure that you deactivate and activate the plugin manually.
To register the activation hook, add the following piece of code at the end of your plugin file:
// Create the custom pages at plugin activation register_activation_hook( __FILE__, array( 'Personalize_Login_Plugin', 'plugin_activated' ) );
Then, inside the plugin class, add the static function plugin_activated
:
/** * Plugin activation hook. * * Creates all WordPress pages needed by the plugin. */ public static function plugin_activated() { // Information needed for creating the plugin's pages $page_definitions = array( 'member-login' => array( 'title' => __( 'Sign In', 'personalize-login' ), 'content' => '[custom-login-form]' ), 'member-account' => array( 'title' => __( 'Your Account', 'personalize-login' ), 'content' => '[account-info]' ), ); foreach ( $page_definitions as $slug => $page ) { // Check that the page doesn't exist already $query = new WP_Query( 'pagename=' . $slug ); if ( ! $query->have_posts() ) { // Add the page using the data from the array above wp_insert_post( array( 'post_content' => $page['content'], 'post_name' => $slug, 'post_title' => $page['title'], 'post_status' => 'publish', 'post_type' => 'page', 'ping_status' => 'closed', 'comment_status' => 'closed', ) ); } } }
The plugin activation function creates a set of pages using the data from the $page_definitions
array defined on lines 8-17. Using this array, we can easily add more pages (as we'll do in the next parts of the series) without copying and pasting page creation code. All we'll need to do is to add a new item to the array, using the page slug (permalink) as the key and an array containing the page's title and initial content as value.
In the code snippet above, I added two pages: one for the login page (member-login
) and one for an account page where we'll direct the logged in non-admin users (member-account
).
On lines 19-36, you'll find the actual page creation code. The function loops through the array of page definitions checking if a page with the given slug exists already and creating a page if it doesn't.
Creating the pages is done using the WordPress function wp_insert_post
. For more information on its parameters, you can check the WordPress Codex.
Now, let's activate the plugin and see that pages are created the way they should. Go to your WordPress admin dashboard and activate the plugin.Then, when you access the Pages menu, you should see both new pages in place:
If you click over to view the Sign In page, you'll find an almost blank page with nothing but a title and our shortcode.
But it won't be blank for long.
Add Content to the Login Page
With the first two pages in place, we can start adding content to them.
In this first tutorial, we will build the Log In — or Sign In (see this article on login best practices for a discussion on what words to use) — page. Then, in the next two tutorials, we will continue from there, building the "Register" and "Forgot Your Password" pages.
Having your own page for the login screen gives you many options for customizing its looks and content. On the easier side, you can use the WordPress page editor to add text and graphics (e.g. Instructions for the user, or maybe an advertisement about the benefits of registering an account). At the other extreme, you could even create your own page template and write the HTML for the page from scratch.
In this tutorial, we will do something from the middle and build a shortcode that will display a login form on the login page. If you later decide to create your own page template, you can always call this shortcode from your theme's code.
Step 1: Create the Shortcode
As you remember from the page creation step, we already added a shortcode (custom-login-form
) in the body of the login page when we created it in the plugin activation hook. However, as we haven't yet built a handler for that shortcode, WordPress now renders it as regular text.
Let's create the handler now. In your plugin class's constructor, add the following shortcode definition:
add_shortcode( 'custom-login-form', array( $this, 'render_login_form' ) );
This line registers a function inside the plugin class, render_login_form
, for rendering the shortcode custom-login-form
.
To make this work, add the function:
/** * A shortcode for rendering the login form. * * @param array $attributes Shortcode attributes. * @param string $content The text content for shortcode. Not used. * * @return string The shortcode output */ public function render_login_form( $attributes, $content = null ) { // Parse shortcode attributes $default_attributes = array( 'show_title' => false ); $attributes = shortcode_atts( $default_attributes, $attributes ); $show_title = $attributes['show_title']; if ( is_user_logged_in() ) { return __( 'You are already signed in.', 'personalize-login' ); } // Pass the redirect parameter to the WordPress login functionality: by default, // don't specify a redirect, but if a valid redirect URL has been passed as // request parameter, use it. $attributes['redirect'] = ''; if ( isset( $_REQUEST['redirect_to'] ) ) { $attributes['redirect'] = wp_validate_redirect( $_REQUEST['redirect_to'], $attributes['redirect'] ); } // Render the login form using an external template return $this->get_template_html( 'login_form', $attributes ); }
First, on lines 10-13, the function reads in the shortcode parameters — currently there is just one, show_title
, which take a boolean value. If the parameter is omitted, by default, it is set to false.
Then, the shortcode function checks if the user is logged in, rendering the form only for users who are not yet logged in.
On lines 19-25, the function checks if a redirect_to
request variable was passed to the login page. This parameter is what gets you to the correct WordPress admin page after login, so it's important to not forget it as we customize the login flow. If no redirect_to
is set, or the URL is not valid, the function uses the current page's permalink instead.
To keep presentation separate from functionality, the rendering is done using a template placed in the templates
directory you created at the beginning of this tutorial.
As we already know we will be adding more templates later in this series (for example the new user registration form), we will prepare for this and separate the template rendering into its own function to avoid duplicating code (lines 27-28). Variables needed in rendering the template are passed as an associative array, $attributes
.
Add the function, get_template_html
, to the plugin class:
/** * Renders the contents of the given template to a string and returns it. * * @param string $template_name The name of the template to render (without .php) * @param array $attributes The PHP variables for the template * * @return string The contents of the template. */ private function get_template_html( $template_name, $attributes = null ) { if ( ! $attributes ) { $attributes = array(); } ob_start(); do_action( 'personalize_login_before_' . $template_name ); require( 'templates/' . $template_name . '.php'); do_action( 'personalize_login_after_' . $template_name ); $html = ob_get_contents(); ob_end_clean(); return $html; }
The function looks for the given template in the templates directory and renders it into a string using an output buffer (lines 14-23). The output buffer collects everything that is printed between ob_start
and ob_end_clean
so that it can then be retrieved as a string using ob_get_contents
.
At the end of the function, the shortcode output is returned for rendering on the page (as you saw in the previous snippet, the shortcode function then returns it to WordPress for rendering).
The do_action
function calls surrounding the require
are optional but show a handy way to give other developers a chance to add further customizations before and after the template is rendered.
To complete the shortcode, you still need to create the HTML template: Create a PHP file in your templates
directory, naming it login_form.php
. Inside the template, to test, just add the text Sign In
. Now, when you navigate to your custom login page, you'll see something like this (using the current WordPress default theme, Twenty Fifteen):
Your shortcode is working, but it's still missing the login form. Let's add that now.
Step 2: Add Contents to the HTML Template
Continuing the work on login_form.php
, remove the placeholder text and replace it with the following content that will show a fully functional WordPress login form:
<div class="login-form-container"> <?php if ( $attributes['show_title'] ) : ?> <h2><?php _e( 'Sign In', 'personalize-login' ); ?></h2> <?php endif; ?> <?php wp_login_form( array( 'label_username' => __( 'Email', 'personalize-login' ), 'label_log_in' => __( 'Sign In', 'personalize-login' ), 'redirect' => $attributes['redirect'], ) ); ?> <a class="forgot-password" href="<?php echo wp_lostpassword_url(); ?>"> <?php _e( 'Forgot your password?', 'personalize-login' ); ?> </a> </div>
First, the template begins with an optional title, controlled by the shortcode parameter, show_title
as explained earlier.
Then, on lines 6-14, you'll notice that most of the rendering of the login form is still done by WordPress, using the function wp_login_form
. The function takes an optional parameter array that can be used to customize the contents of the login form.
Here are the customizations I have made in our example code:
- First, I wanted to use the wording "Sign In" instead of the default "Log In".
- Second, I wanted to use use email addresses as user names, so I changed the label for user name to "Email".
- Third, there is the redirect URL which was forwarded through the shortcode handler function above.
For further customizations, here's the full list of parameters you can use in the parameter array passed to wp_login_form
:
Parameter |
Description |
Default Value |
---|---|---|
echo |
Whether the login form should be rendered. If false, the output is returned as a string. |
true |
redirect |
The URL to redirect to after successful login. | Redirects back to current page. |
form_id |
The HTML id of the login form. |
loginform |
label_username |
The text label for the user name field. |
__( 'Username' ) |
label_password |
The text label for the password field. |
__( 'Password' ) |
label_remember |
The text label for the "Remember Me" checkbox. |
__( 'Remember Me' ) |
label_log_in |
The text label for the "Log In" button. |
__( 'Log In' ) |
id_username |
The HTML id for the user name field. |
user_login |
id_password |
The HTML id for the password field. |
user_pass |
id_remember |
The HTML id for the "Remember Me" checkbox. |
rememberme |
id_submit |
The HTML id for the submit button. | wp-submit |
remember |
Whether the "Remember Me" checkbox should be shown or not. | true |
value_username |
Default value for user name. | Empty |
value_remember |
Whether the "Remember Me" checkbox should be checked initially or not. | false |
Finally, on lines 16-18, there is a link to the WordPress lost password functionality — which we will replace with our own version in Part 3 of the tutorial series.
Now, the login page looks like this:
For most uses, this will be enough customization, but if you want to go deeper, there is nothing stopping you from creating the login form from scratch.
In this case, you can just create a HTML form and post its content to wp-login.php
— the full login URL for your current site is returned by the WordPress function wp_login_url
. The form needs to have the fields log
and pwd
for the login and password respectively.
Here's how you would create a login form that looks (and functions) just like the one above — without using wp_login_form
:
<div class="login-form-container"> <form method="post" action="<?php echo wp_login_url(); ?>"> <p class="login-username"> <label for="user_login"><?php _e( 'Email', 'personalize-login' ); ?></label> <input type="text" name="log" id="user_login"> </p> <p class="login-password"> <label for="user_pass"><?php _e( 'Password', 'personalize-login' ); ?></label> <input type="password" name="pwd" id="user_pass"> </p> <p class="login-submit"> <input type="submit" value="<?php _e( 'Sign In', 'personalize-login' ); ?>"> </p> </form> </div>
WordPress handles the login functionality, so the login form is already fully functional: Enter your valid user name and password and hit Sign In and you will be logged in just fine.
It's when you enter invalid data that you'll notice everything isn't quite ready yet: Instead of being redirected back to the login page we just created, you will see the regular WordPress login page. Also, unless the user points her browser directly to this newly created login page, she still sees the default WordPress login.
We need to take the new login page to use by redirecting users to it.
Redirect the Visitor to Login Page
Let's start with bringing the user to the login page in the first place.
Step 1: Redirect the User to Our Login Page
When a user tries to access any page inside the WordPress admin, or clicks on a Log in link in the Meta widget, he is sent to wp-login.php
. Next, we want to change this functionality and point the user to our own member-login
page instead.
All of the WordPress functionality we are customizing in this series from login to password resetting is handled in wp-login.php
with the different parts of the functionality identified with a parameter called action
.
If you look at the code inside the PHP file, you will notice that right before the actual login functionality begins, two actions are fired: login_init
and login_form_{action}
where {action}
is the name of an action being executed (for example login
, postpass
, or logout
).
The second one is more specific, so by using it, we won't have to do all that much checking ourselves: just by hooking a function to login_form_login
, we can target our redirect functionality precisely to the moment a user enters the login page.
Add the following action to the plugin class's constructor:
add_action( 'login_form_login', array( $this, 'redirect_to_custom_login' ) );
Then, add the action function:
/** * Redirect the user to the custom login page instead of wp-login.php. */ function redirect_to_custom_login() { if ( $_SERVER['REQUEST_METHOD'] == 'GET' ) { $redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : null; if ( is_user_logged_in() ) { $this->redirect_logged_in_user( $redirect_to ); exit; } // The rest are redirected to the login page $login_url = home_url( 'member-login' ); if ( ! empty( $redirect_to ) ) { $login_url = add_query_arg( 'redirect_to', $redirect_to, $login_url ); } wp_redirect( $login_url ); exit; } }
The redirect_to_custom_login
function is called in two situations: first, when the user enters the login page, and second, when he or she submits the login form. To differentiate between the two, we look at the request method: the form is submitted using POST
while the page is loaded using GET
.
So, to let wp-login.php
handle the actual login and authentication, on line 5, we make sure that the redirect is only done on GET
requests.
On line 6, if a request variable redirect_to
is passed in, we'll store it in a temporary variable $redirect_to
.
Already logged in users don't need to go through the login form, so we'll redirect them to either to the WordPress dashboard or the member-account page created earlier (lines 8-11). To prepare for similar redirects in the rest of the tutorial series, we'll separate the redirect to its own function, redirect_logged_in_user
, which we'll add soon.
One thing worth noting is that we are relying on the page slugs (pretty permalinks) in specifying the pages we redirect the user to. If you want to improve this example and make the plugin more flexible, you could look up the pages using the slug and then create permalinks using that data. For now, just turn on pretty permalinks (any option except the default is OK) on the Permalinks settings page and everything will work just fine.
On lines 15-17, we add the redirect_to
parameter to the new request if one is present so that no information is lost at this redirect.
On line 20, you'll notice that we end the execution rather abruptly using an exit
command. This is important because otherwise, the execution will continue with the rest of the action in wp-login.php
, maybe causing problems with our customization. Keep an eye on this also in other redirects we do in the rest of the tutorial.
Now, add the function, redirect_logged_in_user
mentioned above:
/** * Redirects the user to the correct page depending on whether he / she * is an admin or not. * * @param string $redirect_to An optional redirect_to URL for admin users */ private function redirect_logged_in_user( $redirect_to = null ) { $user = wp_get_current_user(); if ( user_can( $user, 'manage_options' ) ) { if ( $redirect_to ) { wp_safe_redirect( $redirect_to ); } else { wp_redirect( admin_url() ); } } else { wp_redirect( home_url( 'member-account' ) ); } }
The function redirects the user to a different URL depending on his or her access level. For admin users, we also check the $redirect_to
parameter: if one is set, the user is directed to this URL instead of the default admin_url
.
Step 2: Redirect When There Are Errors
We have now covered the first redirect and so, whenever the user first clicks on a link to wp-login.php
or tries to access the admin dashboard, he or she is sent to our new login page. But we're not done yet: when there are errors in the login (invalid username or password, empty form, and so on) the user still sees the default login page.
In WordPress, user authentication happens through a function called wp_authenticate
— located in pluggable.php
if you want to take a look.
The function does some basic sanitization of parameters and then calls the filters attached to the filter hook authenticate
. This is to allow plugins to replace the authentication flow with their own functionality, but most importantly for us, even the WordPress default authentication is done through this filter.
At this point, we are not interested in replacing the authentication, but the filter hook is still useful as it gives us a chance to collect the errors from other filters and proceed accordingly:
- Hook a new function to the
authenticate
filter using a high enough priority (in WordPress filters and actions, a higher number in the priority parameter means that the function will be called later in the filter processing order). - The filter function will receive the results from previous filters in the filter chain as a parameter. Use this data to check if there were any errors in the login.
- If no errors are found, let everything proceed normally so WordPress can finish the login.
- If there are errors, instead of letting WordPress do its regular error handling, redirect to our custom login page.
In the current WordPress version (4.2 at the time of writing), WordPress has the following two filters hooked to authenticate
:
add_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 ); add_filter( 'authenticate', 'wp_authenticate_spam_check', 99 );
The basic authentication has priority 20
and the priority for wp_authenticate_spam_check
is 99
, so if we set ours at higher than 99
(I picked 101
), it will be run after both of these. It is of course possible that another plugin you use adds a filter with a higher priority, in which case you’ll need to increase the number to take that plugin's errors into account.
First, add the new filter definition in our plugin class's constructor:
add_filter( 'authenticate', array( $this, 'maybe_redirect_at_authenticate' ), 101, 3 );
Then, add the function, maybe_redirect_at_authenticate
, at the end of the plugin class:
/** * Redirect the user after authentication if there were any errors. * * @param Wp_User|Wp_Error $user The signed in user, or the errors that have occurred during login. * @param string $username The user name used to log in. * @param string $password The password used to log in. * * @return Wp_User|Wp_Error The logged in user, or error information if there were errors. */ function maybe_redirect_at_authenticate( $user, $username, $password ) { // Check if the earlier authenticate filter (most likely, // the default WordPress authentication) functions have found errors if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) { if ( is_wp_error( $user ) ) { $error_codes = join( ',', $user->get_error_codes() ); $login_url = home_url( 'member-login' ); $login_url = add_query_arg( 'login', $error_codes, $login_url ); wp_redirect( $login_url ); exit; } } return $user; }
The authenticate
filter functions take three parameters:
-
$user
is the signed in user's user object, or an instance of theWp_Error
class if there have been errors in the filter chain so far. -
$username
is the user name entered by the user trying to log in. -
$password
is the password entered by the user.
For our use, the first parameter is the interesting one: if $user
is an error that means one of the earlier authenticate
filter functions has found an error in the login. That's when our function jumps in (on line 13) to collect all of the error codes and append them to the redirect URL on lines 15-18. Finally, the function redirects the user to our custom login page to display the errors.
Step 3: Render Error Messages
The plugin is now catching the errors and redirecting the user back to our custom login page also when it finds some. To make the experience useful for the user, we still need to print out the error messages.
First, in our shortcode function, we will use the login
parameter sent by the redirect created in the previous step to see what errors have occurred and to replace the error codes with readable error messages. This is a good place to use your imagination and create some fun and personal (but hopefully not annoying) error messages.
Create a function for converting error codes to error messages, using a simple switch...case
construct. The error codes are the ones used by the WordPress login functionality.
/** * Finds and returns a matching error message for the given error code. * * @param string $error_code The error code to look up. * * @return string An error message. */ private function get_error_message( $error_code ) { switch ( $error_code ) { case 'empty_username': return __( 'You do have an email address, right?', 'personalize-login' ); case 'empty_password': return __( 'You need to enter a password to login.', 'personalize-login' ); case 'invalid_username': return __( "We don't have any users with that email address. Maybe you used a different one when signing up?", 'personalize-login' ); case 'incorrect_password': $err = __( "The password you entered wasn't quite right. <a href='%s'>Did you forget your password</a>?", 'personalize-login' ); return sprintf( $err, wp_lostpassword_url() ); default: break; } return __( 'An unknown error occurred. Please try again later.', 'personalize-login' ); }
Then, to use the error messages, add the following code to render_login_form
, right before rendering the login form template. It will go through the list of error codes and collect matching error messages into an array, $errors
:
// Error messages $errors = array(); if ( isset( $_REQUEST['login'] ) ) { $error_codes = explode( ',', $_REQUEST['login'] ); foreach ( $error_codes as $code ) { $errors []= $this->get_error_message( $code ); } } $attributes['errors'] = $errors;
The actual rendering of the error messages is done in the login_form.php
template. Add the following right after the login form's title and before the login form:
<!-- Show errors if there are any --> <?php if ( count( $attributes['errors'] ) > 0 ) : ?> <?php foreach ( $attributes['errors'] as $error ) : ?> <p class="login-error"> <?php echo $error; ?> </p> <?php endforeach; ?> <?php endif; ?>
Now, the error messages are in place and we can test them. Try signing in using incorrect information and you will be redirected back to our custom page with a custom error message of our own:
Step 4: Show Correct Message at Logout
We're almost done — but not quite. There are still two more things to do before we can call it for a day and say we have replaced all of the WordPress login flow with our own version: rendering the correct message when a user signs out, and redirecting the user to the correct page after a successful login.
Let's start with the logout.
Once the user has been logged out from your site, in the function wp_logout
, WordPress fires the action wp_logout
. This is a good place to hook to and do our own redirects before WordPress redirects the user back to wp-login.php
.
In the plugin class's constructor, add the following line:
add_action( 'wp_logout', array( $this, 'redirect_after_logout' ) );
Then add the matching function:
/** * Redirect to custom login page after the user has been logged out. */ public function redirect_after_logout() { $redirect_url = home_url( 'member-login?logged_out=true' ); wp_safe_redirect( $redirect_url ); exit; }
The function redirects the user to our member-login
page with the parameter logged_out
appended to the request and then stops the execution.
Next, modify the login form shortcode function to take this parameter into account. A good place for this code is right after the error message handling we created above:
// Check if user just logged out $attributes['logged_out'] = isset( $_REQUEST['logged_out'] ) && $_REQUEST['logged_out'] == true;
Finally, we need to print a notification in the template. Again, add this right after printing out potential errors:
<!-- Show logged out message if user just logged out --> <?php if ( $attributes['logged_out'] ) : ?> <p class="login-info"> <?php _e( 'You have signed out. Would you like to sign in again?', 'personalize-login' ); ?> </p> <?php endif; ?>
That's it. Now when you sign in and then sign out, you'll see something like this:
Redirect the User to the Account Page When Logged In
We have now implemented the custom login and handled all of the error cases. As a final touch, to make everything feel just right, let's add one last redirect. This one will make sure the user always ends up where he or she wants (and is allowed) to go.
For admins, this is the admin dashboard and for regular, non-admin users, the (currently empty) member-account
page.
As WordPress provides a filter for returning the redirect URL after a successful login, this one is rather straightforward: all we need to do is to create a function that checks the current user's capabilities and returns the correct redirect URL accordingly.
To do this, add one last filter, again in the plugin's constructor:
add_filter( 'login_redirect', array( $this, 'redirect_after_login' ), 10, 3 );
Then add the matching filter function:
/** * Returns the URL to which the user should be redirected after the (successful) login. * * @param string $redirect_to The redirect destination URL. * @param string $requested_redirect_to The requested redirect destination URL passed as a parameter. * @param WP_User|WP_Error $user WP_User object if login was successful, WP_Error object otherwise. * * @return string Redirect URL */ public function redirect_after_login( $redirect_to, $requested_redirect_to, $user ) { $redirect_url = home_url(); if ( ! isset( $user->ID ) ) { return $redirect_url; } if ( user_can( $user, 'manage_options' ) ) { // Use the redirect_to parameter if one is set, otherwise redirect to admin dashboard. if ( $requested_redirect_to == '' ) { $redirect_url = admin_url(); } else { $redirect_url = $requested_redirect_to; } } else { // Non-admin users always go to their account page after login $redirect_url = home_url( 'member-account' ); } return wp_validate_redirect( $redirect_url, home_url() ); }
First, the function checks if the user really was logged in (on line 13) and redirects back to the site's home page if no user is found.
Then, it picks the correct redirect depending on the current user's capabilities:
- Admins (checked on line 17, using the user_can function) are redirected to the WordPress admin dashboard (line 20) unless a
redirect_to
parameter was passed with the request. - Regular users are always redirected to the
member-account
page to prevent them from accessing the WordPress dashboard. This is of course up to you: if you want, you can for example point them to their user page in the WordPress admin.
If you like, you can also add more fine grained redirects depending on users' roles and capabilities.
What’s next?
That's it. You have now built a plugin to replace the WordPress login screen with a custom page that you can customize to make it fit your theme, or the product you are building on top of WordPress.
In the next tutorial in this series we will build upon this and create a new user registration flow, adding a few new fields and a captcha to prevent bots from registering.
Until then, have fun tweaking your login page!
Comments