Enhance e-mail invitations
- Built-in e-mail template - More template placeholders
This commit is contained in:
@@ -234,9 +234,25 @@ invite:
|
|||||||
# The display name used in the e-mail
|
# The display name used in the e-mail
|
||||||
name: "Matrix Identity"
|
name: "Matrix Identity"
|
||||||
|
|
||||||
# The MIME content to send, UTF-8 expected
|
# The E-mail template to use.
|
||||||
|
#
|
||||||
|
# The template is expected to be a full e-mail body, including client headers, using MIME and UTF-8 encoding.
|
||||||
|
# The following headers will be set by mxisd directly and should not be present in the template:
|
||||||
|
# - From
|
||||||
|
# - To
|
||||||
|
# - Date
|
||||||
|
# - Message-Id
|
||||||
|
# - X-Mailer
|
||||||
#
|
#
|
||||||
# The following placeholders are possible:
|
# The following placeholders are possible:
|
||||||
# - %SENDER_DISPLAY_NAME%
|
# - %DOMAIN% Domain name as per server.name config item
|
||||||
# - %ROOM_NAME%
|
# - %DOMAIN_PRETTY% Word capitalize version of the domain. e.g. example.org -> Example.org
|
||||||
contentPath: "/absolute/path/to/file"
|
# - %FROM_EMAIL% Value of this section's email config item
|
||||||
|
# - %FROM_NAME% Value of this section's name config item
|
||||||
|
# - %SENDER_ID% Matrix ID of the invitation sender
|
||||||
|
# - %SENDER_NAME% Display name of the invitation sender, empty if not available
|
||||||
|
# - %SENDER_NAME_OR_ID% Value of %SENDER_NAME% or, if empty, value of %SENDER_ID%
|
||||||
|
# - %ROOM_ID% ID of the room where the invitation took place
|
||||||
|
# - %ROOM_NAME% Name of the room, empty if not available
|
||||||
|
# - %ROOM_NAME_OR_ID% Value of %ROOM_NAME% or, if empty, value of %ROOM_ID%
|
||||||
|
template: "/absolute/path/to/file"
|
||||||
|
@@ -42,7 +42,7 @@ public class EmailSenderConfig {
|
|||||||
private String password;
|
private String password;
|
||||||
private String email;
|
private String email;
|
||||||
private String name;
|
private String name;
|
||||||
private String contentPath;
|
private String template;
|
||||||
|
|
||||||
public String getHost() {
|
public String getHost() {
|
||||||
return host;
|
return host;
|
||||||
@@ -100,12 +100,12 @@ public class EmailSenderConfig {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getContentPath() {
|
public String getTemplate() {
|
||||||
return contentPath;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContentPath(String contentPath) {
|
public void setTemplate(String template) {
|
||||||
this.contentPath = contentPath;
|
this.template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@@ -117,14 +117,18 @@ public class EmailSenderConfig {
|
|||||||
log.info("Login: {}", getLogin());
|
log.info("Login: {}", getLogin());
|
||||||
log.info("Has password: {}", StringUtils.isBlank(getPassword()));
|
log.info("Has password: {}", StringUtils.isBlank(getPassword()));
|
||||||
log.info("E-mail: {}", getEmail());
|
log.info("E-mail: {}", getEmail());
|
||||||
if (StringUtils.isBlank(getContentPath())) {
|
if (!StringUtils.startsWith(getTemplate(), "classpath:")) {
|
||||||
log.warn("invite.sender.contentPath is empty! Will not send invites");
|
if (StringUtils.isBlank(getTemplate())) {
|
||||||
} else {
|
log.warn("invite.sender.template is empty! Will not send invites");
|
||||||
File cp = new File(getContentPath()).getAbsoluteFile();
|
} else {
|
||||||
log.info("Content path: {}", cp.getAbsolutePath());
|
File cp = new File(getTemplate()).getAbsoluteFile();
|
||||||
if (!cp.exists() || !cp.isFile() || !cp.canRead()) {
|
log.info("Template: {}", cp.getAbsolutePath());
|
||||||
log.warn(getContentPath() + " does not exist, is not a file or cannot be read");
|
if (!cp.exists() || !cp.isFile() || !cp.canRead()) {
|
||||||
|
log.warn(getTemplate() + " does not exist, is not a file or cannot be read");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.info("Template: Built-in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -137,7 +137,7 @@ public class InvitationManager {
|
|||||||
|
|
||||||
ThreePid pid = new ThreePid(invitation.getMedium(), invitation.getAddress());
|
ThreePid pid = new ThreePid(invitation.getMedium(), invitation.getAddress());
|
||||||
|
|
||||||
log.info("Storing invite for {}:{} from {} in room {}", pid.getMedium(), pid.getAddress(), invitation.getSender(), invitation.getRoomId());
|
log.info("Handling invite for {}:{} from {} in room {}", pid.getMedium(), pid.getAddress(), invitation.getSender(), invitation.getRoomId());
|
||||||
if (invitations.containsKey(pid)) {
|
if (invitations.containsKey(pid)) {
|
||||||
log.info("Invite is already pending for {}:{}, returning data", pid.getMedium(), pid.getAddress());
|
log.info("Invite is already pending for {}:{}, returning data", pid.getMedium(), pid.getAddress());
|
||||||
return invitations.get(pid);
|
return invitations.get(pid);
|
||||||
|
@@ -22,13 +22,17 @@ package io.kamax.mxisd.invitation.sender;
|
|||||||
|
|
||||||
import com.sun.mail.smtp.SMTPTransport;
|
import com.sun.mail.smtp.SMTPTransport;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.config.invite.sender.EmailSenderConfig;
|
import io.kamax.mxisd.config.invite.sender.EmailSenderConfig;
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.lang.WordUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@@ -37,7 +41,6 @@ import javax.mail.MessagingException;
|
|||||||
import javax.mail.Session;
|
import javax.mail.Session;
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -51,6 +54,12 @@ public class EmailInviteSender implements IInviteSender {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private EmailSenderConfig cfg;
|
private EmailSenderConfig cfg;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ServerConfig srvCfg;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationContext app;
|
||||||
|
|
||||||
private Session session;
|
private Session session;
|
||||||
private InternetAddress sender;
|
private InternetAddress sender;
|
||||||
|
|
||||||
@@ -77,16 +86,30 @@ public class EmailInviteSender implements IInviteSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String templateBody = IOUtils.toString(new FileInputStream(cfg.getContentPath()), StandardCharsets.UTF_8);
|
String domainPretty = WordUtils.capitalizeFully(srvCfg.getName());
|
||||||
templateBody =
|
String senderName = invite.getInvite().getProperties().getOrDefault("sender_display_name", "");
|
||||||
templateBody.replace("%SENDER_DISPLAY_NAME%", invite.getInvite().getProperties().get("sender_display_name"))
|
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId());
|
||||||
.replace("%ROOM_NAME%", invite.getInvite().getProperties().get("room_name"));
|
String roomName = invite.getInvite().getProperties().getOrDefault("room_name", "");
|
||||||
|
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId());
|
||||||
|
|
||||||
|
String templateBody = IOUtils.toString(app.getResource(cfg.getTemplate()).getInputStream(), StandardCharsets.UTF_8);
|
||||||
|
templateBody = templateBody.replace("%DOMAIN%", srvCfg.getName());
|
||||||
|
templateBody = templateBody.replace("%DOMAIN_PRETTY%", domainPretty);
|
||||||
|
templateBody = templateBody.replace("%FROM_EMAIL%", cfg.getEmail());
|
||||||
|
templateBody = templateBody.replace("%FROM_NAME%", cfg.getName());
|
||||||
|
templateBody = templateBody.replace("%SENDER_ID%", invite.getInvite().getSender().getId());
|
||||||
|
templateBody = templateBody.replace("%SENDER_NAME%", senderName);
|
||||||
|
templateBody = templateBody.replace("%SENDER_NAME_OR_ID%", senderNameOrId);
|
||||||
|
templateBody = templateBody.replace("%ROOM_ID%", invite.getInvite().getRoomId());
|
||||||
|
templateBody = templateBody.replace("%ROOM_NAME%", roomName);
|
||||||
|
templateBody = templateBody.replace("%ROOM_NAME_OR_ID%", roomNameOrId);
|
||||||
|
|
||||||
MimeMessage msg = new MimeMessage(session, IOUtils.toInputStream(templateBody, StandardCharsets.UTF_8));
|
MimeMessage msg = new MimeMessage(session, IOUtils.toInputStream(templateBody, StandardCharsets.UTF_8));
|
||||||
msg.setHeader("X-Mailer", "mxisd"); // TODO set version
|
msg.setHeader("X-Mailer", "mxisd"); // TODO set version
|
||||||
msg.setSentDate(new Date());
|
msg.setSentDate(new Date());
|
||||||
msg.setFrom(sender);
|
msg.setFrom(sender);
|
||||||
msg.setRecipients(Message.RecipientType.TO, invite.getInvite().getAddress());
|
msg.setRecipients(Message.RecipientType.TO, invite.getInvite().getAddress());
|
||||||
|
msg.saveChanges();
|
||||||
|
|
||||||
log.info("Sending invite to {} via SMTP using {}:{}", invite.getInvite().getAddress(), cfg.getHost(), cfg.getPort());
|
log.info("Sending invite to {} via SMTP using {}:{}", invite.getInvite().getAddress(), cfg.getHost(), cfg.getPort());
|
||||||
SMTPTransport transport = (SMTPTransport) session.getTransport("smtp");
|
SMTPTransport transport = (SMTPTransport) session.getTransport("smtp");
|
||||||
|
@@ -1,3 +1,11 @@
|
|||||||
|
logging:
|
||||||
|
level:
|
||||||
|
org:
|
||||||
|
springframework: "WARN"
|
||||||
|
apache:
|
||||||
|
catalina: "WARN"
|
||||||
|
directory: "WARN"
|
||||||
|
|
||||||
server:
|
server:
|
||||||
port: 8090
|
port: 8090
|
||||||
|
|
||||||
@@ -18,16 +26,17 @@ lookup:
|
|||||||
ldap:
|
ldap:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
firebase:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
forward:
|
forward:
|
||||||
servers:
|
servers:
|
||||||
- "https://matrix.org"
|
- "https://matrix.org"
|
||||||
- "https://vector.im"
|
- "https://vector.im"
|
||||||
|
|
||||||
firebase:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
invite:
|
invite:
|
||||||
sender:
|
sender:
|
||||||
email:
|
email:
|
||||||
tls: 1
|
tls: 1
|
||||||
name: "mxisd Identity Server"
|
name: "mxisd Identity Server"
|
||||||
|
template: "classpath:email/invite-template.eml"
|
||||||
|
91
src/main/resources/email/invite-template.eml
Normal file
91
src/main/resources/email/invite-template.eml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
Subject: You have been invited to %DOMAIN%
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: multipart/alternative;
|
||||||
|
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
|
||||||
|
|
||||||
|
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Disposition: inline
|
||||||
|
|
||||||
|
Hi,
|
||||||
|
|
||||||
|
%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on
|
||||||
|
Matrix. To join the conversation, register an account on http://%DOMAIN%
|
||||||
|
|
||||||
|
|
||||||
|
About Matrix:
|
||||||
|
|
||||||
|
Matrix is an open standard for interoperable, decentralised, real-time communication
|
||||||
|
over IP, supporting group chat, file transfer, voice and video calling, integrations to
|
||||||
|
other apps, bridges to other communication systems and much more. It can be used to power
|
||||||
|
Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication.
|
||||||
|
|
||||||
|
Thanks,
|
||||||
|
|
||||||
|
%DOMAIN_PRETTY% Admins
|
||||||
|
|
||||||
|
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
|
||||||
|
Content-Type: multipart/related;
|
||||||
|
boundary="M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR";
|
||||||
|
type="text/html"
|
||||||
|
|
||||||
|
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Content-Disposition: inline
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page {
|
||||||
|
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
||||||
|
font-color: #454545;
|
||||||
|
font-size: 12pt;
|
||||||
|
width: 100%%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner {
|
||||||
|
width: 640px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table id="page">
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td id="inner">
|
||||||
|
<p>Hi,</p>
|
||||||
|
|
||||||
|
<p>%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on
|
||||||
|
Matrix. To join the conversation, register an account on <a href="http://%DOMAIN%">%DOMAIN%</a>.</p>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<p>About Matrix:</p>
|
||||||
|
|
||||||
|
<p>Matrix is an open standard for interoperable, decentralised, real-time communication
|
||||||
|
over IP, supporting group chat, file transfer, voice and video calling, integrations to
|
||||||
|
other apps, bridges to other communication systems and much more. It can be used to power
|
||||||
|
Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication.</p>
|
||||||
|
|
||||||
|
<p>Thanks,</p>
|
||||||
|
|
||||||
|
<p>%DOMAIN_PRETTY% Admins</p>
|
||||||
|
</td>
|
||||||
|
<td> </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
|
||||||
|
|
||||||
|
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--
|
Reference in New Issue
Block a user