Copied to clipboard

Flag this post as spam?

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


  • Craig100 1136 posts 2523 karma points c-trib
    Jan 19, 2015 @ 20:28
    Craig100
    0

    GeoIP Detected Redirection

    In other non-Umbraco projects I've used the Geo2Lite DB from MaxMind to provide rough indication and redirection based on a user's IP address. I've been asked for the same for a multi-lingual Umbraco MVC (V7.2.1) site and am wondering how to approach it. Should this sit outside Umbraco or is there a way of hijacking the Umbraco routing to determine the results of a DB query and forward accordingly? Just looking for ideas.

    Cheers,

    Craig

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Jan 19, 2015 @ 21:38
    Nicholas Westby
    1

    I've done that a couple times. First, get the data into a database (the Umbraco database or another). I use this: https://github.com/rhythmagency/rhythm.import-ip-location-data

    You'll have to modify that slightly depending on your needs (e.g., get state or country based on IP rather than coordinates).

    Then, in an action method or master view, you can query the database with the request IP address. If you are in an action method, you can return a redirect. If you are in a master view, you can call Response.Redirect.

  • Craig100 1136 posts 2523 karma points c-trib
    Jan 30, 2015 @ 00:06
    Craig100
    0

    Thanks,

    I've installed MaxMind's Nuget Package that installed MaxMind.Db and MaxMind.GeoIP2.

    Where do you put this action method so it gets hit at the first request? This is MVC V7.2.2. I'm guessing it's going to have to be the default action method for the whole site so it works in all cases. But I don't see a controller anywhere so I'm guessing it's in code and I'll have to extend an existing one, but which one?

    I've read http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers but it doesn't seem to apply to V7+

    Any advice appreciated.

    Craig

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Jan 30, 2015 @ 03:32
    Nicholas Westby
    0

    You can set the default controller with the ApplicationStarting event: http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers#Changethedefaultcontroller

    I see that you mentioned that page doesn't "seem" to apply to V7+. Just because it doesn't explicitly say it applies does not mean it does not apply. I use ApplicationStarting in my Umbraco 7 sites (and there should be no issue using a default controller in Umbraco 7): https://github.com/rhythmagency/rhythm.umbraco.extensions/blob/master/trunk/Rhythm.Extensions/Rhythm.Extensions/Events/RhythmEventHandler.cs#L79

  • Craig100 1136 posts 2523 karma points c-trib
    Jan 30, 2015 @ 16:53
    Craig100
    0

    This is what's slightly confusing, not sure if it's a language issue, but the Global.asax events I'm familiar with don't include "ApplicationStarting" unless the article means "Application_Start".  In any event, wouldn't GeoIP Redirection best be handles by "Application_BeginRequest"? Otherwise, once the application has been started by User A, User B from another GeoIP range won't be redirected.

    Just a thought

    Craig

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Jan 31, 2015 @ 05:36
    Nicholas Westby
    0

    I think I understand your confusion. To clarify, you won't actually be looking at IP addresses in ApplicationStarting. Also, ApplicationStarting is is not an ASP.NET concept like Application_Start. It's an Umbraco concept. Notice that it is inside of a class that inherits from ApplicationEventHandler. That is also an Umbraco concept. Umbraco scans for all classes inheriting from ApplicationEventHandler and allows them to handle events. One of those events is ApplicationStarting (which is called once when the application starts).

    The old way of doing that was to inherit from UmbracoApplication and set your global.asax to use that class. However, that is no longer recommended or necessary (in most cases).

    Notice that the code is actually setting up a default controller (though it doesn't show the implementation of that controller). The idea is that that controller would have an action method, and that action method would return either the current Umbraco page, or a redirect to the current Umbraco page with the appropriate URL (based on the user's IP address).

    So, it is not the ApplicationStarting that would handle the redirection. It is the action method in the default controller setup by the ApplicationStarting event that would do the redirection.

    Keep in mind that none of that is really necessary. You could do all of this in your primary cshtml file (i.e., by doing a Response.Redirect). Though, the more MVC pattern-adhering technique would be to use a controller.

  • Craig100 1136 posts 2523 karma points c-trib
    Jan 31, 2015 @ 11:48
    Craig100
    0

    Great explanation, thanks Nicholas.

    By doing it in the "primary cshtml file", I guess that would only redirect when navigating to the home page? I need to read up some more on .net mvc C# before I do this. I dip in and out of loads of web stuff so not always at the top of the game on any given area :)  This looks interesting though: http://lukencode.com/2010/05/19/ip-to-geo-location-in-asp-net-mvc/

    Craig

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Feb 01, 2015 @ 01:19
    Nicholas Westby
    0

    When I say "primary cshtml file", I assume all of your cshtml file are inheriting from a primary one (similar to how masterpages worked in webforms). This would mean that your homepage, and all your child pages have a single "primary cshtml file".

    So, to answer your question, you would NOT only redirect when navigating to the homepage. You would redirect for every page, and that redirect would be handled in a single location.

  • Craig100 1136 posts 2523 karma points c-trib
    Feb 01, 2015 @ 15:25
    Craig100
    0

    Getting there slowly now and have rising confidence that what I need to do can be done. Have the MaxMind look up working and cookie setting and reading.

    So, I've created a new controller which, for now, redirects to one of the language instances, hard coded in:-

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using Umbraco.Core;
    using Umbraco.Web.Mvc;
    using TestProject.classLib;
    
    namespace TestProject.Controllers
    { public class CustomApplicationEventHandler : ApplicationEventHandler { protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { DefaultRenderMvcControllerResolver.Current.SetDefaultControllerType(typeof(IPRedirectController)); base.ApplicationStarting(umbracoApplication, applicationContext); } } public class IPRedirectController : RenderMvcController { public ActionResult Index() { // Check for cookie Cookies cookie = new Cookies(); if(cookie.ReadCookie()!=null) { // If cookie exists, look up appropriate site for country and redirect } else { // Or get the user's country IPRedirect getCountry = new IPRedirect(); string country = getCountry.GetUsersCountry(); // Look up the appropriate site for the user's country and redirect } string returnUrl = "http://localhost:50383/de-de"; return this.Redirect(returnUrl); } } }

    Now, not sure if this is an Umbraco or MVC thing, but it is now in a permanent redirect loop. How do I get it to do this redirection to the original Umbraco controller that I just replaced. I suspect I'm doing this either in the wrong place or maybe I should be pointing at a node rather than a url? Either way, I don't want to miss any of the functionality of the original Umbraco controller.

    Advice appreciated.

    Craig

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Feb 01, 2015 @ 21:51
    Nicholas Westby
    0

    You'll want to check if you've arrived at an internationalized page (e.g., one containing "/de-de" in the URL). If so, you'll want to defer to the default behavior by returning this:

    return base.Index(model);
    

    Because you're passing a model to the base action method, you'll need that model, which you can get by changing your action method signature to accept a RenderModel:

    public ActionResult Index(RenderModel model)
    

    There are a few other things you can return, such as CurrentTemplate, which you can read about here (same page previously mentioned): http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers

    By the way, it's up to you to figure out a way to determine if the URL has been internationalized. I usually rewrite URL's on my multilingual sites, so there is typically a query string I can check to see if the language has been set.

  • Craig100 1136 posts 2523 karma points c-trib
    Feb 02, 2015 @ 15:38
    Craig100
    0

    Thanks again.  I've stopped the looping with 

               string returnUrl = "http://localhost:50383/en-US";
    
                //checked if we're on required site
                if(HttpContext.Request.Url.ToString() == returnUrl) {
                    return base.Index(model);
                } else {
                    return this.Redirect(returnUrl);
                }

    However, I'm not happy with the whole idea as the requirement is to have loads of countries that point to just a few languages.

    So I commented out the code about and tried adding this, hoping it would work:-

                Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("de-DE");
    
                return base.Index(model);

    But it doesn't. How does Umbraco work out what site to hit given the language?

    Craig

     

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Feb 02, 2015 @ 16:16
    Nicholas Westby
    0

    I'm not happy with the whole idea as the requirement is to have loads of countries that point to just a few languages

    Not sure what you mean by that. What aren't you happy with?

    How does Umbraco work out what site to hit given the language?

    Not sure what you mean. Can you expand on that? There are tons of different ways to implement multilingual sites in Umbraco, so assuming you are talking about that, I'd need to know about what approach you are taking.

  • Craig100 1136 posts 2523 karma points c-trib
    Feb 02, 2015 @ 16:34
    Craig100
    0

    lol, indeed. I meant I wasn't happy with the approach I've been taking up to now, given the final problem I have to solve.  

    I've yet to see a single implementation of GeoIP Redirection in detail, that other solutions can be based on.  I'm trying to work out how to do the redirection bit. I have the users country based on IP address and I can look up which site (/en-GB/, /de-DE/, etc.) it should point to, but then what? Redirecting to another explicit URL doesn't seem to be right as it will always point to the home page.  All my (6) sites have languages and cultures set for them so I was kind of guessing that might be what triggered the redirection. But apparently not.

    Craig

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Feb 02, 2015 @ 17:29
    Nicholas Westby
    0

    Still not sure what you mean and I'm not sure why you'd point to the home page when you redirect. What I do is ascertain the appropriate culture and redirect to the current page with that culture. So, if a user ends up going to:

    site.com/path/page-1

    I'd redirect them to:

    site.com/en-us/path/page-1

    Which in the backend rewrites to the server as (the user never sees this URL):

    site.com/path/page-1?lang=en-us

    Not sure if that helps you, but then I'm not quite sure what issue you're having.

  • Craig100 1136 posts 2523 karma points c-trib
    Feb 02, 2015 @ 17:50
    Craig100
    0

    Ok, my issue is that my client only wants to translate into 10 languages but wants to trade in 40 countries. They want anyone arriving at the site to be greeted with their own language if possible. So all 40 countries are mapped to 1 of 10 languages.  The language switcher will be a seperate page, because there's too many to show in a dropdown.

    So far I can get their country from the IP Address. Haven't worked out yet how I'm going to hold the mapping. For an Archetype solution I developed a custom dropdown json datatype with nuPickers for the countries, a dropdown for the destination sites and a checkbox for whether ecommerce should be available.   Archetype can't show anything from my custom datatype, so that's no use, as I'd have 40-odd items with no title, unless it's typed into a text field. 

    I also don't really like the fact that a lookup will have to be done for every request to the site, which makes it unscaleable with a Varnish server in my view. I'm just ignoring that for now.

    It's being a difficult nut to crack and taking ages. Not least because I'm fairly new to C# and MVC. There doesn't seem to be any "detailed" instructions on how to go about redirection in Umbraco at this level. I've read the links you sent and many, many others (most for old versions) but am not much nearer a solution.

     

  • Nicholas Westby 2054 posts 7103 karma points c-trib
    Feb 02, 2015 @ 18:24
    Nicholas Westby
    0

    I don't see any issues there that can't be solved pretty easily. Let me know what you think of my solutions below.

    Ok, my issue is that my client only wants to translate into 10 languages but wants to trade in 40 countries. They want anyone arriving at the site to be greeted with their own language if possible. So all 40 countries are mapped to 1 of 10 languages. The language switcher will be a seperate page, because there's too many to show in a dropdown.

    Not an issue (more of a requirement), so I'll skip this.

    Haven't worked out yet how I'm going to hold the mapping.

    Use Archetype (which you are doing so far it sounds like). Lookup once then cache to a static variable.

    For an Archetype solution I developed a custom dropdown json datatype with nuPickers for the countries

    Not sure why you need nuPickers. Can't you just use a normal dropdown?

    Archetype can't show anything from my custom datatype, so that's no use, as I'd have 40-odd items with no title, unless it's typed into a text field.

    I have run into this before. You can add a text field that is used explicitly to label the Archetype row.

    I also don't really like the fact that a lookup will have to be done for every request to the site, which makes it unscaleable with a Varnish server

    You don't have to do a lookup for every request. Just the first request per user, then use a cache. I don't know much about Varnish, but if it's a proxy server I am guessing it can look at a cookie to decide whether to use its cache (and you could set that cookie after the culture has been set). I'd need more details before I could advise further.

    It's being a difficult nut to crack and taking ages. Not least because I'm fairly new to C# and MVC. There doesn't seem to be any "detailed" instructions on how to go about redirection in Umbraco at this level. I've read the links you sent and many, many others (most for old versions) but am not much nearer a solution.

    Let us know if you have any specific questions and perhaps we can help.

  • Craig100 1136 posts 2523 karma points c-trib
    Feb 02, 2015 @ 19:20
    Craig100
    0

    Thanks for the above, it will be helpful.

    Hope don't need to ask for a quote for a package, but I might yet given the deadline, lol.

    Craig

Please Sign in or register to post replies

Write your reply to:

Draft