Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vortex/docs/content/drupal/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ You would need to add more modules and themes once you finish the initial setup.
| [`pathauto`](https://www.drupal.org/project/pathauto) | Automatically generates URL/path aliases for content. |
| [`redirect`](https://www.drupal.org/project/redirect) | Provides URL redirection management. |
| [`redis`](https://www.drupal.org/project/redis) | Integrates Redis caching backend. |
| [`reroute_email`](https://www.drupal.org/project/reroute_email) | Intercepts outgoing emails and reroutes them to a configurable address. |
| [`robotstxt`](https://www.drupal.org/project/robotstxt) | Manages the robots.txt file for controlling search engine crawler access. |
| [`search_api`](https://www.drupal.org/project/search_api) | Provides a flexible framework for creating search pages. |
| [`search_api_solr`](https://www.drupal.org/project/search_api_solr) | Integrates Apache Solr with Search API. |
Expand Down
83 changes: 83 additions & 0 deletions .vortex/docs/content/drupal/settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,89 @@ Each settings file should:

</details>

#### Shield

[Shield](https://www.drupal.org/project/shield) restricts access to your site
by requiring HTTP authentication credentials. **Vortex** configures Shield to
protect non-production environments from public access while keeping production,
local development, and CI environments accessible without authentication.

**Environment behavior:**

| Environment | Shield enabled | Reason |
|-------------|----------------|-------------------------------------------------|
| Local | No | No need for HTTP auth during local development |
| CI | No | Automated tests must access the site freely |
| Dev | **Yes** | Protects development environments from crawlers |
| Stage | **Yes** | Protects staging environments from public access|
| Prod | No | Production is publicly accessible |

**Environment variables:**

| Variable | Default | Purpose |
|-----------------------|----------------------|-------------------------------------------|
| `DRUPAL_SHIELD_USER` | | HTTP authentication username |
| `DRUPAL_SHIELD_PASS` | | HTTP authentication password |
| `DRUPAL_SHIELD_PRINT` | `Restricted access.` | Message shown in the authentication popup |

**Overriding default behavior:**

Set `DRUPAL_SHIELD_DISABLED` to any non-empty value to completely disable Shield
in an environment where it would otherwise be enabled. This is useful for
temporary access during debugging or when an environment does not require
protection.

import ShieldModuleSettingsExample from '!!raw-loader!./../../../../web/sites/default/includes/modules/settings.shield.php';

<details>
<summary>Example of the `Shield` module `settings.shield.php` file</summary>

<CodeBlock language="php">{ShieldModuleSettingsExample}</CodeBlock>

</details>

#### Reroute Email

[Reroute Email](https://www.drupal.org/project/reroute_email) intercepts
outgoing emails and redirects them to a configurable address instead of the
intended recipients. This prevents accidental email delivery to real users in
non-production environments while still allowing email functionality to be
tested.

**Environment behavior:**

| Environment | Rerouting enabled | Reason |
|-------------|-------------------|---------------------------------------------------------------|
| Local | No | Emails are typically not sent in local development |
| CI | No | Automated tests do not send real emails |
| Dev | **Yes** | Prevents accidental delivery to real users |
| Stage | No | Stage may require real email delivery for UAT |
| Prod | No | Production emails must reach actual recipients |

Rerouting is also enabled in any custom environments (e.g., PR environments)
that do not match the standard environment types listed above.

Comment on lines +305 to +317
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Clarify environment behavior for custom/PR environments.

The documentation mentions that "Rerouting is also enabled in any custom environments (e.g., PR environments)" at lines 315-316, but this behavior is not reflected in the environment behavior table above. Consider either:

  1. Adding a row to the table for "Custom/PR" environments, or
  2. Adding a footnote to the table indicating additional environments where rerouting is enabled.

This will help users understand the complete picture of when rerouting is active.

🧰 Tools
🪛 LanguageTool

[grammar] ~305-~305: Ensure spelling is correct
Context: ...ng email functionality to be tested. Environment behavior: | Environment | Rerouting ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.vortex/docs/content/drupal/settings.mdx around lines 305 - 317, Update the
environment behavior table to explicitly reflect the sentence "Rerouting is also
enabled in any custom environments (e.g., PR environments)..." by either adding
a new row labeled "Custom/PR" with "Rerouting enabled: Yes" and a short reason
("Prevents accidental delivery in ephemeral PR environments"), or add a clear
footnote below the table referencing that additional non-standard environments
enable rerouting; ensure the table and the existing sentence remain consistent
(same wording for "Rerouting enabled") so readers see the behaviour both in the
table and in the explanatory line.

**Environment variables:**

| Variable | Default | Purpose |
|--------------------------------|------------------------------------|------------------------------------------------------|
| `DRUPAL_REROUTE_EMAIL_ADDRESS` | `webmaster@your-site-domain.example` | Address to receive all rerouted emails |
| `DRUPAL_REROUTE_EMAIL_ALLOWED` | `*@your-site-domain.example` | Pattern for addresses allowed to bypass rerouting |

**Overriding default behavior:**

Set `DRUPAL_REROUTE_EMAIL_DISABLED` to any non-empty value to completely disable
email rerouting in an environment where it would otherwise be enabled.

import RerouteEmailModuleSettingsExample from '!!raw-loader!./../../../../web/sites/default/includes/modules/settings.reroute_email.php';

<details>
<summary>Example of the `Reroute Email` module `settings.reroute_email.php` file</summary>

<CodeBlock language="php">{RerouteEmailModuleSettingsExample}</CodeBlock>

</details>

### Local overrides

At the end of the `settings.php`, there is an option to include additional local
Expand Down
1 change: 1 addition & 0 deletions .vortex/docs/content/features.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
### Admin modules

- [Environment indicator](https://www.drupal.org/project/environment_indicator) for visual identification
- [Reroute Email](https://www.drupal.org/project/reroute_email) for email interception in non-production environments
- [Shield](https://www.drupal.org/project/shield) for HTTP authentication
- [Stage file proxy](https://www.drupal.org/project/stage_file_proxy) for development efficiency
- Configuration for [Search API](https://www.drupal.org/project/search_api) ([Solr](https://www.drupal.org/project/search_api_solr))
Expand Down
1 change: 1 addition & 0 deletions .vortex/installer/src/Prompts/Handlers/Modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ public static function getAvailableModules(): array {
'environment_indicator' => 'Environment indicator',
'pathauto' => 'Pathauto',
'redirect' => 'Redirect',
'reroute_email' => 'Reroute email',
'robotstxt' => 'Robots.txt',
'seckit' => 'Seckit',
'shield' => 'Shield',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"drupal/pathauto": "__VERSION__",
"drupal/redirect": "__VERSION__",
"drupal/redis": "__VERSION__",
"drupal/reroute_email": "__VERSION__",
"drupal/robotstxt": "__VERSION__",
"drupal/search_api": "__VERSION__",
"drupal/search_api_solr": "__VERSION__",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ if echo "${environment}" | grep -q -e dev -e stage -e ci -e local; then
drush php:eval "\Drupal::service('config.factory')->getEditable('system.site')->set('name', 'star wars')->save();"

task "Installing contrib modules."
drush pm:install admin_toolbar coffee config_split config_update media environment_indicator pathauto redirect robotstxt shield stage_file_proxy xmlsitemap
drush pm:install admin_toolbar coffee config_split config_update media environment_indicator pathauto redirect reroute_email robotstxt shield stage_file_proxy xmlsitemap

task "Installing Redis module."
drush pm:install redis || true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public function testEnvironmentNoOverrides(): void {
$config['shield.settings']['shield_enable'] = TRUE;
$config['xmlsitemap.settings']['disable_cron_regeneration'] = TRUE;
$config['xmlsitemap_engines.settings']['submit'] = FALSE;
$config['reroute_email.settings']['enable'] = TRUE;
$config['reroute_email.settings']['address'] = 'webmaster@star-wars.com';
$config['reroute_email.settings']['allowed'] = '*@star-wars.com';
$config['system.performance']['cache']['page']['max_age'] = 900;
$this->assertConfig($config);

Expand Down Expand Up @@ -153,6 +156,9 @@ public function testEnvironmentOverrides(): void {
$config['shield.settings']['shield_enable'] = TRUE;
$config['xmlsitemap.settings']['disable_cron_regeneration'] = TRUE;
$config['xmlsitemap_engines.settings']['submit'] = FALSE;
$config['reroute_email.settings']['enable'] = TRUE;
$config['reroute_email.settings']['address'] = 'webmaster@star-wars.com';
$config['reroute_email.settings']['allowed'] = '*@star-wars.com';
$config['system.performance']['cache']['page']['max_age'] = 1800;
$this->assertConfig($config);

Expand Down Expand Up @@ -202,6 +208,9 @@ public function testEnvironmentLocal(): void {
$config['xmlsitemap_engines.settings']['submit'] = FALSE;
$config['system.logging']['error_level'] = 'all';
$config['system.performance']['cache']['page']['max_age'] = 900;
$config['reroute_email.settings']['enable'] = FALSE;
$config['reroute_email.settings']['address'] = 'webmaster@star-wars.com';
$config['reroute_email.settings']['allowed'] = '*@star-wars.com';
$config['seckit.settings']['seckit_xss']['csp']['checkbox'] = FALSE;
$config['seckit.settings']['seckit_xss']['csp']['upgrade-req'] = FALSE;
$this->assertConfig($config);
Expand Down Expand Up @@ -251,6 +260,9 @@ public function testEnvironmentLocalContainer(): void {
$config['xmlsitemap_engines.settings']['submit'] = FALSE;
$config['system.logging']['error_level'] = 'all';
$config['system.performance']['cache']['page']['max_age'] = 900;
$config['reroute_email.settings']['enable'] = FALSE;
$config['reroute_email.settings']['address'] = 'webmaster@star-wars.com';
$config['reroute_email.settings']['allowed'] = '*@star-wars.com';
$config['seckit.settings']['seckit_xss']['csp']['checkbox'] = FALSE;
$config['seckit.settings']['seckit_xss']['csp']['upgrade-req'] = FALSE;
$this->assertConfig($config);
Expand Down Expand Up @@ -302,6 +314,9 @@ public function testEnvironmentGha(): void {
$config['xmlsitemap_engines.settings']['submit'] = FALSE;
$config['system.logging']['error_level'] = 'all';
$config['system.performance']['cache']['page']['max_age'] = 900;
$config['reroute_email.settings']['enable'] = FALSE;
$config['reroute_email.settings']['address'] = 'webmaster@star-wars.com';
$config['reroute_email.settings']['allowed'] = '*@star-wars.com';
$config['seckit.settings']['seckit_xss']['csp']['checkbox'] = FALSE;
$config['seckit.settings']['seckit_xss']['csp']['upgrade-req'] = FALSE;
$this->assertConfig($config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,145 @@ public static function dataProviderShield(): \Iterator {
];
}

/**
* Test Reroute Email config.
*/
#[DataProvider('dataProviderRerouteEmail')]
public function testRerouteEmail(string $env, array $vars, array $expected_present, array $expected_absent = []): void {
$this->setEnvVars($vars + ['DRUPAL_ENVIRONMENT' => $env]);

$this->requireSettingsFile();

$this->assertConfigContains($expected_present);
$this->assertConfigNotContains($expected_absent);
}

/**
* Data provider for testRerouteEmail().
*/
public static function dataProviderRerouteEmail(): \Iterator {
// Local: disabled by default.
yield [
self::ENVIRONMENT_LOCAL,
[],
[
'reroute_email.settings' => ['enable' => FALSE, 'address' => 'webmaster@star-wars.com', 'allowed' => '*@star-wars.com'],
],
];

// CI: disabled by default.
yield [
self::ENVIRONMENT_CI,
[],
[
'reroute_email.settings' => ['enable' => FALSE, 'address' => 'webmaster@star-wars.com', 'allowed' => '*@star-wars.com'],
],
];

// Dev: enabled by default.
yield [
self::ENVIRONMENT_DEV,
[],
[
'reroute_email.settings' => ['enable' => TRUE, 'address' => 'webmaster@star-wars.com', 'allowed' => '*@star-wars.com'],
],
];

// SUT: enabled by default.
yield [
self::ENVIRONMENT_SUT,
[],
[
'reroute_email.settings' => ['enable' => TRUE, 'address' => 'webmaster@star-wars.com', 'allowed' => '*@star-wars.com'],
],
];

// Stage: disabled by default.
yield [
self::ENVIRONMENT_STAGE,
[],
[
'reroute_email.settings' => ['enable' => FALSE, 'address' => 'webmaster@star-wars.com', 'allowed' => '*@star-wars.com'],
],
];

// Prod: disabled by default.
yield [
self::ENVIRONMENT_PROD,
[],
[
'reroute_email.settings' => ['enable' => FALSE, 'address' => 'webmaster@star-wars.com', 'allowed' => '*@star-wars.com'],
],
];

// Dev with DRUPAL_REROUTE_EMAIL_DISABLED: forced off.
yield [
self::ENVIRONMENT_DEV,
[
'DRUPAL_REROUTE_EMAIL_DISABLED' => 1,
],
[
'reroute_email.settings' => ['enable' => FALSE],
],
];

// SUT with DRUPAL_REROUTE_EMAIL_DISABLED: forced off.
yield [
self::ENVIRONMENT_SUT,
[
'DRUPAL_REROUTE_EMAIL_DISABLED' => 1,
],
[
'reroute_email.settings' => ['enable' => FALSE],
],
];

// Custom address and allowed list.
yield [
self::ENVIRONMENT_DEV,
[
'DRUPAL_REROUTE_EMAIL_ADDRESS' => 'dev@example.com',
'DRUPAL_REROUTE_EMAIL_ALLOWED' => '*@example.com',
],
[
'reroute_email.settings' => ['enable' => TRUE, 'address' => 'dev@example.com', 'allowed' => '*@example.com'],
],
];

// DRUPAL_REROUTE_EMAIL_DISABLED with empty value: not disabled.
yield [
self::ENVIRONMENT_DEV,
[
'DRUPAL_REROUTE_EMAIL_DISABLED' => '',
],
[
'reroute_email.settings' => ['enable' => TRUE],
],
];

// DRUPAL_REROUTE_EMAIL_DISABLED with 0: not disabled.
yield [
self::ENVIRONMENT_DEV,
[
'DRUPAL_REROUTE_EMAIL_DISABLED' => 0,
],
[
'reroute_email.settings' => ['enable' => TRUE],
],
];

// DRUPAL_REROUTE_EMAIL_DISABLED with string '1': disabled.
yield [
self::ENVIRONMENT_DEV,
[
'DRUPAL_REROUTE_EMAIL_DISABLED' => '1',
],
[
'reroute_email.settings' => ['enable' => FALSE],
],
];
}

/**
* Test Stage File Proxy config.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* @file
* Reroute email settings.
*/

declare(strict_types=1);

// Default reroute email address and allowed list.
$config['reroute_email.settings']['address'] = getenv('DRUPAL_REROUTE_EMAIL_ADDRESS') ?: 'webmaster@star-wars.com';
$config['reroute_email.settings']['allowed'] = getenv('DRUPAL_REROUTE_EMAIL_ALLOWED') ?: '*@star-wars.com';

// Enable rerouting in all environments except local, ci, stage and prod.
// This covers dev and any custom environments (e.g., PR environments).
if (!in_array($settings['environment'], [ENVIRONMENT_LOCAL, ENVIRONMENT_CI, ENVIRONMENT_STAGE, ENVIRONMENT_PROD])) {
$config['reroute_email.settings']['enable'] = TRUE;
}
else {
$config['reroute_email.settings']['enable'] = FALSE;
}

// Allow to disable reroute email completely in the environment.
if (!empty(getenv('DRUPAL_REROUTE_EMAIL_DISABLED'))) {
$config['reroute_email.settings']['enable'] = FALSE;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@@ -280,9 +280,9 @@
@@ -292,9 +292,9 @@
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@@ -22,7 +22,6 @@
"drupal/redis": "__VERSION__",
@@ -23,7 +23,6 @@
"drupal/reroute_email": "__VERSION__",
"drupal/robotstxt": "__VERSION__",
"drupal/search_api": "__VERSION__",
- "drupal/search_api_solr": "__VERSION__",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@@ -280,9 +280,9 @@
@@ -292,9 +292,9 @@
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@@ -280,9 +280,9 @@
@@ -292,9 +292,9 @@
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@@ -280,9 +280,9 @@
@@ -292,9 +292,9 @@
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@@ -126,38 +126,38 @@
@@ -127,38 +127,38 @@
"[web-root]/web.config": false
},
"locations": {
Expand Down
Loading
Loading