Hide menu Last updated: Oct 12 2015

This topic describes how the Episerver Commerce notification/template engine automatically sends out email notifications for new orders, customer registrations, email forms, and so on. The notification/template engine generates types of templated content and creates personalized emails for customers regarding orders.

How it works

The template engine itself is based on the ASP.NET 2.0 Provider Model. You can customize or replace the engine without needing to access any of the framework source code. The default implementation is an XSL-based template provider, allowing for the creation of templates using XML Stylesheet Language (XSL).

See also Configuring Notifications for related information.

Classes in this topic are available in the following namespaces:

Key classes and files

  • TemplateService.cs. Loads the appropriate template provider and returns formatted text for the email.
  • CheckoutWizardModule.ascx. Uses the TemplateService to generate the text for order-related emails. See the SendEmails methods.
  • XslTemplateProvider.cs. Custom template provider included.
  • TemplateProvider.cs. Base class from which all template providers must inherit.

Quick overview

The web.config file of your public/admin sites contain this section:

<system.net>
  <mailSettings>E:\Template\en-us\order-purchaseorder-confirm.xsl.
    <smtp>
      <network host="localhost"/>
    </smtp>
  </mailSettings>
</system.net>

You can add a user name and password in the file and authentication for the SMTP server, if required. See MSDN for more information on configuring email settings.

Example: configuring email notifications

XML
<configuration>
  <configSections>
    <sectionGroup name="FrameworkProviders">
      ...
      <section name="templateService" type="Mediachase.Commerce.Engine.Template.TemplateProviderSection, Mediachase.Commerce"/>
    </sectionGroup>
    ...
  <FrameworkProviders>
    ...
    <providers>
      <add name="XslTemplateProvider" type="Mediachase.Commerce.Engine.Template.Providers.XslTemplateProvider, Mediachase.Commerce" applicationName="eCommerceFramework" templateSource="c:\templates\{0}\{1}.xsl"/>
    </providers>
    </templateService>
  </FrameworkProviders>

The templateSource attribute shows how to use the template name and CultureInfo parameters. Use CultureInfo for the folder name containing the templates for that language. The filename is specified as the template name with an .xsl extension.

An example of the resulting file path for a template

  • in English (CultureInfo name = "en-us"),
  • for the order confirmation (template name = "order-purchaseorder-confirm"),
  • where your templateSource setting is "E:\Template{0}{1}.xsl"

would be:

E:\Template\en-us\order-purchaseorder-confirm.xsl

 

Place the PublicLayer\Templates\en-us items in the directory in the template source. These are the template emails you need to customize.

Template service

An example of calling the TemplateService is in the CheckoutWizardModule control. Relevant data, including the PurchaseOrder object, are placed in a dictionary and passed to the TemplateService.Process() method with the name of the template, and a CultureInfo instance representing the desired/current culture. The Process() method returns the formatted custom text.

Example: sending out emails

C#
private void SendEmails(PurchaseOrder order, string email)
        {
            // Add input parameter
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("OrderGroup", order);

            // Send out emails
            // Create smtp client
            SmtpClient client = new SmtpClient();

            MailMessage msg = new MailMessage();
            msg.From = new MailAddress(StoreEmail, StoreTitle);
            msg.IsBodyHtml = true;

            // Send confirmation email
            msg.Subject = String.Format("{1}: Order Confirmation for {0}", order.CustomerName, StoreTitle);
            msg.To.Add(new MailAddress(email, order.CustomerName));
            msg.Body = TemplateService.Process("order-purchaseorder-confirm", Thread.CurrentThread.CurrentCulture, dic);

            // send email
            client.Send(msg);

            msg = new MailMessage();
            msg.From = new MailAddress(StoreEmail, StoreTitle);
            msg.IsBodyHtml = true;

            // Send notify email
            msg.Subject = String.Format("{1}: Order Notification {0}", order.TrackingNumber, StoreTitle);
            msg.To.Add(new MailAddress(StoreEmail, StoreTitle));
            msg.Body = TemplateService.Process("order-purchaseorder-notify", Thread.CurrentThread.CurrentCulture, dic);

            // send email
            client.Send(msg);
        }

The following code is taken from CheckoutWizard.ascx.cs in the front end, but you can apply it to the back end also. The template service replaces the placeholders with the proper text. You can also add your own files to be used with the template service.

Template provider

The XSL/XMLTemplate Provider renders templates using a set of XSL files and expects that all parameters passed in the dictionary context can be serialized to XML. The provider then looks in the path specified in the configuration file (templateSource property) for the specific XSL. If it cannot find the file in the language-specific folder, it tries to fall back and look in the "default" folder.

Example: XMLgenerated when PurchaseOrder is passed to the provider

XML
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="en-us/order-confirm.xsl" type="text/xsl"?>
<ContextDoc>
  <PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Created>2008-05-15T12:30:02.17</Created>
    <Modified>2008-05-15T12:30:02.17</Modified>
    <OrderForms>
      <OrderForm>
        <Created>2008-05-15T12:29:39.013</Created>
        <Modified>2008-05-15T12:29:39.013</Modified>
        <Shipments>
          <Shipment>
            <Created>2008-05-15T12:30:01.78</Created>
            <Modified>2008-05-15T12:30:01.78</Modified>
            <Discounts />
            <OrderGroupId>131</OrderGroupId>
            <OrderFormId>141</OrderFormId>
            <ShippingMethodId>17995798-a2cc-43ad-81e8-bb932f6827e4</ShippingMethodId>
            <ShippingMethodName>Online Download</ShippingMethodName>
            <ShippingAddressId>Home</ShippingAddressId>
            <ShipmentTrackingNumber />
            <ShipmentTotal>10.0000</ShipmentTotal>
            <ShippingDiscountAmount>0.0000</ShippingDiscountAmount>
            <Status />
            <LineItemIds />
          </Shipment>
        </Shipments>
        <Payments>
          <Payment xsi:type="CreditCardPayment">
            <Created>2008-05-15T12:29:39.513</Created>
            <Modified>2008-05-15T12:29:39.513</Modified>
            <OrderFormId>141</OrderFormId>
            <OrderGroupId>131</OrderGroupId>
            <BillingAddressId />
            <PaymentMethodId>e2dff5b7-8ec1-4f14-91a1-357c1bb968a1</PaymentMethodId>
            <PaymentMethodName>Pay By Credit Card</PaymentMethodName>
            <CustomerName />
            <Amount>10.0000</Amount>
            <PaymentType>CreditCard</PaymentType>
            <ValidationCode />
            <AuthorizationCode />
            <Status />
            <CardType />
            <CreditCardNumber />
            <CreditCardSecurityCode />
            <ExpirationMonth>7</ExpirationMonth>
            <ExpirationYear>2009</ExpirationYear>
          </Payment>
        </Payments>
        <LineItems>
          <LineItem>
            <Created>2008-05-15T12:29:39.373</Created>
            <Modified>2008-05-15T12:29:39.373</Modified>
            <Discounts />
            <OrderFormId>141</OrderFormId>
            <OrderGroupId>131</OrderGroupId>
            <Catalog>Everything2</Catalog>
            <CatalogNode>Everything</CatalogNode>
            <ParentCatalogEntryId />
            <CatalogEntryId>thecode</CatalogEntryId>
            <Quantity>2.0000</Quantity>
            <MinQuantity>1.0000</MinQuantity>
            <MaxQuantity>100.0000</MaxQuantity>
            <PlacedPrice>0.0000</PlacedPrice>
            <ListPrice>100.0000</ListPrice>
            <LineItemDiscountAmount>0.0000</LineItemDiscountAmount>
            <OrderLevelDiscountAmount>0.0000</OrderLevelDiscountAmount>
            <ShippingAddressId>Home</ShippingAddressId>
            <ShippingMethodName>Online Download</ShippingMethodName>
            <ShippingMethodId>17995798-a2cc-43ad-81e8-bb932f6827e4</ShippingMethodId>
            <ExtendedPrice>200.0000</ExtendedPrice>
            <Description />
            <Status />
            <DisplayName>Samsung LN52A650 52-inch 1080p 120Hz LCD HDTV with RED Touch of Color</DisplayName>
            <AllowBackordersAndPreorders>false</AllowBackordersAndPreorders>
            <InStockQuantity>0.0000</InStockQuantity>
            <PreorderQuantity>0.0000</PreorderQuantity>
            <BackorderQuantity>0.0000</BackorderQuantity>
            <InventoryStatus>0</InventoryStatus>
            <LineItemOrdering>2008-05-15T12:29:39.373</LineItemOrdering>
            <ConfigurationId />
            <ProviderId />
          </LineItem>
          <LineItem>
            <Created>2008-05-15T12:29:39.42</Created>
            <Modified>2008-05-15T12:29:39.42</Modified>
            <Discounts />
            <OrderFormId>141</OrderFormId>
            <OrderGroupId>131</OrderGroupId>
            <Catalog>Everything2</Catalog>
            <CatalogNode>Everything</CatalogNode>
            <ParentCatalogEntryId />
            <CatalogEntryId>thecode</CatalogEntryId>
            <Quantity>2.0000</Quantity>
            <MinQuantity>1.0000</MinQuantity>
            <MaxQuantity>100.0000</MaxQuantity>
            <PlacedPrice>0.0000</PlacedPrice>
            <ListPrice>100.0000</ListPrice>
            <LineItemDiscountAmount>0.0000</LineItemDiscountAmount>
            <OrderLevelDiscountAmount>0.0000</OrderLevelDiscountAmount>
            <ShippingAddressId>Home</ShippingAddressId>
            <ShippingMethodName>Online Download</ShippingMethodName>
            <ShippingMethodId>17995798-a2cc-43ad-81e8-bb932f6827e4</ShippingMethodId>
            <ExtendedPrice>200.0000</ExtendedPrice>
            <Description />
            <Status />
            <DisplayName>Samsung LN52A650 52-inch 1080p 120Hz LCD HDTV with RED Touch of Color</DisplayName>
            <AllowBackordersAndPreorders>false</AllowBackordersAndPreorders>
            <InStockQuantity>0.0000</InStockQuantity>
            <PreorderQuantity>0.0000</PreorderQuantity>
            <BackorderQuantity>0.0000</BackorderQuantity>
            <InventoryStatus>0</InventoryStatus>
            <LineItemOrdering>2008-05-15T12:29:39.42</LineItemOrdering>
            <ConfigurationId />
            <ProviderId />
          </LineItem>
          <LineItem>
            <Created>2008-05-15T12:29:39.467</Created>
            <Modified>2008-05-15T12:29:39.467</Modified>
            <Discounts />
            <OrderFormId>141</OrderFormId>
            <OrderGroupId>131</OrderGroupId>
            <Catalog>Everything2</Catalog>
            <CatalogNode>Everything</CatalogNode>
            <ParentCatalogEntryId />
            <CatalogEntryId>thecode</CatalogEntryId>
            <Quantity>2.0000</Quantity>
            <MinQuantity>1.0000</MinQuantity>
            <MaxQuantity>100.0000</MaxQuantity>
            <PlacedPrice>0.0000</PlacedPrice>
            <ListPrice>100.0000</ListPrice>
            <LineItemDiscountAmount>0.0000</LineItemDiscountAmount>
            <OrderLevelDiscountAmount>0.0000</OrderLevelDiscountAmount>
            <ShippingAddressId>Home</ShippingAddressId>
            <ShippingMethodName>Online Download</ShippingMethodName>
            <ShippingMethodId>17995798-a2cc-43ad-81e8-bb932f6827e4</ShippingMethodId>
            <ExtendedPrice>200.0000</ExtendedPrice>
            <Description />
            <Status />
            <DisplayName>Samsung LN52A650 52-inch 1080p 120Hz LCD HDTV with RED Touch of Color</DisplayName>
            <AllowBackordersAndPreorders>false</AllowBackordersAndPreorders>
            <InStockQuantity>0.0000</InStockQuantity>
            <PreorderQuantity>0.0000</PreorderQuantity>
            <BackorderQuantity>0.0000</BackorderQuantity>
            <InventoryStatus>0</InventoryStatus>
            <LineItemOrdering>2008-05-15T12:29:39.467</LineItemOrdering>
            <ConfigurationId />
            <ProviderId />
          </LineItem>
        </LineItems>
        <Discounts />
        <Name>default</Name>
        <BillingAddressId>Home</BillingAddressId>
        <ShippingTotal>10.0000</ShippingTotal>
        <HandlingTotal>0.0000</HandlingTotal>
        <TaxTotal>0.0000</TaxTotal>
        <DiscountAmount>0.0000</DiscountAmount>
        <SubTotal>600.0000</SubTotal>
        <Total>610.0000</Total>
        <Status />
        <ProviderId />
      </OrderForm>
    </OrderForms>
    <OrderAddresses>
      <OrderAddress>
        <Created>2008-05-15T12:29:39.013</Created>
        <Modified>2008-05-15T12:29:39.013</Modified>
        <OrderGroupId>131</OrderGroupId>
        <Name>Home</Name>
        <FirstName />
        <LastName />
        <Organization>Company</Organization>
        <Line1 />
        <Line2 />
        <City />
        <State />
        <CountryCode />
        <CountryName />
        <PostalCode />
        <RegionCode />
        <RegionName />
        <DaytimePhoneNumber />
        <EveningPhoneNumber />
        <FaxNumber />
        <Email>who@someone.com</Email>
      </OrderAddress>
    </OrderAddresses>
    <InstanceId>a74023d4-4abd-4615-8d5e-51c766c158c4</InstanceId>
    <ApplicationId>e1dff5b7-8ec1-4f14-91a1-357c1bb968a0</ApplicationId>
    <AffiliateId>00000000-0000-0000-0000-000000000000</AffiliateId>
    <Name>Default</Name>
    <CustomerId>bf6da67a-8917-4173-ac96-7776efdb77e1</CustomerId>
    <CustomerName>John Doe</CustomerName>
    <AddressId />
    <ShippingTotal>10.0000</ShippingTotal>
    <HandlingTotal>0.0000</HandlingTotal>
    <TaxTotal>0.0000</TaxTotal>
    <SubTotal>600.0000</SubTotal>
    <Total>610.0000</Total>
    <BillingCurrency>USD</BillingCurrency>
    <Status>NewOrder</Status>
    <ProviderId />
    <TrackingNumber>PO131336</TrackingNumber>
    <ExpirationDate>9999-12-31T23:59:59.9999999</ExpirationDate>
  </PurchaseOrder>
</ContextDoc>
<OrderForms>

It is then processed by the template similar to this example.

Example: template processing

XML
<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" />
    <xsl:include href="order-shared.xsl"/>

    <xsl:template match="/">
        <html>
            <head id="Head1">
                <style type="text/css">
                    #PurchaseOrder {}
                    h1 {font-size: 20px;}
                    h2 {font-size: 18px;}
                    h3 {font-size: 16px; background-color: #cccccc; padding: 2px 2px 2px 2px}
                    .introduction {padding: 5px 0 0 0}
                    .footer {padding: 5px 0 0 0}
                </style>
                <title>
                    Order Notification
                </title>
            </head>
            <body>
                <xsl:apply-templates select="//PurchaseOrder"></xsl:apply-templates>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="PurchaseOrder">
        <div id="PurchaseOrder">
            <h1>Sale/Order Notification from the Mediachase Store</h1>

            <h1>**ORDER SUMMARY</h1>
            <xsl:call-template name="OrderHeader"></xsl:call-template>
            <div class="OrderForms">
                <h2>Products Purchased:</h2>
                <xsl:apply-templates select="OrderForms/OrderForm"></xsl:apply-templates>
            </div>

            <xsl:call-template name="OrderFooter"></xsl:call-template>

            <div class="Footer">
                Regards,<br/> Mediachase Software.
            </div>
        </div>
    </xsl:template>
</xsl:stylesheet>

Example: Order-shared.xsl

XML
<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" />

    <xsl:template name="OrderHeader">
        Order Number: <xsl:value-of select="TrackingNumber"/><br/>
        Status: <xsl:value-of select="Status"/><br/>
        Name: <xsl:value-of select="CustomerName"/><br/>
        Email: <a>
            <xsl:attribute name="href">
                mailto:<xsl:value-of select="//PurchaseOrder/OrderAddresses/OrderAddress[Name=//PurchaseOrder/OrderForms/OrderForm/BillingAddressId]/Email"/>
            </xsl:attribute>
            <xsl:value-of select="//PurchaseOrder/OrderAddresses/OrderAddress[Name=//PurchaseOrder/OrderForms/OrderForm/BillingAddressId]/Email"/>
        </a>
    </xsl:template>

    <xsl:template name="OrderFooter">
        <h3>Order Summary</h3>
        <div class="OrderSummary">
            Sub Total: <xsl:value-of select="BillingCurrency"/>&#160;<xsl:value-of select="format-number(SubTotal, '###,###.00')"/><br/>
            Handling Total: <xsl:value-of select="BillingCurrency"/>&#160;<xsl:value-of select="format-number(HandlingTotal, '###,###.00')"/><br/>
            Shipping Total: <xsl:value-of select="BillingCurrency"/>&#160;<xsl:value-of select="format-number(ShippingTotal, '###,###.00')"/><br/>
            Total Tax: <xsl:value-of select="BillingCurrency"/>&#160;<xsl:value-of select="format-number(TaxTotal, '###,###.00')"/><br/>
            TOTAL: <xsl:value-of select="BillingCurrency"/>&#160;<xsl:value-of select="format-number(Total, '###,###.00')"/><br/>
        </div>
    </xsl:template>

    <xsl:template match="OrderForm">
        <div class="OrderForm">
            <div class="OrderForms">
                <h3>Line Items</h3>
                <xsl:apply-templates select="LineItems/LineItem"></xsl:apply-templates>
                <h3>Payments</h3>
                <xsl:apply-templates select="Payments/Payment"></xsl:apply-templates>
            </div>
            <div class="OrderSummary">
                <!--
                Sub Total: <xsl:value-of select="SubTotal"/><br/>
                Handling Total: <xsl:value-of select="HandlingTotal"/><br/>
                Shipping Total: <xsl:value-of select="ShippingTotal"/><br/>
                Total Tax: <xsl:value-of select="TaxTotal"/><br/>
                Discount: <xsl:value-of select="DiscoutnAmount"/><br/>
                TOTAL: <xsl:value-of select="Total"/><br/>
                -->
            </div>
        </div>
    </xsl:template>

    <xsl:template match="LineItem">
        <div class="LineItem">
            <xsl:value-of select="format-number(Quantity, '###,###.##')"/>&#160;<xsl:value-of select="DisplayName"/> - <xsl:value-of select="//PurchaseOrder/BillingCurrency"/>&#160;<xsl:value-of select="format-number(ListPrice, '###,###.00')"/> each
        </div>
    </xsl:template>

    <xsl:template match="Payment">
        <div class="Payment">
            Payment Method: <xsl:value-of select="PaymentMethodName"/><br/>
            Amount: <xsl:value-of select="//PurchaseOrder/BillingCurrency"/>&#160;<xsl:value-of select="format-number(Amount, '###,###.00')"/>
        </div>
    </xsl:template>

</xsl:stylesheet>

Template engine

Calling the template engine API is done as in the example below.

Example: calling the template engine

C#
// Execute template processor
    string body = TemplateService.Process("order-confirm", Thread.CurrentThread.CurrentCulture, 
    new Dictionary<string, object>());

As you can see, three parameters are passed: template name, current culture, and a dictionary collection.

Example: sending an order notification

C#
using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MetaDataTest.Common;
using Mediachase.Commerce.Orders;
using Mediachase.MetaDataPlus;
using Mediachase.Commerce.Engine.Template;
using System.Threading;
using System.Net.Mail;

namespace UnitTests.OrderSystem
    /// <summary>
    /// Test class for different notifications related to order system.
    /// </summary>
    [TestClass]
    public class OrderSystem_Notifications
    {

        public OrderSystem_Notifications()
        {
        }

        [TestMethod]
        public void Notifications_CreditCard_CustomerEmail()
        {
            Cart cart = OrderHelper.CreateCartSimple(Guid.NewGuid());
            cart.OrderForms[0].Payments.Clear();
            cart.OrderForms[0].Payments.Add(OrderHelper.CreateCreditCardPayment());
            cart.AcceptChanges();
            cart.RunWorkflow("CartValidate");
            cart.RunWorkflow("CartPrepare");
            cart.RunWorkflow("CartCheckout");
            cart.AcceptChanges();
            PurchaseOrder po = cart.SaveAsPurchaseOrder();

            po = OrderContext.Current.GetPurchaseOrder(po.CustomerId, po.OrderGroupId);

            // Send emails
            SendEmail(po, "order-confirm");
            SendEmail(po, "order-notify");

            // Validate
            Assert.AreEqual(po.OrderForms[0].Payments.Count, 1);
        }

        private void SendEmail(PurchaseOrder order, string template)
        {
            // Add input parameter
            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.Add("OrderGroup", order);

            // Execute template processor
            string body = TemplateService.Process(template, Thread.CurrentThread.CurrentCulture, dic);

            // Send out emails
            MailMessage msg = new MailMessage();
            msg.From = new MailAddress("store@yourcompany.com", "UNIT TEST: Store");
            msg.To.Add(new MailAddress(order.OrderAddresses[0].Email, "UNIT TEST: " + order.Name));
            msg.Subject = "UNIT TEST: Store: Order Notification";
            msg.Body = body;
            msg.IsBodyHtml = true;

            SmtpClient client = new SmtpClient();
            client.Send(msg);
        }
    }
}

Related topic

 

Comments