Support reply to name

This commit is contained in:
Matej Bačo
2026-04-20 11:47:06 +02:00
parent 06a2d48deb
commit bc592903db
10 changed files with 166 additions and 69 deletions
+56 -25
View File
@@ -2295,8 +2295,8 @@ Http::post('/v1/account/tokens/magic-url')
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
$replyToEmail = '';
$replyToName = '';
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
@@ -2305,8 +2305,11 @@ Http::post('/v1/account/tokens/magic-url')
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
if (!empty($smtp['replyToEmail'])) {
$replyToEmail = $smtp['replyToEmail'];
}
if (!empty($smtp['replyToName'])) {
$replyToName = $smtp['replyToName'];
}
$queueForMails
@@ -2323,8 +2326,11 @@ Http::post('/v1/account/tokens/magic-url')
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
if (!empty($customTemplate['replyToEmail'])) {
$replyToEmail = $customTemplate['replyToEmail'];
}
if (!empty($customTemplate['replyToName'])) {
$replyToName = $customTemplate['replyToName'];
}
$body = $customTemplate['message'] ?? '';
@@ -2332,7 +2338,8 @@ Http::post('/v1/account/tokens/magic-url')
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpReplyToEmail($replyToEmail)
->setSmtpReplyToName($replyToName)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
@@ -2611,7 +2618,8 @@ Http::post('/v1/account/tokens/email')
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
$replyToEmail = '';
$replyToName = '';
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
@@ -2620,8 +2628,11 @@ Http::post('/v1/account/tokens/email')
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
if (!empty($smtp['replyToEmail'])) {
$replyToEmail = $smtp['replyToEmail'];
}
if (!empty($smtp['replyToName'])) {
$replyToName = $smtp['replyToName'];
}
$queueForMails
@@ -2638,8 +2649,11 @@ Http::post('/v1/account/tokens/email')
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
if (!empty($customTemplate['replyToEmail'])) {
$replyToEmail = $customTemplate['replyToEmail'];
}
if (!empty($customTemplate['replyToName'])) {
$replyToName = $customTemplate['replyToName'];
}
$body = $customTemplate['message'] ?? '';
@@ -2647,7 +2661,8 @@ Http::post('/v1/account/tokens/email')
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpReplyToEmail($replyToEmail)
->setSmtpReplyToName($replyToName)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
@@ -3743,7 +3758,8 @@ Http::post('/v1/account/recovery')
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
$replyToEmail = '';
$replyToName = '';
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
@@ -3752,8 +3768,11 @@ Http::post('/v1/account/recovery')
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
if (!empty($smtp['replyToEmail'])) {
$replyToEmail = $smtp['replyToEmail'];
}
if (!empty($smtp['replyToName'])) {
$replyToName = $smtp['replyToName'];
}
$queueForMails
@@ -3770,8 +3789,11 @@ Http::post('/v1/account/recovery')
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
if (!empty($customTemplate['replyToEmail'])) {
$replyToEmail = $customTemplate['replyToEmail'];
}
if (!empty($customTemplate['replyToName'])) {
$replyToName = $customTemplate['replyToName'];
}
$body = $customTemplate['message'] ?? '';
@@ -3779,7 +3801,8 @@ Http::post('/v1/account/recovery')
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpReplyToEmail($replyToEmail)
->setSmtpReplyToName($replyToName)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
@@ -4060,7 +4083,8 @@ Http::post('/v1/account/verifications/email')
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
$replyToEmail = '';
$replyToName = '';
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
@@ -4069,8 +4093,11 @@ Http::post('/v1/account/verifications/email')
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
if (!empty($smtp['replyToEmail'])) {
$replyToEmail = $smtp['replyToEmail'];
}
if (!empty($smtp['replyToName'])) {
$replyToName = $smtp['replyToName'];
}
$queueForMails
@@ -4087,8 +4114,11 @@ Http::post('/v1/account/verifications/email')
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
if (!empty($customTemplate['replyToEmail'])) {
$replyToEmail = $customTemplate['replyToEmail'];
}
if (!empty($customTemplate['replyToName'])) {
$replyToName = $customTemplate['replyToName'];
}
$body = $customTemplate['message'] ?? '';
@@ -4096,7 +4126,8 @@ Http::post('/v1/account/verifications/email')
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpReplyToEmail($replyToEmail)
->setSmtpReplyToName($replyToName)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
+12 -6
View File
@@ -772,7 +772,9 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale')
'message' => $message,
'subject' => $localeObj->getText('emails.' . $type . '.subject'),
'senderEmail' => '',
'senderName' => ''
'senderName' => '',
'replyToEmail' => '',
'replyToName' => ''
];
}
@@ -873,10 +875,11 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
->param('message', '', new Text(0), 'Template message')
->param('senderName', '', new Text(255, 0), 'Name of the email sender', true)
->param('senderEmail', '', new Email(), 'Email of the sender', true)
->param('replyTo', '', new Email(), 'Reply to email', true)
->param('replyToEmail', '', new Email(), 'Reply to email', true)
->param('replyToName', '', new Text(255, 0), 'Reply to name', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $type, string $locale, string $subject, string $message, string $senderName, string $senderEmail, string $replyTo, Response $response, Database $dbForPlatform) {
->action(function (string $projectId, string $type, string $locale, string $subject, string $message, string $senderName, string $senderEmail, string $replyToEmail, string $replyToName, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
@@ -889,7 +892,8 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
'senderName' => $senderName,
'senderEmail' => $senderEmail,
'subject' => $subject,
'replyTo' => $replyTo,
'replyToEmail' => $replyToEmail,
'replyToName' => $replyToName,
'message' => $message
];
@@ -901,7 +905,8 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
'senderName' => $senderName,
'senderEmail' => $senderEmail,
'subject' => $subject,
'replyTo' => $replyTo,
'replyToEmail' => $replyToEmail,
'replyToName' => $replyToName,
'message' => $message
]), Response::MODEL_EMAIL_TEMPLATE);
});
@@ -1026,7 +1031,8 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale')
'senderName' => $template['senderName'],
'senderEmail' => $template['senderEmail'],
'subject' => $template['subject'],
'replyTo' => $template['replyTo'],
'replyToEmail' => $template['replyToEmail'] ?? '',
'replyToName' => $template['replyToName'] ?? '',
'message' => $template['message']
]), Response::MODEL_EMAIL_TEMPLATE);
});
+2 -1
View File
@@ -131,7 +131,8 @@ class Mails extends Listener
->setSmtpUsername($smtp['username'] ?? '')
->setSmtpPassword($smtp['password'] ?? '')
->setSmtpSecure($smtp['secure'] ?? '')
->setSmtpReplyTo($customTemplate['replyTo'] ?? $smtp['replyTo'] ?? '')
->setSmtpReplyToEmail($customTemplate['replyToEmail'] ?? $smtp['replyToEmail'] ?? '')
->setSmtpReplyToName($customTemplate['replyToName'] ?? $smtp['replyToName'] ?? '')
->setSmtpSenderEmail($customTemplate['senderEmail'] ?? $smtp['senderEmail'] ?? System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM))
->setSmtpSenderName($customTemplate['senderName'] ?? $smtp['senderName'] ?? System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
}
+29 -7
View File
@@ -251,14 +251,26 @@ class Mail extends Event
}
/**
* Set SMTP reply to
* Set SMTP reply-to email
*
* @param string $replyTo
* @param string $email
* @return self
*/
public function setSmtpReplyTo(string $replyTo): self
public function setSmtpReplyToEmail(string $email): self
{
$this->smtp['replyTo'] = $replyTo;
$this->smtp['replyToEmail'] = $email;
return $this;
}
/**
* Set SMTP reply-to name
*
* @param string $name
* @return self
*/
public function setSmtpReplyToName(string $name): self
{
$this->smtp['replyToName'] = $name;
return $this;
}
@@ -333,13 +345,23 @@ class Mail extends Event
}
/**
* Get SMTP reply to
* Get SMTP reply-to email
*
* @return string
*/
public function getSmtpReplyTo(): string
public function getSmtpReplyToEmail(): string
{
return $this->smtp['replyTo'] ?? '';
return $this->smtp['replyToEmail'] ?? '';
}
/**
* Get SMTP reply-to name
*
* @return string
*/
public function getSmtpReplyToName(): string
{
return $this->smtp['replyToName'] ?? '';
}
/**
@@ -253,7 +253,8 @@ class Create extends Action
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
$replyToEmail = '';
$replyToName = '';
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
@@ -262,8 +263,11 @@ class Create extends Action
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
if (!empty($smtp['replyToEmail'])) {
$replyToEmail = $smtp['replyToEmail'];
}
if (!empty($smtp['replyToName'])) {
$replyToName = $smtp['replyToName'];
}
$queueForMails
@@ -280,8 +284,11 @@ class Create extends Action
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
if (!empty($customTemplate['replyToEmail'])) {
$replyToEmail = $customTemplate['replyToEmail'];
}
if (!empty($customTemplate['replyToName'])) {
$replyToName = $customTemplate['replyToName'];
}
$body = $customTemplate['message'] ?? '';
@@ -289,7 +296,8 @@ class Create extends Action
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpReplyToEmail($replyToEmail)
->setSmtpReplyToName($replyToName)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
@@ -104,7 +104,8 @@ class Create extends Action
$senderName = $paramSenderName ?: ($smtp['senderName'] ?? '');
$senderEmail = $paramSenderEmail ?: ($smtp['senderEmail'] ?? '');
$replyTo = $paramReplyTo ?: ($smtp['replyTo'] ?? '');
$replyToEmail = $paramReplyTo ?: ($smtp['replyToEmail'] ?? '');
$replyToName = $smtp['replyToName'] ?? '';
$host = $paramHost ?: ($smtp['host'] ?? '');
$port = $paramPort ?? ($smtp['port'] ?? '');
$username = $paramUsername ?: ($smtp['username'] ?? '');
@@ -127,13 +128,15 @@ class Create extends Action
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'SMTP port must be configured on the project to send a test email.');
}
$replyToEmail = !empty($replyTo) ? $replyTo : $senderEmail;
// Fallback to sender details when reply-to is not explicitly configured
$replyToEmailDisplay = !empty($replyToEmail) ? $replyToEmail : $senderEmail;
$replyToNameDisplay = !empty($replyToName) ? $replyToName : $senderName;
$subject = 'Custom SMTP email sample';
$template = Template::fromFile(APP_CE_CONFIG_DIR . '/locale/templates/email-smtp-test.tpl');
$template
->setParam('{{from}}', "{$senderName} ({$senderEmail})")
->setParam('{{replyTo}}', "{$senderName} ({$replyToEmail})")
->setParam('{{replyTo}}', "{$replyToNameDisplay} ({$replyToEmailDisplay})")
->setParam('{{logoUrl}}', $plan['logoUrl'] ?? APP_EMAIL_LOGO_URL)
->setParam('{{accentColor}}', $plan['accentColor'] ?? APP_EMAIL_ACCENT_COLOR)
->setParam('{{twitterUrl}}', $plan['twitterUrl'] ?? APP_SOCIAL_TWITTER)
@@ -149,7 +152,8 @@ class Create extends Action
->setSmtpUsername($username)
->setSmtpPassword($password)
->setSmtpSecure($secure)
->setSmtpReplyTo($replyTo)
->setSmtpReplyToEmail($replyToEmail)
->setSmtpReplyToName($replyToName)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName)
->setRecipient($email)
@@ -341,7 +341,8 @@ class Create extends Action
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = '';
$replyToEmail = '';
$replyToName = '';
if ($smtpEnabled) {
if (! empty($smtp['senderEmail'])) {
@@ -350,8 +351,11 @@ class Create extends Action
if (! empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (! empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
if (! empty($smtp['replyToEmail'])) {
$replyToEmail = $smtp['replyToEmail'];
}
if (! empty($smtp['replyToName'])) {
$replyToName = $smtp['replyToName'];
}
$queueForMails
@@ -368,8 +372,11 @@ class Create extends Action
if (! empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (! empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
if (! empty($customTemplate['replyToEmail'])) {
$replyToEmail = $customTemplate['replyToEmail'];
}
if (! empty($customTemplate['replyToName'])) {
$replyToName = $customTemplate['replyToName'];
}
$body = $customTemplate['message'] ?? '';
@@ -377,7 +384,8 @@ class Create extends Action
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpReplyToEmail($replyToEmail)
->setSmtpReplyToName($replyToName)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
+2 -2
View File
@@ -173,8 +173,8 @@ class Mails extends Action
$replyTo = $customMailOptions['replyToEmail'] ?? $replyTo;
$replyToName = $customMailOptions['replyToName'] ?? $replyToName;
} elseif (!empty($smtp)) {
$replyTo = !empty($smtp['replyTo']) ? $smtp['replyTo'] : ($smtp['senderEmail'] ?? $replyTo);
$replyToName = $smtp['senderName'] ?? $replyToName;
$replyTo = !empty($smtp['replyToEmail']) ? $smtp['replyToEmail'] : ($smtp['senderEmail'] ?? $replyTo);
$replyToName = !empty($smtp['replyToName']) ? $smtp['replyToName'] : ($smtp['senderName'] ?? $replyToName);
}
$attachments = null;
@@ -22,12 +22,18 @@ class TemplateEmail extends Template
'default' => '',
'example' => 'mail@appwrite.io',
])
->addRule('replyTo', [
->addRule('replyToEmail', [
'type' => self::TYPE_STRING,
'description' => 'Reply to email address',
'default' => '',
'example' => 'emails@appwrite.io',
])
->addRule('replyToName', [
'type' => self::TYPE_STRING,
'description' => 'Reply to name',
'default' => '',
'example' => 'Support Team',
])
->addRule('subject', [
'type' => self::TYPE_STRING,
'description' => 'Email subject',
+22 -11
View File
@@ -67,7 +67,8 @@ trait SMTPBase
$this->assertArrayHasKey('smtpEnabled', $response['body']);
$this->assertArrayHasKey('smtpSenderName', $response['body']);
$this->assertArrayHasKey('smtpSenderEmail', $response['body']);
$this->assertArrayHasKey('smtpReplyTo', $response['body']);
$this->assertArrayHasKey('smtpReplyToEmail', $response['body']);
$this->assertArrayHasKey('smtpReplyToName', $response['body']);
$this->assertArrayHasKey('smtpHost', $response['body']);
$this->assertArrayHasKey('smtpPort', $response['body']);
$this->assertArrayHasKey('smtpUsername', $response['body']);
@@ -115,14 +116,16 @@ trait SMTPBase
senderEmail: 'sender@example.com',
host: 'maildev',
port: 1025,
replyTo: 'reply@example.com',
replyToEmail: 'reply@example.com',
replyToName: 'Full Reply',
);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(true, $response['body']['smtpEnabled']);
$this->assertSame('Full Sender', $response['body']['smtpSenderName']);
$this->assertSame('sender@example.com', $response['body']['smtpSenderEmail']);
$this->assertSame('reply@example.com', $response['body']['smtpReplyTo']);
$this->assertSame('reply@example.com', $response['body']['smtpReplyToEmail']);
$this->assertSame('Full Reply', $response['body']['smtpReplyToName']);
$this->assertSame('maildev', $response['body']['smtpHost']);
$this->assertSame(1025, $response['body']['smtpPort']);
@@ -188,7 +191,8 @@ trait SMTPBase
$this->assertArrayHasKey('smtpEnabled', $response['body']);
$this->assertArrayHasKey('smtpSenderName', $response['body']);
$this->assertArrayHasKey('smtpSenderEmail', $response['body']);
$this->assertArrayHasKey('smtpReplyTo', $response['body']);
$this->assertArrayHasKey('smtpReplyToEmail', $response['body']);
$this->assertArrayHasKey('smtpReplyToName', $response['body']);
$this->assertArrayHasKey('smtpHost', $response['body']);
$this->assertArrayHasKey('smtpPort', $response['body']);
$this->assertArrayHasKey('smtpUsername', $response['body']);
@@ -279,7 +283,7 @@ trait SMTPBase
senderEmail: 'sender@example.com',
host: 'maildev',
port: 1025,
replyTo: 'not-an-email',
replyToEmail: 'not-an-email',
);
$this->assertSame(400, $response['headers']['status-code']);
@@ -664,15 +668,17 @@ trait SMTPBase
$senderName = 'SMTP Test Sender';
$senderEmail = 'smtptest@appwrite.io';
$replyToEmail = 'smtpreply@appwrite.io';
$replyToName = 'SMTP Reply Team';
$recipientEmail = 'smtpdelivery-' . \uniqid() . '@appwrite.io';
// Configure SMTP with replyTo and auth credentials
// Configure SMTP with reply-to and auth credentials
$response = $this->updateSMTPCredentials(
senderName: $senderName,
senderEmail: $senderEmail,
host: 'maildev',
port: 1025,
replyTo: $replyToEmail,
replyToEmail: $replyToEmail,
replyToName: $replyToName,
username: 'user',
password: 'password',
);
@@ -693,7 +699,7 @@ trait SMTPBase
$this->assertSame($senderEmail, $email['from'][0]['address']);
$this->assertSame($senderName, $email['from'][0]['name']);
$this->assertSame($replyToEmail, $email['replyTo'][0]['address']);
$this->assertSame($senderName, $email['replyTo'][0]['name']);
$this->assertSame($replyToName, $email['replyTo'][0]['name']);
$this->assertSame('Custom SMTP email sample', $email['subject']);
$this->assertStringContainsStringIgnoringCase('working correctly', $email['text']);
$this->assertStringContainsStringIgnoringCase('working correctly', $email['html']);
@@ -769,7 +775,8 @@ trait SMTPBase
string $senderEmail = '',
string $host = '',
int $port = 587,
?string $replyTo = null,
?string $replyToEmail = null,
?string $replyToName = null,
?string $username = null,
?string $password = null,
?string $secure = null,
@@ -792,8 +799,12 @@ trait SMTPBase
'port' => $port,
];
if (!\is_null($replyTo)) {
$params['replyTo'] = $replyTo;
if (!\is_null($replyToEmail)) {
$params['replyToEmail'] = $replyToEmail;
}
if (!\is_null($replyToName)) {
$params['replyToName'] = $replyToName;
}
if (!\is_null($username)) {