From 3fe5dd5a2194e360008fc2a4056f597d97b706f0 Mon Sep 17 00:00:00 2001 From: "DK\\dmko" Date: Wed, 14 Mar 2018 11:29:53 +0200 Subject: [PATCH] Prevents the System.ArgumentNullException when trying to delete or archive an item with broken links --- .../zzz/Sitecore.Support.211195.config | 5 + src/Sitecore.Support.211195/LayoutField.cs | 780 +----------------- 2 files changed, 14 insertions(+), 771 deletions(-) diff --git a/src/Sitecore.Support.211195/App_Config/Include/zzz/Sitecore.Support.211195.config b/src/Sitecore.Support.211195/App_Config/Include/zzz/Sitecore.Support.211195.config index ec1e0d0..4a6600d 100644 --- a/src/Sitecore.Support.211195/App_Config/Include/zzz/Sitecore.Support.211195.config +++ b/src/Sitecore.Support.211195/App_Config/Include/zzz/Sitecore.Support.211195.config @@ -1,4 +1,9 @@ + + + Sitecore.Support.Data.Fields.LayoutField, Sitecore.Support.211195 + + \ No newline at end of file diff --git a/src/Sitecore.Support.211195/LayoutField.cs b/src/Sitecore.Support.211195/LayoutField.cs index 3907233..14bdda1 100644 --- a/src/Sitecore.Support.211195/LayoutField.cs +++ b/src/Sitecore.Support.211195/LayoutField.cs @@ -1,493 +1,23 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Xml; using System.Xml.Linq; +using Sitecore.Data.Fields; using Sitecore.Data.Items; using Sitecore.Diagnostics; -using Sitecore.Globalization; using Sitecore.Layouts; using Sitecore.Links; -using Sitecore.Pipelines; -using Sitecore.Pipelines.GetLayoutSourceFields; -using Sitecore.Pipelines.ResolveRenderingDatasource; using Sitecore.Text; -using Sitecore.Xml; -using Sitecore.Xml.Patch; -namespace Sitecore.Data.Fields +namespace Sitecore.Support.Data.Fields { - /// Represents a Layout field. - public class LayoutField : CustomField + public class LayoutField : Sitecore.Data.Fields.LayoutField { - #region Constants and Fields - - /// - /// Specifies empty value for the layout field. - /// - public const string EmptyValue = ""; - - /// The data. - private readonly XmlDocument data; - - #endregion - - #region Constructors and Destructors - /// Initializes a new instance of the class. Creates LayoutField from specific item. /// Item to get layout for. public LayoutField([NotNull] Item item) - : this(item.Fields[FieldIDs.FinalLayoutField]) - { - } - - /// Initializes a new instance of the class. Creates a new instance. - /// Inner field. - public LayoutField([NotNull] Field innerField) - : base(innerField) - { - Assert.ArgumentNotNull(innerField, "innerField"); - - this.data = this.LoadData(); - } - - /// Initializes a new instance of the class. - /// The inner field. - /// The runtime value. - public LayoutField([NotNull] Field innerField, [NotNull] string runtimeValue) - : base(innerField, runtimeValue) - { - Assert.ArgumentNotNull(innerField, "innerField"); - Assert.ArgumentNotNullOrEmpty(runtimeValue, "runtimeValue"); - - this.data = this.LoadData(); - } - - #endregion - - #region Properties - - /// - /// Gets the XML data document. - /// - /// The data. - [NotNull] - public XmlDocument Data + : base(item) { - get - { - return this.data; - } - } - - #endregion - - #region Operators - - /// - /// Converts a to a . - /// - /// The field. - /// The implicit operator. - public static implicit operator LayoutField([CanBeNull] Field field) - { - if (field != null) - { - return new LayoutField(field); - } - - return null; - } - - #endregion - - #region Public Methods - - /// Extracts the layout ID. - /// Device node. - /// The layout ID. - [NotNull] - public static ID ExtractLayoutID([NotNull] XmlNode deviceNode) - { - Assert.ArgumentNotNull(deviceNode, "deviceNode"); - - string value = XmlUtil.GetAttribute("l", deviceNode); - - if (value.Length > 0 && ID.IsID(value)) - { - return ID.Parse(value); - } - - return ID.Null; - } - - /// Extracts the Rendering references. - /// Device node. - /// Language. - /// Database. - /// The references. - [NotNull] - public static RenderingReference[] ExtractReferences( - [NotNull] XmlNode deviceNode, [NotNull] Language language, [NotNull] Database database) - { - Assert.ArgumentNotNull(deviceNode, "deviceNode"); - Assert.ArgumentNotNull(language, "language"); - Assert.ArgumentNotNull(database, "database"); - - XmlNodeList nodes = deviceNode.SelectNodes("r"); - Assert.IsNotNull(nodes, "nodes"); - - var references = new RenderingReference[nodes.Count]; - - for (int n = 0; n < nodes.Count; n++) - { - references[n] = new RenderingReference(nodes[n], language, database); - } - - return references; - } - - /// Gets the field value, applying any layout deltas. - /// The field to get value for. - /// The calculated layout value. - [NotNull] - public static string GetFieldValue([NotNull] Field field) - { - Assert.ArgumentNotNull(field, "field"); - Assert.IsTrue(field.ID == FieldIDs.LayoutField || field.ID == FieldIDs.FinalLayoutField, "The field is not a layout/renderings field"); - - var args = new GetLayoutSourceFieldsArgs(field); - bool success = GetLayoutSourceFieldsPipeline.Run(args); - - var fieldValues = new List(); - - - if (success) - { - // fieldValue.GetValue(false, false, true, true) does not fit requirements since it uses fieldValue.GetInheritedValue(true) under the hood - fieldValues.AddRange(args.FieldValuesSource.Select(fieldValue => fieldValue.GetValue(false, false) ?? fieldValue.GetInheritedValue(false) ?? fieldValue.GetValue(false, false, allowFallbackValue: true, allowInheritValue: false, allowInnerValue: false))); - fieldValues.AddRange(args.StandardValuesSource.Select(fieldValue => fieldValue.GetStandardValue())); - } - else - { - fieldValues = DoGetFieldValue(field); - } - - var stackPatches = new Stack(); - string baseXml = null; - - foreach (string value in fieldValues) - { - if (string.IsNullOrWhiteSpace(value)) - { - continue; - } - - if (XmlPatchUtils.IsXmlPatch(value)) - { - stackPatches.Push(value); - continue; - } - - baseXml = value; - break; - } - - if (string.IsNullOrWhiteSpace(baseXml)) - { - return string.Empty; - } - - // patching - return stackPatches.Aggregate(baseXml, XmlDeltas.ApplyDelta); - } - - /// Sets the field value. - /// The field. - /// The value. - public static void SetFieldValue([NotNull] Field field, [NotNull] string value) - { - Assert.ArgumentNotNull(field, "field"); - Assert.ArgumentNotNull(value, "value"); - Assert.IsTrue(field.ID == FieldIDs.LayoutField || field.ID == FieldIDs.FinalLayoutField, "The field is not a layout/renderings field"); - - string baseXml = null; - bool isStandardValues = field.Item.Name == Constants.StandardValuesItemName; - bool isShared = field.ID == FieldIDs.LayoutField; - - Field baseField; - - // find base field - if (isStandardValues && isShared) - { - baseField = null; - } - else if (isStandardValues) - { - baseField = field.Item.Fields[FieldIDs.LayoutField]; - } - else if (isShared) - { - TemplateItem template = field.Item.Template; - baseField = (template != null && template.StandardValues != null) ? template.StandardValues.Fields[FieldIDs.FinalLayoutField] : null; - } - else - { - baseField = field.Item.Fields[FieldIDs.LayoutField]; - } - - if (baseField != null) - { - baseXml = GetFieldValue(baseField); - } - - if (XmlUtil.XmlStringsAreEqual(value, baseXml)) - { - field.Reset(); - return; - } - - if (!string.IsNullOrWhiteSpace(baseXml)) - { - field.Value = XmlDeltas.GetDelta(value, baseXml); - } - else - { - field.Value = value; - } - } - - /// - /// Sets the field value. - /// - /// The field. - /// The value. - /// The base value. - public static void SetFieldValue([NotNull] Field field, [NotNull] string value, [NotNull] string baseValue) - { - Assert.ArgumentNotNull(field, "field"); - Assert.ArgumentNotNull(value, "value"); - Assert.ArgumentNotNull(baseValue, "baseValue"); - Assert.IsTrue(field.ID == FieldIDs.LayoutField || field.ID == FieldIDs.FinalLayoutField, "The field is not a layout/renderings field"); - - string result; - - if (XmlUtil.XmlStringsAreEqual(value, baseValue)) - { - field.Reset(); - return; - } - - if (!string.IsNullOrWhiteSpace(baseValue)) - { - result = XmlDeltas.GetDelta(value, baseValue); - } - else - { - result = value; - } - - if (!XmlUtil.XmlStringsAreEqual(XmlDeltas.ApplyDelta(baseValue, field.Value), XmlDeltas.ApplyDelta(baseValue, result))) - { - field.Value = result; - } - } - - /// Gets the device node. - /// Device. - /// The device node. - /// - /// - /// - [CanBeNull] - public XmlNode GetDeviceNode([CanBeNull] DeviceItem device) - { - if (device != null) - { - return this.Data.DocumentElement.SelectSingleNode("d[@id='" + device.ID + "']"); - } - - return null; - } - - /// Gets the layout ID. - /// Device. - /// The layout ID. - /// - /// - /// - [NotNull] - public ID GetLayoutID([NotNull] DeviceItem device) - { - Assert.ArgumentNotNull(device, "device"); - - XmlNode deviceNode = this.GetDeviceNode(device); - - if (deviceNode != null) - { - return ExtractLayoutID(deviceNode); - } - - return ID.Null; - } - - /// Gets the Rendering references for a device. - /// Device. - /// The references. - /// - /// - /// - [CanBeNull] - public RenderingReference[] GetReferences([NotNull] DeviceItem device) - { - Assert.ArgumentNotNull(device, "device"); - - XmlNode deviceNode = this.GetDeviceNode(device); - - if (deviceNode != null) - { - return ExtractReferences(deviceNode, this.InnerField.Language, this.InnerField.Database); - } - - return null; - } - - /// Relinks the specified item. - /// The item link. - /// The new link. - public override void Relink([NotNull] ItemLink itemLink, [NotNull] Item newLink) - { - Assert.ArgumentNotNull(itemLink, "itemLink"); - Assert.ArgumentNotNull(newLink, "newLink"); - - string value = this.Value; - if (string.IsNullOrEmpty(value)) - { - return; - } - - LayoutDefinition layoutDefinition = LayoutDefinition.Parse(value); - - ArrayList devices = layoutDefinition.Devices; - if (devices == null) - { - return; - } - - string targetItemID = itemLink.TargetItemID.ToString(); - string newLinkID = newLink.ID.ToString(); - - for (int n = devices.Count - 1; n >= 0; n--) - { - var device = devices[n] as DeviceDefinition; - if (device == null) - { - continue; - } - - if (device.ID == targetItemID) - { - device.ID = newLinkID; - continue; - } - - if (device.Layout == targetItemID) - { - device.Layout = newLinkID; - continue; - } - - if (device.Placeholders != null) - { - string targetPath = itemLink.TargetPath; - bool isLinkFound = false; - for (int j = device.Placeholders.Count - 1; j >= 0; j--) - { - var placeholderDefinition = device.Placeholders[j] as PlaceholderDefinition; - if (placeholderDefinition == null) - { - continue; - } - - if ( - string.Equals( - placeholderDefinition.MetaDataItemId, targetPath, StringComparison.InvariantCultureIgnoreCase) || - string.Equals( - placeholderDefinition.MetaDataItemId, targetItemID, StringComparison.InvariantCultureIgnoreCase)) - { - placeholderDefinition.MetaDataItemId = newLink.Paths.FullPath; - isLinkFound = true; - } - } - - if (isLinkFound) - { - continue; - } - } - - if (device.Renderings == null) - { - continue; - } - - for (int r = device.Renderings.Count - 1; r >= 0; r--) - { - var rendering = device.Renderings[r] as RenderingDefinition; - if (rendering == null) - { - continue; - } - - if (rendering.ItemID == targetItemID) - { - rendering.ItemID = newLinkID; - } - - if (rendering.Datasource == targetItemID) - { - rendering.Datasource = newLinkID; - } - - if (rendering.Datasource == itemLink.TargetPath) - { - rendering.Datasource = newLink.Paths.FullPath; - } - - if (!string.IsNullOrEmpty(rendering.Parameters)) - { - Item layoutItem = this.InnerField.Database.GetItem(rendering.ItemID); - - if (layoutItem == null) - { - continue; - } - - var renderingParametersFieldCollection = this.GetParametersFields(layoutItem, rendering.Parameters); - - foreach (var field in renderingParametersFieldCollection.Values) - { - if (!string.IsNullOrEmpty(field.Value)) - { - field.Relink(itemLink, newLink); - } - } - - rendering.Parameters = renderingParametersFieldCollection.GetParameters().ToString(); - } - - if (rendering.Rules != null) - { - var rulesField = new RulesField(this.InnerField, rendering.Rules.ToString()); - rulesField.Relink(itemLink, newLink); - rendering.Rules = XElement.Parse(rulesField.Value); - } - } - } - - this.Value = layoutDefinition.ToXml(); } /// Removes the link. @@ -590,6 +120,11 @@ public override void RemoveLink([NotNull] ItemLink itemLink) if (!string.IsNullOrEmpty(rendering.Parameters)) { + if (rendering.ItemID == null) + { + continue; + } + Item layoutItem = this.InnerField.Database.GetItem(rendering.ItemID); if (layoutItem == null) @@ -622,200 +157,6 @@ public override void RemoveLink([NotNull] ItemLink itemLink) this.Value = layoutDefinition.ToXml(); } - /// Validates the links. - /// The result. - public override void ValidateLinks([NotNull] LinksValidationResult result) - { - Assert.ArgumentNotNull(result, "result"); - - string value = this.Value; - if (string.IsNullOrEmpty(value)) - { - return; - } - - LayoutDefinition layoutDefinition = LayoutDefinition.Parse(value); - - ArrayList devices = layoutDefinition.Devices; - if (devices == null) - { - return; - } - - foreach (DeviceDefinition device in devices) - { - if (!string.IsNullOrEmpty(device.ID)) - { - Item deviceItem = this.InnerField.Database.GetItem(device.ID); - - if (deviceItem != null) - { - result.AddValidLink(deviceItem, device.ID); - } - else - { - result.AddBrokenLink(device.ID); - } - } - - if (!string.IsNullOrEmpty(device.Layout)) - { - Item layoutItem = this.InnerField.Database.GetItem(device.Layout); - - if (layoutItem != null) - { - result.AddValidLink(layoutItem, device.Layout); - } - else - { - result.AddBrokenLink(device.Layout); - } - } - - this.ValidatePlaceholderSettings(result, device); - - if (device.Renderings == null) - { - continue; - } - - foreach (RenderingDefinition rendering in device.Renderings) - { - if (rendering.ItemID == null) - { - continue; - } - - Item renderingItem = this.InnerField.Database.GetItem(rendering.ItemID); - - if (renderingItem != null) - { - result.AddValidLink(renderingItem, rendering.ItemID); - } - else - { - result.AddBrokenLink(rendering.ItemID); - } - - string datasource = rendering.Datasource; - if (!string.IsNullOrEmpty(datasource)) - { - using (new ContextItemSwitcher(this.InnerField.Item)) - { - var args = new ResolveRenderingDatasourceArgs(datasource); - CorePipeline.Run("resolveRenderingDatasource", args, false); - datasource = args.Datasource; - } - - Item dataSourceItem = this.InnerField.Database.GetItem(datasource); - if (dataSourceItem != null) - { - result.AddValidLink(dataSourceItem, datasource); - } - else - { - if (!datasource.Contains(":")) - { - result.AddBrokenLink(datasource); - } - } - } - - string mvTest = rendering.MultiVariateTest; - if (!string.IsNullOrEmpty(mvTest)) - { - Item testDefinitionItem = this.InnerField.Database.GetItem(mvTest); - if (testDefinitionItem != null) - { - result.AddValidLink(testDefinitionItem, mvTest); - } - else - { - result.AddBrokenLink(mvTest); - } - } - - string personalizationTest = rendering.PersonalizationTest; - if (!string.IsNullOrEmpty(personalizationTest)) - { - Item testDefinitionItem = this.InnerField.Database.GetItem(personalizationTest); - if (testDefinitionItem != null) - { - result.AddValidLink(testDefinitionItem, personalizationTest); - } - else - { - result.AddBrokenLink(personalizationTest); - } - } - - if (renderingItem != null && !string.IsNullOrEmpty(rendering.Parameters)) - { - var renderingParametersFieldCollection = this.GetParametersFields(renderingItem, rendering.Parameters); - - foreach (var field in renderingParametersFieldCollection.Values) - { - field.ValidateLinks(result); - } - } - - if (rendering.Rules != null) - { - var rulesField = new RulesField(this.InnerField, rendering.Rules.ToString()); - rulesField.ValidateLinks(result); - } - } - } - } - - /// - /// Fallback method that used in case getLayoutSourceFields pipeline is not defined. - /// - /// The field to get value for. - /// List of values to use as a source for field value. - [NotNull] - [Obsolete("Use GetLayoutSourceFieldsPipeline.Run(GetLayoutSourceFieldsArgs args) method instead.")] - private static List DoGetFieldValue([NotNull] Field field) - { - Debug.ArgumentNotNull(field, "field"); - - var item = field.Item; - var fields = item.Fields; - - IEnumerable> fieldValues = new[] - { - // versioned field of the item - new Lazy(() => fields[FieldIDs.FinalLayoutField].GetValue(false, false) ?? fields[FieldIDs.FinalLayoutField].GetInheritedValue(false)), - - // shared field of the item - new Lazy(() => fields[FieldIDs.LayoutField].GetValue(false, false) ?? fields[FieldIDs.LayoutField].GetInheritedValue(false)), - - // versioned field of the Standard values - new Lazy(() => fields[FieldIDs.FinalLayoutField].GetStandardValue()), - - // shared field of the Standard values - new Lazy(() => fields[FieldIDs.LayoutField].GetStandardValue()) - }; - - bool isStandardValues = item.Name == Constants.StandardValuesItemName; - - bool isShared = field.ID == FieldIDs.LayoutField; - if (isStandardValues && isShared) - { - fieldValues = fieldValues.Skip(3); - } - else if (isStandardValues) - { - fieldValues = fieldValues.Skip(2); - } - else if (isShared) - { - fieldValues = fieldValues.Skip(1); - } - - return fieldValues.Select(x => x.Value).ToList(); - } - /// /// Gets the parameters fields. /// @@ -833,108 +174,5 @@ private RenderingParametersFieldCollection GetParametersFields(Item layoutItem, return parametersFields; } - - #endregion - - #region Methods - - /// Sets the layout hack. - /// The value. - /// - /// - /// - internal void SetLayoutHack([NotNull] string value) - { - Assert.ArgumentNotNull(value, "value"); - - XmlNodeList nodes = this.Data.DocumentElement.SelectNodes("d"); - Assert.IsNotNull(nodes, "nodes"); - - if (nodes.Count > 0) - { - foreach (XmlNode node in nodes) - { - XmlUtil.SetAttribute("l", value, node); - } - - this.Value = this.Data.OuterXml; - } - } - - /// Gets the actual value of this field. - /// Actual value of this field object - [NotNull] - protected override string GetValue() - { - if (this._hasRuntimeValue) - { - return this._runtimeValue; - } - - return GetFieldValue(this._innerField); - } - - /// - /// Sets the value of this field. - /// - /// Value to set. - protected override void SetValue([NotNull] string value) - { - Assert.ArgumentNotNull(value, "value"); - - if (this._hasRuntimeValue) - { - this._runtimeValue = value; - } - - SetFieldValue(this._innerField, value); - } - - /// Validates the placeholder settings. - /// The result. - /// The device. - protected virtual void ValidatePlaceholderSettings( - [NotNull] LinksValidationResult result, [NotNull] DeviceDefinition device) - { - Assert.ArgumentNotNull(result, "result"); - Assert.ArgumentNotNull(device, "device"); - - ArrayList placeholders = device.Placeholders; - if (placeholders != null) - { - foreach (PlaceholderDefinition placeholder in placeholders) - { - if (placeholder != null && !string.IsNullOrEmpty(placeholder.MetaDataItemId)) - { - Item settingsItem = this.InnerField.Database.GetItem(placeholder.MetaDataItemId); - if (settingsItem != null) - { - result.AddValidLink(settingsItem, placeholder.MetaDataItemId); - } - else - { - result.AddBrokenLink(placeholder.MetaDataItemId); - } - } - } - } - } - - /// Loads the data. - /// The data. - [NotNull] - private XmlDocument LoadData() - { - string xml = this.Value; - - if (!string.IsNullOrEmpty(xml)) - { - return XmlUtil.LoadXml(xml); - } - - return XmlUtil.LoadXml(""); - } - - #endregion } } \ No newline at end of file