Skip to main content
deleted 125 characters in body
Source Link
public class TypeResolver : Sitecore.Modules.EmailCampaign.Core.TypeResolver
{
    public override MessageItem GetCorrectMessageObject(Item item)
    {
        if (SMS.IsCorrectMessageItem(item))
        {
            return SMS.FromItemEx(item);
        }
        if (ABTestMessage.IsCorrectMessageItem(item))
        {
            return ABTestMessage.FromItem(item);
        }
        return base.GetCorrectMessageObject(item);
    }
}
public class TypeResolver : Sitecore.Modules.EmailCampaign.Core.TypeResolver
{
    public override MessageItem GetCorrectMessageObject(Item item)
    {
        if (SMS.IsCorrectMessageItem(item))
        {
            return SMS.FromItemEx(item);
        }
        if (ABTestMessage.IsCorrectMessageItem(item))
        {
            return ABTestMessage.FromItem(item);
        }
        return base.GetCorrectMessageObject(item);
    }
}
public class TypeResolver : Sitecore.Modules.EmailCampaign.Core.TypeResolver
{
    public override MessageItem GetCorrectMessageObject(Item item)
    {
        if (ABTestMessage.IsCorrectMessageItem(item))
        {
            return ABTestMessage.FromItem(item);
        }
        return base.GetCorrectMessageObject(item);
    }
}
Source Link

EXM 3.4 removes href from link tags and src from img tags in some cases. This happens through the use of Sitecore.Modules.EmailCampaign.Core.Links.LinksManager, which calls ChangeLink based on a parameter function (which is what will change the actual link). However, if this function returns null, the entire matched link (including href=" / src=") will be matched with the original link itself - thus removing the important attribute part of the link tag.

The functions returns null if the pipeline is aborted, but this is wrong, it should have returned the link itself instead. This for instance happens for anchor links. This bug also affects the auto embedded image opened image.

These bugs are fixed in EXM 3.5, so upgrading will fix it.

If that's not an option, we fixed it by replacing the mail type in order to overwrite this unwanted behaviour - we only use ABTestMessage, but it's inherited from WebPageMail and HtmlMailBase, so you can replace any of these you need. What it looks like for us now is this:

ABTestMessage:

public class ABTestMessage : Sitecore.Modules.EmailCampaign.Messages.ABTestMessage
{
    private CoreFactory _coreFactory;

    protected ABTestMessage(Item item) : this(item, CoreFactory.Instance)
    {

    }

    internal ABTestMessage(Item item, CoreFactory coreFactory) : base(item)
    {
        this._coreFactory = coreFactory;
    }

    //needed to instantiate correct type
    public new static ABTestMessage FromItem(Item item)
    {
        if (item == null || !ABTestMessage.IsCorrectMessageItem(item))
        {
            return null;
        }
        return new ABTestMessage(item);
    }
    
    //needed to clone to the correct type
    public override object Clone()
    {
        ABTestMessage aBTestMessage = new ABTestMessage(base.InnerItem);
        this.CloneFields(aBTestMessage);
        return aBTestMessage;
    }

    //edited with the only purpose of the inline delegate returning link instead of null if the pipeline was aborted
    public override void CollectRelativeFiles(bool preview = false)
    {
        if (this.ManagerRoot.Settings.EmbedImages)
        {
            base.CollectRelativeFiles(preview);
            return;
        }
        
        try
        {
            this._relativeFiles = new List<FileInMemory>();
            if (this.Body != null)
            {
                DateTime utcNow = DateTime.UtcNow;
                LinksManager linksManager = new LinksManager(this.Body, LinkType.Src | LinkType.Css);
                this.Body = linksManager.Replace(delegate (string link)
                {
                    GenerateLinkPipelineArgs generateLinkPipelineArgs = new GenerateLinkPipelineArgs(link, this, preview, this.ManagerRoot.Settings.WebsiteSiteConfigurationName);
                    this.PipelineHelper.RunPipeline("modifyImageLink", generateLinkPipelineArgs);
                    if (!generateLinkPipelineArgs.Aborted)
                    {
                        return generateLinkPipelineArgs.GeneratedUrl;
                    }
                    return link;
                });
                Util.TraceTimeDiff("Modify 'src' links", utcNow);
            }
        }
        catch (Exception ex)
        {
            this.Warnings.Add(ex.Message);
            Logger.Instance.LogError(ex);
        }
    }

    //edited with the only purpose of the inline delegate returning link instead of null if the pipeline was aborted
    protected override string CorrectHtml(string html, bool preview)
    {
        HtmlHelper htmlHelper = new HtmlHelper(html);
        htmlHelper.CleanHtml();
        DateTime utcNow = DateTime.UtcNow;
        htmlHelper.InsertStyleSheets();
        Util.TraceTimeDiff("Insert style sheets", utcNow);
        utcNow = DateTime.UtcNow;
        LinksManager linksManager = new LinksManager(htmlHelper.Html, LinkType.Href);
        html = linksManager.Replace(delegate (string link)
        {
            GenerateLinkPipelineArgs generateLinkPipelineArgs = new GenerateLinkPipelineArgs(link, this, preview, ManagerRoot.Settings.WebsiteSiteConfigurationName);
            PipelineHelper.RunPipeline("modifyHyperlink", generateLinkPipelineArgs);
            if (!generateLinkPipelineArgs.Aborted)
            {
                return generateLinkPipelineArgs.GeneratedUrl;
            }
            return link;
        });
        Util.TraceTimeDiff("Modify 'href' links", utcNow);
        utcNow = DateTime.UtcNow;
        html = HtmlHelper.EncodeSrc(html);
        Util.TraceTimeDiff("Encode 'src' links", utcNow);
        return html;
    }

    //needed by the overriden CollectRelativeFiles - we just take it from base via reflection
    private List<FileInMemory> _relativeFiles
    {
        get
        {
            return (List<FileInMemory>) typeof(HtmlMailBase).GetField("_relativeFiles", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        }
        set
        {
            typeof(HtmlMailBase).GetField("_relativeFiles", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, value);
        }
    }
}

Then you need to overwrite the TypeResolver to use your new implementation:

public class TypeResolver : Sitecore.Modules.EmailCampaign.Core.TypeResolver
{
    public override MessageItem GetCorrectMessageObject(Item item)
    {
        if (SMS.IsCorrectMessageItem(item))
        {
            return SMS.FromItemEx(item);
        }
        if (ABTestMessage.IsCorrectMessageItem(item))
        {
            return ABTestMessage.FromItem(item);
        }
        return base.GetCorrectMessageObject(item);
    }
}

And lastly you need to configure the use of the resolver in a .config file:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <TypeResolver type="Sitecore.Modules.EmailCampaign.Core.TypeResolver,Sitecore.EmailCampaign" singleInstance="true"> <patch:attribute name="type">YOURNAMESPACE.TypeResolver, YOURASSEMBLY</patch:attribute> </TypeResolver> </sitecore> </configuration>