Creating a custom registration form with email verification

This tutorial will teach you how to use a custom module to create a custom usr registration form with email verification in your Quanta application.

For this tutorial You will need:

  1. A valid email address
  2. a custom module

Step 1: (optional) create empty "onboard" and "boarded" nodes

This can be any node in the system. 

I usually call "onboard" the node where I will place the custom registration form.

Then you can include this "onboard" node anywhere as a block, or use it standalone. 

Another node, called "boarded", will be used as a "Thank you" page after the registration. This is also facultative, do it or not at your wish.


Step 2: create the custom registration form

To build this form, we take inspiration from the default user registration form, to which we apply some small changes:

1) we remove the username field (it will default as the user email)

2) we add a "description" text field.


[INPUT|title=Nome|name=first_name|value={USER_ATTRIBUTE|name=first_name}:user_form]
[INPUT|title=Cognome|name=last_name|value={USER_ATTRIBUTE|name=last_name}:user_form]
[INPUT|title=Email|name=email|value={USER_ATTRIBUTE|name=email}:user_form]
[INPUT|title=Describe yourself|name=password|type=text:user_form]
[INPUT|title=Choose a Password|type=password|name=password:user_form]
[INPUT|title=Repeat your Password|type=password|name=password_rp:user_form]
[INPUT|type=submit|name=submit|value=Complete subscription:user_form]
[FORM:user_form]


As you see this is still a very basic form, but you can add as many other fields as you wish.

We are going to use the standard user form (user_form) name, in order to make use of all default registration hooks included in Quanta.


Step 3: create a pre_validate hook

The first thing we want to do, is implementing a pre_validate hook, so we can disable the alphanumeric check.

function custom_reg_user_pre_validate($env, &$vars) {
  $vars['skip_validate']['username_alphanumeric'] = TRUE;
}

This is needed as we won't set any username for the new user, but use its email address as the username.


Step 4: create a form validation hook

We now need to make sure that the submitted data is valid, and attempt to register the user.

function custom_reg_form_user_form_form_validate($env, &$vars) {
  $form_values = $vars['form']->getFormValues();
  $user_values = $form_values;
  $user_values['name'] = array($env->getCandidatePath(normalizePath(array_pop($form_values['first_name']) . '-' . array_pop($form_values['last_name']))));

  $user = UserFactory::requestAction($env, USER_ACTION_REGISTER, $user_values);

  if (!empty($user->getData('validation_errors'))) {
    $vars['form']->setData('validation_errors', $user->getData('validation_errors'));
  }
  else {
    $vars['form']->setRedirect('/boarded');
  }
}


Step 5: create an user registration hook

To integrate our email registration feature, we have to hook into the user_register function.

function custom_reg_user_register($env, &$vars) {
  /** @var User $user */
  $user = &$vars['user'];
  $validation_code = md5($vars['user']->getName() . $vars['user']->getData('artemarziale'));
  $validation_link = $env->getProtocol() . '://' . $_SERVER['HTTP_HOST'] . '/validate/' . $user->getName() . '/' . $validation_code;

  // By default, set the user to not be validated.
  $user->setAttributeJSON('validated', 0);

  // Register the validation code in the user's json.
  $user->setAttributeJSON('validation_code', $validation_code);

  // Wrap the verification email.
  $mail = new Mail($env, 'email');
  $mail->setData('to', $user->getEmail());
  $mail->setData('subject', 'Please validate your registration');
  $mail->setBody('Dear ' . $user->getFirstName() . ', we received your subscription request to our site. In order to validate your registration, please click here:' . $validation_link . '. We hope to see you soon onboard!');
  $mail->setData('to_name', $user->getFirstName() . ' ' . $user->getLastName());
  $mail->setData('from', 'THE FROM ADDRESS');
  $mail->setData('from_name', 'THE FROM NAME');
  $mail->setData('host', 'YOUR SMTP SERVER');
  $mail->setData('port', 587);
  $mail->setData('username', 'YOUR USERNAME...');
  $mail->setData('password', 'YOUR PASSWORD...');

  // Send the verification mail.
  $mail->send();
}


Step 6: Using the verification link

When the user will receive the email, and click the verification link, it will go to a page (/validate/...) where the registration code will be validated.

If the code will be the same that we have saved in the user profile, we can consider the email verification complete. 

To implement the new page, we use the hook_boot() function:

function custom_reg_boot($env, $vars) {
  if ($env->request[1] == 'validate') {
    $user = UserFactory::load($env, $env->request[2]);
    // If the code is the same as stored in the user's json, validate the user.
    if ($user->getAttributeJSON('validation_code') == $env->request[3]) {
      // Set the validated variable to 1.
      $user->setAttributeJSON('validated', 1);
      // Save the user.
      $user->save();
      // Login the user immediately.
      $user->logIn($user->getPassword(), NULL, TRUE);
      $user->rebuildSession();
      // Redirect somewhere (i.e. to a "validated" page where you thank the user for validating).
      redirect('/validated');
    }
    else {
      // If the code is invalid, block everything.
      die(t('The code you are trying to use is invalid. Please repeat the procedure'));
    }
  }
}


Step 7: Done! The user is now validated

If you followed all the instructions up to this point, you have a very nice, completely custom user registration form with email validation. 

Now, feel free to extend it as you wish!