From 2f37489a26b3fed9ec11669483310070d1bd523c Mon Sep 17 00:00:00 2001 From: Bert Jansen Date: Wed, 13 Mar 2024 12:00:31 +0100 Subject: [PATCH] Loading site pages is more robust when a custom web part on the page is erroring #1410 --- src/sdk/CHANGELOG.md | 1 + .../Pages/Internal/CanvasControl.cs | 2 +- .../Model/SharePoint/Pages/Internal/Page.cs | 8 ++-- .../SharePoint/Pages/Internal/PageText.cs | 4 +- .../SharePoint/Pages/Internal/PageWebPart.cs | 43 +++++++++++++++---- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/sdk/CHANGELOG.md b/src/sdk/CHANGELOG.md index 66fed6f49f..068aaa62f1 100644 --- a/src/sdk/CHANGELOG.md +++ b/src/sdk/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Calls to `GetUserEffectivePermissions` are not not clearing collections loaded in the used `PnPContext` instance #1416 [jansenbe - Bert Jansen] - Verify TermId is a valid GUID before assigning it #1414 [jansenbe - Bert Jansen] +- Loading site pages is more robust when a custom web part on the page is erroring #1410 [jansenbe - Bert Jansen] ## [1.12.0] diff --git a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/CanvasControl.cs b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/CanvasControl.cs index ab7c05b9cb..d02364e1b1 100644 --- a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/CanvasControl.cs +++ b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/CanvasControl.cs @@ -348,7 +348,7 @@ internal static Type GetType(string controlDataJson) #endregion #region Internal and private methods - internal virtual void FromHtml(IElement element) + internal virtual void FromHtml(IElement element, bool isHeader) { var controlData = JsonSerializer.Deserialize(element.GetAttribute(ControlDataAttribute), PnPConstants.JsonSerializer_IgnoreNullValues); diff --git a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/Page.cs b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/Page.cs index e30660216b..1bfb3acb66 100644 --- a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/Page.cs +++ b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/Page.cs @@ -1171,7 +1171,7 @@ private void LoadFromHtml(string html, string pageHeaderHtml) { if (string.IsNullOrEmpty(html)) { - throw new ArgumentNullException(nameof(pageHeaderHtml)); + throw new ArgumentNullException(nameof(html)); } HtmlParser parser = new HtmlParser(new HtmlParserOptions() { IsEmbedded = true }); @@ -1195,7 +1195,7 @@ private void LoadFromHtml(string html, string pageHeaderHtml) { Order = controlOrder }; - control.FromHtml(clientSideControl); + control.FromHtml(clientSideControl, false); // Handle control positioning in sections and columns ApplySectionAndColumn(control, control.SpControlData.Position, control.SpControlData.Emphasis, control.SpControlData.ZoneGroupMetadata); @@ -1208,7 +1208,7 @@ private void LoadFromHtml(string html, string pageHeaderHtml) { Order = controlOrder }; - control.FromHtml(clientSideControl); + control.FromHtml(clientSideControl, false); // Handle control positioning in sections and columns ApplySectionAndColumn(control, control.SpControlData.Position, control.SpControlData.Emphasis, control.SpControlData.ZoneGroupMetadata); @@ -1455,7 +1455,7 @@ private void LoadFromHtml(string html, string pageHeaderHtml) Order = headerControlOrder, IsHeaderControl = true, }; - control.FromHtml(clientSideHeaderControl); + control.FromHtml(clientSideHeaderControl, true); HeaderControls.Add(control); headerControlOrder++; diff --git a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageText.cs b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageText.cs index c18b9f1e45..98b046c4f9 100644 --- a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageText.cs +++ b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageText.cs @@ -168,9 +168,9 @@ public override string ToHtml(float controlIndex) #endregion #region Internal and private methods - internal override void FromHtml(IElement element) + internal override void FromHtml(IElement element, bool isHeader) { - base.FromHtml(element); + base.FromHtml(element, isHeader); var div = element.GetElementsByTagName("div").FirstOrDefault(a => a.HasAttribute(TextRteAttribute)); diff --git a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageWebPart.cs b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageWebPart.cs index 9d70f66b26..2f6c9ca2e4 100644 --- a/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageWebPart.cs +++ b/src/sdk/PnP.Core/Model/SharePoint/Pages/Internal/PageWebPart.cs @@ -515,9 +515,9 @@ internal void RenderHtmlProperties(ref StringBuilder htmlWriter) #endregion #region Internal and private methods - internal override void FromHtml(IElement element) + internal override void FromHtml(IElement element, bool isHeader) { - base.FromHtml(element); + base.FromHtml(element, isHeader); // Set/update dataVersion if it was provided as html attribute var webPartDataVersion = element.GetAttribute(WebPartDataVersionAttribute); @@ -530,11 +530,11 @@ internal override void FromHtml(IElement element) controlType = SpControlData.ControlType; RichTextEditorInstanceId = SpControlData.RteInstanceId; - var wpDiv = element.GetElementsByTagName("div").FirstOrDefault(a => a.HasAttribute(WebPartDataAttribute)); + IElement wpDiv = null; + string decodedWebPart = null; - string decodedWebPart; // Some components are in the page header and need to be handled as a control instead of a webpart - if (wpDiv == null) + if (isHeader) { // Decode the html encoded string decodedWebPart = WebUtility.HtmlDecode(element.GetAttribute(ControlDataAttribute)); @@ -542,10 +542,37 @@ internal override void FromHtml(IElement element) } else { - WebPartData = wpDiv.GetAttribute(WebPartAttribute); + wpDiv = element.GetElementsByTagName("div").FirstOrDefault(a => a.HasAttribute(WebPartDataAttribute)); + if (wpDiv != null) + { + // This is a valid web part + WebPartData = wpDiv.GetAttribute(WebPartAttribute); - // Decode the html encoded string - decodedWebPart = WebUtility.HtmlDecode(wpDiv.GetAttribute(WebPartDataAttribute)); + // Decode the html encoded string + decodedWebPart = WebUtility.HtmlDecode(wpDiv.GetAttribute(WebPartDataAttribute)); + } + else + { + // The web part is not presented by a data-sp-webpartdata attribute on the DIV, typically + // this means the web part is broken. Check the page and see if it renders properly + + // Let's try to get the web part data from the webPartData element in the control data attribute json content + var controlDataAttributeJsonContent = WebUtility.HtmlDecode(element.GetAttribute(ControlDataAttribute)); + if (!string.IsNullOrEmpty(controlDataAttributeJsonContent)) + { + var controlDataAttributeJsonObject = JsonSerializer.Deserialize(controlDataAttributeJsonContent); + if (controlDataAttributeJsonObject.TryGetProperty("webPartData", out JsonElement webPartDataProperty) && webPartDataProperty.ValueKind != JsonValueKind.Null) + { + decodedWebPart = webPartDataProperty.ToString(); + } + } + } + } + + // If above fallback code did not result in web part data then return with just the basic info we have + if (string.IsNullOrEmpty(decodedWebPart)) + { + return; } var wpJObject = JsonSerializer.Deserialize(decodedWebPart);