ip_geoloc.module in IP Geolocation Views & Maps 8
Same filename and directory in other branches
IPGV&M is a mapping engine for Views that contain locations of entities and/or visitors. Google Maps, Leaflet and OpenLayers2 maps are all supported and available through this module. Using a number of optional sources IPGV&M also retrieves and stores geographical and street address information of your site visitors, based on either their HTML5-retrieved positions or their IP addresses. It stores this information in a form suitable for further processing, reporting, exporting and displaying via the Views module, either as tables or as maps. Ready-to-use views, blocks and maps are provided. For programmers there's an API.
File
ip_geoloc.moduleView source
<?php
/**
* @file
* IPGV&M is a mapping engine for Views that contain locations of entities and/or visitors. Google Maps, Leaflet and OpenLayers2 maps are all supported and available through this module. Using a number of optional sources IPGV&M also retrieves and stores geographical and street address information of your site visitors, based on either their HTML5-retrieved positions or their IP addresses. It stores this information in a form suitable for further processing, reporting, exporting and displaying via the Views module, either as tables or as maps. Ready-to-use views, blocks and maps are provided. For programmers there's an API.
*/
use Drupal\Component\Utility\UrlHelper;
use Drupal\views\ViewExecutable;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityTypeBundleInfo;
define('IP_GEOLOC_MAX_NUM_FONT_ICON_LIBS', 10);
define('IP_GEOLOC_CALLBACK_TIMEOUT', 30);
define('IP_GEOLOC_LOCATION_CHECK_INTERVAL', 60);
define('IP_GEOLOC_DEFAULT_PAGE_EXCLUSIONS', "admin/*\nsites/default/*\nsystem/ajax\njs/*");
define('IP_GEOLOC_MAP_DIV_DEFAULT_STYLE', 'height:300px');
define('IP_GEOLOC_CURRENT_VISITOR_MAP_OPTIONS', '{"mapTypeId":"roadmap", "disableDefaultUI":true, "zoom":15, "zoomControl":true}');
define('IP_GEOLOC_RECENT_VISITORS_MAP_OPTIONS', '{"mapTypeId":"roadmap", "disableDefaultUI":true, "zoom":2, "zoomControl":true, "scaleControl":true}');
define('IP_GEOLOC_EXAMPLE_MAP_OPTIONS', '{"mapTypeId":"roadmap", "zoom":10, "separator":", ", styles":[{"featureType":"road", "stylers":[{"saturation":-80}] }] }');
define('IP_GEOLOC_DOC_GOOGLE_MAP_OPTIONS', 'http://code.google.com/apis/maps/documentation/javascript/reference.html#MapOptions');
// Same as used for dates.
define('IP_GEOLOC_RANGE_SEPARATOR1', '--');
// Alternative delimiter. Must not be something common, e.g., the colon used in
// times 23:12:59.
define('IP_GEOLOC_RANGE_SEPARATOR2', '...');
define('IP_GEOLOC_LEAFLET_MARKERCLUSTER_REGIONBOUND_JS', 'leaflet.markercluster-regionbound.min.js');
define('IP_GEOLOC_THROBBER_PREFIX', '<div class="ajax-progress ajax-progress-throbber"><div class="throbber">');
define('IP_GEOLOC_THROBBER_DEFAULT_TEXT', t('Locating you') . '...');
// require_once 'ip_geoloc_api.inc';.
require_once 'ip_geoloc_blocks.inc';
require_once 'ip_geoloc_theme.inc';
include_once 'ip_geoloc.openlayers.inc';
include_once 'ip_geoloc.tokens.inc';
include_once 'ip_geoloc.context.inc';
/**
* Implements hook_ctools_plugin_api().
*
* Required to add a layer to OpenLayers, see ip_geoloc_openlayers_layers().
*/
/*function ip_geoloc_ctools_plugin_api($module, $api) {
if ($module == 'context') {
return array('version' => 3);
}
if ($module == 'openlayers' && $api == 'openlayers_layers') {
return array('version' => 1);
}
}
*/
/**
* Implements hook_help().
*/
function ip_geoloc_help($route_name, RouteMatchInterface $route_match) {
if ($route_match
->getRouteName() == 'admin/help#ip_geoloc') {
$project_url = Link::fromTextAndUrl('http://drupal.org/project/ip_geoloc', 'http://drupal.org/project/ip_geoloc')
->toString();
$readme_url = Link::fromTextAndUrl(drupal_get_path('module', 'ip_geoloc') . '/README.txt', drupal_get_path('module', 'ip_geoloc') . '/README.txt')
->toString();
return t('Detailed information is on the <a href="@ip_geoloc">IP Geolocation project page</a> and in the <a href="@README">README</a> file', [
'@ip_geoloc' => $project_url,
'@README' => $readme_url,
]);
}
}
/**
* Implements hook_library().
*/
/*function ip_geoloc_library() {
$libraries = array();
$path_fullscreen = libraries_get_path('leaflet-fullscreen') . '/dist';
$libraries['leaflet-fullscreen'] = array(
'title' => 'Leaflet Fullscreen',
'version' => '0.0.3',
'js' => array(
array(
'type' => 'file',
'data' => "$path_fullscreen/Leaflet.fullscreen.min.js",
'group' => JS_LIBRARY,
'weight' => 2,
'preprocess' => FALSE,
),
),
'css' => array(
"$path_fullscreen/leaflet.fullscreen.css" => array(
'type' => 'file',
'media' => 'screen',
'group' => CSS_DEFAULT,
'weight' => 2,
),
),
);
$path_minimap = libraries_get_path('leaflet-minimap') . '/dist';
$libraries['leaflet-minimap'] = array(
'title' => 'Leaflet MiniMap',
'version' => '2.1.0',
'js' => array(
array(
'type' => 'file',
'data' => "$path_minimap/Control.MiniMap.min.js",
'group' => JS_LIBRARY,
'weight' => 2,
'preprocess' => FALSE,
),
),
'css' => array(
"$path_minimap/Control.MiniMap.min.css" => array(
'type' => 'file',
'media' => 'screen',
'group' => CSS_DEFAULT,
'weight' => 2,
),
),
);
foreach (ip_geoloc_get_font_icon_libs() as $css_file) {
$css_file = trim($css_file);
if (!empty($css_file)) {
$libraries['ip_geoloc_font_icon_libs']['css'][$css_file] = array(
'type' => 'file',
'media' => 'all',
'group' => -101,
);
}
}
if (!empty($libraries['ip_geoloc_font_icon_libs']['css'])) {
$libraries['ip_geoloc_font_icon_libs']['title'] = t('IPGV&M font icon libraries');
}
return $libraries;
}
*
* */
/**
* Function ip_geoloc_library_alter(&$libraries, $module) {.
*/
function ip_geoloc_library_info_alter(&$libraries, $extension) {
if (isset($libraries['current_location_js']['js'])) {
$js_array = array_keys($libraries['current_location_js']['js']);
$library_config = $libraries['current_location_js']['js'][$js_array[0]];
unset($libraries['current_location_js']['js'][$js_array[0]]);
$google_with_key[IP_GEOLOC_GOOGLE_MAPS] = $library_config;
$libraries['current_location_js']['js'] = $google_with_key + $libraries['current_location_js']['js'];
}
if (isset($libraries['multi_location_js_style_map']['js'])) {
$js_array = array_keys($libraries['multi_location_js_style_map']['js']);
$library_config = $libraries['multi_location_js_style_map']['js'][$js_array[0]];
unset($libraries['multi_location_js_style_map']['js'][$js_array[0]]);
$google_with_key[IP_GEOLOC_GOOGLE_MAPS] = $library_config;
$libraries['multi_location_js_style_map']['js'] = $google_with_key + $libraries['multi_location_js_style_map']['js'];
}
}
/**
* Implements hook_js_info().
*/
function ip_geoloc_js_info() {
// With the js.module installed, and the .htaccess file edited to route
// through js.php, this array defines what function to call when a POST is
// received on "js/ip_geoloc/current_location".
// We're calling the same function as defined in ip_geoloc_menu(), but using
// a faster, more economic bootstrap phase, without hook_init().
$dependencies = \Drupal::moduleHandler()
->moduleExists('session_cache_file') ? [
'session_cache',
'session_cache_file',
] : (\Drupal::moduleHandler()
->moduleExists('session_cache') ? [
'session_cache',
] : []);
$js_info = [
'current_location' => [
'bootstrap' => \Drupal::moduleHandler()
->moduleExists('better_statistics') ? DRUPAL_BOOTSTRAP_FULL : DRUPAL_BOOTSTRAP_SESSION,
'callback function' => 'ip_geoloc_current_location_ajax_recipient',
'dependencies' => $dependencies,
'skip init' => TRUE,
'token' => FALSE,
],
];
return $js_info;
}
/**
* Pending doc.
*/
function ip_geoloc_page_attachments(array &$attachments) {
$api = \Drupal::service('ip_geoloc.api');
$geoloc_global = \Drupal::service('ip_geoloc.global');
$location = $api
->getVisitorLocation();
$scheduled_reverse_geocode = $geoloc_global
->checkLocation($location);
// 2nd condition is to avoid HTTP 503 error.
if ($scheduled_reverse_geocode && !\Drupal::state()
->get('system.maintenance_mode')) {
// Insert some javascript to first retrieve the user's lat/lon coords,
// HTML5 style (requiring the user to accept a browser prompt) and then
// optionally (default) use Google Maps API to reverse-geocode the lat/lon
// into a street address.
// This is all done via client-side calls, so the Drupal server will not
// rake up any calls against its Google-imposed quotum, i.e. the
// OVER_QUERY_LIMIT.
// When done, the javascript calls us back on the default menu callback,
// '/js/ip_geoloc/current_location', which receives the geolocation data
// from the Google Maps call via the $_POST variable and stores it on the
// session.
// Naturally all of this will only work if the browser is connected to
// the internet and has javascript enabled.
// See also: _ip_geoloc_process_find_me_ajax().
$geoloc_session = \Drupal::service('ip_geoloc.session');
$settings = $api
->getCurrentLocation();
$attachments['#attached']['library'][] = 'ip_geoloc/current_location_js';
$attachments['#attached']['drupalSettings'] = $settings;
$geoloc_session
->setSessionValue('last_position_check', time());
}
}
/**
* Implements hook_views_pre_render().
*/
function ip_geoloc_views_pre_render(ViewExecutable $view) {
if (isset($view) && strstr(get_class($view
->getStyle()), 'IpGeoLocPluginStyleMap')) {
// $view->element['#attached']['library'][] = 'ip_geoloc/multi_location_js_style_map';
// drupal_add_js(IP_GEOLOC_GOOGLE_MAPS, ['type' => 'external', 'group' => JS_LIBRARY]);
// drupal_add_js(drupal_get_path('module', 'ip_geoloc') . '/js/ip_geoloc_gmap_multi_visitor.js');
// if (!isset($map_options)) {
// $map_options = IP_GEOLOC_RECENT_VISITORS_MAP_OPTIONS;
// }
// $settings = [
// 'ip_geoloc_locations' => $locations,
// 'ip_geoloc_multi_location_map_div' => $div_id,
// 'ip_geoloc_multi_location_map_options' => drupal_json_decode($map_options),
// ];
// drupal_add_js($settings, 'setting');
}
}
/**
* Implements hook_preprocess_HOOK() for theme_ip_geoloc_leaflet().
*/
function template_preprocess_views_view_ip_geoloc_leaflet(&$variables) {
}
/**
* Implements hook_preprocess_HOOK()
*/
/*function ip_geoloc_preprocess_block(&$variables) {
if ($variables['plugin_id'] == 'current_visitor_map') {
$variables['#attached']['drupalSettings']['mycustomblock']['test'] = 'test';
}
}*/
/**
* Log errors via the watchdog.
*/
function ip_geoloc_log_errors() {
if ($error = _ip_geoloc_get_session_value('error')) {
// @todo How do we treat repeated 'user declined to share location' errors?
watchdog('IPGV&M', $error, NULL, WATCHDOG_NOTICE);
ip_geoloc_debug('IPGV&M, ' . ip_address() . ': ' . $error, 'warning');
_ip_geoloc_set_session_value('error', NULL);
}
}
/**
* Returns whether this was the first click of the session.
*
* @return bool
* TRUE if it was, i.e. if there has been no position check before.
*/
function ip_geoloc_is_first_click() {
$last_position_check = _ip_geoloc_get_session_value('last_position_check');
return empty($last_position_check);
}
/**
* Reinitialises the supplied location array.
*/
function _ip_geoloc_reinit_location(array &$location, $reverse_geocode_client_timeout) {
$location = [
'fixed_address' => isset($location['fixed_address']) ? (int) $location['fixed_address'] : NULL,
'regions' => isset($location['regions']) ? $location['regions'] : NULL,
];
// Calls below are synchronous, $location is filled upon return.
if (ip_geoloc_use_smart_ip_if_enabled($location) || ip_geoloc_use_geoip_api_if_enabled($location)) {
if ($reverse_geocode_client_timeout) {
watchdog('IPGV&M', 'Location timeout (waited %sec s). Fallback: %address.', [
'%sec' => number_format($reverse_geocode_client_timeout, 1),
'%address' => isset($location['formatted_address']) ? $location['formatted_address'] : '',
], WATCHDOG_NOTICE);
}
}
else {
ip_geoloc_debug(t('Smart IP and GeoIP API fallbacks NOT enabled.'));
}
}
/**
* Use Smart IP (if enabled) to retrieve lat/long and address info.
*
* Note that smart_ip_get_location() will invoke
* hook_smart_ip_get_location_alter($location), which we use to format the
* address.
*
* @param array $location
* If $location['ip_address'] isn't filled out the current user's
* IP address will be used.
*
* @return bool
* TRUE upon success, FALSE otherwise.
*/
function ip_geoloc_use_smart_ip_if_enabled(array &$location) {
if (\Drupal::state()
->get('ip_geoloc_smart_ip_as_backup', FALSE)) {
if (function_exists('smart_ip_get_location')) {
if (empty($location['ip_address'])) {
$location['ip_address'] = ip_address();
}
$fixed_address = isset($location['fixed_address']) ? $location['fixed_address'] : 0;
$region = isset($location['region']) ? $location['region'] : 0;
// See also: ip_geoloc_smart_ip_get_location_alter().
$location = [
'provider' => 'smart_ip',
'fixed_address' => $fixed_address,
'region' => $region,
] + smart_ip_get_location($location['ip_address']);
return TRUE;
}
ip_geoloc_debug(t('IPGV&M: Smart IP configured as a backup, but is not enabled.'));
}
// $location['formatted_address'] = '';.
return FALSE;
}
/**
* Module GeoIP API does not expose a hook, but it does expose an API.
*
* @param array $location
* If $location['ip_address'] isn't filled out the current user's
* IP address will be used.
*
* @return bool
* TRUE upon success, FALSE otherwise.
*/
function ip_geoloc_use_geoip_api_if_enabled(array &$location) {
if (!function_exists('geoip_city')) {
return FALSE;
}
$location['provider'] = 'geoip';
if (empty($location['ip_address'])) {
$location['ip_address'] = ip_address();
}
$geoip_location = (array) geoip_city($location['ip_address']);
if (reset($geoip_location)) {
// Where different, convert GeoIP names to our equivalents.
$geoip_location['country'] = isset($geoip_location['country_name']) ? $geoip_location['country_name'] : '';
unset($geoip_location['country_name']);
$location = array_merge($geoip_location, $location);
ip_geoloc_format_address($location);
}
ip_geoloc_debug(t('IPGV&M: GeoIP API retrieved: !location', [
'!location' => ip_geoloc_pretty_print($location),
]));
return TRUE;
}
/**
* Return whether a the visitor's location is due for an update.
*
* Updates are only performed on selected configured pages.
* An update is due when more than a configurable number of seconds have
* elapsed. If that number is set to zero, then the user's location will be
* requested until at least the location's country is known, which is
* normally immediately at the start of the session.
*
* @param array $location
* Array of location components.
*
* @return bool
* TRUE if an update is due.
*/
function _ip_geoloc_check_location(array $location = NULL) {
if (!\Drupal::state()
->get('ip_geoloc_google_to_reverse_geocode', FALSE)) {
return FALSE;
}
$path_alias = drupal_get_path_alias();
$include_pages = \Drupal::state()
->get('ip_geoloc_include_pages', '*');
if (!drupal_match_path($path_alias, $include_pages)) {
return FALSE;
}
$exclude_pages = \Drupal::state()
->get('ip_geoloc_exclude_pages', IP_GEOLOC_DEFAULT_PAGE_EXCLUSIONS);
if (drupal_match_path($path_alias, $exclude_pages)) {
return FALSE;
}
global $user;
$roles_to_reverse_geocode = \Drupal::state()
->get('ip_geoloc_roles_to_reverse_geocode', [
DRUPAL_ANONYMOUS_RID,
DRUPAL_AUTHENTICATED_RID,
]);
$roles_applicable = array_intersect($roles_to_reverse_geocode, array_keys($user->roles));
if (empty($roles_applicable)) {
return FALSE;
}
$interval = (int) \Drupal::state()
->get('ip_geoloc_location_check_interval', IP_GEOLOC_LOCATION_CHECK_INTERVAL);
if ($interval == 0) {
return !isset($location['latitude']);
}
$last_position_check = _ip_geoloc_get_session_value('last_position_check');
if (isset($last_position_check)) {
$time_elapsed = time() - $last_position_check;
if ($time_elapsed < $interval) {
ip_geoloc_debug(t('IPGV&M: next update scheduled for first click after %seconds seconds (unless overridden or on excluded page).', [
'%seconds' => $interval - $time_elapsed,
]));
return FALSE;
}
}
return TRUE;
}
/**
* Handle timeout of the Google Maps reverse-geocode callback, if enabled.
*
* This is based on $position_pending_since being set to the current time when
* the service was initiated.
*/
function _ip_geoloc_reverse_geocode_timeout() {
$pending_since = _ip_geoloc_get_session_value('position_pending_since');
if (isset($pending_since)) {
$time_elapsed = microtime(TRUE) - $pending_since;
if ($time_elapsed > IP_GEOLOC_CALLBACK_TIMEOUT) {
ip_geoloc_debug(t('IPGV&M timeout: the last reverse-geocode request was @sec s ago.', [
'@sec' => number_format($time_elapsed, 1),
]));
_ip_geoloc_set_session_value('position_pending_since', NULL);
return $time_elapsed;
}
}
return FALSE;
}
/**
* Poor man's address formatter.
*
* It doesn't take local format conventions into account. Luckily this is only
* called as a fallback when lat/long could not be established or the Google
* reverse-geocode function returned an error.
*
* @param array $location
* Array of location components.
*/
function ip_geoloc_format_address(array &$location) {
$location['formatted_address'] = isset($location['city']) ? $location['city'] : '';
if (!empty($location['region'])) {
$location['formatted_address'] .= ' ' . $location['region'];
}
if (!empty($location['postal_code']) && $location['postal_code'] != '-') {
$location['formatted_address'] .= ' ' . $location['postal_code'] . ',';
}
if (!empty($location['country'])) {
$location['formatted_address'] .= ' ' . $location['country'];
}
$location['formatted_address'] = trim($location['formatted_address']);
}
/**
* Fleshes out the $ip_geoloc_address array.
*
* This is based on the additional data provided in the $google_address array.
* This may involve tweaking of the 'latitude' and 'longitude' entries so that
* they remain consistent with the street address components.
*
* @param array $google_address
* Array of address components as returned by Google service.
* @param array $ip_geoloc_address
* The $google_address in flattened form.
*
* @return bool
* TRUE, unless google_address or ip_geoloc_address are empty
*/
function ip_geoloc_flatten_google_address(array $google_address, array &$ip_geoloc_address) {
if (is_array($google_address) && is_array($google_address['address_components']) && is_array($ip_geoloc_address)) {
$ip_geoloc_address['provider'] = 'google';
foreach ($google_address['address_components'] as $component) {
$long_name = $component['long_name'];
if (!empty($long_name)) {
$type = $component['types'][0];
$ip_geoloc_address[$type] = $long_name;
if ($type == 'country' && !empty($component['short_name'])) {
$ip_geoloc_address['country_code'] = $component['short_name'];
}
}
}
$ip_geoloc_address['formatted_address'] = $google_address['formatted_address'];
// The following may be slightly different from the original lat,long passed
// into ip_geoloc_reverse_geocode().
$ip_geoloc_address['latitude'] = $google_address['geometry']['location']['lat'];
$ip_geoloc_address['longitude'] = $google_address['geometry']['location']['lng'];
return TRUE;
}
return FALSE;
}
/**
* Print the location array nicely.
*
* @param array $location
* Array of location components.
*
* @return string
* The location array formatted as string.
*/
function ip_geoloc_pretty_print(array $location) {
$t = '';
foreach ($location as $label => $value) {
if (!empty($value)) {
$t .= SafeMarkup::checkPlain($label) . ': <strong>' . SafeMarkup::checkPlain($value) . '</strong> ';
}
}
return empty($t) ? t('nothing') : $t;
}
/**
* Returns the path to the configured marker directory.
*/
function ip_geoloc_marker_directory() {
$path = drupal_get_path('module', 'ip_geoloc');
$config = \Drupal::config('ip_geoloc.settings');
$marker_directory = $config
->get('ip_geoloc_marker_directory');
return $marker_directory ? $marker_directory : "{$path}/" . (\Drupal::moduleHandler()
->moduleExists('leaflet') ? 'amarkers' : 'markers');
}
/**
* Return the height and width of the markers in the selected set.
*
* @return string
* For example '32 x 42' or '21 x 34'.
*/
function ip_geoloc_marker_dimensions() {
$config = \Drupal::config('ip_geoloc.settings');
$dimensions = $config
->get('ip_geoloc_marker_dimensions');
if (empty($dimensions)) {
$directory = ip_geoloc_marker_directory();
$dimensions = strpos($directory, '/amarkers') ? '32 x 42' : '21 x 34';
}
return $dimensions;
}
/**
* Return available marker colors for use in a select drop-down.
*
* List is compiled based on available .png files in ip_geoloc/markers dir.
*
* @return array
* Array of color names indexed by machine names
*/
function ip_geoloc_marker_colors() {
$color_list =& drupal_static(__FUNCTION__);
if (!isset($color_list)) {
$color_list = [
'' => '<' . t('default') . '>',
0 => '<' . t('no marker') . '>',
];
if ($directory_handle = opendir(ip_geoloc_marker_directory())) {
while (($filename = readdir($directory_handle)) !== FALSE) {
if ($ext_pos = strrpos($filename, '.png')) {
$color = drupal_substr($filename, 0, $ext_pos);
// Ok... relies on translations done elsewhere.
$color_list[$color] = t($color);
}
}
closedir($directory_handle);
}
asort($color_list);
}
return $color_list;
}
/**
* Return available OpenLayers marker layers for use in a select drop-down.
*
* @return array
* An array indexed by marker layer number (1..n)
*/
function ip_geoloc_openlayers_marker_layers() {
$config = \Drupal::config('ip_geoloc.settings');
$num_location_marker_layers = $config
->get('ip_geoloc_num_location_marker_layers');
$num_location_marker_layers = $num_location_marker_layers ? $num_location_marker_layers : IP_GEOLOC_DEF_NUM_MARKER_LAYERS;
$marker_layers = [];
for ($layer = 1; $layer <= $num_location_marker_layers; $layer++) {
$marker_layers[$layer] = t('Marker layer') . " #{$layer}";
}
return $marker_layers;
}
/**
* Implements hook_form_FORMID_alter().
*/
function ip_geoloc_form_views_ui_edit_display_form_alter(&$form, &$form_state) {
// Append our own handler to deal with saving of the differentiator table.
if (isset($form['options']['style_options']['differentiator'])) {
$form['buttons']['submit']['#submit'][] = 'ip_geoloc_plugin_style_diff_color_ass_submit';
}
}
/**
* Implements hook_smart_ip_get_location_alter().
*
* Called from the bottom of smart_ip_get_location() when it has fleshed out
* the $location array as much as it can. Used here to format the address.
*/
function ip_geoloc_smart_ip_get_location_alter(&$location) {
if (empty($location['postal_code']) && isset($location['zip'])) {
$location['postal_code'] = $location['zip'];
}
ip_geoloc_format_address($location);
ip_geoloc_debug(t('IPGV&M: Smart IP retrieved: !location', [
'!location' => ip_geoloc_pretty_print($location),
]));
}
/**
* Determines if a value is within the supplied numeric or alphabetical range. String comparison is based on the ASCII/UTF8 order, so is case-sensitive.
*
* @param string $value
* The value to check in $range.
* @param string $range
* Of the form '1.5--4.5' (range is inclusive of end points)
*
* @return bool
* TRUE if the value is in range
*/
function ip_geoloc_is_in_range($value, $range, $view_args = NULL) {
if (!isset($value) || !isset($range)) {
return FALSE;
}
// Defensive programming to make sure we have a string.
if (is_array($range)) {
$range = reset($range);
}
$from_to = explode(IP_GEOLOC_RANGE_SEPARATOR1, $range);
if (count($from_to) < 2) {
$from_to = explode(IP_GEOLOC_RANGE_SEPARATOR2, $range);
}
if (($from = _ip_geoloc_extract_value($from_to[0], $view_args)) === NULL) {
return FALSE;
}
if (count($from_to) == 1) {
// Single value.
return trim($value) == trim($from);
}
if (($to = _ip_geoloc_extract_value($from_to[1], $view_args)) === NULL) {
return FALSE;
}
if ($from == '' && $to == '') {
// Range separator without values.
return TRUE;
}
if ($from != '' && $to != '') {
return $value >= $from && $value <= $to;
}
if ($from != '') {
return $value >= $from;
}
return $value <= $to;
}
/**
* Extracts a Views argument value from the supplied string.
*
* @param string $string
* The string to parse.
* @param array $view_args
* The View arguments.
*
* @return string
* The extracted value.
*/
function _ip_geoloc_extract_value($string, array $view_args) {
if (preg_match('/^!([0-9])/', $string, $matches)) {
$arg = $matches[1];
return isset($view_args[$arg - 1]) ? $view_args[$arg - 1] : arg($arg);
}
return $string;
}
/**
* FAPI validation of a range element.
*
* We want to cover numeric and alphabetic ranges, as well as the special
* replacement strings !1, !2 ... So we can't be very strict.
*/
function ip_geoloc_range_widget_validate($element, &$form_state) {
$range = $element['#value'];
$from_to = explode(IP_GEOLOC_RANGE_SEPARATOR1, $range);
if (count($from_to) < 2) {
$from_to = explode(IP_GEOLOC_RANGE_SEPARATOR2, $range);
}
if (count($from_to) < 2) {
// Not a range but a single value. This is ok. If we knew we were checking
// for a number we would pass the input through is_numeric(), but we don't.
}
else {
$from = trim($from_to[0]);
$to = trim($from_to[1]);
if (preg_match('/^![0-9]/', $from) || preg_match('/^![0-9]/', $to)) {
return;
}
$ok = TRUE;
// If either $from or $to is numeric then assume numeric range and apply
// validation accordingly.
if (is_numeric($from) || is_numeric($to)) {
// If one end is numeric, then the other must also be, or be empty.
$ok = empty($from) && empty($to) || empty($from) && is_numeric($to) || empty($to) && is_numeric($from) || is_numeric($from) && is_numeric($to) && $from <= $to;
}
elseif (!empty($from) && !empty($to)) {
// Alphabetic range validation.
$ok = $from <= $to;
}
if (!$ok) {
form_error($element, t('Invalid range.'));
}
}
}
/**
* Returns an array of libraries as entered on the config page.
*/
function ip_geoloc_get_font_icon_libs() {
$config = \Drupal::config('ip_geoloc.settings');
$libs = [];
for ($i = 1; $i <= IP_GEOLOC_MAX_NUM_FONT_ICON_LIBS; $i++) {
$file = $config
->get("ip_geoloc_font_icon_lib{$i}");
if (!empty($file)) {
$libs[$i] = $file;
}
}
$known_install = 'sites/all/libraries/font-awesome/css/font-awesome.min.css';
if (empty($libs) && file_exists($known_install)) {
$libs[1] = $known_install;
}
return $libs;
}
/**
* Returns whether debug is on for the current user.
*
* @global type $user
*/
function ip_geoloc_debug_flag() {
global $user;
$config = \Drupal::config('ip_geoloc.settings');
$user_names = explode(',', SafeMarkup::checkPlain($config
->get('ip_geoloc_debug')));
foreach ($user_names as $user_name) {
$user_name = Unicode::strtolower(trim($user_name));
$match = isset($user->name) ? $user_name == Unicode::strtolower(trim($user->name)) : $user_name == 'anon' || $user_name == 'anonymous';
if ($match) {
return TRUE;
}
}
return FALSE;
}
/**
* Special debug function: messages selected user names only.
*
* @param string $message
* The message string to bue output as a debug message.
* @param string $type
* Defaults to 'status'.
*
* @return mixed
* A multidimensional array with keys corresponding to the set message types.
* If there are no messages set, the function returns NULL.
*/
function ip_geoloc_debug($message, $type = 'status') {
if (ip_geoloc_debug_flag()) {
return drupal_set_message($message, $type, FALSE);
}
}
/**
* Returns true if the previous page was reloaded.
*/
function ip_geoloc_same_path() {
if (empty($_SERVER['HTTP_REFERER'])) {
return FALSE;
}
$referer = $_SERVER['HTTP_REFERER'];
global $base_url;
if (strpos($referer, $base_url) === 0) {
$prev_path = drupal_substr($referer, drupal_strlen($base_url) + 1);
if (empty($prev_path) && drupal_is_front_page()) {
return TRUE;
}
return $prev_path == current_path() || $prev_path == drupal_get_path_alias();
}
return FALSE;
}
/**
* Implements hook_ctools_plugin_directory().
*/
function ip_geoloc_ctools_plugin_directory($module, $plugin) {
if ($module == 'ctools' || $module == 'panels') {
return 'plugins/' . $plugin;
}
}
/**
* Implements hook_geofield_handler_argument_proximity_alter().
*
* We use this to place the visitor marker at the centre of the contextual
* proximity filter circle, so that the user has visual feedback as to the
* exact proximity centre as interpreted by Geocoder. Example: "/Oregon".
*
* function ip_geoloc_geofield_handler_argument_proximity_alter($contextual_filter_proximity_handler, $lat_lon_dist) {
* _ip_geoloc_set_session_value('location', $lat_lon_dist);
*/
/* A sneaky way to place leaflet features in the footer
function ip_geoloc_js_alter(&$javascript) {
if (isset($javascript[0]['data']['leaflet'])) {
$javascript[0]['type'] = 'setting';
}
}
*/
/**
* Implements hook_views_post_render().
*
* Used to clear the 'is_updated' flag on the visitor location after any
* map Views have had the opportunity to respond to it being set.
*/
function ip_geoloc_views_post_render(ViewExecutable $view, &$output, $cache) {
if (!isset($view) || !isset($view->style_plugin->plugin_name)) {
return;
}
$name = $view->style_plugin->plugin_name;
if (strpos($name, 'ip_geoloc') === 0 && ($location = ip_geoloc_get_visitor_location())) {
$location['is_updated'] = FALSE;
// This performs a merge.
_ip_geoloc_set_session_value('location', $location);
}
}
/**
* Implements hook_views_api().
*/
function ip_geoloc_views_api() {
return [
'api' => views_api_version(),
'path' => drupal_get_path('module', 'ip_geoloc') . '/views',
];
}
/**
* Implements hook_statistics_api().
*
* From Better Statistics module.
*/
function ip_geoloc_statistics_api() {
return [
'version' => 1,
'path' => drupal_get_path('module', 'ip_geoloc') . '/plugins',
'file' => 'ip_geoloc.statistics.inc',
];
}
/**
* Builds the javascript maps api url based on authentication method.
*
* Patch from https://www.drupal.org/node/2776209
*/
function ip_geoloc_build_google_api_url() {
$google_api_url = 'https://maps.googleapis.com/maps/api/js';
// Append query parameters for the Google Maps url.
// See https://developers.google.com/maps/documentation/javascript/versions
$query = [];
$config = \Drupal::config('ip_geoloc.settings');
$auth_method = $config
->get('ip_geoloc_auth_method') ? $config
->get('ip_geoloc_auth_method') : 0;
switch ($auth_method) {
case 1:
$key = $config
->get('ip_geoloc_apikey') ? $config
->get('ip_geoloc_apikey') : '';
$key = trim($key);
if (!empty($key)) {
$query['key'] = $key;
}
break;
case 2:
$client_id = $config
->get('ip_geoloc_client_id') ? $config
->get('ip_geoloc_client_id') : '';
$client_id = trim($client_id);
if (!empty($client_id)) {
$query['client'] = $client_id;
$client_id = $config
->get('ip_geoloc_signature') ? $config
->get('ip_geoloc_signature') : '';
$signature = trim($client_id);
if (!empty($signature)) {
$query['signature'] = $signature;
}
}
break;
default:
}
// Add query params to API url.
if (!empty($query)) {
// Include version number as it's required for Premium plans.
$query['v'] = '3.26';
$google_api_url .= '?' . UrlHelper::buildQuery($query);
}
// print_r($google_api_url);die();
return $google_api_url;
}
/**
* Implements hook_get_display_fields().
*/
function ip_geoloc_get_display_fields($view_display_handler, $with_table_prefixes = TRUE) {
$fields = [
'' => '<' . t('none') . '>',
];
// $field_handlers = $view_display->get_handlers('field');.
$field_handlers = $view_display_handler->handlers['field'];
$field_names = $view_display_handler
->getFieldLabels();
foreach ($field_handlers as $field_id => $field_handler) {
$field_id_ori = $field_id;
if ($with_table_prefixes && strpos($field_id, 'field_') === FALSE) {
// Example: 'title' becomes 'node_title'.
$field_id = $field_handler->table . "_{$field_id}";
}
$fields[$field_id] = $field_names[$field_id_ori];
}
return $fields;
}
/**
* Implements hook_get_field_instances().
*/
function ip_geoloc_get_field_instances($field_name) {
$instances = [];
// @TODO Check this migration
/*foreach (field_info_instances() as $type_bundles) {
foreach ($type_bundles as $bundle_instances) {
foreach ($bundle_instances as $fld_name => $instance) {
if ($fld_name == $field_name) {
$instances[] = $instance;
}
}
}
}*/
$entity_bundle_info = \Drupal::service('entity_type.bundle.info');
$all_bundles = $entity_bundle_info
->getAllBundleInfo();
foreach ($all_bundles as $bundle_type => $type_bundles) {
foreach ($type_bundles as $fld_name => $instance) {
//getFieldDefinitions($entity_type_id, $bundle)
if ($fld_name == $field_name) {
$instances[] = $instance;
}
}
}
return $instances;
}
Functions
Name | Description |
---|---|
ip_geoloc_build_google_api_url | Builds the javascript maps api url based on authentication method. |
ip_geoloc_ctools_plugin_directory | Implements hook_ctools_plugin_directory(). |
ip_geoloc_debug | Special debug function: messages selected user names only. |
ip_geoloc_debug_flag | Returns whether debug is on for the current user. |
ip_geoloc_flatten_google_address | Fleshes out the $ip_geoloc_address array. |
ip_geoloc_format_address | Poor man's address formatter. |
ip_geoloc_form_views_ui_edit_display_form_alter | Implements hook_form_FORMID_alter(). |
ip_geoloc_get_display_fields | Implements hook_get_display_fields(). |
ip_geoloc_get_field_instances | Implements hook_get_field_instances(). |
ip_geoloc_get_font_icon_libs | Returns an array of libraries as entered on the config page. |
ip_geoloc_help | Implements hook_help(). |
ip_geoloc_is_first_click | Returns whether this was the first click of the session. |
ip_geoloc_is_in_range | Determines if a value is within the supplied numeric or alphabetical range. String comparison is based on the ASCII/UTF8 order, so is case-sensitive. |
ip_geoloc_js_info | Implements hook_js_info(). |
ip_geoloc_library_info_alter | Function ip_geoloc_library_alter(&$libraries, $module) {. |
ip_geoloc_log_errors | Log errors via the watchdog. |
ip_geoloc_marker_colors | Return available marker colors for use in a select drop-down. |
ip_geoloc_marker_dimensions | Return the height and width of the markers in the selected set. |
ip_geoloc_marker_directory | Returns the path to the configured marker directory. |
ip_geoloc_openlayers_marker_layers | Return available OpenLayers marker layers for use in a select drop-down. |
ip_geoloc_page_attachments | Pending doc. |
ip_geoloc_pretty_print | Print the location array nicely. |
ip_geoloc_range_widget_validate | FAPI validation of a range element. |
ip_geoloc_same_path | Returns true if the previous page was reloaded. |
ip_geoloc_smart_ip_get_location_alter | Implements hook_smart_ip_get_location_alter(). |
ip_geoloc_statistics_api | Implements hook_statistics_api(). |
ip_geoloc_use_geoip_api_if_enabled | Module GeoIP API does not expose a hook, but it does expose an API. |
ip_geoloc_use_smart_ip_if_enabled | Use Smart IP (if enabled) to retrieve lat/long and address info. |
ip_geoloc_views_api | Implements hook_views_api(). |
ip_geoloc_views_post_render | Implements hook_views_post_render(). |
ip_geoloc_views_pre_render | Implements hook_views_pre_render(). |
template_preprocess_views_view_ip_geoloc_leaflet | Implements hook_preprocess_HOOK() for theme_ip_geoloc_leaflet(). |
_ip_geoloc_check_location | Return whether a the visitor's location is due for an update. |
_ip_geoloc_extract_value | Extracts a Views argument value from the supplied string. |
_ip_geoloc_reinit_location | Reinitialises the supplied location array. |
_ip_geoloc_reverse_geocode_timeout | Handle timeout of the Google Maps reverse-geocode callback, if enabled. |