Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/html mails #262

Merged
merged 13 commits into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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 composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"drupal/gin_login": "^1.0@RC",
"drupal/gin_toolbar": "^1.0@beta",
"drupal/inline_entity_form": "^1.0@RC",
"drupal/mailsystem": "^4.3",
"drupal/masquerade": "^2.0@beta",
"drupal/message": "^1.2",
"drupal/openid_connect": "2.x-dev@dev",
Expand Down
82 changes: 73 additions & 9 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions config/sync/core.extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module:
language: 0
link: 0
locale: 0
mailsystem: 0
media: 0
media_library: 0
menu_link_content: 0
Expand Down
6 changes: 6 additions & 0 deletions config/sync/mailsystem.settings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
theme: current
defaults:
sender: os2loop_mail_notifications
formatter: os2loop_mail_notifications
_core:
default_config_hash: IhwTepsVwtbtbcT5GzQKhCXDCRvbk3MNkWqPiuiZ10s
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function sendNotification(User $user, array $groupedMessages) {
$section = array_map(function (Message $message) use ($langcode) {
return $this->getMessageContent($message, $langcode);
}, $messages);
$section = implode(PHP_EOL, $section);
$section = implode('<br>', $section);
$sections[$type] = $section;
$params[$type] = $section;
}
Expand Down Expand Up @@ -119,12 +119,12 @@ public function sendNotification(User $user, array $groupedMessages) {

$messagesWithHeadings = '';
foreach ($messageSections as $heading => $content) {
$messagesWithHeadings .= $heading . PHP_EOL . PHP_EOL . '* ' . implode(PHP_EOL . '* ', $content) . PHP_EOL . PHP_EOL;
$messagesWithHeadings .= $heading . '<br><br>' . implode('<br>', $content) . '<br><br>';
}
$params['messages_with_headings'] = $messagesWithHeadings;

$sections = array_filter($sections);
$params['messages'] = implode(PHP_EOL . PHP_EOL, $sections);
$params['messages'] = implode('<br><br>', $sections);
$params['user'] = $user;

$result = $this->mailer->mail(Helper::MODULE, self::NOTIFICATION_MAIL, $user->getEmail(), $langcode, $params, NULL, TRUE);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?php

namespace Drupal\os2loop_mail_notifications\Plugin\Mail;

use Drupal\Core\Mail\MailInterface;
use Drupal\Core\Site\Settings;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\UnstructuredHeader;

/**
* Copy of the default Drupal mail backend, using PHP's native mail() function.
*
* The only thing changed is the namespace, id, format() method and
* $message['headers']['Content-Type'] in mail() method.
*
* @Mail(
* id = "os2loop_mail_notifications",
* label = @Translation("Custom PHP mailer"),
* description = @Translation("Sends the message as plain text, using PHP's native mail() function.")
* )
*/
class PhpMail implements MailInterface {

/**
* A list of headers that can contain multiple email addresses.
*
* @see \Symfony\Component\Mime\Header\Headers::HEADER_CLASS_MAP
*/
private const MAILBOX_LIST_HEADERS = ['from', 'to', 'reply-to', 'cc', 'bcc'];

/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;

/**
* PhpMail constructor.
*/
public function __construct() {
$this->configFactory = \Drupal::configFactory();
}

/**
* Concatenates and wraps the email body for plain-text mails.
*
* @param array $message
* A message array, as described in hook_mail_alter().
*
* @return array
* The formatted $message.
*/
public function format(array $message) {
$message['body'] = $message['params']['messages_with_headings'];

return $message;
}

/**
* Sends an email message.
*
* @param array $message
* A message array, as described in hook_mail_alter().
*
* @return bool
* TRUE if the mail was successfully accepted, otherwise FALSE.
*
* @see http://php.net/manual/function.mail.php
* @see \Drupal\Core\Mail\MailManagerInterface::mail()
*/
public function mail(array $message) {
// If 'Return-Path' isn't already set in php.ini, we pass it separately
// as an additional parameter instead of in the header.
if (isset($message['headers']['Return-Path'])) {
$return_path_set = strpos(ini_get('sendmail_path'), ' -f');
if (!$return_path_set) {
$message['Return-Path'] = $message['headers']['Return-Path'];
unset($message['headers']['Return-Path']);
}
}

$headers = new Headers();
$message['headers']['Content-Type'] = 'text/html; charset=iso-8859-1;';
foreach ($message['headers'] as $name => $value) {
if (in_array(strtolower($name), self::MAILBOX_LIST_HEADERS, TRUE)) {
$value = explode(',', $value);
}
$headers->addHeader($name, $value);
}
$line_endings = Settings::get('mail_line_endings', PHP_EOL);
// Prepare mail commands.
$mail_subject = (new UnstructuredHeader('subject', $message['subject']))->getBodyAsString();
// Note: email uses CRLF for line-endings. PHP's API requires LF
// on Unix and CRLF on Windows. Drupal automatically guesses the
// line-ending format appropriate for your system. If you need to
// override this, adjust $settings['mail_line_endings'] in settings.php.
$mail_body = preg_replace('@\r?\n@', $line_endings, $message['body']);
// For headers, PHP's API suggests that we use CRLF normally,
// but some MTAs incorrectly replace LF with CRLF. See #234403.
$mail_headers = str_replace("\r\n", "\n", $headers->toString());
$mail_subject = str_replace("\r\n", "\n", $mail_subject);

$request = \Drupal::request();

// We suppress warnings and notices from mail() because of issues on some
// hosts. The return value of this method will still indicate whether mail
// was sent successfully.
if (!$request->server->has('WINDIR') && strpos($request->server->get('SERVER_SOFTWARE'), 'Win32') === FALSE) {
// On most non-Windows systems, the "-f" option to the sendmail command
// is used to set the Return-Path. There is no space between -f and
// the value of the return path.
// We validate the return path, unless it is equal to the site mail, which
// we assume to be safe.
$site_mail = $this->configFactory->get('system.site')->get('mail');
$additional_headers = isset($message['Return-Path']) && ($site_mail === $message['Return-Path'] || static::isShellSafe($message['Return-Path'])) ? '-f' . $message['Return-Path'] : '';
$mail_result = @mail(
$message['to'],
$mail_subject,
$mail_body,
$mail_headers,
$additional_headers
);
}
else {
// On Windows, PHP will use the value of sendmail_from for the
// Return-Path header.
$old_from = ini_get('sendmail_from');
ini_set('sendmail_from', $message['Return-Path']);
$mail_result = @mail(
$message['to'],
$mail_subject,
$mail_body,
$mail_headers
);
ini_set('sendmail_from', $old_from);
}

return $mail_result;
}

/**
* Disallows potentially unsafe shell characters.
*
* Functionally similar to PHPMailer::isShellSafe() which resulted from
* CVE-2016-10045. Note that escapeshellarg and escapeshellcmd are inadequate
* for this purpose.
*
* @param string $string
* The string to be validated.
*
* @return bool
* True if the string is shell-safe.
*
* @see https://github.com/PHPMailer/PHPMailer/issues/924
* @see https://github.com/PHPMailer/PHPMailer/blob/v5.2.21/class.phpmailer.php#L1430
*
* @todo Rename to ::isShellSafe() and/or discuss whether this is the correct
* location for this helper.
*/
protected static function isShellSafe($string) {
if (escapeshellcmd($string) !== $string ||
!in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
) {
return FALSE;
}
if (preg_match('/[^a-zA-Z0-9@_\-.]/', $string) !== 0) {
return FALSE;
}
return TRUE;
}

}