1

I have an item with a General Link field named "MyLinkField". In my view I have the following:

@Sitecore.Html().Field("MyLinkField")

That works great to output the link. Now I have added a multiselect field to the source item called "QueryStringItems". This allows the author to select one or more query string content items. Each query string content item has a field called "Key" and field called "Value". This allows us to have a central listing of query string key/value pairs. We want the author to be able to easily select which key/value pairs they would like to add to the query string for a given link.

Is this even possible using the out of the box Sitecore helpers? Is there some way I could write a custom helper to extend the out of the box one to also include any selected QueryStringItems?

UPDATE: Building off of Dan's answer, here is the code that I got to work both with Internal and External General Links.

public class CustomLinkRenderer : LinkRenderer
{
    public CustomLinkRenderer(Item item) : base(item)
    { }

    protected override string GetUrl(XmlField field)
    {
        if(field?.InnerField?.Item != null)
        {
            MultilistField queryStringItemsField = (MultilistField)field.InnerField.Item.Fields["Query String Items"];
            Item[] queryStringItems = queryStringItemsField?.GetItems();
            if(queryStringItems != null && queryStringItems.Length > 0)
            {
                SafeDictionary<string> existingQueryString = WebUtil.ParseQueryString(field.GetAttribute("querystring"));
                foreach(Item queryStringItem in queryStringItems)
                {
                    existingQueryString[queryStringItem["Key"]] = queryStringItem["Value"];
                }
                field.SetAttribute("querystring", WebUtil.BuildQueryString(existingQueryString, false));

                var linkType = field.GetAttribute("linktype");
                if(linkType == "external")
                {
                    var url = field.GetAttribute("url");
                    if(!string.IsNullOrEmpty(url))
                    {
                        if (!url.StartsWith("/", StringComparison.InvariantCulture) && url.IndexOf("://", StringComparison.InvariantCulture) < 0)
                            url = "http://" + url;
                        var queryString = WebUtil.BuildQueryString(existingQueryString, false);

                        if (url.Contains("?"))
                        { url += "&" + queryString; }
                        else
                        { url += "?" + queryString; }

                        return url;
                    }
                }
                else
                {
                    return (new LinkUrl()).GetUrl(field, this.Item.Database);
                }
            }
        }
        return base.GetUrl(field);
    }
}
2

I'm not sure if there's a way using the helpers available in the view, but you could certainly replace a few layers of the renderField pipeline to look for any QueryStringItems data and append it to the links.

You'll need to extend a few classes:

1. Sitecore.Pipelines.RenderField.GetLinkFieldValue

using Sitecore.Pipelines.RenderField;

public class GetLinkFieldValueWithQueryStringItems : GetLinkFieldValue
{
    protected override LinkRenderer CreateRenderer(Item item)
    {
        return new LinkRendererWithQueryStringItems(item);
    }
}

2. Sitecore.Xml.Xsl.LinkRenderer

using Sitecore.Xml.Xsl;

/// <summary>
/// Implements the Link Renderer.
/// </summary>
public class LinkRendererWithQueryStringItems : LinkRenderer
{
    protected virtual string GetUrl(XmlField field)
    {
        if (field?.InnerField?.Item != null)
        {
            MultilistField queryStringItemsField = (MultilistField)field.InnerField.Item.Fields["QueryStringItems"];
            Item[] queryStringItems = queryStringItemsField?.GetItems();
            if (queryStringItems != null && queryStringItems.Length > 0)
            {
                SafeDictionary<string> existingQueryString = WebUtil.ParseQueryString(field.GetAttribute("querystring"));
                foreach (Item queryStringItem in queryStringItems)
                {
                    existingQueryString[queryStringItem["Key"]] = queryStringItem["Value"];
                }
                field.SetAttribute("querystring", WebUtil.BuildQueryString(existingQueryString, false));
                return (new LinkUrl()).GetUrl(field, this.Item.Database);
            }
        }
        return base.GetUrl(field)
    }
}

And the obligatory patch config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
        <renderField>
            <processor patch:instead="processor[@type='Sitecore.Pipelines.RenderField.GetLinkFieldValue, Sitecore.Kernel']" type="Custom.Web.Pipelines.RenderField.GetLinkFieldValueWithQueryStringItems, Custom.Web"/>
        </renderField>
    </pipelines>
  </sitecore>
</configuration>

Full disclosure: I've not tested this code exactly as-is. If you run into issues that need to be resolved, please comment below so I can fix them here for future users.

|improve this answer|||||
  • This is close. The one problem I see is that the field "QueryStringItems" is not a field on the target item that I am linking to. It is a field on the datasource item of the view itself. I assume that the LinkProvider only knows about the item that you are linking to. When you call GetItemUrl you are feeding it the item that you want to link to. – Corey Burnett Mar 11 '19 at 19:56
  • Oh, I misread that. I've edited it to pull them from the original item, instead. It's a little more complex, but it should work. – Dan Sinclair Mar 11 '19 at 20:42
  • This seems to only work for internal General Links. Is there a way to get this to work also for external General Links? I get the sense that external links don't pay any attention to the "querystring" attribute. – Corey Burnett Mar 12 '19 at 13:40
  • Actually I just figured out how to get it to work with external General Links. Thanks so much for pointing me in the right direction. Your answer was perfect! – Corey Burnett Mar 12 '19 at 14:16
  • Out of curiosity, how did you address external links? Did you override a different renderField pipeline processor? – Dan Sinclair Mar 12 '19 at 14:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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