37

How to display the WordPress user registration form (the form that appears in "www.mywebsite.com/wp-register.php" page) in the front end of my blog?

I have customised the registration form. But don't know how to call that form in the front end page. Any support will be really great help.

Thanks in advance. :)

3
  • Best solution i found is Theme My Login plugin.
    – wyrfel
    Commented Feb 24, 2011 at 14:37
  • this Article provides a greate tutorial on how to create you own frontend register/login/restore password forms. or if you are looking for a plugin then i've used these before and can recomend them: - Ajax Login/Register - Login With Ajax
    – Bainternet
    Commented Feb 24, 2011 at 15:02
  • Cristian from Cosmolabs have post a great tutorial with source files that give you the ability to build a front-end User Profile, Login and Register templates.
    – Philip
    Commented Feb 24, 2011 at 17:58

6 Answers 6

43
+100

The process involves 2 steps:

  1. show the frontend form
  2. save the data on submission

There are 3 different approach that comes to my mind to show the frontend:

  • use the built-in registration form, editing styles, etc to make it more "frontend like"
  • use a WordPress page/post, and display form using a shortcode
  • use a dedicate template not connected with any page/post, but called by a specific url

For this answer I'll use the latter. The reasons are:

  • using built-in registration form can be a good idea, deep customizations can be very hard using built-in form, and if one also want customize form fields the pain increase
  • use a WordPress page in combination with a shortcode, is not so reliable, and also I think that shorcodes should not be used for functionality, just for formatting and such

1: Build the url

All of us knows that default registration form of a WordPress site is often a target for spammers. Using a custom url is an help to solve this problem. In addition I want also use a variable url, i.e. the registration form url should not be always the same, this makes spammers life harder. The trick is done using a nonce in the url:

/**
* Generate dynamic registration url
*/
function custom_registration_url() {
  $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
  return home_url( $nonce );
}

/**
* Generate dynamic registration link
*/
function custom_registration_link() {
  $format = '<a href="%s">%s</a>';
  printf(
    $format,
    custom_registration_url(), __( 'Register', 'custom_reg_form' )
  );
}

Using this functions is easy to display in templates a link to the registration form even if it's dynamic.

2: Recognize the url, first stub of Custom_Reg\Custom_Reg class

Now we need to recognize the url. For the pourpose I'll start to write a class, that will be finished later in the answer:

<?php
// don't save, just a stub
namespace Custom_Reg;

class Custom_Reg {

  function checkUrl() {
    $url_part = $this->getUrl();
    $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
    if ( ( $url_part === $nonce ) ) {
      // do nothing if registration is not allowed or user logged
      if ( is_user_logged_in() || ! get_option('users_can_register') ) {
        wp_safe_redirect( home_url() );
        exit();
      }
      return TRUE;
    }
  }

  protected function getUrl() {
    $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' );
    $relative = trim(str_replace($home_path, '', esc_url(add_query_arg(array()))), '/');
    $parts = explode( '/', $relative );
    if ( ! empty( $parts ) && ! isset( $parts[1] ) ) {
      return $parts[0];
    }
  }

}

The function look at the first part of the url after home_url(), and if it matches with our nonce it return TRUE. this function will be used to check our request and perform needed action to display our form.

3: The Custom_Reg\Form class

I'll now write a class, that will be responsible to generate the form markup. I'll use it also to store in a property the template file path that should be used to display the form.

<?php 
// file: Form.php
namespace Custom_Reg;

class Form {

  protected $fields;

  protected $verb = 'POST';

  protected $template;

  protected $form;

  public function __construct() {
    $this->fields = new \ArrayIterator();
  }

  public function create() {
    do_action( 'custom_reg_form_create', $this );
    $form = $this->open();
    $it =  $this->getFields();
    $it->rewind();
    while( $it->valid() ) {
      $field = $it->current();
      if ( ! $field instanceof FieldInterface ) {
        throw new \DomainException( "Invalid field" );
      }
      $form .= $field->create() . PHP_EOL;
      $it->next();
    }
    do_action( 'custom_reg_form_after_fields', $this );
    $form .= $this->close();
    $this->form = $form;
    add_action( 'custom_registration_form', array( $this, 'output' ), 0 );
  }

  public function output() {
    unset( $GLOBALS['wp_filters']['custom_registration_form'] );
    if ( ! empty( $this->form ) ) {
      echo $this->form;
    }
  }

  public function getTemplate() {
    return $this->template;
  }

  public function setTemplate( $template ) {
    if ( ! is_string( $template ) ) {
      throw new \InvalidArgumentException( "Invalid template" );
    }
    $this->template = $template;
  }

  public function addField( FieldInterface $field ) {
    $hook = 'custom_reg_form_create';
    if ( did_action( $hook ) && current_filter() !== $hook ) {
      throw new \BadMethodCallException( "Add fields before {$hook} is fired" );
    }
    $this->getFields()->append( $field );
  }

  public function getFields() {
    return $this->fields;
  }

  public function getVerb() {
    return $this->verb;
  }

  public function setVerb( $verb ) {
    if ( ! is_string( $verb) ) {
     throw new \InvalidArgumentException( "Invalid verb" );
    }
    $verb = strtoupper($verb);
    if ( in_array($verb, array( 'GET', 'POST' ) ) ) $this->verb = $verb;
  }

  protected function open() {
    $out = sprintf( '<form id="custom_reg_form" method="%s">', $this->verb ) . PHP_EOL;
    $nonce = '<input type="hidden" name="_n" value="%s" />';
    $out .= sprintf( $nonce,  wp_create_nonce( 'custom_reg_form_nonce' ) ) . PHP_EOL;
    $identity = '<input type="hidden" name="custom_reg_form" value="%s" />';
    $out .= sprintf( $identity,  __CLASS__ ) . PHP_EOL;
    return $out;
  }

  protected function close() {
    $submit =  __('Register', 'custom_reg_form');
    $out = sprintf( '<input type="submit" value="%s" />', $submit );
    $out .= '</form>';
    return $out;
  }

}

The class generate form markup looping all the fields added calling create method on each of them. Each field must be instance of Custom_Reg\FieldInterface. An additional hidden field is added for nonce verifiction. Form method is 'POST' by default, but it can be setted to 'GET' using setVerb method. Once created the markup is saved inside the $form object property that is echoed by output() method, hooked into 'custom_registration_form' hook: in the form template, simply call do_action( 'custom_registration_form' ) will output the form.

4: The default template

As I said the template for form can be easily overriden, however we need a basic template as a fallback. I'll wrote here a very rough template, more a proof of concept than a real template.

<?php
// file: default_form_template.php
get_header();

global $custom_reg_form_done, $custom_reg_form_error;

if ( isset( $custom_reg_form_done ) && $custom_reg_form_done ) {
  echo '<p class="success">';
  _e(
    'Thank you, your registration was submitted, check your email.',
    'custom_reg_form'
  );
  echo '</p>';
} else {
  if ( $custom_reg_form_error ) {
    echo '<p class="error">' . $custom_reg_form_error  . '</p>';
  }
  do_action( 'custom_registration_form' );
}

get_footer();

5: The Custom_Reg\FieldInterface interface

Every field should be an object that implements the following interface

<?php 
// file: FieldInterface.php
namespace Custom_Reg;

interface FieldInterface {

  /**
   * Return the field id, used to name the request value and for the 'name' param of
   * html input field
   */
  public function getId();

  /**
   * Return the filter constant that must be used with
   * filter_input so get the value from request
   */
  public function getFilter();

  /**
   * Return true if the used value passed as argument should be accepted, false if not
   */
  public function isValid( $value = NULL );

  /**
   * Return true if field is required, false if not
   */
  public function isRequired();

  /**
   * Return the field input markup. The 'name' param must be output 
   * according to getId()
   */
  public function create( $value = '');
}

I think that comments explain what classes implementing this interface should do.

6: Adding some fields

Now we need some fields. We can create a file called 'fields.php' where we define the fields classes:

<?php
// file: fields.php
namespace Custom_Reg;

abstract class BaseField implements FieldInterface {

  protected function getType() {
    return isset( $this->type ) ? $this->type : 'text';
  }

  protected function getClass() {
    $type = $this->getType();
    if ( ! empty($type) ) return "{$type}-field";
  }

  public function getFilter() {
    return FILTER_SANITIZE_STRING;
  }

  public function isRequired() {
    return isset( $this->required ) ? $this->required : FALSE;
  }

  public function isValid( $value = NULL ) {
    if ( $this->isRequired() ) {
      return $value != '';
    }
    return TRUE;
  }

  public function create( $value = '' ) {
    $label = '<p><label>' . $this->getLabel() . '</label>';
    $format = '<input type="%s" name="%s" value="%s" class="%s"%s /></p>';
    $required = $this->isRequired() ? ' required' : '';
    return $label . sprintf(
      $format,
      $this->getType(), $this->getId(), $value, $this->getClass(), $required
    );
  }

  abstract function getLabel();
}


class FullName extends BaseField {

  protected $required = TRUE;

  public function getID() {
    return 'fullname';
  }

  public function getLabel() {
    return __( 'Full Name', 'custom_reg_form' );
  }

}

class Login extends BaseField {

  protected $required = TRUE;

  public function getID() {
    return 'login';
  }

  public function getLabel() {
    return __( 'Username', 'custom_reg_form' );
  }
}

class Email extends BaseField {

  protected $type = 'email';

  public function getID() {
    return 'email';
  }

  public function getLabel() {
    return __( 'Email', 'custom_reg_form' );
  }

  public function isValid( $value = NULL ) {
    return ! empty( $value ) && filter_var( $value, FILTER_VALIDATE_EMAIL );
  }
}

class Country extends BaseField {

  protected $required = FALSE;

  public function getID() {
    return 'country';
  }

  public function getLabel() {
    return __( 'Country', 'custom_reg_form' );
  }
}

I've use a base class to define the default interface implemantation, however, one can add very customized fields directly implementing the interface or extending the base class and overriding some methods.

At this point we have everything to display the form, now we need something to validate and save the fields.

7: The Custom_Reg\Saver class

<?php
// file: Saver.php
namespace Custom_Reg;

class Saver {

  protected $fields;

  protected $user = array( 'user_login' => NULL, 'user_email' => NULL );

  protected $meta = array();

  protected $error;

  public function setFields( \ArrayIterator $fields ) {
    $this->fields = $fields;
  }

  /**
  * validate all the fields
  */
  public function validate() {
    // if registration is not allowed return false
    if ( ! get_option('users_can_register') ) return FALSE;
    // if no fields are setted return FALSE
    if ( ! $this->getFields() instanceof \ArrayIterator ) return FALSE;
    // first check nonce
    $nonce = $this->getValue( '_n' );
    if ( $nonce !== wp_create_nonce( 'custom_reg_form_nonce' ) ) return FALSE;
    // then check all fields
    $it =  $this->getFields();
    while( $it->valid() ) {
      $field = $it->current();
      $key = $field->getID();
      if ( ! $field instanceof FieldInterface ) {
        throw new \DomainException( "Invalid field" );
      }
      $value = $this->getValue( $key, $field->getFilter() );
      if ( $field->isRequired() && empty($value) ) {
        $this->error = sprintf( __('%s is required', 'custom_reg_form' ), $key );
        return FALSE;
      }
      if ( ! $field->isValid( $value ) ) {
        $this->error = sprintf( __('%s is not valid', 'custom_reg_form' ), $key );
        return FALSE;
      }
      if ( in_array( "user_{$key}", array_keys($this->user) ) ) {
        $this->user["user_{$key}"] = $value;
      } else {
        $this->meta[$key] = $value;
      }
      $it->next();
    }
    return TRUE;
  }

  /**
  * Save the user using core register_new_user that handle username and email check
  * and also sending email to new user
  * in addition save all other custom data in user meta
  *
  * @see register_new_user()
  */
  public function save() {
    // if registration is not allowed return false
    if ( ! get_option('users_can_register') ) return FALSE;
    // check mandatory fields
    if ( ! isset($this->user['user_login']) || ! isset($this->user['user_email']) ) {
      return false;
    }
    $user = register_new_user( $this->user['user_login'], $this->user['user_email'] );
    if ( is_numeric($user) ) {
      if ( ! update_user_meta( $user, 'custom_data', $this->meta ) ) {
        wp_delete_user($user);
        return FALSE;
      }
      return TRUE;
    } elseif ( is_wp_error( $user ) ) {
      $this->error = $user->get_error_message();
    }
    return FALSE;
  }

  public function getValue( $var, $filter = FILTER_SANITIZE_STRING ) {
    if ( ! is_string($var) ) {
      throw new \InvalidArgumentException( "Invalid value" );
    }
    $method = strtoupper( filter_input( INPUT_SERVER, 'REQUEST_METHOD' ) );
    $type = $method === 'GET' ? INPUT_GET : INPUT_POST;
    $val = filter_input( $type, $var, $filter );
    return $val;
  }

  public function getFields() {
    return $this->fields;
  }

  public function getErrorMessage() {
    return $this->error;
  }

}

That class, has 2 main methods, one (validate) that loop the fields, validate them and saving good data into an array, the second (save) save all data in database and send password via email to new user.

8: Using defined classes: finishing the Custom_Reg class

Now we can work again on Custom_Reg class, adding the methods that "glues" the object defined and make them works

<?php 
// file Custom_Reg.php
namespace Custom_Reg;

class Custom_Reg {

  protected $form;

  protected $saver;

  function __construct( Form $form, Saver $saver ) {
    $this->form = $form;
    $this->saver = $saver;
  }

  /**
   * Check if the url to recognize is the one for the registration form page
   */
  function checkUrl() {
    $url_part = $this->getUrl();
    $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
    if ( ( $url_part === $nonce ) ) {
      // do nothing if registration is not allowed or user logged
      if ( is_user_logged_in() || ! get_option('users_can_register') ) {
        wp_safe_redirect( home_url() );
        exit();
      }
      return TRUE;
    }
  }

  /**
   * Init the form, if submitted validate and save, if not just display it
   */
  function init() {
    if ( $this->checkUrl() !== TRUE ) return;
    do_action( 'custom_reg_form_init', $this->form );
    if ( $this->isSubmitted() ) {
      $this->save();
    }
    // don't need to create form if already saved
    if ( ! isset( $custom_reg_form_done ) || ! $custom_reg_form_done ) {
      $this->form->create();
    }
    load_template( $this->getTemplate() );
    exit();
  }

  protected function save() {
    global $custom_reg_form_error;
    $this->saver->setFields( $this->form->getFields() );
    if ( $this->saver->validate() === TRUE ) { // validate?
      if ( $this->saver->save() ) { // saved?
        global $custom_reg_form_done;
        $custom_reg_form_done = TRUE;
      } else { // saving error
        $err =  $this->saver->getErrorMessage(); 
        $custom_reg_form_error = $err ? : __( 'Error on save.', 'custom_reg_form' );
      }
    } else { // validation error
       $custom_reg_form_error = $this->saver->getErrorMessage();
    }
  }

  protected function isSubmitted() {
    $type = $this->form->getVerb() === 'GET' ? INPUT_GET : INPUT_POST;
    $sub = filter_input( $type, 'custom_reg_form', FILTER_SANITIZE_STRING );
    return ( ! empty( $sub ) && $sub === get_class( $this->form ) );
  }

  protected function getTemplate() {
    $base = $this->form->getTemplate() ? : FALSE;
    $template = FALSE;
    $default = dirname( __FILE__ ) . '/default_form_template.php';
    if ( ! empty( $base ) ) {
      $template = locate_template( $base );
    }
    return $template ? : $default;
  }

   protected function getUrl() {
    $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' );
    $relative = trim( str_replace( $home_path, '', add_query_arg( array() ) ), '/' );
    $parts = explode( '/', $relative );
    if ( ! empty( $parts ) && ! isset( $parts[1] ) ) {
      return $parts[0];
    }
  }

}

The constructor of the class accepts an instance of Form and one of Saver.

init() method (using checkUrl()) look at the first part of the url after home_url(), and if it matches with the right nonce, it checks if the form was already submitted, if so using the Saver object, it validate and save the userdata, otherwise just print the form.

init() method also fires the action hook 'custom_reg_form_init' passing the form instance as argument: this hook should be used to add fields, to setup the custom template and also to customize the form method.

9: Putting things together

Now we need to write the main plugin file, where we can

  • require all the files
  • load the textdomain
  • startup entire process using instantiating Custom_Reg class and call init() method on it using a reasonably early hook
  • use the 'custom_reg_form_init' to add the fields to form class

So:

<?php 
/**
 * Plugin Name: Custom Registration Form
 * Description: Just a rough plugin example to answer a WPSE question
 * Plugin URI: https://wordpress.stackexchange.com/questions/10309/
 * Author: G. M.
 * Author URI: https://wordpress.stackexchange.com/users/35541/g-m
 *
 */

if ( is_admin() ) return; // this plugin is all about frontend

load_plugin_textdomain(
  'custom_reg_form',
  FALSE,
  plugin_dir_path( __FILE__ ) . 'langs'
); 

require_once plugin_dir_path( __FILE__ ) . 'FieldInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'fields.php';
require_once plugin_dir_path( __FILE__ ) . 'Form.php';
require_once plugin_dir_path( __FILE__ ) . 'Saver.php';
require_once plugin_dir_path( __FILE__ ) . 'CustomReg.php';

/**
* Generate dynamic registration url
*/
function custom_registration_url() {
  $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
  return home_url( $nonce );
}

/**
* Generate dynamic registration link
*/
function custom_registration_link() {
  $format = '<a href="%s">%s</a>';
  printf(
    $format,
    custom_registration_url(), __( 'Register', 'custom_reg_form' )
  );
}

/**
* Setup, show and save the form
*/
add_action( 'wp_loaded', function() {
  try {
    $form = new Custom_Reg\Form;
    $saver = new Custom_Reg\Saver;
    $custom_reg = new Custom_Reg\Custom_Reg( $form, $saver );
    $custom_reg->init();
  } catch ( Exception $e ) {
    if ( defined('WP_DEBUG') && WP_DEBUG ) {
      $msg = 'Exception on  ' . __FUNCTION__;
      $msg .= ', Type: ' . get_class( $e ) . ', Message: ';
      $msg .= $e->getMessage() ? : 'Unknown error';
      error_log( $msg );
    }
    wp_safe_redirect( home_url() );
  }
}, 0 );

/**
* Add fields to form
*/
add_action( 'custom_reg_form_init', function( $form ) {
  $classes = array(
    'Custom_Reg\FullName',
    'Custom_Reg\Login',
    'Custom_Reg\Email',
    'Custom_Reg\Country'
  );
  foreach ( $classes as $class ) {
    $form->addField( new $class );
  }
}, 1 );

10: Missing tasks

Now everithing is pretty done. We have just to customize the template, probably adding a custom template file in our theme.

We can add specific styles and scripts only to the custom registration page in this way

add_action( 'wp_enqueue_scripts', function() {
  // if not on custom registration form do nothing
  if ( did_action('custom_reg_form_init') ) {
    wp_enqueue_style( ... );
    wp_enqueue_script( ... );
  }
});

Using that method we can enqueue some js scripts to handle client side validation, e.g. this one. The markup needed to make that script work can be easily handled editing the Custom_Reg\BaseField class.

If we want to customize the registration email, we can use standard method and having custom data saved on meta, we can make use of them in the email.

Last task we probably we want to implement is prevent request to default registration form, as easy as:

add_action( 'login_form_register', function() { exit(); } );

All the files can be found in a Gist here.

2
  • 1
    Wow, this is a complete redesign of the registration functionality ! That probably is a good solution if you want to override completely the built-in registration process. I think not using the built-in registration form is not a good idea because you will loose other core features such as lost password form. And then, a newly registered user would need to display the traditional back-end login form to sign-in. Commented Mar 13, 2014 at 14:21
  • 3
    @FabienQuatravaux lost password and login form can just be used as usual (backend). Yes, code is incomplete because lost password and login form are not handled, but OP question was only about registration form and the answer was already too long to add other functionalities...
    – gmazzap
    Commented Mar 13, 2014 at 14:45
17

TLDR; Put the following form into your theme, the name and id attributes are important:

<form action="<?php echo site_url('wp-login.php?action=register', 'login_post') ?>" method="post">
    <input type="text" name="user_login" value="Username" id="user_login" class="input" />
    <input type="text" name="user_email" value="E-Mail" id="user_email" class="input"  />
    <?php do_action('register_form'); ?>
    <input type="submit" value="Register" id="register" />
</form>

I found an excellent Tutsplus article on Making a fancy Wordpress Register Form from scratch. This spends quite a lot of its time on styling the form, but has the following fairly simple section on the required wordpress code:

Step 4. WordPress

There is nothing fancy here; we only require two WordPress snippets, hidden within the wp-login.php file.

The first snippet:

<?php echo site_url('wp-login.php?action=register', 'login_post') ?>  

And:

<?php do_action('register_form'); ?>

Edit: I've added the extra final bit from the article to explain where to put the above code snippets - its just a form so it can go in any page template or sidebar or make a shortcode out of it. The important section is the form which contains the above snippets and the important required fields.

The final code should look like so:

<div style="display:none"> <!-- Registration -->
        <div id="register-form">
        <div class="title">
            <h1>Register your Account</h1>
            <span>Sign Up with us and Enjoy!</span>
        </div>
            <form action="<?php echo site_url('wp-login.php?action=register', 'login_post') ?>" method="post">
            <input type="text" name="user_login" value="Username" id="user_login" class="input" />
            <input type="text" name="user_email" value="E-Mail" id="user_email" class="input"  />
                <?php do_action('register_form'); ?>
                <input type="submit" value="Register" id="register" />
            <hr />
            <p class="statement">A password will be e-mailed to you.</p>


            </form>
        </div>
</div><!-- /Registration -->

Please note that it's really important, and necessary, to have user_login as a name and as an id attribute in your text input; the same is true for the email input. Otherwise, it won't work.

And with that, we're done!

2
  • Great solution ! Simple and efficient. But where do you put those snippets ? In a sidebar ? This tip seams to only work with an ajax registration form. Commented Mar 17, 2014 at 8:12
  • 1
    Thanks @FabienQuatravaux, I've updated the answer to include the last section of the article. There should be no need for an AJAX form - its just a POST form that submits to the wp-login.php?action=register page
    – icc97
    Commented Mar 18, 2014 at 10:31
6

this Article provides a greate tutorial on how to create you own frontend register/login/restore password forms.

or if you are looking for a plugin then i've used these before and can recomend them:

4

I made a website some time ago that was displaying a customized registration form on the front end side. This website is not live anymore but here are some screenshots. login form registration form lost password form

Here are the steps I have followed:

1) Activate the possibility for all visitors to request a new account via Settings > General > Membership option. The registration page now appears at the URL /wp-login.php?action=register

2) Customize the registration form so that it looks like your site front-end. This is more tricky and depends on the theme you are using.

Here is an example with twentythirteen :

// include theme scripts and styles on the login/registration page
add_action('login_enqueue_scripts', 'twentythirteen_scripts_styles');

// remove admin style on the login/registration page
add_filter( 'style_loader_tag', 'user16975_remove_admin_css', 10, 2);
function user16975_remove_admin_css($tag, $handle){
    if ( did_action('login_init')
    && ($handle == 'wp-admin' || $handle == 'buttons' || $handle == 'colors-fresh'))
        return "";

    else return $tag;
}

// display front-end header and footer on the login/registration page
add_action('login_footer', 'user16975_integrate_login');
function user16975_integrate_login(){
    ?><div id="page" class="hfeed site">
        <header id="masthead" class="site-header" role="banner">
            <a class="home-link" href="<?php echo esc_url( home_url( '/' ) ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
                <h1 class="site-title"><?php bloginfo( 'name' ); ?></h1>
                <h2 class="site-description"><?php bloginfo( 'description' ); ?></h2>
            </a>

            <div id="navbar" class="navbar">
                <nav id="site-navigation" class="navigation main-navigation" role="navigation">
                    <h3 class="menu-toggle"><?php _e( 'Menu', 'twentythirteen' ); ?></h3>
                    <a class="screen-reader-text skip-link" href="#content" title="<?php esc_attr_e( 'Skip to content', 'twentythirteen' ); ?>"><?php _e( 'Skip to content', 'twentythirteen' ); ?></a>
                    <?php wp_nav_menu( array( 'theme_location' => 'primary', 'menu_class' => 'nav-menu' ) ); ?>
                    <?php get_search_form(); ?>
                </nav><!-- #site-navigation -->
            </div><!-- #navbar -->
        </header><!-- #masthead -->

        <div id="main" class="site-main">
    <?php get_footer(); ?>
    <script>
        // move the login form into the page main content area
        jQuery('#main').append(jQuery('#login'));
    </script>
    <?php
}

Then modify the theme stylesheet to make the form appear as you want.

3) You can further modify the form by tweaking the displayed messages :

add_filter('login_message', 'user16975_login_message');
function user16975_login_message($message){
    if(strpos($message, 'register') !== false){
        $message = 'custom register message';
    } else {
        $message = 'custom login message';
    }
    return $message;
}

add_action('login_form', 'user16975_login_message2');
function user16975_login_message2(){
    echo 'another custom login message';
}

add_action('register_form', 'user16975_tweak_form');
function user16975_tweak_form(){
    echo 'another custom register message';
}

4) If you need a front-end registration form, you will probably don't want that registered users see the backend when they log-in.

add_filter('user_has_cap', 'user16975_refine_role', 10, 3);
function user16975_refine_role($allcaps, $cap, $args){
    global $pagenow;

    $user = wp_get_current_user();
    if($user->ID != 0 && $user->roles[0] == 'subscriber' && is_admin()){
        // deny access to WP backend
        $allcaps['read'] = false;
    }

    return $allcaps;
}

add_action('admin_page_access_denied', 'user16975_redirect_dashbord');
function user16975_redirect_dashbord(){
    wp_redirect(home_url());
    die();
}

There are lots of steps, but the result is here !

0
3

Way easier: use a WordPress function called wp_login_form() (Codex page here).

You can make your own plugin so that you can use a shortcode in on of your pages:

<?php
/*
Plugin Name: WP Login Form Shortcode
Description: Use <code>[wp_login_form]</code> to show WordPress' login form.
Version: 1.0
Author: WP-Buddy
Author URI: http://wp-buddy.com
License: GPLv2 or later
*/

add_action( 'init', 'wplfsc_add_shortcodes' );

function wplfsc_add_shortcodes() {
    add_shortcode( 'wp_login_form', 'wplfsc_shortcode' );
}

function wplfsc_shortcode( $atts, $content, $name ) {

$atts = shortcode_atts( array(
        'redirect'       => site_url( $_SERVER['REQUEST_URI'] ),
        'form_id'        => 'loginform',
        'label_username' => __( 'Username' ),
        'label_password' => __( 'Password' ),
        'label_remember' => __( 'Remember Me' ),
        'label_log_in'   => __( 'Log In' ),
        'id_username'    => 'user_login',
        'id_password'    => 'user_pass',
        'id_remember'    => 'rememberme',
        'id_submit'      => 'wp-submit',
        'remember'       => false,
        'value_username' => NULL,
        'value_remember' => false
), $atts, $name );

// echo is always false
$atts['echo'] = false;

// make real boolean values
$atts['remember']       = filter_var( $atts['remember'], FILTER_VALIDATE_BOOLEAN );
$atts['value_remember'] = filter_var( $atts['value_remember'], FILTER_VALIDATE_BOOLEAN );

return '<div class="cct-login-form">' . wp_login_form( $atts ) . '</div>';

}

All you have to do is to style your form on the frontend.

-1

If you're open to the use of plugins, I've used the User Registration add-on for Gravity Forms before, it worked very well:

http://www.gravityforms.com/add-ons/user-registration/

Edit: I realise this isn't a very detailed solution, but it does exactly what you need and is a good solution.

Edit: To expand on my answer further, the User Registration add-on for gravity forms allows you to map any fields in a form created using Gravity Forms, to user-specific fields. For example, you can create a form with First Name, Last Name, Email, Website, Password. Upon submission, the add-on will map those inputs to the relevant user fields.

Another great thing about it, is you can add any registered users into an approval queue. Their user accounts would only be created once approved in the backend by an admin.

If the above link breaks, just Google "User Registration add on for Gravity Forms"

5
  • 2
    Have you read notes @kaiser added to the question (bold mine): "We're looking for long answers that provide some explanation and context. Don't just give a one-line answer; explain why your answer is right, ideally with citations. Answers that don't include explanations may be removed"
    – gmazzap
    Commented Mar 18, 2014 at 14:32
  • I have, but I felt the add-on is still worth mentioning, as the OP doesn't mention a need to custom code it. Happy to move it to a comment if you feel it's necessary
    – James Kemp
    Commented Mar 18, 2014 at 14:39
  • I'm not a mod, so I can't move to comment your answer. I can only vote down, but I haven't do that because I think that your link contain useful info, however, link-only answer is not useful, even because that link can be easily change and so your answer bring to a 404. Try to report here relevant code and explain waht that code does, then your answer is fine, I guess.
    – gmazzap
    Commented Mar 18, 2014 at 14:45
  • James, I awarded the bounty to a real answer that includes code. If you want an additional bounty, please tear the plugin apart and show us exactly what it's doing. Thanks.
    – kaiser
    Commented Mar 18, 2014 at 22:17
  • Hi Kaiser, I'm not after the bounty, just wanted to share my knowledge of the plugin!
    – James Kemp
    Commented Mar 19, 2014 at 9:13