Sitecore’s Experience Editor aids Content Author/Editor in creating pages effortlessly, however, it can be bewildering at the same time for authors who are creating new pages with minimal knowledge about the website’s placeholders and renderings.
Experience Editor by default does not provide details about which rendering is present and what data source it is using, you may have to check rendering properties to get some of this information.
Adding Rendering Wrappers in Experience editor is a way to help Content Authors to build and edit pages more efficiently by highlighting each rendering’s hierarchical details making page structure distinguishable and less confusing if multiple child components are present.
Lets see how it can be done in Sitecore and Sitecore JSS.
To achieve this, we need to create a processor that implements RenderRenderingProcessor and patch it after AddWrapper Pipeline.
Below is snippet of Class extending RenderRenderingProcessor:
public class AddPageEditorMetadata : RenderRenderingProcessor { private readonly List<Regex> _parentNameMatcher = new List<Regex>(); private readonly List<Regex> _itemNameMatcher = new List<Regex>(); // checks the immediate parent folder of the rendering against each pattern in the list. If any pattern matches, the rendering is wrapped. public void AddParentNamePattern(string pattern) { _parentNameMatcher.Add(new Regex(pattern)); } //checks the name of the rendering itself against each regular expression in the list. If any pattern is matched, the rendering is wrapped. public void AddItemNamePattern(string pattern) { _itemNameMatcher.Add(new Regex(pattern)); } public override void Process(RenderRenderingArgs args) { if (args.Rendered || Context.Site == null || !Context.PageMode.IsExperienceEditorEditing) { return; } RenderingContext renderingContext = RenderingContext.CurrentOrNull; Rendering rendering = renderingContext?.Rendering; // rendering.RenderingItem is null when presentation details points to a rendering that is no longer in Sitecore // RenderingXml property is only set on renderings that were bound to a placeholder via presentation details if (rendering?.RenderingItem == null || rendering.Properties["RenderingXml"].IsWhiteSpaceOrNull()) { return; } DoProcess(rendering, args); } protected virtual void DoProcess(Rendering rendering, RenderRenderingArgs args) { Assert.ArgumentCondition(args.Disposables.Count > 0, "args.Disposables", "expect to be more than zero"); RenderingItem renderingItem = rendering.RenderingItem; // do we need to wrap component with meta data to make it visible within EE, usually it is necessary for a structural component. bool isMetaReq = _parentNameMatcher.Count > 0 && _parentNameMatcher.Any(x => x.IsMatch(renderingItem.InnerItem.Parent.Name)) || _itemNameMatcher.Count > 0 && _itemNameMatcher.Any(x => x.IsMatch(renderingItem.InnerItem.Name)); int index = args.Disposables.FindIndex(x => x.GetType() == typeof(Wrapper)); if (index < 0) { Log.Warn($"Cannot find rendering chrome wrapper and will not insert metadata wrapper for [{rendering}]", this); return; } IMarker marker = isMetaReq ? new MetadataMarker(rendering, string.Empty) as IMarker : null; if (marker == null) return; args.Disposables.Insert(index, new Wrapper(args.Writer, marker)); } }
Then create a class that implements IMarker and actually insert the HTML markup in wrapper to display the details of rendering.
public class MetadataMarker : IMarker { private readonly Rendering _rendering; private readonly string _xprIconHtml; public MetadataMarker(Rendering rendering, string xprIconHtml) { Assert.ArgumentNotNull(rendering, "_rendering"); _rendering = rendering; _xprIconHtml = xprIconHtml; } public virtual string GetStart() { var result = new StringBuilder(); RenderingItem renderingItem = _rendering.RenderingItem; string folderName = renderingItem.InnerItem.Parent.Name; string componentName = renderingItem.DisplayName; var datasourceIcon = string.Empty; if (!string.IsNullOrEmpty(_rendering.DataSource)) { datasourceIcon = $"<span class=\"glyphicon glyphicon-list-alt\"></span>{(_rendering.Item != null ? _rendering.Item.DisplayName : string.Empty)}"; } var title = $"<span class=\"panel-title\"><span>{folderName}:</span>{componentName} {datasourceIcon} {_xprIconHtml}</span>"; result.AppendFormat("{0}", title); return result.ToString(); } }
Finally, patch the new processors after Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper
<mvc.renderRendering> <processor patch:after="processor[@type='Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper, Sitecore.Mvc.ExperienceEditor']" type=".............AddPageEditorMetadata, ProjectName"> <parentNamePatterns hint="list:AddParentNamePattern"> <pattern>(Product)|(Page Content)|(Structure)</pattern> </parentNamePatterns> <itemNamePatterns hint="list:AddItemNamePattern"> <pattern>(Full Width Container)|(Three Column Container)|(Three Column Large Middle)</pattern> </itemNamePatterns> </processor> </mvc.renderRendering>
It renders something like below:
I hope you find this piece of information helpful.
Great article Mahima, keep blogging!