/[drupal]/contributions/modules/country_code/country_code.module
ViewVC logotype

Contents of /contributions/modules/country_code/country_code.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.96 - (show annotations) (download) (as text)
Wed Dec 10 13:46:50 2008 UTC (11 months, 2 weeks ago) by catch
Branch: MAIN
CVS Tags: HEAD
Changes since 1.95: +5 -7 lines
File MIME type: text/x-php
#328194 by recidive: use lowercase country codes.
1 <?php
2 // $Id: country_code.module,v 1.95 2008/12/10 12:22:01 catch Exp $
3
4 /**
5 * @file
6 * Enables location-appropriate path and content handling based on the user's IP address.
7 */
8
9 /**
10 * Custom language_negotiation option for country-based language negotiation.
11 */
12 define('LANGUAGE_NEGOTIATION_COUNTRY_CODE', 15);
13
14 define('COUNTRY_CODE_LANGUAGE_LONG', 'long');
15
16 define('COUNTRY_CODE_LANGUAGE_SHORT', 'short');
17
18 /**
19 * Used when it was not possible to detect user's country, or when user's
20 * country is not in the list of supported countries.
21 */
22 define('COUNTRY_CODE_GLOBAL', 'xx');
23
24 /**
25 * Use the default country.
26 */
27 define('COUNTRY_CODE_DEFAULT', 'default');
28
29
30 /**
31 * Implementation of hook_init().
32 *
33 * Set global $language variable based on user's country.
34 */
35 function country_code_init() {
36 // Language negotiations must be set to country based.
37 if (variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_COUNTRY_CODE) {
38 return;
39 }
40
41 // When first installing, this hook is called without a prior call to hook_boot().
42 if (!function_exists('country_code')) {
43 country_code_boot();
44 }
45
46 // Set a country-based language if applicable.
47 _country_code_init_language();
48 }
49
50 /**
51 * Initialize a country code in the session.
52 *
53 * A country code can be set via a path prefix in the form cc/ provided the prefix is a
54 * valid two-digit country code for an installed and enabled country.
55 *
56 * The special global code defined in COUNTRY_CODE_GLOBAL can be passed through a
57 * query string in the form ?country_code=xx. This form is allowed to avoid a prefix
58 * with the global code.
59 *
60 * Requests originating externally are given special handling, such that incoming
61 * visitors can be given a country code based on their location rather than the path
62 * prefix on the link they followed.
63 *
64 * If no country code was set on the basis of the query string or a path prefix,
65 * regular handling is called.
66 */
67 function _country_code_init_code() {
68 // Cribbed from language_init().
69 $args = isset($_GET['q']) ? explode('/', $_GET['q']) : array();
70 $prefix = array_shift($args);
71 // Accept a country code passed in through the URL, if the country is enabled.
72 if ($prefix && array_key_exists($prefix, country_code_countries())) {
73 // Rebuild $GET['q'] with the country code removed.
74 // If there are no further args, we're at the front page.
75 if (empty($args)) {
76 $_GET['q'] = variable_get('site_frontpage', 'node');
77 }
78 else {
79 $_GET['q'] = implode('/', $args);
80 }
81 // If the link is external, only use path if country_code_external is true.
82 // If there is no country_code data in the session, this is a first visit
83 // and therefore equivalent to external.
84 if (!variable_get('country_code_external', 0) || isset($_SESSION['country_code']) || _country_code_request_is_external()) {
85 country_code_set($prefix);
86 return;
87 }
88 }
89 // Accept a global code passed in the query string.
90 if (isset($_GET['country_code']) && $_GET['country_code'] == COUNTRY_CODE_GLOBAL) {
91 country_code_set(COUNTRY_CODE_GLOBAL);
92 return;
93 }
94 // Default to fetching a country code by IP or another setting-based approach.
95 country_code();
96 }
97
98 /**
99 * Handler for country code determination based on IP.
100 *
101 * Note: This service already returns not identified country codes as 'xx' (global).
102 * Other services implementing hook_country_code_handler() will need to ensure they
103 * return this code for unidentified countries.
104 */
105 function country_code_get_country_code_hostip($ip_address) {
106 return strtolower(file_get_contents('http://api.hostip.info/country.php?ip=' . $ip_address));
107 }
108
109 /**
110 * Implementation of hook_country_code_handler().
111 */
112 function country_code_country_code_handler() {
113 return array(
114 'country_code_hostip' => array(
115 // Because this function is called at hook_boot, t() may not exist.
116 'title' => function_exists('t') ? t('hostip.info') : 'hostip.info',
117 'callback' => 'country_code_get_country_code_hostip',
118 ),
119 );
120 }
121
122 /**
123 * Determine if a request is external.
124 */
125 function _country_code_request_is_external() {
126 global $base_url;
127
128 // HTTP_REFERER will not be set by some browsers.
129 if (!isset($_SERVER['HTTP_REFERER'])) {
130 return FALSE;
131 }
132
133 $base = parse_url($base_url);
134 $referer = parse_url($_SERVER['HTTP_REFERER']);
135
136 return !($base['host'] == $referer['host'] && (!empty($base['path']) && strpos($referer['path'], $base['path']) === 0));
137 }
138
139 /**
140 * Set a country-based language if applicable.
141 */
142 function _country_code_init_language() {
143 global $language;
144
145 $languages = language_list('enabled');
146 $languages = $languages[1];
147 $country_languages = country_code_languages();
148
149 // Recognize a language passed through GET.
150 // Only accept an incoming language if it's one of the current country's languages.
151 if (isset($_GET['language']) && (isset($country_languages[$_GET['language']]) || isset($languages[$_GET['language']]))) {
152 $_SESSION['country_code_language'] = $_GET['language'];
153 }
154 // If we are viewing a node, priorize the node's language.
155 // We do this partly because, when we switch countries when viewing a node
156 // that has translations, we link to a node in a language to be set.
157 // See country_code_get_context_language().
158 elseif ($node = menu_get_object()) {
159 $node_language = $node && $node->tnid ? $node->language : NULL;
160 $_SESSION['country_code_language'] = country_code_language(NULL, $node_language);
161 }
162 // If there is a langauge already in the session, look for it.
163 elseif (isset($_SESSION['country_code_language'])) {
164 $set = FALSE;
165 $language_short = (strlen($_SESSION['country_code_language']) > 2) ? substr($_SESSION['country_code_language'], 0, 2) : $_SESSION['country_code_language'];
166 // If it's global, return a two-digit language
167 if ($language_short == COUNTRY_CODE_GLOBAL && isset($country_languages[$language_short])) {
168 $_SESSION['country_code_language'] = $language_short;
169 $set = TRUE;
170 }
171 // If we have a country, priorize a language that has the same base
172 // language as the current session language.
173 else {
174 foreach (array_keys($country_languages) as $langcode) {
175 if (substr($langcode, 0, 2) == $language_short) {
176 $_SESSION['country_code_language'] = $langcode;
177 $set = TRUE;
178 break;
179 }
180 }
181 }
182 // If no language was set, use the default.
183 if (!$set) {
184 $_SESSION['country_code_language'] = country_code_language();
185 }
186 }
187 // If no language was set, use the default.
188 else {
189 $_SESSION['country_code_language'] = country_code_language();
190 }
191
192 $language = $languages[$_SESSION['country_code_language']];
193 // Reset the path with the correct language. This is needed e.g. to ensure that
194 // language-specific aliases work.
195 $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'), $language->language);
196 }
197
198 /**
199 * Implementation of hook_boot().
200 *
201 * Include url rewriting functions.
202 */
203 function country_code_boot() {
204 // drupal_get_path() is not declared yet.
205 // TODO: handle conflicts with other modules declaring url rewrite functions.
206 if (!(function_exists('custom_url_rewrite_inbound') || function_exists('custom_url_rewrite_outbound'))) {
207 $path = dirname(__FILE__) . '/country_code.inc';
208 require_once($path);
209 }
210 _country_code_init_code();
211 }
212
213 /**
214 * Implementation of hook_menu().
215 */
216 function country_code_menu() {
217 $items = array();
218
219 // Country administration.
220 $items['admin/settings/country-code'] = array(
221 'title' => 'Country code',
222 'description' => "Configure how site should act based on current user's country.",
223 'page callback' => 'drupal_get_form',
224 'page arguments' => array('country_code_admin_overview'),
225 'access arguments' => array('administer site configuration'),
226 'file' => 'country_code.admin.inc',
227 );
228 $items['admin/settings/country-code/overview'] = array(
229 'title' => 'List',
230 'weight' => 0,
231 'type' => MENU_DEFAULT_LOCAL_TASK,
232 );
233 $items['admin/settings/country-code/add'] = array(
234 'title' => 'Add country',
235 'page callback' => 'drupal_get_form',
236 'page arguments' => array('country_code_admin_country_form'),
237 'access arguments' => array('administer site configuration'),
238 'weight' => 5,
239 'type' => MENU_LOCAL_TASK,
240 'file' => 'country_code.admin.inc',
241 );
242 $items['admin/settings/country-code/configure'] = array(
243 'title' => 'Configure',
244 'page callback' => 'drupal_get_form',
245 'page arguments' => array('country_code_admin_settings'),
246 'access arguments' => array('administer site configuration'),
247 'weight' => 10,
248 'type' => MENU_LOCAL_TASK,
249 'file' => 'country_code.admin.inc',
250 );
251
252 // Country editing, country language management.
253 $items['admin/settings/country-code-country/%country_code_country'] = array(
254 'title' => 'Edit country',
255 'page callback' => 'drupal_get_form',
256 'page arguments' => array('country_code_admin_country_form', 3),
257 'access arguments' => array('administer site configuration'),
258 'file' => 'country_code.admin.inc',
259 );
260 $items['admin/settings/country-code-country/%country_code_country/properties'] = array(
261 'title' => 'Edit',
262 'weight' => 0,
263 'type' => MENU_DEFAULT_LOCAL_TASK,
264 );
265 $items['admin/settings/country-code-country/%country_code_country/languages'] = array(
266 'title' => 'Languages',
267 'page callback' => 'drupal_get_form',
268 'page arguments' => array('country_code_admin_country_languages', 3),
269 'access arguments' => array('administer site configuration'),
270 'weight' => 10,
271 'type' => MENU_LOCAL_TASK,
272 'file' => 'country_code.admin.inc',
273 );
274 $items['admin/settings/country-code-country/%country_code_country/languages/list'] = array(
275 'title' => 'List',
276 'weight' => 0,
277 'type' => MENU_DEFAULT_LOCAL_TASK,
278 );
279 $items['admin/settings/country-code-country/%country_code_country/languages/add'] = array(
280 'title' => 'Add language',
281 'page callback' => 'drupal_get_form',
282 'page arguments' => array('country_code_admin_language_form', 3),
283 'access arguments' => array('administer site configuration'),
284 'weight' => 10,
285 'type' => MENU_LOCAL_TASK,
286 'file' => 'country_code.admin.inc',
287 );
288
289 // Delete country.
290 $items['admin/settings/country-code/delete/%country_code_country'] = array(
291 'title' => 'Confirm',
292 'page callback' => 'drupal_get_form',
293 'page arguments' => array('country_code_admin_country_delete', 4),
294 'access arguments' => array('administer site configuration'),
295 'type' => MENU_CALLBACK,
296 'file' => 'country_code.admin.inc',
297 );
298
299 return $items;
300 }
301
302 /**
303 * Implementation of hook_help().
304 */
305 function country_code_help($path, $arg) {
306 switch ($path) {
307 case 'admin/settings/country-code':
308 if (variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_COUNTRY_CODE) {
309 return '<p>'. t('To enable language negotiation based on country code, go to <a href="@language-settings">language settings</a>.', array('@language-settings' => url('admin/settings/language/configure'))) .'</p>';
310 }
311 break;
312 }
313 }
314
315 /**
316 * Return a user's preferred country, if set, or a default.
317 */
318 function country_code_preferred_country($account, $default = TRUE) {
319 $countries = country_code_countries();
320 if (!empty($account->country_code) && isset($countries[$account->country_code])) {
321 return $account->country_code;
322 }
323 else {
324 return $default ? country_code_default() : FALSE;
325 }
326 }
327
328 /**
329 * Return the default country, if set.
330 */
331 function country_code_default() {
332 return variable_get('site_country_default_country', '');
333 }
334
335 /**
336 * Implementation of hook_user().
337 */
338 function country_code_user($op, $edit, &$account, $category = NULL) {
339 global $user;
340
341 // If the site is set to enable user country selection and we have more
342 // than one country and are either creating a user on the admin
343 // interface or editing the user, show the country selector.
344 if (variable_get('country_code_user_country', 0) && variable_get('country_code_count', 0) > 0 && ($op == 'register' && user_access('administer users') || $op == 'form' && $category == 'account')) {
345 $countries = country_code_countries();
346
347 // If the user is being created and is anonymous, we set the user country by
348 // the user's location.
349 $user_preferred_country = $account ? country_code_preferred_country($account) : (!$user ? _country_code_ip_code() : country_code_default());
350
351 $names = array(
352 COUNTRY_CODE_GLOBAL => t('Global'),
353 );
354 foreach ($countries as $code => $country) {
355 $names[$code] = $country->name;
356 }
357 $form['country_code'] = array(
358 '#type' => 'fieldset',
359 '#title' => t('Country settings'),
360 // Assign a weight just above the locale module's section.
361 '#weight' => 0.9,
362 );
363
364 // Get language negotiation settings.
365 $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE);
366 $form['country_code']['country_code'] = array(
367 '#type' => (count($names) <= 5 ? 'radios' : 'select'),
368 '#title' => t('Country'),
369 '#default_value' => isset($account->country_code) ? $account->country_code : $user_preferred_country,
370 '#options' => $names,
371 '#description' => ($mode == LANGUAGE_NEGOTIATION_COUNTRY_CODE) ? t("This account's default country for language selection, content filtering and date formats") : t("This account's default country for content filtering and date formats."),
372 );
373
374 return $form;
375 }
376 elseif ($op == 'update') {
377 country_code_set($edit['country_code']);
378 }
379 }
380
381
382 /**
383 * Implementation of hook_theme().
384 */
385 function country_code_theme() {
386 return array(
387 'country_code_admin_overview' => array(
388 'arguments' => array('form' => array()),
389 ),
390 'country_code_admin_country_languages' => array(
391 'arguments' => array('form' => array()),
392 ),
393 );
394 }
395
396 /**
397 * Implementation of hook_form_alter().
398 *
399 * Unset or adjust the language selection element on user edit forms.
400 */
401 function country_code_form_alter(&$form, $form_state, $form_id) {
402 if (in_array($form_id, array('user_profile_form', 'user_register')) && isset($form['locale'])) {
403 // If user language selection is disabled, unset the locale element.
404 if (!variable_get('country_code_user_language', 1)) {
405 unset($form['locale']);
406 }
407 // Otherwise, limit the available languages to those of the user's selected
408 // country.
409 elseif (isset($form['country_code']) && $form['country_code']['country_code']['#default_value']) {
410 $countries = country_code_countries();
411 $country = $form['country_code']['country_code']['#default_value'];
412 if (isset($countries[$country])) {
413 $languages = country_code_languages($country);
414 $options = array_intersect_key($form['locale']['language']['#options'], $languages);
415 // Use the short names for these languages.
416 foreach ($options as $key => $value) {
417 $options[$key] = $languages[$key]->name . ' (' . $languages[$key]->native . ')';
418 }
419 }
420 }
421 // If not using country, restrict to two-digit languages, since only these
422 // can be linked with countries.
423 else {
424 $options = array_intersect_key($form['locale']['language']['#options'], country_code_languages_short());
425 }
426 if (isset($options)) {
427 // If at least two options remain, limit the options to them.
428 if (count($options) > 1) {
429 $form['locale']['language']['#options'] = $options;
430 }
431 // Otherwise, unset the element.
432 else {
433 unset($form['locale']);
434 }
435 }
436 }
437 }
438
439 /**
440 * Implementation of hook_form_form_id_alter().
441 *
442 * Add a custom language negotiation option to the locale_languages_configure form.
443 */
444 function country_code_form_locale_languages_configure_form_alter(&$form, $form_state) {
445 $form['language_negotiation']['#options'][LANGUAGE_NEGOTIATION_COUNTRY_CODE] = t('Based on <a href="@countries">country settings</a>.', array('@countries' => url('admin/settings/country-code')));
446 }
447
448 /**
449 * Implementation of hook_form_form_id_alter().
450 *
451 * Add a custom language negotiation option to the locale_languages_configure form.
452 */
453 function country_code_form_system_modules_alter(&$form, $form_state) {
454 $form['#submit'][] = 'country_code_system_modules_submit';
455 }
456
457 /**
458 * Form submit handler for system_modules altered form.
459 */
460 function country_code_system_modules_submit($form, &$form_state) {
461 if ($form_state['values']['status']['country_code'] == 0 && in_array('country_code', $form['status']['#default_value'])) {
462 _country_code_reset();
463 }
464 }
465
466 /**
467 * Reset cached country code information.
468 */
469 function _country_code_reset() {
470 // Set the country code to global.
471 // TODO: should we be calling country_code(TRUE) instead?
472 // country_code(TRUE);
473 country_code_set(COUNTRY_CODE_GLOBAL);
474 // Refresh static variables.
475 if (!(function_exists('custom_url_rewrite_inbound') || function_exists('custom_url_rewrite_outbound'))) {
476 $path = dirname(__FILE__) . '/country_code.inc';
477 require_once($path);
478 }
479 $path = '';
480 $options = array();
481 custom_url_rewrite_outbound($path, $options, '', TRUE);
482 }
483
484 /**
485 * Implementation of hook_block().
486 *
487 * Display a country switcher. Country links may be provided by other modules.
488 */
489 function country_code_block($op = 'list', $delta = 0) {
490 if ($op == 'list') {
491 $block[0]['info'] = t('Country switcher');
492 // Not worth caching.
493 $block[0]['cache'] = BLOCK_NO_CACHE;
494 return $block;
495 }
496
497 // Only show if we have at least two countries and country dependent
498 // web addresses, so we can actually link to other language versions.
499 elseif ($op == 'view') {
500 if (variable_get('country_code_count', 0) > 1 ) {
501 $links = array();
502 $countries = country_code_countries();
503 $global = country_code_global();
504 array_unshift($countries, $global);
505
506 $node = menu_get_object();
507 $translations = $node && $node->tnid ? module_invoke('translation', 'node_get_translations', $node->tnid) : array();
508 foreach ($countries as $country) {
509 if (!empty($translations)) {
510 if ($best_language = country_code_get_context_language(array_keys($translations), $country->country)) {
511 $path = 'node/' . $translations[$best_language]->nid;
512 }
513 }
514 $links[$country->country] = array(
515 // Add a prefix only if this is not the current country.
516 // This way the 'active' class will be set correctly.
517 'href' => ($country->country == country_code() ? '' : $country->country . '/') . (isset($path) ? $path : $_GET['q']),
518 'title' => $country->name,
519 'attributes' => array('class' => 'country-code-link'),
520 );
521 }
522 // Pass the global code in the query string.
523 $links[COUNTRY_CODE_GLOBAL]['query'] = 'country_code=' . COUNTRY_CODE_GLOBAL;
524 // Allow modules to alter country links.
525 drupal_alter('country_code_link', $links, $_GET['q']);
526
527 $block['subject'] = t('Countries');
528 $block['content'] = theme('links', $links, array());
529 return $block;
530 }
531 }
532 }
533
534 /**
535 * Return an object representing the global version of a site.
536 */
537 function country_code_global() {
538 $global = new stdClass();
539 $global->country = COUNTRY_CODE_GLOBAL;
540 $global->name = t('Global');
541 return $global;
542 }
543
544 /**
545 * Implementation of hook_link_alter().
546 *
547 * Remove links not supported by current country.
548 */
549 function country_code_link_alter(&$links, &$node) {
550 $languages = country_code_languages();
551 foreach ($links as $module => $link) {
552 if (strpos($module, 'node_translation') === 0) {
553 if (!variable_get('country_code_node_links', 1)) {
554 unset($links[$module]);
555 }
556 else {
557 $langcode = substr($module, 17);
558 if (!isset($languages[$langcode])) {
559 unset($links[$module]);
560 }
561 else {
562 $links[$module]['title'] = $languages[$langcode]->native;
563 }
564 }
565 }
566 }
567 }
568
569 /**
570 * Implementation of hook_translation_link_alter().
571 *
572 * Strip out languages not supported by current country.
573 */
574 function country_code_translation_link_alter(&$links) {
575 global $language;
576
577 if (variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) == LANGUAGE_NEGOTIATION_COUNTRY_CODE) {
578 $country_code = country_code();
579 $country_languages = country_code_languages($country_code);
580 $links = array_intersect_key($links, $country_languages);
581 foreach ($links as $key => $link) {
582 // If the link's language is not the current one, prepend a prefix
583 // and pass a language key. The prepended prefix ensures the links
584 // don't get the 'active' class.
585 if ($link['language']->language != $language->language) {
586 // Only add the prefix if the base language is different.
587 if (substr($link['language']->language, 0, 2) != substr($language->language, 0, 2)) {
588 $links[$key]['href'] = drupal_is_front_page() ? $country_code : $country_code . '/' . $links[$key]['href'];
589 }
590 $links[$key]['query'] = array('language' => $key);
591 }
592 // Show short language names.
593 $links[$key]['title'] = $country_languages[$key]->native;
594 }
595 }
596 }
597
598 /**
599 * Implementation of hook_views_api().
600 */
601 function country_code_views_api() {
602 return array(
603 'api' => 2,
604 );
605 }
606
607 /**
608 * Get a list of countries from database.
609 *
610 * @param $enabled
611 * Whether to return only enabled countries.
612 * @param $reset
613 * Whether to reset the countries array.
614 * @return
615 * An array of countries.
616 */
617 function country_code_countries($enabled = TRUE, $reset = FALSE) {
618 static $countries, $enabled_countries;
619
620 if (!isset($countries) || $reset) {
621 $countries = array();
622 $enabled_countries = array();
623 $result = db_query('SELECT country, enabled FROM {country_code_country}');
624 while ($country = db_fetch_object($result)) {
625 $country->name = country_code_country_name($country->country);
626 $countries[$country->country] = $country;
627 if ($country->enabled) {
628 $enabled_countries[$country->country] = $country;
629 }
630 }
631 }
632
633 return $enabled ? $enabled_countries : $countries;
634 }
635
636 /**
637 * Return a language for the given country. Or try to discover the language
638 * based on user preferences and country settings.
639 *
640 * @param $country
641 * Country code.
642 * @param $languages
643 * An array of languages that the returned language should be one of.
644 * @return
645 * Language code.
646 */
647 function country_code_language($country = NULL, $reference_language = NULL) {
648 global $user;
649
650 if (!isset($country)) {
651 $country = country_code();
652 }
653
654 $languages = country_code_languages($country);
655
656 // If there is a reference language and it is valid, return it.
657 if (!empty($reference_language) && isset($languages[$reference_language])) {
658 return $reference_language;
659 }
660
661 // Return user's preferred language or the first key.
662 if (variable_get('country_code_user_language', 1) && isset($user->language) && isset($languages[$user->language])) {
663 return $user->language;
664 }
665 // Otherwise, return a supported language based on the user's browser data.
666 elseif (variable_get('country_code_browser_language', 1) && $language = language_from_browser()) {
667 if (isset($languages[$language->language])) {
668 return $language->language;
669 }
670 // Match a two-digit browser language.
671 if (strlen($language->language) == 2) {
672 foreach (array_keys($languages) as $langcode) {
673 if (substr($langcode, 0, 2) == $language->language) {
674 return $langcode;
675 }
676 }
677 }
678 }
679
680 // Default to the first (lowest weight) language.
681 return key($languages);
682 }
683
684 /**
685 * Fetch an individual country.
686 *
687 * Because we use '%country_code_country' as a wildcard in our hook_menu()
688 * handler, this function will also be called automatically when we go to edit
689 * or delete a country.
690 *
691 * @param $country
692 * A country code.
693 * @return
694 * A single record in array format, or FALSE if none matched the incoming
695 * country code.
696 */
697 function country_code_country_load($country) {
698 $sql = "SELECT * FROM {country_code_country} WHERE country = '%s'";
699 $result = db_query($sql, $country);
700 if ($record = db_fetch_array($result)) {
701 $record['name'] = country_code_country_name($record['country']);
702 return $record;
703 }
704 else {
705 return FALSE;
706 }
707 }
708
709 /**
710 * Insert a new country, or update an existing one.
711 *
712 * @param $record
713 * A country record to be saved. If country already exists in database, the
714 * record will be updated. Otherwise, a new record will be inserted into the
715 * database.
716 * @return
717 * The flag SAVED_NEW or SAVED_UPDATE based on the operation performed.
718 */
719 function country_code_country_save($record) {
720 $existing = country_code_country_load($record['country']);
721 if ($existing) {
722 // If it is the default country, enforce it as enabled.
723 $record['enabled'] = (variable_get('site_country_default_country', '') == $record['country']) ? TRUE : $record['enabled'];
724 // Update enabled country count if we are changing the enabled status of a country.
725 if (isset($record['enabled']) && $existing['enabled'] != $record['enabled']) {
726 $current_count = variable_get('country_code_count', 0);
727 variable_set('country_code_count', $record['enabled'] ? $current_count + 1 : $current_count - 1);
728 }
729 return drupal_write_record('country_code_country', $record, 'country');
730 }
731 else {
732 // If this is the first country, set it as the default and enable it.
733 if (count(country_code_countries(FALSE)) == 0) {
734 $record['enabled'] = 1;
735 variable_set('site_country_default_country', $record['country']);
736 }
737 // Increment enabled country count if we are adding an enabled country.
738 if ($record['enabled']) {
739 variable_set('country_code_count', variable_get('country_code_count', 0) + 1);
740 }
741 return drupal_write_record('country_code_country', $record);
742 }
743 }
744
745 /**
746 * Deletes a country, given its country code.
747 *
748 * @param $country
749 * The country code of the country to be deleted.
750 * @return
751 * 1 for success, 0 for failure if an attempt was made to remove the last
752 * country which was also the default.
753 */
754 function country_code_country_delete($country) {
755 // Don't allow users to delete the default country, if it is the last country
756 // remaining (enabled or disabled).
757 if (count(country_code_countries(FALSE)) == 1) {
758 return 0;
759 }
760 if ($country['enabled']) {
761 variable_set('country_code_count', variable_get('country_code_count', 0) > 1 ? variable_get('country_code_count', 0) - 1 : 0);
762 }
763 db_query("DELETE FROM {country_code_country} WHERE country = '%s'", $country['country']);
764 $countries = country_code_countries(TRUE, TRUE); // Reset country list.
765 // If the deleted country was the default, try to set a new default.
766 if (variable_get('site_country_default_country', '') == $country['country']) {
767 // Only true if there is at least one enabled country configured.
768 if (count($countries) > 0) {
769 $new = array_shift($countries);
770 variable_set('site_country_default_country', $new->country);
771 }
772 // Try to set a new default from the disabled countries.
773 else {
774 // Check first if there any disabled countries we can use.
775 $countries = country_code_countries(FALSE);
776 // If yes, enable it and make it the default.
777 if (count($countries) > 0) {
778 $new = array_shift($countries);
779 $new->enabled = 1;
780 drupal_write_record('country_code_country', $new, 'country');
781 variable_set('site_country_default_country', $new->country);
782 }
783 // If this was the last, delete the variable.
784 else {
785 variable_del('site_country_default_country');
786 }
787 }
788 }
789 return 1;
790 }
791
792 /**
793 * Given a country code, return the country name.
794 *
795 * @return
796 * String, country name.
797 */
798 function country_code_country_name($code) {
799 $countries = site_country_country_list();
800 return isset($countries[$code]) ? $countries[$code] : NULL;
801 }
802
803 /**
804 * Given a country code, return all languages for that country.
805 *
806 * @return
807 * Array of language objects.
808 */
809 function country_code_languages($country = NULL, $enabled = TRUE, $index = COUNTRY_CODE_LANGUAGE_LONG) {
810 static $languages = array();
811
812 if (is_null($country)) {
813 $country = country_code();
814 }
815
816 if (!isset($languages[$country])) {
817 $languages[$country] = array();
818 // If requesting for global, return all two-digit languages.
819 if ($country == COUNTRY_CODE_GLOBAL) {
820 if ($enabled) {
821 $result = db_query("SELECT language, name, native, weight FROM {languages} WHERE CHAR_LENGTH(language) = 2 AND enabled = 1 ORDER BY weight, name");
822 }
823 else {
824 $result = db_query("SELECT language, name, native, weight FROM {languages} WHERE CHAR_LENGTH(language) = 2 ORDER BY weight, name");
825 }
826 }
827 // Otherwise, return a country's languages.
828 else {
829 if ($enabled) {
830 $result = db_query("SELECT c.language, g.language as language_short, g.name, g.native, c.weight FROM {languages} c INNER JOIN {languages} g ON SUBSTRING(c.language, 1, 2) = g.language WHERE SUBSTRING(c.language, 4) = '%s' AND c.enabled = 1 ORDER BY c.weight, c.name", $country);
831 }
832 else {
833 $result = db_query("SELECT c.language, g.language as language_short, g.name, g.native, c.weight FROM {languages} c INNER JOIN {languages} g ON SUBSTRING(c.language, 1, 2) = g.language WHERE SUBSTRING(c.language, 4) = '%s' ORDER BY c.weight, c.name", $country);
834 }
835 }
836 while ($language = db_fetch_object($result)) {
837 $key = ($index == COUNTRY_CODE_LANGUAGE_LONG) ? $language->language : $language->language_short;
838 $languages[$country][$key] = $language;
839 }
840 }
841
842 return $languages[$country];
843 }
844
845 /**
846 * Fetch a language code for a language and country, or for the current language and
847 * current country, if such a language is installed.
848 *
849 * @return
850 * A language code form en-ca where en is the language and ca
851 * is the country.
852 */
853 function country_code_get_country_language($language_code = NULL, $country_code = NULL, $enabled = TRUE) {
854 global $language;
855
856 if (!isset($language_code)) {
857 // We can assume current language is always enabled.
858 return $language->language;
859 }
860
861 if (!isset($country_code)) {
862 $country_code = country_code();
863 }
864
865 $country_language = $language_code . '-' . $country_code;
866
867 if ($enabled) {
868 $enabled_languages = locale_language_list();
869 return isset($enabled_languages[$country_language]) ? $country_language : FALSE;
870 }
871 else {
872 return $country_language;
873 }
874 }
875
876 /**
877 * Return the currently enabled two-digit languages.
878 *
879 * @return
880 * An array of languages with codes as keys and names as values.
881 */
882 function country_code_languages_short() {
883 $languages = locale_language_list('name', TRUE);
884 foreach ($languages as $id => $name) {
885 if (strlen($id) != 2) {
886 unset($languages[$id]);
887 }
888 }
889 return $languages;
890 }
891
892 /**
893 * Return the currently enabled five-digit languages for currently enabled countries.
894 *
895 * @return
896 * An array of languages with codes as keys and names as values.
897 */
898 function country_code_languages_long() {
899 $languages = locale_language_list('name', TRUE);
900 $country_languages = country_code_languages(COUNTRY_CODE_GLOBAL);
901 foreach ($languages as $id => $name) {
902 if (strlen($id) != 5) {
903 unset($languages[$id]);
904 }
905 }
906 return $languages;
907 }
908
909 /**
910 * Determine if a language exists.
911 */
912 function country_code_language_exists($langcode) {
913 return db_result(db_query("SELECT COUNT(*) FROM {languages} WHERE language = '%s'", $langcode)) != 0;
914 }
915
916 /**
917 * Return the appropriate translation for a nid.
918 *
919 * @param $nid
920 * A node ID.
921 * @param $tnid
922 * Optional: a translation ID (tnid) corresponding to the node ID.
923 * @return
924 * The best match node ID.
925 */
926 function country_code_get_country_node_id($nid, $tnid = NULL) {
927 global $language;
928
929 if ($tnid == NULL) {
930 $tnid = db_result(db_query('SELECT tnid FROM {node} WHERE nid = %d', $nid));
931 }
932
933 if ($tnid) {
934 $translations = translation_node_get_translations($tnid);
935
936 // Priorize country-specific language.
937 $languages = array($language->language);
938
939 if (strlen($language->language) > 2) {
940 // Also search for 'generic', non country specific language.
941 $languages[] = substr($language->language, 0, 2);
942 }
943
944 // Return the first found translation.
945 foreach ($languages as $langcode) {
946 if (isset($translations[$langcode])) {
947 return $translations[$langcode]->nid;
948 }
949 }
950 // TODO: return FALSE here?
951 // return FALSE;
952 }
953
954 return $nid;
955 }
956
957 /**
958 * Return the appropriate translation for a node.
959 *
960 * @param $node
961 * A fully loaded node object.
962 * @return
963 * The best match node.
964 */
965 function country_code_get_country_node($node) {
966 return node_load(country_code_get_country_node_id($node->nid, $node->tnid));
967 }
968
969 /**
970 * Given a set of languages, return the best language for the current context.
971 *
972 * @param $languages
973 * Array, list of language codes.
974 * @param $country
975 * String, country code.
976 * @param enabled
977 * Whether to search only on enabled languages.
978 *
979 * @return unknown
980 */
981 function country_code_get_context_language($languages, $country = NULL, $enabled = TRUE) {
982 global $language;
983
984 if (!isset($country)) {
985 $country = country_code();
986 }
987
988 $language_short = (strlen($language->language) > 2) ? substr($language->language, 0, 2) : $language->language;
989
990 // If it's global, return two-digits language
991 if ($country == COUNTRY_CODE_GLOBAL && in_array($language_short, $languages)) {
992 return $language_short;
993 }
994
995 // First see if there is a country specific language matching current
996 // two digits language code.
997 if ($country_language = country_code_get_country_language($language_short, $country, $enabled)) {
998 if (in_array($country_language, $languages)) {
999 return $country_language;
1000 }
1001 }
1002
1003 // Search for a language on all languages set for the given country.
1004 $country_languages = country_code_languages($country, $enabled);
1005 foreach ($country_languages as $langcode => $country_language) {
1006 if (in_array($langcode, $languages)) {
1007 return $langcode;
1008 }
1009 elseif (in_array($country_language->language_short, $languages)) {
1010 return $country_language->language_short;
1011 }
1012 }
1013
1014 return FALSE;
1015 }
1016
1017 /**
1018 * Return the ip_address. This is only used for testing. You can override the
1019 * ip address by setting this in your settings.php (the IP is for cbc.ca, in
1020 * Canada):
1021 * $conf = array(
1022 * 'country_code_ip_address' => '159.33.3.85',
1023 * );
1024 */
1025 function _country_code_ip_address() {
1026 return variable_get('country_code_ip_address', ip_address());
1027 }
1028
1029 /**
1030 * Return the country code of the user, based on the user's IP address.
1031 *
1032 * @TODO: support IP to country (ip2cc) or other similar modules?
1033 */
1034 function country_code($reset = FALSE) {
1035 global $user;
1036
1037 if ($reset || empty($_SESSION['country_code'])) {
1038 // Accept a user country preference.
1039 if (variable_get('country_code_user_country', 0) && $country = country_code_preferred_country($user, FALSE)) {
1040 $country_code = $country;
1041 }
1042 // Otherwise, try IP detection.
1043 else {
1044 $country_code = _country_code_ip_code();
1045 }
1046 // If we have countries but the detected country is not supported, use a fallback.
1047 if (variable_get('country_code_count', 0) && !array_key_exists($country_code, country_code_countries())) {
1048 // Get the country code fallback. If it's not set to global, then get the 'country_code_default' variable setting.
1049 $country_code = variable_get('country_code_fallback', COUNTRY_CODE_GLOBAL) != COUNTRY_CODE_GLOBAL && country_code_default() ? country_code_default() : COUNTRY_CODE_GLOBAL;
1050 }
1051 country_code_set($country_code);
1052 }
1053 return $_SESSION['country_code'];
1054 }
1055
1056 /**
1057 * Return a country code based on the visitor's IP address.
1058 */
1059 function _country_code_ip_code() {
1060 static $country_code;
1061 if (!isset($country_code)) {
1062 $handlers = module_invoke_all('country_code_handler');
1063 $handler = $handlers[variable_get('country_code_handler', 'country_code_hostip')];
1064 $country_code = call_user_func($handler['callback'], _country_code_ip_address());
1065 }
1066 return $country_code;
1067 }
1068
1069 /**
1070 * Set session country code.
1071 */
1072 function country_code_set($country_code) {
1073 $_SESSION['country_code'] = $country_code;
1074 }
1075
1076 /**
1077 * Return a path prefix consisting of a country code and a forward slash or an empty string
1078 * in the case of a country of xx (unknown).
1079 */
1080 function _country_code_prefix() {
1081 $country_code = country_code();
1082 return country_code() == COUNTRY_CODE_GLOBAL ? '' : $country_code . '/';
1083 }
1084
1085 /**
1086 * Implementation of hook_form_alter().
1087 */
1088 function country_code_form_system_site_information_settings_alter(&$form, $form_state) {
1089 $form['#submit'][] = 'country_code_system_site_information_settings_submit';
1090 }
1091
1092 /**
1093 * Form submit handler for system_site_information_settings altered form.
1094 */
1095 function country_code_system_site_information_settings_submit($form, &$form_state) {
1096 $country = $form_state['values']['site_country_default_country'];
1097 if (!empty($country)) {
1098 $record = array();
1099 $record['country'] = $country;
1100 $record['enabled'] = 1;
1101 country_code_country_save($record);
1102 }
1103 }

  ViewVC Help
Powered by ViewVC 1.1.2