Skip to content

Instantly share code, notes, and snippets.

@trslater
Last active May 12, 2020 13:47
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save trslater/3e69eee9ef3f6962233b to your computer and use it in GitHub Desktop.
Save trslater/3e69eee9ef3f6962233b to your computer and use it in GitHub Desktop.
Custom Registration Plugin

Changelog

1.1

  • Fixed: errors if $fields array is empty
  • Fixed: extra 'WP_Errors' string artifact from previous use of 'is_a' function

Original Changes

  • Took out styling - this is unnecessary to the tutorial and poor separation. Let the theme handle styling
  • Removed globals - in general, using globals is a bad practice, because it forces reliance and causes conflicts
  • Decoupled everything - every function now stands on its own, which makes the code more maintainable, extensible, debuggable, modular, etc.
  • Added cr_ prefix to functions (as suggested by Kitt Ross) - this will help avoid conflict with other plugins
  • Moved error display to form display function - this is generally whre you want it, it also separates validation and display
  • Fields now always use the same names as wp_insert_user uses - this saves a step
  • Errors are only checked once
  • Removed registration function - was unnecessary for something that basically just wraps another function
  • Removed echoing of password back to form - passwords should not be brought back as other data is
  • Field data is now cleared on success
<?php
/*
Plugin Name: Custom Registration
Description: Updates user rating based on number of posts.
Version: 1.1
Author: Tristan Slater w/ Agbonghama Collins
Author URI: http://kanso.ca
*/
/////////////////
// PLUGIN CORE //
/////////////////
function cr(&$fields, &$errors) {
// Check args and replace if necessary
if (!is_array($fields)) $fields = array();
if (!is_wp_error($errors)) $errors = new WP_Error;
// Check for form submit
if (isset($_POST['submit'])) {
// Get fields from submitted form
$fields = cr_get_fields();
// Validate fields and produce errors
if (cr_validate($fields, $errors)) {
// If successful, register user
wp_insert_user($fields);
// And display a message
echo 'Registration complete. Goto <a href="' . get_site_url() . '/wp-login.php">login page</a>.';
// Clear field data
$fields = array();
}
}
// Santitize fields
cr_sanitize($fields);
// Generate form
cr_display_form($fields, $errors);
}
function cr_sanitize(&$fields) {
$fields['user_login'] = isset($fields['user_login']) ? sanitize_user($fields['user_login']) : '';
$fields['user_pass'] = isset($fields['user_pass']) ? esc_attr($fields['user_pass']) : '';
$fields['user_email'] = isset($fields['user_email']) ? sanitize_email($fields['user_email']) : '';
$fields['user_url'] = isset($fields['user_url']) ? esc_url($fields['user_url']) : '';
$fields['first_name'] = isset($fields['first_name']) ? sanitize_text_field($fields['first_name']) : '';
$fields['last_name'] = isset($fields['last_name']) ? sanitize_text_field($fields['last_name']) : '';
$fields['nickname'] = isset($fields['nickname']) ? sanitize_text_field($fields['nickname']) : '';
$fields['description'] = isset($fields['description']) ? esc_textarea($fields['description']) : '';
}
function cr_display_form($fields = array(), $errors = null) {
// Check for wp error obj and see if it has any errors
if (is_wp_error($errors) && count($errors->get_error_messages()) > 0) {
// Display errors
?><ul><?php
foreach ($errors->get_error_messages() as $key => $val) {
?><li>
<?php echo $val; ?>
</li><?php
}
?></ul><?php
}
// Disaply form
?><form action="<?php $_SERVER['REQUEST_URI'] ?>" method="post">
<div>
<label for="user_login">Username <strong>*</strong></label>
<input type="text" name="user_login" value="<?php echo (isset($fields['user_login']) ? $fields['user_login'] : '') ?>">
</div>
<div>
<label for="user_pass">Password <strong>*</strong></label>
<input type="password" name="user_pass">
</div>
<div>
<label for="email">Email <strong>*</strong></label>
<input type="text" name="user_email" value="<?php echo (isset($fields['user_email']) ? $fields['user_email'] : '') ?>">
</div>
<div>
<label for="website">Website</label>
<input type="text" name="user_url" value="<?php echo (isset($fields['user_url']) ? $fields['user_url'] : '') ?>">
</div>
<div>
<label for="firstname">First Name</label>
<input type="text" name="first_name" value="<?php echo (isset($fields['first_name']) ? $fields['first_name'] : '') ?>">
</div>
<div>
<label for="website">Last Name</label>
<input type="text" name="last_name" value="<?php echo (isset($fields['last_name']) ? $fields['last_name'] : '') ?>">
</div>
<div>
<label for="nickname">Nickname</label>
<input type="text" name="nickname" value="<?php echo (isset($fields['nickname']) ? $fields['nickname'] : '') ?>">
</div>
<div>
<label for="bio">About / Bio</label>
<textarea name="description"><?php echo (isset($fields['description']) ? $fields['description'] : '') ?></textarea>
</div>
<input type="submit" name="submit" value="Register">
</form><?php
}
function cr_get_fields() {
return array(
'user_login' => isset($_POST['user_login']) ? $_POST['user_login'] : '',
'user_pass' => isset($_POST['user_pass']) ? $_POST['user_pass'] : '',
'user_email' => isset($_POST['user_email']) ? $_POST['user_email'] : '',
'user_url' => isset($_POST['user_url']) ? $_POST['user_url'] : '',
'first_name' => isset($_POST['first_name']) ? $_POST['first_name'] : '',
'last_name' => isset($_POST['last_name']) ? $_POST['last_name'] : '',
'nickname' => isset($_POST['nickname']) ? $_POST['nickname'] : '',
'description' => isset($_POST['description']) ? $_POST['description'] : ''
);
}
function cr_validate(&$fields, &$errors) {
// Make sure there is a proper wp error obj
// If not, make one
if (!is_wp_error($errors)) $errors = new WP_Error;
// Validate form data
if (empty($fields['user_login']) || empty($fields['user_pass']) || empty($fields['user_email'])) {
$errors->add('field', 'Required form field is missing');
}
if (strlen($fields['user_login']) < 4) {
$errors->add('username_length', 'Username too short. At least 4 characters is required');
}
if (username_exists($fields['user_login']))
$errors->add('user_name', 'Sorry, that username already exists!');
if (!validate_username($fields['user_login'])) {
$errors->add('username_invalid', 'Sorry, the username you entered is not valid');
}
if (strlen($fields['user_pass']) < 5) {
$errors->add('user_pass', 'Password length must be greater than 5');
}
if (!is_email($fields['user_email'])) {
$errors->add('email_invalid', 'Email is not valid');
}
if (email_exists($fields['user_email'])) {
$errors->add('email', 'Email Already in use');
}
if (!empty($fields['user_url'])) {
if (!filter_var($fields['user_url'], FILTER_VALIDATE_URL)) {
$errors->add('user_url', 'Website is not a valid URL');
}
}
// If errors were produced, fail
if (count($errors->get_error_messages()) > 0) {
return false;
}
// Else, success!
return true;
}
///////////////
// SHORTCODE //
///////////////
// The callback function for the [cr] shortcode
function cr_cb() {
$fields = array();
$errors = new WP_Error();
// Buffer output
ob_start();
// Custom registration, go!
cr($fields, $errors);
// Return buffer
return ob_get_clean();
}
add_shortcode('cr', 'cr_cb');
@KittRoss
Copy link

Hi Tristan,
Good work in setting up the gisthub for this edit.
We will still get errors from the array $fields. As it is passed as blank array when the array is sanitised, references to $fields['any_thing_in_here'] will register as an error notice.
It will have to be initialised.
Also, in the above code input sanitation is happening before the empty form and not to the POST form.
Oh and one more thing line 65. if (is_wp_error($errors, 'WP_Error') will always be true as it is of type WP_Error, this conditional could be just if (count($errors->get_error_messages()) > 0).

Try this, it uses array_fill_keys() to initialise the array.
And note the alteration of the forname and last name fields. Mr Collins muddied the waters a little here. I see from your code you chose firstname instead of fname. Either or.

function cr_registration_construct() {
    if (isset($_POST['submit'])) {

    $reg_array = array(
        'username' => $_POST['username'],
        'password' => $_POST['password'],
        'email' => $_POST['email'],
        'website' => $_POST['website'],
        'fname' => $_POST['fname'],
        'lname' => $_POST['lname'],
        'nickname' => $_POST['nickname'],
        'bio' => $_POST['bio']
    );

            //return true or false from validation
    if( cr_registration_validation( $reg_array ) ){

        // sanitize user form input
        $reg_array = array(
            'username'  =>  sanitize_user($_POST['username']),
            'password'  =>  esc_attr($_POST['password']),
            'email'     =>  sanitize_email($_POST['email']),
            'website'   =>  esc_url($_POST['website']),
            'fname' => sanitize_text_field($_POST['fname']),
            'lname' =>  sanitize_text_field($_POST['lname']),
            'nickname'  =>  sanitize_text_field($_POST['nickname']),
            'bio'       =>  esc_textarea($_POST['bio']),
        );

        // call @function complete_registration to create the user
        // only when no WP_error is found
        cr_registration_complete( $reg_array );

    }

} else {

    $keys = array('username','password','email','website','fname','lname','nickname','bio');
    $reg_array = array_fill_keys($keys, '');

}

cr_registration_form( $reg_array );

}

@trslater
Copy link
Author

trslater commented Feb 3, 2015

Hi Kitt,

Thanks for the input! The issue should be fixed now. Let me know if you catch anything else.

Also, some clarification on some of the points to brought up:

  • I went for just checking each field instead of using array_fill_keys
  • I'm not sure what you mean by "code input sanitation is happening before the empty form and not to the POST form", the sanitization always happens. The $fields array is populated from the $_POST array first using cr_getfields. Sanitization isn't supposed to happen until right before you use the data to avoid contamination along the way. The unsanitized data is sent to wp_insert_user, because it does sanitization itself
  • The is_wp_error is necessary, because otherwise PHP won't find get_error_messages and return an error, but the 'WP_Error' string is unnecessary, so I took it out. That was a holdover from when I initially used is_a.
  • I switched all of the field names (e.g. fname -> first_name) and all var names to match the keys used by wp_insert_user to save the step of converting the values. This way, I can just provide $fields as the argument for wp_insert_user.

@w3guy
Copy link

w3guy commented Feb 3, 2015

Thank you Tristan for correcting my errors.

Job well done.

@nexjsystems
Copy link

Evening Tristan,

Love the plugin - I just have one question.

I am looking to add a field to this form called "Access Code" with the access code default/static of "ABCDE". I am wondering how I implement this field with a validation check to make sure the Access Code the user entered is correct upon submission.

Can you provide me with an example of how to implement this small feature?

Cheers,
Andrew

@zachhall
Copy link

Hi Tristan,

Thank you so much for this plugin! Really working well. I was wondering how you would add a page redirect on a successful registration instead of just echoing out a message. I've tried adding a redirect and filter to registration_redirect, but it isn't working. Any help would be really appreciated!

@CreativeWolf
Copy link

Heya!

This is awesome work, thank you!

It'd be great if this can be extended to support a file upload field as well.

I'm trying to figure out a way to let a user upload a file during registration.

Appreciate any help!

Thanks

@CreativeWolf
Copy link

@CreativeWolf
Copy link

I did try to tinker a little bit, but while the user is getting registered, the file field is not getting created or file isn't stored. Here's my tinkered code.

<?php

/*
  Plugin Name: Custom Registration
  Description: Updates user rating based on number of posts.
  Version: 1.1
  Author: Tristan Slater w/ Agbonghama Collins
  Author URI: http://kanso.ca
 */



/////////////////
// PLUGIN CORE //
/////////////////

function cr(&$fields, &$errors) {

  // Check args and replace if necessary
  if (!is_array($fields))     $fields = array();
  if (!is_wp_error($errors))  $errors = new WP_Error;

  // Check for form submit
  if (isset($_POST['submit'])) {

    // Get fields from submitted form
    $fields = cr_get_fields();

    // Validate fields and produce errors
    if (cr_validate($fields, $errors)) {

      // If successful, register user
      wp_insert_user($fields);

      // And display a message
      echo 'Registration complete. Goto <a href="' . get_site_url() . '/wp-login.php">login page</a>.';

      // Clear field data
      $fields = array(); 
    }
  }

  // Santitize fields
  cr_sanitize($fields);

  // Generate form
  cr_display_form($fields, $errors);
}

function cr_sanitize(&$fields) {
  $fields['user_login']   =  isset($fields['user_login'])  ? sanitize_user($fields['user_login']) : '';
  $fields['user_pass']    =  isset($fields['user_pass'])   ? esc_attr($fields['user_pass']) : '';
  $fields['user_email']   =  isset($fields['user_email'])  ? sanitize_email($fields['user_email']) : '';
  $fields['user_url']     =  isset($fields['user_url'])    ? esc_url($fields['user_url']) : '';
  $fields['first_name']   =  isset($fields['first_name'])  ? sanitize_text_field($fields['first_name']) : '';
  $fields['last_name']    =  isset($fields['last_name'])   ? sanitize_text_field($fields['last_name']) : '';
  $fields['nickname']     =  isset($fields['nickname'])    ? sanitize_text_field($fields['nickname']) : '';
  $fields['description']  =  isset($fields['description']) ? esc_textarea($fields['description']) : '';
  $fields['resume']  =  isset($fields['resume']) ? sanitize_file_name($fields['resume']) : '';
}

function cr_display_form($fields = array(), $errors = null) {

  // Check for wp error obj and see if it has any errors  
  if (is_wp_error($errors) && count($errors->get_error_messages()) > 0) {

    // Display errors
    ?><ul><?php
    foreach ($errors->get_error_messages() as $key => $val) {
      ?><li>
        <?php echo $val; ?>
      </li><?php
    }
    ?></ul><?php
  }

  // Disaply form

  ?>

<form action="<?php $_SERVER['REQUEST_URI'] ?>" method="post">
    <div>
      <label for="user_login">Username <strong>*</strong></label>
      <input type="text" name="user_login" value="<?php echo (isset($fields['user_login']) ? $fields['user_login'] : '') ?>">
    </div>

    <div>
      <label for="user_pass">Password <strong>*</strong></label>
      <input type="password" name="user_pass">
    </div>

    <div>
      <label for="email">Email <strong>*</strong></label>
      <input type="text" name="user_email" value="<?php echo (isset($fields['user_email']) ? $fields['user_email'] : '') ?>">
    </div>

    <div>
      <label for="website">Website</label>
      <input type="text" name="user_url" value="<?php echo (isset($fields['user_url']) ? $fields['user_url'] : '') ?>">
    </div>

    <div>
      <label for="firstname">First Name</label>
      <input type="text" name="first_name" value="<?php echo (isset($fields['first_name']) ? $fields['first_name'] : '') ?>">
    </div>

    <div>
      <label for="website">Last Name</label>
      <input type="text" name="last_name" value="<?php echo (isset($fields['last_name']) ? $fields['last_name'] : '') ?>">
    </div>

    <div>
      <label for="nickname">Nickname</label>
      <input type="text" name="nickname" value="<?php echo (isset($fields['nickname']) ? $fields['nickname'] : '') ?>">
    </div>

    <div>
      <label for="bio">About / Bio</label>
      <textarea name="description"><?php echo (isset($fields['description']) ? $fields['description'] : '') ?></textarea>
    </div>

    <div>
      <label for="resume">Resume</label>
      <input type="file" name="resume" value="<?php echo (isset($fields['resume']) ? $fields['resume'] : '') ?>">
    </div>

    <input type="submit" name="submit" value="Register">
    </form>

    <?php
}

function cr_get_fields() {
  return array(
    'user_login'   =>  isset($_POST['user_login'])   ?  $_POST['user_login']   :  '',
    'user_pass'    =>  isset($_POST['user_pass'])    ?  $_POST['user_pass']    :  '',
    'user_email'   =>  isset($_POST['user_email'])   ?  $_POST['user_email']        :  '',
    'user_url'     =>  isset($_POST['user_url'])     ?  $_POST['user_url']     :  '',
    'first_name'   =>  isset($_POST['first_name'])   ?  $_POST['first_name']        :  '',
    'last_name'    =>  isset($_POST['last_name'])    ?  $_POST['last_name']        :  '',
    'nickname'     =>  isset($_POST['nickname'])     ?  $_POST['nickname']     :  '',
    'description'  =>  isset($_POST['description'])  ?  $_POST['description']  :  '',
    'resume'  =>  isset($_POST['resume'])  ?  $_POST['resume']  :  '',
  );
}

function cr_validate(&$fields, &$errors) {

  // Make sure there is a proper wp error obj
  // If not, make one
  if (!is_wp_error($errors))  $errors = new WP_Error;

  // Validate form data

  if (empty($fields['user_login']) || empty($fields['user_pass']) || empty($fields['user_email'])) {
    $errors->add('field', 'Required form field is missing');
  }

  if (strlen($fields['user_login']) < 4) {
    $errors->add('username_length', 'Username too short. At least 4 characters is required');
  }

  if (username_exists($fields['user_login']))
    $errors->add('user_name', 'Sorry, that username already exists!');

  if (!validate_username($fields['user_login'])) {
    $errors->add('username_invalid', 'Sorry, the username you entered is not valid');
  }

  if (strlen($fields['user_pass']) < 5) {
    $errors->add('user_pass', 'Password length must be greater than 5');
  }

  if (!is_email($fields['user_email'])) {
    $errors->add('email_invalid', 'Email is not valid');
  }

  if (email_exists($fields['user_email'])) {
    $errors->add('email', 'Email Already in use');
  }

  if (!empty($fields['user_url'])) {
    if (!filter_var($fields['user_url'], FILTER_VALIDATE_URL)) {
      $errors->add('user_url', 'Website is not a valid URL');
    }
  }

if ($_FILES['resume']['error'] === UPLOAD_ERR_OK ) {
      $files = $_FILES['resume'];
      foreach ($files['resume'] as $key => $value) {
      if ($files['resume'][$key]) {
      $file = array(
      'name'     => $files['name'][$key],
      'type'     => $files['type'][$key],
      'tmp_name' => $files['tmp_name'][$key],
      'error'    => $files['error'][$key],
      'size'     => $files['size'][$key]
      );

      $uploaded_file_type = $files['type'][$key];
      $allowed_file_types = array('image/jpg', 'image/jpeg', 'application/pdf');
      if(in_array($uploaded_file_type, $allowed_file_types)) {
                //if in array run the attachment function
                        $_FILES = array("upload" => $file);
                        foreach ($_FILES as $file => $array) {
                        $newupload = insert_attachment($file,$post_id);
                        }

        } else { // wrong file type
            $errors->add('Invalid File Type.');
            }   
        }
     }
     }


  // If errors were produced, fail
  if (count($errors->get_error_messages()) > 0) {
    return false;
  }

  // Else, success!
  return true;
}



///////////////
// SHORTCODE //
///////////////

// The callback function for the [cr] shortcode
function cr_cb() {
  $fields = array();
  $errors = new WP_Error();

  // Buffer output
  ob_start();

  // Custom registration, go!
  cr($fields, $errors);

  // Return buffer
  return ob_get_clean();
}
add_shortcode('cr', 'cr_cb');

@kerekesz
Copy link

Hi Tristan,

This is an awesome plugin and working well!
It'd be great if this can be extended to autologin after registration.
Appreciate any help!

Best regards,
Zoli

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment