Skip to main content

(18) Notifications and Emails

- Support Ticket Tutorial -

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.

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
    Set Value Type to: Text
  • Set Name to: SMTP Port
    Set Type to: Number
    Set Default value to: 587
  • Set Name to: SMTP Login
    Set Type to: Text
  • Set Name to: SMTP Password
    Set Type to: Text
  • Set Name to: SMTP Email
    Set Type to: Text

Save changes: Ctrl + S

2. Add NuGet Package

At the top of the Config side panel, click Resources

  • Click Add Resource
  • Select “Add NuGet Package Assembly Resource”
  • Search for MailKit. Click + Add Package
  • Click OK
  • Save changes: Ctrl + S

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:
Click to expand the code block
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. Establish the Text for the Email

Go to Code > Templates. Create a new page.

  • Set Name of Template to: Text For Email
  • Verify that Template Code Language is set to: Html
  • Verify that Model Type is set to: Entity
  • Set Entity to: Ticket
  • Click Finish

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:
Click to expand the code block
@{
var url = App.Urls.EntityPages.Ticket_detail.Generate(Model.Id);
}

<html>
<head>
</head>

<body>
<p>Hi @Model.AssignedTo.Name,</p>
<p>Ticket <a href="@url">@Model.Title</a> has been assigned to you.</p>
<p>Please resolve the issue as quickly as possible.</p>
</body>
</html>
  • Click Finish

5. Establish Business Command to Send Email to Assigned Person

Go to Business > Commands. Create a new page.

  • Set Name to: Mail Assigned Person
  • Set Type to: Entity Command
  • Set Entity to: Ticket
  • Click Finish
  • Copy and paste the following:
Click to expand the code block
(ticket, db, ctx) =>
{
//this checks if the ticket is assigned to someone
if (ticket.AssignedTo == null)
{
App.Log.Warning($"The email could not be sent because the ticket {ticket.Title} is not assigned to anyone.");
return;
}

var content = App.Templates.TextForEmail.Render(ticket);
var subject = "Task Assigned";
var mail = ticket.AssignedTo.Email;

//this renders a template with ticket its called on
App.Templates.TextForEmail.Render(ticket);
//this sends email
Util.SmtpMailer.Default.SendHtmlEmail(mail, subject, content);
}

6. 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
  • Verify that Type is set to: Execute entity command
  • Set Entity Command to: Mail Assigned Person
  • In the UI section, set Button Label to: Send Email
  • Set Icon to: Envelope
  • Click OK

7. Release the App

8. Create a Production Instance

A Production instance should be created in order to send an email to an actual user.
If you have not yet created a Production instance, follow these steps:

  • Click + Create Instance
  • On the Create New Application Instance page, Set Instance Name to: Onboarding Production App
  • Verify that Application Version is set to the most recent release
  • Verify that Hosting is set to: Jetveo Cloud
  • Set Application Type to: Production
  • Verify that Authentication is set to: Jetveo account
  • Click Create

If you already have a Production instance, update it to the latest release.

9. 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