#CEF Emails and {{Tokens}}
For each email a set of email appSettings must be entered.
Generally, a set of default email appSettings are already present.
<add key="Clarity.Emails.BatchSize" value="100" />
<add key="Clarity.Emails.Defaults.BackOfficeEmail" value="project@claritymis.com" />
<add key="Clarity.Emails.Defaults.BCC" value="" />
<add key="Clarity.Emails.Defaults.CC" value="" />
<add key="Clarity.Emails.Defaults.From" value="no-reply@clarityclient.com" />
<add key="Clarity.Emails.Defaults.LoadFromSeparateDomain" value="false" />
<add key="Clarity.Emails.Defaults.RequiresHttps" value="false" />
<add key="Clarity.Emails.Defaults.ReturnPath" value="/" />
<add key="Clarity.Emails.Defaults.Subject" value="An Email from {{CompanyName}}" />
<add key="Clarity.Emails.Defaults.TemplatesRoot" value="/Portals/_default/Skins/Clarity/Ecommerce/Email/" />
<add key="Clarity.Emails.Defaults.To" value="" />
<add key="Clarity.Emails.Interval" value="60000" />
<add key="Clarity.Emails.SchedulerOverride" value="false" />
The notable ones are '.Subject', and '.From', as both of these are required for the email to be sent.
For each specific email, you will also see a set of appSettings
<add key="Clarity.Notifications.SalesOrders.BackOffice.Submitted.Normal.BodyTemplatePath" value="SalesOrders.BackOffice.Submitted.Normal.html" />
<add key="Clarity.Notifications.SalesOrders.BackOffice.Submitted.Normal.Subject" value="A new order has been submitted for {{CompanyName}}" />
<add key="Clarity.Notifications.SalesOrders.BackOffice.Submitted.Normal.To" value="no-reply@clarityclient.com" />
Note here the .BodyTemplatePath, the .Subject, and the .To. On backoffice emails, we must specifiy the destination. On customer emails, the order object has the email we need for the .To
These need to all be present and correct for the emailer to work.\
In each of the Email templates located in your 08.Clarity.Ecommerce.UI.Skins\Clarity\Ecommerce\Email folder, you'll see each has a set of tokens.
<a style="text-decoration:none;" href="{{RootUrl}}">
<img src="{{RootUrl}}/images/logo.png" width="115" style="width:115px; font:23px Arial, Helvetica, sans-serif; color:#269fdc; vertical-align:top;" alt="{{CompanyName}}" />
</a>
<font style="font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #000; text-align: left;">
{{BillingCompany}}<br />
{{BillingFirstName}} {{BillingLastName}}<br />
{{BillingStreetAddress}}<br />
{{BillingCity}}, {{BillingState}} {{BillingZipCode}}
</font>
As part of your front end tasks you may be asked to customize the email templates, and in whatever example you have you may notice that none of the present tokens correspond to some piece of data that the new template requires.\
All of the tokens are replaced in a .cs file, \CEF\05.Clarity.Ecommerce.Workflow\Messaging\EmailQueueWorkflow.QueuableEmails.cs, however included in this file is a mostly comprehensive list of all the available tokens.
#####Standard Sales Collection (Order, Quote, Invoice) Replacements
["{{AccountContactName}}"] = account's user firstname + lastname
["{{AccountID}}"] = Sales account ID OR blank if not present
["{{AccountKey}}"] = Sales account customKey OR blank if not present
["{{AccountName}}"] = Sales account Name
["{{BillingAddress}}"] = Sales BillingContact Address OR blank if not present
["{{BillingCity}}"] = Sales BillingContact City OR blank if not present
["{{BillingCompany}}"] = Sales BillingContact Company OR blank if not present
["{{BillingEmail}}"] = Sales BillingContact Email OR blank if not present
["{{BillingFirstName}}"] = Sales BillingContact FirstName OR blank if not present
["{{BillingLastName}}"] = Sales BillingContact LastName OR blank if not present
["{{BillingName}}"] = Sales BillingContact Name OR blank if not present
["{{BillingPhone}}"] = Sales BillingContact Phone OR blank if not present
["{{BillingState}}"] = Sales BillingContact State OR blank if not present
["{{BillingStreetAddress}}"] = Sales BillingContact StreetAddress OR blank if not present
["{{BillingZipCode}}"] = Sales BillingContact ZipCode OR blank if not present
["{{CompanyName}}"] = appSetting/email: Clarity.CompanyName / Clarity.Emails.Defaults.CompanyName OR Template Specific: Clarity.Notifications.SalesOrders.BackOffice.Submitted.Normal.CompanyName
["{{Date}}"] = The OrderDate of the Sale
["{{Email}}"] = Generally the Email of the BillingAddress, but can vary depending on each email function
["{{FirstName}}"] = The FirstName of the UserModel or blank.
["{{Guid}}"] = If the Sales SerializableAttributes contains a "ShipToStoreGuid" value, that or blank
["{{ID}}"] = Sales specific: cart.ID
["{{LastName}}"] = The LastName of the UserModel or blank.
["{{Name}}"] = FirstName + LastName
["{{OrderDate}}"] = The OrderDate of the Sale
["{{OrderTotal}}"] = The total$ of the Sale including shipping
["{{Phone}}"] = Generally BillingPhone of the Sale
["{{ProductDetailUrlFragment}}"] = appSetting: Clarity.Routes.ProductDetail.RelativePath
["{{PurchaseDetails}}"] = Sales specific. HTML table built in the CS.
["{{ReturnPath}}"] = appSetting/email: Clarity.Emails.Defaults.ReturnPath OR Template Specific: Clarity.Notifications.SalesOrders.BackOffice.Submitted.Normal.ReturnPath
["{{RootUrl}}"] = appSetting: Clarity.Routes.Site.RootUrl
["{{ShippingAddress}}"] = Sales ShippingContact Address OR blank if not present
["{{ShippingCity}}"] = Sales ShippingContact City OR blank if not present
["{{ShippingCompany}}"] = Sales ShippingContact Company OR blank if not present
["{{ShippingEmail}}"] = Sales ShippingContact Email OR blank if not present
["{{ShippingFirstName}}"] = Sales ShippingContact FirstName OR blank if not present
["{{ShippingLastName}}"] = Sales ShippingContact LastName OR blank if not present
["{{ShippingMethod}}"] = If the Sales SerializableAttributes contains a 'ShippingMethod' key and value, that value.
["{{ShippingName}}"] = Sales ShippingContact Name OR blank if not present
["{{ShippingPhone}}"] = Sales ShippingContact Phone OR blank if not present
["{{ShippingState}}"] = Sales ShippingContact State OR blank if not present
["{{ShippingStreetAddress}}"] = Sales ShippingContact StreetAddress OR blank if not present
["{{ShippingZipCode}}"] = Sales ShippingContact ZipCode OR blank if not present
["{{StoreAddress}}"] = Only present if Stores/Vendors are activated, the Address of the Store
["{{StoreCity}}"] = Only present if Stores/Vendors are activated, the City of the Store
["{{StoreEmail}}"] = Only present if Stores/Vendors are activated, the Email of the Store
["{{StoreFirstName}}"] = Only present if Stores/Vendors are activated, the FirstName of the Store
["{{StoreLastName}}"] = Only present if Stores/Vendors are activated, the LastName of the Store
["{{StoreName}}"] = Only present if Stores/Vendors are activated, the Name of the Store
["{{StorePhone}}"] = Only present if Stores/Vendors are activated, the Phone of the Store
["{{StorePhoneNumber}}"] = Only present if Stores/Vendors are activated, the PhoneNumber of the Store
["{{StoreState}}"] = Only present if Stores/Vendors are activated, the State of the Store
["{{StoreStreetAddress}}"] = Only present if Stores/Vendors are activated, the StreetAddress of the Store
["{{StoreZipCode}}"] = Only present if Stores/Vendors are activated, the ZipCode of the Store
["{{Total}}"] = Same as OrderTotal
["{{UserID}}"] = The Sale's User ID
Finding specific {{Tokens}}\
EmailQueue Path: \CEF\05.Clarity.Ecommerce.Workflow\Messaging\EmailQueueWorkflow.QueuableEmails.cs
The standard sales emails encompass the majority of the emails, but some are more specific.
For example, the 'User Account Approved Notification' only has access to the following tokens,
{ "{{RootUrl}}", emailSettings.RequiresHttps ? emailSettings.RootUrlSSL : emailSettings.RootUrl },
{ "{{ReturnPath}}", emailSettings.ReturnPath },
{ "{{CompanyName}}", emailSettings.CompanyName },
{ "{{FirstName}}", userModel.Contact.FirstName },
{ "{{Email}}", userModel.Email },
The way to find these and make sure is to search for the specific email you want in the QueueableEmails file.
For example to find the tokens available for the Account Registration email, you might search for 'Registration', and you would find the following two functions:
public async Task<CEFActionResponse<IEmailQueueModel>> QueueUserAccountCompletedRegistrationUserNotificationAsync(
IUserModel user, string contextProfileName)
{
const string SettingRoot = "Clarity.Authentication.NewUserRegistered.User";
var emailSettings = new EmailSettings(SettingRoot);
var replacementDictionary = new Dictionary<string, string>
{
{ "{{RootUrl}}", emailSettings.RequiresHttps ? emailSettings.RootUrlSSL : emailSettings.RootUrl },
{ "{{ReturnPath}}", emailSettings.ReturnPath },
{ "{{CompanyName}}", emailSettings.CompanyName },
};
var result = await FormatAndQueueEmailAsync(
user.Email ?? user.Contact.Email1, replacementDictionary, emailSettings, null, null, contextProfileName).ConfigureAwait(false);
return await GenerateResultAsync(result).ConfigureAwait(false);
}
public async Task<CEFActionResponse<IEmailQueueModel>> QueueUserAccountCompletedRegistrationBackOfficeNotificationAsync(
IUserModel user, string contextProfileName)
{
const string SettingRoot = "Clarity.Authentication.NewUserRegistered.BackOffice";
var emailSettings = new EmailSettings(SettingRoot);
var replacementDictionary = new Dictionary<string, string>
{
{ "{{RootUrl}}", emailSettings.RequiresHttps ? emailSettings.RootUrlSSL : emailSettings.RootUrl },
{ "{{ReturnPath}}", emailSettings.ReturnPath },
{ "{{CompanyName}}", emailSettings.CompanyName },
{ "{{Username}}", user.UserName },
{ "{{EmailAddress}}", user.Email },
{ "{{WholesalerNumber}}", user.SerializableAttributes.SingleOrDefault(obj => obj.Key == "Wholesaler Number").Value.Value.ToString() }
};
var result = await FormatAndQueueEmailAsync(
CEFConfigDictionary.EmailDefaultsBackOfficeEmailAddress,
replacementDictionary,
emailSettings,
null,
null,
contextProfileName)
.ConfigureAwait(false);
return await GenerateResultAsync(result).ConfigureAwait(false);
}
As you might be able to see, neither of these pass the replacements dictionary into the 'StandardSalesCollectionReplacements' function as all the sales emails do, so you will only have access to those specific tokens listed in their replacementDictionary.
Testing your email\
Once your new template is all set up, and your appSettings are all correct and corresponding, you will want to test this.
You do not neccessarily need to have your Scheduler all set up, and your mailSettings pointing to a real email server to do this, as that is its own task itself.
You can check the Messaging.EmailQueue table in the database
SELECT * FROM Messaging.EmailQueue ORDER BY CreatedDate DESC;
You can then copy the generated HTML from the Body column, and paste it into a local .html file and view it directly in your browser. This database table will also show your subject, from address, and to address.
Troubleshooting\
Case: An Email should have fired off, but is not present in the Messaging.EmailQueue table
1: Verify your appSettings has a Default .From address. Emailer will throw and not push anything to the db without this.
2: Verify your appSettings/email section has the corresponding Subject and the correct BodyTemplatePath for your emailTemplate.
3: Verify your appSettings is pointing to the correct TemplatesRoot
<add key="Clarity.Emails.Defaults.TemplatesRoot" value="/Portals/_default/Skins/Clarity/Ecommerce/Email/" />
4: Troubleshoot the originating network call (for example a Checkout that was supposed to generate an Order email) -- does the Shipping or Billing contact have an email associated with them? Does the account or user?
Markdown file: