Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ public function sendTestMail()
[
// Replace the occurrences of "@" and "|" in the site name
'sitename' => str_replace(['@', '|'], '', $app->get('sitename')),
'method' => Text::_('COM_CONFIG_SENDMAIL_METHOD_' . strtoupper($mail->Mailer)),
'method' => Text::_('COM_CONFIG_SENDMAIL_METHOD_' . strtoupper($mail->Mailer ?? $input->get('mailer'))),
]
);
$mailer->addRecipient($user->email, $user->name);
Expand All @@ -1230,10 +1230,10 @@ public function sendTestMail()
}

if ($mailSent === true) {
$methodName = Text::_('COM_CONFIG_SENDMAIL_METHOD_' . strtoupper($mail->Mailer));
$methodName = Text::_('COM_CONFIG_SENDMAIL_METHOD_' . strtoupper($mail->Mailer ?? $input->get('mailer')));

// If JMail send the mail using PHP Mail as fallback.
if ($mail->Mailer !== $app->get('mailer')) {
// If Mail send the mail using PHP Mail as fallback.
if (($mail->Mailer ?? $app->get('mailer')) !== $app->get('mailer')) {
$app->enqueueMessage(Text::sprintf('COM_CONFIG_SENDMAIL_SUCCESS_FALLBACK', $user->email, $methodName), 'warning');
} else {
$app->enqueueMessage(Text::sprintf('COM_CONFIG_SENDMAIL_SUCCESS', $user->email, $methodName), 'message');
Expand Down
33 changes: 33 additions & 0 deletions libraries/src/Mail/FormatConfigurableMailerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* Joomla! Content Management System
*
* @copyright (C) 2026 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\CMS\Mail;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
* Provides a common interface to send emails with HTML.
*
* @since __DEPLOY_VERSION__
*/
interface FormatConfigurableMailerInterface
{
/**
* Sets message type to HTML.
*
* @param boolean $ishtml Boolean true or false.
*
* @return FormatConfigurableMailerInterface Returns this object for chaining.
*
* @since __DEPLOY_VERSION__
*/
public function isHtml($ishtml = true);
}
2 changes: 1 addition & 1 deletion libraries/src/Mail/Mail.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*
* @since 1.7.0
*/
class Mail extends PHPMailer implements MailerInterface
class Mail extends PHPMailer implements MailerInterface, TransportConfigurableMailerInterface, FormatConfigurableMailerInterface
{
/**
* Mail instances container.
Expand Down
37 changes: 27 additions & 10 deletions libraries/src/Mail/MailTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class MailTemplate
/**
* Mailer object to send the actual mail.
*
* @var \Joomla\CMS\Mail\Mail
* @var MailerInterface
* @since 4.0.0
*/
protected $mailer;
Expand Down Expand Up @@ -117,14 +117,15 @@ class MailTemplate
*
* @since 4.0.0
*/
public function __construct($templateId, $language, ?Mail $mailer = null)
public function __construct($templateId, $language, ?MailerInterface $mailer = null)
{
$this->template_id = $templateId;
$this->language = $language;

if ($mailer) {
$this->mailer = $mailer;
} else {
@trigger_error('Mailer must be set, this will not be caught anymore in 8.0.', E_USER_DEPRECATED);
$this->mailer = Factory::getMailer();
}
}
Expand Down Expand Up @@ -274,7 +275,7 @@ public function send()
$replyToName = $app->get('replytoname', '');

if ((int) $config->get('alternative_mailconfig', 0) === 1 && (int) $params->get('alternative_mailconfig', 0) === 1) {
if ($this->mailer->Mailer === 'smtp' || $params->get('mailer') === 'smtp') {
if ($this->mailer instanceof TransportConfigurableMailerInterface && $params->get('mailer') === 'smtp') {
$smtpauth = ($params->get('smtpauth', $app->get('smtpauth')) == 0) ? null : 1;
$smtpuser = $params->get('smtpuser', $app->get('smtpuser'));
$smtppass = $params->get('smtppass', $app->get('smtppass'));
Expand All @@ -284,15 +285,15 @@ public function send()
$this->mailer->useSmtp($smtpauth, $smtphost, $smtpuser, $smtppass, $smtpsecure, $smtpport);
}

if ($params->get('mailer') === 'sendmail') {
$this->mailer->isSendmail();
if ($this->mailer instanceof TransportConfigurableMailerInterface && $params->get('mailer') === 'sendmail') {
$this->mailer->useSendmail('joomla');
}

$mailfrom = $params->get('mailfrom', $app->get('mailfrom'));
$fromname = $params->get('fromname', $app->get('fromname'));

if (MailHelper::isEmailAddress($mailfrom)) {
$this->mailer->setFrom(MailHelper::cleanLine($mailfrom), MailHelper::cleanLine($fromname), false);
$this->mailer->setSender(MailHelper::cleanLine($mailfrom), MailHelper::cleanLine($fromname));
}

$replyTo = $params->get('replyto', $replyTo);
Expand Down Expand Up @@ -335,7 +336,9 @@ public function send()
}

if ($mailStyle === 'html' || $mailStyle === 'both') {
$this->mailer->isHtml(true);
if ($this->mailer instanceof FormatConfigurableMailerInterface) {
$this->mailer->isHtml(true);
}

// If HTML body is empty try to convert the Plain template to html
if (!$htmlBody) {
Expand Down Expand Up @@ -413,7 +416,7 @@ public function send()
break;
case 'to':
default:
$this->mailer->addAddress($recipient->mail, $recipient->name);
$this->mailer->addRecipient($recipient->mail, $recipient->name);
}
}

Expand All @@ -437,15 +440,29 @@ public function send()
}
}

$tmpAttachmentFiles = [];
foreach ($this->attachments as $attachment) {
if (is_file($attachment->file)) {
$this->mailer->addAttachment($attachment->file, $this->getAttachmentName($attachment->file, $attachment->name));
} else {
} elseif ($this->mailer instanceof Mail) {
$this->mailer->addStringAttachment($attachment->file, $attachment->name);
} else {
// If the mailer does not support string attachments, create a temporary file and attach it
$tmpFile = tempnam(JPATH_CACHE, 'mailattachment_');
if (file_put_contents($tmpFile, $attachment->file) !== false) {
$this->mailer->addAttachment($tmpFile, $attachment->name);
$tmpAttachmentFiles[] = $tmpFile;
}
}
}

return $this->mailer->Send();
$success = $this->mailer->send();

foreach ($tmpAttachmentFiles as $tmpFile) {
@unlink($tmpFile);
}

return $success;
}

/**
Expand Down
47 changes: 47 additions & 0 deletions libraries/src/Mail/TransportConfigurableMailerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

/**
* Joomla! Content Management System
*
* @copyright (C) 2026 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\CMS\Mail;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
* Provides a common interface to send emails through an SMTP or sendmail service.
*
* @since __DEPLOY_VERSION__
*/
interface TransportConfigurableMailerInterface
{
/**
* Use SMTP for sending the email.
*
* @param string $auth SMTP Authentication [optional]
* @param string $host SMTP Host [optional]
* @param string $user SMTP Username [optional]
* @param string $pass SMTP Password [optional]
* @param string $secure Use secure methods
* @param integer $port The SMTP port
*
* @return boolean True on success
*
* @since __DEPLOY_VERSION__
*/
public function useSmtp($auth = null, $host = null, $user = null, $pass = null, $secure = null, $port = 25);

/**
* Use sendmail for sending the email.
*
* @return boolean True on success
*
* @since __DEPLOY_VERSION__
*/
public function useSendmail($sendmail = null);
}
2 changes: 2 additions & 0 deletions plugins/system/tasknotification/services/provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Mail\MailerFactoryInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Database\DatabaseInterface;
Expand Down Expand Up @@ -40,6 +41,7 @@ public function register(Container $container): void
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
$plugin->setMailerFactory($container->get(MailerFactoryInterface::class));

return $plugin;
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\UserGroupsHelper;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Mail\MailerFactoryAwareTrait;
use Joomla\CMS\Mail\MailTemplate;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\User\UserFactoryAwareTrait;
Expand Down Expand Up @@ -46,6 +47,7 @@ final class TaskNotification extends CMSPlugin implements SubscriberInterface
{
use DatabaseAwareTrait;
use UserFactoryAwareTrait;
use MailerFactoryAwareTrait;

/**
* The task notification form. This form is merged into the task item form by {@see
Expand Down Expand Up @@ -329,7 +331,7 @@ private function sendMail(string $template, array $data, string $attachment = ''
// Mail all matching users.
foreach ($users as $user) {
try {
$mailer = new MailTemplate($template, $app->getLanguage()->getTag());
$mailer = new MailTemplate($template, $app->getLanguage()->getTag(), $this->getMailerFactory()->createMailer());
$mailer->addTemplateData($data);
$mailer->addRecipient($user->email);

Expand Down
Loading