Posted in: Comments

Update: If you’re using EPiServer 7 (or later version), please see my updated post!

If you move a page in the page tree, the url to the page will change. There are numerous available solutions to handle this, e.g. by saving the old url and set up a re-direction to the new url.

In some cases we’re sending emails with the url in the message, but what happens if we change the url to the page after the email is sent? In this case we could send a permanent link instead, that never change, even when the page is moved. This is also useful when using commenting systems from 3rd parties, like Facebook, where the comments are tied to the url of the page.

First we need a way to create the urls, in this case we’re just using an extension method:

public static string PermanentLink(this PageData page)
{
    var url = new UriBuilder(Settings.Instance.SiteUrl);

    url.Path = string.Format("/permanentlink/{0}", page.ContentGuid);

    return url.Uri.AbsoluteUri;
}

The generated link will look like http://dodavinkeln.se/permanentlink/263581a1-6575-4938-b558-e871a1864f66.

Then we need a handler that maps this url to the page again. In this case we’re going to create a Http Handler, unfortunately you can’t create a route to a Http Handler, so we also need to create a Route Handler and then return our Http Handler.

The Http Handler gets the incoming page guid from the Route Data, then we map the guid to a Page Reference and finally fetch the page and do a re-direct. ExternalUrl() is an extension method that returns the friendly url to the page, which is not covered in this post.

namespace DV.HttpHandlers
{
    using EPiServer;
    using EPiServer.Core;
    using EPiServer.ServiceLocation;
    using EPiServer.Web;
    using System;
    using System.Web;
    using System.Web.Routing;

    public class PermanentLinkRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new PermanentLinkHttpHandler();
        }
    }

    public class PermanentLinkHttpHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            Guid guid;

            if (Guid.TryParse((string)context.Request.RequestContext.RouteData.Values["guid"], out guid))
            {
                var linkMap = PermanentLinkMapStore.Find(guid, PermanentLinkMapStore.StorePreference.Page) as PermanentContentLinkMap;

                if (linkMap != null)
                {
                    var repo = ServiceLocator.Current.GetInstance<IContentRepository>();

                    try
                    {
                        var page = repo.Get<PageData>(linkMap.ContentReference);

                        context.Response.RedirectPermanent(page.ExternalUrl(false));
                    }
                    catch (Exception)
                    {
                    }
                }
            }

            context.Response.Status = "404 Not Found";
            context.Response.StatusCode = 404;
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

We also need to register the route, we can do this in an Initializable Module or on application start in Global. In this case we’re doing it in the application start event:

namespace DV
{
    using System;
    using System.Web.Routing;
    using DV.HttpHandlers;

    public class Global : EPiServer.Global
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.Add(new Route("permanentlink/{guid}", new PermanentLinkRouteHandler()));
        }
    }
}

We could extend the links to handle language and other parameters as well. But I leave that to you.