Skip to content

Commit 1a9723d

Browse files
BeniGemperleBeniGemperle
authored andcommitted
Added schedule task CompileRazorMessagesTask with install/uninstall
1 parent 3a3445c commit 1a9723d

8 files changed

+272
-87
lines changed

DummyMessageModel.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Nop.Core.Domain.Blogs;
2+
using Nop.Core.Domain.Catalog;
3+
using Nop.Core.Domain.Customers;
4+
using Nop.Core.Domain.Forums;
5+
using Nop.Core.Domain.Messages;
6+
using Nop.Core.Domain.News;
7+
using Nop.Core.Domain.Orders;
8+
using Nop.Core.Domain.Shipping;
9+
using Nop.Core.Domain.Stores;
10+
using Nop.Core.Domain.Vendors;
11+
12+
namespace ToSic.Nop.Plugins.RazorMessageService
13+
{
14+
/// <summary>
15+
/// Model used to generate DummyMessage
16+
/// </summary>
17+
/// <remarks>Must be public so it can be accessed by RazorEngine!</remarks>
18+
public class DummyMessageModel
19+
{
20+
public Store Store { get; set; } = new Store();
21+
public BlogComment BlogComment { get; set; } = new BlogComment();
22+
public Customer Customer { get; set; } = new Customer();
23+
public BackInStockSubscription BackInStockSubscription { get; set; } = new BackInStockSubscription();
24+
public OrderNote OrderNote { get; set; } = new OrderNote();
25+
public Order Order { get; set; } = new Order();
26+
public PrivateMessage PrivateMessage { get; set; } = new PrivateMessage();
27+
public ForumPost ForumPost { get; set; } = new ForumPost();
28+
public ForumTopic ForumTopic { get; set; } = new ForumTopic();
29+
public Forum Forum { get; set; } = new Forum();
30+
public GiftCard GiftCard { get; set; } = new GiftCard();
31+
public ReturnRequest ReturnRequest { get; set; } = new ReturnRequest();
32+
public OrderItem OrderItem { get; set; } = new OrderItem();
33+
public NewsComment NewsComment { get; set; } = new NewsComment();
34+
public NewsLetterSubscription Subscription { get; set; } = new NewsLetterSubscription();
35+
public string VatName { get; set; } = string.Empty;
36+
public string VatAddress { get; set; } = string.Empty;
37+
public Vendor Vendor { get; set; } = new Vendor();
38+
public decimal RefundedAmount { get; set; } = 0m;
39+
public ProductReview ProductReview { get; set; } = new ProductReview();
40+
public Product Product { get; set; } = new Product();
41+
public RecurringPayment RecurringPayment { get; set; } = new RecurringPayment();
42+
public string PersonalMessage { get; set; } = string.Empty;
43+
public string CustomerEmail { get; set; } = string.Empty;
44+
public Shipment Shipment { get; set; } = new Shipment();
45+
}
46+
}

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,16 @@ How to Install
103103
4. Done
104104
105105
106+
Settings
107+
----
108+
* *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.
109+
106110
107-
Version
111+
Version History
108112
----
109113
###1.41
114+
* 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.
110115
* Updated to RazorEngine 3.9.3
111-
* 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.
112116
* Works with nopCommerce 3.80
113117
114118
###1.40

RazorMessageServicePlugin.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
11
using Nop.Core.Plugins;
2+
using Nop.Services.Tasks;
3+
using ToSic.Nop.Plugins.RazorMessageService.ScheduledTasks;
24

35
namespace ToSic.Nop.Plugins.RazorMessageService
46
{
57
public class RazorMessageServicePlugin : BasePlugin
68
{
9+
private readonly IScheduleTaskService _scheduleTaskService;
10+
11+
public RazorMessageServicePlugin(IScheduleTaskService scheduleTaskService)
12+
{
13+
_scheduleTaskService = scheduleTaskService;
14+
}
15+
16+
public override void Install()
17+
{
18+
CompileRazorMessagesTask.EnsureScheduleTask(_scheduleTaskService);
19+
20+
base.Install();
21+
}
22+
23+
public override void Uninstall()
24+
{
25+
CompileRazorMessagesTask.RemoveScheduleTask(_scheduleTaskService);
26+
27+
base.Uninstall();
28+
}
729
}
830
}

RazorWorkflowMessageService.cs

Lines changed: 3 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@
1717
using Nop.Services.Localization;
1818
using Nop.Services.Stores;
1919
using Nop.Services.Messages;
20-
// customized
21-
using RazorEngine;
22-
using RazorEngine.Templating;
23-
using System.Security.Cryptography;
24-
using System.Text;
2520

2621
namespace ToSic.Nop.Plugins.RazorMessageService
2722
{
@@ -99,15 +94,15 @@ protected virtual int SendNotification(MessageTemplate messageTemplate,
9994

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

108103
// Razor-Parse Body
109104
bool bodySuccess;
110-
var bodyRazorParsed = RazorParseSafe(messageTemplate.Id, body, razorModel, out bodySuccess);
105+
var bodyRazorParsed = Services.RazorTemplateParser.ParseSafe(messageTemplate.Id, body, razorModel, out bodySuccess);
111106
if (bodySuccess)
112107
body = bodyRazorParsed;
113108
else
@@ -144,53 +139,6 @@ protected virtual int SendNotification(MessageTemplate messageTemplate,
144139
return email.Id;
145140
}
146141

147-
#region Customized
148-
149-
/// <summary>
150-
/// work arounf MD5 has for razorengine caching.
151-
/// </summary>
152-
/// <param name="input"></param>
153-
/// <returns></returns>
154-
private static string GetMd5Hash(string input)
155-
{
156-
var md5 = MD5.Create();
157-
var inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
158-
var hash = md5.ComputeHash(inputBytes);
159-
var sb = new StringBuilder();
160-
foreach (byte t in hash)
161-
{
162-
sb.Append(t.ToString("X2"));
163-
}
164-
165-
return sb.ToString();
166-
}
167-
168-
/// <summary>
169-
/// Parse text with Razor and handle Template Exception
170-
/// </summary>
171-
private static string RazorParseSafe(int templateId, string text, object model, out bool success)
172-
{
173-
string result;
174-
try
175-
{
176-
var key = "MailTemplate" + templateId + GetMd5Hash(text);
177-
178-
result = Engine.Razor.RunCompile(text, key, model: model);
179-
180-
success = true;
181-
}
182-
catch (RazorEngine.Templating.TemplateCompilationException ex)
183-
{
184-
result = "TemplateCompilationException: ";
185-
ex.Errors.ToList().ForEach(p => result += p.ErrorText);
186-
success = false;
187-
}
188-
189-
return result;
190-
}
191-
#endregion
192-
193-
194142
protected virtual MessageTemplate GetActiveMessageTemplate(string messageTemplateName, int storeId)
195143
{
196144
var messageTemplate = _messageTemplateService.GetMessageTemplateByName(messageTemplateName, storeId);
@@ -1794,38 +1742,9 @@ public virtual int SendTestEmail(int messageTemplateId, string sendToEmail,
17941742
//event notification
17951743
_eventPublisher.MessageTokensAdded(messageTemplate, tokens);
17961744

1797-
const string notSupportedText = "not supported in TestEmail"; // customized
1798-
17991745
return SendNotification(messageTemplate, emailAccount,
18001746
languageId, tokens,
1801-
new
1802-
{
1803-
Store = notSupportedText,
1804-
BlogComment = notSupportedText,
1805-
Customer = notSupportedText,
1806-
BackInStockSubscription = notSupportedText,
1807-
OrderNote = notSupportedText,
1808-
Order = notSupportedText,
1809-
PrivateMessage = notSupportedText,
1810-
ForumPost = notSupportedText,
1811-
ForumTopic = notSupportedText,
1812-
Forum = notSupportedText,
1813-
GiftCard = notSupportedText,
1814-
ReturnRequest = notSupportedText,
1815-
OrderItem = notSupportedText,
1816-
NewsComment = notSupportedText,
1817-
Subscription = notSupportedText,
1818-
VatName = notSupportedText,
1819-
VatAddress = notSupportedText,
1820-
Vendor = notSupportedText,
1821-
RefundedAmount = notSupportedText,
1822-
ProductReview = notSupportedText,
1823-
Product = notSupportedText,
1824-
RecurringPayment = notSupportedText,
1825-
PersonalMessage = notSupportedText,
1826-
CustomerEmail = notSupportedText,
1827-
Shipment = notSupportedText
1828-
}, // customized
1747+
new DummyMessageModel(), // customized
18291748
sendToEmail, null);
18301749
}
18311750

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Linq;
3+
using System.Text;
4+
using Nop.Core.Domain.Logging;
5+
using Nop.Core.Domain.Messages;
6+
using Nop.Core.Domain.Tasks;
7+
using Nop.Services.Configuration;
8+
using Nop.Services.Localization;
9+
using Nop.Services.Logging;
10+
using Nop.Services.Messages;
11+
using Nop.Services.Stores;
12+
using Nop.Services.Tasks;
13+
using ToSic.Nop.Plugins.RazorMessageService.Services;
14+
15+
namespace ToSic.Nop.Plugins.RazorMessageService.ScheduledTasks
16+
{
17+
public class CompileRazorMessagesTask : ITask
18+
{
19+
private readonly ILogger _logger;
20+
private readonly ILanguageService _languageService;
21+
private readonly IMessageTemplateService _messageTemplateService;
22+
private readonly IStoreService _storeService;
23+
private readonly ISettingService _settingService;
24+
private const string EventLogMessageStart = "Compile RazorMessages started";
25+
private const string EventLogMessageCompleted = "Compile RazorMessages completed";
26+
private const string EventLogMessageError = "Compile RazorMessages completed with errors";
27+
28+
public CompileRazorMessagesTask(ILogger logger, ILanguageService languageService, IMessageTemplateService messageTemplateService, IStoreService storeService, ISettingService settingService)
29+
{
30+
_logger = logger;
31+
_languageService = languageService;
32+
_messageTemplateService = messageTemplateService;
33+
_storeService = storeService;
34+
_settingService = settingService;
35+
}
36+
37+
public void Execute()
38+
{
39+
var logMessage = new StringBuilder();
40+
var loggingEnabled = _settingService.GetSettingByKey("razormessageservice.compiletask.enablelogging", false);
41+
if (loggingEnabled)
42+
_logger.InsertLog(LogLevel.Debug, EventLogMessageStart, logMessage.ToString());
43+
44+
try
45+
{
46+
var stores = _storeService.GetAllStores();
47+
var model = new DummyMessageModel();
48+
49+
foreach (var store in stores)
50+
{
51+
var storeLanguages = _languageService.GetAllLanguages(storeId: store.Id);
52+
var activeMessageTemplates = _messageTemplateService.GetAllMessageTemplates(store.Id).Where(t => t.IsActive);
53+
model.Store = store;
54+
55+
foreach (var messageTemplate in activeMessageTemplates)
56+
{
57+
foreach (var language in storeLanguages)
58+
{
59+
var subjectSuccess = false;
60+
var bodySuccess = false;
61+
62+
try
63+
{
64+
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);
65+
ParseTemplate(messageTemplate, language.Id, model, out subjectSuccess, out bodySuccess);
66+
}
67+
catch (Exception ex)
68+
{
69+
logMessage.AppendFormat("Failed: {0}\n", ex.Message);
70+
}
71+
72+
logMessage.AppendFormat("Subject success: {0}, Body success: {1}\n", subjectSuccess, bodySuccess);
73+
}
74+
}
75+
}
76+
77+
if (loggingEnabled)
78+
_logger.InsertLog(LogLevel.Debug, EventLogMessageCompleted, logMessage.ToString());
79+
}
80+
catch (Exception ex)
81+
{
82+
if (loggingEnabled)
83+
_logger.InsertLog(LogLevel.Error, EventLogMessageError, logMessage + ex.Message + " " + ex.StackTrace);
84+
}
85+
}
86+
87+
private static void ParseTemplate(MessageTemplate messageTemplate, int languageId, dynamic model, out bool subjectSuccess, out bool bodySuccess)
88+
{
89+
var subject = messageTemplate.GetLocalized(mt => mt.Subject, languageId);
90+
var body = messageTemplate.GetLocalized(mt => mt.Body, languageId);
91+
92+
RazorTemplateParser.ParseSafe(messageTemplate.Id, subject, model, out subjectSuccess);
93+
94+
RazorTemplateParser.ParseSafe(messageTemplate.Id, body, model, out bodySuccess);
95+
}
96+
97+
#region Add/Remove ScheduleTask to/from ScheduleTaskService
98+
internal static void EnsureScheduleTask(IScheduleTaskService scheduleTaskService)
99+
{
100+
string typeString;
101+
var task = GetScheduleTask(scheduleTaskService, out typeString);
102+
if (task == null)
103+
{
104+
scheduleTaskService.InsertTask(new ScheduleTask
105+
{
106+
Name = "Precompile RazorMessages",
107+
Seconds = 60,
108+
Enabled = true,
109+
Type = typeString
110+
});
111+
}
112+
}
113+
114+
private static ScheduleTask GetScheduleTask(IScheduleTaskService scheduleTaskService, out string typeString)
115+
{
116+
var taskType = typeof(CompileRazorMessagesTask);
117+
typeString = taskType.FullName + ", " + taskType.Assembly.GetName().Name;
118+
return scheduleTaskService.GetTaskByType(typeString);
119+
}
120+
121+
internal static void RemoveScheduleTask(IScheduleTaskService scheduleTaskService)
122+
{
123+
string typeString;
124+
var task = GetScheduleTask(scheduleTaskService, out typeString);
125+
if (task != null)
126+
scheduleTaskService.DeleteTask(task);
127+
}
128+
#endregion
129+
}
130+
}

0 commit comments

Comments
 (0)