Sometimes when you creating mobile versions of a site, you could decide to show some page from the desktop version and do not create the mobile layout for them (e.g. they are less important than other pages or not visited frequently). Sitecore has a setting for that called Fallback Device. However, when you are working with the MVC layout it is not really working. A nice moment over here is that it is really easy to enable it.

Implementation

MVC Layout Background

In order to add fix you need to understand how Sitecore understands that you need to switch to MVC and find right place for fix. All magic starts in httpRequestBegin processors section:

1
<processor type="Sitecore.Mvc.Pipelines.HttpRequest.TransferMvcLayout, Sitecore.Mvc" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.LayoutResolver, Sitecore.Kernel']" />

When your page item is resolved and we could get information about its layout Sitecore understands that you are using .cshtml layout and transfer processing to MVC.

During the processing Sitecore MVC execute following section:

1
2
3
<mvc.getPageRendering>
    <processor type="Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc"/>
</mvc.getPageRendering>

This is exactly the code we were searching for ๐Ÿ˜Š

Pipeline Upgrade

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace Example {
    using System.Collections.Generic;
    using System.Linq;
    using Sitecore;
    using Sitecore.Data.Items;
    using Sitecore.Mvc.Extensions;
    using Sitecore.Mvc.Pipelines.Response.GetPageRendering;
    using Sitecore.Mvc.Presentation;

    public class GetLayoutRenderingWithFallback: GetLayoutRendering {
        protected override Rendering SelectLayoutRendering(List renderings, GetPageRenderingArgs args) {
            var result = base.SelectLayoutRendering(renderings, args);

            if (result != null) {
                return result;
            }

            var device = Context.Device;
            while (device != null) {
                var b = device.ValueOrDefault((DeviceItem d) = > d.ID.ToGuid());
                foreach(var current in renderings.Where(current = > current.DeviceId == b && current.RenderingType == "Layout")) {
                    // set fallback device as current.
                    Context.Device = device;
                    return current;
                }

                device = Context.Device.FallbackDevice;
            }

            return null;
        }
    }
}

We inherit our pipeline from Sitecore one and add some logic to handle cases when base implementation was not able to resolve layout (see source).

And the last thing that we need to do is to replace the standard pipeline with a Sitecore config patch. We are done.


Follow me on twitter @true_shoorik. Share if the post was useful for you.