diff --git a/Apps.Contentful/Apps.Contentful.csproj b/Apps.Contentful/Apps.Contentful.csproj
index 4ce9523..30ccd84 100644
--- a/Apps.Contentful/Apps.Contentful.csproj
+++ b/Apps.Contentful/Apps.Contentful.csproj
@@ -6,7 +6,7 @@
enable
Contentful
The headless content management system
- 1.8.13
+ 1.8.14
Apps.Contentful
diff --git a/Apps.Contentful/HtmlHelpers/EntryToHtmlConverter.cs b/Apps.Contentful/HtmlHelpers/EntryToHtmlConverter.cs
index 0344e66..a4381e8 100644
--- a/Apps.Contentful/HtmlHelpers/EntryToHtmlConverter.cs
+++ b/Apps.Contentful/HtmlHelpers/EntryToHtmlConverter.cs
@@ -377,7 +377,19 @@ private string ConvertCustomFieldToHtml(JToken quoteToken)
if (content is null)
return default;
- var richTextToHtmlConverter = new RichTextToHtmlConverter(content, spaceId);
+ Func? assetResolver = null;
+ if (includeReferencedAssets)
+ {
+ var client = new ContentfulClient(invocationContext.AuthenticationCredentialsProviders, environment);
+ assetResolver = assetId =>
+ {
+ var assetTask = client.GetAsset(assetId);
+ assetTask.Wait();
+ return assetTask.Result;
+ };
+ }
+
+ var richTextToHtmlConverter = new RichTextToHtmlConverter(content, spaceId, assetResolver);
var fieldContent = richTextToHtmlConverter.ToHtml();
return WrapFieldInDiv(doc, entryId, field, fieldContent);
diff --git a/Apps.Contentful/HtmlHelpers/HtmlToRichTextConverter.cs b/Apps.Contentful/HtmlHelpers/HtmlToRichTextConverter.cs
index 016e249..7f7047c 100644
--- a/Apps.Contentful/HtmlHelpers/HtmlToRichTextConverter.cs
+++ b/Apps.Contentful/HtmlHelpers/HtmlToRichTextConverter.cs
@@ -81,6 +81,9 @@ private void ParseHtmlToContentful(HtmlNode node, List contentList)
case "a":
content = CreateHyperlink(childNode);
break;
+ case "img":
+ content = CreateAssetFromImage(childNode);
+ break;
default:
ParseHtmlToContentful(childNode, contentList);
break;
@@ -513,6 +516,78 @@ private IContent CreateHyperlink(HtmlNode node)
}
}
+ private IContent? CreateAssetFromImage(HtmlNode node)
+ {
+ var assetId = node.GetAttributeValue("data-contentful-link-id", "");
+ var nodeType = node.GetAttributeValue("data-contentful-rich-text-node-type", "");
+
+ if (string.IsNullOrEmpty(assetId))
+ {
+ var id = node.GetAttributeValue("id", "");
+ if (TryParseRichTextAssetId(id, out nodeType, out assetId) == false)
+ return null;
+ }
+
+ if (string.IsNullOrEmpty(nodeType))
+ nodeType = "embedded-asset-block";
+
+ if (nodeType != "embedded-asset-block" && nodeType != "embedded-asset-inline")
+ return null;
+
+ return new AssetHyperlink
+ {
+ NodeType = nodeType,
+ Content = new List(),
+ Data = new AssetHyperlinkData
+ {
+ Target = new Asset
+ {
+ SystemProperties = new SystemProperties
+ {
+ Id = assetId,
+ Type = "Link",
+ LinkType = "Asset"
+ }
+ }
+ }
+ };
+ }
+
+ private static bool TryParseRichTextAssetId(string id, out string nodeType, out string assetId)
+ {
+ nodeType = "";
+ assetId = "";
+
+ const string blockPrefix = "embedded-asset-block_";
+ const string inlinePrefix = "embedded-asset-inline_";
+
+ if (id.StartsWith(blockPrefix))
+ {
+ nodeType = "embedded-asset-block";
+ assetId = id[blockPrefix.Length..];
+ return !string.IsNullOrEmpty(assetId);
+ }
+
+ if (id.StartsWith(inlinePrefix))
+ {
+ nodeType = "embedded-asset-inline";
+ assetId = id[inlinePrefix.Length..];
+ return !string.IsNullOrEmpty(assetId);
+ }
+
+ return false;
+ }
+
+ private static string GetAssetNodeTypeFromImage(HtmlNode node)
+ {
+ var nodeType = node.GetAttributeValue("data-contentful-rich-text-node-type", "");
+ if (!string.IsNullOrEmpty(nodeType))
+ return nodeType;
+
+ var id = node.GetAttributeValue("id", "");
+ return TryParseRichTextAssetId(id, out nodeType, out _) ? nodeType : "embedded-asset-block";
+ }
+
private string GetHyperlinkTextWithNewlines(HtmlNode node)
{
var text = new StringBuilder();
@@ -651,6 +726,21 @@ private void GetMarksFromHtmlNode(HtmlNode node, List marks)
parentContentList.Add(CreateHyperlink(child));
}
}
+ else if (child.Name == "img")
+ {
+ var assetContent = CreateAssetFromImage(child);
+ if (assetContent == null)
+ continue;
+
+ if (GetAssetNodeTypeFromImage(child) == "embedded-asset-block")
+ {
+ parentContentList.Add(assetContent);
+ }
+ else
+ {
+ paragraph.Content.Add(assetContent);
+ }
+ }
else
{
ParseHtmlToContentful(child, paragraph.Content);
@@ -683,4 +773,4 @@ private void GetMarksFromHtmlNode(HtmlNode node, List marks)
return paragraph;
}
-}
\ No newline at end of file
+}
diff --git a/Apps.Contentful/HtmlHelpers/RichTextToHtmlConverter.cs b/Apps.Contentful/HtmlHelpers/RichTextToHtmlConverter.cs
index 515ee1b..1c7b214 100644
--- a/Apps.Contentful/HtmlHelpers/RichTextToHtmlConverter.cs
+++ b/Apps.Contentful/HtmlHelpers/RichTextToHtmlConverter.cs
@@ -1,10 +1,14 @@
using System.Text;
+using Contentful.Core.Models.Management;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Apps.Contentful.HtmlHelpers;
-public class RichTextToHtmlConverter(JArray content, string spaceId)
+public class RichTextToHtmlConverter(
+ JArray content,
+ string spaceId,
+ Func? assetResolver = null)
{
public string ToHtml()
{
@@ -80,12 +84,83 @@ private string ConvertJsonObjectToHtml(JObject jsonObject)
case "embedded-asset-block":
assetId = jsonObject["data"]["target"]["sys"]["id"].ToString();
uri = $"https://app.contentful.com/spaces/{spaceId}/assets/{assetId}";
+ var imageHtml = ConvertEmbeddedAssetToImageHtml(assetId, nodeType);
+ if (imageHtml != null)
+ {
+ return imageHtml;
+ }
+
return $"Asset {assetId}";
default:
return ConvertContentToHtml(jsonObject["content"]);
}
}
+ private string? ConvertEmbeddedAssetToImageHtml(string assetId, string nodeType)
+ {
+ if (assetResolver == null)
+ return null;
+
+ ManagementAsset? asset;
+ try
+ {
+ asset = assetResolver(assetId);
+ }
+ catch
+ {
+ return null;
+ }
+
+ if (asset?.Files == null)
+ return null;
+
+ foreach (var fileEntry in asset.Files)
+ {
+ var file = fileEntry.Value;
+ if (!file.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
+ continue;
+
+ var imageUrl = NormalizeImageUrl(file.Url);
+ var htmlBuilder = new StringBuilder();
+ htmlBuilder.Append($"
");
+ return htmlBuilder.ToString();
+ }
+
+ return null;
+ }
+
+ private static string NormalizeImageUrl(string imageUrl)
+ {
+ if (imageUrl.StartsWith("//"))
+ {
+ return "https:" + imageUrl;
+ }
+
+ if (imageUrl.StartsWith("/"))
+ {
+ return "https://images.ctfassets.net" + imageUrl;
+ }
+
+ return imageUrl;
+ }
+
+ private static string HtmlEncode(string value) => System.Net.WebUtility.HtmlEncode(value);
+
private string ConvertHeadingToHtml(JObject jsonObject, string nodeType)
{
var tagName = nodeType.Replace("heading-", "h");
@@ -221,4 +296,4 @@ private void GetMarksHtml(JToken marks, out string openingMarks, out string clos
openingMarks = openingMarksBuilder.ToString();
closingMarks = closingMarksBuilder.ToString();
}
-}
\ No newline at end of file
+}