diff --git a/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php b/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php index f99fb86a8f6..ee9c6eeab40 100644 --- a/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php +++ b/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php @@ -12,6 +12,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mailer\Transport; +use Symfony\Component\Mailer\Transport\Dsn; use Symfony\Component\Mime\Email; /** @@ -35,11 +36,16 @@ use Symfony\Component\Mime\Email; * * @code * $config['system.mail']['interface'] = [ 'default' => 'symfony_mailer' ]; - * $config['system.mail']['mailer_dsn'] = 'smtp://user:pass@smtp.example.com:25'; + * $config['system.mail']['mailer_dsn'] = [ + * 'scheme' => 'smtp', + * 'host' => 'smtp.example.com', + * 'port' => 25, + * 'user' => 'user', + * 'password' => 'pass', + * 'options' => [], + * ]; * @endcode * - * Note that special characters in the mailer_dsn need to be URL encoded. - * * @see https://symfony.com/doc/current/mailer.html#using-built-in-transports * * @Mail( @@ -143,6 +149,7 @@ class SymfonyMailer implements MailInterface, ContainerFactoryPluginInterface { protected function getMailer(): MailerInterface { if (!isset($this->mailer)) { $dsn = \Drupal::config('system.mail')->get('mailer_dsn'); + $dsnObject = new Dsn(...$dsn); // Symfony Mailer and Transport classes both optionally depend on the // event dispatcher. When provided, a MessageEvent is fired whenever an @@ -154,7 +161,9 @@ class SymfonyMailer implements MailInterface, ContainerFactoryPluginInterface { // mails into the code path (i.e., event subscribers) of the new API. // Therefore, this plugin deliberately refrains from injecting the event // dispatcher. - $transport = Transport::fromDsn($dsn, logger: $this->logger); + $factories = Transport::getDefaultFactories(logger: $this->logger); + $transportFactory = new Transport($factories); + $transport = $transportFactory->fromDsnObject($dsnObject); $this->mailer = new Mailer($transport); } diff --git a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php index ef9f3bc349c..48c28243b02 100644 --- a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php +++ b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php @@ -330,7 +330,14 @@ trait FunctionalTestSetupTrait { // some tests expect to be able to test mail system implementations. $config->getEditable('system.mail') ->set('interface.default', 'test_mail_collector') - ->set('mailer_dsn', 'null://null') + ->set('mailer_dsn', [ + 'scheme' => 'null', + 'host' => 'null', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ]) ->save(); // By default, verbosely display all errors and disable all production diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php index 10d6305d24b..43f48a1e44a 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php @@ -65,8 +65,12 @@ abstract class MigrateUpgradeExecuteTestBase extends MigrateUpgradeTestBase { 'value' => 'test_mail_collector', 'required' => TRUE, ]; - $settings['config']['system.mail']['mailer_dsn'] = (object) [ - 'value' => 'null://null', + $settings['config']['system.mail']['mailer_dsn']['scheme'] = (object) [ + 'value' => 'null', + 'required' => TRUE, + ]; + $settings['config']['system.mail']['mailer_dsn']['host'] = (object) [ + 'value' => 'null', 'required' => TRUE, ]; $this->writeSettings($settings); diff --git a/core/modules/system/config/install/system.mail.yml b/core/modules/system/config/install/system.mail.yml index 67c1e29f92a..91dd4d0cc5d 100644 --- a/core/modules/system/config/install/system.mail.yml +++ b/core/modules/system/config/install/system.mail.yml @@ -1,3 +1,9 @@ interface: default: 'php_mail' -mailer_dsn: "sendmail://default" +mailer_dsn: + scheme: 'sendmail' + host: 'default' + user: null + password: null + port: null + options: [] diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 83fc5c80a38..a495c9cf7af 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -304,8 +304,45 @@ system.mail: type: string label: 'Interface' mailer_dsn: - type: string + type: mapping label: 'Symfony mailer transport DSN' + mapping: + scheme: + type: string + label: 'Scheme' + constraints: + NotBlank: + message: 'The mailer DSN must contain a scheme.' + host: + type: string + label: 'Host' + constraints: + NotBlank: + message: 'The mailer DSN must contain a host (use "default" by default).' + user: + type: string + nullable: true + label: 'User' + password: + type: string + nullable: true + label: 'Password' + port: + type: integer + nullable: true + label: 'Port' + constraints: + Range: + min: 0 + max: 65535 + options: + type: sequence + label: 'Options' + sequence: + type: string + label: Option + constraints: + NotNull: [] system.theme.global: type: theme_settings diff --git a/core/modules/system/migrations/d7_system_mail.yml b/core/modules/system/migrations/d7_system_mail.yml index d2038043e92..3aec8bf40bf 100644 --- a/core/modules/system/migrations/d7_system_mail.yml +++ b/core/modules/system/migrations/d7_system_mail.yml @@ -19,8 +19,20 @@ process: plugin: static_map source: 'mail_system/default-system' map: - DefaultMailSystem: 'sendmail://default' - MailTestCase: 'null://null' + DefaultMailSystem: + scheme: 'sendmail' + host: 'default' + user: null + password: null + port: null + options: [] + MailTestCase: + scheme: 'null' + host: 'null' + user: null + password: null + port: null + options: [] destination: plugin: config config_name: system.mail diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php index 5940992769b..f027b80634f 100644 --- a/core/modules/system/system.post_update.php +++ b/core/modules/system/system.post_update.php @@ -164,8 +164,21 @@ function system_post_update_set_blank_log_url_to_null() { * Add new default mail transport dsn. */ function system_post_update_mailer_dsn_settings() { +} + +/** + * Add new default mail transport dsn. + */ +function system_post_update_mailer_structured_dsn_settings() { $config = \Drupal::configFactory()->getEditable('system.mail'); - $config->set('mailer_dsn', 'sendmail://default')->save(); + $config->set('mailer_dsn', [ + 'scheme' => 'sendmail', + 'host' => 'default', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ])->save(); } /** diff --git a/core/modules/system/tests/src/Functional/Update/MailDsnSettingsUpdateTest.php b/core/modules/system/tests/src/Functional/Update/MailDsnSettingsUpdateTest.php index 42542cf46db..3e3dfd19bab 100644 --- a/core/modules/system/tests/src/Functional/Update/MailDsnSettingsUpdateTest.php +++ b/core/modules/system/tests/src/Functional/Update/MailDsnSettingsUpdateTest.php @@ -30,7 +30,15 @@ class MailDsnSettingsUpdateTest extends UpdatePathTestBase { // Confirm that config was created. $config = $this->config('system.mail'); - $this->assertEquals('sendmail://default', $config->get('mailer_dsn')); + $expected = [ + 'scheme' => 'sendmail', + 'host' => 'default', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ]; + $this->assertEquals($expected, $config->get('mailer_dsn')); } } diff --git a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php index 6af738f5251..4ace54e8499 100644 --- a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php +++ b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php @@ -59,7 +59,14 @@ class MigrateSystemConfigurationTest extends MigrateDrupal7TestBase { 'interface' => [ 'default' => 'php_mail', ], - 'mailer_dsn' => 'sendmail://default', + 'mailer_dsn' => [ + 'scheme' => 'sendmail', + 'host' => 'default', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ], ], 'system.maintenance' => [ // langcode is not handled by the migration. diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php index 8bd28ac0e96..7cd1ae5682c 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php @@ -197,7 +197,14 @@ abstract class InstallerTestBase extends BrowserTestBase { $this->container->get('config.factory') ->getEditable('system.mail') ->set('interface.default', 'test_mail_collector') - ->set('mailer_dsn', 'null://null') + ->set('mailer_dsn', [ + 'scheme' => 'null', + 'host' => 'null', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ]) ->save(); $this->installDefaultThemeFromClassProperty($this->container); diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index 63e9f732468..3acf47a5ecc 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -430,8 +430,14 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa // While this should be enforced via settings.php prior to installation, // some tests expect to be able to test mail system implementations. $GLOBALS['config']['system.mail']['interface']['default'] = 'test_mail_collector'; - $GLOBALS['config']['system.mail']['mailer_dsn'] = 'null://null'; - + $GLOBALS['config']['system.mail']['mailer_dsn'] = [ + 'scheme' => 'null', + 'host' => 'null', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ]; // Manually configure the default file scheme so that modules that use file // functions don't have to install system and its configuration. // @see file_default_scheme() diff --git a/core/tests/Drupal/Tests/Core/Mail/MailManagerTest.php b/core/tests/Drupal/Tests/Core/Mail/MailManagerTest.php index 1f52ed48a13..b90ae632d64 100644 --- a/core/tests/Drupal/Tests/Core/Mail/MailManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Mail/MailManagerTest.php @@ -114,7 +114,14 @@ class MailManagerTest extends UnitTestCase { $this->configFactory = $this->getConfigFactoryStub([ 'system.mail' => [ 'interface' => $interface, - 'mailer_dsn' => 'null://null', + 'mailer_dsn' => [ + 'scheme' => 'null', + 'host' => 'null', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ], ], 'system.site' => [ 'mail' => 'test@example.com', diff --git a/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php b/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php index ec216092c7a..a139d8a0ac9 100644 --- a/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php +++ b/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php @@ -46,7 +46,14 @@ class PhpMailTest extends UnitTestCase { $this->configFactory = $this->getConfigFactoryStub([ 'system.mail' => [ 'interface' => [], - 'mailer_dsn' => 'null://null', + 'mailer_dsn' => [ + 'scheme' => 'null', + 'host' => 'null', + 'user' => NULL, + 'password' => NULL, + 'port' => NULL, + 'options' => [], + ], ], 'system.site' => [ 'mail' => 'test@example.com',