Custom AliasResolver for shared data items without presentation

One of my editors tried to create an alias for a data item. This is usually straight forward, but since the data item doesn't have presentation the alias goes to the "Layout Not Found" page. My website uses dynamic templates for the rendering and a data item from a shared folder. This initially broke AliasResolver as the data item linked to in the Alias doesn't have any presentation to display the item. To resolve the issue, I wrote a custom AliasResolver class that checks if a presentation exists for the device before setting the context.item. If presentation is missing, it resolves it by using the link manager to get the proper link and redirecting to that url.


Custom Alias Resolver: Download CustomAliasResolver.cs

public class CustomAliasResolver : AliasResolver 
    {

        public override void Process(HttpRequestArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if (!Settings.AliasesActive)
            {
                Tracer.Warning("Aliases are not active.");
                return;
            }
            Sitecore.Data.Database database = Sitecore.Context.Database;
            if (database == null)
            {
                Tracer.Warning("There is no context database in AliasResover.");
                return;
            }
            Profiler.StartOperation("Resolve alias.");
            if (database.Aliases.Exists(args.LocalPath) && !this.ProcessItem(args))
            {
                this.ProcessExternalUrl(args);
            }
            Profiler.EndOperation();
        }

        private bool ProcessItem(HttpRequestArgs args)
        {
            Sitecore.Data.ID targetID = Sitecore.Context.Database.Aliases.GetTargetID(args.LocalPath);
            if (targetID.IsNull)
            {
                Tracer.Error(string.Concat("An alias for \"", args.LocalPath, "\" exists, but points to a non-existing item."));
                return false;
            }
            Sitecore.Data.Items.Item item = args.GetItem(targetID);
            if (item != null)
            {
                //check if the item has a layout before processing
                if (DoesItemHaveLayout(item))
                {
                    this.ProcessItem(args, item);
                }
                else
                {
                    //return false to force call to ProcessExternalUrl which has additional logic to redirect to the display item
                    return false; //use link manager to find proper url
                }
            }
            return true;
        }

        /** 
         *  This method checks whether the item has layout for the context device
         **/
        private bool DoesItemHaveLayout(Sitecore.Data.Items.Item item)
        {
            if (item != null)
            {
                Sitecore.Data.Items.LayoutItem layoutItem = item.Visualization.GetLayout(Sitecore.Context.Device);
                if (layoutItem != null)
                {
                    return true;
                }
            }
            
            return false;
        }  

        private void ProcessItem(HttpRequestArgs args, Sitecore.Data.Items.Item target)
        {
            if (Sitecore.Context.Item == null)
            {
                Sitecore.Context.Item = target;
                object[] localPath = new object[] { "Using alias for \"", args.LocalPath, "\" which points to \"", target.ID, "\"" };
                Tracer.Info(string.Concat(localPath));
            }
        }

        private void ProcessExternalUrl(HttpRequestArgs args)
        {
            Sitecore.Data.ID targetID = Sitecore.Context.Database.Aliases.GetTargetID(args.LocalPath);
            string targetUrl = Sitecore.Context.Database.Aliases.GetTargetUrl(args.LocalPath);
            if (targetUrl.Length > 0)
            {
                //default out of box behavior
                this.ProcessExternalUrl(targetUrl);
            }
            else
            {
                //add logic to support shared data content items with no presentation
                Sitecore.Data.Items.Item item = args.GetItem(targetID);
                if(item!=null){
                    //use link manager to determine url
                    targetUrl = LinkManager.GetItemUrl(item);
                    //redirect to url 
                    HttpContext.Current.Response.RedirectLocation = targetUrl;
                    HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
                    HttpContext.Current.Response.StatusDescription = "301 Moved Permanently";
                    HttpContext.Current.Response.End();                    
                }
            }
        }

        private void ProcessExternalUrl(string path)
        {
            if (Sitecore.Context.Page.FilePath.Length > 0)
            {
                return;
            }
            Sitecore.Context.Page.FilePath = path;
        }
    }

Use a patch configuration file to overwrite the existing processor. Full configuration file


  <processor patch:instead="processor[@type='Sitecore.Pipelines.HttpRequest.AliasResolver, Sitecore.Kernel']" 
type="YourDLLNameSpace.Processors.CustomAliasResolver, YourDLLNameSpace">

NOTE: Do not use this logic if you have modified LinkManager to return Alias first as that may cause issues.

Comments