Login et création de compte unifiés

Qui n'a jamais eu une telle demande sur un projet : avoir le formulaire de login et de création de compte sur la même page ?
Pour un développeur Drupal, cette demande n'est pas complexe ni longue à implémenter. Une page qui englobe les deux formulaires en question, et le tour est joué.

Mais votre client vous demande à présent : Est-ce que l'on peut avoir cette page sous forme de popin ?

Votre premier réflexe devrait alors être : "Bien sûr, grâce à la modal API de Ctools, c'est très facile."
Malheureusement, les choses se compliquent bien rapidement.
En effet, si la modal API permet bien d'afficher un formulaire facilement, ou alors même une page de votre site, il devient très complexe d'y inclure deux formulaires bien distincts, ayant chacun leurs propres méchanismes de validation et de soumission.

C'est là que nous pouvons tirer parti du module "ctools_automodal". Ce module permet de générer une popin via la Modal API de Ctools à partir de n'importe quelle entrée de menu (hook_menu).
A partir de là, il nous faudra néanmoins prendre en charge nous même le processus d'intégration des deux formulaires.

Allons directement à l'exemple concret. Commençons par installer le module "ctools_automodal" et ses dépendances sur notre site, puis nous allons créer un petit module custom.

Dans ce dernier, commençons par implémenter notre hook_menu().

/**
 * Implements hook_menu()
 */
function custom_login_menu() {
  $items = array();
  $items['custom-login'] = array(
    'title' => 'Signup',
    'page callback' => 'custom_login_unified_login_page',
    'access callback' => 'user_is_anonymous',
    'type' => MENU_NORMAL_ITEM,
    'modal' => TRUE,
    'menu_name' => 'user-menu',
  );
  return $items;
}

Rien de bien spécial ici à part la clé "modal => TRUE" qui est fournie par le module "ctools_automodal". Il permet de créer une entrée de menu qui, si elle est appellée à partir d'un lien, s'affichera automatiquement dans une modal gérée par CTools.

Il faut ensuite implémenter le callback de la page que l'on vient de définir.
Ce callback sera en charge de plusieurs choses :
- Savoir si l'utilisateur est déjà loggué ou non, et si tel est le cas, ne pas ouvrir la modal et rediriger l'utilisateur vers la page d'accueil du site
- Si l'utilisateur n'est pas loggué :

  • Récuperer et construire les deux formulaires d'enregistrement de compte et de login
  • Si un formulaire a été soumis, laisser Drupal le traiter et fermer automatiquement la modal
  • Contruire deux variables contenant le rendu des formulaires
  • Passer l'ensemble à une fonction de thème qui sera en charge de l'affichage.

L'ensemble donne donc ceci (attention, ça va piquer) : 

/**
 * Menu callback for user/login creates a unified login/registration form.
 * Inspired by logintobbogan's logintoboggan_unified_login_page().
 * @see logintoboggan_unified_login_page()
 */
function custom_login_unified_login_page() {
  global $user;
  $ajax = FALSE;

  if (substr(request_path(), -5) == '/ajax') {
     $ajax = 'ajax';
  }

  // User is logged in already.
  if ($user->uid) {
    if ($ajax) {
      $commands[] = ctools_modal_command_dismiss();
      $commands[] = ctools_ajax_command_redirect('');
      print ajax_render($commands);
      exit();
    }
    else {
      drupal_goto('');
    }
  }

  // User is not logged in.
  if ($ajax) {
    ctools_include('modal');
    ctools_include('ajax');
    ctools_add_js('ajax-responder');

    $form_state_1 = $form_state_2 = array(
      'ajax' => $ajax,
      'build_info' => array('args' => array()),
      're_render' => FALSE,
      'no_redirect' => TRUE,
    );

    $login_form = drupal_build_form('user_login', $form_state_1);

    // Build the second form only when the first form isnt executed. Otherwise
    // we will be redirected, because both form builders contains drupal_goto()
    // for already logged-in users.
    if (empty($form_state_1['executed'])) {
      $register_form = drupal_build_form('user_register_form', $form_state_2);
    }

    // Handle submitted form.
    if (!empty($form_state_1['executed']) || !empty($form_state_2['executed'])) {
      $commands[] = ctools_modal_command_dismiss();
      $commands[] = ctools_ajax_command_reload();
      print ajax_render($commands);
      exit();
    }

    // If there are messages for the form, render them.
    if ($messages = theme('status_messages')) {
      $messages = '<div class="messages">' . $messages . '</div>';
    }
    $variables = array(
      'login_form' => drupal_render($login_form),
      'register_form' => drupal_render($register_form),
    );
    return $messages . theme('custom_login_unified_login_page', $variables);
  }
  else {
    $login_form = drupal_get_form('user_login');
    $register_form = drupal_get_form('user_register_form');
    $rendered_login_form = drupal_render($login_form);
    $rendered_register_form = drupal_render($register_form);
    $variables = array(
      'login_form' => $rendered_login_form,
      'register_form' => $rendered_register_form,
    );
    return theme('custom_login_unified_login_page', $variables);
  }
}

Il ne reste plus alors qu'à gérer la fonction de thème, qui sera très simple dans notre exemple, c'est pourquoi je ne rentrerai pas dans les détails ici.

/**
 * Implements hook_theme().
 */
function custom_login_theme($existing, $type, $theme, $path) {
  return array(
    'custom_login_unified_login_page' => array(
      'variables' => array(
        'login_form' => NULL,
        'register_form' => NULL,
      ),
    ),
  );
}

/**
 * Theme function for unified login page.
 * @see theme_lt_unified_login_page()
 */
function theme_custom_login_unified_login_page($variables) {
  $output = '';

  // Add the login and registration forms in.
  $output .= '<div id="register-form">' . $variables['register_form'] . '</div>';
  $output .= '<div id="login-form">' . $variables['login_form'] . '</div>';
  return $output;
}

Le module d'exemple fonctionnel contenant l'ensemble de ce code est disponible sur notre github : https://github.com/Happyculture/Custom-unified-login

Si vous l'activez, vous verrez alors un lien "Signup" en haut à droite de votre page d'accueil, et un simple clic ouvrira alors notre modal.

 

login_unifier

 

Une fois de plus, merci à la communauté pour ses modules qui nous permettent de résoudre aussi rapidement ces problematiques. 

Cet article s'inspire fortement de l'issue correspondante sur drupal.org.

Votre commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.
Votre adresse servira à afficher un Gravatar et à vous notifier des réponses. Votre commentaire sera anonymisé si ce billet est dépublié pendant plus de 3 mois.

À propos de Nicolas

Co-fondateur - Expert technique

Je découvre Drupal en 2007 alors que j'occupe le poste d’Adjoint de Production du département Multimédia au sein du Service d'Information du Gouvernement. Je fus chargé de la réalisation de la nouvelle version, encore en ligne à ce jour, du site du Premier ministre.