You are here

domain_alias.module in Domain Access 8

Maps multiple host requests to a single domain record.

File

domain_alias/domain_alias.module
View source
<?php

/**
 * @file
 * Maps multiple host requests to a single domain record.
 */
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use Drupal\Core\Session\AccountInterface;
use Drupal\domain\DomainInterface;
use Drupal\domain\DomainNegotiatorInterface;

/**
 * Implements hook_domain_request_alter().
 *
 * The logic in this function gives us the following matches for a request to
 * foo.example.com.
 *
 *   'foo.*'
 *   '*.example.com'
 *   'foo.*.com'
 *   'foo.example.*'
 *   '*.foo.example.com'
 *   'foo.example.com.*'
 *
 * These patterns should be sufficient for most conditions.
 */
function domain_alias_domain_request_alter(DomainInterface &$domain) {

  // During the installation the entity definition is not yet added when this
  // hook is invoked, so skip if not present.
  $has_definition = \Drupal::entityTypeManager()
    ->hasDefinition('domain_alias');

  // If an exact match has loaded, do nothing.
  if ($domain
    ->getMatchType() === DomainNegotiatorInterface::DOMAIN_MATCHED_EXACT || !$has_definition) {
    return;
  }

  // If no exact match, then run the alias load routine.
  $hostname = $domain
    ->getHostname();
  $alias_storage = \Drupal::entityTypeManager()
    ->getStorage('domain_alias');
  $domain_storage = \Drupal::entityTypeManager()
    ->getStorage('domain');

  /** @var \Drupal\domain_alias\Entity\DomainAlias $alias */
  if ($alias = $alias_storage
    ->loadByHostname($hostname)) {

    /** @var \Drupal\domain\Entity\Domain $domain */
    if ($domain = $domain_storage
      ->load($alias
      ->getDomainId())) {
      $domain
        ->addProperty('alias', $alias);
      $domain
        ->setMatchType(DomainNegotiatorInterface::DOMAIN_MATCHED_ALIAS);
      $redirect = $alias
        ->getRedirect();
      if (!empty($redirect)) {
        $domain
          ->setRedirect($redirect);
      }
    }
    else {

      // If the domain did not load, report an error.
      \Drupal::logger('domain_alias')
        ->error('Found matching alias %alias for host request %hostname, but failed to load matching domain with id %id.', [
        '%alias' => $alias
          ->getPattern(),
        '%hostname' => $hostname,
        '%id' => $alias
          ->getDomainId(),
      ]);
    }
  }
}

/**
 * Implements hook_domain_operations().
 */
function domain_alias_domain_operations(DomainInterface $domain, AccountInterface $account) {
  $operations = [];

  // Check permissions. The user must be a super-admin or assigned to the
  // domain.
  $is_domain_admin = $domain
    ->access('update', $account);
  if ($account
    ->hasPermission('administer domain aliases') || $is_domain_admin && $account
    ->hasPermission('view domain aliases')) {

    // Add aliases to the list of operations.
    $operations['domain_alias'] = [
      'title' => t('Aliases'),
      'url' => Url::fromRoute('domain_alias.admin', [
        'domain' => $domain
          ->id(),
      ]),
      'weight' => 60,
    ];
  }
  return $operations;
}

/**
 * Implements hook_ENTITY_TYPE_load().
 */
function domain_alias_domain_load($entities) {
  static $enabled;

  // We can only perform meaningful actions if url.site is a cache context.
  // Otherwise, the render process ignores our changes.
  if (!isset($enabled)) {
    $required_cache_contexts = \Drupal::getContainer()
      ->getParameter('renderer.config')['required_cache_contexts'];
    if (!in_array('url.site', $required_cache_contexts) && !in_array('url', $required_cache_contexts)) {
      $enabled = FALSE;
      return;
    }
    $enabled = TRUE;
  }

  // We cannot run before the negotiator service has fired.
  $negotiator = \Drupal::service('domain.negotiator');
  $active = $negotiator
    ->getActiveDomain();

  // Do nothing if no domain is active.
  if (empty($active)) {
    return;
  }

  // Load and rewrite environment-specific aliases.
  $alias_storage = \Drupal::entityTypeManager()
    ->getStorage('domain_alias');
  if (isset($active->alias) && $active->alias
    ->getEnvironment() != 'default') {
    foreach ($entities as $id => $domain) {
      if ($environment_aliases = $alias_storage
        ->loadByEnvironmentMatch($domain, $active->alias
        ->getEnvironment())) {
        foreach ($environment_aliases as $environment_alias) {
          $pattern = $environment_alias
            ->getPattern();

          // Add a canonical property.
          $domain
            ->setCanonical();

          // Override the domain hostname and path. We always prefer a string
          // match.
          if (substr_count($pattern, '*') < 1) {
            $domain
              ->setHostname($pattern);
            $domain
              ->setPath();
            $domain
              ->setUrl();
            break;
          }
          else {

            // Do a wildcard replacement based on the current request.
            $request = $negotiator
              ->negotiateActiveHostname();

            // First, check for a wildcard port.
            if (substr_count($pattern, ':*') > 0) {

              // Do not replace ports unless they are nonstandard. See
              // \Symfony\Component\HttpFoundation\Request\getHttpHost().
              if (substr_count($request, ':') > 0) {
                $search = explode(':', $pattern);
                $replace = explode(':', $request);
                if (!empty($search[1]) && !empty($replace[1])) {
                  $pattern = str_replace(':' . $search[1], ':' . $replace[1], $pattern);
                }
              }
              else {
                $pattern = str_replace(':*', '', $pattern);
              }
            }
            $replacements = [
              '.' => '\\.',
              '*' => '(.+?)',
            ];
            $regex = '/^' . strtr($active->alias
              ->getPattern(), $replacements) . '$/';
            if (preg_match($regex, $request, $matches) && isset($matches[1])) {
              $pattern = str_replace('*', $matches[1], $pattern);
            }

            // Do not let the domain loop back on itself.
            if ($pattern != $domain
              ->getCanonical()) {
              $domain
                ->setHostname($pattern);
              $domain
                ->setPath();
              $domain
                ->setUrl();
            }
          }
        }
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 */
function domain_alias_domain_delete(EntityInterface $entity) {
  $alias_storage = \Drupal::entityTypeManager()
    ->getStorage('domain_alias');
  $properties = [
    'domain_id' => $entity
      ->id(),
  ];
  foreach ($alias_storage
    ->loadByProperties($properties) as $alias) {
    $alias
      ->delete();
  }
}