3

In my newsletter, we have an anchor bar implementation. Anchor Bar has all list of anchor links. After clicking on anchor links, Newsletter should scroll to specific Anchor Target. I am using HTML <a> for Anchor Link. It is not Sitecore Link.

My final anchor link HTML code is <a href="#News1" >MyLink</a>

But when Sitecore sends the anchor link at the time of Email Sending, it deletes the href property and make it as

<a #News1="" >MyLink</a>

You can see Anchor HREF's Value (#News1) became the property and Anchor HREF has been deleted.

I am not using any custom pipeline or LinkManager to change the behavior of HREF or link binding.

Version Details are as Sitecore - Sitecore.NET 8.2 (rev. 161221)

Email Experience Manager 4.0.1 rev. 170105

Thanks, Ali

2 Answers 2

1

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 (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>

0
2

When the email is made for the individual user, every link is run through the modifyHyperlink pipeline. The first step of this pipeline is "SkipAnchorLinks", which should make sure that anchors aren't changed, if it's an anchor. But this is exclusively if it's the anchor only - meaning it cannot be either an absolute or relative link. The link needs to start with the # character.

My guess is that you also have relative parts in your links? As long as it's for the same page, that should not be needed. If removed I think you will find it working (at least I have seen it work because of this before).

If that doesn't work for you, it could be interesting to add a processor to the start and the end of the modifyHyperlink pipeline to make sure that this is actually where it happens. You'd want to print the Aborted, GeneratedUrl and ServerUrl properties of the pipeline arguments in this case. If you don't want to make the debug steps manually, you can try this out: https://github.com/alphasolutionsrepo/pipeline.debug

3
  • My Link doesn't have any item or URL. I am assigning the #News1 dynamically. My Link is <a href="#News1" >MyLink</a>. When I was previewing in EXM Experience Manager or previewing as a page, it's rendering properly. But HREF is being deleted when Sitecore is generating the Newsletter.
    – Gulam Ali
    Commented Aug 30, 2018 at 10:39
  • Ahh yes - I know that bug too. Didn't realize it affected this as well, since we solved this before we got to trying anchors. I've just reflected the code and can see this is what happens for you as well. This is going to go somewhat off topic from the actual question of this thread. I can answer it, but maybe you should open a new issue with this? I will start writing down the answer and post it ASAP if you ask. Commented Aug 30, 2018 at 10:58
  • Ahh I see you edited the original question. Will make a new answer here. Commented Aug 30, 2018 at 11:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.