Copied to clipboard

Flag this post as spam?

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


  • Michaël Vanbrabandt 863 posts 3348 karma points c-trib
    Oct 31, 2016 @ 14:05
    Michaël Vanbrabandt
    0

    Url tracker issue with custom UrlProvider

    Hi all,

    I am using the UrlProvider together with the IContentFinder to setup a custom url for my project.

    So I have a structure like:

    Home
        Categories
            Categorie-1
                Sub-categorie-1
                Sub-categorie-2
            Categorie-2
                Sub-categorie-3
                Sub-categorie-4
    

    Using the UrlProvider and ContentFinder I now have an url /categorie-1/ and /sub-categorie-1/ which then shows the correct template.

    But now with Url tracker enabled, when I change categorie Categorie-1 to Categorie-6 it adds the full orignal url eg /categories/categorie-1/ in the url tracker to be redirected and not the /categorie-1/.

    Can I solve this?

    /Michaël

  • Jason Vickers 21 posts 115 karma points
    Nov 03, 2016 @ 00:33
    Jason Vickers
    0

    In my experience with the CustomUrlProvider, that just tells Umbraco what the URL should be; it is still saving the full url for the node. One option would be to not only use the Provider, but also save the URL in the property umbracoUrlAlias, which will override the default URL generated by Umbraco. Not sure if URL Tracker supports this, but it would be my next step in troubleshooting.

  • Micha Somers 134 posts 597 karma points
    Nov 03, 2016 @ 21:21
    Micha Somers
    1

    umbracoUrlAlias can be very useful, but keep in mind that it adds an alias.

    Since the original url also remains valid, Umbraco won't add a redirect to the Redirect URL Management.

    >

    Concerning the contentprovider/urlprovider behaviour for registering redirects for old urls:

    I am not sure if it can be classified as a bug. But Umbraco just doesn't use the information provided by the urlprovider which sounds rather strange and restricting.

    For the current (=old) url, Umbraco is using GetRouteById for the descendants of the content entity (in RedirectTrackingEventHandler.cs, ContentService_Publishing) to get the orignal route:

    foreach (var x in entityContent.DescendantsOrSelf())
    {
      var route = contentCache.GetRouteById(x.Id);
      if (IsNotRoute(route)) continue;
      var wk = UnwrapToKey(x);
      if (wk == null) continue;
      OldRoutes[x.Id] = Tuple.Create(wk.Key, route);
    }
    

    This way, it generates a route that completely ignores the urlproviders url.

    There's probably more needed to deal with all possible situations, but at this moment it is not clear to me why the code for the ContentService_Publishing method does not simply use the urlprovider (as in the GetContentUrls method in UrlProviderExtensions.cs)?

    Hopefully someone of the Umbraco dev team can give some insight in the reason why it is implemented this way. Is there a neat way to override this behaviour (eg. by using a custom UrlSegmentProvider)?

    It would also be interesting to know if something like:

    // Find route using the urlprovider
    string url;
    bool routeFound = false;
    var urlProvider = UmbracoContext.Current.RoutingContext.UrlProvider; 
    try
    {
        url = urlProvider.GetUrl(entityContent.Id).TrimEnd(new[] { '/' });
        if (!IsNotRoute(url))
        {
            var wk = UnwrapToKey(entityContent);
            if (wk != null)
            {
                OldRoutes[entityContent.Id] = Tuple.Create(wk.Key, url);
                routeFound = true;
            }
        }
    }
    catch
    {
        //
    }
    if (!routeFound)
    { 
    
        // the original code continues here ...
        foreach (var x in entityContent.DescendantsOrSelf())
        {
        ..
        }
    
    } 
    

    could work for ContentService_Publishing?

    Out of curiousity (and certainly not for production), I made a quick and dirty change in the Umbraco source code using this example to see what happens. For a local test situation with a similar Company urlprovider Michaël is using, it works fine. Changing a Company node, gives the right redirect path.

  • Michaël Vanbrabandt 863 posts 3348 karma points c-trib
    Nov 04, 2016 @ 07:25
    Michaël Vanbrabandt
    1

    Hi Micha,

    thank you for pointing this out!

    What I had in mind was:

    1. Add new url to the umbracoUrlAlias ( just need to find a way to do this automatically, maybe hook in into the content save and publish event and then copy this new url to here... )
    2. Add url redirect with regex in IIS to do the redirect from the current ( old ) url to the new one ( won't be that hard because the structure is always the same )

    But like you are mentioning, it would be nicer that Umbraco uses the provided custom UrlProvider to get the correct url for a node.

    /Michaël

  • Micha Somers 134 posts 597 karma points
    Nov 04, 2016 @ 08:58
    Micha Somers
    1

    Hi Michaël,

    Yes, I think that might work.

    If you are going to hook into the save and publish event, you could (instead of the two steps you mentioned) try to add your own redirects just like the CreateRedirect method in RedirectTrackingEventHandler.cs.

    private static void CreateRedirect(int contentId, Guid contentKey, string oldRoute)
            {
    
                var contentCache = GetPublishedCache();
                if (contentCache == null) return;
    
                var newRoute = contentCache.GetRouteById(contentId);
                if (IsNotRoute(newRoute) || oldRoute == newRoute) return;
                var redirectUrlService = ApplicationContext.Current.Services.RedirectUrlService;
                redirectUrlService.Register(oldRoute, contentKey);
            }
    

    Have not tried it myself yet, but if you use the urlProvider.GetUrl and assign it to oldRoute in that code, I think it is worth trying.

  • Michaël Vanbrabandt 863 posts 3348 karma points c-trib
    Nov 04, 2016 @ 09:19
    Michaël Vanbrabandt
    0

    Hi Micha,

    nice one!

    I will test this out and let you know what the results are.

    Wonderfull to have such a nice community where you learn more and more each day!

    Kind regards

    /Michaël

  • Micha Somers 134 posts 597 karma points
    Nov 04, 2016 @ 20:17
    Micha Somers
    0

    Hi Michaël,

    With some help from the original Umbraco source code (RedirectTrackingEventHandler.cs), the following code will give a nice start:

    public class CustomEventHandler : ApplicationEventHandler
    {
        private static IPublishedContentWithKey UnwrapToKey(IPublishedContent content)
        {
            if (content == null) return null;
            var withKey = content as IPublishedContentWithKey;
            if (withKey != null) return withKey;
    
            var extended = content as PublishedContentExtended;
            while (extended != null)
                extended = (content = extended.Unwrap()) as PublishedContentExtended;
    
            withKey = content as IPublishedContentWithKey;
            return withKey;
        }
    
        protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            ContentService.Saving += ContentService_Saving;
    
            // Use custom Url provider
            UrlProviderResolver.Current.InsertTypeBefore<DefaultUrlProvider, CompanyUrlProvider>();
    
            // Insert custom finder before ContentFinderByNiceUrl or ContentFinderByNotFoundHandlers
            ContentFinderResolver.Current.InsertTypeBefore<ContentFinderByNiceUrl, CompanyContentFinder>();
        }
    
        private void ContentService_Saving(IContentService sender, SaveEventArgs<IContent> e)
        {
            // Register redirections using UrlProvider
            var contentCache = UmbracoContext.Current.ContentCache;
            var urlProvider = UmbracoContext.Current.RoutingContext.UrlProvider;
            var redirectUrlService = ApplicationContext.Current.Services.RedirectUrlService;
            if (contentCache != null && urlProvider != null && redirectUrlService != null)
            {
                foreach (IContent entity in e.SavedEntities)
                {
                    var oldEntity = ApplicationContext.Current.Services.ContentService.GetById(entity.Id);
                    if (oldEntity == null) continue;
                    var oldSegment = oldEntity.GetUrlSegment();
                    var newSegment = entity.GetUrlSegment();
                    if (oldSegment != newSegment)
                    {
                        // Segment changed; redirection needed.
                        var entityContent = contentCache.GetById(entity.Id);
                        // Get the old url via the UrlProvider
                        var oldRoute = urlProvider.GetUrl(entityContent.Id).TrimEnd(new[] { '/' });
                        var wk = UnwrapToKey(entityContent);
                        if (wk == null) continue;
    
                        // Register the oldroute
                        redirectUrlService.Register(oldRoute, wk.Key);
                    }
                }
            }
            //else
            //{
            //    If no contentCache, urlProvider or redirectUrlService available, 
            //    leave it up to the default redirection handled by the RedirectTrackingEventhandler
            //}
    
            //Clear the content finder cache.
            HttpContext.Current.Cache.Remove("CachedCompanyNodes");
        }
    }
    

    Changing a published Company node (under a Companies node) with the name "Company X" to "Company X1" will register the following redirects:

    • /company-x/ ==> /company-x1/
    • /companies/company-x/ ==> /company-x1/

    The code needs some refinement and further testing, but the idea to register redirects using the urlProvider looks already pretty good.

    Note that with this approach, the default redirection will still be executed and will result in an extra (but not completely right) redirect item in the list. Although the original url of that item is not comform the url provided by the custom urlProvider, it might actually be very handy to have such a redirection as a bonus ;-)

    If anyone has ideas to improve it ... please do not hesitate to share your ideas and code!

    /Micha

Please Sign in or register to post replies

Write your reply to:

Draft