In the last two posts, I’ve introduced how I have implemented Feature Toggles in Sitecore as well as how to improve performance using indexing and caching. In this final post of this series, I’ll discuss how I integrated this with SXA.
When you associate a rendering with a Togglable Feature, through the mvc.renderRendering
pipeline, it will control whether or not it’s rendered on the page. But what about the SXA Toolbox?
If a rendering is disabled by a Toggleable Feature, shouldn’t it also be hidden from the toolbox? Luckily, it’s pretty simple. You just need to provide an alternate implementation of Sitecore.XA.Foundation.Editing.Service.IAvailableRenderingsOrderingService
. Below is my implementation of the ToggledAvailableRenderingsOrderingService
:
public class ToggledAvailableRenderingsOrderingService : AvailableRenderingsOrderingService
{
private readonly IFeatureToggleProvider _featureToggleProvider;
public ToggledAvailableRenderingsOrderingService(IPresentationContext presentationContext, IMultisiteContext multisiteContext, IContentRepository contentRepository, IFeatureToggleProvider featureToggleProvider)
: base(presentationContext, multisiteContext, contentRepository)
{
_featureToggleProvider = featureToggleProvider ?? throw new ArgumentNullException(nameof(featureToggleProvider));
}
public override IList<AvailableRenderingEntry> GetOrderedRenderings(Item siteItem, IList<Item> renderings = null)
{
var orderedRenderings = base.GetOrderedRenderings(siteItem, renderings);
var disabledRenderingIds = _featureToggleProvider.GetDisabledRenderings().ToList();
if (!disabledRenderingIds.Any())
{
return orderedRenderings;
}
foreach (var id in disabledRenderingIds)
{
var disabledRendering = orderedRenderings.FirstOrDefault(x => string.Equals(x.RenderingItem.ID.ToString(), id, StringComparison.InvariantCultureIgnoreCase));
if (disabledRendering != null)
{
orderedRenderings.Remove(disabledRendering);
}
}
return orderedRenderings;
}
}
You can patch this implementation in nice and easy, like:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<services>
<register serviceType="Sitecore.XA.Foundation.Editing.Service.IAvailableRenderingsOrderingService, Sitecore.XA.Foundation.Editing"
implementationType="Foundation.FeatureToggles.Editing.Service.ToggledAvailableRenderingsOrderingService, Foundation.FeatureToggles" lifetime="Singleton"
patch:instead="*[@implementationType='Sitecore.XA.Foundation.Editing.Service.AvailableRenderingsOrderingService, Sitecore.XA.Foundation.Editing']"/>
</services>
</sitecore>
</configuration>
Now, when a rendering is disabled by a Toggleable Feature, the rendering won’t appear in the SXA Toolbox.
Experience Editor
What if you already have a rendering on your page that is disabled via a Toggleable Feature? If you’re in Experience Editor, would you rather it be hidden, as it is in Delivery, or maybe displayed, but with some visual indication that it’s disabled by a Toggleable Feature? I prefer the later!
To do this, we’ll add a few new classes that will add some markup to the html rendered in Experience Editor. First up, we need to provide an implementation of IMarker
to toss out some HTML when called.
public class FeatureDisabledWrapperMarker : IMarker
{
public string GetStart()
{
return "<div class=\"feature-disabled-wrapper\">";
}
public string GetEnd()
{
return "</div>";
}
}
Next, we need a Wrapper
object that will use this IMarker
implementation:
public class ToggledFeatureWarningWrapper : Wrapper
{
public ToggledFeatureWarningWrapper(TextWriter writer, IMarker marker) : base(writer, marker)
{
}
}
Now, we need to add a processor to the mvc.renderRendering
pipeline that will inject this markup.
public class AddToggledFeatureWarningWrapper : RenderRenderingProcessor
{
private readonly IPageMode _pageMode;
private readonly IFeatureToggleProvider _featureToggleProvider;
public AddToggledFeatureWarningWrapper(IPageMode pageMode, IFeatureToggleProvider featureToggleProvider)
{
_pageMode = pageMode ?? throw new ArgumentNullException(nameof(pageMode));
_featureToggleProvider = featureToggleProvider ?? throw new ArgumentNullException(nameof(featureToggleProvider));
}
public override void Process(RenderRenderingArgs args)
{
if (args.Rendered || !_pageMode.IsExperienceEditorEditing || args.Rendering.RenderingType == "Layout")
{
return;
}
if (args.Rendering.RenderingItem != null)
{
var isRenderingDisabled = _featureToggleProvider.IsRenderingDisabled(args.Rendering.RenderingItem.ID.ToString());
if (!isRenderingDisabled)
{
return;
}
args.Disposables.Add(new ToggledFeatureWarningWrapper(args.Writer, new FeatureDisabledWrapperMarker()));
}
}
}
Finally, we need to add one last processor to the mvc.renderRendering
pipeline that will dispose of our ToggledFeatureWarningWrapper
objects:
public class EndToggledFeatureWarningWrapper : RenderRenderingProcessor
{
public override void Process(RenderRenderingArgs args)
{
foreach (IDisposable wrapper in args.Disposables.OfType<ToggledFeatureWarningWrapper>())
{
wrapper.Dispose();
}
}
}
Now, update your patch file that modifies the mvc.renderRendering
pipeline to include these two new processors.
<pipelines>
<mvc.renderRendering>
<processor type="Foundation.FeatureToggles.Pipelines.RenderRendering.CheckFeatureToggle, Foundation.FeatureToggles"
resolve="true"
patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.RenderRendering.InitializeProfiling, Sitecore.Mvc']" />
<processor patch:after="processor[@type='Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper, Sitecore.Mvc.ExperienceEditor']" resolve="true"
type="Foundation.FeatureToggles.Pipelines.RenderRendering.AddToggledFeatureWarningWrapper, Foundation.FeatureToggles"/>
<processor patch:before="processor[@type='Sitecore.Mvc.Pipelines.Response.RenderRendering.AddRecordedHtmlToCache, Sitecore.Mvc']" resolve="true"
type="Foundation.FeatureToggles.Pipelines.RenderRendering.EndToggledFeatureWarningWrapper, Foundation.FeatureToggles"/>
</mvc.renderRendering>
</pipelines>
Last, but not least, add just a little CSS to add the “visual indication” that the rendering is disabled.
.feature-disabled-wrapper {
background-color: #F8FF00cc;
border: solid 2px #CBD000;
.component-content {
opacity: 30%;
}
}
Conclusion
Hopefully, these three posts have provided some insight on how you might implement Feature Toggles in Sitecore and integrate them with SXA.
Happy Sitecore trails, my friend!