Override GlassView.RenderLink/BeginRenderLink for internal items in Sitecore

We are using sitecore 7.2 with TDS and glassmapper, and we came across a need where we needed special logic to render links to specifically internal items.

In our company we follow a certain pattern with items, something I am sure everyone follows themselves. And as a part of the standard set of base fields that every navigable item in sitecore has, we have a Navigation Title and a Redirect Link.

  • A Navigation Title serves the purpose of letting the link text to an item rendered anywhere on the site to be managed separately from the item name / headline itself. You might want a shorter title to links to a page, while the headline of the page itself might be more descriptive. So when we use GlassView.RenderLink – we would like the provision to fallback to this Navigation Title field if no description is manually managed by the content author.
  • A Redirect Link serves the purpose of allowing the content author to temporarily or permanently having an item redirect to a different page (internal or external). In case of external links, this method basically allows us to have placeholder Sitecore items for external links too! Which is especially useful when we want them to show up in Content Tree Navigation renderings. The Redirect Link is at the end of the day a link field. If the content author wanted the external link set in this field to open in a new tab (considering its an external link!), GlassView.RenderLink wouldn’t know to make this happen since this is custom logic we wanted in place based on our custom field. So we needed to make an update to GlassView.RenderLink – to be able to honor the redirect targets as well.

We wrote overrides for RenderLink and also BeginRenderLink methods to cover our bases.

GlassView.RenderLink

        public HtmlString RenderLink<T>(T model, Expression<Func<T, object>> field, object attributes = null,
            bool isEditable = false, string contents = null, bool isContentEmpty = false)
        {
            if (field == null)
                return new HtmlString(string.Empty);

            Link tempLink = field.Compile().Invoke(model) as Link;

            if (tempLink.IsRedirectNewTab())
            {
                if (attributes == null)
                {
                    attributes = new NameValueCollection { { "target", "_blank" } };
                }
                else if (attributes is NameValueCollection)
                {
                    NameValueCollection currentAttributes = attributes as NameValueCollection;

                    if (currentAttributes["target"] != null)
                    {
                        currentAttributes.Remove("target");
                    }
                    currentAttributes.Add(new NameValueCollection { { "target", "_blank" } });
                    attributes = currentAttributes;
                }
            }

            return new HtmlString(GlassHtml.RenderLink(model, field, attributes, isEditable,
                GlobalHelper.GetLinkContents(field.Compile().Invoke(model) as Link, contents, isContentEmpty)));
        }

        public HtmlString RenderLink(Expression<Func<TModel, object>> field, object attributes = null,
            bool isEditable = false, string contents = null, bool isContentEmpty = false)
        {
            return RenderLink(Model, field, attributes, isEditable, contents, isContentEmpty);
        }

GlassView.BeginRenderLink

        public override RenderingResult BeginRenderLink(Expression<Func<TModel, object>> field,
            object attributes = null, bool isEditable = false)
        {
            return BeginRenderLink(Model, field, attributes, isEditable);
        }

        public override RenderingResult BeginRenderLink<T>(T model, Expression<Func<T, object>> field,
            object attributes = null, bool isEditable = false)
        {
            Link tempLink = field.Compile().Invoke(model) as Link;

            if (tempLink.IsRedirectNewTab())
            {
                if (attributes == null)
                {
                    attributes = new NameValueCollection { { "target", "_blank" } };
                }
                else if (attributes is NameValueCollection)
                {
                    NameValueCollection currentAttributes = attributes as NameValueCollection;

                    if (currentAttributes["target"] != null)
                    {
                        currentAttributes.Remove("target");
                    }

                    currentAttributes.Add(new NameValueCollection { { "target", "_blank" } });
                    attributes = currentAttributes;
                }
            }
            return base.BeginRenderLink(model, field, attributes, isEditable);
        }

In BeginRenderLink – we usually are not too concerned about the text – so we haven’t overridden that here.
We have used 2 helper methods here:

  • IsRedirectNewTab() – an extension method to glass Link class to determine if the target of the link is an internal item AND has a redirect link set on it AND the redirect link has a target set to open in a new tab. We could ofcourse go a step further to just pull the target from the link, but the business logic here, called for specifying new window only.
  • GlobalHelper.GetLinkContents() – would return the Navigation Title field – IF no link description was managed AND the link pointed to an internal item.
        public static bool IsRedirectNewTab(this Link link)
        {
            if (link == null || !link.IsValid() || link.Type != LinkType.Internal || link.TargetId == new Guid()) return false;

            Base_Navigation baseNavigation = SharedController.SitecoreCurrentContext.GetItem<Base_Navigation>(link.TargetId);

            if (baseNavigation != null && baseNavigation.Redirect_Link.IsValid())
            {
                return baseNavigation.Redirect_Link.Target.ToLower() == "_blank";
            }

            return false;
        }

Note: We use the singleton pattern to get a single instance of the Glass.Mapper.Sc.SitecoreContext class, which is in SharedController.SitecoreCurrentContext.
Also, we have extension methods for certain classes to validate the corresponding objects (IsValid()). Please find a short note here: Glass field validations in Sitecore views

        public static string GetLinkContents(Link link, string contents, bool isContentEmpty = false)
        {
            if (string.IsNullOrEmpty(contents) && link != null && string.IsNullOrWhiteSpace(link.Text) && !isContentEmpty)
            {
                if (link.Type == LinkType.Internal)
                {
                    Base_Navigation targetItem = SharedController.SitecoreCurrentContext.GetItem<Base_Navigation>(link.TargetId);
                    if (targetItem != null)
                        return targetItem.Navigation_Title;
                }
            }
            return contents;
        }

So now RenderLink will do all the additional processing that we needed without having to make any change to the calls we make in the views to the RenderLink method.
Main thing to note here though, all our views inherit from the custom class we created (instead of GlassView) where we placed these overrides.

namespace MySite.Customizations.Customized_Glass
{
    public abstract class CustomGlassView<TModel> : GlassView<TModel>
    {
        public HtmlString RenderLink<T>(T model, Expression<Func<T, object>> field, object attributes = null,
            bool isEditable = false, string contents = null, bool isContentEmpty = false)
        {
            ...
        }

        public HtmlString RenderLink(Expression<Func<TModel, object>> field, object attributes = null,
            bool isEditable = false, string contents = null, bool isContentEmpty = false)
        {
            ...
        }

        public override RenderingResult BeginRenderLink(Expression<Func<TModel, object>> field,
            object attributes = null, bool isEditable = false)
        {
            ...
        }

        public override RenderingResult BeginRenderLink<T>(T model, Expression<Func<T, object>> field,
            object attributes = null, bool isEditable = false)
        {
            ...
        }
    }
}

So our views all inherit CustomGlassView:

@inherits MySite.Customizations.Customized_Glass.CustomGlassView<...>
Advertisements

One thought on “Override GlassView.RenderLink/BeginRenderLink for internal items in Sitecore

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s