Navigation and service panel


Content

This text is fallbacked from the German Version. If you need use Google Translate


Renderings vererben mit MVC

By Tobias Studer on 3. September 2013, No comments

Komponenten können in Sitecore über den Page Editor eingefügt werden. Solche Elemente werden als Renderings auf dem Item gespeichert. In gewissen Fällen, zum Beispiel für Elemente im Footer-Bereich, macht es Sinn, diese auf einer Seite wie der Startseite einzufügen und zu verwalten, aber auf allen Seiten anzuzeigen. Eine Möglichkeit, wie man dies in einem Sitecore MVC Auftritt erreichen kann, zeigt dieser Beitrag.

Als Ausgangslage haben wir ein Item, welches die Startseite der Website darstellt. Alle weiteren Seiten sind Subitems davon. Im Footer-Bereich haben wir mehrere Placeholder definiert, welche allgemeine Links auf Subitems oder externe Seiten darstellen. Diese Links wollen wir als Komponenten auf der Startseite einfügen, aber auf allen anderen Seiten ebenfals im Footer anzeigen. Dazu soll jede Seite die Renderings in den gewünschten Placeholdern aus der Startseite auslesen und zur Laufzeit in die eigenen Placeholder einfügen.

Wichtig ist es in diesem Zusammenhang zu verstehen, wie Sitecore mit MVC eine Seite aufbaut. Die Grundlage dazu bildet die PageDefinition. Die PageDefinition wird in der mvc.buildPageDefinition-Pipeline aufgebaut. Dabei werden auch die Renderings der aktuellen Seite geladen. Dies scheint ein geeigneter Punkt zu sein, um einen eigenen Prozessor dazwischen zu schalten.

Als erstes lässt man Sitecore die aktuelle PageDefinition unverändert aufbauen. Dies geschieht im ProcessXmlBasedLayoutDefinition-Prozessor. Gleich danach schalten wir einen selbstgeschrieben Prozesssor. Damit wir einige Funktionen des vorgegangenen Prozessor wieder verwenden können, leiten wir unseren Prozessor davon ab. Dananch überschreiben wir die AddRenderings-Methode und fügen die Renderings der Startseite hinzu. Die Renderings werden auf den Items als XML gespeichert. Deshalb muss das XML am richtigen Ort ausgelesen und korrekt interpretiert werden. Dazu stellt Sitecore einen speziellen Parser zur Verfügung.

Um die vererbaren Placeholder konfigurierbar zu machen, definieren wir im Prozessor ein Property. Diesem können wir danach über die Konfiguration mehrere Placeholders übergeben. Danach werden die Renderings der gewünschten Placeholder auf der Startseite auf allen anderen Seiten auch angezeigt. Verändert man eine Komponente auf einer Unterseite, ändert sich natürlich das Rendering auf der Startseite mit. Zusätzliche Komponente können jedoch problemlos in dieselben Placeholder der Unterseite eingefügt werden und werden auch nur da angezeigt.

Der Code des neuen Prozessors könnte folgendermassen aussehen:

namespace Example.Pipelines.Mvc.BuildPageDefinition
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Xml.Linq;
    using Sitecore;
    using Sitecore.Data.Fields;
    using Sitecore.Data.Items;
    using Sitecore.Diagnostics;
    using Sitecore.Mvc.Configuration;
    using Sitecore.Mvc.Extensions;
    using Sitecore.Mvc.Pipelines.Response.BuildPageDefinition;
    using Sitecore.Mvc.Presentation;

    public class InjectRenderings : ProcessXmlBasedLayoutDefinition
    {
        public string Placeholders { get; set; }

        protected override void AddRenderings(PageDefinition pageDefinition, BuildPageDefinitionArgs args)
        {
            var item = args.PageContext.Item;
            if (item == null) return;

            // get the item that holds the renderings to inject
            var sourceItem = SitecoreMagic.GetSourceItem(item);
            if (sourceItem == null) return;

            // don't inject the renderings from the current item
            if (item.ID.Equals(sourceItem.ID)) return;

            // get the layout definition from the source item
            var layoutDefinition = this.GetLayoutDefinition(sourceItem);
            if (layoutDefinition == null) return;

            // get renderings from the configured placeholders
            var foundRenderings = this.GetRenderings(layoutDefinition, this.GetPlaceholders());
            pageDefinition.Renderings.AddRange(foundRenderings);
        }

        private IEnumerable<Rendering> GetRenderings(XElement layoutDefinition, IEnumerable<string> placeholders)
        {
            var parser = MvcSettings.GetRegisteredObject<XmlBasedRenderingParser>();

            var placeholderList = placeholders.ToList();
            foreach (var layoutNode in layoutDefinition.Elements("d"))
            {
                var deviceId = layoutNode.GetAttributeValueOrEmpty("id").ToGuid();
                var layoutId = layoutNode.GetAttributeValueOrEmpty("l").ToGuid();

                foreach (var renderingNode in layoutNode.Elements("r"))
                {
                    var placeholder = renderingNode.GetAttributeValueOrEmpty("ph");
                    if (placeholderList.Any(placeholder.Equals))
                    {
                        // call the base class to get the rendering from the configuration
                        yield return this.GetRendering(renderingNode, deviceId, layoutId, renderingNode.Name.LocalName, parser);
                    }
                }
            }
        }

        private XElement GetLayoutDefinition(Item sourceItem)
        {
            string fieldValue;
            var layoutField = sourceItem.Fields[FieldIDs.LayoutField];
            if (layoutField == null || string.IsNullOrWhiteSpace(fieldValue = LayoutField.GetFieldValue(layoutField))) return null;

            try
            {
                return XElement.Parse(fieldValue);
            }
            catch (Exception exception)
            {
                Log.Error("Error injecting additional renderings", exception, this);
                return null;
            }
        }

        private IEnumerable<string> GetPlaceholders()
        {
            return !string.IsNullOrWhiteSpace(this.Placeholders)
                       ? this.Placeholders.Split('|')
                       : Enumerable.Empty<string>();
        }
    }
}

Zusätzlich muss jetzt noch der Prozessor in die Konfiguration gepatcht und die gewünschten Placeholder angegeben werden:

<mvc.buildPageDefinition>
    <processor type="Example.Pipelines.Mvc.BuildPageDefinition.InjectRenderings, Example" patch:after="processor @type='Sitecore.Mvc.Pipelines.Response.BuildPageDefinition.ProcessXmlBasedLayoutDefinition, Sitecore.Mvc']">
        <Placeholders>footer-placeholder-1|footer-placeholder-2</Placeholders>
    </processor>
</mvc.buildPageDefinition>

Und schon hat man die Renderings der Startseite an alle Unterseiten vererbt.

Credits: Code Teile von Tobias Studer und Dražen Janjiček(@djanjicek).

No comments

Add your comment

Your email address will not be published. Required fields are marked *

*