Copied to clipboard

Flag this post as spam?

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


  • Thomsen 76 posts 268 karma points
    Mar 13, 2019 @ 23:02
    Thomsen
    0

    Using UmbracoHelper in a custom class in v8

    Hi

    Using the UmbracoHelper in a custom class or service etc., this syntax normally works in v7, but it fails in v8.

    var umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
    

    Is there a new way of doing this now in v8?

    Best regards.

  • Warren Harding 38 posts 84 karma points
    Mar 14, 2019 @ 02:02
    Warren Harding
    102

    Previously I would use say:

    var helper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
    var node = helper.TypedContent(nodeId);
    

    Now I've updated this to:

    var helper = Umbraco.Web.Composing.Current.UmbracoHelper;
    var node = helper.Content(nodeId);
    
  • Sebastiaan Janssen 4843 posts 14347 karma points MVP admin hq
    Mar 14, 2019 @ 06:59
    Sebastiaan Janssen
    4

    But please don't use Umbraco.Web.Composing.Current- in most places you will use this kind of code (like an API controller, or a SurfaceController, etc). you can just access the UmbracoHelper already:

    var node = Umbraco.Content(1234);

    If that's not available the question is: what do you need from the UmbracoHelper exactly?

    I'm guessing the most common scenario would be to query some content, in which case you can inject IUmbracoContextFactory and get an umbracoContext from it, the safe way to do that (in case the context does not exist yet) is to Ensure it exists first:

    private readonly IUmbracoContextFactory _context;
    
    public MyComponent(IUmbracoContextFactory context)
    {
        _context = context;
    }
    
    public void Initialize()
    {
        using (var cref = _context.EnsureUmbracoContext())
        {
            var cache = cref.UmbracoContext.ContentCache;
            var node = cache.GetById(1234);
        }
    
  • Thomsen 76 posts 268 karma points
    Mar 17, 2019 @ 20:52
    Thomsen
    0

    Thanks to both of you for the answers.

    What I need is to query content from a custom helper class where Umbraco.Content is not available as it is in a surface controller or in a viewpage.

    Umbraco.Web.Composing.Current

    Solves the issue right now (though it is mentioned beeing dirty) until I get to understand DI better. Looking forward to the documentation update in general.

    Best regards.

  • Sebastiaan Janssen 4843 posts 14347 karma points MVP admin hq
    Mar 18, 2019 @ 07:04
    Sebastiaan Janssen
    1

    Let's get not dirty then!

    The basic principle of Dependency Injection is: ask and you shall receive.

    So since you're looking for Umbraco.Content, we can get there using the IUmbracoContextFactory. In your SurfaceController you can add a constructor to ask for what you need:

    public class MyTestController : SurfaceController
    {
        private readonly IUmbracoContextFactory _context;
    
        public MyTestController(IUmbracoContextFactory context)
        {
            _context = context;
        }
    
        // ... etc
    }
    

    Then in the methods you've created in your SurfaceController you can ask for that _context and use it:

        public ActionResult Index()
        {
            using (var cref = _context.EnsureUmbracoContext())
            {
                var cache = cref.UmbracoContext.ContentCache;
                var node = cache.GetById(1126);
    
                return Content(node.Name);
            }
        }
    

    Happy to look into other things you might be needing from the UmbracoHelper and provide some samples.

    This way you could eventually even start testing the logic in your controllers so you can safely refactor them at some point.

    And to prove it works, the screenshot is a bit clipped but node 1126 is open there in the backoffice:

    enter image description here

  • Bo Jacobsen 257 posts 1197 karma points
    Mar 18, 2019 @ 20:54
    Bo Jacobsen
    0

    Hi Sebastiaan.

    In Umbraco 7 we had to use the UDI when we used Umbraco Cloud. Because the Content and Media items got different id's on each Environment.

    What is the case in Umbraco 8?

    using (var cref = _context.EnsureUmbracoContext())
    {
        var cache = cref.UmbracoContext.ContentCache;
        // Can't find any UDI.
        var node =  cache.GetById(int or Guid);
                    cache.GetByContentType(PublishedContentType);
                    cache.GetByRoute(string);
                    cache.GetByXPath(string or XPathExpression);
    
        return node.Name;
    }
    
  • Thomsen 76 posts 268 karma points
    Mar 18, 2019 @ 07:38
    Thomsen
    0

    Hi again Sebastiaan

    Thanks, for clarifying once again. I try to implement your solution, but it gives me an error already in the MyTestController method:

    "Method must have a return type."

    enter image description here

  • Bjarne Fyrstenborg 1140 posts 3225 karma points MVP 3x c-trib
    Mar 18, 2019 @ 07:40
    Bjarne Fyrstenborg
    0

    The method MyTestController is the constructor of your class and in your case it needs to be TestSurfaceController.

    /Bjarne

  • Sebastiaan Janssen 4843 posts 14347 karma points MVP admin hq
    Mar 18, 2019 @ 07:45
    Sebastiaan Janssen
    1

    Yep like Bjarne points out correctly, your constructor method needs to have the same name as your class name. In my case, my class is named MyTestController so the constructor I create is public MyTestController().

    Pro-tip, in Visual studio when you type ctor and then hit Enter, it will create the constructor method for you.

    enter image description here

  • Bjarne Fyrstenborg 1140 posts 3225 karma points MVP 3x c-trib
    Mar 18, 2019 @ 07:51
    Bjarne Fyrstenborg
    0

    If the purpose is to get a specific node in the SurfaceController it should also be sufficient to access it via UmbracoHelper

    public ActionResult Index()
    {
        var node = Umbraco.Content(1126);
    
        return Content(node.Name);
    }
    

    but I guess this was to demonstrate how Dependency Injection works in v8 and how to access e.g. UmbracoContext in non Umbraco Controllers or with other dependencies?

  • Thomsen 76 posts 268 karma points
    Mar 18, 2019 @ 08:32
    Thomsen
    0

    Yes - you are right, it was to demonstrate how to access content from non Umbraco Controllers or "out of Umbraco scope", thanks.

  • Sebastiaan Janssen 4843 posts 14347 karma points MVP admin hq
    Mar 18, 2019 @ 09:48
    Sebastiaan Janssen
    0

    Ah, I was not awake yet! Yes, in a SurfaceController UmbracoHelper is "just" available to you! Thanks Bjarne. 👍

  • Thomsen 76 posts 268 karma points
    Mar 18, 2019 @ 08:07
    Thomsen
    0

    Hi - thank you,

    Now it makes sense - the constructor should be named after the class name, and it works..! - Except when I try to use _context in a static method within the class. The example here, is from my non Umbraco Controller helper class:

    enter image description here

  • Thomsen 76 posts 268 karma points
    Mar 18, 2019 @ 08:23
    Thomsen
    0

    It seems to work if I remove readonly when defining _context and make it static also (don't know if I am beeing dirty now ;-). Like this:

      private static IUmbracoContextFactory _context;
    
        public ContentHelpers(IUmbracoContextFactory context)
        {
            _context = context;
        }
    
  • David Zweben 213 posts 619 karma points
    Apr 12, 2019 @ 19:02
    David Zweben
    0

    I would also like to know this. Is it a good idea to create a static instance of IUmbracoContextFactory for use in a static method, or is there a better way to do this?

  • David Zweben 213 posts 619 karma points
    Apr 15, 2019 @ 18:20
    David Zweben
    0

    I could use some help with this. I have the code shown below. When I try to create a new instance of CustomUrlHelper in my template so I can use it, though, it expects me to pass it an argument of type IUmbracoContextFactory, because that's specified as a required parameter in the constructor.

    How am I supposed to instantiate a custom class that uses DI?

    namespace MySite.Core
    {
        public class CustomUrlHelper
        {
            private readonly IUmbracoContextFactory _context;
    
            public CustomUrlHelper(IUmbracoContextFactory context)
            {
                _context = context;
            }
    
            public string GetNodeUrl(int nodeId)
            {
                IPublishedContent node = null;
    
                using (var cref = _context.EnsureUmbracoContext())
                {
                    IPublishedContentCache cache = cref.UmbracoContext.ContentCache;
                     node = cache.GetById(nodeId);
                }
    
                return node.Url;
            }
        }
    }
    
  • Søren Gregersen 299 posts 1182 karma points MVP c-trib
    Apr 15, 2019 @ 19:15
    Søren Gregersen
    0

    How am I supposed to instantiate a custom class that uses DI?

    in DI you don't :)

    DI is when your dependecies are injected. That means you don't instantiate them, but get them passed (injected) to you. ( https://en.wikipedia.org/wiki/Dependency_injection | https://www.codementor.io/mrfojo/c-with-dependency-injection-k2qfxbb8q ... and more... )

    To use your helper, you would need a controller, that would take the helper as a constructor parameter.

    public class MyController : Controller {
        private readonly CustomUrlHelper customUrlHelper;
        public MyController(CustomUrlHelper customUrlHelper){
            this.customUrlHelper=customUrlHelper;
        }
        public object Index(){
            //... use customUrlHelper instance...
        }
    }
    
  • David Zweben 213 posts 619 karma points
    Apr 15, 2019 @ 19:25
    David Zweben
    0

    Thanks. So am I understanding correctly that, to use DI in Umbraco, I can't just inject dependencies into my custom class and use that normally (which is what I was trying to do), but I also have to inject any classes that are using injected dependencies into a controller?

    If so, is the best way to make my custom class accessible globally to change the default Umbraco controller via route hijacking to one that takes my class as a construction parameter?

  • Søren Gregersen 299 posts 1182 karma points MVP c-trib
    Apr 15, 2019 @ 19:36
    Søren Gregersen
    0

    I think you misunderstood my answer then :)

    DI handles creation/instantiating/resolving of all the dependencies - this is not just in Umbraco.

    In order for you to use it, you need a constructor (very simplified). Your template does not have a constructor :)

    You are free to instantiate your class in the template, that could be done like this (your class would then need a bit of refactoring):

    @inherits Umbraco.Web.Mvc.UmbracoViewPage<ContentModels.HomePage>
    @{
        var customUrlHelper = new CustomUrlHelper(this.UmbracoContext);
    }
    <a href="@customUrlHelper.GetNodeUrl(123)">text</a>
    
  • David Zweben 213 posts 619 karma points
    Apr 15, 2019 @ 19:41
    David Zweben
    0

    Søren,

    Thanks again for the reply. I'm still a bit confused, though. I thought that the point of DI was to automatically take care of providing the dependencies (such as UmbracoContext). If I have to pass this.UmbracoContext into the custom class as an argument when instantiating it, isn't that a case of me providing the dependency manually? If so, what is LightInject even doing?

  • Søren Gregersen 299 posts 1182 karma points MVP c-trib
    Apr 15, 2019 @ 19:46
    Søren Gregersen
    0

    Well, as I pointed out, you need somewhere your dependencies can be injected (the constructor), which your template don't have.

    Your template can't have dependencies, since you don't control the constructor. The only way you can pass dependencies to a template, is by having a controller adding them to the model passed to the template.

  • David Zweben 213 posts 619 karma points
    Apr 15, 2019 @ 19:50
    David Zweben
    0

    Ok, I see. So basically I am just doing it manually instead of getting the dependencies injected in that case, because I don't have control over the constructor to request what I want?

    If so, wouldn't it also work to use a custom controller via route hijacking so that I can specify the dependencies I want?

  • Søren Gregersen 299 posts 1182 karma points MVP c-trib
    Apr 15, 2019 @ 19:51
  • David Zweben 213 posts 619 karma points
    Apr 15, 2019 @ 19:54
    David Zweben
    1

    Thanks, this helped clarify things a lot!

  • David Zweben 213 posts 619 karma points
    Apr 15, 2019 @ 20:50
    David Zweben
    0

    Sorry to flood you with questions, but I think this will be helpful to a lot of people.

    I set up a custom controller as shown below. I can see that CustomUrlHelper is getting injected into the controller as expected, which is great!

    Is it possible (and if so, good practice) to pass the instance of CustomUrlHelper that I have inside the controller into the template, so that I can use it there? Or is this approach only good if I am keeping logic in the controller?

    namespace MySite.Core.Controllers
    {
        public class DoctypeNameHereController : RenderMvcController
        {
            private readonly CustomUrlHelper customUrlHelper;
    
            public DoctypeNameHereController(CustomUrlHelper injectedCustomUrlHelper)
            {
                customUrlHelper = injectedCustomUrlHelper;
            }
    
            public override ActionResult Index(ContentModel model)
            {
                return base.Index(model);
            }
    
        }
    
    }
    
  • Marc Goodson 978 posts 6526 karma points MVP 4x c-trib
    Apr 21, 2019 @ 23:11
    Marc Goodson
    1

    Hi David

    Thinking one possibility here would be to create your own CustomViewPage to use instead of UmbracoViewPage.. something like this:

    using Umbraco.Web.Mvc;
    using Umbraco8.Services;
    using Current = Umbraco.Web.Composing.Current;
    
    namespace Umbraco8.Models
    {
        public abstract class CustomViewPage<T> : UmbracoViewPage<T>
        {
            public readonly ICustomUrlHelper _customUrlHelper;
            public CustomViewPage()
            {
                _customUrlHelper = (ICustomUrlHelper)Current.Factory.GetInstance(typeof(ICustomUrlHelper));
            }
    
            protected override void InitializePage()
            {
                base.InitializePage();
            }
        }
        public abstract class CustomViewPage : UmbracoViewPage
        {
            public readonly ICustomUrlHelper _customUrlHelper;
            public CustomViewPage()
            {
                _customUrlHelper = (ICustomUrlHelper)Current.Factory.GetInstance(typeof(ICustomUrlHelper));
            }
    
            protected override void InitializePage()
            {
                base.InitializePage();
            }
        }
    }
    

    If you define an interface (eg ICustomUrlHelper) for your CustomUrlHelper and register with DI, in a Composer:

       composition.Register<ICustomUrlHelper, CustomUrlHelper>(Lifetime.Request);
    

    Then you should be able to change the inherits statement at the top of your view templates to use your new CustomViewPage eg:

    @using Umbraco8.Models
    @inherits CustomViewPage<ContentModels.Blogpost>
    @using ContentModels = Umbraco.Web.PublishedModels;
    @{
        Layout = "master.cshtml";
    //use your helper!
       var something = _customUrlHelper.GetCustomUrl("whatever");
    }
    @Html.Partial("~/Views/Partials/SectionHeader.cshtml")
    <section class="section">
        <div class="container">
    

    and be able to call your service within your view/templates....

    The answer to your question though would probably depend more upon the context... the name of your helper seems like it might perhaps live as a an extension method on System.Web.Mvc.UrlHelper... calling it within your views as Url.DoCustomThing(), I guess it depends on the custom thing! but anyway above might be an option if you have a helper service that you want to register with DI and call from either controllers or views...

    regards

    Marc

  • David Zweben 213 posts 619 karma points
    Apr 22, 2019 @ 11:51
    David Zweben
    0

    This is really helpful, thanks!

  • George Phillipson 53 posts 158 karma points
    Apr 22, 2019 @ 11:08
    George Phillipson
    1

    I have been playing around with V8, it's getting hard to work out what the question is about now.

    If you are looking to use DI, then the test code I have below may help.

    using Umbraco.Core;
    using Umbraco.Core.Composing;
    using Umbraco.Core.Models.PublishedContent;
    using Umbraco.Web;
    
    namespace Web.Models.Helpers
    {
        public interface IHelperService
        {
            int Sum(int x, int y);
            IPublishedContent PageContentById(int pageId);
        }
    
        public class HelperComposer : IUserComposer
        {
            public void Compose(Composition composition)
            {
                composition.Register<IHelperService, HelperService>(Lifetime.Singleton);
            }
        }
    
        public class HelperService : IHelperService
        {
            private readonly IUmbracoContextFactory _contextFactory;
    
            public HelperService(IUmbracoContextFactory contextFactory)
            {
                _contextFactory = contextFactory;
            }
            public HelperService() { }
            public int Sum(int x, int y)
            {
                int sum = (x + y);
                return sum;
            }
    
            public IPublishedContent PageContentById(int pageId)
            {
    
                using (var cf = _contextFactory.EnsureUmbracoContext())
                {
                    var cache = cf.UmbracoContext.ContentCache;
                    IPublishedContent node = cache.GetById(pageId);
    
                    return node;
                }
            }
        }
    }
    

    Now in my Controller, I have:

    public class HomeController : RenderMvcController
        {
            private readonly IUmbracoContextFactory _contextFactory;
            private readonly IHelperService _helperService;
    
            public HomeController(IUmbracoContextFactory contextFactory, IHelperService helperService)
            {
                _contextFactory = contextFactory;
                _helperService = helperService;
            }
    
            public ActionResult Home()
            {
                var maths = _helperService.Sum(10, 5);
                var currentPage = CurrentPage.Id;
    
                var composeData = _helperService.PageContentById(currentPage);
    
    
                return View("~/Views/Home.cshtml");
            }
        }
    

    The above returns the following

    enter image description here

    I also played about with the following

    using Umbraco.Core.Models.PublishedContent;
    using Umbraco.Web;
    
    namespace Web.Models.Helpers
    {
        public class PageData
        {
            public IPublishedContent PageContentById(int pageId, IUmbracoContextFactory contextFactory)
            {
                using (var cf = contextFactory.EnsureUmbracoContext())
                {
                    var cache = cf.UmbracoContext.ContentCache;
                    IPublishedContent node = cache.GetById(pageId);
    
                    return node;
                }
            }
        }
    }
    

    Controller

     public ActionResult Home()
            {
                var maths = _helperService.Sum(10, 5);
                var currentPage = CurrentPage.Id;
    
                PageData pData = new PageData();
                var bodyText2 = pData.PageContentById(currentPage, _contextFactory);
                var htmlString = bodyText2.Value<IHtmlString>("bodyText");
    
                var composeData = _helperService.PageContentById(currentPage);
    
                var text = composeData.Value("bodyText");
    
                return View("~/Views/Home.cshtml");
            }
    

    Here I'm passing the _contectFactory to my class along with the page Id and it returns the following

    enter image description here

    Hopefully, this will help someone

    Playing around a bit more, say you have a layout page and you only need to get the page title and description.

    For this test, I created a static class which returned a Tuple to see if it worked to return the title and description.

    I then passed in the current pageId as well as UmbracoContext

    var pageId = Umbraco.AssignedContentItem.Id;
    
        var metaInformation = MetaTitleDescriptionHelper.MetaInformationTuple(pageId,UmbracoContext);
    

    The class is below and the codes works as expected and returns the title and description.

    namespace Web.Helper.MetaHelper
    {
        public static class MetaTitleDescriptionHelper
        {
            public static (string Title, string Description) MetaInformationTuple(int currentPage, UmbracoContext context)
            {
                var nodeId = context.ContentCache.GetById(currentPage);
    
                if (nodeId == null) return (string.Empty, string.Empty);
    
                string title        = nodeId.Value<string>("pageTitle", defaultValue: nodeId.Name);
                string description  = nodeId.Value<string>("pageDescription");
    
                return (title, description);
    
            }
        }
    }
    
  • steschu 66 posts 381 karma points
    Jun 11, 2019 @ 13:45
    steschu
    0

    I just read almost the whole thread about the UmbracoContext issue and DI thing. But it is difficult for me to adapt this in my case.

    I have static class, that uses the UmbracoHelper in order to retrieve a Dictionary values. This class is accessed by various Data Annotation classes. These Data Annotation classes are used as Attributes in a Model class. This model is used in a SurfaceController.

    When I understand DI right, I have to pass the context from the controller to the model, somehow to the Attributes and in the Data Annotations to the static class? Maybe I am using bad architecture... but this seems quite complicated for me.

    Stephan

  • Søren Gregersen 299 posts 1182 karma points MVP c-trib
    Jun 11, 2019 @ 14:09
    Søren Gregersen
    0

    Hi Stephan,

    The problem with using static accessors is that you dont know if/when they are accessible.

    Say you use UmbracoContext.Current, this creates a dependency on something that you can’t control. The umbracocontext is only available after something else has initialized it.

    If you use DI you can register when/how the UmbracoContext should be created/accessed.

    The main purpose of all this is to create code that is more SOLID (https://en.m.wikipedia.org/wiki/SOLID). I tend to think about unit testing and “can this run in a console app” when coding, to help with this. Your attributes accessing UmbracoContext would not fit this thinking I guess

  • steschu 66 posts 381 karma points
    Jun 11, 2019 @ 14:35
    steschu
    0

    So static accessors and DI are no friends? Guess I am old fashioned in my architecture. I use static classes a lot, mostly to render our some recurring HTML, e.g. datapager buttons. And then often need information about the current page, which I retrieve vis the UmbracoContext and UmbracoHelper.

  • Søren Gregersen 299 posts 1182 karma points MVP c-trib
    Jun 11, 2019 @ 14:58
    Søren Gregersen
    0

    Hi,

    As long as you provide the dependencies, you are ok. But think about how you would test it, and how much knowledge you would need to have about the whole umbraco environment, for setting up such a test.

    Think about how you would explain your code to a new developer on the project - “that’s handlede for me”, “that’s magic” and “it’s always there” are just very bad answers when talking about dependencies.

  • George Phillipson 53 posts 158 karma points
    Jun 11, 2019 @ 14:26
    George Phillipson
    0

    Hi Stephan

    Are you pulling the dictionary items for form validation, if yes let me know and I'll send you the code I'm using for this? The only difference I'm pulling the values from my contactDoctype

    George

  • steschu 66 posts 381 karma points
    Jun 11, 2019 @ 14:32
    steschu
    0

    @George: What I am using is this: https://gist.github.com/rasmuseeg/3ca1630d45c54f485088#file-umbracodictionarydataannotations-cs

    And there is a static UmbracoDictionary class that retrieves the values by key from the Dictionary

  • George Phillipson 53 posts 158 karma points
    Jun 11, 2019 @ 15:06
    George Phillipson
    0

    Hi Stephan

    Below is how I have done it, point to note I have 2 different forms on my site, contact and blog, so I have hardcoded those values.

    I have removed a lot of code, but hopefully, you can follow it

    Contact View

    @{
        Layout = "Layout.cshtml";
        IHelperService helperService = new HelperService();
    }
    
    @Html.Partial("~/Views/Partials/Contact/pvContact.cshtml", new ContactViewModel(helperService, UmbracoContext))
    

    ContactViewModel - I have only added FirstName and this has PlaceHolder text and Required Helper message

    public class ContactViewModel
        {
            private static IHelperService   _iHelperService;
            private const string NodeName   = "contact";
            private static UmbracoContext   _context;
    
    
            public ContactViewModel(IHelperService iHelperService, UmbracoContext context)
            {
                _iHelperService = iHelperService;
                _context = context;
    
            }
    
            public ContactViewModel() { }
    
    
            [UmbracoGetPropertyValueDisplayNameHelper("nameLabel", "contact")]
            [UmbracoGetPropertyValueRequiredHelper("nameRequired", "contact")]
            public string FromName { get; set; }
    }
    

    Validation Helper

    using System.Linq;
    using Umbraco.Web;
    using Web.ContentHelper.ContentHelper;
    
    namespace Web.ContentHelper.Helpers.FormValidation
    {
        public static class UmbracoPropertyValidationHelper
        {
            public static string GetPropertyValueItem(string key,string nodeAlias)
            {
                var nodeId          = Umbraco.Web.Composing.Current.UmbracoHelper;
                var currentPage     = nodeId.ContentAtRoot().DescendantsOrSelfOfType(nodeAlias).First().Id;
    
                var data            = nodeId.Content(currentPage);
    
                string errorMessage = data.Value<string>(key);
    
                return errorMessage;
    
            }
        }
    }
    

    If you do not want to use UmbarcoHelper, you can also use the following

       using System.Linq;
    using Umbraco.Web;
    using Current = Umbraco.Web.Composing.Current;
    
    namespace Web.ContentHelper.Helpers.FormValidation
    {
        public static class UmbracoPropertyValidationHelper
        {
            public static string GetPropertyValueItem(string key,string nodeAlias)
            {
                return Current.UmbracoContext.ContentCache.GetByXPath($"//{nodeAlias}").Select(x=>x.Value<string>(key)).First();
    
            }
        }
    }
    

    Placeholder Helper

    using System;
    using System.ComponentModel;
    
        namespace Web.ContentHelper.Helpers.FormValidation
        {
            [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
            public class UmbracoGetPropertyValueDisplayNameHelper : DisplayNameAttribute
            {
                private readonly string _getPropertyValueKey;
                private readonly string _pageId;
                public UmbracoGetPropertyValueDisplayNameHelper(string propertyValueKey, string pageId)
                {
                    _getPropertyValueKey = propertyValueKey;
                    _pageId = pageId;
                }
    
                public override string DisplayName => UmbracoPropertyValidationHelper.GetPropertyValueItem(_getPropertyValueKey, _pageId);
            }
        }
    

    Required Field helper

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Web.Mvc;
    
    namespace Web.ContentHelper.Helpers.FormValidation
    {
        [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
        public class UmbracoGetPropertyValueRequiredHelper : RequiredAttribute, IClientValidatable
        {
            private readonly string _errorMessageKey;
            private readonly string _pageId;
    
            public UmbracoGetPropertyValueRequiredHelper(string errorMessageKey, string pageId)
            {
                _errorMessageKey = errorMessageKey;
                _pageId = pageId;
                ErrorMessage = UmbracoPropertyValidationHelper.GetPropertyValueItem(_errorMessageKey,_pageId);
            }
    
    
    
            public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
            {
                ErrorMessage = UmbracoPropertyValidationHelper.GetPropertyValueItem(_errorMessageKey,_pageId);
                var error = FormatErrorMessage(metadata.DisplayName);
                var rule = new ModelClientValidationRequiredRule(error);
    
                yield return rule;
            }
        }
    }
    

    In my CMS contact doctype I have

    nameLabel

    nameRequired

  • Markus Johansson 1608 posts 4535 karma points
    Jul 10, 2019 @ 14:07
    Markus Johansson
    0

    Hi!

    I might be late to the party here but figured I'll share my solution here.

    Depending on what you need to do with the helper you can inject a "sub set" of the features from the UmbracoHelper. Ie if you're looking to query the cache use IPublishedContentQuery.

    Just put it as a constructor-parameter and Umbraco will inject it for you.

    private readonly IPublishedContentQuery _publishedContent;
    
        public MockSettingsRepository(IPublishedContentQuery publishedContent)
        {
            _publishedContent = publishedContent;
        }
    

    Have a look here to use how the UmbracoHelper is using different interfaces internally: https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/src/Umbraco.Web/UmbracoHelper.cs

  • suzyb 458 posts 864 karma points
    Jul 23, 2019 @ 18:44
    suzyb
    0

    I'm using the following code in Umbraco 8.1.0 but it's telling me this is deprecated now.

    var cache = cref.UmbracoContext.ContentCache
    

    Does anyone know what the new way of doing this would be?

  • Bjarne Fyrstenborg 1140 posts 3225 karma points MVP 3x c-trib
    Jul 23, 2019 @ 18:48
    Bjarne Fyrstenborg
    0

    In Umbraco v8.1 you can just access it via the Content property, e.g.

    var node = cref.UmbracoContext.Content.GetById(1234);
    

    /Bjarne

  • suzyb 458 posts 864 karma points
    Jul 23, 2019 @ 18:51
    suzyb
    0

    That worked but I'm sure it threw an error when I tried it earlier :(

    Thanks for the super quick reply.

  • Marc Love 305 posts 770 karma points
    Jul 27, 2019 @ 11:36
    Marc Love
    0

    Hi Folks,

    Been struggling with this new approach. Can anyone explain how I can access UmbracoHelper in a custom HttpHandler.

    With DI the constructor now has a parameter. If I setup my Custom HttpHandler I get an error regarding the constructor now containing a parameter.

    Cheers,

    Marc

  • Marc Goodson 978 posts 6526 karma points MVP 4x c-trib
    Jul 28, 2019 @ 22:44
    Marc Goodson
    0

    Hi Marc

    With common extension points, Umbraco tends to give you an abstract class to work from that exposes UmbracoHelper, Services etc as 'Properties' on the abstract class - no need to use DI, they are already injected, eg RenderMvcController, SurfaceController, UmbracoViewPage etc... and I believe a HttpHandler is no different!

    If you create your HttpHandler and make it inherit and implement the abstract class UmbracoHttpHandler from Umbraco.Web namespace then you should have access to the usual 'gateway' properties... eg

    using System;
    using System.Web;
    using Umbraco.Core.Models.PublishedContent;
    using Umbraco.Core.Models;
    using Umbraco.Web;
    
    namespace Umbraco8.Handlers
    {
        public class CustomHttpHandler : UmbracoHttpHandler
        {
            public override bool IsReusable => true;
    
            public override void ProcessRequest(HttpContext context)
            {
               // here we have access to Umbraco. and Services. 
                //query the cache
                IPublishedContent myContent = Umbraco.Content(1234);
               //use media service or something
               IMedia mediaItem = Services.MediaService.GetMediaByPath("/media/12345/somepathorsomething.pdf");
    
            }
        }
    }
    

    You can see the Abstract class in the source and what it provides access to:

    https://github.com/umbraco/Umbraco-CMS/blob/853087a75044b814df458457dc9a1f778cc89749/src/Umbraco.Web/UmbracoHttpHandler.cs

    regards

    Marc

  • Marc Love 305 posts 770 karma points
    Jul 29, 2019 @ 10:29
    Marc Love
    0

    Hi Marc,

    Thanks for the reply. Was all excited about trying out the code and then no luck!!!

    It looks like this may be an issue with the new V8 code however I get an error when trying this approach.

    Object reference not set to an instance of an object.
    
    [NullReferenceException: Object reference not set to an instance of an object.]
       Umbraco.Web.Runtime.<>c.<Compose>b__0_6(IFactory factory) in d:\a\1\s\src\Umbraco.Web\Runtime\WebInitialComposer.cs:116
       Umbraco.Core.FactoryExtensions.GetInstance(IFactory factory) in d:\a\1\s\src\Umbraco.Core\FactoryExtensions.cs:22
       Umbraco.Web.UmbracoHttpHandler..ctor() in d:\a\1\s\src\Umbraco.Web\UmbracoHttpHandler.cs:17
       BusinessLogic.ThemeHttpHandler..ctor() +36
       BusinessLogic.ThemeRouteHandler.GetHttpHandler(RequestContext requestContext) +70
       System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +215
       System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +222
       System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +212
       System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +90
    
  • Marc Love 305 posts 770 karma points
    Jul 29, 2019 @ 10:42
    Marc Love
    0

    Actually may be something else I am doing wrong.

    So that I can create all of this via code I have a class which inherits from IUserComposer

    In the Compose Method I have:

    composition.Components().Append<RegisterStyleSheetRouteComponent>();
    

    This calls the following class:

    public class RegisterStyleSheetRouteComponent : IComponent
    {
    
        // initialize: runs once when Umbraco starts
        public void Initialize()
        {
            RouteTable.Routes.Add("ThemeStyle", new Route("theme.style", new BusinessLogic.ThemeRouteHandler()));
        }
    
        // terminate: runs once when Umbraco stops
        public void Terminate()
        {
        }
    }
    

    I then have the following simple class which references my HttpHandler:

    public class ThemeRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        { 
            return new ThemeHttpHandler();
        }
    }
    

    This is expecting a return type of IHttpHandler. How do I change this code to work with the UmbracoHttpHandler.

    Cheers,

    Marc

  • Marc Love 305 posts 770 karma points
    Jul 29, 2019 @ 10:57
    Marc Love
    0

    Adding to this, it looks like there IS an issue with UmbracoHttpHandler.

    I added my handler via web.config and got the same error:

    <add verb="*" path="theme.style" name="HttpHandler" type="StarterKit.BusinessLogic.ThemeHttpHandler, StarterKit"/>
    
  • Marc Goodson 978 posts 6526 karma points MVP 4x c-trib
    Jul 30, 2019 @ 19:20
    Marc Goodson
    1

    Hi Marc

    I created a quick test httphandler in a vanilla V8 site to verify and I get the same issue...

    ... The thing that is null is the injected UmbracoHelper!

    And if you try to use the umbracoContextFactory + ensureumbracocontext approach then this appears also to be null ..

    So I've raised an issue on the tracker for it..

    ... apologies for the bum steer certainly seemed the recommended way....

    Regards

    Marc

  • Marc Love 305 posts 770 karma points
    Jul 31, 2019 @ 08:55
    Marc Love
    1

    Hi Marc,

    Thanks for the heads up and majorly appreciate you looking into this for me. Umbraco 8 seems great for basic stuff but every time I try to do something a bit more complex I keep running into issues. Really itching to get going with a full on Umbraco 8 site but it looks like client sites will need to stay on Umbraco 7 for the foreseeable future.

    Cheers,

    Marc

  • Marc Goodson 978 posts 6526 karma points MVP 4x c-trib
    Aug 20, 2019 @ 08:35
    Marc Goodson
    1

    Hi Marc

    It was interesting to try to pick at what was going awry here, and with new software it's always a case of is it something that's been broken or not, or am I misunderstanding in some way, or something else! Horrible if it's just you that can't make it work too!

    Anyway Shannon has worked out why it's not working as you'd expected.

    https://github.com/umbraco/Umbraco-CMS/issues/6018

    It's because each incoming request is handled differently if it's a server request or a client side request - how this is determined is via the extension of the file, and .style isn't one that Umbraco knows about, and so the request is deemed to be clientside and an UmbracoContext isn't created.

    so if in your web.config you add the handler for theme.axd instead of theme.style

    then your handler that inherits from UmbracoHttpHandler should 'just work'

    and you'll be able to use Umbraco. to access the cache

    regards

    Marc

  • Marc Love 305 posts 770 karma points
    30 days ago
    Marc Love
    0

    Hi Marc,

    Tested your solution and works perfectly. Definitely a pint I owe you if we bump into each other at future meetups.

    Cheers,

    Marc

Please Sign in or register to post replies

Write your reply to:

Draft