Notifications and Emails
When Support Tickets are created, it would be good to have the ability to send an email to the person to whom the task has been assigned.
This feature will involve several steps, including downloading a NuGet package and verifying that there is an Assigned To attribute on the Ticket entity so that there will be a user to whom the email will be sent.
NOTE: This tutorial is built upon the foundation of the Support Ticket application.
1. Establish Email Settings
First you need to establish the configurations that will establish the email communication. Go to Config > App Features. Create a new page.
- Set
Name
to: Email Notifications - Click
Finish
to establish the settings
Add the following Config Items (i.e., click Add Config Item
):
- Set
Name
to: SMTP Server
SetValue Type
to: Text - Set
Name
to: SMTP Port
SetType
to: Number
SetDefault value
to: 587 - Set
Name
to: SMTP Login
SetType
to: Text - Set
Name
to: SMTP Password
SetType
to: Text - Set
Name
to: SMTP Email
SetType
to: Text
2. Add NuGet Package
At the top of the Config subsection, click Resources
- Click
Add Resource
- Select “Add NuGet Package Assembly Resource”
- Search for MailKit. Click
Add Package
- Click
OK
3. Establish Custom Code
Go to Code > Custom Code. Create a new page.
- Set
Name of file
to: StmpMailer - Click
Finish
- Copy and paste the following:
using System.Collections.Generic;
using MailKit.Net.Smtp;
using MimeKit;
using MimeKit.Text;
namespace Util
{
internal class SmtpMailer
{
private readonly bool isEnabled;
private readonly string smtpServer;
private readonly int smtpPort;
private readonly string account;
private readonly string password;
private readonly string email;
public static readonly SmtpMailer Default = new SmtpMailer(
App.Features.EmailNotifications.IsEnabled,
App.Features.EmailNotifications.Config.SMTPServer,
App.Features.EmailNotifications.Config.SMTPPort,
App.Features.EmailNotifications.Config.SMTPLogin,
App.Features.EmailNotifications.Config.SMTPPassword,
App.Features.EmailNotifications.Config.SMTPEmail
);
public SmtpMailer(string smtpServer, int smtpPort, string account, string password) : this(true, smtpServer, smtpPort, account, password, account)
{
}
private SmtpMailer(bool isEnabled, string smtpServer, int smtpPort, string account, string password, string email)
{
this.isEnabled = isEnabled;
this.smtpServer = smtpServer;
this.smtpPort = smtpPort;
this.account = account;
this.password = password;
this.email = email;
}
public void SendEmail(string recipient, string subject, string content)
{
SendInternal(new [ ] { recipient }, subject, content, false);
}
public void SendHtmlEmail(string recipient, string subject, string htmlContent)
{
SendInternal(new [ ] { recipient }, subject, htmlContent, true);
}
public void SendMassEmail(IEnumerable<string> recipients, string subject, string content)
{
SendInternal(recipients, subject, content, false);
}
public void SendMassHtmlEmail(IEnumerable<string> recipients, string subject, string htmlContent)
{
SendInternal(recipients, subject, htmlContent, true);
}
public EmailBuilder NewEmail()
{
return new EmailBuilder(this);
}
private void SendInternal(IEnumerable<string> recipients, string subject, string content, bool isHtml, IEnumerable<EmailAttachment> attachments = null, string customSender = null)
{
if (!this.isEnabled)
{
return;
}
attachments = attachments ?? Enumerable.Empty<EmailAttachment>();
try
{
using(var client = new SmtpClient())
{
var message = new MimeMessage();
message.From.Add(MailboxAddress.Parse(customSender ?? this.email));
foreach(var recipient in recipients)
{
message.To.Add(MailboxAddress.Parse(recipient));
}
message.Subject = subject;
var bodyBuilder = new BodyBuilder();
if (isHtml)
bodyBuilder.HtmlBody = content;
else
bodyBuilder.TextBody = content;
foreach(var a in attachments)
{
bodyBuilder.Attachments.Add(a.FileName, a.ContentProvider(), ContentType.Parse("application/octet-stream"));
}
message.Body = bodyBuilder.ToMessageBody();
client.Connect(smtpServer, smtpPort);
client.Authenticate(new System.Net.NetworkCredential(account, password));
client.Send(message);
client.Disconnect(true);
}
}
catch (Exception e)
{
throw new Exception($"Could not send email [{smtpServer}:{smtpPort}, {account}]", e);
}
}
public class EmailBuilder
{
private string subject = null;
private string content = null;
private string sender = null;
private string replyTo = null;
private bool contentIsHtml = false;
private readonly List<string> recipients = new List<string>();
private readonly List<EmailAttachment> attachments = new List<EmailAttachment>();
private readonly SmtpMailer mailer;
public EmailBuilder(SmtpMailer mailer) { this.mailer = mailer; }
public EmailBuilder AddRecipient(string recipient)
{
this.recipients.Add(recipient);
return this;
}
public EmailBuilder AddRecipients(IEnumerable<string> recipients)
{
this.recipients.AddRange(recipients);
return this;
}
public EmailBuilder SetSender(string senderEmail)
{
this.sender = senderEmail;
return this;
}
public EmailBuilder SetReplyTo(string replyToEmail)
{
this.replyTo = replyToEmail;
return this;
}
public EmailBuilder SetSubject(string subject)
{
this.subject = subject;
return this;
}
public EmailBuilder SetPlainContent(string content)
{
this.content = content;
this.contentIsHtml = false;
return this;
}
public EmailBuilder SetHtmlContent(string content)
{
this.content = content;
this.contentIsHtml = true;
return this;
}
public EmailBuilder AddAttachments(IEnumerable<IDocument> documents)
{
foreach(var d in documents)
{
AddAttachment(d);
}
return this;
}
public EmailBuilder AddAttachments(IEnumerable<IDocumentRevision> documents)
{
foreach(var d in documents)
{
AddAttachment(d);
}
return this;
}
public EmailBuilder AddAttachment(IDocument document)
{
return AddAttachment(document.GetLatestRevision());
}
public EmailBuilder AddAttachment(IDocumentRevision documentRev)
{
if (documentRev == null)
{
return this;
}
this.attachments.Add(new EmailAttachment()
{
FileName = documentRev.FileName,
ContentProvider = () => documentRev.Content
});
return this;
}
public void Send()
{
mailer.SendInternal(recipients, subject, content, contentIsHtml, attachments, sender);
}
}
protected class EmailAttachment
{
public Func<byte[ ]> ContentProvider
{
get;
set;
}
public string FileName
{
get;
set;
}
}
}
}
4. Add 'Assigned To' Attribute
- Go to Data > Entities and double-click Ticket
- In Data Definition > Attributes, click
Add New Attribute
- Set Attribute Name to: Assigned To
- Set Type to: User
- Click
Create Attributes
- Save changes:
Ctrl + S
5. Add 'Assigned To' Attribute to Ticket Detail Page
- Go to UI > Entity Pages and double-click Ticket Detail
- In the top Layout section, click
Edit Layout
- Add Assigned To to the layout.
6. Establish the Text for the Email
Go to Code > Templates. Create a new page.
- Set
Name of Template
to: Text For Email - Set
Template Code Language
to: Html - Set
Model Type
to: Entity - Set
Entity
to: Ticket
Note: Assigned To is used in the template text below. It was created as an attribute on the Ticket entity as part of the Workflow advanced topic. If not, it needs to be added to establish the user to whom the email will be sent.
- Copy and paste the following:
<html>
<head>
</head>
<body>
<table>
<tr>
<td>Ticket Assigned</td>
</tr>
<tr>
<td>Hi @Model.AssignedTo.Name ,</td>
</tr>
<tr>
<td>Ticket "@Model.Title" has been assigned to you.</td>
</tr>
<tr>
<td>Please resolve the issue as quickly as possible.</td>
</tr>
</table>
</body>
</html>
- Click
Finish
7. Establish Business Command to Send Email to Assigned Person
Go to Business > Commands. Create a new page.
- Set
Name
to: Main Assigned Person - Set
Type
to: Entity Command - Set
Entity
to: Ticket - Copy and paste the following:
(ticket, db, ctx) =>
{
var content = App.Templates.TextForEmail.Render(ticket);
var subject = "Task Assigned";
var mail = ticket.AssignedTo.Email;
//this renders a template with task its called on
App.Templates.TextForEmail.Render(ticket);
//this sends email
Util.SmtpMailer.Default.SendHtmlEmail(mail, subject, content);
}
8. Update the Ticket Detail Page
Go to UI > Entity Pages and double-click Ticket Detail
- At the top of the Layout section, click
Add Action
- Set
Entity Command
to: Main Assigned Person - In the UI section, set
Button Label
to: Send Email - Set
Icon
to: Envelope - Click
OK
9. Release the App
10. Create a Production Instance
A Production instance should be created in order to send an email to an actual user.
- Click
+ Create Instance
- On the Create New Application Instance page, Set Instance Name to: Onboarding Production App
- Set Hosting to: Jetveo Cloud
- Set Application Type to: Production
- Set Authentication to: Jetveo Login
- Set Application to: Onboarding App
- Set Application Version to the most recent release
- Click
Create
11. Configure Email Settings on the App Overview
- Click
Settings
- Click the
Features
tab - Mark Email Notifications
- Click
Configure
Input the following information (i.e. click Change
) according to your email server:
SMTP Server: TBD
SMTP Port: 587 (This should already be established.)
SMTP Login: TBD
SMTP Password: TBD
SMTP Email: TBD