You are here

login_destination.module in Login Destination 6.2

Control where users are directed to, once they login

File

login_destination.module
View source
<?php

/**
 * @file
 * Control where users are directed to, once they login
 */

// Destination constants
define('LOGIN_DEST_STATIC', 'static');
define('LOGIN_DEST_SNIPPET', 'snippet');

// Condition constants
define('LOGIN_COND_ALWAYS', 'always');
define('LOGIN_COND_PAGES', 'pages');
define('LOGIN_COND_SNIPPET', 'snippet');

// Permissions
define('LOGIN_DEST_PERM_ADMIN', 'administer login destination');

/**
 * Implementation of hook_perm().
 */
function login_destination_perm() {
  return array(
    LOGIN_DEST_PERM_ADMIN,
  );
}

/**
 * Implementation of hook_menu().
 */
function login_destination_menu() {
  $items['admin/user/login_destination'] = array(
    'title' => 'Login Destination',
    'description' => 'Control where users are redirected to, once they login.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'login_destination_admin_settings',
    ),
    'access arguments' => array(
      LOGIN_DEST_PERM_ADMIN,
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}
function login_destination_admin_settings() {
  $form = array();

  // to where we redirect
  $form['destination'] = array(
    '#type' => 'fieldset',
    '#title' => t('Destination URL settings'),
    '#description' => t('To where exactly should the user be redirected upon login.'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['destination']['ld_destination'] = array(
    '#type' => 'checkbox',
    '#default_value' => variable_get('ld_destination', TRUE),
    '#title' => t('Return user to where he/she came from. (Preserve destination)'),
    '#description' => t("If checked, 'destination=' parameter specified in the URL will be used to perform redirection.<br />\n      <b>NOTE</b>: All options below will be ignored then!."),
  );
  $form['destination']['ld_url_type'] = array(
    '#type' => 'radios',
    '#default_value' => variable_get('ld_url_type', LOGIN_DEST_STATIC),
    '#options' => array(
      LOGIN_DEST_STATIC => t('Static URL'),
      LOGIN_DEST_SNIPPET => t('PHP snippet (experts only)'),
    ),
  );
  $form['destination']['ld_url_destination'] = array(
    '#type' => 'textarea',
    '#default_value' => variable_get('ld_url_destination', 'user'),
    '#title' => t('URL: (<b>IMPORTANT! If using a WYSWYG editor - ensure that you use its plain text mode!
      There is a link below the text box.</b> )'),
    '#rows' => 4,
    '#description' => t('<b>Static URL mode:</b> Enter a static path. Do not use a GET query at the end here. Only the first line of text will be used.
      Example paths are %ex1 or %ex2.', array(
      '%ex1' => 'node/add',
      '%ex2' => 'contact',
    )) . '<br/>' . '<b>' . t("PHP snippet mode") . ": </b>" . t("Enter PHP code to evaluate path") . "(<b>" . t("NO %php tags!", array(
      '%php' => '<?php ?>',
    )) . "</b>)." . t("It should return either a string value or an array like in:<br>\n        <blockquote><tt>@array</tt></blockquote>", array(
      '@array' => "return array('path' => 'node/add/page', 'query' => array('param1' => '100', 'param2' => '200'));",
    )),
  );

  // turn on/off the drupal_goto invocation
  // off by default
  // we need ld_use_drupal_goto == ON, if we want to use absolute urls on login redirect, at least until some better way is found
  $form['use_drupal_goto'] = array(
    '#type' => 'fieldset',
    '#title' => t('Use drupal_goto when redirecting ?'),
    '#description' => t('We need the drupal_goto() function, if we want to use absolute urls (e.g. http://example.com) on login redirect.<br>
( That is at least until some better way is found, because using drupal_goto will break modules like content_profile_registration from the content_profile package. )'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['use_drupal_goto']['ld_use_drupal_goto'] = array(
    '#type' => 'radios',
    '#default_value' => variable_get('ld_use_drupal_goto', 0),
    '#options' => array(
      0 => t('Don\'t use drupal_goto(). (DEFAULT OPTION) ( Redirecting to absolute urls won\'t work, but they are rarely needed for redirect destinations.
On the other hand, content_profile_registration will work with this option selected. )'),
      1 => t('Use drupal_goto(). ( NOTE: will break content_profile_registration saving user data on register. Redirecting to absolute urls will work. )'),
    ),
    '#description' => t('MORE INFO: If your site has set these two settings at admin/user/settings:<br>
1. Selected: "Visitors can create accounts and no administrator approval is required."<br>
2. Unchecked: "Require e-mail verification when a visitor creates an account"<br>
<br>
The "Use drupal_goto()" above will prevent the content_profile_registration custom user fields from being saved and will redirect directly to
 the absolute/relative url you\'ve setup here.<br>'),
  );

  // on which pages we redirect
  $form['condition'] = array(
    '#type' => 'fieldset',
    '#title' => t('Redirection conditions'),
    '#description' => t('When should redirection happen?'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['condition']['ld_condition_type'] = array(
    '#type' => 'radios',
    '#default_value' => variable_get('ld_condition_type', 'always'),
    '#options' => array(
      LOGIN_COND_ALWAYS => t('Always'),
      LOGIN_COND_PAGES => t('List of paths'),
      LOGIN_COND_SNIPPET => t('PHP snippet (experts only)'),
    ),
  );
  $form['condition']['ld_condition_snippet'] = array(
    '#type' => 'textarea',
    '#default_value' => variable_get('ld_condition_snippet', ''),
    '#title' => t('Redirect condition: <b>IMPORTANT! If using a WYSIWYG editor - ensure that you use its plain text mode! There is a link below the text box.</b>'),
    '#rows' => 4,
    '#description' => t('<b>Always:</b> Redirection happens always. Redirect condition field is ignored.') . '<br/>' . t('<b>List of paths mode:</b> Enter a list of paths, one path per one line. Redirection will happen only when logging from specified pages. Example paths are <b>%ex1</b> or <b>%ex2</b>.', array(
      '%ex1' => 'contact',
      '%ex2' => 'user/login',
    )) . '<br/>' . t('<b>PHP snippet mode:</b> Enter PHP code to find should redirection happen or not (<b>NO %php tags!</b>). It should return a boolean value.', array(
      '%php' => '<?php ?>',
    )),
  );
  return system_settings_form($form);
}

/**
 * Implementation of hook_user().
 *
 * We hook here to redirect.
 */
function login_destination_user($op, &$edit, &$account, $category = NULL) {
  static $login_destination;

  // If login and not during an installation profile
  if ($op == 'login' && !isset($login_destination)) {
    $login_destination = true;

    // module_invoke_all('user', $op, $edit, $account, $category); // Removed: causing other hook_user calls in other modules to be called twice. Doesn't look like this implementation is doing anything useful anyway
    $arr = login_destination_calculate_redirection_path_and_query($edit);
    $path = $arr[0];
    $query = $arr[1];

    // if condition is ok and this is not the user_login form - redirect. (on the user_login form we set $form['#redirect'] which
    // takes care of the redirection there.)

    //if (__login_destination_should_we_redirect() && $edit['form_id'] != "user_login") {
    if (__login_destination_should_we_redirect($account)) {
      login_destination_redirect_to_path_and_query($path, $query);
    }
  }
}
function login_destination_redirect_to_path_and_query($path, $query) {

  // prepare query: rawurlencode, handle array/string
  if (!empty($query)) {

    // from array to An urlencoded string which can be appended to/as the URL query string.
    // uses rawurlencode
    if (is_array($query)) {
      $query_str_rawurlencoded = drupal_query_string_encode($query);
      $query_for_goto = $query;

      // pass array as is to drupal_goto
    }
    else {
      $query_str_rawurlencoded = rawurlencode($query);
      $query_for_goto = $query_str_rawurlencoded;

      // drupal_goto can accpet urlencoded query string too
    }
  }
  $query_for_destination = !empty($query_str_rawurlencoded) ? rawurlencode('?') . $query_str_rawurlencoded : '';

  // prepare $path - can be absolute, relative
  $path = ltrim($path, "/");

  // won't harm absolute ones, meant for internal=relative ones
  $path_for_destination = rawurlencode($path);
  $path_for_goto = $path;

  // goto does not need url encoded $path
  // update the destination parameter
  $_REQUEST['destination'] = $path_for_destination . $query_for_destination;

  // 1. Redirect ( using absolute url in $path won't actually work, so if we want it to work we need drupal_goto )
  // 2. potentially breaks content_profile_registration's function: see: 'not working with content profile module ? : https://www.drupal.org/node/761254'
  // ... but only when logging the user in right after register, without email confirmation - see settings page MORE INFO
  if (variable_get('ld_use_drupal_goto', 0) == 1) {
    drupal_goto($path_for_goto, $query_for_goto, NULL, 301);
  }
}
function login_destination_calculate_redirection_path_and_query($form) {

  //$message = print_r($edit, true);

  //watchdog("php", $message, array(), WATCHDOG_NOTICE, NULL);

  // this is a string with the contents of the settings textarea
  $destination_str = variable_get('ld_url_destination', 'user');
  $url_type = variable_get('ld_url_type', LOGIN_DEST_STATIC);

  // override all if "preserve" checkbox set and there is a destination in the URL
  if (variable_get('ld_destination', TRUE) && $_GET['destination']) {
    $url_type = LOGIN_DEST_STATIC;
    $destination_str = $_GET['destination'];
  }

  // if snippet
  if ($url_type == LOGIN_DEST_SNIPPET) {
    $url = eval($destination_str);

    // if an array came from the snippet (an array with "path" and "query" keys)
    if (is_array($url) && !empty($url['path'])) {

      // "/" or "/drupal/" or similar
      $base = base_path();
      global $language;
      if (!empty($language->prefix)) {

        // now becomes probably "/en/" or "/drupal/en/"
        $base .= $language->prefix . '/';
      }
      $path = $url['path'];
      $query = $url['query'];

      // strip base from url (isn't this too paranoic?) (won't hurt)
      $path = preg_replace("!^{$base}!", '', $path);
    }
    else {
      $path = $url;
      $query = NULL;
    }
  }
  else {

    // take only 1st line
    if (preg_match("!^(.*?)\$!", $destination_str, $matches) === 1) {
      $path = $matches[1];
      $query = NULL;
    }
  }

  // support for <front>
  if ($path == "<front>") {
    $path = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
  }
  return array(
    $path,
    $query,
  );
}

/**
 * A helper function to determine whether redirection should happen.
 *
 * @return bool TRUE - do redirect, FALSE - do NOT redirect.
 */
function __login_destination_should_we_redirect($account) {
  global $user;

  // don't redirect on registration's password reset
  // not clear if this affects anything. Can't hurt. At least the wrong redirection does not happen currently. Phew!
  // Also check for force_password_change
  if (arg(0) == 'user' && arg(1) == 'reset' || module_exists('force_password_change') && isset($account->force_password_change) && $account->force_password_change && $account->uid == $user->uid) {
    return FALSE;
  }
  $mode = variable_get('ld_condition_type', LOGIN_COND_ALWAYS);
  if ($mode == LOGIN_COND_ALWAYS) {
    return TRUE;
  }
  else {
    $cond = variable_get('ld_condition_snippet', '');
    if ($mode == LOGIN_COND_PAGES) {
      $page_match = FALSE;
      $path = drupal_get_path_alias($_GET['q']);
      $page_match = drupal_match_path($path, $cond);
      if ($path != $_GET['q']) {
        $page_match = $page_match || drupal_match_path($_GET['q'], $cond);
      }
      return $page_match;
    }
    elseif ($mode == LOGIN_COND_SNIPPET) {
      return drupal_eval('<?php ' . $cond . ' ?>');
    }
  }
}