Skip to content

Commit

Permalink
Added schedule task CompileRazorMessagesTask with install/uninstall
Browse files Browse the repository at this point in the history
  • Loading branch information
BeniGemperle authored and BeniGemperle committed Apr 7, 2017
1 parent 3a3445c commit 1a9723d
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 87 deletions.
46 changes: 46 additions & 0 deletions DummyMessageModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Nop.Core.Domain.Blogs;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Forums;
using Nop.Core.Domain.Messages;
using Nop.Core.Domain.News;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Stores;
using Nop.Core.Domain.Vendors;

namespace ToSic.Nop.Plugins.RazorMessageService
{
/// <summary>
/// Model used to generate DummyMessage
/// </summary>
/// <remarks>Must be public so it can be accessed by RazorEngine!</remarks>
public class DummyMessageModel
{
public Store Store { get; set; } = new Store();
public BlogComment BlogComment { get; set; } = new BlogComment();
public Customer Customer { get; set; } = new Customer();
public BackInStockSubscription BackInStockSubscription { get; set; } = new BackInStockSubscription();
public OrderNote OrderNote { get; set; } = new OrderNote();
public Order Order { get; set; } = new Order();
public PrivateMessage PrivateMessage { get; set; } = new PrivateMessage();
public ForumPost ForumPost { get; set; } = new ForumPost();
public ForumTopic ForumTopic { get; set; } = new ForumTopic();
public Forum Forum { get; set; } = new Forum();
public GiftCard GiftCard { get; set; } = new GiftCard();
public ReturnRequest ReturnRequest { get; set; } = new ReturnRequest();
public OrderItem OrderItem { get; set; } = new OrderItem();
public NewsComment NewsComment { get; set; } = new NewsComment();
public NewsLetterSubscription Subscription { get; set; } = new NewsLetterSubscription();
public string VatName { get; set; } = string.Empty;
public string VatAddress { get; set; } = string.Empty;
public Vendor Vendor { get; set; } = new Vendor();
public decimal RefundedAmount { get; set; } = 0m;
public ProductReview ProductReview { get; set; } = new ProductReview();
public Product Product { get; set; } = new Product();
public RecurringPayment RecurringPayment { get; set; } = new RecurringPayment();
public string PersonalMessage { get; set; } = string.Empty;
public string CustomerEmail { get; set; } = string.Empty;
public Shipment Shipment { get; set; } = new Shipment();
}
}
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,16 @@ How to Install
4. Done
Settings
----
* *razormessageservice.compiletask.enablelogging* - Enable/disable logging when the schedule task runs to compile messages. Default is *False*. This Setting was added in version 1.41.
Version
Version History
----
###1.41
* Improved performance with caching of razor-mail-templates. First use of a mail-template might take up to 2 seconds to compile. Afterward no more recompilation is needed. Updating a mail template causes an automatic re-compilation. There's now a schedule task which automatically compiles all active mail-templates for all stores and languages to improve the performance when using a mail template the first time after an application restart.
* Updated to RazorEngine 3.9.3
* Improved Performance with TemplateCaching. First use of a Mail-Template might take up to 2 Seconds to compile. But afterward no more recompilation is needed. Updating the mail template will cause a re-compilation.
* Works with nopCommerce 3.80
###1.40
Expand Down
22 changes: 22 additions & 0 deletions RazorMessageServicePlugin.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
using Nop.Core.Plugins;
using Nop.Services.Tasks;
using ToSic.Nop.Plugins.RazorMessageService.ScheduledTasks;

namespace ToSic.Nop.Plugins.RazorMessageService
{
public class RazorMessageServicePlugin : BasePlugin
{
private readonly IScheduleTaskService _scheduleTaskService;

public RazorMessageServicePlugin(IScheduleTaskService scheduleTaskService)
{
_scheduleTaskService = scheduleTaskService;
}

public override void Install()
{
CompileRazorMessagesTask.EnsureScheduleTask(_scheduleTaskService);

base.Install();
}

public override void Uninstall()
{
CompileRazorMessagesTask.RemoveScheduleTask(_scheduleTaskService);

base.Uninstall();
}
}
}
87 changes: 3 additions & 84 deletions RazorWorkflowMessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@
using Nop.Services.Localization;
using Nop.Services.Stores;
using Nop.Services.Messages;
// customized
using RazorEngine;
using RazorEngine.Templating;
using System.Security.Cryptography;
using System.Text;

namespace ToSic.Nop.Plugins.RazorMessageService
{
Expand Down Expand Up @@ -99,15 +94,15 @@ protected virtual int SendNotification(MessageTemplate messageTemplate,

// Razor-Parse Subject
bool subjectSuccess;
var subjectRazorParsed = RazorParseSafe(messageTemplate.Id, subject, razorModel, out subjectSuccess);
var subjectRazorParsed = Services.RazorTemplateParser.ParseSafe(messageTemplate.Id, subject, razorModel, out subjectSuccess);
if (subjectSuccess)
subject = subjectRazorParsed;
else
subject += subjectRazorParsed; // in case of an error, append the error-text returned

// Razor-Parse Body
bool bodySuccess;
var bodyRazorParsed = RazorParseSafe(messageTemplate.Id, body, razorModel, out bodySuccess);
var bodyRazorParsed = Services.RazorTemplateParser.ParseSafe(messageTemplate.Id, body, razorModel, out bodySuccess);
if (bodySuccess)
body = bodyRazorParsed;
else
Expand Down Expand Up @@ -144,53 +139,6 @@ protected virtual int SendNotification(MessageTemplate messageTemplate,
return email.Id;
}

#region Customized

/// <summary>
/// work arounf MD5 has for razorengine caching.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string GetMd5Hash(string input)
{
var md5 = MD5.Create();
var inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
var hash = md5.ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (byte t in hash)
{
sb.Append(t.ToString("X2"));
}

return sb.ToString();
}

/// <summary>
/// Parse text with Razor and handle Template Exception
/// </summary>
private static string RazorParseSafe(int templateId, string text, object model, out bool success)
{
string result;
try
{
var key = "MailTemplate" + templateId + GetMd5Hash(text);

result = Engine.Razor.RunCompile(text, key, model: model);

success = true;
}
catch (RazorEngine.Templating.TemplateCompilationException ex)
{
result = "TemplateCompilationException: ";
ex.Errors.ToList().ForEach(p => result += p.ErrorText);
success = false;
}

return result;
}
#endregion


protected virtual MessageTemplate GetActiveMessageTemplate(string messageTemplateName, int storeId)
{
var messageTemplate = _messageTemplateService.GetMessageTemplateByName(messageTemplateName, storeId);
Expand Down Expand Up @@ -1794,38 +1742,9 @@ public virtual int SendTestEmail(int messageTemplateId, string sendToEmail,
//event notification
_eventPublisher.MessageTokensAdded(messageTemplate, tokens);

const string notSupportedText = "not supported in TestEmail"; // customized

return SendNotification(messageTemplate, emailAccount,
languageId, tokens,
new
{
Store = notSupportedText,
BlogComment = notSupportedText,
Customer = notSupportedText,
BackInStockSubscription = notSupportedText,
OrderNote = notSupportedText,
Order = notSupportedText,
PrivateMessage = notSupportedText,
ForumPost = notSupportedText,
ForumTopic = notSupportedText,
Forum = notSupportedText,
GiftCard = notSupportedText,
ReturnRequest = notSupportedText,
OrderItem = notSupportedText,
NewsComment = notSupportedText,
Subscription = notSupportedText,
VatName = notSupportedText,
VatAddress = notSupportedText,
Vendor = notSupportedText,
RefundedAmount = notSupportedText,
ProductReview = notSupportedText,
Product = notSupportedText,
RecurringPayment = notSupportedText,
PersonalMessage = notSupportedText,
CustomerEmail = notSupportedText,
Shipment = notSupportedText
}, // customized
new DummyMessageModel(), // customized
sendToEmail, null);
}

Expand Down
130 changes: 130 additions & 0 deletions ScheduledTasks/CompileRazorMessagesTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Linq;
using System.Text;
using Nop.Core.Domain.Logging;
using Nop.Core.Domain.Messages;
using Nop.Core.Domain.Tasks;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Messages;
using Nop.Services.Stores;
using Nop.Services.Tasks;
using ToSic.Nop.Plugins.RazorMessageService.Services;

namespace ToSic.Nop.Plugins.RazorMessageService.ScheduledTasks
{
public class CompileRazorMessagesTask : ITask
{
private readonly ILogger _logger;
private readonly ILanguageService _languageService;
private readonly IMessageTemplateService _messageTemplateService;
private readonly IStoreService _storeService;
private readonly ISettingService _settingService;
private const string EventLogMessageStart = "Compile RazorMessages started";
private const string EventLogMessageCompleted = "Compile RazorMessages completed";
private const string EventLogMessageError = "Compile RazorMessages completed with errors";

public CompileRazorMessagesTask(ILogger logger, ILanguageService languageService, IMessageTemplateService messageTemplateService, IStoreService storeService, ISettingService settingService)
{
_logger = logger;
_languageService = languageService;
_messageTemplateService = messageTemplateService;
_storeService = storeService;
_settingService = settingService;
}

public void Execute()
{
var logMessage = new StringBuilder();
var loggingEnabled = _settingService.GetSettingByKey("razormessageservice.compiletask.enablelogging", false);
if (loggingEnabled)
_logger.InsertLog(LogLevel.Debug, EventLogMessageStart, logMessage.ToString());

try
{
var stores = _storeService.GetAllStores();
var model = new DummyMessageModel();

foreach (var store in stores)
{
var storeLanguages = _languageService.GetAllLanguages(storeId: store.Id);
var activeMessageTemplates = _messageTemplateService.GetAllMessageTemplates(store.Id).Where(t => t.IsActive);
model.Store = store;

foreach (var messageTemplate in activeMessageTemplates)
{
foreach (var language in storeLanguages)
{
var subjectSuccess = false;
var bodySuccess = false;

try
{
logMessage.AppendFormat("{2:HH:mm:ss} Will parse template {0} (Id: {1}) Language {3} (Id: {4})\n", messageTemplate.Name, messageTemplate.Id, DateTime.Now, language.Name, language.Id);
ParseTemplate(messageTemplate, language.Id, model, out subjectSuccess, out bodySuccess);
}
catch (Exception ex)
{
logMessage.AppendFormat("Failed: {0}\n", ex.Message);
}

logMessage.AppendFormat("Subject success: {0}, Body success: {1}\n", subjectSuccess, bodySuccess);
}
}
}

if (loggingEnabled)
_logger.InsertLog(LogLevel.Debug, EventLogMessageCompleted, logMessage.ToString());
}
catch (Exception ex)
{
if (loggingEnabled)
_logger.InsertLog(LogLevel.Error, EventLogMessageError, logMessage + ex.Message + " " + ex.StackTrace);
}
}

private static void ParseTemplate(MessageTemplate messageTemplate, int languageId, dynamic model, out bool subjectSuccess, out bool bodySuccess)
{
var subject = messageTemplate.GetLocalized(mt => mt.Subject, languageId);
var body = messageTemplate.GetLocalized(mt => mt.Body, languageId);

RazorTemplateParser.ParseSafe(messageTemplate.Id, subject, model, out subjectSuccess);

RazorTemplateParser.ParseSafe(messageTemplate.Id, body, model, out bodySuccess);
}

#region Add/Remove ScheduleTask to/from ScheduleTaskService
internal static void EnsureScheduleTask(IScheduleTaskService scheduleTaskService)
{
string typeString;
var task = GetScheduleTask(scheduleTaskService, out typeString);
if (task == null)
{
scheduleTaskService.InsertTask(new ScheduleTask
{
Name = "Precompile RazorMessages",
Seconds = 60,
Enabled = true,
Type = typeString
});
}
}

private static ScheduleTask GetScheduleTask(IScheduleTaskService scheduleTaskService, out string typeString)
{
var taskType = typeof(CompileRazorMessagesTask);
typeString = taskType.FullName + ", " + taskType.Assembly.GetName().Name;
return scheduleTaskService.GetTaskByType(typeString);
}

internal static void RemoveScheduleTask(IScheduleTaskService scheduleTaskService)
{
string typeString;
var task = GetScheduleTask(scheduleTaskService, out typeString);
if (task != null)
scheduleTaskService.DeleteTask(task);
}
#endregion
}
}
Loading

0 comments on commit 1a9723d

Please sign in to comment.