Kentico Xperience Xplorations: Determining a Request's Page Builder Mode
Tags: aspnetcore, xperience, cms, dotnet
Published: 2021-01-11 15:01:42

---
title: Kentico Xperience Xplorations: Determining a Request's Page Builder Mode
published: true
description: Let's explore how a request in an Xperience application determines the mode of the Page Builder and Preview features.
tags: aspnetcore, xperience, cms, dotnet
series: Kentico Xperience Xplorations
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/algcgsbuf2z4hv65yrw7.jpg
---

> In this post we'll be exploring how to determine the mode of a request in a Kentico Xperience 13.0 ASP.NET Core application, and also how to create a new class and interface to help us use this information in our code.

> You can skip the post and jump right into the [GitHub repository](https://github.com/wiredviews/xperience-page-builder-mode-tag-helper) and [NuGet package](https://www.nuget.org/packages/WiredViews.Xperience.PageBuilderModeTagHelper/) I've already created, or read on to see how it's made and how to use it 🧐!

## Starting Our Journey: The Modes of the Xperience Page Builder

In Kentico Xperience 13.0, the [Page Builder](https://docs.xperience.io/13tutorial/business-tutorial/using-the-xperience-page-builder) has several 'modes'. Each of these modes represents a different context under which a request to the ASP.NET Core application was made 🤔.

If a request was made from a content manager updating a Widget or creating a new Section in an Editable Area, then the Page Builder will be configured in ✍ 'Edit' mode.

Similarly, if the request was made from a [preview URL](https://docs.xperience.io/developing-websites/retrieving-content/adding-preview-mode-support), where a user can see the unpublished updates to content or the Page Builder configuration for a Page but cannot edit the Page, then the Page Builder is in 👀 'Preview' mode.

If the request was made to a live site URL, not using the Page Builder UI in the Content Management application, and not using a preview URL, then the Page Builder can be considered to be in ⚡ 'Live' mode.

## Digging Deeper: Using Xperience's APIs to Determine the Page Builder Mode

When making a request to our ASP.NET Core application to put the Page Builder into 'Edit' or 'Preview' mode, the URL has a distinct structure, which always includes a path starting with `/cmsctx/`. 

If the request has an `?editmode=1` query string parameter, then the request will be in 'Edit' mode, otherwise 'Preview' mode.

However, we never see these URLs in our application code, because Xperience's integration with ASP.NET Core does some rewriting of the request URL so that we don't have to worry about parsing it 🤓. Instead there are some documented APIs for accessing the state of the Page Builder.

These APIs can be found as extensions off the `IHttpContextAccessor`, which is an [ASP.NET Core type](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-5.0) that Xperience ensures is registered with the ASP.NET Core [Dependency Injection container](https://xperience.io/discover/blog/2020-11/asp-net-core-what-is-dependency-injection).

The following shows how we can determine if a request is in 'Preview' mode:

```csharp
public class HomeController : Controller
{
    private readonly IHttpContextAccessor accessor;

    public HomeController(IHttpContextAccessor accessor) =>
        this.accessor = accessor;

    public ActionResult Index()
    {
        // Found in the Kentico.Content.Web.Mvc namespace
        bool isPreviewMode = accessor.HttpContext
            .Kentico()
            .Preview()
            .Enabled;
    }
}
```

Likewise, here is how we determine if a request is in 'Edit' mode:

```csharp
public class HomeController : Controller
{
    private readonly IHttpContextAccessor accessor;

    public HomeController(IHttpContextAccessor accessor) =>
        this.accessor = accessor;

    public ActionResult Index()
    {
        // Found in the Kentico.PageBuilder.Web.Mvc namespace
        bool isEditMode = accessor.HttpContext
            .Kentico()
            .PageBuilder()
            .EditMode;
    }
}
```

## Into the Weeds: Clarifying Our Definition of Page Builder Modes

Technically, 'Preview' mode is independent of the Page Builder 😕, because the Preview feature is used to determine what content is displayed, while the Page Builder editing feature is used to determine whether or not to render the Page Builder Page Template, Section, Widget UI.

> **Note:** Any time a content manager can view the "Page" tab in the Content Management application and also click the "Save" button, then the request to display the ASP.NET Core page, within the "Page" tab, will be in 'Edit' mode - it doesn't matter whether or not you are using Widgets, Page Templates, or any other Page Builder features on the Page 😮.

> Take a look at the `private bool TryGetUrl(out string url)` method in `CMS\CMSModules\Content\CMSDesk\MVC\Edit.aspx.cs` to see how the Preview/Edit mode is set for a URL in the Pages module of the Content Management application 🤓.

Determining the current request mode is a bit trickier 😖 than just checking one of the above APIs - instead we will often need to check both.

Here are two tables showing what we can determine about a request by checking only one of the two APIs:

| Request Type             | Preview().Enabled == true | Preview().Enabled == false |
| ------------------------ | ------------------------- | -------------------------- |
| Site Visitor             | false                     | true                       |
| Readonly Content Manager | ?                         | false                      |
| Editing Content Manager  | ?                         | false                      |

| Request Type             | PageBuilder().EditMode == true | PageBuilder().EditMode == false |
| ------------------------ | ------------------------------ | ------------------------------- |
| Site Visitor             | false                          | ?                               |
| Readonly Content Manager | false                          | ?                               |
| Editing Content Manager  | true                           | false                           |

We can see that by checking only one API, we can have incomplete information.

## Finding a Path: IPageBuilderContext

It can be easy to forget we need to check both APIs to really determine the mode a request is currently in, so let's create a new abstraction, `IPageBuilderContext`, to help alleviate this problem 💪🏽.

Below we can see what that might look like:

```csharp
public interface IPageBuilderContext
{
    /// <summary>
    /// True if either IsLivePreviewMode or IsEditMode is true. 
    /// Also the opposite of IsLiveMode
    /// </summary>
    bool IsPreviewMode { get; }

    /// <summary>
    /// True if IsLivePreviewMode and IsEditMode is false.
    /// Also the opposite of IsPreviewMode
    /// </summary>
    bool IsLiveMode { get; }

    /// <summary>
    /// True if the current request is being made for
    /// a preview version of the Page with editing disabled
    /// </summary>
    bool IsLivePreviewMode { get; }

    /// <summary>
    /// True if the current request is being made for
    /// the Page Builder experience
    /// </summary>
    bool IsEditMode { get; }

    /// <summary>
    /// The current Mode as a PageBuilderMode value
    /// </summary>
    PageBuilderMode Mode { get; }

    /// <summary>
    /// The value of Mode as a string
    /// </summary>
    string ModeName();
}

/// <summary>
/// The various states that a request for a Page can be in,
/// in relation to the Page Builder
/// </summary>
public enum PageBuilderMode
{
    Live,
    LivePreview,
    Edit
}
```

With our abstraction defined, let's look at the implementation:

```csharp
public class PageBuilderContext : IPageBuilderContext
{
    private readonly IHttpContextAccessor accessor;

    public PageBuilderContext (IHttpContextAccessor accessor) =>
        this.accessor = accessor;

    public bool IsPreviewMode => 
        accessor.HttpContext
            .Kentico()
            .Preview()
            .Enabled;

    public bool IsLivePreviewMode => IsPreviewMode && !IsEditMode;

    public bool IsEditMode => 
        accessor.HttpContext
            .Kentico()
            .PageBuilder()
            .EditMode;

    public bool IsLiveMode => !IsPreviewMode;

    public PageBuilderMode Mode => 
        IsLiveMode
            ? PageBuilderMode.Live
            : IsLivePreviewMode
                ? PageBuilderMode.LivePreview
                : PageBuilderMode.Edit;

    public string ModeName() => 
       Enum.GetName(
           typeof(PageBuilderMode), 
           Mode) ?? "";
}
```

## Our Destination: Using IPageBuilderContext

Now that we understand the different modes of a request in our Xperience application, and we have a custom abstraction that neatly exposes that state, we can leverage it in our code 👍🏾.

First, we'll want to register our type with ASP.NET Core's dependency injection system:

```csharp
// Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<
        IPageBuilderContext, 
        PageBuilderModeContext
    >();
}
```

Now, let's return to our initial example, using the `HomeController`:

```csharp
public class HomeController : Controller
{
    private readonly IPageBuilderContext context;
    private readonly IProgressiveCache cache;
    private readonly IEventLogService service;

    public HomeController(
        IPageBuilderContext context,
        IProgressiveCache cache,
        IEventLogService service)
    {
        this.context = context;
        this.cache = cache;
        this.service = service;
    }

    public ActionResult Index()
    {
        if (context.IsEditMode)
        {
            return View(Calculate());
        }

        string name = $"calc|mode:{context.Mode}";

        var cs = new CacheSettings(
            cacheMinutes: 10,
            cacheItemNameParts: name);

        int calculation = cache.Load(Calculate, cs);

        return View(calculation);
    }

    private int Calculate(CacheSettings cs)
    {
        if (context.IsLiveMode)
        {
            log.LogInformation("Calculation", "CACHE_MISS");
        }

        // Define the cache dependency keys

        // Do something that takes some time to compute

        return calculation;
    }
}
```

In this example we are using the `IPageBuilderContext` to determine if the request is in 'Edit' mode, and if so, we skip caching completely and return an (expensively) calculated result.

Otherwise, we use the cache, ensuring that we have separate cache entries for "Live Preview" mode and "Live" mode by way of the cache item name.

> The cache item name could look like `"calc|mode:LivePreview"` or `"calc|mode:Live"`.

The reason for using the cache in `LivePreview` mode is that we want previewing the Page to be quick, especially if the preview URL is shared between viewers 😊.

But we also want to record all cache misses in Live mode to determine how effective our use of the cache is on the live site.

Since we don't want previews of the Page to effect the log, we have two different cached values 🧠.

...

If we find ourselves needing to access the mode of a request throughout our application, the usefulness of our `IPageBuilderContext` abstraction becomes apparent 👏!

## Conclusion

There's a lot going on in the Kentico Xperience platform, especially when using the Page Builder!

It's worth diving into the details and figuring out 🧐 how and why things work the way they do, especially when we want our application's code to use the platform correctly.

While Xperience provides us with ways of determining the mode of a request to our ASP.NET Core application, creating a reusable abstraction, like `IPageBuilderContext`, can help add more meaning to the information that the platform exposes.

In a follow-up post I'll show how we use the `IPageBuilderContext` to create a custom [ASP.NET Core Tag Helper](https://docs.microsoft.com/en-US/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-5.0) that we can use in our Razor views in the same way we use the `IPageBuilderContext` in our C# code.

Until then, thanks for reading 🙏!

## References

- [Enabling Xperience Preview Mode](https://docs.xperience.io/developing-websites/retrieving-content/adding-preview-mode-support)
- [Using the Request mode when Querying for Pages](https://docs.xperience.io/custom-development/working-with-pages-in-the-api#WorkingwithpagesintheAPI-Retrievingcontentinthepreviewmodeandpagebuilder)
- [Xperience Page Builder Mode Tag Helper](https://github.com/wiredviews/xperience-page-builder-mode-tag-helper) repository
- [Xperience Page Builder Mode Tag Helper](https://www.nuget.org/packages/WiredViews.Xperience.PageBuilderModeTagHelper/) NuGet package

---

<figcaption>Photo by <a href="https://unsplash.com/@jordanmadrid">Jordan Madrid
</a> on <a href="https://unsplash.com">Unsplash</a></figcaption>

---

We've put together a list over on [Kentico's GitHub account](https://github.com/Kentico/Home/blob/master/RESOURCES.md) of developer resources. Go check it out!

If you are looking for additional Kentico content, checkout the Kentico or Xperience tags here on DEV.

{% tag kentico %}

{% tag xperience %}

Or my [Kentico Xperience blog series](https://dev.to/seangwright/series), like:

- [Kentico Xperience Xplorations](https://dev.to/seangwright/series/8185)
- [Kentico Xperience MVC Widget Experiments](https://dev.to/seangwright/series/9483)
- [Kentico 12 - Design Patterns](https://dev.to/seangwright/series/1046)