Search form

Show or hide a menu item in Drupal 7 based on a user's location

This lesson provides a way in Drupal 7 to hide or show an individual menu item based on the location of each visitor. The use case for this example was to show a Shop menu item to visitors in New Zealand and Australia, and hide it for visitors from all other countries.

Pre-requisites

Install and configure the IP2Location module. This allows you to determine the country of a user based on their IP address. Note that this solution is dependent on accurate geolocation based on IP, which may not always be reliable.

Enable the menu item you want to show/hide. This code will decide whether or not to show the menu item to the user. It does this after the menu has been created, but before the menu is rendered. If the menu item doesn't exist, or is disabled, this code will do nothing.

Determine the path of the menu item. The code in this example uses the node path for the menu item to exclude, i.e. node/238. You can change this to whichever menu path you want.

Finally, you need to know the country code(s) of the countries that should (or shouldn't) see the menu item. This example will only show the menu item if you are in New Zealand (NZ) or Australia (AU).

Implementation

Place the following code in the template.php file for your theme, and modify the code as follows:

  • Change the function name from YOURTHEME to reflect the name of your theme.
  • Change the countries that should be able to see the menu item. This example uses NZ and AU.
  • Change the path of the menu item that will be shown/hidden. This example uses node/238 for both the check and the path of the menu item if it is shown.
  • Uncomment the watchdog lines so you can test whether the function is working for you. The watchdog items will appear in the Drupal log each time the menu is loaded. You can (and should, for performance reasons) comment the lines again once you verify the function is working.

function YOURTHEME_translated_menu_link_alter(&$item, $map) {
//	watchdog('Shop menu check', 'Working on it ...');
	$detectedcountry = module_invoke('ip2country','get_country',$_SERVER['REMOTE_ADDR']);
	$ecommerce = array(
		'NZ', 'AU'
	);

    if (($item['link_path'] == 'node/238') && (in_array($detectedcountry,$ecommerce))) {
      $item['access'] = TRUE;
      $item['href'] = 'node/238';
//	  watchdog('Shop menu check', 'Showing menu.' . ' Visitor Country: ' . $detectedcountry . ', IP address ' . $_SERVER['REMOTE_ADDR']);
    }
    else {
      $item['access'] = FALSE;      
//	  watchdog('Shop menu check', 'Not showing menu.' . ' Visitor Country: ' . $detectedcountry . ', IP address ' . $_SERVER['REMOTE_ADDR']);
    }
}

Testing

  • The first thing you need to do when implementing this function in your theme is to check that this code didn't break your menu (and therefore, potentially, your site).
  • Then, check your logs to see if the watchdog entries have appeared. You'll need to uncomment the watchdog entries in the code first. Don't forget to comment them again after testing or the logs will fill up with these entries.
  • Verify that the watchdog entries are correct, and that the menu item is shown or hidden correctly based on where you are located.
  • Find a way to load the site for a country where the menu item should be hidden.
    • I used Google's Pagespeed Insights tool. This allowed me to load the site from the US, which is not one of the allowed countries in my function.
    • You may need to find another method if you wish to test the code from outside the US.
    • I initially tried to use some of the responsive design preview sites but found that they would not load - the sites I tested tried to load the site in an iframe, which recent versions of Drupal do not allow.

Extensions

Ways you might extend this function include:

  • Limit the function to a specific menu. This code will apply to the "Shop" menu item on my site regardless of which menu the link is found in. The original example I based this on (see link above) explicitly limited the function to the main-menu.
  • Add additional checks before deciding whether to show the code. The example this code was based on used the menu link title rather than the node path. You can find the original code sample here (with my thanks to the author). In my case, I knew that the menu link title might change in the future but the node path would not. Note that the original code was written to be used in a standalone module. I found that the geolocation didn't work until I added it as a theme function rather than as a module.

Potential problems

If you don't get the results you expect, check your caching. Boost will cause problems even if you disable it. When you disable Boost, the cache folder remains, so cached pages are still being served to anonymous users. The solution is to disable Boost and delete the cache folder for your site (normally found in <yoursite>/normal/cache/<sitefolder>).

Comments and questions

The code in this example is based on real working code on a live site. However, it may not work in all cases. Feel free to post questions you may have below and I will do what I can to help.

Our Comment Policy.

We welcome your comments and questions about this lesson. We don't welcome spam. Our readers get a lot of value out of the comments and answers on our lessons and spam hurts that experience. Our spam filter is pretty good at stopping bots from posting spam, and our admins are quick to delete spam that does get through. We know that bots don't read messages like this, but there are people out there who manually post spam. I repeat - we delete all spam, and if we see repeated posts from a given IP address, we'll block the IP address. So don't waste your time, or ours.

Add a comment to this lesson