Merge pull request #6515 from nextcloud/templating-of-email-subjects

Allow templating of email subjects
This commit is contained in:
Joas Schilling 2017-10-18 15:41:18 +02:00 committed by GitHub
commit bea04d12d5
17 changed files with 204 additions and 127 deletions

View File

@ -380,8 +380,6 @@ class ShareByMailProvider implements IShareProvider {
\DateTime $expiration = null) {
$initiatorUser = $this->userManager->get($initiator);
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
$message = $this->mailer->createMessage();
$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [
@ -392,6 +390,7 @@ class ShareByMailProvider implements IShareProvider {
'shareWith' => $shareWith,
]);
$emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
$emailTemplate->addHeader();
$emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
@ -428,9 +427,7 @@ class ShareByMailProvider implements IShareProvider {
$emailTemplate->addFooter();
}
$message->setSubject($subject);
$message->setPlainBody($emailTemplate->renderText());
$message->setHtmlBody($emailTemplate->renderHtml());
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
}
@ -455,7 +452,6 @@ class ShareByMailProvider implements IShareProvider {
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
$subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
$plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
$htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
@ -468,6 +464,8 @@ class ShareByMailProvider implements IShareProvider {
'initiatorEmail' => $initiatorEmailAddress,
'shareWith' => $shareWith,
]);
$emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]));
$emailTemplate->addHeader();
$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
$emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart);
@ -491,9 +489,7 @@ class ShareByMailProvider implements IShareProvider {
}
$message->setTo([$shareWith]);
$message->setSubject($subject);
$message->setBody($emailTemplate->renderText(), 'text/plain');
$message->setHtmlBody($emailTemplate->renderHtml());
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
$this->createPasswordSendActivity($share, $shareWith, false);
@ -524,7 +520,6 @@ class ShareByMailProvider implements IShareProvider {
);
}
$subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);
$bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]);
$message = $this->mailer->createMessage();
@ -536,6 +531,7 @@ class ShareByMailProvider implements IShareProvider {
'shareWith' => $shareWith,
]);
$emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]));
$emailTemplate->addHeader();
$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
$emailTemplate->addBodyText($bodyPart);
@ -547,9 +543,7 @@ class ShareByMailProvider implements IShareProvider {
$message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
}
$message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
$message->setSubject($subject);
$message->setBody($emailTemplate->renderText(), 'text/plain');
$message->setHtmlBody($emailTemplate->renderHtml());
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
$this->createPasswordSendActivity($share, $shareWith, true);

View File

@ -835,26 +835,14 @@ class ShareByMailProviderTest extends TestCase {
->expects($this->once())
->method('addFooter')
->with('UnitTestCloud - Testing like 1990');
$message
$template
->expects($this->once())
->method('setSubject')
->willReturn('Mrs. Owner User shared »file.txt« with you');
$template
->expects($this->once())
->method('renderText')
->willReturn('Text Render');
->with('Mrs. Owner User shared »file.txt« with you');
$message
->expects($this->once())
->method('setPlainBody')
->with('Text Render');
$template
->expects($this->once())
->method('renderHtml')
->willReturn('HTML Render');
$message
->expects($this->once())
->method('setHtmlBody')
->with('HTML Render');
->method('useTemplate')
->with($template);
$this->mailer
->expects($this->once())
->method('send')
@ -936,26 +924,14 @@ class ShareByMailProviderTest extends TestCase {
->expects($this->once())
->method('addFooter')
->with('');
$message
$template
->expects($this->once())
->method('setSubject')
->willReturn('Mr. Initiator User shared »file.txt« with you');
$template
->expects($this->once())
->method('renderText')
->willReturn('Text Render');
->with('Mr. Initiator User shared »file.txt« with you');
$message
->expects($this->once())
->method('setPlainBody')
->with('Text Render');
$template
->expects($this->once())
->method('renderHtml')
->willReturn('HTML Render');
$message
->expects($this->once())
->method('setHtmlBody')
->with('HTML Render');
->method('useTemplate')
->with($template);
$this->mailer
->expects($this->once())
->method('send')

View File

@ -309,6 +309,7 @@ class LostController extends Controller {
'link' => $link,
]);
$emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
$emailTemplate->addHeader();
$emailTemplate->addHeading($this->l10n->t('Password reset'));
@ -327,10 +328,8 @@ class LostController extends Controller {
try {
$message = $this->mailer->createMessage();
$message->setTo([$email => $user->getUID()]);
$message->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
$message->setPlainBody($emailTemplate->renderText());
$message->setHtmlBody($emailTemplate->renderHtml());
$message->setFrom([$this->from => $this->defaults->getName()]);
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
} catch (\Exception $e) {
throw new \Exception($this->l10n->t(

View File

@ -226,6 +226,7 @@ return array(
'OCP\\Lockdown\\ILockdownManager' => $baseDir . '/lib/public/Lockdown/ILockdownManager.php',
'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => $baseDir . '/lib/public/Mail/IMessage.php',
'OCP\\Migration\\BigIntMigration' => $baseDir . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => $baseDir . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php',

View File

@ -256,6 +256,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\Lockdown\\ILockdownManager' => __DIR__ . '/../../..' . '/lib/public/Lockdown/ILockdownManager.php',
'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/IMessage.php',
'OCP\\Migration\\BigIntMigration' => __DIR__ . '/../../..' . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php',

View File

@ -50,6 +50,8 @@ class EMailTemplate implements IEMailTemplate {
/** @var array */
protected $data;
/** @var string */
protected $subject = '';
/** @var string */
protected $htmlBody = '';
/** @var string */
@ -358,6 +360,15 @@ EOF;
$this->data = $data;
}
/**
* Sets the subject of the email
*
* @param string $subject
*/
public function setSubject($subject) {
$this->subject = $subject;
}
/**
* Adds a header to the email
*/
@ -595,6 +606,15 @@ EOF;
$this->plainBody .= str_replace('<br>', PHP_EOL, $text);
}
/**
* Returns the rendered email subject as string
*
* @return string
*/
public function renderSubject() {
return $this->subject;
}
/**
* Returns the rendered HTML email as string
*

View File

@ -29,6 +29,7 @@ use OCP\IURLGenerator;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\ILogger;
use OCP\Mail\IMessage;
/**
* Class Mailer provides some basic functions to create a mail message that can be used in combination with
@ -84,7 +85,7 @@ class Mailer implements IMailer {
/**
* Creates a new message object that can be passed to send()
*
* @return Message
* @return IMessage
*/
public function createMessage() {
return new Message(new \Swift_Message());
@ -124,13 +125,13 @@ class Mailer implements IMailer {
* Send the specified message. Also sets the from address to the value defined in config.php
* if no-one has been passed.
*
* @param Message $message Message to send
* @param IMessage|Message $message Message to send
* @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and
* therefore should be considered
* @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address
* has been supplied.)
*/
public function send(Message $message) {
public function send(IMessage $message) {
$debugMode = $this->config->getSystemValue('mail_smtpdebug', false);
if (empty($message->getFrom())) {

View File

@ -23,6 +23,8 @@
namespace OC\Mail;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMessage;
use Swift_Message;
/**
@ -30,7 +32,7 @@ use Swift_Message;
*
* @package OC\Mail
*/
class Message {
class Message implements IMessage {
/** @var Swift_Message */
private $swiftMessage;
@ -250,4 +252,15 @@ class Message {
$this->swiftMessage->setBody($body, $contentType);
return $this;
}
/**
* @param IEMailTemplate $emailTemplate
* @return $this
*/
public function useTemplate(IEMailTemplate $emailTemplate) {
$this->setSubject($emailTemplate->renderSubject());
$this->setPlainBody($emailTemplate->renderText());
$this->setHtmlBody($emailTemplate->renderHtml());
return $this;
}
}

View File

@ -702,7 +702,6 @@ class Manager implements IManager {
\DateTime $expiration = null) {
$initiatorUser = $this->userManager->get($initiator);
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$subject = $l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
$message = $this->mailer->createMessage();
@ -714,6 +713,7 @@ class Manager implements IManager {
'shareWith' => $shareWith,
]);
$emailTemplate->setSubject($l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
$emailTemplate->addHeader();
$emailTemplate->addHeading($l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
$text = $l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
@ -750,9 +750,7 @@ class Manager implements IManager {
$emailTemplate->addFooter();
}
$message->setSubject($subject);
$message->setPlainBody($emailTemplate->renderText());
$message->setHtmlBody($emailTemplate->renderHtml());
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
}

View File

@ -52,6 +52,15 @@ namespace OCP\Mail;
*/
interface IEMailTemplate {
/**
* Sets the subject of the email
*
* @param string $subject
*
* @since 13.0.0
*/
public function setSubject($subject);
/**
* Adds a header to the email
*
@ -130,6 +139,15 @@ interface IEMailTemplate {
*/
public function addFooter($text = '');
/**
* Returns the rendered email subject as string
*
* @return string
*
* @since 13.0.0
*/
public function renderSubject();
/**
* Returns the rendered HTML email as string
*

View File

@ -23,7 +23,6 @@
*/
namespace OCP\Mail;
use OC\Mail\Message;
/**
* Class IMailer provides some basic functions to create a mail message that can be used in combination with
@ -34,7 +33,7 @@ use OC\Mail\Message;
* $mailer = \OC::$server->getMailer();
* $message = $mailer->createMessage();
* $message->setSubject('Your Subject');
* $message->setFrom(['cloud@domain.org' => 'ownCloud Notifier']);
* $message->setFrom(['cloud@domain.org' => 'Nextcloud Notifier']);
* $message->setTo(['recipient@domain.org' => 'Recipient']);
* $message->setPlainBody('The message text');
* $message->setHtmlBody('The <strong>message</strong> text');
@ -49,7 +48,7 @@ interface IMailer {
/**
* Creates a new message object that can be passed to send()
*
* @return Message
* @return IMessage
* @since 8.1.0
*/
public function createMessage();
@ -68,14 +67,14 @@ interface IMailer {
* Send the specified message. Also sets the from address to the value defined in config.php
* if no-one has been passed.
*
* @param Message $message Message to send
* @param IMessage $message Message to send
* @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and
* therefore should be considered
* @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address
* has been supplied.)
* @since 8.1.0
*/
public function send(Message $message);
public function send(IMessage $message);
/**
* Checks if an e-mail address is valid

View File

@ -0,0 +1,84 @@
<?php
/**
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Mail;
/**
* Class Message
*
* @package OCP\Mail
* @since 13.0.0
*/
interface IMessage {
/**
* Set the from address of this message.
*
* If no "From" address is used \OC\Mail\Mailer will use mail_from_address and mail_domain from config.php
*
* @param array $addresses Example: array('sender@domain.org', 'other@domain.org' => 'A name')
* @return $this
* @since 13.0.0
*/
public function setFrom(array $addresses);
/**
* Set the Reply-To address of this message
*
* @param array $addresses
* @return $this
* @since 13.0.0
*/
public function setReplyTo(array $addresses);
/**
* Set the to addresses of this message.
*
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
* @return $this
* @since 13.0.0
*/
public function setTo(array $recipients);
/**
* Set the CC recipients of this message.
*
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
* @return $this
* @since 13.0.0
*/
public function setCc(array $recipients);
/**
* Set the BCC recipients of this message.
*
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
* @return $this
* @since 13.0.0
*/
public function setBcc(array $recipients);
/**
* @param IEMailTemplate $emailTemplate
* @return $this
* @since 13.0.0
*/
public function useTemplate(IEMailTemplate $emailTemplate);
}

View File

@ -151,6 +151,7 @@ class MailSettingsController extends Controller {
'displayname' => $displayName,
]);
$template->setSubject($this->l10n->t('Email setting test'));
$template->addHeader();
$template->addHeading($this->l10n->t('Well done, %s!', [$displayName]));
$template->addBodyText($this->l10n->t('If you received this email, the email configuration seems to be correct.'));
@ -158,9 +159,7 @@ class MailSettingsController extends Controller {
$message = $this->mailer->createMessage();
$message->setTo([$email => $displayName]);
$message->setSubject($this->l10n->t('Email setting test'));
$message->setHtmlBody($template->renderHtml());
$message->setPlainBody($template->renderText());
$message->useTemplate($template);
$errors = $this->mailer->send($message);
if (!empty($errors)) {
throw new \RuntimeException($this->l10n->t('Email could not be sent. Check your mail server log'));

View File

@ -122,6 +122,8 @@ class Hooks {
'emailAddress' => $user->getEMailAddress(),
'instanceUrl' => $instanceUrl,
]);
$template->setSubject($this->l->t('Password for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
$template->addHeader();
$template->addHeading($this->l->t('Password changed for %s', [$user->getDisplayName()]), false);
$template->addBodyText($text . ' ' . $this->l->t('If you did not request this, please contact an administrator.'));
@ -130,10 +132,7 @@ class Hooks {
$message = $this->mailer->createMessage();
$message->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
$message->setSubject($this->l->t('Password for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
$message->setBody($template->renderText(), 'text/plain');
$message->setHtmlBody($template->renderHtml());
$message->useTemplate($template);
$this->mailer->send($message);
}
}
@ -193,6 +192,8 @@ class Hooks {
'oldEMailAddress' => $oldMailAddress,
'instanceUrl' => $instanceUrl,
]);
$template->setSubject($this->l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
$template->addHeader();
$template->addHeading($this->l->t('Email address changed for %s', [$user->getDisplayName()]), false);
$template->addBodyText($text . ' ' . $this->l->t('If you did not request this, please contact an administrator.'));
@ -204,10 +205,7 @@ class Hooks {
$message = $this->mailer->createMessage();
$message->setTo([$oldMailAddress => $user->getDisplayName()]);
$message->setSubject($this->l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceUrl]));
$message->setBody($template->renderText(), 'text/plain');
$message->setHtmlBody($template->renderHtml());
$message->useTemplate($template);
$this->mailer->send($message);
}
}

View File

@ -124,6 +124,7 @@ class NewUserMailHelper {
'resetTokenGenerated' => $generatePasswordResetToken,
]);
$emailTemplate->setSubject($this->l10n->t('Your %s account was created', [$this->themingDefaults->getName()]));
$emailTemplate->addHeader();
if ($displayName === $userId) {
$emailTemplate->addHeading($this->l10n->t('Welcome aboard'));
@ -159,10 +160,8 @@ class NewUserMailHelper {
IEMailTemplate $emailTemplate) {
$message = $this->mailer->createMessage();
$message->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
$message->setSubject($this->l10n->t('Your %s account was created', [$this->themingDefaults->getName()]));
$message->setHtmlBody($emailTemplate->renderHtml());
$message->setPlainBody($emailTemplate->renderText());
$message->setFrom([$this->fromAddress => $this->themingDefaults->getName()]);
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
}
}

View File

@ -324,20 +324,9 @@ class LostControllerTest extends \Test\TestCase {
->with(['test@example.com' => 'ExistingUser']);
$message
->expects($this->at(1))
->method('setSubject')
->with(' password reset');
$message
->expects($this->at(2))
->method('setPlainBody')
->with('text body');
$message
->expects($this->at(3))
->method('setHtmlBody')
->with('HTML body');
$message
->expects($this->at(4))
->method('setFrom')
->with(['lostpassword-noreply@localhost' => null]);
$emailTemplate = $this->createMock(IEMailTemplate::class);
$emailTemplate->expects($this->any())
->method('renderHtml')
@ -345,6 +334,12 @@ class LostControllerTest extends \Test\TestCase {
$emailTemplate->expects($this->any())
->method('renderText')
->willReturn('text body');
$message
->expects($this->at(2))
->method('useTemplate')
->with($emailTemplate);
$this->mailer
->expects($this->at(0))
->method('createEMailTemplate')
@ -407,20 +402,9 @@ class LostControllerTest extends \Test\TestCase {
->with(['test@example.com' => 'ExistingUser']);
$message
->expects($this->at(1))
->method('setSubject')
->with(' password reset');
$message
->expects($this->at(2))
->method('setPlainBody')
->with('text body');
$message
->expects($this->at(3))
->method('setHtmlBody')
->with('HTML body');
$message
->expects($this->at(4))
->method('setFrom')
->with(['lostpassword-noreply@localhost' => null]);
$emailTemplate = $this->createMock(IEMailTemplate::class);
$emailTemplate->expects($this->any())
->method('renderHtml')
@ -428,6 +412,12 @@ class LostControllerTest extends \Test\TestCase {
$emailTemplate->expects($this->any())
->method('renderText')
->willReturn('text body');
$message
->expects($this->at(2))
->method('useTemplate')
->with($emailTemplate);
$this->mailer
->expects($this->at(0))
->method('createEMailTemplate')
@ -484,20 +474,9 @@ class LostControllerTest extends \Test\TestCase {
->with(['test@example.com' => 'ExistingUser']);
$message
->expects($this->at(1))
->method('setSubject')
->with(' password reset');
$message
->expects($this->at(2))
->method('setPlainBody')
->with('text body');
$message
->expects($this->at(3))
->method('setHtmlBody')
->with('HTML body');
$message
->expects($this->at(4))
->method('setFrom')
->with(['lostpassword-noreply@localhost' => null]);
$emailTemplate = $this->createMock(IEMailTemplate::class);
$emailTemplate->expects($this->any())
->method('renderHtml')
@ -505,6 +484,12 @@ class LostControllerTest extends \Test\TestCase {
$emailTemplate->expects($this->any())
->method('renderText')
->willReturn('text body');
$message
->expects($this->at(2))
->method('useTemplate')
->with($emailTemplate);
$this->mailer
->expects($this->at(0))
->method('createEMailTemplate')

View File

@ -620,26 +620,18 @@ EOF;
->expects($this->at(0))
->method('setTo')
->with(['recipient@example.com' => 'John Doe']);
$this->defaults
->expects($this->exactly(2))
->method('getName')
->willReturn('TestCloud');
$message
->expects($this->at(1))
->method('setSubject')
->with('Your TestCloud account was created');
$message
->expects($this->at(2))
->method('setHtmlBody')
->with($emailTemplate->renderHtml());
$message
->expects($this->at(3))
->method('setPlainBody')
->with($emailTemplate->renderText());
$message
->expects($this->at(4))
->method('setFrom')
->with(['no-reply@nextcloud.com' => 'TestCloud']);
$message
->expects($this->at(2))
->method('useTemplate')
->with($emailTemplate);
$this->defaults
->expects($this->exactly(1))
->method('getName')
->willReturn('TestCloud');
$this->mailer
->expects($this->once())
->method('createMessage')