Copied to clipboard

Flag this post as spam?

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


  • Peter Laurie 42 posts 116 karma points
    Sep 20, 2019 @ 16:44
    Peter Laurie
    0

    Modify Umbraco 8 URLs with the UrlProvider and ContentFinder

    Hi, One of my final tasks in converting our Umbraco 7 build to Umbraco 8 is to remove the category from our guide links. For example: website/guide/this-category/this-article becomes website/guide/this-article. On our Umbraco 7 built I used the following tutorial to get this working: https://24days.in/umbraco-cms/2014/urlprovider-and-contentfinder

    Now, I am trying to convert this to Umbraco 8.

    I have used the following code to register the classes at start up:

    Content UrlProvider:

    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    public class UrlProviderStartUp : IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.UrlProviders().InsertBefore<DefaultUrlProvider, GuideUrlProvider>();
        }
    }
    

    Content finder:

    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    public class ContentFinderStartUp : IUserComposer
    {
        public void Compose(Composition composition)
       {
    composition.ContentFinders().InsertBefore<ContentFinderByUrl, GuideContentFinder>();
        }
    }
    

    First of all here is my Umbraco 8 code for the UrlProvider, which is causing the biggest issue, Umbraco now uses UrlInfo

    public class GuideUrlProvider : BaseClass, IUrlProvider
    {
        public IEnumerable<UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
        {
            return Enumerable.Empty<UrlInfo>();
         }
    
    public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current)
    {
      try
      {
        var contentUrl = umbracoContext.Content.GetById(content.Id);
        if (contentUrl != null && content.ContentType.Alias == "guideArticle" && content.Parent != null && content.Parent.Parent.Name == "Buy to let guide")
        {
          // code to remove CATEGORY section of url
    
          if (content.Parent.ContentType.Alias == "guidesIndex")
          {
            var url = string.Empty;
            //this will remove the category name from the guide url
            var path = content.Parent.Url;
            var parts = path.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
    
            UrlInfo urll = "/" + parts[0] + "/" + content.Url + "/";
    
            return urll;
          }
        }
      }
      catch { }
    
    
      return null;
       }
    }
    

    The line : UrlInfo urll = "/" + parts[0] + "/" + content.Url + "/"; Cannot convert string to Umbraco.Web.Routing.UrlInfo and UrlInfo says its read only.

    Any assistance in converting this to Umbraco 8 would be appreciated.

    Thank you,

    Pete

  • Marc Goodson 2126 posts 14218 karma points MVP 8x c-trib
    Sep 20, 2019 @ 17:24
    Marc Goodson
    0

    Hi Pete

    Would you return a new instance of the UrlInfo class?

    This example in the docs seems to suggest you can...?

    https://our.umbraco.com/Documentation/Reference/Routing/Request-Pipeline/outbound-pipeline-vpre81#example-1

    return new UrlInfo("/" + parts[0] + "/" + content.Url + "/", true,defaultUrlInfo.Culture);

    although the example seems to suggest that a good strategy is to inherit from the DefaultUrlProvider, and override the abstract GetUrl method...

    ... anyway I reckon you can just new up a UrlInfo class to return!

    regards

    Marc

  • Peter Laurie 42 posts 116 karma points
    Sep 22, 2019 @ 06:16
    Peter Laurie
    0

    HI, Thank you for your reply. I have implemented this, but now have come across a bigger problem. The updated code get stuck in a loop and I get a StackOverflowException, it crashes the whole application, so at the moment I don't know of a way to implement what was done on Umbraco 7 using the tutorial above.

  • Marc Goodson 2126 posts 14218 karma points MVP 8x c-trib
    Sep 22, 2019 @ 17:06
    Marc Goodson
    0

    Hi Peter

    I suspect when inside your UrlProvider, when you make the call to content.Url ... this will in fact call the registered UrlProviders in turn to determine the Url, and therefore cyclically calling the same UrlProvider over and over!

    This is where the strategy of inheriting from the DefaultUrlProvider can be advantageous, as you can call the base.GetUrl() method on the DefaultUrlProvider to get the 'Url' that 'would have been generated' had your new provider not been put in place... and that avoids cyclically calling the same provider over and over...

        public class GuideUrlProvider : DefaultUrlProvider
        {
    
                public GuideUrlProvider(IRequestHandlerSection requestSettings, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) : base(requestSettings,logger,globalSettings,siteDomainHelper)
                {   }
    
          public override IEnumerable<UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
            {
        // manipulate 'other' urls if required, these appear in backoffice in addition to the main url for the item
                  return base.GetOtherUrls(umbracoContext, id, current);
             }
    
           public override UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current)
                {
        {
         if (content != null && content.ContentType.Alias == "guideArticle"){
        // content.Level will give you the depth of the item in the tree
        // content.Path will give you ids of parent items, if this should only work in a certain section of the site (if you can avoid traversing with Parent.Parent etc this will be more efficient)
          try
          {
              // code to remove CATEGORY section of url
    
        // Can articles be created anywhere other than under Guide Index pages?
        // could content.Level or content.Path be used instead?
              //if (content.Parent.ContentType.Alias == "guidesIndex")
              {
                var url = string.Empty;
                //this will remove the category name from the guide url
        // I'm not sure of your logic here, based on your content tree.. but do what you have to do!//
    
        // but use
    
        var parentUrlInfo = base.GetUrl(umbracoContext, content,Parent, mode, culture,current);
        var defaultUrlInfo = base.GetUrl(umbracoContext, content, mode, culture,current);
    
        // and you can use IsUrl to detect whether item is published, if not published then the Url will be a text message instead of a url!
    
        if (!defaultUrlInfo.IsUrl){
        return defaultUrlInfo;
        }
    
        string defaultUrl =  defaultUrlInfo.Text;
        string parentUrl = parentUrlInfo.Text;
    
        string yourNewUrlAfterMessingAroundWithSegments;
    
        return     return new UrlInfo(yourNewUrlAfterMessingAroundWithSegments, true,defaultUrlInfo.Culture);
    
              }
            }
          }
          catch {  
        //log error? return default provider?
         return base.GetUrl(umbracoContext, content, mode, culture, current);
    }   
    
           return base.GetUrl(umbracoContext, content, mode, culture, current);
           }
        }
    

    or at least that is the gist, to avoid calling itself...

    regards

    marc

Please Sign in or register to post replies

Write your reply to:

Draft