Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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/development/composer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ where each module is installed.
| [`drupal/devel`](https://www.drupal.org/project/devel) | A suite of development tools for inspecting variables, entities and the service container while debugging. | [Settings](settings.mdx#per-module-overrides) |
| [`drupal/drupal_helpers`](https://www.drupal.org/project/drupal_helpers) | A collection of helper functions that simplify writing update hooks and deployment operations. | [Drupal helpers](modules/drupal-helpers.mdx) |
| [`drupal/environment_indicator`](https://www.drupal.org/project/environment_indicator) | Shows a colored banner identifying the current environment to prevent accidental changes on the wrong site. | [Settings](settings.mdx#per-module-overrides) |
| [`drupal/fast_404`](https://www.drupal.org/project/fast_404) | Answers requests for missing files with a 404 before Drupal bootstraps, saving the bootstrap that core's own fast 404 still performs. | [Settings](settings.mdx#per-module-overrides) |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| [`drupal/generated_content`](https://www.drupal.org/project/generated_content) | Generates deterministic placeholder content from declarative definitions for development and testing. | [Generated content](modules/generated-content.mdx), [Settings](settings.mdx#per-module-overrides) |
| [`drupal/migrate_plus`](https://www.drupal.org/project/migrate_plus) | Extends the core Migrate API with extra source and process plugins and configuration-entity migrations. | [Migrations](migrations.mdx) |
| [`drupal/migrate_tools`](https://www.drupal.org/project/migrate_tools) | Provides Drush commands and a UI to run, roll back and monitor migrations. | [Migrations](migrations.mdx) |
Expand Down
10 changes: 10 additions & 0 deletions .vortex/docs/content/development/modules/contributed-modules.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ No settings override.

[`settings.environment_indicator.php`](https://github.com/drevops/vortex/blob/main/web/sites/default/includes/modules/settings.environment_indicator.php) names the indicator after the detected environment and colors it per environment - red for production, yellow for stage, green for dev - with the toolbar integration and favicon marker turned on.

## Fast 404

[Fast 404](https://www.drupal.org/project/fast_404) answers requests for missing files without bootstrapping Drupal. Drupal core already returns a lightweight 404 for the same file extensions, but only after a full bootstrap, so on a site serving many such requests the saving is the bootstrap itself.

- **Installed:** Never enabled as a Drupal module. The handler runs from the settings file during the settings load, before the module system exists, so requiring the package is all that is needed.

### Configuration

[`settings.fast_404.php`](https://github.com/drevops/vortex/blob/main/web/sites/default/includes/modules/settings.fast_404.php) sets the file extensions to intercept, whitelists the front controller and the other entry-point scripts, allows anonymous access to image style derivatives, and calls `fast404_preboot()` with those settings.

## Generated Content

[Generated content](https://www.drupal.org/project/generated_content) generates deterministic placeholder content from declarative definitions for development and testing.
Expand Down
1 change: 1 addition & 0 deletions .vortex/docs/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"phpcompatibility",
"phpspec",
"phpstorm",
"preboot",
"pyrech",
"redis",
"renovatebot",
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 @@ -159,6 +159,7 @@ public static function getAvailableModules(): array {
'devel' => 'Devel',
'drupal_helpers' => 'Drupal helpers',
'environment_indicator' => 'Environment indicator',
'fast_404' => 'Fast 404',
'generated_content' => 'Generated content',
'navigation_extra_tools' => 'Navigation extra tools',
'pathauto' => 'Pathauto',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"drupal/devel": "__VERSION__",
"drupal/drupal_helpers": "__VERSION__",
"drupal/environment_indicator": "__VERSION__",
"drupal/fast_404": "__VERSION__",
"drupal/generated_content": "__VERSION__",
"drupal/navigation_extra_tools": "__VERSION__",
"drupal/pathauto": "__VERSION__",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ public function testEnvironmentNoOverrides(): void {
$settings['container_yamls'][0] = $this->app_root . '/' . $this->site_path . '/services.yml';
$settings['entity_update_batch_size'] = 50;
$settings['environment'] = self::ENVIRONMENT_SUT;
$settings['fast404_allow_anon_imagecache'] = TRUE;
$settings['fast404_exts'] = '/^(?!\/robots)^(?!\/system\/files).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
$settings['fast404_html'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';
$settings['fast404_whitelist'] = [
'index.php',
'rss.xml',
'install.php',
'cron.php',
'update.php',
'xmlrpc.php',
];
$settings['file_public_path'] = 'sites/default/files';
$settings['file_private_path'] = 'sites/default/files/private';
$settings['file_temp_path'] = '/tmp';
Expand All @@ -109,6 +120,7 @@ public function testEnvironmentNoOverrides(): void {
$settings['trusted_host_patterns'] = [
'^localhost$',
];

$this->assertSettings($settings);
}

Expand Down Expand Up @@ -181,6 +193,17 @@ public function testEnvironmentOverrides(): void {
$settings['container_yamls'][0] = $this->app_root . '/' . $this->site_path . '/services.yml';
$settings['entity_update_batch_size'] = 50;
$settings['environment'] = self::ENVIRONMENT_SUT;
$settings['fast404_allow_anon_imagecache'] = TRUE;
$settings['fast404_exts'] = '/^(?!\/robots)^(?!\/system\/files).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
$settings['fast404_html'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';
$settings['fast404_whitelist'] = [
'index.php',
'rss.xml',
'install.php',
'cron.php',
'update.php',
'xmlrpc.php',
];
$settings['file_public_path'] = 'custom_public';
$settings['file_private_path'] = 'custom_private';
$settings['file_temp_path'] = 'custom_temp';
Expand Down Expand Up @@ -238,6 +261,17 @@ public function testEnvironmentLocal(): void {
$settings['container_yamls'][0] = $this->app_root . '/' . $this->site_path . '/services.yml';
$settings['entity_update_batch_size'] = 50;
$settings['environment'] = self::ENVIRONMENT_LOCAL;
$settings['fast404_allow_anon_imagecache'] = TRUE;
$settings['fast404_exts'] = '/^(?!\/robots)^(?!\/system\/files).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
$settings['fast404_html'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';
$settings['fast404_whitelist'] = [
'index.php',
'rss.xml',
'install.php',
'cron.php',
'update.php',
'xmlrpc.php',
];
$settings['file_public_path'] = 'sites/default/files';
$settings['file_private_path'] = 'sites/default/files/private';
$settings['file_temp_path'] = '/tmp';
Expand All @@ -252,6 +286,7 @@ public function testEnvironmentLocal(): void {
$settings['trusted_host_patterns'] = [
'^localhost$',
];

$this->assertSettings($settings);
}

Expand Down Expand Up @@ -296,6 +331,17 @@ public function testEnvironmentLocalContainer(): void {
$settings['container_yamls'][0] = $this->app_root . '/' . $this->site_path . '/services.yml';
$settings['entity_update_batch_size'] = 50;
$settings['environment'] = self::ENVIRONMENT_LOCAL;
$settings['fast404_allow_anon_imagecache'] = TRUE;
$settings['fast404_exts'] = '/^(?!\/robots)^(?!\/system\/files).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
$settings['fast404_html'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';
$settings['fast404_whitelist'] = [
'index.php',
'rss.xml',
'install.php',
'cron.php',
'update.php',
'xmlrpc.php',
];
$settings['file_public_path'] = 'sites/default/files';
$settings['file_private_path'] = 'sites/default/files/private';
$settings['file_temp_path'] = '/tmp';
Expand All @@ -312,6 +358,7 @@ public function testEnvironmentLocalContainer(): void {
'^example\-site\.docker\.amazee\.io$',
'^nginx$',
];

$this->assertSettings($settings);
}

Expand Down Expand Up @@ -397,6 +444,17 @@ public function testEnvironmentGha(): void {
$settings['container_yamls'][0] = $this->app_root . '/' . $this->site_path . '/services.yml';
$settings['entity_update_batch_size'] = 50;
$settings['environment'] = self::ENVIRONMENT_CI;
$settings['fast404_allow_anon_imagecache'] = TRUE;
$settings['fast404_exts'] = '/^(?!\/robots)^(?!\/system\/files).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
$settings['fast404_html'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';
$settings['fast404_whitelist'] = [
'index.php',
'rss.xml',
'install.php',
'cron.php',
'update.php',
'xmlrpc.php',
];
$settings['file_public_path'] = 'sites/default/files';
$settings['file_private_path'] = 'sites/default/files/private';
$settings['file_temp_path'] = '/tmp';
Expand All @@ -411,6 +469,7 @@ public function testEnvironmentGha(): void {
$settings['trusted_host_patterns'] = [
'^localhost$',
];

$this->assertSettings($settings);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,47 @@ protected function requireSettingsFile(array $pre_settings = [], array $pre_conf
$this->databases = $databases;
}

/**
* Require a per-module settings file with an explicit contrib path.
*
* Per-module settings files guarded by the presence of a module directory
* are unreachable through the site's own contrib path when the module is not
* required by the project.
*
* @param string $module
* Module name as it appears in the `settings.<module>.php` file name.
* @param string $contrib_path
* Path to the contrib modules directory to expose to the settings file.
* @param array $pre_settings
* Array of settings to pre-populate.
* @param array $pre_config
* Array of configs to pre-populate.
*/
protected function requireModuleSettingsFile(string $module, string $contrib_path, array $pre_settings = [], array $pre_config = []): void {
$app_root = getcwd() . '/web';

if (!file_exists($app_root)) {
throw new \RuntimeException('Could not determine application root.');
}

$site_path = 'sites/default';
$file = implode(DIRECTORY_SEPARATOR, [$app_root, $site_path, 'includes', 'modules', sprintf('settings.%s.php', $module)]);

if (!file_exists($file)) {
throw new \RuntimeException(sprintf('Settings file %s does not exist.', $file));
}

$config = $pre_config;
$settings = $pre_settings;

require $file;

$this->app_root = $app_root;
$this->site_path = $site_path;
$this->config = $config;
$this->settings = $settings;
}

/**
* Assert that config retrieved from the real settings file match test data.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Drupal;

use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;

Expand All @@ -19,6 +21,22 @@
#[Group('drupal_settings')]
class SwitchableSettingsTest extends SettingsTestCase {

/**
* Path to the contrib modules directory fixture.
*/
protected ?string $contribFixture = NULL;

/**
* {@inheritdoc}
*/
protected function tearDown(): void {
if (!is_null($this->contribFixture)) {
$this->removeContribFixture($this->contribFixture);
}

parent::tearDown();
}

/**
* Test ClamAV configs in Daemon mode with defaults.
*/
Expand Down Expand Up @@ -223,6 +241,103 @@ public static function dataProviderEnvironmentIndicator(): \Iterator {
];
}

/**
* Test Fast 404 settings.
*
* Runs isolated so that the module stub owns the 'fast404_preboot()'
* declaration and its invocation marker is conclusive.
*/
#[DataProvider('dataProviderFast404')]
#[RunInSeparateProcess]
#[PreserveGlobalState(FALSE)]
public function testFast404(bool $module_installed, array $expected_present, array $expected_absent = []): void {
$contrib_path = $this->createContribFixture($module_installed);

$this->requireModuleSettingsFile('fast_404', $contrib_path);

$this->assertSettingsContains($expected_present);
$this->assertSettingsNotContains($expected_absent);
$this->assertSame($module_installed, file_exists($contrib_path . '/fast_404/preboot'), 'Preboot invocation');
}

/**
* Data provider for testFast404().
*/
public static function dataProviderFast404(): \Iterator {
yield 'module installed' => [
TRUE,
[
'fast404_exts' => '/^(?!\/robots)^(?!\/system\/files).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i',
'fast404_allow_anon_imagecache' => TRUE,
'fast404_whitelist' => ['index.php', 'rss.xml', 'install.php', 'cron.php', 'update.php', 'xmlrpc.php'],
'fast404_html' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>',
],
];
yield 'module not installed' => [
FALSE,
[],
[
'fast404_exts' => NULL,
'fast404_allow_anon_imagecache' => NULL,
'fast404_whitelist' => NULL,
'fast404_html' => NULL,
],
];
}

/**
* Create a contrib modules directory fixture.
*
* The Fast 404 module is not required by the project, so its settings file
* guard can only be satisfied by a stub.
*
* @param bool $with_fast404
* Create a stub of the Fast 404 module within the fixture. The stub marks
* the directory when its preboot function is called.
*
* @return string
* Path to the contrib modules directory fixture.
*/
protected function createContribFixture(bool $with_fast404): string {
$this->contribFixture = getcwd() . '/.artifacts/tmp/' . uniqid('contrib-');

mkdir($this->contribFixture, 0777, TRUE);

if ($with_fast404) {
$stub = <<<'PHP'
<?php

function fast404_preboot(array $settings = []): void {
touch(__DIR__ . '/preboot');
}

PHP;

mkdir($this->contribFixture . '/fast_404');
file_put_contents($this->contribFixture . '/fast_404/fast404.inc', $stub);
}

return $this->contribFixture;
}

/**
* Remove the contrib modules directory fixture.
*
* @param string $path
* Path to the contrib modules directory fixture.
*/
protected function removeContribFixture(string $path): void {
foreach (glob($path . '/*/*') ?: [] as $file) {
unlink($file);
}

foreach (glob($path . '/*', GLOB_ONLYDIR) ?: [] as $dir) {
rmdir($dir);
}

rmdir($path);
}

/**
* Test Redis settings.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@

declare(strict_types=1);

if (file_exists($contrib_path . '/fast404/fast404.inc')) {
// @codeCoverageIgnoreStart
$settings['fast404_exts'] = '/^(?!robots).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
if (file_exists($contrib_path . '/fast_404/fast404.inc')) {
$settings['fast404_exts'] = '/^(?!\/robots)^(?!\/system\/files).*\.(txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
$settings['fast404_allow_anon_imagecache'] = TRUE;
$settings['fast404_whitelist'] = [
'index.php',
Expand All @@ -19,10 +18,8 @@
'update.php',
'xmlrpc.php',
];
$settings['fast404_string_whitelisting'] = ['/advagg_'];
$settings['fast404_html'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';
include_once $contrib_path . '/fast404/fast404.inc';
include_once $contrib_path . '/fast_404/fast404.inc';
// @phpstan-ignore-next-line
fast404_preboot($settings);
// @codeCoverageIgnoreEnd
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@@ -356,9 +356,9 @@
@@ -403,9 +403,9 @@
}

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

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@@ -26,7 +26,6 @@
@@ -27,7 +27,6 @@
"drupal/robotstxt": "__VERSION__",
"drupal/sdc_devel": "__VERSION__",
"drupal/search_api": "__VERSION__",
Expand Down
Loading