Pipeline your pop-ups/modal-windows in Sitecore

Dear sitecorians, long time no see! It has been a while since I wrote a blog post but I’m back again 🙂

Today I would like to share with you guys how to use pipelines to show “temporary content”. In this case, it will be a modal-window.
It’s nothing new but I love the “clean” way by using a pipeline to render the modal-window.
All credits goes to this great blog-post – Wrapping a view rendering to automatically add additional markup

So my scenario is this, a modal-window/pop-up needs to be shown on every page(until the visitor has accepted and closed it). As always, you can do this in many many ways…
My approach will be as the blog-post(above) suggested, by adding a “wrapping view” to the “mvc.getrender” pipeline. The wrapping view will present a modal-window. This post is great and it’s still rocking!

How do we do this? First, we need to create a wrapper model. We need it to store the original renderer.

using Sitecore.Mvc.Presentation;
namespace Sandbox.Feature.SomeModule.Models
{
    public class WrapperModel
    {
        public ViewRenderer Renderer { get; set; }
    }
}

Next thing will be the actual view, where we will present the modal-window. The magic here is that we include the original view rendering as a partial view.

@using Sandbox.Foundation.Dictionary.Extensions
@using Sitecore.Mvc

@model Sandbox.Feature.SomeModule.Models.WrapperModel

@Html.Partial(Model.Renderer.ViewPath, Model.Renderer.Model)

<div class="modal fade in" id="pipelineModal" tabindex="-1" role="dialog" aria-hidden="true" style="display: block;">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" style="text-align:center" id="exampleModalCenterTitle">@Html.Sitecore().Dictionary("/ModalWindow/Header", "PIPELINE YOUR MODAL WINDOWS")</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
            </div>
            <div class="modal-body">
                @Html.Raw(Html.Sitecore().DictionaryRichText("/ModalWindow/Body", "This is a rich text"))

            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" onclick="javascript: document.getElementById('pipelineModal').className = 'modal fade out'; document.getElementById('pipelineModal').style.display = 'none';" data-dismiss="modal">@Html.Sitecore().Dictionary("/ModalWindow/CloseButton", "Close")</button>

            </div>
        </div>
    </div>
</div>

Notice the

Html.Sitecore().DictionaryRichText... 

read all about it in Adding Rich Text to your dictionary in Sitecore 😉

We also need to wrap the viewRenderer, let’s call it WrapperViewRenderer. The reason is that we want to be able to use the caching mechanism.

using Sitecore.Mvc.Presentation;

namespace Sandbox.Feature.MyModule.Models
{
    public class WrapperViewRenderer : ViewRenderer
    {
        private readonly Renderer _innerRenderer;
        public WrapperViewRenderer(Renderer innerRenderer)
        {
            _innerRenderer = innerRenderer;
        }

        public override string CacheKey => _innerRenderer.CacheKey;
    }
}

Ok guys, what’s left is the actual pipeline. Let’s call it ModalMeProcessor. We are inheriting the GetRendererProcessor and hooking up to the “mvc.getRenderer”. We have some failsafe checks in case the view is missing. We also want to add the modal-window to the footer rendering.

using System;
using System.IO;
using System.Web;
using Sandbox.Feature.SomeModule.Models;
using Sitecore.Diagnostics;
using Sitecore.Mvc.Pipelines.Response.GetRenderer;
using Sitecore.Mvc.Presentation;


namespace Sandbox.Feature.SomeModule.Pipelines.MvcGetRenderer
{
    public class ModalMeProcessor : GetRendererProcessor
    {
        private static bool _viewExists = false;


        private static bool ViewExists
        {
            get
            {
                if (_viewExists)
                    return _viewExists;

                _viewExists =
                    File.Exists(
                        $"{HttpContext.Current.Server.MapPath("/")}{Constants.ViewPaths.ModalMeView}");

                return _viewExists;
            }
        }

        public override void Process(GetRendererArgs args)
        {

            Assert.ArgumentNotNull(args, "args");

            // check site
            if (Sitecore.Context.GetSiteName() != "SomeSiteName")
                return;

            // check if view renderer
            if (!(args.Result is ViewRenderer))
                return;

            if (args.Rendering == null)
                return;

            // We look for Footer rendering, this is where we will add the modal-window
            if (args.Rendering.RenderingItemPath != Constants.Renderings.Footer)
                return;

            var wrapperViewRenderer = GetWrapperViewRenderer(args);

            if (wrapperViewRenderer == null)
                return;

            args.Result = wrapperViewRenderer;

        }

        private WrapperViewRenderer GetWrapperViewRenderer(GetRendererArgs args)
        {
            try
            {
                // View is missing
                if (!ViewExists)
                {
                    Log.Error($"View {Constants.ViewPaths.ModalMeView} is missing", this);
                    return null;
                }

                var model = new WrapperModel
                {
                    Renderer = (ViewRenderer)args.Result,
                };

                var wrapperResult = new WrapperViewRenderer(args.Result)
                {
                    Model = model,
                    Rendering = args.Rendering,
                    ViewPath = Constants.ViewPaths.ModalMeView
                };

                return wrapperResult;
            }
            catch (Exception ex)
            {
                Log.Error("Pipeline ModalMeProcessor: GetWrapperViewRenderer", ex);
                return null;
            }
        }
    }



}

The magic here is the GetWrapperViewRenderer, which will set the args.Result in the ModalMeProcessor pipeline.

Finally the config patch for the pipeline:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <mvc.getRenderer>
        <processor patch:after="processor[@type='Sitecore.Mvc.Pipelines.Response.GetRenderer.GetViewRenderer, Sitecore.Mvc']"
                   type="Sandbox.Feature.MyModule.Pipelines.MvcGetRenderer.ModalMeProcessor, Sandbox.Feature.MyModule" />
      </mvc.getRenderer>
    </pipelines>
  </sitecore>
</configuration>

If you don’t want to show the modal-window anymore, just disable the config patch 😉

A big “Thank You” to all the great post’s out there describing this topic:
Wrapping a view rendering to automatically add additional markup
Wrapping your Sitecore renderings in a container
Synthesis does this for diagnostics
I’ll bet it’s more out there…

That’s all for now folks 🙂


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.