From 99b80e0f569ce24623eb331fd683b5255a64c672 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 8 Jan 2019 12:19:29 +0100 Subject: [PATCH] Ensure that media in RTEs are always rendered with up-to-date sources --- .../PropertyEditors/RichTextPropertyEditor.cs | 6 ++- .../RteMacroRenderingValueConverter.cs | 5 ++- .../Templates/TemplateUtilities.cs | 44 +++++++++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index de65a326f7..33f04d7595 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Macros; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors { @@ -65,7 +66,8 @@ namespace Umbraco.Web.PropertyEditors if (property.Value == null) return null; - var parsed = MacroTagParser.FormatRichTextPersistedDataForEditor(property.Value.ToString(), new Dictionary()); + var propertyValueWithMediaResolved = TemplateUtilities.ResolveMediaFromTextString(property.Value.ToString()); + var parsed = MacroTagParser.FormatRichTextPersistedDataForEditor(propertyValueWithMediaResolved, new Dictionary()); return parsed; } @@ -87,4 +89,4 @@ namespace Umbraco.Web.PropertyEditors } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index a6ea79d283..c430dbc1ba 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -71,9 +71,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var sourceString = source.ToString(); - // ensures string is parsed for {localLink} and urls are resolved correctly + // ensures string is parsed for {localLink} and urls and media are resolved correctly sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); + sourceString = TemplateUtilities.ResolveMediaFromTextString(sourceString); // ensure string is parsed for macros and macros are executed correctly sourceString = RenderRteMacros(sourceString, preview); @@ -118,4 +119,4 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return sourceString; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index a7e6738374..86db8092a0 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -102,6 +102,9 @@ namespace Umbraco.Web.Templates private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)([^""]*""[^>]*data-udi="")([^""]*)(""[^>]*>)", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + /// /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path. /// @@ -145,5 +148,46 @@ namespace Umbraco.Web.Templates { return text.CleanForXss(ignoreFromClean); } + + /// + /// Parses the string looking for Umbraco image tags and updates them to their up-to-date image sources. + /// + /// + /// + /// Umbraco image tags are identified by their data-udi attributes + public static string ResolveMediaFromTextString(string text) + { + // don't attempt to proceed without a context + if (UmbracoContext.Current == null || UmbracoContext.Current.MediaCache == null) + { + return text; + } + + return ResolveImgPattern.Replace(text, match => + { + // match groups: + // - 1 = from the beginning of the image tag until src attribute value begins + // - 2 = the src attribute value excluding the querystring (if present) + // - 3 = anything after group 2 and before the data-udi attribute value begins + // - 4 = the data-udi attribute value + // - 5 = anything after group 4 until the image tag is closed + var src = match.Groups[2].Value; + var udi = match.Groups[4].Value; + if(src.IsNullOrWhiteSpace() || udi.IsNullOrWhiteSpace() || GuidUdi.TryParse(udi, out var guidUdi) == false) + { + return match.Value; + } + var media = UmbracoContext.Current.MediaCache.GetById(guidUdi.Guid); + if(media == null) + { + // image does not exist - we could choose to remove the image entirely here (return empty string), + // but that would leave the editors completely in the dark as to why the image doesn't show + return match.Value; + } + + var url = media.Url; + return $"{match.Groups[1].Value}{url}{match.Groups[3].Value}{udi}{match.Groups[5].Value}"; + }); + } } }