KEYCLOAK-4937 - convert time units in emails into human-friendly format
This commit is contained in:
parent
82815ff614
commit
a5f675d693
13 changed files with 281 additions and 46 deletions
|
@ -17,6 +17,16 @@
|
|||
|
||||
package org.keycloak.email.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.email.EmailException;
|
||||
|
@ -34,25 +44,19 @@ import org.keycloak.theme.FreeMarkerException;
|
|||
import org.keycloak.theme.FreeMarkerUtil;
|
||||
import org.keycloak.theme.Theme;
|
||||
import org.keycloak.theme.ThemeProvider;
|
||||
import org.keycloak.theme.beans.LinkExpirationFormatterMethod;
|
||||
import org.keycloak.theme.beans.MessageFormatterMethod;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
||||
|
||||
protected KeycloakSession session;
|
||||
/** authenticationSession can be null for some email sendings, it is filled only for email sendings performed as part of the authentication session (email verification, password reset, broker link etc.)! */
|
||||
/**
|
||||
* authenticationSession can be null for some email sendings, it is filled only for email sendings performed as part of the authentication session (email verification, password reset, broker link
|
||||
* etc.)!
|
||||
*/
|
||||
protected AuthenticationSessionModel authenticationSession;
|
||||
protected FreeMarkerUtil freeMarker;
|
||||
protected RealmModel realm;
|
||||
|
@ -109,8 +113,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
public void sendPasswordReset(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
addLinkInfoIntoAttributes(link, expirationInMinutes, attributes);
|
||||
|
||||
attributes.put("realmName", getRealmName());
|
||||
|
||||
|
@ -134,8 +137,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
public void sendConfirmIdentityBrokerLink(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
addLinkInfoIntoAttributes(link, expirationInMinutes, attributes);
|
||||
|
||||
attributes.put("realmName", getRealmName());
|
||||
|
||||
|
@ -154,8 +156,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
public void sendExecuteActions(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
addLinkInfoIntoAttributes(link, expirationInMinutes, attributes);
|
||||
|
||||
attributes.put("realmName", getRealmName());
|
||||
|
||||
|
@ -166,14 +167,31 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>(this.attributes);
|
||||
attributes.put("user", new ProfileBean(user));
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
addLinkInfoIntoAttributes(link, expirationInMinutes, attributes);
|
||||
|
||||
attributes.put("realmName", getRealmName());
|
||||
|
||||
send("emailVerificationSubject", "email-verification.ftl", attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add link info into template attributes.
|
||||
*
|
||||
* @param link to add
|
||||
* @param expirationInMinutes to add
|
||||
* @param attributes to add link info into
|
||||
*/
|
||||
protected void addLinkInfoIntoAttributes(String link, long expirationInMinutes, Map<String, Object> attributes) throws EmailException {
|
||||
attributes.put("link", link);
|
||||
attributes.put("linkExpiration", expirationInMinutes);
|
||||
try {
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
attributes.put("linkExpirationFormatter", new LinkExpirationFormatterMethod(getTheme().getMessages(locale), locale));
|
||||
} catch (IOException e) {
|
||||
throw new EmailException("Failed to template email", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void send(String subjectKey, String template, Map<String, Object> attributes) throws EmailException {
|
||||
send(subjectKey, Collections.emptyList(), template, attributes);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
* Copyright 2018 Red Hat Inc. and/or its affiliates and other contributors
|
||||
* as indicated by the @authors tag. All rights reserved.
|
||||
*/
|
||||
package org.keycloak.theme.beans;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import freemarker.template.TemplateMethodModelEx;
|
||||
import freemarker.template.TemplateModelException;
|
||||
|
||||
/**
|
||||
* Method used to format link expiration time period in emails.
|
||||
*
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
public class LinkExpirationFormatterMethod implements TemplateMethodModelEx {
|
||||
|
||||
protected final Properties messages;
|
||||
protected final Locale locale;
|
||||
|
||||
public LinkExpirationFormatterMethod(Properties messages, Locale locale) {
|
||||
this.messages = messages;
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Object exec(List arguments) throws TemplateModelException {
|
||||
Object val = arguments.isEmpty() ? null : arguments.get(0);
|
||||
if (val == null)
|
||||
return "";
|
||||
|
||||
try {
|
||||
//input value is in minutes, as defined in EmailTemplateProvider!
|
||||
return format(Long.parseLong(val.toString().trim()) * 60);
|
||||
} catch (NumberFormatException e) {
|
||||
// not a number, return it as is
|
||||
return val.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected String format(long valueInSeconds) {
|
||||
|
||||
String unitKey = "seconds";
|
||||
long value = valueInSeconds;
|
||||
|
||||
if (value > 0 && value % 60 == 0) {
|
||||
unitKey = "minutes";
|
||||
value = value / 60;
|
||||
if (value % 60 == 0) {
|
||||
unitKey = "hours";
|
||||
value = value / 60;
|
||||
if (value % 24 == 0) {
|
||||
unitKey = "days";
|
||||
value = value / 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value + " " + getUnitTextFromMessages(unitKey, value);
|
||||
}
|
||||
|
||||
protected String getUnitTextFromMessages(String unitKey, long value) {
|
||||
String msg = messages.getProperty("linkExpirationFormatter.timePeriodUnit." + unitKey + "." + value);
|
||||
if (msg != null)
|
||||
return msg;
|
||||
return messages.getProperty("linkExpirationFormatter.timePeriodUnit." + unitKey);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
* Copyright 2018 Red Hat Inc. and/or its affiliates and other contributors
|
||||
* as indicated by the @authors tag. All rights reserved.
|
||||
*/
|
||||
package org.keycloak.theme.beans;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import freemarker.template.TemplateModelException;
|
||||
|
||||
/**
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
public class LinkExpirationFormatterMethodTest {
|
||||
|
||||
protected static final Locale locale = Locale.ENGLISH;
|
||||
protected static final Properties messages = new Properties();
|
||||
static {
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.seconds.1", "second");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.seconds", "seconds");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.minutes.1", "minute");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.minutes.3", "minutes-3");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.minutes", "minutes");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.hours.1", "hour");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.hours", "hours");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.days.1", "day");
|
||||
messages.put("linkExpirationFormatter.timePeriodUnit.days", "days");
|
||||
}
|
||||
|
||||
protected List<Object> toList(Object... objects) {
|
||||
return Arrays.asList(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputtypes_null() throws TemplateModelException{
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("", tested.exec(Collections.emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputtypes_string_empty() throws TemplateModelException{
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("", tested.exec(toList("")));
|
||||
Assert.assertEquals(" ", tested.exec(toList(" ")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputtypes_string_number() throws TemplateModelException{
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("2 minutes", tested.exec(toList("2")));
|
||||
Assert.assertEquals("2 minutes", tested.exec(toList(" 2 ")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputtypes_string_notanumber() throws TemplateModelException{
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("ahoj", tested.exec(toList("ahoj")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputtypes_number() throws TemplateModelException{
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("5 minutes", tested.exec(toList(new Integer(5))));
|
||||
Assert.assertEquals("5 minutes", tested.exec(toList(new Long(5))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void format_second_zero() throws TemplateModelException {
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("0 seconds", tested.exec(toList(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void format_minute_one() throws TemplateModelException {
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("1 minute", tested.exec(toList(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void format_minute_more() throws TemplateModelException {
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("2 minutes", tested.exec(toList(2)));
|
||||
//test support for languages with more plurals depending on the value
|
||||
Assert.assertEquals("3 minutes-3", tested.exec(toList(3)));
|
||||
Assert.assertEquals("5 minutes", tested.exec(toList(5)));
|
||||
Assert.assertEquals("24 minutes", tested.exec(toList(24)));
|
||||
Assert.assertEquals("59 minutes", tested.exec(toList(59)));
|
||||
Assert.assertEquals("61 minutes", tested.exec(toList(61)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void format_hour_one() throws TemplateModelException {
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("1 hour", tested.exec(toList(60)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void format_hour_more() throws TemplateModelException {
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("2 hours", tested.exec(toList(2 * 60)));
|
||||
Assert.assertEquals("5 hours", tested.exec(toList(5 * 60)));
|
||||
Assert.assertEquals("23 hours", tested.exec(toList(23 * 60)));
|
||||
Assert.assertEquals("25 hours", tested.exec(toList(25 * 60)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void format_day_one() throws TemplateModelException {
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("1 day", tested.exec(toList(60 * 24)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void format_day_more() throws TemplateModelException {
|
||||
LinkExpirationFormatterMethod tested = new LinkExpirationFormatterMethod(messages, locale);
|
||||
Assert.assertEquals("2 days", tested.exec(toList(2 * 24 * 60)));
|
||||
Assert.assertEquals("5 days", tested.exec(toList(5 * 24 * 60)));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -685,11 +685,11 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
assertTrue(body.getText().contains("Update Password"));
|
||||
assertTrue(body.getText().contains("your Admin-client-test account"));
|
||||
assertTrue(body.getText().contains("This link will expire within 720 minutes"));
|
||||
assertTrue(body.getText().contains("This link will expire within 12 hours"));
|
||||
|
||||
assertTrue(body.getHtml().contains("Update Password"));
|
||||
assertTrue(body.getHtml().contains("your Admin-client-test account"));
|
||||
assertTrue(body.getHtml().contains("This link will expire within 720 minutes"));
|
||||
assertTrue(body.getHtml().contains("This link will expire within 12 hours"));
|
||||
|
||||
String link = MailUtils.getPasswordResetEmailLink(body);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
${msg("emailVerificationBodyHtml",link, linkExpiration, realmName)?no_esc}
|
||||
${msg("emailVerificationBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))?no_esc}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
<html>
|
||||
<body>
|
||||
${msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText)?no_esc}
|
||||
${msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration))?no_esc}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
${msg("identityProviderLinkBodyHtml", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration)?no_esc}
|
||||
${msg("identityProviderLinkBodyHtml", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration, linkExpirationFormatter(linkExpiration))?no_esc}
|
||||
</body>
|
||||
</html>
|
|
@ -1,5 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
${msg("passwordResetBodyHtml",link, linkExpiration, realmName)?no_esc}
|
||||
${msg("passwordResetBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))?no_esc}
|
||||
</body>
|
||||
</html>
|
|
@ -1,18 +1,18 @@
|
|||
emailVerificationSubject=Verify email
|
||||
emailVerificationBody=Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you didn''t create this account, just ignore this message.
|
||||
emailVerificationBodyHtml=<p>Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address</p><p><a href="{0}">Link to e-mail address verification</a></p><p>This link will expire within {1} minutes.</p><p>If you didn''t create this account, just ignore this message.</p>
|
||||
emailVerificationBody=Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {3}.\n\nIf you didn''t create this account, just ignore this message.
|
||||
emailVerificationBodyHtml=<p>Someone has created a {2} account with this email address. If this was you, click the link below to verify your email address</p><p><a href="{0}">Link to e-mail address verification</a></p><p>This link will expire within {3}.</p><p>If you didn''t create this account, just ignore this message.</p>
|
||||
emailTestSubject=[KEYCLOAK] - SMTP test message
|
||||
emailTestBody=This is a test message
|
||||
emailTestBodyHtml=<p>This is a test message</p>
|
||||
identityProviderLinkSubject=Link {0}
|
||||
identityProviderLinkBody=Someone wants to link your "{1}" account with "{0}" account of user {2} . If this was you, click the link below to link accounts\n\n{3}\n\nThis link will expire within {4} minutes.\n\nIf you don''t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.
|
||||
identityProviderLinkBodyHtml=<p>Someone wants to link your <b>{1}</b> account with <b>{0}</b> account of user {2} . If this was you, click the link below to link accounts</p><p><a href="{3}">Link to confirm account linking</a></p><p>This link will expire within {4} minutes.</p><p>If you don''t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.</p>
|
||||
identityProviderLinkBody=Someone wants to link your "{1}" account with "{0}" account of user {2} . If this was you, click the link below to link accounts\n\n{3}\n\nThis link will expire within {5}.\n\nIf you don''t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.
|
||||
identityProviderLinkBodyHtml=<p>Someone wants to link your <b>{1}</b> account with <b>{0}</b> account of user {2} . If this was you, click the link below to link accounts</p><p><a href="{3}">Link to confirm account linking</a></p><p>This link will expire within {5}.</p><p>If you don''t want to link account, just ignore this message. If you link accounts, you will be able to login to {1} through {0}.</p>
|
||||
passwordResetSubject=Reset password
|
||||
passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {1} minutes.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">Link to reset credentials</a></p><p>This link will expire within {1} minutes.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
|
||||
passwordResetBody=Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.\n\n{0}\n\nThis link and code will expire within {3}.\n\nIf you don''t want to reset your credentials, just ignore this message and nothing will be changed.
|
||||
passwordResetBodyHtml=<p>Someone just requested to change your {2} account''s credentials. If this was you, click on the link below to reset them.</p><p><a href="{0}">Link to reset credentials</a></p><p>This link will expire within {3}.</p><p>If you don''t want to reset your credentials, just ignore this message and nothing will be changed.</p>
|
||||
executeActionsSubject=Update Your Account
|
||||
executeActionsBody=Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
|
||||
executeActionsBodyHtml=<p>Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.</p><p><a href="{0}">Link to account update</a></p><p>This link will expire within {1} minutes.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
|
||||
executeActionsBody=Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.\n\n{0}\n\nThis link will expire within {4}.\n\nIf you are unaware that your admin has requested this, just ignore this message and nothing will be changed.
|
||||
executeActionsBodyHtml=<p>Your administrator has just requested that you update your {2} account by performing the following action(s): {3}. Click on the link below to start this process.</p><p><a href="{0}">Link to account update</a></p><p>This link will expire within {4}.</p><p>If you are unaware that your admin has requested this, just ignore this message and nothing will be changed.</p>
|
||||
eventLoginErrorSubject=Login error
|
||||
eventLoginErrorBody=A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.
|
||||
eventLoginErrorBodyHtml=<p>A failed login attempt was detected to your account on {0} from {1}. If this was not you, please contact an admin.</p>
|
||||
|
@ -31,3 +31,17 @@ requiredAction.terms_and_conditions=Terms and Conditions
|
|||
requiredAction.UPDATE_PASSWORD=Update Password
|
||||
requiredAction.UPDATE_PROFILE=Update Profile
|
||||
requiredAction.VERIFY_EMAIL=Verify Email
|
||||
|
||||
# units for link expiration timeout formatting
|
||||
linkExpirationFormatter.timePeriodUnit.seconds=seconds
|
||||
linkExpirationFormatter.timePeriodUnit.seconds.1=second
|
||||
linkExpirationFormatter.timePeriodUnit.minutes=minutes
|
||||
linkExpirationFormatter.timePeriodUnit.minutes.1=minute
|
||||
#for language which have more unit plural forms depending on the value (eg. Czech and other Slavic langs) you can override unit text for some other values like this:
|
||||
#linkExpirationFormatter.timePeriodUnit.minutes.2=minuty
|
||||
#linkExpirationFormatter.timePeriodUnit.minutes.3=minuty
|
||||
#linkExpirationFormatter.timePeriodUnit.minutes.4=minuty
|
||||
linkExpirationFormatter.timePeriodUnit.hours=hours
|
||||
linkExpirationFormatter.timePeriodUnit.hours.1=hour
|
||||
linkExpirationFormatter.timePeriodUnit.days=days
|
||||
linkExpirationFormatter.timePeriodUnit.days.1=day
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<#ftl output_format="plainText">
|
||||
${msg("emailVerificationBody",link, linkExpiration, realmName)}
|
||||
${msg("emailVerificationBody",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))}
|
|
@ -1,4 +1,4 @@
|
|||
<#ftl output_format="plainText">
|
||||
<#assign requiredActionsText><#if requiredActions??><#list requiredActions><#items as reqActionItem>${msg("requiredAction.${reqActionItem}")}<#sep>, </#items></#list><#else></#if></#assign>
|
||||
|
||||
${msg("executeActionsBody",link, linkExpiration, realmName, requiredActionsText)}
|
||||
${msg("executeActionsBody",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration))}
|
|
@ -1,2 +1,2 @@
|
|||
<#ftl output_format="plainText">
|
||||
${msg("identityProviderLinkBody", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration)}
|
||||
${msg("identityProviderLinkBody", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration, linkExpirationFormatter(linkExpiration))}
|
|
@ -1,2 +1,2 @@
|
|||
<#ftl output_format="plainText">
|
||||
${msg("passwordResetBody",link, linkExpiration, realmName)}
|
||||
${msg("passwordResetBody",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))}
|
Loading…
Reference in a new issue