mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-11-03 05:23:48 +00:00
docs: update email templating docs with complete variable reference
- adds new `Author` template var and injects it into all templates - make author fields empty for all automated system generated emails
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Templating
|
||||
|
||||
Templating in outgoing emails allows you to personalize content by embedding dynamic expressions like `{{ .Recipient.FullName }}`. These expressions reference fields from the conversation, contact, and recipient objects.
|
||||
Templating in outgoing emails allows you to personalize content by embedding dynamic expressions like `{{ .Recipient.FullName }}`. These expressions reference fields from the conversation, contact, recipient, and author objects.
|
||||
|
||||
## Outgoing Email Template Expressions
|
||||
|
||||
@@ -8,36 +8,53 @@ If you want to customize the look of outgoing emails, you can do so in the Admin
|
||||
|
||||
### Conversation Variables
|
||||
|
||||
| Variable | Value |
|
||||
| Variable | Value |
|
||||
|---------------------------------|--------------------------------------------------------|
|
||||
| {{ .Conversation.ReferenceNumber }} | The unique reference number of the conversation |
|
||||
| {{ .Conversation.Subject }} | The subject of the conversation |
|
||||
| {{ .Conversation.UUID }} | The unique identifier of the conversation |
|
||||
| {{ .Conversation.ReferenceNumber }} | The unique reference number of the conversation |
|
||||
| {{ .Conversation.Subject }} | The subject of the conversation |
|
||||
| {{ .Conversation.Priority }} | The priority level of the conversation |
|
||||
| {{ .Conversation.UUID }} | The unique identifier of the conversation |
|
||||
|
||||
### Contact Variables
|
||||
| Variable | Value |
|
||||
|
||||
| Variable | Value |
|
||||
|------------------------------|------------------------------------|
|
||||
| {{ .Contact.FirstName }} | First name of the contact/customer |
|
||||
| {{ .Contact.LastName }} | Last name of the contact/customer |
|
||||
| {{ .Contact.FullName }} | Full name of the contact/customer |
|
||||
| {{ .Contact.Email }} | Email address of the contact/customer |
|
||||
| {{ .Contact.FirstName }} | First name of the contact/customer |
|
||||
| {{ .Contact.LastName }} | Last name of the contact/customer |
|
||||
| {{ .Contact.FullName }} | Full name of the contact/customer |
|
||||
| {{ .Contact.Email }} | Email address of the contact/customer |
|
||||
|
||||
### Recipient Variables
|
||||
| Variable | Value |
|
||||
|--------------------------------|-----------------------------------|
|
||||
| {{ .Recipient.FirstName }} | First name of the recipient |
|
||||
| {{ .Recipient.LastName }} | Last name of the recipient |
|
||||
| {{ .Recipient.FullName }} | Full name of the recipient |
|
||||
| {{ .Recipient.Email }} | Email address of the recipient |
|
||||
|
||||
| Variable | Value |
|
||||
|--------------------------------|-----------------------------------|
|
||||
| {{ .Recipient.FirstName }} | First name of the recipient |
|
||||
| {{ .Recipient.LastName }} | Last name of the recipient |
|
||||
| {{ .Recipient.FullName }} | Full name of the recipient |
|
||||
| {{ .Recipient.Email }} | Email address of the recipient |
|
||||
|
||||
### Author Variables
|
||||
|
||||
| Variable | Value |
|
||||
|------------------------------|-----------------------------------|
|
||||
| {{ .Author.FirstName }} | First name of the message author |
|
||||
| {{ .Author.LastName }} | Last name of the message author |
|
||||
| {{ .Author.FullName }} | Full name of the message author |
|
||||
| {{ .Author.Email }} | Email address of the message author |
|
||||
|
||||
### Example outgoing email template
|
||||
|
||||
```html
|
||||
Dear {{ .Recipient.FirstName }}
|
||||
Dear {{ .Recipient.FirstName }},
|
||||
|
||||
{{ template "content" . }}
|
||||
|
||||
Best regards,
|
||||
{{ .Author.FullName }}
|
||||
---
|
||||
Reference: {{ .Conversation.ReferenceNumber }}
|
||||
```
|
||||
|
||||
Here, the `{{ template "content" . }}` serves as a placeholder for the body of the outgoing email. It will be replaced with the actual email content at the time of sending.
|
||||
|
||||
Similarly, the `{{ .Recipient.FirstName }}` expression will dynamically insert the recipient's first name when the email is sent.
|
||||
@@ -800,40 +800,30 @@ func (m *Manager) SendAssignedConversationEmail(userIDs []int, conversation mode
|
||||
|
||||
content, subject, err := m.template.RenderStoredEmailTemplate(template.TmplConversationAssigned,
|
||||
map[string]any{
|
||||
// Kept these lower case keys for backward compatibility.
|
||||
"conversation": map[string]string{
|
||||
"subject": conversation.Subject.String,
|
||||
"uuid": conversation.UUID,
|
||||
"reference_number": conversation.ReferenceNumber,
|
||||
"priority": conversation.Priority.String,
|
||||
},
|
||||
"agent": map[string]string{
|
||||
"full_name": agent.FullName(),
|
||||
},
|
||||
// Following the new structure.
|
||||
"Conversation": map[string]any{
|
||||
"ReferenceNumber": conversation.ReferenceNumber,
|
||||
"Subject": conversation.Subject.String,
|
||||
"Priority": conversation.Priority.String,
|
||||
"UUID": conversation.UUID,
|
||||
},
|
||||
"Agent": map[string]any{
|
||||
"FirstName": agent.FirstName,
|
||||
"LastName": agent.LastName,
|
||||
"FullName": agent.FullName(),
|
||||
"Email": agent.Email,
|
||||
},
|
||||
"Contact": map[string]any{
|
||||
"FirstName": conversation.Contact.FirstName,
|
||||
"LastName": conversation.Contact.LastName,
|
||||
"FullName": conversation.Contact.FullName(),
|
||||
"Email": conversation.Contact.Email,
|
||||
"Email": conversation.Contact.Email.String,
|
||||
},
|
||||
"Recipient": map[string]any{
|
||||
"FirstName": agent.FirstName,
|
||||
"LastName": agent.LastName,
|
||||
"FullName": agent.FullName(),
|
||||
"Email": agent.Email,
|
||||
"Email": agent.Email.String,
|
||||
},
|
||||
// Automated messages do not have an author.
|
||||
"Author": map[string]any{
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"FullName": "",
|
||||
"Email": "",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -847,8 +837,8 @@ func (m *Manager) SendAssignedConversationEmail(userIDs []int, conversation mode
|
||||
Provider: notifier.ProviderEmail,
|
||||
}
|
||||
if err := m.notifier.Send(nm); err != nil {
|
||||
m.lo.Error("error sending notification message", "error", err)
|
||||
return fmt.Errorf("sending notification message: %w", err)
|
||||
m.lo.Error("error sending notification message", "template", template.TmplConversationAssigned, "conversation_uuid", conversation.UUID, "error", err)
|
||||
return fmt.Errorf("sending notification message with template %s: %w", template.TmplConversationAssigned, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ func (m *Manager) sendOutgoingMessage(message models.Message) {
|
||||
}
|
||||
|
||||
// Render content in template
|
||||
if err := m.RenderContentInTemplate(inbox.Channel(), &message); err != nil {
|
||||
if err := m.RenderMessageInTemplate(inbox.Channel(), &message); err != nil {
|
||||
handleError(err, "error rendering content in template")
|
||||
return
|
||||
}
|
||||
@@ -213,8 +213,8 @@ func (m *Manager) sendOutgoingMessage(message models.Message) {
|
||||
}
|
||||
}
|
||||
|
||||
// RenderContentInTemplate renders message content in template.
|
||||
func (m *Manager) RenderContentInTemplate(channel string, message *models.Message) error {
|
||||
// RenderMessageInTemplate renders message content in template.
|
||||
func (m *Manager) RenderMessageInTemplate(channel string, message *models.Message) error {
|
||||
switch channel {
|
||||
case inbox.ChannelEmail:
|
||||
conversation, err := m.GetConversation(0, message.ConversationUUID)
|
||||
@@ -222,8 +222,14 @@ func (m *Manager) RenderContentInTemplate(channel string, message *models.Messag
|
||||
m.lo.Error("error fetching conversation", "uuid", message.ConversationUUID, "error", err)
|
||||
return fmt.Errorf("fetching conversation: %w", err)
|
||||
}
|
||||
// Pass conversation and contact data to the template for rendering any placeholders.
|
||||
message.Content, err = m.template.RenderEmailWithTemplate(map[string]any{
|
||||
|
||||
sender, err := m.userStore.GetAgent(message.SenderID, "")
|
||||
if err != nil {
|
||||
m.lo.Error("error fetching message sender user", "sender_id", message.SenderID, "error", err)
|
||||
return fmt.Errorf("fetching message sender user: %w", err)
|
||||
}
|
||||
|
||||
data := map[string]any{
|
||||
"Conversation": map[string]any{
|
||||
"ReferenceNumber": conversation.ReferenceNumber,
|
||||
"Subject": conversation.Subject.String,
|
||||
@@ -234,15 +240,33 @@ func (m *Manager) RenderContentInTemplate(channel string, message *models.Messag
|
||||
"FirstName": conversation.Contact.FirstName,
|
||||
"LastName": conversation.Contact.LastName,
|
||||
"FullName": conversation.Contact.FullName(),
|
||||
"Email": conversation.Contact.Email,
|
||||
"Email": conversation.Contact.Email.String,
|
||||
},
|
||||
"Recipient": map[string]any{
|
||||
"FirstName": conversation.Contact.FirstName,
|
||||
"LastName": conversation.Contact.LastName,
|
||||
"FullName": conversation.Contact.FullName(),
|
||||
"Email": conversation.Contact.Email,
|
||||
"Email": conversation.Contact.Email.String,
|
||||
},
|
||||
}, message.Content)
|
||||
"Author": map[string]any{
|
||||
"FirstName": sender.FirstName,
|
||||
"LastName": sender.LastName,
|
||||
"FullName": sender.FullName(),
|
||||
"Email": sender.Email.String,
|
||||
},
|
||||
}
|
||||
|
||||
// For automated replies set author fields to empty strings as the recipients will see name as System.
|
||||
if sender.IsSystemUser() {
|
||||
data["Author"] = map[string]any{
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"FullName": "",
|
||||
"Email": "",
|
||||
}
|
||||
}
|
||||
|
||||
message.Content, err = m.template.RenderEmailWithTemplate(data, message.Content)
|
||||
if err != nil {
|
||||
m.lo.Error("could not render email content using template", "id", message.ID, "error", err)
|
||||
return fmt.Errorf("could not render email content using template: %w", err)
|
||||
|
||||
@@ -15,8 +15,6 @@ const (
|
||||
|
||||
// Message represents a message to be sent as a notification.
|
||||
type Message struct {
|
||||
// Recipients of the message
|
||||
UserIDs []int
|
||||
// Email addresses of the recipients
|
||||
RecipientEmails []string
|
||||
// Subject of the message
|
||||
|
||||
@@ -674,18 +674,19 @@ func (m *Manager) SendNotification(scheduledNotification models.ScheduledSLANoti
|
||||
"Priority": "",
|
||||
"UUID": appliedSLA.ConversationUUID,
|
||||
},
|
||||
"Agent": map[string]any{
|
||||
"FirstName": agent.FirstName,
|
||||
"LastName": agent.LastName,
|
||||
"FullName": agent.FullName(),
|
||||
"Email": agent.Email,
|
||||
},
|
||||
"Recipient": map[string]any{
|
||||
"FirstName": agent.FirstName,
|
||||
"LastName": agent.LastName,
|
||||
"FullName": agent.FullName(),
|
||||
"Email": agent.Email,
|
||||
},
|
||||
// Automated emails do not have an author, so we set empty values.
|
||||
"Author": map[string]any{
|
||||
"FirstName": "",
|
||||
"LastName": "",
|
||||
"FullName": "",
|
||||
"Email": "",
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -80,3 +80,7 @@ func (u *User) FullName() string {
|
||||
func (u *User) HasAdminRole() bool {
|
||||
return slices.Contains(u.Roles, rmodels.RoleAdmin)
|
||||
}
|
||||
|
||||
func (u *User) IsSystemUser() bool {
|
||||
return u.Email.String == SystemUserEmail
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user