Skip to content

Instantly share code, notes, and snippets.

@tommcfarlin
Forked from csknk/ajax-reg.js
Last active August 26, 2017 14:08
Show Gist options
  • Save tommcfarlin/2bf25b85a6360808ea61 to your computer and use it in GitHub Desktop.
Save tommcfarlin/2bf25b85a6360808ea61 to your computer and use it in GitHub Desktop.
/**
* This file is enqueued by means of wp_enqueue_script() - variables are passed
* in from PHP by means of wp_localize_script()
*
*/
/* TM: We use an anonymous function to invoke the JavaScript. Also refactored for proper
* WordPress coding standards.
*/
(function( $ ) {
'use strict';
$(function() {
$('#btn-new-user').click( function(event) {
// Prevent default action
// -----------------------
event.preventDefault();
// Show 'Please wait' loader to user
// ---------------------------------
$( '.indicator' ).fadeIn( 400 );
$( '.result-message' ).hide();
// Collect data from inputs, and slot into variables
// -------------------------------------------------
var reg_nonce = $( '#cw_new_user_nonce' ).val();
var reg_user_role = $( '#cw_user_role' ).val();
var reg_email = $( '#cw_email' ).val();
var reg_firstname = $( '#cw_firstname' ).val();
var reg_lastname = $( '#cw_lastname' ).val();
var reg_cw_ajax = 'true'; // TM: I think this would work better as a boolean literal rather than as a string.
// AJAX URL: where to send data (set in the localize_script function)
// --------------------------------------------------
var ajax_url = carawebs_reg_vars.carawebs_ajax_url;
var coordinatorID = carawebs_reg_vars.carawebs_coordinator_id;
// Data to send
// -----------------
data = {
action: 'register_new_user',
cw_coord_id: coordinatorID,
cw_user_role: reg_user_role,
cw_new_user_nonce: reg_nonce,
cw_email: reg_email,
cw_firstname: reg_firstname,
cw_lastname: reg_lastname,
cw_ajax: reg_cw_ajax // let the processing function know it's Ajax - callback function can be tailored.
};
// Do AJAX request
$.post( ajax_url, data, function( respons e) {
if ( response ) {
alert( 'Response from server is: ' + response.status );
// Hide 'Please wait' indicator
$( '.indicator' ).hide();
var status = response.status;
// TM: Rewritten to use Yoda Conditions as per the WPCS
if( 'error' === response.status ) {
$( '.result-message' ).empty(); // Clear old user list/error messages
var output = response.message // decided to build message in PHP and send as a string
$( '.result-message' ).append( output );
$( '.result-message' ).show( 800 ); // Show results div
} else {
// Clear the form, so that another user can be easily registered
$( '#user-reg-form' )[0].reset();
$( '.result-message' ).empty(); // Clear old user list/error messages
// Build a success message
var studentReturn =
"<p>" + response.message + "</p>";
$( '.result-message' ).html( studentReturn );
$( '.result-message' ).show( 800 ); // Show results div
}
}
});
});
//var coordinatorID = carawebs_reg_vars.carawebs_coordinator_id;
//$('#coord-id').html('<h3>' + coordinatorID + '</h3>');
// Handle another form on the same page
$( '#btn-different-id' ).click( function(event) {
// Process the form data
});
});
})( jQuery );
<?php
/**
* Facade function to control form validation, data processing and error/success reporting.
*
* @param int $coordinator_ID User Id of originating coordinator
* @param string $user_role The user role for the newly created user
*
* The function has empty values for $coordinator_ID and $user_role by default, because:
* - If the form is submitted by PHP, the values will be passed in from the originating call and will be available
* for form processing.
* - If the form is submitted by Ajax, the function will be called by add_action('wp_ajax_..., function_name)
* and the relevant values will be submitted by the jQuery .post() function in ajax-reg.js.
*
*
*/
/**
* TM: This function is a bit long such that it should probably be refactored so that each conditional
* is calls it's own method. So rather than having so many if and if/else branches in the code it would
* still have the actual conditionals but it would call "private functions") like this:
*
* if ( conditional ) {
* _acme_function_do_work( $params );
* }
*
* This will make the code easier to trace and even read a bit more line English. A lot of this is like
* the work that you're doing with classes where to ultimate goal is to get the units of work down to the
* most atomic level possible.
*/
}
function carawebs_userform_process_facade ( $coordinator_ID = '', $user_role = '' ) {
if ( ! empty( $_POST ) ) {
if ( isset ($_POST['cw_new_user_nonce'] ) ) {
$nonce = $_POST['cw_new_user_nonce'];
}
// check to see if the submitted nonce matches the generated nonce
// TM: Always use braces, even in one line conditionals/loops/etc. It's too dangerous not to :).
if ( ! wp_verify_nonce( $nonce, 'cw_new_user' ) ) {
die ( 'Sorry, but the security check has failed and we can\'t process this form');
}
// Define variables and set to empty values, then add $_POST data if set.
// -------------------------------------------------------------------------
$firstname = $email = $lastname = $user_role = $coordinator_ID = '';
$firstname = ( isset( $_POST['cw_firstname'] ) ) ? $_POST['cw_firstname'] : '';
$lastname = ( isset( $_POST['cw_lastname'] ) ) ? $_POST['cw_lastname'] : '';
$email = ( isset( $_POST['cw_email'] ) ) ? $_POST['cw_email'] : '';
$user_role = ( isset( $_POST['cw_user_role'] ) ) ? $_POST['cw_user_role'] : '';
$coordinator_ID = ( isset( $_POST['cw_coord_id'] ) ) ? $_POST['cw_coord_id'] : ''; // if Ajax submission, set by ajax-reg.js
$cw_ajax = isset( $_POST['cw_ajax'] ) ? $_POST['cw_ajax'] : 'false'; // Set cw_ajax in the jQuery function - this allows a differential response for ajax submissions
// TM: the above 'true/false' values would probably be better as boolean literals than string literals
// Validate form data
// -------------------
$form = new CW_Reg_Validator( $firstname, $lastname, $email );
$form->is_valid();
$errors = $form->get_errors(); // Get the validation errors, if they exist.
// Form data is valid
// -----------------------------------------------------------------------
if ( 'true' == $form->is_valid() ) { // returns 'true' if no validation errors found
// Create new user
// ---------------
$new_user = new CW_Create_User( $user_role, $coordinator_ID, $form->get_values() );
// register a user, using values that have been checked for errors & sanitised
// If the user is created, $user_created will be set to true.
$user_created = $new_user->register( $user_role );
// User Creation Success
// ---------------------
if ( true == $user_created ){
/**
* Build a success message - common for PHP & Ajax
*
* $new_user->new_user_info = array(
* 'first_name' => $user->user_firstname,
* 'last_name' => $user->user_lastname,
* 'email' => $user->user_email,
* 'login' => $user->user_login,
* 'display_name' => $user->user_nicename,
* );
*
*/
// Build a success message
// -----------------------
$new_user_details = $new_user->new_user_info;
/* TM: If you wrap the string in double-quotes ("), you won't have to escape (\') single quotes
* and you can also drop the variables directly in the string and they'll be processed.
* For example:
*
* $user_created = "You've just created a new $user_role.";
*/
$user_created = 'You\'ve just created a new ' . $user_role . '. Their details are:
<ul class="user-list">
<li>Name: ' . $new_user_details['first_name'] . ' ' . $new_user_details['last_name'] . '</li>
<li>Email: ' . $new_user_details['email'] . '</li>
<li>Login Username: ' . $new_user_details['login'] . '</li>
<li>Display name: ' . $new_user_details['display_name'] . '</li>
</ul>';
// Success Return for Ajax
// ---------------
if ( 'true' == $cw_ajax ) {
$response = array(); // this will be a JSON array
$response['status'] = 'success';
$response['message'] = $user_created;
wp_send_json( $response ); // sends $response as a JSON object
} else {
// Success Return for PHP
// ---------------
echo $new_user->success_message;
}
} else {
// User Creation Failure
// ---------------------
$user_creation_failure = 'User creation failed.';
// TODO: Proper error reporting here.
// Failure return for Ajax
// ---------------
if ('true' == $cw_ajax) {
$response = array(); // this will be a JSON array
$response['status'] = 'error';
//$response['message'] = json_encode($errors, JSON_FORCE_OBJECT);
$response['message'] = $user_creation_failure;//json_encode($user_creation_failure);
} else {
// Failure return for PHP
// ---------------
echo $user_creation_failure;
}
}
}
// Form Data is invalid: Response
// -----------------------------------------------------------------------
if ( ! empty( $errors ) ) {
// Build the error message
// ------------------------
$error_message =
'<p>Sorry - we can\'t process your form because:</p>
<ul class="error-list">';
foreach ( $errors as $error ) {
$error_message .= '<li>' . $error . '</li>';
}
$error_message .= '</ul>';
if ( 'true' == $cw_ajax ) {
// Error message for Ajax
// -----------------------
$response = array(); // this will be a JSON array
$response['status'] = 'error';
$response['message'] = $error_message;
wp_send_json( $response ); // sends $response as a JSON object
} else {
// Error message for PHP
// ---------------------
echo $error_message;
}
}
}
$view = new CW_View_Form( $user_role, $coordinator_ID );
echo $view->render();
}
<?php
/**
* CW_Create_User - Create a new user from form input.
*
* All input values MUST have been sanitized previously. Form render, validation & sanitization
* is handled by separate classes, to separate the logic.
*
* This class is called by a facade type function. For Student Studio, the following classes are related:
*
* - CW_Reg_Validator, which validates & sanitizes input, /classes/class-cw-reg-validator.php
* - CW_View_Form, which builds the form, /classes/class-cw-view-form.php
*
* The facade function used for supervisor & student registration is carawebs_userform_process_facade(),
* located in /lib/forms.php
*
* WordPress Version 4.1
* @package CW_Create_User
* @author David Egan <[email protected]>
*
* @TODO return error messages if:
* - email doesn't get sent
* - wp_insert_user fails
*
*/
class CW_Create_User {
public $new_user_info = array();
public $insert_user_error;
public $firstname;
public $lastname;
public $email;
public $user_role;
public $coordinator_ID;
public $username;
public $success_message;
/**
* Construct method to set properties.
* @param string $user_role This will be either 'student', 'supervisor'.
* @param int $coordinator_ID The originating coordinator's user ID
* @param array $user_values
*/
function __construct ( $user_role, $coordinator_ID, $user_values ) {
$this->user_role = $user_role;
$this->coordinator_ID = $coordinator_ID;
$this->firstname = $user_values['first_name'];
$this->lastname = $user_values['last_name'];
$this->email = $user_values['email'];
$this->username = $user_values['email'];
}
/**
* Uses wp_insert_user to programmatically create a new user. New user will have an auto-generated password.
* The user login is set to the email address. Uses add_user_meta to set the user role.
* @param string $user_role User role: 'student' or 'supervisor'
* @return [type] [description]
*/
public function register( $user_role ) {
$password = wp_generate_password();
$username = $this->username;
$userdata = array(
'user_login' => $this->email,
'user_pass' => $password,
'user_email' => $this->email,
'first_name' => $this->firstname,
'last_name' => $this->lastname,
'user_nicename' => $this->firstname,
);
$user_id = wp_insert_user( $userdata ) ;
if ( is_wp_error( $user_id ) ) {
// If there is an error inserting a new user...
$insert_user_error = $user_id->get_error_message();
} else {
// Set the user role
// -----------------
$user = new WP_User( $user_id );
$user->set_role( $this->user_role );
// Set the Student's Coordinator
// ------------------------------
add_user_meta( $user_id, 'coordinator_id', $this->coordinator_ID ); // $coordinator_id comes from the originating page
$coordinator_info = get_userdata( $this->coordinator_ID );
$coordinator_name = $coordinator_info->user_firstname . ' ' . $coordinator_info->user_lastname;
// Build an array that can be returned, for a success message
// ----------------------------------------------------------
$this->new_user_info = array(
'first_name' => $user->user_firstname,
'last_name' => $user->user_lastname,
'email' => $user->user_email,
'login' => $user->user_login,
'display_name' => $user->user_nicename,
);
return true;
// TM: This shouldn't ever fire since a return statement is directly before it.
$this->email_new_user( $password );
}
}
/**
* Send email to the new user with instructions & password.
* Email content is generated from the content of a specified 'email' CPT -
* this will allow site admins to control the message.
*
* @TODO make message specific to 'student' and 'supervisor' user roles.
* @TODO set up success and error messages.
* @param string $password Auto generated password.
* @return [type] [description]
*
*/
public function email_new_user( $password ){
// Email the user
// --------------
// Allow html in email
add_filter( 'wp_mail_content_type', function($content_type){
return 'text/html';
});
/**
* TODO allow Admin to select the attachment in back end of site
*/
//$attachments = array( WP_CONTENT_DIR . '/uploads/2015/01/RDBMStoMongoDBMigration.pdf' );
$headers = 'From: [email protected]' . "\r\n";
$welcome = 'Welcome, ' . $this->firstname . '!';
/**
* TODO set this up so that the right 'email' CPT is referenced.
* It might be better to set up to reference by post_title, and instruct client to
* give appropriate name to the relevant email CPT.
*/
$post_id = 62; // Refers to the Student Intro email custom post - rough & ready solution.
$post_object = get_post( $post_id );
$mailmessage = $post_object->post_content; // Build a mail message from the content of a post.
$mailmessage .= '<hr><p>Your password is: ' . $password . '</p>';
wp_mail( $this->email, $welcome, $mailmessage, $headers/*, $attachments*/ );
}
}
<?php
/**
* Class to validate & sanitise form input.
*
* WordPress Version 4.1
* @package CW_Reg_Validator
* @author David Egan <[email protected]>
*
*
*/
class CW_Reg_Validator {
public $form_errors = array();
public $user_values = array();
/**
* Construct method
* @param string $firstname Un-sanitized form input
* @param string $lastname Un-sanitized form input
* @param string $email Un-sanitized form input
*
*/
function __construct ( $firstname, $lastname, $email ) {
$this->firstname = $firstname;
$this->lastname = $lastname;
$this->email = $email;
}
/**
* Check validity of class proprties
* @return boolean Returns true if there are no errors, otherwise pushes errors into $form_errors array.
*
*/
public function is_valid() {
// allow letters (both cases), hyphen, space and (\').
$preg_str_check="#[^][(\\\\') A-Za-z-]#";
if ( empty( $this->email ) ) {
array_push( $this->form_errors, 'You must enter an email address.' );
}
if ( empty( $this->firstname ) ) {
array_push( $this->form_errors, 'You must enter a first name.' );
}
if( preg_match( $preg_str_check, $this->firstname ) ) {
array_push( $this->form_errors, 'The first name you entered doesn\'t look right - please try again with alphabet characters only.' );
}
if ( empty( $this->lastname ) ) {
array_push( $this->form_errors, 'You must enter a last name.' );
}
if( preg_match( $preg_str_check, $this->lastname ) ) {
array_push( $this->form_errors, 'The last name you entered doesn\'t look right - please try again with alphabet characters only.' );
}
if ( ( $this->email) && ! is_email( $this->email ) ) {
array_push( $this->form_errors, 'The email address you submitted doesn\'t look right - please try again, with the format [email protected]');
}
if ( email_exists( $this->email ) ) {
array_push( $this->form_errors, 'This email is already in use.');
}
if ( empty( $this->form_errors ) ) { // There are no errors, so return the string 'true'
// TM: Inconsistently returning true and 'true' in your code. Pick one variable type and be consistent with it throughout.
return 'true';
}
}
/**
* Return form submission/validation errors
* @return array Form errors.
*
*/
public function get_errors() {
return $this->form_errors;
}
/**
* Sanitize input data.
* @param string $data Form input data: firstname, lastname, email.
* @return string Cleaned up data - sanitised.
*/
public function sanitise_inputs( $data ) {
$data = trim( $data );
$data = stripslashes( $data );
$data = strip_tags( $data );
$data = htmlspecialchars( $data );
/* TM: May simplify this to the following:
* return trim( stripslashes ( strip_tags( htmlspecialchars( $data ) ) ) );
*/
return $data;
}
/**
* Build an array of data relating to the new user & return it.
* @return array contains user firstname, lastname, email address.
*
*/
public function get_values() {
$this->firstname = $this->sanitise_inputs($this->firstname);
$this->lastname = $this->sanitise_inputs($this->lastname);
$this->email = sanitize_email($this->email);
$this->user_values = array(
'first_name' => $this->firstname,
'last_name' => $this->lastname,
'email' => $this->email
);
return $this->user_values;
}
}
<?php
/**
* Create a HTML form for front-end user registration.
* WordPress Version 4.1
* @package CW_View_Form
* @author David Egan <[email protected]>
*
*
*/
class CW_View_Form {
public $proto_session_id;
/**
* Create a user registration form.
* @param string $user_role User role for this instance: 'student' or 'supervisor'
* @param int $coordinator_ID User ID of originating coordinator
*
*
*/
/**
* Set up properties
* @param string $user_role The user role for the new user
* @param int $coordinator_ID The user ID of the originating coordinator
*/
function __construct ( $user_role, $coordinator_ID ) {
$this->user_role = $user_role;
$this->coordinator_ID = $coordinator_ID;
//$this->sessionnonce = $sessionnonce;
$this->proto_session_id = md5( 'whoohoo'. microtime() ); // do we need an original nonce??
// TM: Ideally, I also use whatever nonce WordPress generates. Easiest for code maintainability.
}
/**
* Build a front-end form for user creation
* @return string Form html
*
*/
public function render() {
$user_role = ucfirst($this->user_role);
$_SESSION['cw_session_id'] = $this->proto_session_id;
// TM: Consider moving this to a 'partial' file that is basically an HTML file with nested PHP and then run an include_once
$form = '
<div class="cw-registration-form">
<form id="user-reg-form" class="registration-form" role="form" action="' . $_SERVER['REQUEST_URI'] . '" method="post">
<input id="cw_user_role" type="hidden" name="cw_user_role" value="' . $this->user_role . '" />
<input type="hidden" size="45" name="sessionnonce" value="' . $this->proto_session_id . '" />
<div class="form-group">
<label for="cw_firstname" class="sr-only">' . $user_role . '\'s First Name</label>
<input autocomplete="off" type="text" name="cw_firstname" id="cw_firstname" value="" placeholder="' . $user_role . '\'s First Name" class="form-control" />
</div>
<div class="form-group">
<label for="cw_lastname" class="sr-only">' . $user_role . '\'s Last Name</label>
<input autocomplete="off" type="text" name="cw_lastname" id="cw_lastname" value="" placeholder="' . $user_role . '\'s Last Name" class="form-control" />
</div>
<div class="form-group">
<label for="cw_email" class="sr-only">' . $user_role . '\'s Email</label>
<input autocomplete="off" type="email" name="cw_email" id="cw_email" value="" placeholder="' . $user_role . '\'s Email" class="form-control" />
</div>' .
wp_nonce_field('cw_new_user','cw_new_user_nonce', true, true ) .
'<input type="submit" name="cw_register" class="btn btn-primary" id="btn-new-user" value="Register" />
</form>
<div class="indicator">Please wait...</div>
<div class="result-message well topspace"></div>
</div>';
return $form;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment