Page Editor – Chrome Data erweitern

Der WYSIWYG-Editor von Sitecore bietet einem Autor ein sehr exklusives Werkzeug für die Bearbeitung seiner Seite. Diese Werkzeuge sind out of the Box sehr umfangreich und bieten dem Benutzer viele Informationen und Hilfsmittel für die Pflege seines Inhaltes.

Was ist Chrome Data?
Wie nahezu jeder Baustein im System Sitecore kann auch dieser nach belieben angepasst werden. Als kleines Beispiel wollen wir Euch kurz zeigen, wie Chrome Data-Titel angepasst werden können. Als Chrome Data bezeichnet Sitecore die „aufpoppenden“ Fenster bei der Selektion eines Elementes. Diese Informationen werden im Rahmen des Page Editors vor und nach jedem Elementes (Placeholder, Rendering, Edit-Frame, Field) in einem Code-Tag abgespeichert. Das Inner-HTML dieses Tags beinhaltet Daten im Json-Format.
Je nach Benutzerinteraktion werden entsprechende Pop-Up-Fenster zur Laufzeit geparsed und angezeigt.

blog-chromedata-popup

Ein solches Chrome-Data-Window beinhaltet alle Informationen wie Titel, Buttons (inkl. Verhalten), Zusatztexte, usw.

Früher wurde der Page-Editor mit der Hilfe der /configuration/sitecore/pipelines/setupEditFrame Pipeline erstellt. Seit Chrome Data wird der Aufbau über /configuration/sitecore/pipelines/getChromeData gesteuert. Beide Nodes sind in der web.Config zu finden.

Erweiterung (Beispiel)
Wir erweitern nun die Standard-Titel-Anzeige bei Renderings und einzelne Felder, welche über den PageEditor (PE) bearbeitet werden können.

Im Page Editor ist es schwierig zu erkennen, ob eine vorhandene Komponente bereits irgendwo auf einer anderen Seite verwendet wird. Je nach Templating der Webseite ist es möglich, einen Inhalt auf mehreren Seiten zu verwenden. Somit reduziert sich der Pflegeaufwand und im Gegenzug erhöht sich die Einheitlichkeit der Online-Präsenz.

Genau diese fehlende Anzeigeoption wollen wir nun herstellen.

blog-chromedata-schema

Die Regelung wie folgt:

Renderings:
Bei den Renderings gebe ich zusätzlich im Titelfeld an, wie viele Referenzen die Datensource zu anderen Elementen bzw. Webseiten besitzt. Dies hilft einem Autor zu erkennen, dass eine Änderung des Komponenteninhalts auch eine Änderung auf den referenzierten Elementen erzeugt. Dies ist nützlich, insofern man Inhalt auf mehreren Seiten wiederverwendet.

Zusätzlich soll die Aktualität der Datensource gegenüber der „web“-Datenbank geprüft werden. Hierfür öffne ich die Web-Datenbank und überprüfe, ob dieses Item dort existiert oder eben noch nicht publiziert wurde. Existiert das Item bereits, vergleiche ich die Aktualität über den Wert des Feldes „__Updated“, welches sich bei jedem „Speichern“ des Items automatisch aktualisiert. Diese Informationen werden im ToolTip-Element der Chrome-Data’s angezeigt, wenn ich mit der Maus über den „Titel“ fahre.

Fields:
Bei den Feldern gebe ich zusätzlich im Titel an, zu welchem Item dieses Feld zugeordnet ist. Vor allem bei verschachtelten Komponenten bzw. Komponenten, welche Child-Items auslesen ist diese Information sehr hilfreich.

Als weiterer Punkt wird wie bei den Renderings der Item-Publizierungsstatus des Feldes überprüft und entsprechend im ToolTip angezeigt.

Code:
Hier wird der eigentliche Chrome-Data-Processor erstellt, welches die benötigten Informationen aus der Pipeline erhaltet und entsprechend das weitergereichte Objekt mit eigenen Informationen anreichert:

namespace Namics.Opensource.Snippets.PublishingTools.Processors
{
using System.Linq;
using System.Text;

using Namics.Opensource.Snippets.PublishingTools.Helpers;

using Sitecore;
using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Layouts;
using Sitecore.Pipelines.GetChromeData;

using ItemState = Namics.Opensource.Snippets.PublishingTools.Helpers.ItemState;

///
/// This class extends the page editor functionality of Sitecore with publishing and reference informations
///

public class AdditionalChromeData : GetChromeDataProcessor
{
///
/// Startpoint of the Additional ChromeData Processor
///

///public override void Process(GetChromeDataArgs args)
{
Assert.ArgumentNotNull(args, „args“);
Assert.IsNotNull(args.ChromeData, „Chrome Data“);

switch (args.ChromeType.ToLower())
{
case „rendering“:
RenderingElement(args);
break;
case „field“:
FieldElement(args);
break;
}
}

///
/// Checks the references to this field and the publishing information of the field-item
///

///Sitecore Chrome Data Arguments private void FieldElement(GetChromeDataArgs args)
{
Field argument = args.CustomData[„field“] as Field;
Item item = args.Item;
var refBuilder = new StringBuilder();

if (item != null && argument != null)
{
var format = „{0} (“ + item.DisplayName + „)“;
args.ChromeData.DisplayName = string.Format(format, argument.DisplayName);
if (!string.IsNullOrEmpty(argument.ToolTip))
{
refBuilder.Append(string.Format(format, argument.ToolTip));
}

var publishingStateTextDatasource = this.PublishingStateText(PublishingHelper.CheckItemState(PublishingHelper.DEFAULT_DATABASE, item));
refBuilder.Append(string.Format(„\n \n \n Publishing State ({0}) \n ———— \n {1}“, item.DisplayName, publishingStateTextDatasource));

args.ChromeData.ExpandedDisplayName = refBuilder.ToString();
}
}

///
/// Checks the references and the publishing information of the rendering datasource
///

///Sitecore Chrome Data Arguments private void RenderingElement(GetChromeDataArgs args)
{
var argument = args.CustomData[„renderingReference“] as RenderingReference;
string format = string.Empty;
Item item = args.Item;
Item datasourceItem = argument != null && (!string.IsNullOrEmpty(argument.Settings.DataSource)) ? argument.RenderingItem.Database.GetItem(new ID(argument.Settings.DataSource)) : null;
item = datasourceItem ?? item;
var refBuilder = new StringBuilder();

if (item != null)
{
var referenceItems = Globals.LinkDatabase.GetReferrers(item);
if (referenceItems.Any())
{
format = string.Format(„{0} – {1} References“, args.ChromeData.DisplayName, referenceItems.Count());

refBuilder.Append(string.Format(„{0} – {1} References \n ————„, args.ChromeData.DisplayName, referenceItems.Count()));

foreach (var referenceItem in referenceItems)
{
var sourceItem = referenceItem.GetSourceItem();
if (sourceItem != null)
{
refBuilder.Append(string.Format(„\n – {0} ({1} – {2})“, sourceItem.DisplayName, sourceItem.Paths.FullPath, sourceItem.ID));
}
}
args.ChromeData.ExpandedDisplayName = refBuilder.ToString();

var datasourceItemState = (datasourceItem != null) ? PublishingHelper.CheckItemState(PublishingHelper.DEFAULT_DATABASE, datasourceItem) : ItemState.NotFound;
var publishingStateTextDatasource = this.PublishingStateText(datasourceItemState);

refBuilder.Append(string.Format(„\n \n \n Publishing State (Datasource) \n ———— \n {0}“, publishingStateTextDatasource));
}

args.ChromeData.DisplayName = (string.IsNullOrEmpty(format)) ? args.ChromeData.DisplayName : format;
}

if (argument != null && !string.IsNullOrEmpty(argument.RenderingItem.InnerItem.Appearance.ShortDescription))
{
refBuilder.Append(string.Format(„{0} \n\n“, argument.RenderingItem.InnerItem.Appearance.ShortDescription));
}

args.ChromeData.ExpandedDisplayName = refBuilder.ToString();
}

private string PublishingStateText(ItemState publishingItemState)
{
switch (publishingItemState)
{
case ItemState.Actual:
return (Translate.Text(string.Format(„Found and actual in Database: {0}“, PublishingHelper.DEFAULT_DATABASE)));
case ItemState.NotExisting:
return (Translate.Text(string.Format(„Not existing in Database: {0}“, PublishingHelper.DEFAULT_DATABASE)));
case ItemState.NotActual:
return (Translate.Text(string.Format(„Not actual in Database: {0}“, PublishingHelper.DEFAULT_DATABASE)));
case ItemState.NotFound:
return (Translate.Text(string.Format(„Database not found: {0}“, PublishingHelper.DEFAULT_DATABASE)));
}

return string.Empty;
}
}
}

Diese Klasse überprüft entsprechend die Item-Versionen und gibt ihnen einen entsprechenden Status. Diese Klasse kann beispielsweise auch für den Bau eines entsprechenden Gutters verwendet werden.

namespace Namics.Opensource.Snippets.PublishingTools.Helpers
{
using Sitecore.Configuration;
using Sitecore.Data.Items;
using Sitecore.Data.Managers;
using Sitecore.Diagnostics;

using System;

///
/// Enum of an Item state
///

public enum ItemState
{
Actual = 0,
NotExisting = 1,
NotActual = 2,
NotFound = 3
}

public class PublishingHelper
{
///
/// The language fallback field name
///

internal const string FALLBACK = „Fallback“;

///
/// The databases field name
///

internal const string DATABASES = „databases“;

///
/// The default_database field name
///

internal const string DEFAULT_DATABASE = „web“;

///
/// The updated_field field name
///

internal const string UPDATED_FIELD = „__Updated“;

///
/// Here we check and change the current master-state for Icon-generation
///

///The new Item state ///The current master Item state /// The new master Item state
public static ItemState SetItemState(ItemState newItemState, ItemState originalItemState)
{
switch (newItemState)
{
case ItemState.NotExisting:
return ItemState.NotExisting;
case ItemState.NotActual:
return originalItemState == ItemState.NotExisting ? originalItemState : newItemState;
case ItemState.Actual:
return originalItemState == ItemState.NotFound ? newItemState : originalItemState;
}

return originalItemState;
}

///
/// Check if an item actual or existing on the requestet database
///

///The name of the database ///Current item to check /// The Item state
public static ItemState CheckItemState(string databaseName, Item baseItem)
{
try
{
var db = Factory.GetDatabase(databaseName);
if (db != null)
{
var item = db.GetItem(baseItem.ID, baseItem.Language);
if (item != null)
{
if (!string.IsNullOrEmpty(item[UPDATED_FIELD]) && !string.IsNullOrEmpty(baseItem[UPDATED_FIELD]))
{
if (item[UPDATED_FIELD].Equals(baseItem[UPDATED_FIELD], StringComparison.InvariantCultureIgnoreCase))
{
return ItemState.Actual;
}

return ItemState.NotActual;

}

if (string.IsNullOrEmpty(item[UPDATED_FIELD]) && string.IsNullOrEmpty(baseItem[UPDATED_FIELD]))
{
return ItemState.Actual;
}
}

return ItemState.NotExisting;
}
}
catch (Exception ex)
{
Log.Warn(string.Format(„Gets an Error to connect to the database: {0}“, ex.Message), typeof(PublishingHelper));
}

return ItemState.NotFound;
}

///
/// This checks the right Item-Version. If an item is not existing, it will be search a language-fallback Item if this Module installed
///

///Requestet Item /// The correct Item
public static Item EvaluateItemWithVersion(Item baseItem)
{
try
{
var language = LanguageManager.GetLanguage(baseItem.Language.CultureInfo.Name);
var langItem = language.GetItem(baseItem.Database);

if (langItem != null && !string.IsNullOrEmpty(langItem[PublishingHelper.FALLBACK]))
{
var fallbackLangItem = LanguageManager.GetLanguage(langItem[PublishingHelper.FALLBACK]);
if (fallbackLangItem != null)
{
var fallbackItem = baseItem.Database.GetItem(baseItem.ID, fallbackLangItem);
if (fallbackItem != null)
{
if (fallbackItem.Versions.Count > 0)
{
return fallbackItem;
}
}
}
}

return baseItem;
}
catch (Exception ex)
{
Log.Warn(string.Format(„Gets an Error to check the Language-Versions with message: {0}“, ex.Message), typeof(PublishingHelper));
}

return null;
}
}
}

Damit die Pipeline auch angesprochen wird, erstellen wir eine zusätzliche Sitecore-Config und fügen sie in den Ordner App_Config\Include\ unseres Sitecore Web-Projektes.

<!--?xml version="1.0" encoding="utf-8" ?-->

Resultat:
Letztendlich kann man auf seiner Seite in den Page Editor wechseln. Die Felder und Renderings zeigen nun erweiterte Informationen zum selektierten Element an:

Feld, welches noch nie publiziert wurde:
blog-chromedata-popup-field-title

Feld, bzw. das Item des Feldes existiert auf „web“, jedoch nicht in dieser aktuellen Form:
blog-chromedata-popup-field-title2

Eine eingebundene Bühne, welche insgesamt in drei verschiedenen Seiten eingebunden wurde, inkl. der Publizierungsstatus der Bühne selber:
blog-chromedata-popup-rendering-title-references

Ressourcen:
Wer diese Funktionalität schnell installieren und direkt ausprobieren möchte, kann dies auch effizient über NuGet machen. Dieses Snippet ist unter NuGet erreichbar.

Für die Enwickler:
Beim Packaging Manager:
PM> Install-Package Namics.Opensource.Snippets.PublishingTools
…eingeben und schon ist die kleine Beispielserweiterung installiert.

Fazit:
Sitecore ist auch in diesem Bereich individuell anpassbar und kann mit wenigen Zeilen Code nützlich erweitert werden. Fragt doch einfach mal Eure Autoren, was ihnen fehlt und unterstützt sie bei ihrer täglichen Arbeit in der Inhaltserfassung.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>