The country filter lists lots of countries which have no entries. For usability issues I just want to show the countries which are used. At the moment I use 5 countries, but there could be more. I don’t want to manually edit the location.inc because the countries will also disappear from the node edit forms.

Comments

ambientdrup’s picture

I'd like to be able to do this as well. Any suggestions?

-backdrifting

Wolfgang Reszel’s picture

Well, I did a quick hack.

At "location_handler_filter_location_country.inc" and "location_views_handler_filter_proximity.inc" I changed:

$options = location_get_iso3166_list();
to
$options = location_get_iso3166_list(false, true);

At "location.inc" I changed:

function location_get_iso3166_list($upper = FALSE) {
to
function location_get_iso3166_list($upper = FALSE, $filter = FALSE) {

and I added after "natcasesort($countries);" the following code block:

  // If $filter is true then only output the countries which are used
  if ($filter) {
    $newcountries = array();
    $result = db_query('SELECT country, count(country) FROM location GROUP by country ORDER BY (count(country)) DESC');
    while ( $line = db_fetch_array($result) ) {
      $newcountries[$line[country]] = $countries[$line[country]].' ('.$line['count(country)'].')';
    }
    $countries = $newcountries;
  }

The countries are sorted by usage and the usage is added to the name like this: Germany (16)

The SQL query does not respect revisions. So that could be a problem. I don't know how to enhance the query to only use the countries of the actual version of a node.

Wolfgang Reszel’s picture

I've found the query to respect the revisions. I also changed the sorting, so that the most used countries are on top and countries with equal usage are sorted alphabetically by the translated string.

<?php
  // If $filter is true then only output the countries which are used
  if ($filter) {
    $newcountries = array();
    $result = db_query('SELECT location.country AS country, count(location.country) AS count FROM node node  LEFT JOIN location_instance location_instance ON node.vid = location_instance.vid LEFT JOIN location location ON location_instance.lid = location.lid LEFT JOIN content_type_trader node_data_field_contact ON node.vid = node_data_field_contact.vid  WHERE (node.type = \'trader\') GROUP BY (country)');
    while ( $line = db_fetch_array($result) ) {
      $newcountries[$line[country]] = (9999999-$line[count]).'--'.$countries[$line[country]].' ('.$line[count].')';
    }
    $countries = $newcountries;
    natcasesort($countries);
    foreach ($countries as $key => $value) {
      $countries[$key] = preg_replace('/^\d+--/', '', $value);
    }
  }
?>

Remember: Change content_type_trader and node.type = \’trader\' to your content type.

ambientdrup’s picture

Issue tags: +hack

This is not working for me. I've made the code changes you suggested to both of the above files in my install but I'm still getting my old location list with all the countries.

Are you missing anything in the above instructions? In my location.inc file I see this code before the block of code you are adding (after the natcasesort($countries);

 // In fact, the ISO codes for countries are all Upper Case.
  // So, if someone needs the list as the official records,
  // it will convert.
  if (!empty($upper)) {
    return array_change_key_case($countries, CASE_UPPER);
  }
  return $countries;
}

What should I do with that?
Again, this code I've added based on your suggestion is not working.

-backdrifting

ambientdrup’s picture

Never mind - my bad. I had the code block after the existing one. I moved it so it is called first immediately after the natcasesort($countries) and it's working now.

Good work BTW - this is a nice hack. It would be nice to make this into a module vs. having to hack the location files (core). I'll see if I have time to help you out on that if you're interested in making this an official module.

-backdrifting

ambientdrup’s picture

Per your post #3 I tried adding your revised code for the sorting but it's not working - no countries show up in my drop down select now. I just see but no countries are listed.

-backdrifting

ambientdrup’s picture

How can I remove one country from my drop down list (using your above code)? I want to remove one country from the exposed view drop down entirely.

Is this possible?

-backdrifting

Wolfgang Reszel’s picture

Add this to the code:
while ( $line = db_fetch_array($result) ) {
if ($line[country] == 'fr') continue;
$newcountries[$line[country]] = (9999999-$line[...

Is it now working for you?

Wolfgang Reszel’s picture

Big sorry.

You have to modify my query from #3 for your own installation. Change content_type_trader and node.type = \'trader\' to your content type.

ambientdrup’s picture

Beautiful! Yes it works. Thanks!

-backdrifting

ambientdrup’s picture

Ok thanks. I'm going to stick with your first set of code for now - I'm not sure if I need to worry about revisions on this site but if I do I'll play with the other code you posted.

Thanks again. We should probably work to commit this to a separate module or patch to the Location module. That would be cool.

-backdrifting

ionchannels’s picture

You can add this code to template.php:

function location_form_alter(  &$form, $formState, $formId ) {
unset($form['distance']['country']['#options']);
$form['distance']['country']['#options'][ca]='Canada';
$form['distance']['country']['#options'][us]='United States';
$form['distance']['country']['#default_value']='us';
}
nyleve101’s picture

Hi Tekl,

How would the code in comment 3 be amended to apply to all content types rather than a particular type please? I'm using the views exposed filter for 3 different content types.

Thanks in advance!

Evelyn

Wolfgang Reszel’s picture

Sorry I don’t know at the moment. I’m no code and I don’t understand SQL very well. You see the hardcoded types in the query. Maybe you find a way to generalize it.

fabianx’s picture

Hi,

Here is a better SQL query:

SELECT location.country AS country, count(location.country) AS count FROM location location LEFT JOIN location_instance location_instance ON location.lid = location_instance.lid LEFT JOIN node node ON location_instance.vid = node.vid WHERE node.type in (\'type 1\', \'type 2\', \'type 3\');

If you need this to work for all content types, you can also omit the where clause; query would then be:

SELECT location.country AS country, count(location.country) AS count FROM location location LEFT JOIN location_instance location_instance ON location.lid = location_instance.lid LEFT JOIN node node ON location_instance.vid = node.vid;

Or the same in other order (more like original statement):

SELECT location.country AS country, count(location.country) AS count FROM node node LEFT JOIN location_instance location_instance ON node.vid = location_instance.vid LEFT JOIN location location ON location_instance.lid = location.lid WHERE node.type in (\'type 1\', \'type 2\');

Best Wishes,

Fabian (LionsAd)

aniebel’s picture

Without messing with the module code, how would this be implemented in the theme?

vosechu’s picture

I know this isn't a direct response but you may find that this issue is very close to what you're working on. If nothing, it may give you the framework to build a patch that would be accepted into the module: #467412: Cannot Select Particular Countries for Views Location Filter

fabianx’s picture

There is also Views Hacks Module doing almost the same, but without showing the node count.

See: http://drupal.org/project/views_hacks

(Interesting for better user experience could also be: http://drupal.org/project/better_exposed_filters)

Best Wishes,

Fabian

squarecandy’s picture

subscribe

evucan’s picture

Hi & Thank you, I really found this useful.

I also wanted to strip the provinces down to the only ones that are actually being used for filtered searches. This is what I did:

At "location.inc" I changed:

function location_get_provinces($country = 'us') {
to
function location_get_provinces($country = 'us', $filter=false) {

At "location.module" I changed:

drupal_json(location_get_provinces($country));
to
drupal_json(location_get_provinces($country, true));

and in "location.inc" again I changed the location_get_provinces function to:

// @@@ New in 3.x, document.
/**
 * Fetch the provinces for a country.
 */
function location_get_provinces($country = 'us', $filter=false) {  // Filtered province hack: $filter is a new parameter

	// Filtered province hack - NEW CODE BLOCK - 1 BEGIN
	if ($filter==true){  //  We need to keep a seaprate cache depending if the province list is based on a filter or not.
		$cacheprefix="filtered_";
	}else{
		$cacheprefix="";
	}
	// Filtered province hack - NEW CODE BLOCK - 1 END

	static $provinces = array();
	location_standardize_country_code($country);
	if (isset($provinces[$country])) {
		return $provinces[$country];
	}
	if ($cache = cache_get($cacheprefix."provinces:$country", 'cache_location')) { // Filtered province hack: $filter is a new variable
		$provinces[$country] = $cache->data;
		return $provinces[$country];
	}
	
	location_load_country($country);
	$func = 'location_province_list_'. $country;
	
	if (function_exists($func)) {
	  	$provinces[$country] = $func();
			
		// Filtered province hack - NEW CODE BLOCK - 2 BEGIN
		if ($filter==true){
		   $arr_used_province = array();
		   $result = db_query('SELECT province, count(province) FROM location GROUP by province ORDER BY (count(province)) DESC');
		   while ( $line = db_fetch_array($result) ) {
			$arr_used_province[]= $line[province];
		   }
		
		foreach ($provinces[$country] as $key => $orig_province) { 
		   		if (in_array($key, $arr_used_province)) {
			}else{
				unset($provinces[$country][$key]); 
			}
		}	
	       // Filtered province hack - NEW CODE BLOCK - 2 BEGIN

	}		
	
	
	  cache_set($cacheprefix."provinces:$country", $provinces[$country], 'cache_location');  // Filtered province hack: $filter is a new variable

	  return $provinces[$country];
	}
	return array();
}

Hope someone finds this useful.

evucan’s picture

Hi & Thank you, I really found this useful (in response 20 post #2).

I also wanted to strip the provinces down to the only ones that are actually being used for filtered searches. This is what I did:

At "location.inc" I changed:

function location_get_provinces($country = 'us') {
to
function location_get_provinces($country = 'us', $filter=false) {

At "location.module" I changed:

drupal_json(location_get_provinces($country));
to
drupal_json(location_get_provinces($country, true));

and in "location.inc" again I changed the location_get_provinces function to:

// @@@ New in 3.x, document.
/**
 * Fetch the provinces for a country.
 */
function location_get_provinces($country = 'us', $filter=false) {  // Filtered province hack: $filter is a new parameter

	// Filtered province hack - NEW CODE BLOCK - 1 BEGIN
	if ($filter==true){  //  We need to keep a seaprate cache depending if the province list is based on a filter or not.
		$cacheprefix="filtered_";
	}else{
		$cacheprefix="";
	}
	// Filtered province hack - NEW CODE BLOCK - 1 END

	static $provinces = array();
	location_standardize_country_code($country);
	if (isset($provinces[$country])) {
		return $provinces[$country];
	}
	if ($cache = cache_get($cacheprefix."provinces:$country", 'cache_location')) { // Filtered province hack: $filter is a new variable
		$provinces[$country] = $cache->data;
		return $provinces[$country];
	}
	
	location_load_country($country);
	$func = 'location_province_list_'. $country;
	
	if (function_exists($func)) {
	  	$provinces[$country] = $func();
			
		// Filtered province hack - NEW CODE BLOCK - 2 BEGIN
		if ($filter==true){
		   $arr_used_province = array();
		   $result = db_query('SELECT province, count(province) FROM location GROUP by province ORDER BY (count(province)) DESC');
		   while ( $line = db_fetch_array($result) ) {
			$arr_used_province[]= $line[province];
		   }
		
		foreach ($provinces[$country] as $key => $orig_province) { 
		   		if (in_array($key, $arr_used_province)) {
			}else{
				unset($provinces[$country][$key]); 
			}
		}	
	       // Filtered province hack - NEW CODE BLOCK - 2 BEGIN

	}		
	
	
	  cache_set($cacheprefix."provinces:$country", $provinces[$country], 'cache_location');  // Filtered province hack: $filter is a new variable

	  return $provinces[$country];
	}
	return array();
}

Hope someone finds this useful.

Tino’s picture

function location_form_alter(  &$form, $formState, $formId ) {
unset($form['distance']['country']['#options']);
$form['distance']['country']['#options']['ca']='Canada';
$form['distance']['country']['#options']['us']='United States';
$form['distance']['country']['#default_value']='us';
}

Thanks for this workaround within theme's template.php.
It's a convenient solution, but it only works (D7) when quotes are added to ['us'] and ['ca'].
But as soon as I check "Cache pages for anonymous users" on Administration » Configuration » Development, this workaround does not work anymore. :(

dbassendine’s picture

@tino Thanks for the suggestion here. hook_form_alter is not generally used in template.php so it may work better for you if you set up the hook in a custom module, check $form_id is "views_exposed_form", then alter the form as you have above.

Overall, this is a problem or limitation to the Distance / Proximity filter I'd like to see fixed in the Location module itself, rather than using the workarounds above. There seem to be two approaches to the problem:

  1. Check all the locations and only show countries where nodes / users are present
  2. Allow the admin to configure the countries that can be selected manually (as in the similar ticket for the single Country filter (http://drupal.org/node/467412)

The first could add performance issues, but I can see use cases for both. What are other peoples' thoughts?

Cheers, David

Tino’s picture

@dbassendine: I'd be happy with both solutions.

I have currently hidden the country field through css since there are only two countries in use (The Netherlands & Belgium). It automatically seems to recognize how to calculate from user's input. Anyway, the results are always OK.

.form-item-distance-country {
    display: none;
}
ankur’s picture

Status: Active » Closed (fixed)

I second the advice that the implementation of hook_form_alter() be placed in a custom module rather than your template.php file.

roknrod12’s picture

Subscribing. I, too, would like to see this implemented w/o having to hack core files.

thanks,
Rodney

langweer’s picture

Yes, limiting the list of countries would be very helpful!

pdelorme’s picture

Here is a solution for D7 based on solution #2

In "modules/location/handlers/location_handler_filter_location_country.inc" :
replace
$options = location_get_iso3166_list();
by
$options = location_get_iso3166_list(false, true);

In "modules/location/location.inc" :
Replace
function location_get_iso3166_list($upper = FALSE) {
by
function location_get_iso3166_list($upper = FALSE, $filter = FALSE) {

and I added after just before the final return of location_get_iso3166_list() add the folowing :

  // If $filter is true then only output the countries which are used
  if ($filter) {
  	$newcountries = array();
  	$result = db_query('SELECT country, count(country) as count FROM {location} GROUP by country ORDER BY (count(country)) DESC')->fetchAllAssoc("country",PDO::FETCH_ASSOC);
  	foreach ($result as $code => $row) {
  		$newcountries[$code] = $countries[$code].' ('.$row['count'].')';
  	}
  	$countries = $newcountries;
  }

Enjoy !