Copied to clipboard

Flag this post as spam?

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


  • Riccardo De Mace 9 posts 109 karma points
    Sep 13, 2022 @ 14:49
    Riccardo De Mace
    0

    Content Node URL customization

    Hi,

    I'm having trouble finding a way to update the url of the the node content. enter image description here

    This is the structure.

    If i go to an article ( articolo ) the route results in www.domain.com/articolo1_test

    But i want to appear the father of the node, before the name of it.

    Like: www.domain.com/articoli/articolo1_test

    I already tried to use umbracoUrlName and umbracoUrlAlias, but the shorter url still works, i want to override it.

    Is it possible?

    Thank you in advance.

  • Marc Goodson 2155 posts 14408 karma points MVP 9x c-trib
    Sep 13, 2022 @ 19:25
    Marc Goodson
    101

    Hi Riccardo

    Have a look at the documentation around custom UrlProviders

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

    And custom ContentFinders

    https://our.umbraco.com/Documentation/Reference/Routing/Request-Pipeline/IContentFinder

    Essentially when the Url is generated for a content in the backoffice then a series of UrlProviders attempt to take responsibility for generating a Url, the first one in the queue to do so wins... Therefore you can create your own provider that acts only on your article doc type and works your custom maguc for generating the alternative url pattern...

    However when people visit this Url, umbraco won't know how to match the pattern to find the content item...

    This is where content finders come into play...

    You can create your own custom content finder that knows how to match the new url pattern to the content and add this to your sites collection of content finders...

    So the combination of the two should give you control over how urls are.generated and routed for your site...

    Regards

    Marc

  • Riccardo De Mace 9 posts 109 karma points
    Sep 14, 2022 @ 09:42
    Riccardo De Mace
    0

    First of all thank you, it works... kind of

    I created the url provider, and the content finder specific for the article entity content.

     public class ArticlePageUrlProvider : DefaultUrlProvider
    {
        public ArticlePageUrlProvider(
            IOptionsMonitor<RequestHandlerSettings> requestSettings,
            ILogger<DefaultUrlProvider> logger,
            ISiteDomainMapper siteDomainMapper,
            IUmbracoContextAccessor umbracoContextAccessor,
            UriUtility uriUtility)
            : base(requestSettings, logger, siteDomainMapper, umbracoContextAccessor, uriUtility){}
    
        public override IEnumerable<UrlInfo> GetOtherUrls(int id, Uri current)
        {
            return base.GetOtherUrls(id, current);
        }
    
        public override UrlInfo? GetUrl(IPublishedContent content, UrlMode mode, string? culture, Uri current)
        {
            if (content is null)
            {
                return null;
            }
    
            if (content.ContentType.Alias == "articolo")
            {
                UrlInfo? defaultUrlInfo = base.GetUrl(content, mode, culture, current);
                if (defaultUrlInfo is null)
                {
                    return null;
                }
    
                if (!defaultUrlInfo.IsUrl)
                {
                    return defaultUrlInfo;
                }
                else
                {
                    var originalUrl = defaultUrlInfo.Text;
                    var customUrl = $"articoli{originalUrl}";
                    return new UrlInfo(customUrl, true, defaultUrlInfo.Culture);
                }
            }
    
            return base.GetUrl(content, mode, culture, current);
        }
    }
    

    public class ArticleContentFinder : IContentFinder
    {
        private readonly IUmbracoContextAccessor _umbracoContextAccessor;
    
        public ArticleContentFinder(IUmbracoContextAccessor umbracoContextAccessor)
        {
            _umbracoContextAccessor = umbracoContextAccessor;
        }
    
        public Task<bool> TryFindContent(IPublishedRequestBuilder contentRequest)
        {
            var path = contentRequest.Uri.GetAbsolutePathDecoded();
            if (path.StartsWith("/articoli") is false)
            {
                return Task.FromResult(false); // Not found
            }
    
            if (!_umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
            {
                return Task.FromResult(false);
            }
    
            var content = umbracoContext.Content.GetByRoute("/" + path.Split("/").Last());
    
            if (content is null)
            {
                return Task.FromResult(false);
            }
    
            contentRequest.SetPublishedContent(content);
            return Task.FromResult(true);
        }
    }
    

    And then added them to the main composer:

    public class SubscribeToContentServiceSavingComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            builder.Dashboards().Remove<ContentDashboard>();
            builder.UrlProviders().Insert<ArticlePageUrlProvider>();
            builder.ContentFinders().InsertBefore<ContentFinderByUrl, ArticleContentFinder>();
    
        }
    }
    

    And it works.

    But now in the backend, when i try to edit an article, it gives me an error:

    enter image description here

    Stacktrace:

        at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind, UriCreationOptions& creationOptions)
       at System.Uri..ctor(String uriString)
       at Umbraco.Extensions.UriExtensions.MakeAbsolute(Uri uri, Uri baseUri)
       at Umbraco.Extensions.UrlProviderExtensions.DetectCollisionAsync(ILogger logger, IContent content, String url, String culture, IUmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, IVariationContextAccessor variationContextAccessor, UriUtility uriUtility)
       at Umbraco.Extensions.UrlProviderExtensions.GetContentUrlsByCultureAsync(IContent content, IEnumerable`1 cultures, IPublishedRouter publishedRouter, IUmbracoContext umbracoContext, IContentService contentService, ILocalizedTextService textService, IVariationContextAccessor variationContextAccessor, ILogger logger, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider)
       at Umbraco.Extensions.UrlProviderExtensions.GetContentUrlsAsync(IContent content, IPublishedRouter publishedRouter, IUmbracoContext umbracoContext, ILocalizationService localizationService, ILocalizedTextService textService, IContentService contentService, IVariationContextAccessor variationContextAccessor, ILogger`1 logger, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider)
       at Umbraco.Cms.Web.BackOffice.Mapping.ContentMapDefinition.GetUrls(IContent source)
       at Umbraco.Cms.Web.BackOffice.Mapping.ContentMapDefinition.Map[TVariant](IContent source, ContentItemDisplay`1 target, MapperContext context)
       at Umbraco.Cms.Core.Mapping.UmbracoMapper.Map[TTarget](Object source, Type sourceType, MapperContext context)
       at Umbraco.Cms.Core.Mapping.UmbracoMapper.Map[TTarget](Object source, MapperContext context)
       at Umbraco.Cms.Web.BackOffice.Controllers.ContentController.MapToDisplayWithSchedule(IContent content)
       at Umbraco.Cms.Web.BackOffice.Controllers.ContentController.GetById(Int32 id)
       at lambda_method623(Closure , Object , Object[] )
       at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
    

    It does this only on article content. I don't know what to do.

    Thank you in advance.

  • Riccardo De Mace 9 posts 109 karma points
    Sep 14, 2022 @ 14:16
    Riccardo De Mace
    0

    enter image description here

    For now, i resolved the problem like this...

    I don't think it's the best way to do it, but it works.

    I would love to hear if there's a better way.

    Thank you in advance.

  • Simon Dingley 1474 posts 3431 karma points c-trib
    Aug 07, 2024 @ 12:45
    Simon Dingley
    0

    I am guessing you already resolved this however for anyone else that stumbles across this I believe the problem lies here:

    var originalUrl = defaultUrlInfo.Text;
    var customUrl = $"articoli{originalUrl}";
    return new UrlInfo(customUrl, true, defaultUrlInfo.Culture);
    

    You are creating a UrlInfo object with a relative url - there is no hostname or base url for it to be relative to and so it can't make an absolute url using it.

Please Sign in or register to post replies

Write your reply to:

Draft