Jacob Khan
Apr 5, 2016
  9020
(6 votes)

Preview Content in View Mode

One of our enterprise customers recently approached me with an interesting challenge. They wanted to generate a URL of unpublished content and send it to an external party for validation of look and feel and an obligatory “thumbs up” before publishing.

In CMS 6 this could be achieved by going to the template URL and adding the version id. www.alloy.com/templates/pages/standardpage.aspx?id=5_5

However editors wouldn’t know the template / controller in newer version of Episerver, so I needed a different way of accomplishing this.

I used partial routing to fetch the previous version and show that in the URL.

 public class PreviewPartialRouter : IPartialRouter<SitePageData, PageVersion>
    {

        public PartialRouteData GetPartialVirtualPath(PageVersion version, string language, RouteValueDictionary routeValues, RequestContext requestContext)
        {
            var contentLink = ContentRoute.GetValue("node", requestContext, routeValues) as ContentReference;
         
            if (PageEditing.PageIsInEditMode)
            {
                return null;
            }

            return new PartialRouteData
            {
                BasePathRoot = contentLink,
                PartialVirtualPath = String.Format("{0}/", version.ToString())
            };

        }


        public object RoutePartial(SitePageData content, EPiServer.Web.Routing.Segments.SegmentContext segmentContext)
        {
            var namePart = segmentContext.GetNextValue(segmentContext.RemainingPath);
            if (!String.IsNullOrEmpty(namePart.Next))
            {
                var version = HttpUtility.UrlDecode(namePart.Next);
                try
                {
                    var versionNumber = new ContentReference(version);

                    if (versionNumber != null)
                    {
                        var remaingPath = namePart.Remaining;
                        //Update RemainingPath on context.
                        segmentContext.RemainingPath = remaingPath;
                        var versionRepository = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<IContentVersionRepository>();

                        var ActualVersion = versionRepository.Load(versionNumber);

                        segmentContext.RoutedContentLink = ActualVersion.ContentLink;
                        var contentRepository = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<IContentLoader>();
                        var pd = contentRepository.Get<SitePageData>(ActualVersion.ContentLink);
                        segmentContext.SetCustomRouteData<PageVersion>("version", (PageVersion)ActualVersion);


                        return pd;
                    }        
                 }catch{
                    return content;
                 }

              

                       
            }

            return null;
        }	

This example is with Alloy, where I use sitepagedata as my base. As you can see, I am getting a request in for a partial route meaning the main page url /”something”. “Something” refers to ContentReference.ID + “_” + Version Workid (5_18). This enables me to switch the current version to an older one.

I also need to register my partial route:

 [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class PreviewInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
          
            var partialRouter = new PreviewPartialRouter();

            RouteTable.Routes.RegisterPartialRouter<SitePageData, PageVersion>(partialRouter);

        }

      
        public void Preload(string[] parameters) { }

        public void Uninitialize(InitializationEngine context)
        {
           
    
        }
    }

Rather simple if you ask me, but works well. To get the version ID, go into the version gadget and pick the previous version you would like to send. Since it isn’t always super easy to find a version and work id, I created a gadget which creates a simple version link for you

PreviewContent

You can download the code here. Just place it in your project under the Business folder if you are using Alloy. In your own project, make sure to fix the path for the PreviewComponent and any namespace or SitePageData issues to your base

https://drive.google.com/open?id=0B_XVbM6ery9FcXEzbkZ4Skt3YUE
Apr 05, 2016

Comments

Marija Jemuovic
Marija Jemuovic Apr 6, 2016 01:46 PM

Very useful, thx!

Arve Systad
Arve Systad Apr 7, 2016 03:34 PM

Should be implemented right into the product itself, I'd say. 

That said, you DO have the actual preview button (top left, rectangle with magnifying glass) that removes all editing stuff/dojo-widget-mumbojumbo and shows the page as  is. Works fairly well too :p

Apr 7, 2016 04:26 PM

Nice solution! And the next challange is to activate the preview feature used in the Projects feature so all blocks that still are unpulished on the page also becomes visible...

Jacob Khan
Jacob Khan Apr 7, 2016 10:45 PM

@fredrik, great idea to think about the projects instead. Maybe Just add the Project in the URL and then load in the Project view. I'll get to work :)

@arne, you are right, the preview button works for this but we sometimes have the requirements that people outside of Epi edit mode wants to review content. 

eGandalf
eGandalf May 25, 2016 11:32 PM

If you give the Anonymous role Read + Change permissions to the content the anonymous preview works. I *think* this would be safe as any anonymous (non-authenticated) user would never have access to the authoring interface or API-level access (keep the role out of the ServiceAPI, for example). Once someone is authenticated, they're no longer anonymous. 

Barring some other solution, I'd probably implement an IP-range or domain restriction in Jacob's code above so that it would only function if the incoming request was from the right network or was against the right domain - say an internal authoring domain.

Great solution and easy to implement!

Jared Wooden
Jared Wooden Oct 12, 2016 07:35 PM

@jacob, did you ever get this working with projects? I was working on something similar for one of our customers when I came across this [much simpler] alternative. Thanks!

Please login to comment.
Latest blogs
Missing Properties tool for Optimizely CMS

If you have been working with Optimizely CMS for a while you have probably accumulated some technical debt in your property definitions. When you...

Per Nergård (MVP) | Mar 10, 2026

AI Generated Optimizely Developer Newsletter

Updates in the Optimizely ecosystem are everywhere: blog posts, forums, release notes, NuGet packages, and documentation changes. This newsletter...

Allan Thraen | Mar 10, 2026 |

Lessons from Building Production-Ready Opal Tools

AI tools are becoming a normal part of modern digital platforms. With  Optimizely Opal , teams can build tools that automate real tasks across the...

Praful Jangid | Mar 7, 2026

My Takeaway from Optimizely Opal Agents in Action 2026 - What Agentic AI Means for the Future of Digital Marketing

I would like to share with you what stayed in my head after this amazing virtual event organized by Optimizely. Agents in Action 2026 , a live...

Augusto Davalos | Mar 6, 2026

From Vision to Velocity: Introducing the Optimizely MVP Technical Roundtable

Digital transformation is a two-sided coin. On one side, you have the high-level strategy, the business cases, the customer journeys, and the...

Patrick Lam | Mar 6, 2026

Commerce 14.45.0 is incompatible with CMS 12.34.2 (but that's an easy fix!)

Incompatible is a strong word, but that is to get your attention. This is one of the small thing that can be overlooked, but if you run into it, it...

Quan Mai | Mar 5, 2026