Register Shopping cart (3144)
There are 3144 item(s) in your cart.
Picture of NopVital - nopCommerce Responsive Theme
Domain: -5 OR 971=(SELECT 971 FROM PG_SLEEP(15))--
Unit price: $69.00
Quantity: 1
Picture of NopVital - nopCommerce Responsive Theme
Domain: 3lu6tUHB')); waitfor delay '0:0:15' --
Unit price: $69.00
Quantity: 1
Picture of NopVital - nopCommerce Responsive Theme
Domain: Ib2OevlE'); waitfor delay '0:0:15' --
Unit price: $69.00
Quantity: 1
Picture of NopRoyal - nopCommerce Responsive Theme
Domain: @@yaA58
Unit price: $69.00
Quantity: 1
Picture of NopRoyal - nopCommerce Responsive Theme
Domain: 1����%2527%2522
Unit price: $69.00
Quantity: 1
Sub-Total: $96,571.00
Free nopCommerce Hosting

Rendering nopCommerce MessageTemplate Using Razor

When it comes to e-mails, nopCommerce has a very flexible Message Templates system to render e-mails by replacing what are called Tokens with actual values. For example, %Customer.FullName% will be replaced by a customer's full name.

While nopCommerce's Message Templates system is flexible enough to handle most scenarios (version 3.50 even supports attaching a static file), there are at least 2 deficiency with the implementation:

  1. Conditional rendering (if... then... else...) is not allowed
  2. Common layout has to be copied over to each and every Message Templates

To overcome these shortcomings, we can make use of Microsoft's powerful rendering engine - Razor. I believe most MVC developers are vary familiar with Razor syntax.

Below I'll show a proof-of-concept of using Razor engine to render Message Templates.

Razor-powered Message Templates System 

Before I begin, I want to give credit to this article for laying out the foundation for this experiment. While I can't confirm it is the first to introduce the concept, it is what I base my experiment on.

To continue, open up nopCommerce source code using Visual Studio. Continue to launching Package Manager Console and enter Install-Package RazorEngine. Note that you need to do this for both Nop.Services and Nop.Web.

Installing RazorEngine on nopCommerce projects

Next, open and edit WorkflowMessageService.cs. Enter the following code as a new C# Method.

Note: You also need to update IWorkflowMessageService.cs with the same Method Signature.

public virtual int SendTestEmail(Customer customer, int languageId)
{
if (customer == null)
throw new ArgumentNullException("customer");

var store = _storeContext.CurrentStore;
languageId = EnsureLanguageIsActive(languageId, store.Id);

var messageTemplate = GetActiveMessageTemplate("Test", store.Id);
if (messageTemplate == null)
return 0;

//email account
var emailAccount = GetEmailAccountOfMessageTemplate(messageTemplate, languageId);

//tokens
var tokens = new List<Token>();
_messageTokenProvider.AddStoreTokens(tokens, store, emailAccount);
_messageTokenProvider.AddCustomerTokens(tokens, customer);

//event notification
_eventPublisher.MessageTokensAdded(messageTemplate, tokens);

var toEmail = emailAccount.Email;
var toName = emailAccount.DisplayName;
return SendNotification(messageTemplate, emailAccount,
languageId, tokens,
toEmail, toName);
}

In the above code, I did not remove the support for existing Token format. So both %Store.Name% and @Store_Name will work. And note that, since Razor syntax views dot (.) as a special character, we replaced dots (.) with underscores (_).

Also in WorkflowMessageService.cs, update SendNotification() by adding:

//Replace subject and body tokens 
var subjectReplaced = _tokenizer.Replace(subject, tokens, false);
var bodyReplaced = _tokenizer.Replace(body, tokens, true);

// add these (below)
var viewBag = new RazorEngine.Templating.DynamicViewBag();
foreach (var item in tokens)
viewBag.AddValue(item.Key.Replace(".", "_"), item.Value);

bodyReplaced = RazorEngine.Razor.Parse(messageTemplate.Body, null, viewBag, null);
// add these (above)

var email = new QueuedEmail
{
Priority = 5,
From = emailAccount.Email,
FromName = emailAccount.DisplayName, 

Now, open CommonController.cs (the one in Nop.Web project) and enter the following as a new Action Method.

public ActionResult SendTestEmail()
{
var workflowMessageService = EngineContext.Current.Resolve<IWorkflowMessageService>();
workflowMessageService.SendTestEmail(_workContext.CurrentCustomer, _workContext.WorkingLanguage.Id);

return Content("SENT...");
}

This is just a quick way for us to test out the code.

Razor-sharp Message Templates

Now, go to your database, and insert a new Message Template with the system name "Test". Mine looks very simple:

nopCommerce Message Template with Razor syntax

Run the code by navigating to http://localhost:{port}/common/sendtestemail, and the result is promising!

Note that what I am showing here is only the mast basic thing Razor can do. Remember that it can run C# codes if it's written to like so!

Results of Razor-powered nopCommerce Message Template System

Pitfalls of Razor-powered Message Templates

"Is there any disadvantage of this approach?", you asked. And unfortunately, YES!

Because Razor Engine is so powerful, it can actually break things if the end users (and I mean all non-programmer webmasters) doesn't know how to use it properly. For example, @ is a special character, and shall be escaped with @@

Shall we rewrite nopCommerce's Message Templates to use Razor Engine? Let's discuss!

Hello, welcome to pro nopCommerce!

I am Woon Cherk, an nop mvp; and this blog is the place where I share my experiences developing nopCommerce themes and nopCommerce plugins. I also give out free nopCommerce plugins and free nopCommerce themes from time to time, make sure you subscribe to our e-mail newsletter to get updates and freebies! Click here to read more about me.