November Happy Hour will be moved to Thursday December 5th.

Relative to Public (absolute) URL's in TinyMCE content in EPiServer

Vote:
 

In one of our page types on our site we have a property that allows end users to create email templates that are used for sending out automated emails to visitors of te website. Inside the property they can add images and links, but the problem i am finding is that images and links are being stored inside the TinyMCE editor as relative paths so when the email is sent out the visitor cannot view the image or get to the actual link.

So for example, an image added to the property in the TinyMCE editor is stored like this:

<img src="/EPiServer/CMS/Content/globalassets/en/images/archto_footer.png,,1743?epieditmode=False" alt="ArchTO_Footer.png" width="187" height="70" />

As you can see the above URL is not the correct format that i need:

  • It has a relative path.
  • Inside the URL the relative path also doesn't look to be the public URL of the image since it has "/EPiServer/CMS/Content/" within it, as well as ",,1743?epieditmode=False".

Even if i put the URL above in the browser on a icognito tab and adjust the URL to include the base URL of the site, it asks me to login to the website but the image is not locked and is public, which tells me the URL given is more the editor URL and not the public URL that i need.

The same goes for regular links to content as well.


How can i go abouts resolving this so that when an image is added to the TinyMCE editor or even a link that the URL is actually the public one so that when i generate the emails and take the content from the email template property that the URL's are properly formed and not relative

#229694
Oct 21, 2020 14:21
Vote:
 

Hi, without code on how you create the email it is not easy to help, but I guess this link can help https://antecknat.se/blog/2016/03/07/xhtmlstring-render-blocks-and-convert-link-to-external-without-htmlhelper/

It is for epi 8, but I don't think the xhtmlstring have changed and should still be valid

#229697
Oct 21, 2020 18:02
Vote:
 

There really is no code to show because I'm not doing anything fancy to the value that the user entered into the page in the "EmailTemplate" property.  The property is just a basic property that when in the editor renders as the TinyMCE editor.

Here is the property on my pagetype that the user fills in with what the emails should say when sent out:

        [CultureSpecific]
        [Display(Name = "Email template for visitor submission",
            GroupName = SystemTabNames.Content,
            Description = "Message sent to the submitter once the event has been sucessfully submitted.",
            Order = 250)]
        [Searchable(false)]
        public override XhtmlString EmailTemplateBodyMessage { get; set; }

When the user fills out the contents of that property and adds images and links to make the email more presentable, then i take the actual XhtmlString result and add it to the body of the email and send it out via SMTP.

When examining that property before i assign it to the email body the URL's are in relative fashion and look as they do in my original post.

I also tried the code above via that link you posted by trying to parse the URL fragments out of the XhtmlString result but the result is the same. Is there a setting or something that needs to be applied to allow images and links to not be saved as relative within the editor and stored in the XhtmlString property?

#229698
Oct 21, 2020 18:51
Vote:
 

Ugh, this was harder then I though

This should have worked:

public static XhtmlString ToExternalLinks(this XhtmlString xhtmlString)
        {
            var result = new StringBuilder();
            foreach (var fragment in xhtmlString.Fragments)
            {
                var urlFragment = fragment as UrlFragment;
                result.Append(urlFragment != null ? UrlResolver.Current.GetUrl((new UrlBuilder(urlFragment.InternalFormat)), new VirtualPathArguments() { ContextMode = ContextMode.Default, ForceAbsolute = true }) : fragment.InternalFormat);
            }

            return new XhtmlString(result.ToString());
        }

Since epi have added "ForceAbsolute = true" in the VirtualPathArguments, but did it work here? No.

So if you have Geta.EPi.Extensions installed, you can instead use this

public static XhtmlString ToExternalLinks(this XhtmlString xhtmlString)
        {
            var result = new StringBuilder();
            foreach (var fragment in xhtmlString.Fragments)
            {
                var urlFragment = fragment as UrlFragment;
                result.Append(urlFragment != null ? UrlResolver.Current.GetUrl((new UrlBuilder(urlFragment.InternalFormat)), new VirtualPathArguments() { ContextMode = ContextMode.Default }).AddHost() : fragment.InternalFormat);
            }

            return new XhtmlString(result.ToString());
        }

If you don't have that addon and don't want to install it, you need these extensions too

public class UriHelpers
    {
        /// <summary>
        /// Returns base URI for the site.
        /// </summary>
        /// <returns>Base site URI</returns>
        public static Uri GetBaseUri()
        {
            var context = HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) : null;
            return GetBaseUri(context, SiteDefinition.Current);
        }

        /// <summary>
        /// Returns base URI for the site.
        /// </summary>
        /// <returns>Base site URI</returns>
        public static Uri GetBaseUri(HttpContextBase context, SiteDefinition siteDefinition)
        {
            var siteUri = context != null
                ? context.Request.Url
                : siteDefinition.SiteUrl;

            var scheme = context != null && !string.IsNullOrEmpty(context.Request.Headers["X-Forwarded-Proto"])
                ? context.Request.Headers["X-Forwarded-Proto"].Split(',')[0]
                : siteUri?.Scheme;

            var urlBuilder = new UrlBuilder(siteUri)
            {
                Scheme = scheme ?? "https"
            };
            return urlBuilder.Uri;
        }
    }
/// <summary>
    ///     String extensions.
    /// </summary>
    public static class StringExtensions
    {

        /// <summary>
        /// Adds scheme and host to a relative URL. Uses UriHelpers.GetBaseUri() to retrieve base URL for the scheme and host.
        /// </summary>
        /// <param name="url">URL</param>
        /// <returns>Returns URL with scheme and host.</returns>
        public static string AddHost(this string url)
        {
            return url.AddHost(UriHelpers.GetBaseUri);
        }

        /// <summary>
        /// Adds scheme and host to a relative URL.
        /// </summary>
        /// <param name="url">URL</param>
        /// <param name="getBaseUri">Function which returns base URL.</param>
        /// <returns>Returns URL with scheme and host.</returns>
        public static string AddHost(this string url, Func<Uri> getBaseUri)
        {
            var baseUri = getBaseUri();
            var uri = new Uri(baseUri, url).ToString();
            return
                new UriBuilder(uri) { Scheme = baseUri.Scheme, Port = -1 }
                .ToString();
        }
    }

https://github.com/Geta/EPi.Extensions/blob/master/src/Geta.EPi.Extensions/StringExtensions.cs

https://github.com/Geta/EPi.Extensions/blob/master/src/Geta.EPi.Extensions/Helpers/UriHelpers.cs

Hope it works then :) 

#229699
Oct 21, 2020 21:05
Sebastian Enberget - Nov 09, 2021 9:13
Note to self as I had to do the same now: if returning as a new xhtmlstring, then it will convert the url back to internal url.
Vote:
 

Thanks for taking the time to help Sebbe. I will try the extension and see if that works for what i need.

Cheers!

#229700
Oct 21, 2020 21:07
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.