Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • mizzle 90 posts 325 karma points
    Dec 18, 2023 @ 17:12
    mizzle
    1

    Outputting SVG code from Media Picker for customization

    I found this page on customizing SVGs and it's exactly what I need, but I'm unable to use "HostingEnvironment" in the following code:

    var helperSvg = helperIconUrl != "" ? 
     System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + helperIconUrl) : "";
    

    because it says it's "inaccessible due to its protection level". Is there a way to get around this or fix it? Or is there an alternative code I could use to accomplish the same thing?

  • Brendan Rice 538 posts 1102 karma points
    Dec 20, 2023 @ 16:19
    Brendan Rice
    100

    I use the following helper function to get me the content of SVGs:

    public static async Task<HtmlString> RenderInlineSvgFromUrlAsync(this IHtmlHelper helper, string relativeUrl, string cssClass, Dictionary<string, string> additionalAttributes = null)
    {
        if (!string.IsNullOrEmpty(relativeUrl) && relativeUrl.EndsWith(".svg"))
        {
            var absoluteUrl = $"{helper.ViewContext.HttpContext.Request.Scheme}://{helper.ViewContext.HttpContext.Request.Host}{relativeUrl}";
    
            using var httpClient = new HttpClient();
            var svgContent = await httpClient.GetStringAsync(absoluteUrl);
    
            if (!string.IsNullOrEmpty(cssClass) || (additionalAttributes != null && additionalAttributes.Count > 0))
            {
                var svgStartTagPattern = new Regex("<svg[^>]*>", RegexOptions.IgnoreCase);
                var svgStartTagMatch = svgStartTagPattern.Match(svgContent);
                if (svgStartTagMatch.Success)
                {
                    var svgStartTag = svgStartTagMatch.Value;
    
                    // Add CSS class
                    if (!string.IsNullOrEmpty(cssClass))
                    {
                        var classAttributePattern = new Regex("class=\"[^\"]*\"", RegexOptions.IgnoreCase);
                        var classAttributeMatch = classAttributePattern.Match(svgStartTag);
    
                        if (classAttributeMatch.Success)
                        {
                            var newClassAttributeValue = classAttributeMatch.Value.Substring(0, classAttributeMatch.Value.Length - 1) + " " + cssClass + "\"";
                            svgContent = svgContent.Replace(classAttributeMatch.Value, newClassAttributeValue);
                        }
                        else
                        {
                            svgStartTag = svgStartTag.Insert(4, " class=\"" + cssClass + "\"");
                        }
                    }
    
                    // Add other attributes
                    if (additionalAttributes != null)
                    {
                        foreach (var attribute in additionalAttributes)
                        {
                            if (!svgStartTag.Contains($" {attribute.Key}="))
                            {
                                svgStartTag = svgStartTag.Insert(4, $" {attribute.Key}=\"{attribute.Value}\"");
                            }
                        }
    
                    }
                    svgContent = svgContent.Replace(svgStartTagMatch.Value, svgStartTag);
                }
            }
    
            return new HtmlString(svgContent);
        }
        return HtmlString.Empty;
    }
    

    I can then call it like this:

    <div class="show-on-scroll flex items-center justify-center">
        @await Html.RenderInlineSvgFromUrlAsync(logo.Url(), "max-h-6 max-w-4 lg:max-h-8 lg:max-w-6 object-contain object-left")
    </div>
    
  • mizzle 90 posts 325 karma points
    Dec 20, 2023 @ 17:03
    mizzle
    0

    I'm sorry, I'm entirely self-taught and I miss a lot of the basics - could you dumb this down a little bit further? Where do I actually put the helper function code? Is this something that can be called across the site in views, partials, etc.?

    It looks like what I need though, so thank you for helping me along!

  • Brendan Rice 538 posts 1102 karma points
    Dec 20, 2023 @ 17:06
    Brendan Rice
    1

    Helper is just a C# class, you can put it anywhere in the Visual Studio solution:

    using System.Text.RegularExpressions;
    using Microsoft.AspNetCore.Html;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Umbraco.Cms.Core.Models.PublishedContent;
    
    public static class SvgHelper
    {
        public static async Task<HtmlString> RenderInlineSvgFromUrlAsync(this IHtmlHelper helper, string relativeUrl, string cssClass, Dictionary<string, string> additionalAttributes = null)
        {
            // ...
        }
    }
    

    Here's Microsoft's documentation on HtmlHelpers:

    https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/views/creating-custom-html-helpers-cs

  • mizzle 90 posts 325 karma points
    Dec 20, 2023 @ 17:46
    mizzle
    0

    Thank you!

    I'm probably missing something, but it looks like this can't be used in my Partial files because of the async. Trying to find a fix just leads to different errors and I have a feeling the site's just not structured the way it should be to allow me to use this.

    I do appreciate you trying to help me out, though!

  • Brendan Rice 538 posts 1102 karma points
    Dec 20, 2023 @ 17:48
    Brendan Rice
    0

    Did you call it using await?

    @await Html.RenderInlineSvgFromUrlAsync(logo.Url(), "max-h-6 max-w-4 lg:max-h-8 lg:max-w-6 object-contain object-left")
    
  • mizzle 90 posts 325 karma points
    Dec 20, 2023 @ 17:57
    mizzle
    0

    Yeah, that's what throws the error: "Can only be used within an async method". The Partials aren't async (not that I fully understand what that means).

    It looks like it would work within a View, but most of the site is made up of Partials as widgets or specific pieces of code.

  • Brendan Rice 538 posts 1102 karma points
    Dec 20, 2023 @ 18:03
    Brendan Rice
    0

    Try render your partial like this:

    @await Html.PartialAsync("~/views/partials/layout/header.cshtml")
    

    And the MS docs:

    https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-8.0

  • mizzle 90 posts 325 karma points
    Dec 20, 2023 @ 18:49
    mizzle
    1

    Okay, I had to restructure some of the code within the Partial I was working in, then I had to change this bit:

    var absoluteUrl = $"{helper.ViewContext.HttpContext.Request.Scheme}://{helper.ViewContext.HttpContext.Request.Host}{relativeUrl}";
    

    because it was throwing a System.UriFormatException error, to:

    var absoluteUrl = $"{relativeUrl}";
    

    I'm not sure if it's just because I'm working on localhost right now and I'll need to add that back in, but it's working!

    I can't thank you enough for being patient and helping me figure this out.

  • Brendan Rice 538 posts 1102 karma points
    Dec 20, 2023 @ 18:51
    Brendan Rice
    0

    No worries, it took me a while to figure it out so I feel your pain. That is such a handy helper function to have as well, I'm glad I was able to help.

  • mizzle 90 posts 325 karma points
    Jan 22, 2024 @ 20:51
    mizzle
    0

    Hi Brendan,

    It looks like the line var svgContent = await httpClient.GetStringAsync(absoluteUrl); is throwing an error in the log and causing a 404 error when the site is Published. I'm not familiar enough with what's going on with the code to figure out what could be going wrong. If you have any ideas of where I should look for how to fix it, I'd really appreciate it.

Please Sign in or register to post replies

Write your reply to:

Draft