Where to store email templates

I have an asp.net web application that sends several emails to users during the registration process. Right now I have built-in code, but I would like to keep them in a central place where I can edit them without entering into VS.

What will be the best place / format for storing these HTML templates?

+7
source share
5 answers

I save all my email templates for my web application as ASP.NET MVC Razor Views , but as a built-in resource in an easy assembly, which I can easily link from any project.

The template looks like this (pay attention to localization):

@model Milkshake.Commerce.Model.Users.UserDto @using Milkshake.Core.Internationalization; @using Milkshake.Commerce.Model.Meta; @if (Language.CurrentForInterface.TwoLetterISOLanguageName.Equals("da")) { <h1>Hej @Model.FirstName</h1> <p> Din nye brugerkonto til Milkshake Commerce er blevet oprettet. </p> <p> Gå til dine <a href="http://@ShopSettings.Instance.Domain.TrimEnd('/')/Account">konto indstillinger</a>, brug din e-mail adresse som adgangskode og du vil blive videreført til dine konto indstillinger, hvor du kan ændre din adgangskode. </p> <p>Ha' en god dag!</p> <h2>The Milkshake Commerce Team!</h2> } else { <h1>Hi @Model.FirstName</h1> <p> Your new user account for Milkshake Commerce has been created for you. </p> <p> Go to your <a href="http://@ShopSettings.Instance.Domain.TrimEnd('/')/Account">user account page</a>, use your e-mail address as password and you'll be taken directly to your account page where you can change your password. </p> <p>Have a nice day!</p> <h2>The Milkshake Commerce Team!</h2> } 

Then I have a "main" template called _AppEmailTemplate.cshtml :

 @using Milkshake.Commerce.Model.Resources <!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <style type="text/css"> body { font-family: Arial, Helvetica; } .layout-wrapper { width: 600px; } .header { background-color: #242225; } .header img { display: block; } .content { background-color: #ffffff; padding: 10px 20px; border: 10px solid #eaeaea; border-top: none; } .footer { padding: 20px; padding-top: 5px; font-size: 10px; color: #cccccc; } p { font-size: 14px; } p.company-details { font-size: 12px; } h1 { font-size: 20px; } h2 { font-size: 16px; } </style> <style type="text/css" id="mobile"> @@media only screen and (max-device-width: 480px) { body { } .layout-wrapper { width: 480px !important; } .header { background-color: transparent !important; } .header img { width: 480px !important; } .content { border: none !important; } .footer { padding-top: 15px !important; } p { font-size: 22px !important; } h1 { font-size: 28px !important; } h2 { font-size: 24px !important; } } </style> </head> <body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0" bgcolor="#f1f1f1"> <table width="100%" cellpadding="0" cellspacing="0" bgcolor="#f1f1f1"> <tr> <td valign="top" align="center"> <table cellpadding="0" cellspacing="0" width="100%" height="80"> <tr> <td class="header" align="center"> <table cellpadding="0" cellspacing="0" width="600" height="80" class="layout-wrapper" style="width: 600px;"> <tr> <td> <img src="http://example.com/email-header.png" alt="Milkshake Commerce" /> </td> </tr> </table> </td> </tr> </table> <table cellpadding="0" cellspacing="0" width="600" class="layout-wrapper"> <tr> <td class="content" align="left"> #¤#¤CONTENTSECTION#¤#¤ </td> </tr> <tr> <td class="footer" align="left"> <p>@Text.appEmailDisclaimer</p> <p>@Text.appEmailFooterAd.UrlDecode()</p> <p class="company-details"><i>Company name etc.</i></p> </td> </tr> </table> </td> </tr> </table> </body> </html> 

To send an email, I use RazorEngine to render:

 public void SendSystemEmail<T>(string templateName, string subject, string fromName, string recipientEmail, T model) { dynamic template = this.GetEmailTemplate(templateName); string layoutBody = RazorEngine.Razor.Parse(template.Layout as string, model); string emailBody = RazorEngine.Razor.Parse(template.Template as string, model); emailBody = layoutBody.Replace(CONTENTSECTIONREPLACETOKEN, emailBody); PreMailer.Net.PreMailer pm = new PreMailer.Net.PreMailer(); emailBody = pm.MoveCssInline(emailBody, true); EmailDto email = new EmailDto(); email.Body = emailBody; email.IsBodyHtml = true; email.FromEmail = " support@example.com "; email.ReplyToEmail = email.FromEmail; email.FromName = fromName; email.RecipientEmail = recipientEmail; email.Subject = subject; email.Type = EmailTypes.Transactional; if (String.IsNullOrWhiteSpace(email.FromName)) { email.FromName = "Milkshake Software"; } this.SendMailMessages(new List<EmailDto>() { email }, false); } 

The code above uses my own EmailDto object. Here you can easily create an instance of [MailMessage][2] directly and send it using [SmtpClient][3] .

Also, in order to get the best rendering in all email clients, I use my own PreMailer.Net library to move all the inline CSS. Read my blog post here for more information. (The code is on Github)

GetEmailTemplate does the following:

 /// <summary> /// Gets the email template. /// </summary> /// <param name="templateName">Name of the template.</param> /// <returns>Returns the e-mail template.</returns> private dynamic GetEmailTemplate(string templateName) { string masterTemplateContents = this.GetTemplateFileContents("_AppEmailTemplate.cshtml"); string templateContents = this.GetTemplateFileContents(templateName + ".html.cshtml"); return new { Layout = masterTemplateContents, Template = templateContents }; } /// <summary> /// Gets the template file contents. /// </summary> /// <param name="templateFileName">The name of the template file.</param> /// <returns>Returns the contents of the template file.</returns> private string GetTemplateFileContents(string templateFileName) { return this.GetEmailFileContents("Templates", templateFileName); } /// <summary> /// Gets the email file contents. /// </summary> /// <param name="lastNamespaceToken">The last namespace token.</param> /// <param name="templateFileName">The name of the template file.</param> /// <returns> /// Returns the contents of the template file. /// </returns> private string GetEmailFileContents(string lastNamespaceToken, string templateFileName) { var assembly = Assembly.GetExecutingAssembly(); if (assembly != null) { StringBuilder sb = new StringBuilder(); using (StreamReader sr = new StreamReader(assembly.GetManifestResourceStream(String.Format("MyApp.BusinessLogic.Communication.{0}.{1}", lastNamespaceToken, templateFileName)))) { while (!sr.EndOfStream) { var line = sr.ReadLine(); if (!line.StartsWith("@model")) { sb.AppendLine(line); } } } return sb.ToString(); } return null; } 
+13
source

I would recommend storing email templates in an XML file, which will allow you to scale it in the future by adding attributes to the mail template, and also make it easy to edit.

+1
source

It depends on how often the templates change and who will change them. For example:

Modified by users of the application, changes are urgent and potentially frequent:

  • It is probably best to store in a database and upload every time an email is sent.

Changed by developers (i.e. you), changes are infrequent and do not require:

  • text files on a web server, upload them to the cache and store them there, only reloading them when the cache crashes or the application restarts.
0
source

You can store email templates in a .html file . Then format it in such a way as to support the parameter you want to enable. eg.

 <head> <title></title> </head> <body> Hello <!--Name--> , This is a test template User Name: <!--UserName--> ............................. ............................. </body> </html> 

Whenever you send an email to a user, you want to set the template as a user, so you can replace this parameter at runtime.

0
source

Thanks to everyone for telling us how they work. I gathered a lot of knowledge from here. I liked @MartinHN using the Razor parser with a specific data model.

However, for me this did not work.

Demand:

  • I need to store letter templates so that I can display them on
    Interested parties at any time. Thus, it should be available for viewing the Intranet - preferably through the sae website as a hosted API.

  • Interface designers should be able to easily modify templates. Thus, I want to save it in the usual HTML format so that the designer does not need to go through too many technical details.

  • Email templates should be easily accessible to administrators (future requirements). In the near future, various notifications for SMS, Screen. Thus, the patterns are different.

Based on these requirements, I did the following:

  • Since I used MVC, I created a folder called "STATIC" that is viewable directly (and the MVC / http handler excludes this folder from performing its MVC actions).

    With this approach, I could easily achieve the first requirement, and I can send my link to shareholders as http://api.aksdfjl.com/static/welcomeemailtemplate.html

  • Each email template gave its own html, so it was easy for the designer to access it and transfer it to their repository as a shortcut to my repository folder. CSS is embedded in Html and it is completely independent html - via email.

  • The last basic requirement was to support this design, and the user can modify it. Well, then definitely I don’t want to deal with the file system at all. What I did is now these notifications are stored in the database, and I initialize them once. After that, the admin panel has a wysiwyg html editor that can give them a quick look, as well as control over what it should send.

Now I wanted to make sure that future requirements are easily handled, and since my company introduced various notifications for different modes, such as Email, Screen, SMS notifications. I decided to expand the software design with an XML template initializer that stores these responses.

The mother of the whole template, called MessageTemplates.xml, stores various information that I need to initialize templates of different types, that is, email, SMS, screen, etc.

enter image description here

Here's what the code looks like now.

  [HttpGet] [Route("applications/initializenotificationtemplate")] public IHttpActionResult InitializeNotificationTemplate() { return InitializeNotificationTemplate(Path.Combine(HostingEnvironment.ApplicationPhysicalPath, @"Static\InitializeData\MessageTemplates.xml")); } [NonAction] public IHttpActionResult InitializeMailTemplate(string filePath) { try { _applicationService.InitializeTemplate(filePath); return Ok("Application Notification templates are initialized."); } catch (Exception ex) { return InternalServerError(ex); } } 

_applicationService.InitializeTemplate has the following definition:

 public bool InitializeTemplate(string filePath) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException("File Path"); } if (!File.Exists(filePath)) { throw new FileNotFoundException(filePath); } var data = _notificationTemplateService.Get(); var exceptionMessages = string.Empty; if (data != null) { var historicalTemplates = data.ToList(); historicalTemplates.ForEach((d) => _notificationTemplateService.Delete(d, out exceptionMessages)); } XDocument xmlDocument = XDocument.Load(filePath); IEnumerable<NotificationTemplate> templates = (from template in xmlDocument.Descendants("Template") select new NotificationTemplate() { Title = template.Element("Subject").Value, Description = template.Element("Body").Value, Type = (NotificationTypeOptions)Enum.Parse(typeof(NotificationTypeOptions), template.Element("Type").Value, true), Category = (NotificationCategoryOptions)Enum.Parse(typeof(NotificationCategoryOptions), template.Attribute("category").Value, true), }).ToList(); foreach (var t in templates) { var path = Path.Combine(Path.GetDirectoryName(filePath), Regex.Replace(t.Description, @"\t|\n|\r| ", "")); if (File.Exists(path)) { StreamReader reader = new StreamReader(path); t.Description = reader.ReadToEnd(); } else { t.Description = string.Empty; } } return _notificationTemplateService.InsertRange(templates, out exceptionMessages); } 

This is how my model looks, similar to the database model (first the code is the EF approach).

  public class NotificationTemplate : IdentityBase { public string Category { get; set; } public NotificationTypeOptions Type { get; set; } public string Title { get; set; } public string Description { get; set; } public NotificationTemplate() { Type = NotificationTypeOptions.Email; } } [Flags] public enum NotificationTypeOptions { Email = 0, Screen = 1, } 

The first time I install my application, I call the API initialization call, which installs my notification templates into the database, and all other options are available and ready to use.

Now with this approach, I have made everyone happy in the organization, and I have great power to expand this further, so it’s easy for me to introduce new templates.

0
source

All Articles