You are here

hosting_alias.module in Hosting 7.4

Allow sites to have domain aliases that they can be accessed with.

File

alias/hosting_alias.module
View source
<?php

/**
 * @file
 * Allow sites to have domain aliases that they can be accessed with.
 */

/**
 * An alias of this type is a custom, user generated one.
 */
define('HOSTING_ALIAS_CUSTOM', 0);

/**
 * An alias of this type is an automatically generated one.
 *
 * @see hosting_alias_automatic_aliases()
 */
define('HOSTING_ALIAS_AUTOMATIC', 1);

/**
 * Implements hook_menu().
 */
function hosting_alias_menu() {
  $items['admin/hosting/aliases'] = array(
    'title' => 'Site aliases',
    'description' => 'Configure aliases for hosted sites',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'hosting_alias_settings',
    ),
    'access arguments' => array(
      'administer hosting aliases',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['hosting_alias/js'] = array(
    'title' => 'Javascript Alias Form',
    'page callback' => 'hosting_alias_form_js',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_help().
 */
function hosting_alias_help($path, $arg) {
  switch ($path) {
    case 'admin/hosting/aliases':
      $output = t('Site aliases allow you to let sites be available through multiple domain addresses.<br />The most common use of this functionality is to provide automatic aliasing for www.mysite.com and mysite.com variants of the domain name.<br />');
      $output .= t('This module will also allow you to provide a "temporary url" that sites will always be accessible from, in case of DNS problems.<br />');
      $output .= t('Settings made here do not take effect automatically for existing sites.<br />');
      return $output;
  }
}

/**
 * Implements hook_form_alter().
 *
 * Add a textbox to site node forms with a newline
 * separated list of aliases to the site
 */
function hosting_alias_form_site_node_form_alter(&$form, &$form_state) {
  if (user_access('create site aliases')) {
    $form['#validate'][] = 'hosting_alias_site_form_validate';
    return hosting_alias_form_data($form, $form_state);
  }
}

/**
 * Validation handler for site form.
 *
 * Makes sure aliases are not more than HOSTING_MAX_ALIAS_LENGTH characters.
 */
function hosting_alias_site_form_validate($form, &$form_state) {
  $aliases = $form_state['values']['aliases'] = array_filter($form_state['values']['aliases']);
  foreach ($aliases as $key => $alias) {
    if (is_numeric($key)) {
      hosting_alias_validate_alias($form_state['node'], $alias, $key);
    }
  }
}

/**
 * Alter the node form for a site to add the aliases and redirection items.
 *
 * @param array $form
 *   The form to alter, should come from hook_form_alter().
 * @param array $form_state
 *   A keyed array containing the current state of the form.
 */
function hosting_alias_form_data(&$form, &$form_state) {

  // List the automatic aliasses first.
  $automatic_aliases = hosting_alias_get_aliases($form['#node'], HOSTING_ALIAS_AUTOMATIC);
  if (count($automatic_aliases)) {
    foreach ($automatic_aliases as $link) {
      $links[] = l($link, "http://{$link}");
    }
    $form['aliases_automatic'] = array(
      '#type' => 'item',
      '#title' => t('Automatic domain aliases'),
      '#markup' => implode(', ', $links),
      '#weight' => 10,
    );
  }

  // Add a wrapper for the aliases and more button.
  $form['aliases_wrapper'] = array(
    '#tree' => FALSE,
    '#title' => t('Domain Names'),
    '#type' => 'fieldset',
  );
  $form['aliases_wrapper']['aliases'] = array(
    '#prefix' => '<div class="clear-block" id="hosting-aliases-wrapper">',
    '#suffix' => '</div>',
    '#tree' => TRUE,
  );
  $form['aliases_wrapper']['aliases']['_default'] = array(
    '#type' => 'item',
    '#title' => t('Primary domain name'),
    '#description' => t('The primary domain name for this site.'),
    '#markup' => l($form['#node']->title, $form['#node']->title, array(
      'options' => array(
        'attributes' => array(
          'target' => '_blank',
        ),
      ),
    )),
  );
  $form['aliases_wrapper']['aliases']['label'] = array(
    '#type' => 'item',
    '#title' => t('Domain name aliases'),
    '#description' => t('Configure the web server to respond to these domain aliases in addition to the primary domain.'),
  );

  // Get the list of existing aliases, either from form_state or the node.
  if (isset($form_state['input']['aliases'])) {
    $aliases = array_filter($form_state['input']['aliases']);
  }
  elseif (isset($form['#node']->aliases)) {
    $aliases = array_filter($form['#node']->aliases);
  }
  else {
    $aliases = array();
  }

  // Add alias textfields.
  for ($delta = 0; $delta <= count($aliases); $delta++) {
    $form['aliases_wrapper']['aliases'][$delta] = array(
      '#type' => 'textfield',
      '#default_value' => isset($aliases[$delta]) ? $aliases[$delta] : '',
    );
  }

  // "Add Alias" button.
  $form['aliases_wrapper']['add_alias'] = array(
    '#type' => 'submit',
    '#value' => t('Add another alias'),
    '#submit' => array(
      'hosting_alias_add_alias_submit',
    ),
    '#states' => array(
      'visible' => array(
        '#hosting-aliases-wrapper .form-type-textfield:last input' => array(
          'filled' => TRUE,
        ),
      ),
    ),
    '#ajax' => array(
      'callback' => 'hosting_alias_add_alias_callback',
      'wrapper' => 'hosting-aliases-wrapper',
      'method' => 'replace',
      'effect' => 'fade',
    ),
  );

  // Redirection Domain.
  $options = array();
  $options[0] = t('No redirection');
  if (isset($form['#node']->title)) {
    $options[$form['#node']->title] = $form['#node']->title;
  }
  if (!empty($aliases)) {
    $options += array_combine($aliases, $aliases);
  }
  if (!empty($automatic_aliases)) {
    $options += array_combine($automatic_aliases, $automatic_aliases);
  }
  $default = isset($form['#node']->redirection) ? $form['#node']->redirection : '';
  $form['aliases_wrapper']['aliases']['redirection'] = array(
    '#type' => 'select',
    '#title' => t('Domain redirection target'),
    '#description' => t('Requests to all domains will be redirected to the redirection target.'),
    '#options' => $options,
    '#default_value' => $default,
  );
  return $form;
}

/**
 * Callback to handle "Add Alias" button.
 */
function hosting_alias_add_alias_submit($form, &$form_state) {
  $form_state['rebuild'] = TRUE;
}

/**
 * Ajax callback for returning multiple alias fields.
 */
function hosting_alias_add_alias_callback($form, $form_state) {
  return $form['aliases_wrapper']['aliases'];
}

/**
 * Retrieve a list of aliases for a site.
 *
 * @param Object $node
 *   The site to get the aliases for.
 * @param int $type
 *   Restrict the type of aliases returned, defaults to returning all aliases.
 *   Should be one of:
 *   - HOSTING_ALIAS_CUSTOM
 *   - HOSTING_ALIAS_AUTOMATIC
 *
 * @return array
 *   An array of aliases for the given site.
 */
function hosting_alias_get_aliases($node, $type = NULL) {
  if (empty($node->vid)) {
    return array();
  }
  $alias = array();
  $query = db_select('hosting_site_alias')
    ->addTag('hosting_alias_get_aliases')
    ->fields('hosting_site_alias', array(
    'alias',
  ))
    ->condition('vid', $node->vid)
    ->orderBy('alias', 'ASC');
  if (!is_null($type)) {
    $query
      ->condition('automatic', $type);
  }
  $result = $query
    ->execute();
  foreach ($result as $obj) {
    $alias[] = $obj->alias;
  }
  $alias = array_unique($alias);
  if (count($alias)) {
    return $alias;
  }
  return array();
}

/**
 * Save stored aliases for a new site.
 *
 * @param Object $node
 *   The node of the site containing the aliases to save.
 */
function hosting_alias_insert($node) {
  $node->aliases = isset($node->aliases) ? $node->aliases : array();
  $aliases = is_array($node->aliases) ? $node->aliases : explode("\n", str_replace(",", "\n", $node->aliases));
  $automatic = hosting_alias_automatic_aliases(hosting_site_clean_domain($node->title));
  if (empty($aliases) && empty($automatic)) {
    return;
  }
  if (count($aliases)) {
    $aliases = array_unique($aliases);
    foreach ($aliases as $alias) {
      $alias = hosting_site_clean_domain($alias);

      // Only flag custom aliases that aren't automatically generated
      if (in_array($alias, $automatic)) {
        continue;
      }
      $id = db_insert('hosting_site_alias')
        ->fields(array(
        'vid' => $node->vid,
        'nid' => $node->nid,
        'alias' => $alias,
        'automatic' => HOSTING_ALIAS_CUSTOM,
        'redirection' => isset($node->redirection) ? $node->redirection : 0,
      ))
        ->execute();
    }
  }
  if (count($automatic)) {
    foreach ($automatic as $alias) {
      $alias = hosting_site_clean_domain($alias);

      // Only insert valid FQDNs.
      if (!_hosting_valid_fqdn_wildcard($alias)) {
        continue;
      }
      $id = db_insert('hosting_site_alias')
        ->fields(array(
        'vid' => $node->vid,
        'nid' => $node->nid,
        'alias' => $alias,
        'automatic' => HOSTING_ALIAS_AUTOMATIC,
        'redirection' => isset($node->redirection) ? $node->redirection : 0,
      ))
        ->execute();
    }
  }
}

/**
 * Update stored aliases for an existing site.
 *
 * @param Object $node
 *   The node of the site containing the aliases to save.
 */
function hosting_alias_update($node) {

  // We need to wipe clean existing aliases if we are not making a new revision.
  if (empty($node->revision)) {
    hosting_alias_delete_revision($node);
  }
  hosting_alias_insert($node);
}

/**
 * Remove the stored aliases for and existing site.
 *
 * @param Object $node
 *   The site node.
 */
function hosting_alias_delete($node) {
  db_delete('hosting_site_alias')
    ->condition('nid', $node->nid)
    ->execute();
}

/**
 * Remove the stored aliases for and existing site for a specific version.
 *
 * @param Object $node
 *   The site node.
 */
function hosting_alias_delete_revision($node) {
  db_delete('hosting_site_alias')
    ->condition('nid', $node->nid)
    ->condition('vid', $node->vid)
    ->execute();
}

/**
 * Implements hook_node_insert().
 */
function hosting_alias_node_insert($node) {
  if ($node->type == 'site') {
    hosting_alias_insert($node);
  }
}

/**
 * Implements hook_node_update().
 */
function hosting_alias_node_update($node) {
  if ($node->type == 'site') {
    hosting_alias_update($node);
  }
}

/**
 * Implements hook_node_delete().
 */
function hosting_alias_node_delete($node) {
  if ($node->type == 'site') {
    hosting_alias_delete($node);
  }
}

/**
 * Implements hook_node_revision_delete().
 */
function hosting_alias_node_revision_delete($node) {
  if ($node->type == 'site') {
    hosting_alias_delete_revision($node);
  }
}

/**
 * Ensure that an alias is valid, and not already in use.
 *
 * @param object $site
 *   A Hosting site node.
 * @param string $alias
 *   An alias to have point to the site.
 * @param string $key
 *   The array index of this alias, to set any error on the proper sub-field.
 */
function hosting_alias_validate_alias($site, $alias, $key) {
  $alias = hosting_site_clean_domain($alias);
  $params = isset($site->nid) ? array(
    'nid' => $site->nid,
  ) : array();
  $length = strlen($alias);
  if (!hosting_domain_allowed($alias, $params) || $alias == $site->title) {
    form_set_error("aliases][{$key}", t('The domain name @alias is already in use', array(
      '@alias' => $alias,
    )));
  }
  if (!_hosting_valid_fqdn_wildcard($alias)) {
    form_set_error("aliases][{$key}", t('The domain name @alias is not a valid url', array(
      '@alias' => $alias,
    )));
  }
  if ($length > HOSTING_MAX_ALIAS_LENGTH) {
    $long = $length - HOSTING_MAX_ALIAS_LENGTH;
    form_set_error("aliases][{$key}", t('The domain name @alias is @long character(s) too long. Please shorten.', array(
      '@alias' => $alias,
      '@long' => $long,
    )));
  }
}

/**
 * Implements hook_node_load().
 */
function hosting_alias_node_load($nodes, $types) {
  foreach ($nodes as $nid => &$node) {
    if ($node->type == 'site') {

      // XXX: this returns only the first redirection status. it
      // works since they are all set to the same in hook_insert(),
      // but we should return an associative alias => redirection
      // array instead.
      $nodes[$nid]->redirection = db_query("SELECT redirection FROM {hosting_site_alias} WHERE vid = :vid", array(
        ':vid' => $node->vid,
      ))
        ->fetchField();

      // Only retrieves custom aliases, as they are all that can be modified.
      $nodes[$nid]->aliases = hosting_alias_get_aliases($node, HOSTING_ALIAS_CUSTOM);
      $nodes[$nid]->aliases_automatic = hosting_alias_get_aliases($node, HOSTING_ALIAS_AUTOMATIC);
    }
  }
}

/**
 * Implements hook_node_view().
 */
function hosting_alias_node_view($node, $view_mode, $langcode) {
  if ($node->type == 'site') {
    if (count($node->aliases)) {
      foreach ($node->aliases as $link) {
        $links[] = l($link, "http://{$link}");
      }
      $node->content['info']['aliases'] = array(
        '#type' => 'item',
        '#title' => t('Domain aliases'),
        '#markup' => implode(', ', $links),
        '#weight' => 10,
      );
      $redirection = db_query("SELECT redirection FROM {hosting_site_alias} WHERE vid = :vid", array(
        ':vid' => $node->vid,
      ))
        ->fetchAssoc();
      $node->content['info']['redirection'] = array(
        '#type' => 'item',
        '#title' => t('Redirection'),
        '#markup' => $redirection['redirection'] ? t('Yes') : t('No'),
        '#weight' => 12,
      );
    }

    // List the automatic aliasses.
    if (count($node->aliases_automatic)) {
      $links = array();
      foreach ($node->aliases_automatic as $link) {
        $links[] = l($link, "http://{$link}");
      }
      $node->content['info']['aliases_automatic'] = array(
        '#type' => 'item',
        '#title' => t('Auto aliases'),
        '#markup' => implode(', ', $links),
        '#weight' => 11,
      );
    }
  }
}

/**
 * Implements hook_permission().
 */
function hosting_alias_permission() {
  return array(
    'create site aliases' => array(
      'title' => t('create site aliases'),
    ),
    'administer hosting aliases' => array(
      'title' => t('administer hosting aliases'),
    ),
  );
}

/**
 * Configuration form for site aliases.
 *
 * @see hosting_alias_menu()
 */
function hosting_alias_settings($form, &$form_state) {
  $form['hosting_alias_subdomain'] = array(
    '#type' => 'textfield',
    '#title' => t('Domain used for automatic subdomain hosting'),
    '#description' => t('To be able to provide a temporary url for your sites, you need to have configured a wild card dns entry<br /> resolving all calls to subdomains of your chosen domain, to point at your web server.'),
    '#default_value' => variable_get('hosting_alias_subdomain', ''),
  );
  $form['hosting_alias_subdomain_replace_dash'] = array(
    '#type' => 'checkbox',
    '#title' => t('Replace dashes'),
    '#description' => t('Replace dashes in automatic subdomain aliases. Changes only take effect when a site is verified.'),
    '#default_value' => variable_get('hosting_alias_subdomain_replace_dash', TRUE),
  );
  $form['hosting_alias_subdomain_dash_substitute'] = array(
    '#type' => 'textfield',
    '#title' => t('Dash substitute'),
    '#description' => t('Dashes in automatic subdomain aliases will be replaced with the given value. Changes only take effect when a site is verified.'),
    '#default_value' => variable_get('hosting_alias_subdomain_dash_substitute', '--'),
    '#states' => array(
      'visible' => array(
        ':input[name="hosting_alias_subdomain_replace_dash"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );
  $form['hosting_alias_automatic_www'] = array(
    '#type' => 'checkbox',
    '#title' => t('Generate www.domain.com alias automatically'),
    '#description' => t('If a domain name does not start with www., automatically create an alias for www.domain?'),
    '#default_value' => variable_get('hosting_alias_automatic_www', FALSE),
  );
  $form['hosting_alias_automatic_no_www'] = array(
    '#type' => 'checkbox',
    '#title' => t('Generate domain.com alias automatically'),
    '#description' => t('If a domain name starts with www., automatically create an alias for domain.com?'),
    '#default_value' => variable_get('hosting_alias_automatic_no_www', FALSE),
  );
  $form['hosting_alias_redirection'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use redirects instead of aliases by default'),
    '#description' => t('Instead of serving the primary domain under a symlinked site alias, this module can also redirect the user to the primary domain from an alias. This setting can be controlled per site. Setting this option here will make redirection the default behavior for site aliases.'),
    '#default_value' => variable_get('hosting_alias_redirection', FALSE),
  );
  $form['#submit'][] = 'hosting_alias_settings_validate';
  return system_settings_form($form);
}

/**
 * Validation handler for hosting_alias_settings form.
 */
function hosting_alias_settings_validate($form, $form_state) {
  if (!empty($form_state['values']['hosting_alias_subdomain']) && !valid_url($form_state['values']['hosting_alias_subdomain'])) {
    form_set_error('hosting_alias_subdomain', t('The provided domain is invalid.'));
  }
}

/**
 * Generate a default set of aliases for the site based on the global options.
 */
function hosting_alias_automatic_aliases($url) {
  $aliases = module_invoke_all('hosting_automatic_aliases', $url);
  $context = [
    'url' => $url,
  ];
  drupal_alter('hosting_automatic_aliases', $aliases, $context);
  return $aliases;
}

/**
 * Implements hook_hosting_automatic_aliases().
 *
 * Generate automatic subdomain aliases.
 *
 * @see hosting_alias_hosting_automatic_aliases_alter().
 */
function hosting_alias_hosting_automatic_aliases($url) {
  $aliases = [];
  if ($sub = variable_get('hosting_alias_subdomain', FALSE)) {
    if (!preg_match("/\\." . preg_quote($sub, '/') . "\$/", $url)) {
      if (variable_get('hosting_alias_subdomain_replace_dash', TRUE)) {
        $find[] = '-';
        $replace[] = variable_get('hosting_alias_subdomain_dash_substitute', '--');
      }
      $find[] = '.';
      $replace[] = '-';
      $aliases[] = str_replace($find, $replace, $url) . "." . trim($sub, ".");
    }
  }
  return $aliases;
}

/**
 * Implements hook_hosting_automatic_aliases_alter().
 *
 * Generate automatic 'www' or bare domain aliases.
 *
 * @see hosting_alias_hosting_automatic_aliases().
 */
function hosting_alias_hosting_automatic_aliases_alter(&$aliases, $context) {
  $url = $context['url'];
  if (!preg_match('/^www\\./', $url) && variable_get('hosting_alias_automatic_www', FALSE)) {
    $auto_alias = "www." . $url;

    // Lookup DNS, only add if we get an IP back.
    if (gethostbyname($auto_alias) != $auto_alias) {
      $aliases[] = $auto_alias;
    }
  }
  elseif (preg_match('/^www\\./', $url) && variable_get('hosting_alias_automatic_no_www', FALSE)) {
    $auto_alias = str_replace("www.", "", $url);

    // Lookup DNS, only add if we get an IP back.
    if (gethostbyname($auto_alias) != $auto_alias) {
      $aliases[] = $auto_alias;
    }
  }
}

/**
 * Implements hook_allow_domain().
 *
 * This function will check the existing aliases and the automatically
 * generated aliases to ensure that this url has not been used before
 */
function hosting_alias_allow_domain($url, $params = array()) {
  $query = db_select('node', 'n')
    ->fields('n', array(
    'nid',
  ))
    ->condition('n.type', 'site');
  $query
    ->leftJoin('hosting_site', 'h', 'h.nid = n.nid');
  $query
    ->condition('h.status', HOSTING_SITE_DELETED, '<>');
  $query
    ->leftJoin('hosting_site_alias', 'a', 'n.vid = a.vid');
  $query
    ->condition('a.alias', $url);

  // For existing sites, don't match the site's current aliases.
  if (isset($params['nid'])) {
    $query
      ->condition('n.nid', $params['nid'], '<>');
  }
  return !$query
    ->countQuery()
    ->execute()
    ->fetchField();
}

/**
 * Views integration.
 */
function hosting_alias_views_api() {
  return [
    'api' => 3,
    'path' => drupal_get_path('module', 'hosting_alias') . '/includes/views',
  ];
}

Functions

Namesort descending Description
hosting_alias_add_alias_callback Ajax callback for returning multiple alias fields.
hosting_alias_add_alias_submit Callback to handle "Add Alias" button.
hosting_alias_allow_domain Implements hook_allow_domain().
hosting_alias_automatic_aliases Generate a default set of aliases for the site based on the global options.
hosting_alias_delete Remove the stored aliases for and existing site.
hosting_alias_delete_revision Remove the stored aliases for and existing site for a specific version.
hosting_alias_form_data Alter the node form for a site to add the aliases and redirection items.
hosting_alias_form_site_node_form_alter Implements hook_form_alter().
hosting_alias_get_aliases Retrieve a list of aliases for a site.
hosting_alias_help Implements hook_help().
hosting_alias_hosting_automatic_aliases Implements hook_hosting_automatic_aliases().
hosting_alias_hosting_automatic_aliases_alter Implements hook_hosting_automatic_aliases_alter().
hosting_alias_insert Save stored aliases for a new site.
hosting_alias_menu Implements hook_menu().
hosting_alias_node_delete Implements hook_node_delete().
hosting_alias_node_insert Implements hook_node_insert().
hosting_alias_node_load Implements hook_node_load().
hosting_alias_node_revision_delete Implements hook_node_revision_delete().
hosting_alias_node_update Implements hook_node_update().
hosting_alias_node_view Implements hook_node_view().
hosting_alias_permission Implements hook_permission().
hosting_alias_settings Configuration form for site aliases.
hosting_alias_settings_validate Validation handler for hosting_alias_settings form.
hosting_alias_site_form_validate Validation handler for site form.
hosting_alias_update Update stored aliases for an existing site.
hosting_alias_validate_alias Ensure that an alias is valid, and not already in use.
hosting_alias_views_api Views integration.

Constants

Namesort descending Description
HOSTING_ALIAS_AUTOMATIC An alias of this type is an automatically generated one.
HOSTING_ALIAS_CUSTOM An alias of this type is a custom, user generated one.