Sana Assistant (online)
Table of Contents

Implementing email sending

From this article, you will learn how to send emails from an extension.

Please use the following reference articles to find more details on extensions infrastructure:

Start with a new project

Create a new add-on project as described in the add-on development tutorial.

It can be payment, shipping, customer export, product export and etc extension.

Webstore and Admin mail templates

Sana supports 2 types of mail templates:

  • Webstore mail templates, the primary recipients of which are the webstore customers.
  • Admin mail templates, the recipients of which are admin users.

The configuration of the mail template will differ depending on who the recipient of the email is.

Webstore mail templates

Adding webstore mail templates

Add a MailTemplates folder to the root directory of the extension.

Add a MailTemplates.resx file to this folder.

Create records for email body and subject in the format [templateName]_Body and [templateName]_Subject.

Body and Subject in resx file

Adding views for webstore mail templates

If the body of the letter is simple, the developer of the extension may not add a custom view, so the default one will be used. Otherwise, the developer can add a custom view to create advanced mail template. To do this, add the Views folder to the root directory of the extension. This folder should contain the _ViewImports.cshtml file and the MailTemplates folder. The MailTemplates folder should contain main view with name in [templateName].cshtml format and partial views if needed.

Mail templates directory structure may look like the following:

Sana.Extensions.CustomExtension 📂/
├── MailTemplates 📁/
│   └── MailTemplates.resx
└── Views 📂/
    ├── MailTemplates 📂/
    │   ├── Order 📂/
    │   │   └── OrderDetails.cshtml
    │   └── FollowEmail.cshtml
    └── _ViewImports.cshtml

FollowEmail.cshtml

@model string
@using System.Collections.Specialized
@using Sana.Extensions.Models.Orders
@using System.IO
@using System.Text.Encodings.Web

@{
    var orderDetailsModel = (Order)ViewData["OrderDetails"];

    async Task<string> PartialToStringAsync(IHtmlHelper<string> html, string viewName, object model)
    {
        var result = await html.PartialAsync(viewName, model);

        using (var writer = new StringWriter())
        {
            result.WriteTo(writer, HtmlEncoder.Default);
            return writer.ToString();
        }
    }

    async Task<NameValueCollection> CreateReplacementTags()
    {
        var replacementTags = new NameValueCollection();
        replacementTags.Add("ORDERDETAILS", await PartialToStringAsync(Html, @"Order\OrderDetails", orderDetailsModel));

        return replacementTags;
    }
}

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <title></title>
</head>
<body style="font-family:Verdana, Arial, Helvetica, sans-serif; font-size: 12px; background-color:White;">
    <table cellpadding="0" cellspacing="0" width="700">
        <tr>
            <td>
                @Html.Raw(Model.ReplaceTags(await CreateReplacementTags()))
            </td>
        </tr>
    </table>
</body>
</html>

OrderDetails.cshtml

@model Order
@using Sana.Extensions.Models.Orders

<table>
    <tbody>
        <tr>
            <td>@Extension.SimpleText("OrderId")</td>
            <td>@Model.Id</td>
        </tr>
        <tr>
            <td>@Extension.SimpleText("TotalPrice")</td>
            <td>@Extension.FormatAsPrice(Model.TotalPrice)</td>
        </tr>
    </tbody>
</table>

_ViewImports.cshtml

@inject Sana.Extensions.ViewFeatures.IExtensionViewHelper Extension

More details about IExtensionViewHelper can be found in this article.

Implement webstore email sending functionality

An example of a simple implementation of sending webstore email:

var replacementTags = new NameValueCollection();
replacementTags.Add("LINK", "example.com");
Api.MailManager.SendEmail(new List<string>() { "some@sana-commerce.com" }, "FollowEmail", replacementTags);

An example of the advanced implementation of sending webstore email with a partial view for a payment extension:

public override NextAction StartPayment(PaymentStartContext context)
{
    ...
    var replacementTags = new NameValueCollection();
    replacementTags.Add("LINK", "example.com");
    replacementTags.Add("ORDERDETAILS", "[ORDERDETAILS]");
    var additionalData = new Dictionary<string, object>();
    additionalData.Add("OrderDetails", context.Order);
    Api.MailManager.SendEmail(new[] { some@sana-commerce.com }, "FollowEmail", replacementTags, additionalData);
    ...
}

Admin mail templates

Adding admin mail templates

Add a AdminMailTemplates folder to the root directory of the extension.

Add a AdminMailTemplates.resx file to this folder.

Create records for email body and subject in the format [templateName]_Body and [templateName]_Subject.

Adding views for admin mail templates

If the body of the letter is simple, the developer of the extension may not add a custom view, so the default one will be used. Otherwise, the developer can add a custom view to create advanced mail template. To do this, add the Views folder to the root directory of the extension. This folder should contain the _ViewImports.cshtml file and the AdminMailTemplates folder. The AdminMailTemplates folder should contain main view with name in [templateName].cshtml format and partial views if needed.

Mail templates directory structure may look like the following:

Sana.Extensions.CustomExtension 📂/
├── AdminMailTemplates 📁/
│   └── AdminMailTemplates.resx
└── Views 📂/
    ├── AdminMailTemplates 📂/
    │   ├── Order 📂/
    │   │   └── OrderDetails.cshtml
    │   └── OrderAdminNotification.cshtml
    └── _ViewImports.cshtml

OrderAdminNotification.cshtml

@model string
@using System.Collections.Specialized
@using Sana.Extensions.Models.Orders
@using System.IO
@using System.Text.Encodings.Web

@{
    var orderDetailsModel = (Order)ViewData["OrderDetails"];

    async Task<string> PartialToStringAsync(IHtmlHelper<string> html, string viewName, object model)
    {
        var result = await html.PartialAsync(viewName, model);

        using (var writer = new StringWriter())
        {
            result.WriteTo(writer, HtmlEncoder.Default);
            return writer.ToString();
        }
    }

    async Task<NameValueCollection> CreateReplacementTags()
    {
        var replacementTags = new NameValueCollection();
        replacementTags.Add("ORDERDETAILS", await PartialToStringAsync(Html, @"Order\OrderDetails", orderDetailsModel));

        return replacementTags;
    }
}

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <title></title>
</head>
<body style="font-family:Verdana, Arial, Helvetica, sans-serif; font-size: 12px; background-color:White;">
    <table cellpadding="0" cellspacing="0" width="700">
        <tr>
            <td>
                @Html.Raw(Model.ReplaceTags(await CreateReplacementTags()))
            </td>
        </tr>
    </table>
</body>
</html>

OrderDetails.cshtml

@model Order
@using Sana.Extensions.Models.Orders

<table>
    <tbody>
        <tr>
            <td>@Extension.AdminText("OrderId")</td>
            <td>@Model.Id</td>
        </tr>
        <tr>
            <td>@Extension.AdminText("TotalPrice")</td>
            <td>@Extension.FormatAsPrice(Model.TotalPrice)</td>
        </tr>
    </tbody>
</table>

_ViewImports.cshtml

@inject Sana.Extensions.ViewFeatures.IExtensionViewHelper Extension

More details about IExtensionViewHelper can be found in this article.

Implement admin email sending functionality

An example of a simple implementation of sending admin email to the customer service email addresses:

var replacementTags = new NameValueCollection();
replacementTags.Add("LINK", "example.com");
Api.MailManager.SendAdminEmailToCustomerService("OrderAdminNotification", replacementTags);

An example of the advanced implementation of sending admin email with a partial view for a payment extension:

public override NextAction StartPayment(PaymentStartContext context)
{
    ...
    var replacementTags = new NameValueCollection();
    replacementTags.Add("LINK", "example.com");
    replacementTags.Add("ORDERDETAILS", "[ORDERDETAILS]");
    var additionalData = new Dictionary<string, object>();
    additionalData.Add("OrderDetails", context.Order);
    Api.MailManager.SendAdminEmailToCustomerService("OrderAdminNotification", replacementTags, additionalData);
    ...
}

See also