Last active
March 28, 2019 09:13
-
-
Save izharikov/52ee05aa78bdad2d49c6b343c7d75e74 to your computer and use it in GitHub Desktop.
Sitecore - EXM PersonalizationManager extension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using Sitecore.Diagnostics; | |
using Sitecore.EDS.Core.Dispatch; | |
using Sitecore.EmailCampaign.Cm.Pipelines.SendEmail; | |
using Sitecore.EmailCampaign.Model.Message; | |
using Sitecore.ExM.Framework.Diagnostics; | |
using Sitecore.Modules.EmailCampaign; | |
using Sitecore.Modules.EmailCampaign.Core.Crypto; | |
using Sitecore.Modules.EmailCampaign.Core.Personalization; | |
using Sitecore.Modules.EmailCampaign.Messages; | |
namespace Sitecore.Exm.Custom.Pipelines | |
{ | |
public class CustomFillEmail | |
{ | |
private readonly IStringCipher _cipher; | |
private readonly ILogger _logger; | |
public CustomFillEmail(IStringCipher cipher, ILogger logger) | |
{ | |
_cipher = cipher; | |
_logger = logger; | |
} | |
public void Process(SendMessageArgs args) | |
{ | |
if (args.EcmMessage == null) | |
return; | |
this.GetMailFiller(args)?.FillEmail(); | |
} | |
private MailFiller GetMailFiller(SendMessageArgs args) | |
{ | |
HtmlMailBase ecmMessage1 = args.EcmMessage as HtmlMailBase; | |
if (ecmMessage1 != null) | |
// here we return custom mail filler | |
return (MailFiller) new CustomHtmlMailFiller(ecmMessage1, args, this._logger, this._cipher); | |
MailMessageItem ecmMessage2 = args.EcmMessage as MailMessageItem; | |
if (ecmMessage2 != null) | |
return new MailMessageFiller(ecmMessage2, args, this._cipher); | |
return null; | |
} | |
} | |
public class CustomHtmlMailFiller : HtmlMailFiller | |
{ | |
private readonly HtmlMailBase _htmlMailBase; | |
public CustomHtmlMailFiller(HtmlMailBase message, SendMessageArgs args, ILogger logger, IStringCipher cipher) : | |
base(message, args, logger, cipher) | |
{ | |
_htmlMailBase = message; | |
} | |
protected override void FillBody() | |
{ | |
var utcNowStart = DateTime.UtcNow; | |
var body = _htmlMailBase.Body; | |
Email.ContentType = MessageContentType.Html; | |
// generate custom manager | |
var personalizationManager = GetForMessage(_htmlMailBase); | |
Email.HtmlBody = personalizationManager.ModifyText(body); | |
Util.TraceTimeDiff("SetEmailHtmlBody(ReplaceTokens(Body))", utcNowStart); | |
var utcNowEnd = DateTime.UtcNow; | |
Email.PlainTextBody = _htmlMailBase.ReplaceTokens(_htmlMailBase.AlternateText); | |
Util.TraceTimeDiff("SetEmailAltBody(ReplaceTokens(AlternateText))", utcNowEnd); | |
} | |
/// <summary> | |
/// method to generate custom personalization manager. Replacement of getter PersonalizationManager as its protected in <see cref="Sitecore.Modules.EmailCampaign.Messages.MessageItem"/> | |
/// </summary> | |
/// <param name="messageItem"></param> | |
/// <returns>CustomPersonalizationManager</returns> | |
private static CustomPersonalizationManager GetForMessage(MessageItem messageItem) | |
{ | |
var personalizationManager = new CustomPersonalizationManager(Array.Empty<TokenMapper>()); | |
if (messageItem.CustomPersonTokens.Count > 0) | |
{ | |
DictionaryTokenMapper dictionaryTokenMapper = new DictionaryTokenMapper(); | |
foreach (KeyValuePair<string, object> customPersonToken in messageItem.CustomPersonTokens) | |
{ | |
if (!string.IsNullOrWhiteSpace(customPersonToken.Key) && customPersonToken.Value != null) | |
dictionaryTokenMapper.BindToken(new Token(customPersonToken.Key), customPersonToken.Value); | |
} | |
personalizationManager.AddTokenMapper((TokenMapper) dictionaryTokenMapper); | |
} | |
if (messageItem.PersonalizationRecipient != null) | |
personalizationManager.AddTokenMapper( | |
(TokenMapper) new RecipientPropertyTokenMapper(messageItem.PersonalizationRecipient)); | |
else if (messageItem.MessageType == MessageType.Automated && Context.User != null) | |
personalizationManager.AddTokenMapper((TokenMapper) new ContextUserTokenMapper()); | |
return personalizationManager; | |
} | |
} | |
public class CustomPersonalizationManager | |
{ | |
private static readonly TokenHandler ResolveAndModifyTokenHandler = | |
(string text, string key, IList<TokenMapper> tokenMappers, | |
ref bool moveStartIndex) => | |
{ | |
TokenValue tokenValue = null; | |
if (!string.IsNullOrWhiteSpace(key)) | |
tokenValue = ResolveAndModifyTokenValue(new Token(key), tokenMappers); | |
if (tokenValue != null) | |
{ | |
var str = tokenValue.Value == null ? string.Empty : tokenValue.Value.ToString(); | |
// HERE CHANGES. was: | |
// text = text.Replace("$" + key + "$", WebUtility.HtmlEncode(str)); | |
text = text.Replace("$" + key + "$", str); | |
} | |
else | |
{ | |
moveStartIndex = true; | |
} | |
return text; | |
}; | |
private static readonly TokenHandler ModifyTokenHandlerValue = | |
(string text, string key, IList<TokenMapper> tokenMappers, | |
ref bool moveStartIndex) => | |
{ | |
var source = (IEnumerable<TokenValue>) null; | |
if (!string.IsNullOrWhiteSpace(key)) | |
source = tokenMappers.Select( | |
tokenMapper => tokenMapper.ResolveToken(new Token(key))); | |
if (source != null && source.Any()) | |
text = text.Replace("$" + key + "$", key); | |
else | |
moveStartIndex = true; | |
return text; | |
}; | |
private readonly IList<TokenMapper> _tokenMappers; | |
public CustomPersonalizationManager(params TokenMapper[] tokenMappers) | |
{ | |
Assert.ArgumentNotNull(tokenMappers, nameof(tokenMappers)); | |
Assert.ArgumentCondition( | |
tokenMappers.All(t => t != null), | |
nameof(tokenMappers), "The specified collection of token mappers contains null references."); | |
_tokenMappers = tokenMappers.ToList(); | |
} | |
public void AddTokenMapper(TokenMapper tokenMapper) | |
{ | |
Assert.ArgumentNotNull(tokenMapper, nameof(tokenMapper)); | |
_tokenMappers.Add(tokenMapper); | |
} | |
public virtual string ModifyText(string text) | |
{ | |
Assert.ArgumentNotNull(text, nameof(text)); | |
return ModifyText(text, | |
ResolveAndModifyTokenHandler, _tokenMappers); | |
} | |
private static string ModifyText(string text, TokenHandler handler, | |
IList<TokenMapper> tokenMappers) | |
{ | |
char[] anyOf = new char[6] | |
{ | |
' ', | |
'.', | |
'\r', | |
'\n', | |
'<', | |
'>' | |
}; | |
var startIndex = text.IndexOf('$'); | |
while (startIndex > -1 && startIndex < text.Length - 1) | |
{ | |
var num = text.IndexOf('$', startIndex + 1); | |
if (num != -1) | |
{ | |
if (text.IndexOfAny(anyOf, startIndex + 1, num - startIndex - 1) > -1) | |
{ | |
startIndex = num; | |
} | |
else | |
{ | |
var key = text.Substring(startIndex + 1, num - startIndex - 1); | |
var moveStartIndex = false; | |
text = handler(text, key, tokenMappers, ref moveStartIndex); | |
if (moveStartIndex) | |
startIndex = num; | |
startIndex = startIndex < text.Length - 1 ? text.IndexOf('$', startIndex) : -1; | |
} | |
} | |
else | |
{ | |
break; | |
} | |
} | |
return text; | |
} | |
public string[] ModifyTexts(string[] texts) | |
{ | |
Assert.ArgumentNotNull(texts, nameof(texts)); | |
var strArray = new string[texts.Length]; | |
for (var index = 0; index < texts.Length; ++index) | |
{ | |
var text = texts[index]; | |
strArray[index] = string.IsNullOrEmpty(text) ? null : ModifyText(text); | |
} | |
return strArray; | |
} | |
public TokenValue ResolveTokenValue(Token token) | |
{ | |
Assert.ArgumentNotNull(token, nameof(token)); | |
return ResolveAndModifyTokenValue(token, _tokenMappers); | |
} | |
private static TokenValue ResolveAndModifyTokenValue(Token token, IList<TokenMapper> tokenMappers) | |
{ | |
Assert.ArgumentNotNull(token, nameof(token)); | |
return ModifyTokenValue( | |
tokenMappers | |
.Select( | |
tokenMapper => tokenMapper.ResolveToken(token)) | |
.FirstOrDefault(tokenValue => tokenValue.KnownToken), | |
tokenMappers); | |
} | |
private static TokenValue ModifyTokenValue(TokenValue tokenValue, IList<TokenMapper> tokenMappers) | |
{ | |
if (tokenValue == null) | |
return null; | |
if (tokenValue.Value == null || tokenValue.Value.GetType() != typeof(string)) | |
return tokenValue; | |
var text = tokenValue.Value.ToString(); | |
tokenValue.Value = ModifyText(text, | |
ModifyTokenHandlerValue, tokenMappers); | |
return tokenValue; | |
} | |
private delegate string TokenHandler(string text, string key, IList<TokenMapper> tokenMappers, | |
ref bool moveStartIndex); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Sitecore.Exm.Custom.Pipelines; | |
using Sitecore.Modules.EmailCampaign.Core.Personalization; | |
using Xunit; | |
using Xunit.Abstractions; | |
namespace Sitecore.Exml.Custom.Test.Pipelines | |
{ | |
public class CustomPersonalizationManagerTest | |
{ | |
private readonly ITestOutputHelper _output; | |
public CustomPersonalizationManagerTest(ITestOutputHelper output) | |
{ | |
_output = output; | |
} | |
[Fact] | |
public void TestCustom() | |
{ | |
var personalizationManager = new CustomPersonalizationManager(); | |
var dictionaryTokenMapper = new DictionaryTokenMapper(); | |
dictionaryTokenMapper.BindToken(new Token("sampleHtml"), "<span>123</span>"); | |
personalizationManager.AddTokenMapper(dictionaryTokenMapper); | |
var result = personalizationManager.ModifyText("Hello! <div>$sampleHtml$</div>"); | |
_output.WriteLine(result); | |
} | |
[Fact] | |
public void TestOotb() | |
{ | |
var personalizationManager = new PersonalizationManager(); | |
var dictionaryTokenMapper = new DictionaryTokenMapper(); | |
dictionaryTokenMapper.BindToken(new Token("sampleHtml"), "<span>123</span>"); | |
personalizationManager.AddTokenMapper(dictionaryTokenMapper); | |
var result = personalizationManager.ModifyText("Hello! <div>$sampleHtml$</div>"); | |
_output.WriteLine(result); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> | |
<sitecore> | |
<pipelines> | |
<SendEmail> | |
<processor type="Sitecore.EmailCampaign.Cm.Pipelines.SendEmail.FillEmail, Sitecore.EmailCampaign.Cm"> | |
<patch:attribute name="type"> | |
Sitecore.Exm.Custom.Pipelines.CustomFillEmail, Sitecore.Exm.Custom | |
</patch:attribute> | |
</processor> | |
</SendEmail> | |
</pipelines> | |
</sitecore> | |
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## TestCustom | |
``` | |
Hello! <div><span>123</span></div> | |
``` | |
## TestOotb | |
``` | |
Hello! <div><span>123</span></div> | |
``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment