¿Cómo construir un formulario personalizado de inicio de sesión/registro con manejo de errores?
Estoy trabajando en un plugin donde estoy creando una página personalizada que contiene un formulario de inicio de sesión y otro para el registro de usuarios; tengo los formularios construidos y funcionando, este es mi código.
<!-- Inicio formulario de login -->
<form name="loginform" id="login_form" class="login_form" action="<?php echo esc_url( wp_login_url() ); ?>" method="post">
<p>
<input type="text" name="log" id="user_login" class="input" placeholder="Nombre de usuario" />
</p>
<p>
<input type="password" name="pwd" id="user_pass" class="input" placeholder="Contraseña" />
</p>
<button name="wp-submit" id="wp-submit" class="btn"><?php _e("Iniciar sesión", "shorti"); ?></button>
<input type="hidden" name="redirect_to" value="<?php echo $_SERVER["REQUEST_URI"]; ?>" />
</form>
<!-- fin formulario de login -->
<!-- Inicio formulario de registro -->
<form method="post" id="register_form" class="wp-user-form" action="<?php echo site_url('wp-login.php?action=register', 'login_post') ?>">
<p><!-- Nombre de usuario -->
<input type="text" name="user_login" id="user_login" class="input" placeholder="nombre de usuario" />
</p>
<p><!-- Email para enviar contraseña -->
<input type="email" name="user_email" id="user_email" class="input" placeholder="dirección de email" />
</p>
<p class="small-text">Recibirás un email con una contraseña generada<br />(que podrás cambiar en tu "configuración de usuario")</p>
<?php do_action('register_form'); ?>
<button name="wp-submit" id="wp-submit" class="btn"><?php _e("¡Registrarse!", "shorti"); ?></button>
<?php $register = $_GET['register']; if($register == true) { echo '<p>¡Revisa tu email para ver la contraseña!</p>'; } ?>
<input type="hidden" name="redirect_to" value="<?php echo $_SERVER['REQUEST_URI']; ?>?register=true" />
<input type="hidden" name="user-cookie" value="1" />
</form><!-- fin formulario de registro -->
Esto funciona bien excepto que cuando hay un mensaje de error redirige al usuario a wp-login.php. Necesito que la página con el formulario maneje todos los errores con AJAX; para dar una mejor experiencia de usuario. ¿Cuál es la mejor manera de hacer esto?

Observa el elemento de formulario de apertura. El valor para action determina a dónde se envían los datos del formulario. Al cambiar el valor, puedes indicar fácilmente que el formulario se redirija a sí mismo en lugar de a otra página.
El codex ofrece un enfoque alternativo para generar el marcado necesario para el formulario:
http://codex.wordpress.org/Customizing_the_Login_Form#Make_a_Custom_Login_Page
Aquí hay un tutorial mucho más detallado:
http://digwp.com/2010/12/login-register-password-code/
Siempre podrías usar o bifurcar un plugin popular basado en Ajax ya existente para el inicio de sesión:

Sí, el enlace Digging into WP es un gran recurso... es lo que he utilizado para construir lo que tengo actualmente (el código que compartí), pero no muestran cómo manejar los errores en la misma página (al menos no puedo ver dónde lo hacen). En segundo lugar, estoy construyendo esto para un plugin personalizado en el que estoy trabajando, lo que significa; realmente no quiero depender de que otro plugin funcione continuamente para que el mío funcione.

Esto es lo que hago. Utilizo el formulario de inicio de sesión para crear el login https://codex.wordpress.org/Function_Reference/wp_login_form
A continuación se muestran las redirecciones de login y logout, además del manejo de errores de inicio de sesión. Esto te ayudará con problemas de acceso.
/** Redirección de Login
* Redirige al usuario después de un inicio de sesión exitoso.
*
* @param string $redirect_to URL para redireccionar.
* @param string $request URL desde donde viene el usuario.
* @param object $user Datos del usuario logueado.
* @return string
*/
function my_login_redirect( $redirect_to, $request, $user ) {
// ¿hay un usuario para verificar?
global $user;
if ( isset( $user->roles ) && is_array( $user->roles ) ) {
// verificar si es administrador
if ( in_array( 'administrator', $user->roles ) ) {
// redirigir al área por defecto
return home_url('/wp-admin/');
} else {
return home_url();
}
} else {
return $redirect_to;
}
}
/* Manejo de Error en Login */
add_action( 'wp_login_failed', 'aa_login_failed' ); // hook para login fallido
function aa_login_failed( $user ) {
// verificar desde qué página viene el intento de login
$referrer = $_SERVER['HTTP_REFERER'];
// verificar que no estemos en la página de login por defecto
if ( !empty($referrer) && !strstr($referrer,'wp-login') && !strstr($referrer,'wp-admin') && $user!=null ) {
// asegurarnos de que no haya ya un intento fallido
if ( !strstr($referrer, '?login=failed' )) {
// Redirigir a la página de login y añadir un parámetro de login fallido
wp_redirect( $referrer . '?login=failed');
} else {
wp_redirect( $referrer );
}
exit;
}
}
/* Manejo de Error por Campos Vacíos en Login */
add_action( 'authenticate', 'pu_blank_login');
function pu_blank_login( $user ){
// verificar desde qué página viene el intento de login
$referrer = $_SERVER['HTTP_REFERER'];
$error = false;
if($_POST['log'] == '' || $_POST['pwd'] == '')
{
$error = true;
}
// verificar que no estemos en la página de login por defecto
if ( !empty($referrer) && !strstr($referrer,'wp-login') && !strstr($referrer,'wp-admin') && $error ) {
// asegurarnos de que no haya ya un intento fallido
if ( !strstr($referrer, '?login=failed') ) {
// Redirigir a la página de login y añadir un parámetro de login fallido
wp_redirect( $referrer . '?login=failed' );
} else {
wp_redirect( $referrer );
}
exit;
}
}
/* Redirección de Logout */
add_filter( 'login_redirect', 'my_login_redirect', 10, 3 );
function go_home(){
wp_redirect( home_url('/login/') );
exit();
}
add_action('wp_logout','go_home');
