diff --git a/typo3/sysext/core/Classes/Mail/MailMessage.php b/typo3/sysext/core/Classes/Mail/MailMessage.php index f4e85d8967a3..83a42f5764c4 100644 --- a/typo3/sysext/core/Classes/Mail/MailMessage.php +++ b/typo3/sysext/core/Classes/Mail/MailMessage.php @@ -63,6 +63,12 @@ public function send() if (empty($this->getFrom())) { $this->setFrom(MailUtility::getSystemFrom()); } + if (empty($this->getReplyTo())) { + $replyTo = MailUtility::getSystemReplyTo(); + if (!empty($replyTo)) { + $this->setReplyTo($replyTo); + } + } $this->initializeMailer(); $this->sent = true; $this->getHeaders()->addTextHeader('X-Mailer', $this->mailerHeader); diff --git a/typo3/sysext/core/Classes/Utility/MailUtility.php b/typo3/sysext/core/Classes/Utility/MailUtility.php index 5d4752401f3f..2a0ab2ed5f93 100644 --- a/typo3/sysext/core/Classes/Utility/MailUtility.php +++ b/typo3/sysext/core/Classes/Utility/MailUtility.php @@ -114,6 +114,30 @@ public static function getSystemFromAddress() return $address; } + /** + * Gets a default "reply-to" for mail messages (email and name). + * + * Ready to be passed to $mail->setReplyTo() + * + * @return array List of email-addresses. Specifying a realname can be done in the form of "replyToName ". + */ + public static function getSystemReplyTo(): array + { + $mailConfiguration = $GLOBALS['TYPO3_CONF_VARS']['MAIL']; + $replyToAddress = $mailConfiguration['defaultMailReplyToAddress']; + if (empty($replyToAddress) || !GeneralUtility::validEmail($replyToAddress)) { + return []; + } + + if (!empty($mailConfiguration['defaultMailReplyToName'])) { + $replyTo = [$replyToAddress => $mailConfiguration['defaultMailReplyToName']]; + } else { + $replyTo = [$replyToAddress]; + } + + return $replyTo; + } + /** * Breaks up a single line of text for emails * Words - longer than $lineWidth - will not be split into parts diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php index 415224b53159..2b876f3d2cf5 100644 --- a/typo3/sysext/core/Configuration/DefaultConfiguration.php +++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php @@ -1055,7 +1055,9 @@ 'transport_sendmail_command' => '', 'transport_mbox_file' => '', 'defaultMailFromAddress' => '', - 'defaultMailFromName' => '' + 'defaultMailFromName' => '', + 'defaultMailReplyToAddress' => '', + 'defaultMailReplyToName' => '', ], 'HTTP' => [ // HTTP configuration to tune how TYPO3 behaves on HTTP requests made by TYPO3. Have a look at http://docs.guzzlephp.org/en/latest/request-options.html for some background information on those settings. 'allow_redirects' => [ // Mixed, set to false if you want to allow redirects, or use it as an array to add more values, diff --git a/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml b/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml index bfb9e0cbd62e..51dfd07fbbc9 100644 --- a/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml +++ b/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml @@ -484,10 +484,16 @@ MAIL: description: 'only with transport=mbox: The file where to write the mails into. This file will be conforming the mbox format described in RFC 4155. It is a simple text file with a concatenation of all mails. Path must be absolute.' defaultMailFromAddress: type: text - description: 'This default email address is used when no other "from" address is set for a TYPO3-generated email. You can specify an email address only (ex. info@example.org).' + description: 'This default email address is used when no other "from" address is set for a TYPO3-generated email. You can specify an email address only (eg. info@example.org).' defaultMailFromName: type: text description: 'This default name is used when no other "from" name is set for a TYPO3-generated email.' + defaultMailReplyToAddress: + type: text + description: 'This default email address is used when no other "reply-to" address is set for a TYPO3-generated email. You can specify an email address only (eg. info@example.org).' + defaultMailReplyToName: + type: text + description: 'This default name is used when no other "reply-to" name is set for a TYPO3-generated email.' HTTP: type: container items: diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-78332-AllowSettingADefaultReplyTo-email-addressForNotification-mails.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-78332-AllowSettingADefaultReplyTo-email-addressForNotification-mails.rst new file mode 100644 index 000000000000..62e88e189508 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-78332-AllowSettingADefaultReplyTo-email-addressForNotification-mails.rst @@ -0,0 +1,36 @@ +.. include:: ../../Includes.txt + +====================================================================================== +Feature: #78332 - Allow setting a default replyTo-email-address for notification-mails +====================================================================================== + +See :issue:`78332` + +Description +=========== + +Two new LocalConfiguration settings are introduced: + +.. code-block:: php + + $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailReplyToAddress'] + $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailReplyToName'] + +Also a new function to build a mail address for SwiftMailer from these settings is introduced: + +.. code-block:: php + + MailUtility::getSystemReplyTo() + +If no default reply-to address is set this function will return an empty array. + +This function is used in :php:`ContentObjectRenderer::sendNotifyEmail()` to set a ReplyTo address in case no address was supplied in the function parameters. +In other places where notifications are sent for e.g. (failed) login attempts, reports and where the notification uses the system from address this function is also used. + + +Impact +====== + +It's now possible to set a reply-to address for notification mails from TYPO3. Extensions can also use this system reply-to address by calling :php:`MailUtility::getSystemReplyTo()`. + +.. index:: LocalConfiguration, NotScanned \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Unit/Utility/MailUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/MailUtilityTest.php index 36fbb47fdd64..c273e7481402 100644 --- a/typo3/sysext/core/Tests/Unit/Utility/MailUtilityTest.php +++ b/typo3/sysext/core/Tests/Unit/Utility/MailUtilityTest.php @@ -14,6 +14,8 @@ * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Core\Utility\MailUtility; + /** * Testcase for the \TYPO3\CMS\Core\Utility\MailUtility class. */ @@ -40,7 +42,7 @@ protected function tearDown() */ public function breakLinesForEmailReturnsEmptyStringIfEmptryStringIsGiven() { - $this->assertEmpty(\TYPO3\CMS\Core\Utility\MailUtility::breakLinesForEmail('')); + $this->assertEmpty(MailUtility::breakLinesForEmail('')); } /** @@ -51,7 +53,7 @@ public function breakLinesForEmailReturnsOneLineIfCharWithIsNotExceeded() $newlineChar = LF; $lineWidth = 76; $str = 'This text is not longer than 76 chars and therefore will not be broken.'; - $returnString = \TYPO3\CMS\Core\Utility\MailUtility::breakLinesForEmail($str, $newlineChar, $lineWidth); + $returnString = MailUtility::breakLinesForEmail($str, $newlineChar, $lineWidth); $this->assertEquals(1, count(explode($newlineChar, $returnString))); } @@ -63,7 +65,7 @@ public function breakLinesForEmailBreaksTextIfCharWithIsExceeded() $newlineChar = LF; $lineWidth = 50; $str = 'This text is longer than 50 chars and therefore will be broken.'; - $returnString = \TYPO3\CMS\Core\Utility\MailUtility::breakLinesForEmail($str, $newlineChar, $lineWidth); + $returnString = MailUtility::breakLinesForEmail($str, $newlineChar, $lineWidth); $this->assertEquals(2, count(explode($newlineChar, $returnString))); } @@ -76,7 +78,7 @@ public function breakLinesForEmailBreaksTextWithNoSpaceFoundBeforeLimit() $lineWidth = 10; // first space after 20 chars (more than $lineWidth) $str = 'abcdefghijklmnopqrst uvwxyz 123456'; - $returnString = \TYPO3\CMS\Core\Utility\MailUtility::breakLinesForEmail($str, $newlineChar, $lineWidth); + $returnString = MailUtility::breakLinesForEmail($str, $newlineChar, $lineWidth); $this->assertEquals($returnString, 'abcdefghijklmnopqrst' . LF . 'uvwxyz' . LF . '123456'); } @@ -86,7 +88,7 @@ public function breakLinesForEmailBreaksTextWithNoSpaceFoundBeforeLimit() public function breakLinesForEmailBreaksTextIfLineIsLongerThanTheLineWidth() { $str = 'Mein Link auf eine News (Link: http://zzzzzzzzzzzzz.xxxxxxxxx.de/index.php?id=10&tx_ttnews%5Btt_news%5D=1&cHash=66f5af320da29b7ae1cda49047ca7358)'; - $returnString = \TYPO3\CMS\Core\Utility\MailUtility::breakLinesForEmail($str); + $returnString = MailUtility::breakLinesForEmail($str); $this->assertEquals($returnString, 'Mein Link auf eine News (Link:' . LF . 'http://zzzzzzzzzzzzz.xxxxxxxxx.de/index.php?id=10&tx_ttnews%5Btt_news%5D=1&cHash=66f5af320da29b7ae1cda49047ca7358)'); } @@ -123,7 +125,45 @@ public function parseAddressesProvider() */ public function parseAddressesTest($source, $addressList) { - $returnArray = \TYPO3\CMS\Core\Utility\MailUtility::parseAddresses($source); + $returnArray = MailUtility::parseAddresses($source); $this->assertEquals($addressList, $returnArray); } + + /** + * @return array + */ + public function replyToProvider(): array + { + return [ + 'only address' => [ + ['defaultMailReplyToAddress' => 'noreply@example.org', 'defaultMailReplyToName' => ''], + ['noreply@example.org'], + ], + 'name and address' => [ + ['defaultMailReplyToAddress' => 'noreply@example.org', 'defaultMailReplyToName' => 'John Doe'], + ['noreply@example.org' => 'John Doe'], + ], + 'no address' => [ + ['defaultMailReplyToAddress' => '', 'defaultMailReplyToName' => ''], + [], + ], + 'invalid address' => [ + ['defaultMailReplyToAddress' => 'foo', 'defaultMailReplyToName' => ''], + [], + ], + ]; + } + + /** + * @test + * @dataProvider replyToProvider + * @param array $configuration + * @param array $expectedReplyTo + */ + public function getSystemReplyToTest(array $configuration, array $expectedReplyTo) + { + $GLOBALS['TYPO3_CONF_VARS']['MAIL'] = $configuration; + $returnArray = MailUtility::getSystemReplyTo(); + $this->assertSame($expectedReplyTo, $returnArray); + } }