Merge pull request #10203 from appwrite/fix-templates

Fix: templates on `1.7.x`.
This commit is contained in:
Matej Bačo 2025-07-25 11:14:26 +02:00 committed by GitHub
commit 91fda66d78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 62 additions and 15 deletions

View file

@ -9,4 +9,4 @@
<p>If you have trouble with the sender's image, ensure it is set in the <a href="https://gravatar.com/">Gravatar database</a>.</p>
<p style="margin-block-end: 0;">Best regards,</p>
<p style="margin-block-start: 0;">Appwrtite team</p>
<p style="margin-block-start: 0;">Appwrite team</p>

View file

@ -14,7 +14,7 @@
<table border="0" cellspacing="0" cellpadding="0" style="padding-top: 10px; padding-bottom: 10px; margin-top: 32px">
<tr>
<td style="border-radius: 8px; display: block; width: 100%;">
<a class="mobile-full-width" rel="noopener" target="_blank" href="{{host}}{{path}}" style="font-size: 14px; font-family: Inter; color: #ffffff; text-decoration: none; background-color: #FD366E; border-radius: 8px; padding: 9px 14px; border: 1px solid #FD366E; display: inline-block; text-align:center; box-sizing: border-box;">Webhook settings</a>
<a class="mobile-full-width" rel="noopener" target="_blank" href="{{host}}{{path}}" style="font-size: 14px; font-family: Inter; color: #ffffff; text-decoration: none; background-color: #2D2D31; border-radius: 8px; padding: 9px 14px; border: 1px solid #414146; display: inline-block; text-align:center; box-sizing: border-box;">Webhook settings</a>
</td>
</tr>
</table>

View file

@ -133,6 +133,16 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
->setSmtpSenderName($senderName);
}
// session alerts should always have a client name!
$clientName = $session->getAttribute('clientName');
if (empty($clientName)) {
// fallback to the user agent and then unknown!
$userAgent = $session->getAttribute('userAgent');
$clientName = !empty($userAgent) ? $userAgent : 'UNKNOWN';
$session->setAttribute('clientName', $clientName);
}
$emailVariables = [
'direction' => $locale->getText('settings.direction'),
'date' => (new \DateTime())->format('F j'),

View file

@ -2175,7 +2175,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
->setSmtpSenderName($senderName)
->setRecipient($email)
->setName('')
->setbodyTemplate(__DIR__ . '/../../config/locale/templates/email-base-styled.tpl')
->setBodyTemplate(__DIR__ . '/../../config/locale/templates/email-base-styled.tpl')
->setBody($template->render())
->setVariables([])
->setSubject($subject)
@ -2268,16 +2268,53 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
$localeObj = new Locale($locale);
if (is_null($template)) {
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
/**
* different templates, different placeholders.
*/
$templateConfigs = [
'magicSession' => [
'file' => 'email-magic-url.tpl',
'placeholders' => ['optionButton', 'buttonText', 'optionUrl', 'clientInfo', 'securityPhrase']
],
'mfaChallenge' => [
'file' => 'email-mfa-challenge.tpl',
'placeholders' => ['description', 'clientInfo']
],
'otpSession' => [
'file' => 'email-otp.tpl',
'placeholders' => ['description', 'clientInfo', 'securityPhrase']
],
'sessionAlert' => [
'file' => 'email-session-alert.tpl',
'placeholders' => ['body', 'listDevice', 'listIpAddress', 'listCountry', 'footer']
],
];
// fallback to the base template.
$config = $templateConfigs[$type] ?? [
'file' => 'email-inner-base.tpl',
'placeholders' => ['buttonText', 'body', 'footer']
];
$templateString = file_get_contents(__DIR__ . '/../../config/locale/templates/' . $config['file']);
// We use `fromString` due to the replace above
$message = Template::fromString($templateString);
// Set type-specific parameters
foreach ($config['placeholders'] as $param) {
$escapeHtml = !in_array($param, ['clientInfo', 'body', 'footer', 'description']);
$message->setParam("{{{$param}}}", $localeObj->getText("emails.{$type}.{$param}"), escapeHtml: $escapeHtml);
}
$message
// common placeholders on all the templates
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
->setParam('{{footer}}', $localeObj->getText("emails.{$type}.footer"))
->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escapeHtml: false)
->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks"))
->setParam('{{buttonText}}', $localeObj->getText("emails.{$type}.buttonText"))
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"))
->setParam('{{direction}}', $localeObj->getText('settings.direction'));
$message = $message->render();
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"));
// `useContent: false` will strip new lines!
$message = $message->render(useContent: true);
$template = [
'message' => $message,

View file

@ -145,7 +145,7 @@ class Mail extends Event
* @param string $bodyTemplate
* @return self
*/
public function setbodyTemplate(string $bodyTemplate): self
public function setBodyTemplate(string $bodyTemplate): self
{
$this->bodyTemplate = $bodyTemplate;
@ -157,7 +157,7 @@ class Mail extends Event
*
* @return string
*/
public function getbodyTemplate(): string
public function getBodyTemplate(): string
{
return $this->bodyTemplate;
}

View file

@ -389,7 +389,7 @@ class Certificates extends Action
->setPreview($preview)
->setBody($body)
->setName('Appwrite Administrator')
->setbodyTemplate(__DIR__ . '/../../../../app/config/locale/templates/email-base-styled.tpl')
->setBodyTemplate(__DIR__ . '/../../../../app/config/locale/templates/email-base-styled.tpl')
->setVariables($emailVariables)
->setRecipient(System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')))
->trigger();

View file

@ -63,7 +63,7 @@ class Template extends View
*
* @throws Exception
*/
public function render($minify = true): string
public function render($minify = true, $useContent = false): string
{
if ($this->rendered) { // Don't render any template
return '';
@ -72,7 +72,7 @@ class Template extends View
if (\is_readable($this->path)) {
$template = \file_get_contents($this->path); // Include template file
} elseif (!empty($this->content)) {
$template = $this->print($this->content, self::FILTER_NL2P);
$template = !$useContent ? $this->print($this->content, self::FILTER_NL2P) : $this->content;
} else {
throw new Exception('"' . $this->path . '" template is not readable or not found');
}