Skip to content

Instantly share code, notes, and snippets.

@izharikov
Last active March 28, 2019 09:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save izharikov/52ee05aa78bdad2d49c6b343c7d75e74 to your computer and use it in GitHub Desktop.
Save izharikov/52ee05aa78bdad2d49c6b343c7d75e74 to your computer and use it in GitHub Desktop.
Sitecore - EXM PersonalizationManager extension
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);
}
}
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);
}
}
}
<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>
## TestCustom
```
Hello! <div><span>123</span></div>
```
## TestOotb
```
Hello! <div>&lt;span&gt;123&lt;/span&gt;</div>
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment