domain_alias.module 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. /**
  3. * @file
  4. * Maps multiple host requests to a single domain record.
  5. */
  6. use Drupal\Core\Entity\EntityInterface;
  7. use Drupal\domain\DomainInterface;
  8. use Drupal\domain\DomainNegotiator;
  9. use Drupal\domain\DomainAccessControlHandler;
  10. use Drupal\Core\Url;
  11. use Drupal\Core\Session\AccountInterface;
  12. /**
  13. * Implements hook_domain_request_alter().
  14. *
  15. * The logic in this function gives us the following matches for a request to
  16. * foo.example.com.
  17. *
  18. * 'foo.*'
  19. * '*.example.com'
  20. * 'foo.*.com'
  21. * 'foo.example.*'
  22. * '*.foo.example.com'
  23. * 'foo.example.com.*'
  24. *
  25. * These patterns should be sufficient for most conditions.
  26. */
  27. function domain_alias_domain_request_alter(DomainInterface &$domain) {
  28. // If an exact match has loaded, do nothing.
  29. if ($domain->getMatchType() == DomainNegotiator::DOMAIN_MATCH_EXACT) {
  30. return;
  31. }
  32. // If no exact match, then run the alias load routine.
  33. $hostname = $domain->getHostname();
  34. $alias_storage = \Drupal::service('entity_type.manager')->getStorage('domain_alias');
  35. $domain_storage = \Drupal::service('entity_type.manager')->getStorage('domain');
  36. /** @var \Drupal\domain_alias\Entity\DomainAlias $alias */
  37. if ($alias = $alias_storage->loadByHostname($hostname)) {
  38. /** @var \Drupal\domain\Entity\Domain $domain */
  39. if ($domain = $domain_storage->load($alias->getDomainId())) {
  40. $domain->addProperty('alias', $alias);
  41. $domain->setMatchType(DomainNegotiator::DOMAIN_MATCH_ALIAS);
  42. $redirect = $alias->getRedirect();
  43. if (!empty($redirect)) {
  44. $domain->setRedirect($redirect);
  45. }
  46. }
  47. else {
  48. // If the domain did not load, report an error.
  49. \Drupal::logger('domain_alias')->error('Found matching alias %alias for host request %hostname, but failed to load matching domain with id %id.',
  50. ['%alias' => $alias->getPattern(),
  51. '%hostname' => $hostname,
  52. '%id' => $alias->getDomainId(),
  53. ]);
  54. }
  55. }
  56. }
  57. /**
  58. * Implements hook_domain_operations().
  59. */
  60. function domain_alias_domain_operations(DomainInterface $domain, AccountInterface $account) {
  61. $operations = [];
  62. // Check permissions. The user must be a super-admin or assigned to the domain.
  63. $is_domain_admin = $domain->access('update', $account);
  64. if ($account->hasPermission('administer domain aliases') || ($is_domain_admin && $account->hasPermission('view domain aliases'))) {
  65. // Add aliases to the list of operations.
  66. $operations['domain_alias'] = array(
  67. 'title' => t('Aliases'),
  68. 'url' => Url::fromRoute('domain_alias.admin', array('domain' => $domain->id())),
  69. 'weight' => 60,
  70. );
  71. }
  72. return $operations;
  73. }
  74. /**
  75. * Implements hook_ENTITY_TYPE_load().
  76. */
  77. function domain_alias_domain_load($entities) {
  78. static $enabled;
  79. // We can only perform meaningful actions if url.site is a cache context. Otherwise,
  80. // the render process ignores our changes.
  81. if (!isset($enabled)) {
  82. $required_cache_contexts = \Drupal::getContainer()->getParameter('renderer.config')['required_cache_contexts'];
  83. if (!in_array('url.site', $required_cache_contexts) && !in_array('url', $required_cache_contexts)) {
  84. $enabled = FALSE;
  85. return;
  86. }
  87. $enabled = TRUE;
  88. }
  89. // We cannot run before the negotiator service has fired.
  90. $negotiator = \Drupal::service('domain.negotiator');
  91. $active = $negotiator->getActiveDomain();
  92. // Do nothing if no domain is active.
  93. if (is_null($active)) {
  94. return;
  95. }
  96. // Load and rewrite environement-specific aliases.
  97. $alias_storage = \Drupal::service('entity_type.manager')->getStorage('domain_alias');
  98. if (isset($active->alias) && $active->alias->getEnvironment() != 'default') {
  99. foreach ($entities as $id => $domain) {
  100. if ($environment_aliases = $alias_storage->loadByEnvironmentMatch($domain, $active->alias->getEnvironment())) {
  101. foreach($environment_aliases as $environment_alias) {
  102. $pattern = $environment_alias->getPattern();
  103. // Add a canonical property.
  104. $domain->setCanonical();
  105. // Override the domain hostname and path. We always prefer a string match.
  106. if (substr_count($pattern, '*') < 1) {
  107. $domain->setHostname($pattern);
  108. $domain->setPath();
  109. $domain->setUrl();
  110. break;
  111. }
  112. else {
  113. // Do a wildcard replacement based on the current request.
  114. $request = $negotiator->negotiateActiveHostname();
  115. // First, check for a wildcard port.
  116. if (substr_count($pattern, ':*') > 0) {
  117. // Do not replace ports unless they are nonstandard. See
  118. // \Symfony\Component\HttpFoundation\Request\getHttpHost().
  119. if (substr_count($request, ':') > 0) {
  120. $search = explode(':', $pattern);
  121. $replace = explode(':', $request);
  122. if (!empty($search[1]) && !empty($replace[1])) {
  123. $pattern = str_replace(':' . $search[1], ':' . $replace[1], $pattern);
  124. }
  125. }
  126. // If no port wildcard, then remove the port entirely.
  127. else {
  128. $pattern = str_replace(':*', '', $pattern);
  129. }
  130. }
  131. $replacements = ['.' => '\.', '*' => '(.+?)'];
  132. $regex = '/^' . strtr($active->alias->getPattern(), $replacements) . '$/';
  133. if (preg_match($regex, $request, $matches) && isset($matches[1])) {
  134. $pattern = str_replace('*', $matches[1], $pattern);
  135. }
  136. // Do not let the domain loop back on itself.
  137. if ($pattern != $domain->getCanonical()) {
  138. $domain->setHostname($pattern);
  139. $domain->setPath();
  140. $domain->setUrl();
  141. }
  142. }
  143. }
  144. }
  145. }
  146. }
  147. }
  148. /**
  149. * Implements hook_ENTITY_TYPE_delete().
  150. */
  151. function domain_alias_domain_delete(EntityInterface $entity) {
  152. $alias_storage = \Drupal::service('entity_type.manager')->getStorage('domain_alias');
  153. $properties = ['domain_id' => $entity->id()];
  154. foreach ($alias_storage->loadByProperties($properties) as $alias) {
  155. $alias->delete();
  156. }
  157. }