diff --git a/administrator/components/com_config/src/Model/ApplicationModel.php b/administrator/components/com_config/src/Model/ApplicationModel.php index 30e8ecd5cbfe0..2501624877b32 100644 --- a/administrator/components/com_config/src/Model/ApplicationModel.php +++ b/administrator/components/com_config/src/Model/ApplicationModel.php @@ -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); @@ -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'); diff --git a/libraries/src/Mail/FormatConfigurableMailerInterface.php b/libraries/src/Mail/FormatConfigurableMailerInterface.php new file mode 100644 index 0000000000000..43bba17ff7721 --- /dev/null +++ b/libraries/src/Mail/FormatConfigurableMailerInterface.php @@ -0,0 +1,33 @@ + + * @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); +} diff --git a/libraries/src/Mail/Mail.php b/libraries/src/Mail/Mail.php index 0b5d037654449..873bec62d7d02 100644 --- a/libraries/src/Mail/Mail.php +++ b/libraries/src/Mail/Mail.php @@ -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. diff --git a/libraries/src/Mail/MailTemplate.php b/libraries/src/Mail/MailTemplate.php index c495457f23598..fa8445891f990 100644 --- a/libraries/src/Mail/MailTemplate.php +++ b/libraries/src/Mail/MailTemplate.php @@ -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; @@ -117,7 +117,7 @@ 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; @@ -125,6 +125,7 @@ public function __construct($templateId, $language, ?Mail $mailer = null) 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(); } } @@ -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')); @@ -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); @@ -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) { @@ -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); } } @@ -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; } /** diff --git a/libraries/src/Mail/TransportConfigurableMailerInterface.php b/libraries/src/Mail/TransportConfigurableMailerInterface.php new file mode 100644 index 0000000000000..d7cefff650cb8 --- /dev/null +++ b/libraries/src/Mail/TransportConfigurableMailerInterface.php @@ -0,0 +1,47 @@ + + * @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); +} diff --git a/plugins/system/tasknotification/services/provider.php b/plugins/system/tasknotification/services/provider.php index e7fd317ab1510..5f29fae3ac986 100644 --- a/plugins/system/tasknotification/services/provider.php +++ b/plugins/system/tasknotification/services/provider.php @@ -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; @@ -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; }) diff --git a/plugins/system/tasknotification/src/Extension/TaskNotification.php b/plugins/system/tasknotification/src/Extension/TaskNotification.php index a0f5a0269b24b..e2c183dd88b24 100644 --- a/plugins/system/tasknotification/src/Extension/TaskNotification.php +++ b/plugins/system/tasknotification/src/Extension/TaskNotification.php @@ -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; @@ -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 @@ -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);