Index: smart_ip.install
===================================================================
--- smart_ip.install	(revision 207)
+++ smart_ip.install	(working copy)
@@ -146,6 +146,7 @@
   variable_set('smart_ip_blocks_csv_pointer', 0);
   variable_set('smart_ip_blocks_csv_last_pointer', 0);
   variable_set('smart_ip_roles_to_geolocate', array(DRUPAL_AUTHENTICATED_RID));
+  variable_set('smart_ip_hours_between_fresh_geolocation', 24);
 }
 
 /**
@@ -188,4 +189,5 @@
   variable_del('smart_ip_debug');
   variable_del('smart_ip_test_ip_address');
   variable_del('smart_ip_roles_to_geolocate');
+  variable_del('smart_ip_hours_between_fresh_geolocation');
 }
\ No newline at end of file
Index: smart_ip.module
===================================================================
--- smart_ip.module	(revision 207)
+++ smart_ip.module	(working copy)
@@ -3,18 +3,18 @@
 
 /**
  * @file
- * Determines country, geo location (longitude/latitude), region, city and 
+ * Determines country, geo location (longitude/latitude), region, city and
  * postal code of the user, based on IP address
  *
  * This module uses the IP address that a user is connected from to extract
- * the location information where the user is located.  This method is not 
- * foolproof, because a user may connect through an anonymizing proxy, or may 
+ * the location information where the user is located.  This method is not
+ * foolproof, because a user may connect through an anonymizing proxy, or may
  * be in an unusual case, such as getting service from a neighboring country,
  * or using an IP block leased from a company in another country.
  * Additionaly, users accessing a server on a local network may be using
  * an IP that is not assigned to any country (e.g. 192.168.x.x).
  *
- * @author Roland Michael dela Peña.
+ * @author Roland Michael dela Peï¿½a.
  * @version $Id: smart_ip.module,v 1.1.4.3 2010/12/16 11:40:06 arpeggio Exp $
  */
 define('SMART_IP_LOCATION_CSV', 'GeoLiteCity-Location.csv');
@@ -29,41 +29,41 @@
 function smart_ip_help($path, $arg) {
   switch ($path) {
     case 'admin/help#smart_ip':
-      return 
-      '<p>' . 
-      t("Smart IP identify visitor's geographical location (longitude/latitude), country, 
-      region, city and postal code based on the IP address of the user. These information 
-      will be stored at session variable (&#36;_SESSION) with array key 'smart_ip' and Drupal 
-      &#36;user->data object with array key 'geoip_location' of the user but optionally it can 
-      be disabled (by role) at Smart IP admin page. Other modules can use the function 
-      smart_ip_get_location(&#36;ip_address) that returns an array containing the visitor's 
-      ISO 3166 2-character country code, longitude, latitude, region, city and postal code. It 
-      provides a feature for you to perform your own IP lookup and admin spoofing of an arbitrary IP 
-      for testing purposes.") . '</p><p>' .       
-      t("Maxmind's database is the source of Smart IP database that makes the association between IP 
-      address and geographical location (longitude/latitude), region, city and postal code. It can 
-      be found at !maxmind it has two versions: a very accurate 
-      and up to date payable version and a not quite accurate free lite version. Smart IP downloads 
-      and process the CSV files (GeoLiteCity-Location.csv and GeoLiteCity-Blocks.csv) to store to 
-      Smart IP database. An optional once a month (Maxmind updates its database every first day of 
-      a month) automatic update of the Smart IP database is provided or it can be manually updated 
-      at Smart IP admin page. The database of Maxmind is very huge, the two CSV files size is about 
-      150MB and the size when stored to SQL database is about 450MB with almost 5 million rows and 
-      about 600MB additional database space for temporary working table for Smart IP database 
-      update. The process of downloading the archived CSV files from Maxmind's server, extracting 
-      the downloaded zip file, parsing the CSV files and storing to the database will took more or 
-      less eight hours (depends on server's speed). It uses the batch system process. If interrupted 
-      by an unexpected error, it can recover from where it stopped or the administrator can manually 
+      return
+      '<p>' .
+      t("Smart IP identify visitor's geographical location (longitude/latitude), country,
+      region, city and postal code based on the IP address of the user. These information
+      will be stored at session variable (&#36;_SESSION) with array key 'smart_ip' and Drupal
+      &#36;user->data object with array key 'geoip_location' of the user but optionally it can
+      be disabled (by role) at Smart IP admin page. Other modules can use the function
+      smart_ip_get_location(&#36;ip_address) that returns an array containing the visitor's
+      ISO 3166 2-character country code, longitude, latitude, region, city and postal code. It
+      provides a feature for you to perform your own IP lookup and admin spoofing of an arbitrary IP
+      for testing purposes.") . '</p><p>' .
+      t("Maxmind's database is the source of Smart IP database that makes the association between IP
+      address and geographical location (longitude/latitude), region, city and postal code. It can
+      be found at !maxmind it has two versions: a very accurate
+      and up to date payable version and a not quite accurate free lite version. Smart IP downloads
+      and process the CSV files (GeoLiteCity-Location.csv and GeoLiteCity-Blocks.csv) to store to
+      Smart IP database. An optional once a month (Maxmind updates its database every first day of
+      a month) automatic update of the Smart IP database is provided or it can be manually updated
+      at Smart IP admin page. The database of Maxmind is very huge, the two CSV files size is about
+      150MB and the size when stored to SQL database is about 450MB with almost 5 million rows and
+      about 600MB additional database space for temporary working table for Smart IP database
+      update. The process of downloading the archived CSV files from Maxmind's server, extracting
+      the downloaded zip file, parsing the CSV files and storing to the database will took more or
+      less eight hours (depends on server's speed). It uses the batch system process. If interrupted
+      by an unexpected error, it can recover from where it stopped or the administrator can manually
       continue the broken process at Smart IP admin page.", array(
         '!maxmind' => l('http://www.maxmind.com/app/geolitecountry', 'http://www.maxmind.com/app/geolitecountry'),
-      )) . '</p><p>' . 
-      t("Another source of Smart IP is the IPInfoDB.com service which also uses Maxmind's database, in 
-      this case IPInfoDB.com will handle database resource load instead of your server's database. 
-      By default the use of IPInfoDB.com service as source is disabled. If IPInfoDB.com is desired to  
+      )) . '</p><p>' .
+      t("Another source of Smart IP is the IPInfoDB.com service which also uses Maxmind's database, in
+      this case IPInfoDB.com will handle database resource load instead of your server's database.
+      By default the use of IPInfoDB.com service as source is disabled. If IPInfoDB.com is desired to
       handle database resource load, it can be configured at Smart IP admin page settings..")
-      . '</p><div class="messages"><p>' . 
-      t('Note: The Smart IP database is empty upon initial installation of this module. Either manually 
-      update the Smart IP database at admin page or wait for the cron to run and update Smart IP 
+      . '</p><div class="messages"><p>' .
+      t('Note: The Smart IP database is empty upon initial installation of this module. Either manually
+      update the Smart IP database at admin page or wait for the cron to run and update Smart IP
       database automatically for you.') . '</p></div>';
       break;
   }
@@ -82,15 +82,9 @@
  * Allows geo location of anonymous users
  */
 function smart_ip_init(){
-  // Save a database hit. If there's already session data, don't check if we need to 
   // geolocate they anonymous role
-  if(empty($_SESSION['smart_ip']['location'])) {
-    $roles_to_geo_locate = variable_get('smart_ip_roles_to_geolocate', array(DRUPAL_AUTHENTICATED_RID));
-    if(in_array(DRUPAL_ANONYMOUS_RID, $roles_to_geo_locate) ){
-      global $user;
-      smart_ip_set_location_data($user, smart_ip_get_location(ip_address()));
-    }
-  }
+  global $user;
+  smart_ip_geolocation_init($user, false);
 }
 
 /**
@@ -136,7 +130,7 @@
       ),
     );
     // Set the db update access flag to allow cron to execute db update
-    $_smart_ip_db_update_access = TRUE; 
+    $_smart_ip_db_update_access = TRUE;
     drupal_execute('smart_ip_cron_db_update', $form_state);
   }
   else {
@@ -192,8 +186,59 @@
  * Detects IP and geo location upon user login.
  */
 function smart_ip_user($op, &$edit, &$account, $category = NULL) {
+  // Ensure the user information always reflects the state of the user
   switch ($op) {
     case 'login':
+      smart_ip_geolocation_init($account, true);
+      break;
+    case 'logout':
+      smart_ip_geolocation_init($account, true);
+      break;
+  }
+
+}
+
+/******************************************************************************
+ * Module API                                                                 *
+ ******************************************************************************/
+/**
+ * Centralized access point to geolocation functionality
+ *
+ */
+function smart_ip_geolocation_init($user, $override_hours_between_fresh_gelocation = false){
+  $roles_to_geolocate = variable_get('smart_ip_roles_to_geolocate', array(DRUPAL_AUTHENTICATED_RID));
+  $role_requires_geolocation = false;
+
+  // Allow for anonymous geolocation
+  if($user->uid == 0 && in_array(DRUPAL_ANONYMOUS_RID, $roles_to_geolocate) ){
+    $role_requires_geolocation = true;
+  } else {
+    // Check if any roles enable geolocation
+    foreach($roles_to_geolocate as $rid){
+      if(array_key_exists($rid, $user->roles)){
+        $role_requires_geolocation = true;
+        break;
+      }
+    }
+  }
+
+  if($role_requires_geolocation){
+    $fresh_geolocation_required = false;
+    if( $override_hours_between_fresh_gelocation || empty($_SESSION['smart_ip']['location']['timestamp']) ){
+      $fresh_geolocation_required = true;
+    } else {
+      $hours_between_fresh_geolocation = variable_get('smart_ip_hours_between_fresh_geolocation', 24);
+      if($hours_between_fresh_geolocation > 0){
+        // Setting is in hours, timestamps are in seconds
+        $seconds_between_fresh_geolocation = $hours_between_fresh_geolocation * 60 * 60; // Hours * Minutes * Seconds
+        $geolocation_timestamp_expiration = $_SESSION['smart_ip']['location']['timestamp'] + $seconds_between_fresh_geolocation;
+        if( $geolocation_timestamp_expiration < time() ){
+          $fresh_geolocation_required = true;
+        }
+      }
+    }
+
+    if($fresh_geolocation_required){
       // Now check to see if this user has "administer smart_ip" permission
       // and if debug mode set.  If both are TRUE, use debug information
       // instead of real information
@@ -202,7 +247,7 @@
         $location = smart_ip_get_location($ip);
         if ($location) {
           drupal_set_message(t('Using debug IP: %ip / Country: %country / Region: %region / City: %city / Postal code: %zip / Longitude: %long / Latitude: %lat', array(
-            '%ip'      => $ip, 
+            '%ip'      => $ip,
             '%country' => $location['country'],
             '%region'  => $location['region'],
             '%city'    => $location['city'],
@@ -212,24 +257,18 @@
           )));
           $_SESSION['smart_ip']['location'] = $location;
         }
+      } else {
+        $ip = ip_address();
+        $location = smart_ip_get_location($ip);
       }
-      else {
-        $roles_to_geo_locate = variable_get('smart_ip_roles_to_geolocate', array(DRUPAL_AUTHENTICATED_RID));
-        foreach($roles_to_geo_locate as $rid){
-          if(array_key_exists($rid, $account->roles)){
-            smart_ip_set_location_data($account, smart_ip_get_location(ip_address()));
-            break;
-          }
-        }
-      }
-      break;
+      smart_ip_set_location_data($user, $location, $ip);
+      // Allow any module that wants to react the opportunity
+      module_invoke_all('smart_ip_geolocation', 'presave', $user, $location, $ip);
+    }
   }
- 
 }
 
-/******************************************************************************
- * Module API                                                                 *
- ******************************************************************************/
+
 /**
  * Get the geo location from the IP address
  *
@@ -298,9 +337,9 @@
 function smart_ip_set_location_data($account, $location) {
   global $user;
   if($account->uid == $user->uid){
-    smart_ip_set_session_data($location);
+    smart_ip_set_user_data($account, $location);
   }
-  smart_ip_set_user_data($account, $location);
+  smart_ip_set_session_data($location);
 }
 
 /**
@@ -332,7 +371,7 @@
  */
 function smart_ip_get_csv_source_filename($bypass_fallback = FALSE) {
   static $maxmind_csv;
-  
+
   if (empty($maxmind_csv)) {
     $maxmind_csv = check_url('http://geolite.maxmind.com/download/geoip/database/GeoLiteCity_CSV/GeoLiteCity_' . format_date(time(), 'custom', 'Ym') . '01.zip');
     // Check if Maxmind CSV file not updated, then fallback to the previous month
@@ -348,7 +387,7 @@
  */
 function smart_ip_get_region_static($country_code, $region_code) {
   static $region;
-  
+
   if (!isset($region[$country_code][$region_code])) {
     module_load_include('inc', 'smart_ip', 'includes/smart_ip.region_lookup');
     $region[$country_code][$region_code] = smart_ip_get_region($country_code, $region_code);
Index: includes/smart_ip.admin.inc
===================================================================
--- includes/smart_ip.admin.inc	(revision 207)
+++ includes/smart_ip.admin.inc	(working copy)
@@ -113,6 +113,12 @@
     '#options'       => user_roles(),
     '#description'   => t('Select the roles you wish to geolocate. Note that selecting the anonymous role will add substantial overhead.')
   );
+  $form['smart_ip_preferences']['smart_ip_hours_between_fresh_geolocation'] = array(
+    '#type'           => 'textfield',
+    '#title'          => 'Hours between fresh geolocation',
+    '#default_value'  => variable_get('smart_ip_hours_between_fresh_geolocation', 24),
+    '#description'   => t('This options allows you to set a number of hours an ip\'s geocode remains valid. After a set amount of hours, fresh geocode will be fired. This is useful for statistics and long running sessions. Set to 0 to disable this feature.'),
+  );
 
   // Form to turn on debugging
   $form['smart_ip_preferences']['smart_ip_debug'] = array(
@@ -198,6 +204,9 @@
   if ($form_state['values']['smart_ip_ipinfodb_key'] != $ipinfodb_key) {
     variable_set('smart_ip_correct_ipinfodb_key', FALSE);
   }
+  if( $form_state['values']['smart_ip_hours_between_fresh_geolocation'] < 0 ){
+    form_set_error('smart_ip_hours_between_fresh_geolocation', t('Hours between fresh geolocation must be greater than or equal to zero.'));
+  }
 }
 
 /**
Index: modules/smart_ip_statistics/smart_ip_statistics.info
===================================================================
--- modules/smart_ip_statistics/smart_ip_statistics.info	(revision 0)
+++ modules/smart_ip_statistics/smart_ip_statistics.info	(revision 0)
@@ -0,0 +1,8 @@
+name = Smart IP Statistics
+description = Allows you to store and create reports about the data gathered from Smart IP
+package = Location
+
+core = 6.x
+php = 5.0
+dependencies[] = smart_ip
+dependencies[] = views
Index: modules/smart_ip_statistics/smart_ip_statistics.install
===================================================================
--- modules/smart_ip_statistics/smart_ip_statistics.install	(revision 0)
+++ modules/smart_ip_statistics/smart_ip_statistics.install	(revision 0)
@@ -0,0 +1,256 @@
+<?php
+// $Id: smart_ip.install,v 1.1.4.1 2010/12/15 22:00:08 arpeggio Exp $
+
+/**
+ * Smart IP Details Statistics schema definition array.
+ *
+ * @return
+ *   A Schema API table definition array.
+ */
+function smart_ip_statistics_detailed_schema_array(){
+  return array(
+    'description' => 'Stores detailed logs of each geocodes.',
+    'fields' => array(
+      'log_id' => array(
+        'description' => 'Unique ID of the log entry',
+        'type' => 'serial',
+        'not null' => TRUE,
+      ),
+      'geoip_id' => array(
+        'description' => 'ID assigned by maxmind',
+        'type'        => 'int',
+        'size'        => 'big',
+        'not null'    => TRUE,
+      ),
+      'ip_ref' => array(
+        'description' => 'Lowest IP Address in Range',
+        'type'        => 'int',
+        'size'        => 'big',
+        'not null'    => TRUE,
+      ),
+      'country' => array(
+        'description' => 'Country Name as Reported',
+        'type'        => 'varchar',
+        'length'      => 128,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'country_code' => array(
+        'description' => 'ISO 3166 2-Character Country Code',
+        'type'        => 'varchar',
+        'length'      => 3,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'region' => array(
+        'description' => 'Region Name as Reported',
+        'type'        => 'varchar',
+        'length'      => 128,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'region_code' => array(
+        'description' => '2-Character Region Code',
+        'type'        => 'varchar',
+        'length'      => 3,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'city' => array(
+        'description' => 'Name of city targeted by the coordinates',
+        'type'        => 'varchar',
+        'length'      => 128,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'zip' => array(
+        'description' => 'Postal code of targeted by the coordinates',
+        'type'        => 'varchar',
+        'length'      => 8,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'latitude' => array(
+        'description' => 'Latitude',
+        'type'        => 'float',
+        'size'        => 'big',
+        'not null'    => TRUE,
+      ),
+      'longitude' => array(
+        'description' => 'Longitude',
+        'type'        => 'float',
+        'size'        => 'big',
+        'not null'    => TRUE,
+      ),
+      'ip' => array(
+        'description' => 'The IP at the time of the log entry',
+        'type'        => 'varchar',
+        'length'      => '128',
+        'not null'    => TRUE, // IPv6 Calls for 45 if you take into account tunneling. Added room for port, and future changes
+        'default'     => '',
+      ),
+      'timestamp' => array(
+        'description' => 'Time of the geoencode',
+        'type'        => 'int',
+        'not null'    => TRUE,
+      ),
+      'path' => array(
+        'description' => 'Path at time of encoding',
+        'type'        => 'varchar',
+        'length'      => 128,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'uid' => array(
+        'description' => 'User ID',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0
+      )
+    ),
+    'primary key' => array('log_id'),
+    'indexes' => array(
+      'country_code' => array('country_code'),
+      'region' => array('region'),
+      'city' => array('city'),
+      'zip' => array('zip'),
+      'latitude' => array('latitude'),
+      'longitude' => array('longitude'),
+      'timestamp' => array('timestamp'),
+      'path' => array('path'),
+    ),
+  );
+}
+
+/**
+ * Smart IP Summary Statistics schema definition array.
+ *
+ * @return
+ *   A Schema API table definition array.
+ */
+function smart_ip_statistics_summary_schema_array(){
+  return array(
+    'description' => 'Association between IP range and Geo location',
+    'fields' => array(
+      'statistic_id' => array(
+        'description' => 'A unique ID',
+        'type' => 'serial',
+        'not null' => TRUE,
+      ),
+      'country' => array(
+        'description' => 'Country Name as Reported',
+        'type'        => 'varchar',
+        'length'      => 128,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'country_code' => array(
+        'description' => 'ISO 3166 2-Character Country Code',
+        'type'        => 'varchar',
+        'length'      => 3,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'region' => array(
+        'description' => 'Region Name as Reported',
+        'type'        => 'varchar',
+        'length'      => 128,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'region_code' => array(
+        'description' => '2-Character Region Code',
+        'type'        => 'varchar',
+        'length'      => 3,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'city' => array(
+        'description' => 'Name of city targeted by the coordinates',
+        'type'        => 'varchar',
+        'length'      => 128,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'zip' => array(
+        'description' => 'Postal code of targeted by the coordinates',
+        'type'        => 'varchar',
+        'length'      => 8,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'latitude' => array(
+        'description' => 'Latitude',
+        'type'        => 'float',
+        'size'        => 'big',
+        'not null'    => TRUE,
+      ),
+      'longitude' => array(
+        'description' => 'Longitude',
+        'type'        => 'float',
+        'size'        => 'big',
+        'not null'    => TRUE,
+      ),
+      'year' => array(
+        'description' => 'Year',
+        'type' => 'int',
+        'not null' => TRUE
+      ),
+      'month' => array(
+        'description' => 'Month',
+        'type' => 'int',
+        'not null' => TRUE
+      ),
+      'hits' => array(
+        'description' => 'Total hits at this location',
+        'type'        => 'int',
+        'size'        => 'big',
+        'not null'    => TRUE,
+        'default'     => 0
+      ),
+    ),
+    'primary key' => array('statistic_id'),
+    'indexes' => array(
+      'country_code' => array('country_code'),
+      'region' => array('region'),
+      'city' => array('city'),
+      'zip' => array('zip'),
+      'latitude' => array('latitude'),
+      'longitude' => array('longitude'),
+      'hits' => array('hits'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_schema().
+ */
+function smart_ip_statistics_schema() {
+  $schema['smart_ip_detailed_statistics'] = smart_ip_statistics_detailed_schema_array();
+  $schema['smart_ip_summary_statistics'] = smart_ip_statistics_summary_schema_array();
+  return $schema;
+}
+
+
+/**
+ * Implements hook_install().
+ *
+ * Creates database tables needed by this module.
+ */
+function smart_ip_statistics_install() {
+  /* Create tables */
+  drupal_install_schema('smart_ip_statistics');
+  variable_set('smart_ip_statistics_days_to_keep_detailed_statistics', 365);
+}
+
+/**
+ * Implements hook_uninstall().
+ *
+ * Removes all tables and variables inserted into the
+ * database by this module.
+ */
+function smart_ip_statistics_uninstall() {
+  /* Remove all database tables created by this module */
+  drupal_uninstall_schema('smart_ip_statistics');
+  variable_del('smart_ip_statistics_days_to_keep_detailed_statistics');
+}
\ No newline at end of file
Index: modules/smart_ip_statistics/smart_ip_statistics.module
===================================================================
--- modules/smart_ip_statistics/smart_ip_statistics.module	(revision 0)
+++ modules/smart_ip_statistics/smart_ip_statistics.module	(revision 0)
@@ -0,0 +1,142 @@
+<?php
+/**
+ * @file
+ * Determines country, geo location (longitude/latitude), region, city and
+ * postal code of the user, based on IP address
+ *
+ * This module uses the IP address that a user is connected from to extract
+ * the location information where the user is located.  This method is not
+ * foolproof, because a user may connect through an anonymizing proxy, or may
+ * be in an unusual case, such as getting service from a neighboring country,
+ * or using an IP block leased from a company in another country.
+ * Additionaly, users accessing a server on a local network may be using
+ * an IP that is not assigned to any country (e.g. 192.168.x.x).
+ *
+ * @author Lance Gliser.
+ */
+
+/******************************************************************************
+ * Drupal Hooks                                                               *
+ ******************************************************************************/
+ /**
+ * Implements hook_help().
+ */
+/*
+function smart_ip_statistics_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#smart_ip_statistics':
+      return '';
+      break;
+  }
+}
+*/
+
+/**
+ * Implements hook_permission().
+ */
+function smart_ip_statistics_perm() {
+  return array(
+    'administer smart_ip_statistics'
+    ,'view smart_ip reports'
+  );
+}
+
+/**
+ * Implements hook_cron().
+ *
+ * Updates the Smart IP database automatically on one month periodic basis.
+ */
+function smart_ip_statistics_cron() {
+  // Clear out records older than the allowed date from the details table
+  $expiration_seconds = variable_get('smart_ip_statistics_days_to_keep_detailed_statistics', 365) * 24 * 60 * 60;
+  if($expiration_seconds > 0){
+    $query =
+      "DELETE FROM smart_ip_detailed_statistics
+      WHERE TIMESTAMP < %d;";
+    $expiration_timestamp = time() - $expiration_seconds;
+    db_query($query, $expiration_timestamp);
+  }
+}
+
+/**
+ * Implements hook_menu().
+ *
+ * Called when Drupal is building menus.  Cache parameter lets module know
+ * if Drupal intends to cache menu or not - different results may be
+ * returned for either case.
+ *
+ * @return
+ *   An array with the menu path, callback, and parameters.
+ */
+function smart_ip_statistics_menu() {
+  $items = array();
+
+  $items['admin/settings/smart_ip_statistics'] = array(
+    'title'            => t('Smart IP Statistics'),
+    'description'      => t('Configure the Smart IP Statistics settings'),
+    'access arguments' => array('administer smart_ip_statistics'),
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('smart_ip_statistics_admin_settings'),
+    'file'             => 'includes/admin.inc',
+  );
+  $items['admin/reports/smart_ip_statistics'] = array(
+    'title'            => t('Smart IP Statistics'),
+    'description'      => t("View data about your site's visitor's locations."),
+    'access arguments' => array('view smart_ip reports'),
+    'page callback'    => 'smart_ip_statistics_reports_summary',
+    'file' => 'includes/statistics.inc',
+    'type' => MENU_NORMAL_ITEM,
+  );
+    $items['admin/reports/smart_ip_statistics/summary'] = array(
+      'title'            => t('Summary'),
+      'description'      => t("View summary data about your site's visitor's locations."),
+      'access arguments' => array('view smart_ip reports'),
+      'page callback'    => 'smart_ip_statistics_reports_summary',
+      'file' => 'includes/statistics.inc',
+      'type' => MENU_DEFAULT_LOCAL_TASK,
+      'weight' => 0,
+    );
+    $items['admin/reports/smart_ip_statistics/detailed'] = array(
+      'title'            => t('Details'),
+      'description'      => t("View detailed data about your site's visitor's locations."),
+      'access arguments' => array('view smart_ip reports'),
+      'page callback'    => 'smart_ip_statistics_reports_detailed',
+      'file' => 'includes/statistics.inc',
+      'type' => MENU_LOCAL_TASK,
+      'weight' => 1,
+    );
+
+  return $items;
+}
+
+/******************************************************************************
+ * Other Modules' Hooks
+ ******************************************************************************/
+
+function smart_ip_statistics_smart_ip_geolocation($op, $user, $location, $ip){
+  switch($op){
+    case 'presave':
+      module_load_include('inc', 'smart_ip_statistics', 'includes/statistics');
+      smart_ip_statistics_log_geolocate($user, $location, $ip, $_GET["q"]);
+    break;
+  }
+}
+
+/**
+ * Views Integration Hook
+ */
+function smart_ip_statistics_views_api() {
+  return array(
+    'api' => 2,
+    'path' => drupal_get_path('module', 'smart_ip_statistics') . '/views',
+  );
+}
+
+/******************************************************************************
+ * Module API
+ ******************************************************************************/
+
+
+/******************************************************************************
+ * Helper Functions
+ ******************************************************************************/
\ No newline at end of file
Index: modules/smart_ip_statistics/includes/admin.inc
===================================================================
--- modules/smart_ip_statistics/includes/admin.inc	(revision 0)
+++ modules/smart_ip_statistics/includes/admin.inc	(revision 0)
@@ -0,0 +1,43 @@
+<?php
+// $Id: smart_ip.admin.inc,v 1.1.4.2 2010/12/16 11:40:06 arpeggio Exp $
+
+/**
+ * @file
+ * Admin interface callbacks/handlers to configure Smart IP Statistics.
+ */
+
+/**
+ * Smart IP Statistics administration settings.
+ *
+ * @return
+ *   Forms for administrator to set configuration options.
+ */
+function smart_ip_statistics_admin_settings(&$form_state) {
+  // Define submit handler function
+  $form['#submit'][] = 'smart_ip_statistics_admin_settings_submit';
+  $form['#validate'][] = 'smart_ip_statistics_admin_settings_validate';
+
+  $form['smart_ip_statistics']['smart_ip_statistics_days_to_keep_detailed_statistics'] = array(
+    '#type'           => 'textfield',
+    '#title'          => 'Days to keep detailed statistics',
+    '#default_value'  => variable_get('smart_ip_statistics_days_to_keep_detailed_statistics', 365),
+    '#description'   => t('This option allows you to remove detailed records from your statistics table if they are more than a set number of days old. You may Set this to 0 to disable this feature, but it is not recommended.'),
+  );
+
+  return system_settings_form($form);
+}
+
+/**
+ * Validates settings page
+ */
+function smart_ip_statistics_admin_settings_validate($form, &$form_state) {
+  if( $form_state['values']['smart_ip_days_to_keep_detailed_statistics'] < 0 ){
+    form_set_error('smart_ip_days_to_keep_detailed_statistics', t('Days to keep detailed statistics must be greater than or equal to zero.'));
+  }
+}
+
+/**
+ * Process Forms submitted by IP to Country administration page
+ */
+function smart_ip_statistics_admin_settings_submit($form, &$form_state) {
+}
\ No newline at end of file
Index: modules/smart_ip_statistics/includes/statistics.inc
===================================================================
--- modules/smart_ip_statistics/includes/statistics.inc	(revision 0)
+++ modules/smart_ip_statistics/includes/statistics.inc	(revision 0)
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * Centralized access point for logging a new geolocate
+ */
+function smart_ip_statistics_log_geolocate($account, $location, $ip, $path = ''){
+  //drupal_set_message('smart_ip_statistics_log_geolocate');
+  smart_ip_statistics_log_geolocate_detailed_data($account, $location, $ip, $path);
+  smart_ip_statistics_log_geolocate_summary_data($location);
+}
+
+function smart_ip_statistics_log_geolocate_detailed_data($account, $location, $ip, $path = ''){
+  $query = "
+  INSERT INTO {smart_ip_detailed_statistics}
+    (geoip_id, ip_ref, country, country_code, region, region_code, city, zip, latitude, longitude, ip, timestamp, path, uid)
+  VALUES
+    (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %f, %f, '%s', %d, '%s', %d);";
+  db_query($query
+    ,$location['geoip_id']
+    ,$location['ip_ref']
+    ,(!empty($location['country'])? $location['country'] : '' )
+    ,(!empty($location['country_code'])? $location['country_code'] : '' )
+    ,(!empty($location['region'])? $location['region'] : '' )
+    ,(!empty($location['region_code'])? $location['region_code'] : '' )
+    ,(!empty($location['city'])? $location['city'] : '' )
+    ,(!empty($location['zip'])? $location['zip'] : '' )
+    ,$location['latitude']
+    ,$location['longitude']
+    ,$ip
+    ,time()
+    ,$path
+    ,$account->uid
+  );
+}
+
+function smart_ip_statistics_log_geolocate_summary_data($location){
+  // We're unable to really count on region, or zip data, even country could be somewhat inaccurate.
+  // The most accurate data we've got is latitude longitude, but keeping summary data at that level...
+  // Well, it's just too accurate. We'll be keeping logs accurate to one decimal place
+  // According to http://en.wikipedia.org/wiki/Decimal_degrees, that's roughly accurate to within 11.1km, or 6.9~ miles
+  // Since we'll round up or down, the max distance off is really 3.45 miles off. Pretty good.
+
+  if(
+    !is_numeric($location['latitude'])
+    || !is_numeric($location['latitude'])
+  ){
+    return NULL;
+  }
+
+  $latitude = round($location['latitude'], 1);
+  $longitude = round($location['longitude'], 1);
+  $today = time();
+  $year = date('Y', $today);
+  $month = date('m', $today);
+
+  $query = "
+  SELECT statistic_id
+  FROM smart_ip_summary_statistics
+  WHERE latitude = %f
+  AND longitude = %f;";
+  $results = db_query($query, $latitude, $longitude);
+  if($result = db_fetch_array($results)){
+    $query = "
+    UPDATE smart_ip_summary_statistics
+      SET
+      country = '%s'
+      ,country_code = '%s'
+      ,region = '%s'
+      ,region_code = '%s'
+      ,city = '%s'
+      ,zip = '%s'
+      ,year = %d
+      ,month = %d
+      ,hits = hits + 1
+      WHERE
+      statistic_id = %d;";
+    db_query($query, $location['country'], $location['country_code'], $location['region'], $location['region_code'], $location['city'], $location['zip'], $year, $month, $result['statistic_id']);
+  } else {
+    $query =
+    "INSERT INTO smart_ip_summary_statistics
+      (country
+      ,country_code
+      ,region
+      ,region_code
+      ,city
+      ,zip
+      ,latitude
+      ,longitude
+      ,year
+      ,month
+      ,hits
+      )
+      VALUES
+      ('%s', '%s', '%s', '%s', '%s', '%s', %f, %f, %d, %d, 1);";
+    db_query($query, $location['country'], $location['country_code'], $location['region'], $location['region_code'], $location['city'], $location['zip'], $latitude, $longitude, $year, $month);
+  }
+}
+
+/****************
+ * Report Pages
+ ***************/
+
+function smart_ip_statistics_reports_summary(){
+  $output = '';
+  $output .= views_embed_view('smart_ip_summary_statistics', 'default');
+
+  return $output;
+}
+
+function smart_ip_statistics_reports_detailed(){
+  $output = '';
+  $output .= views_embed_view('smart_ip_detailed_statistics', 'default');
+
+  return $output;
+}
\ No newline at end of file
Index: modules/smart_ip_statistics/views/smart_ip_statistics.views.inc
===================================================================
--- modules/smart_ip_statistics/views/smart_ip_statistics.views.inc	(revision 0)
+++ modules/smart_ip_statistics/views/smart_ip_statistics.views.inc	(revision 0)
@@ -0,0 +1,272 @@
+<?php
+function smart_ip_statistics_views_data() {
+  $data = array(
+    'smart_ip_summary_statistics' => array(
+      'table' => array(
+        'group' => t('Smart IP')
+        ,'base' => array(
+          'field' => 'statistic_id'
+          ,'title' => t('Smart IP Summary Data')
+          ,'help' => t('Summary data shows your visitor\'s activity over the long term.')
+        )
+      )
+      ,'country' => array(
+       'title' => t('Country')
+        ,'help' => t('The full name of the country.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'country_code' => array(
+       'title' => t('Country code')
+        ,'help' => t('The two digit code of the country.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'region' => array(
+       'title' => t('Region')
+        ,'help' => t('The full name of the Region. This will often be the state or province.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'region_code' => array(
+       'title' => t('Region Code')
+        ,'help' => t('An identifying code for the region.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'city' => array(
+       'title' => t('City')
+        ,'help' => t('The name of the city. This data is not always available or reliable.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'zip' => array(
+       'title' => t('Zip')
+        ,'help' => t('The zip or postal code. This data is not always available or reliable.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'latitude' => array(
+       'title' => t('Latitude')
+        ,'help' => t('The full latitude as encoded.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'longitude' => array(
+       'title' => t('Longitude')
+        ,'help' => t('The full longitude as encoded.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'year' => array(
+       'title' => t('Year')
+        ,'help' => t('The year of the summary record.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'month' => array(
+       'title' => t('Month')
+        ,'help' => t('The month of the summary record.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'hits' => array(
+       'title' => t('Hits')
+        ,'help' => t('The number of hits for that summary record.')
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      /* Clue for getting proximity searches from location module
+      ,'distance' => array(
+        'title' => t('Distance / Proximity')
+        ,'help' => t("The distance from the selected location and either the current user or a specific location.")
+        ,'field' => array(
+          'handler' => 'location_handler_field_location_distance'
+          ,'click sortable' => TRUE
+        )
+        ,'sort' => array(
+          'handler' => 'location_handler_sort_location_distance'
+        )
+        ,'argument' => array(
+          'handler' => 'location_handler_argument_location_proximity'
+        )
+        ,'filter' => array(
+          'handler' => 'location_views_handler_filter_proximity'
+        )
+      )
+      */
+    )
+    ,'smart_ip_detailed_statistics' => array(
+      'table' => array(
+        'group' => t('Smart IP')
+        ,'base' => array(
+          'field' => 'log_id'
+          ,'title' => t('Smart IP Detailed Log Entry')
+          ,'help' => t('Detailed entries allow you to view visitor activity.')
+        )
+        ,'join' => array(
+          'users' => array(
+            'left_field' => 'uid'
+            ,'field' => 'uid'
+          )
+        )
+      )
+      ,'country' => array(
+       'title' => t('Country')
+        ,'help' => t('The full name of the country.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'country_code' => array(
+       'title' => t('Country code')
+        ,'help' => t('The two digit code of the country.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'region' => array(
+       'title' => t('Region')
+        ,'help' => t('The full name of the Region. This will often be the state or province.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'region_code' => array(
+       'title' => t('Region Code')
+        ,'help' => t('An identifying code for the region.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'city' => array(
+       'title' => t('City')
+        ,'help' => t('The name of the city. This data is not always available or reliable.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'zip' => array(
+       'title' => t('Zip')
+        ,'help' => t('The zip or postal code. This data is not always available or reliable.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'latitude' => array(
+       'title' => t('Latitude')
+        ,'help' => t('The full latitude as encoded.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'longitude' => array(
+       'title' => t('Longitude')
+        ,'help' => t('The full longitude as encoded.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+      )
+      ,'ip' => array(
+       'title' => t('IP Address')
+        ,'help' => t('The IP Address as encoded.') // The help that appears on the UI,
+        ,'field' => array('click sortable' => TRUE)
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'timestamp' => array(
+       'title' => t('Timestamp')
+        ,'help' => t('The full longitude as encoded.') // The help that appears on the UI,
+        ,'field' => array(
+          'handler' => 'views_handler_field_date'
+          ,'click sortable' => TRUE
+        )
+        ,'sort' => array('handler' => 'views_handler_sort_date')
+        ,'filter' => array('handler' => 'views_handler_filter_date')
+        ,'argument' => array('handler' => 'views_handler_argument_date')
+      )
+      ,'path' => array(
+       'title' => t('Path')
+        ,'help' => t('The page the user was visiting.') // The help that appears on the UI,
+        ,'field' => array(
+          'handler' => 'views_handler_field_url'
+          ,'click sortable' => TRUE
+        )
+        ,'sort' => array('handler' => 'views_handler_sort')
+        ,'filter' => array('handler' => 'views_handler_filter_string')
+        ,'argument' => array('handler' => 'views_handler_argument_string')
+      )
+      ,'uid' => array(
+       'title' => t('User')
+        ,'help' => t('The uid of the visitior.') // The help that appears on the UI,
+        ,'field' => array(
+          'click sortable' => TRUE
+        )
+        ,'filter' => array('handler' => 'views_handler_filter_numeric')
+        ,'argument' => array('handler' => 'views_handler_argument_numeric')
+        ,'relationship' => array(
+          'base' => 'users'
+          ,'base field' => 'uid'
+          ,'field' => 'uid'
+          ,'handler' => 'views_handler_relationship'
+          ,'label' => t('User')
+        )
+      )
+      ,'whois' => array(
+        'title' => t('Who Is')
+        ,'real field' => 'ip'
+        ,'help' => t('Takes you to a page on whois.domaintools.com that allows you to view further location details about an ip address.')
+        ,'field' => array(
+          'click sortable' => FALSE
+          ,'handler' => 'smart_ip_statistics_handler_field_whois'
+        )
+      )
+    )
+  );
+
+  return $data;
+}
+
+function smart_ip_statistics_views_handlers() {
+  return array(
+    'info' => array(
+      'path' => drupal_get_path('module', 'smart_ip_statistics') .'/views'
+    )
+    ,'handlers' => array(
+      'smart_ip_statistics_handler_field_whois' => array(
+        'parent' => 'views_handler_field',
+      )
+    )
+  );
+}
\ No newline at end of file
Index: modules/smart_ip_statistics/views/smart_ip_statistics.views_default.inc
===================================================================
--- modules/smart_ip_statistics/views/smart_ip_statistics.views_default.inc	(revision 0)
+++ modules/smart_ip_statistics/views/smart_ip_statistics.views_default.inc	(revision 0)
@@ -0,0 +1,884 @@
+<?php
+
+/**
+ * Implementation of hook_views_default_views().
+ */
+function smart_ip_statistics_views_default_views() {
+  /*
+   * View 'smart_ip_detailed_statistics'
+   */
+  $view = new view;
+  $view->name = 'smart_ip_detailed_statistics';
+  $view->description = t('Provides detailed history going back a variable number of days about vistors to your site.');
+  $view->tag = 'smart_ip_statistics';
+  $view->view_php = '';
+  $view->base_table = 'smart_ip_detailed_statistics';
+  $view->is_cacheable = FALSE;
+  $view->api_version = 2;
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->override_option('relationships', array(
+    'uid' => array(
+      'label' => t('User'),
+      'required' => 0,
+      'id' => 'uid',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'uid',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('fields', array(
+    'country' => array(
+      'label' => t('Country'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'country',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'country',
+      'relationship' => 'none',
+    ),
+    'region' => array(
+      'label' => t('Region'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'region',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'region',
+      'relationship' => 'none',
+    ),
+    'city' => array(
+      'label' => t('City'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'city',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'city',
+      'relationship' => 'none',
+    ),
+    'zip' => array(
+      'label' => t('Zip'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'zip',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'zip',
+      'relationship' => 'none',
+    ),
+    'latitude' => array(
+      'label' => t('Latitude'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'latitude',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'latitude',
+      'relationship' => 'none',
+    ),
+    'longitude' => array(
+      'label' => t('Longitude'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'longitude',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'longitude',
+      'relationship' => 'none',
+    ),
+    'path' => array(
+      'label' => t('Path'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 0,
+        'ellipsis' => 0,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'display_as_link' => 1,
+      'exclude' => 0,
+      'id' => 'path',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'path',
+      'relationship' => 'none',
+    ),
+    'timestamp' => array(
+      'label' => t('Timestamp'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'date_format' => 'large',
+      'custom_date_format' => '',
+      'exclude' => 0,
+      'id' => 'timestamp',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'timestamp',
+      'relationship' => 'none',
+    ),
+    'name' => array(
+      'label' => t('User'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'link_to_user' => 1,
+      'overwrite_anonymous' => 0,
+      'anonymous_text' => '',
+      'exclude' => 0,
+      'id' => 'name',
+      'table' => 'users',
+      'field' => 'name',
+      'relationship' => 'uid',
+    ),
+    'whois' => array(
+      'label' => t('Who Is'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'whois',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'whois',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('sorts', array(
+    'timestamp' => array(
+      'order' => 'DESC',
+      'granularity' => 'second',
+      'id' => 'timestamp',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'timestamp',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('filters', array(
+    'country' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'country_op',
+        'identifier' => 'country',
+        'label' => t('Country'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'country',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'country',
+      'relationship' => 'none',
+    ),
+    'region' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'region_op',
+        'identifier' => 'region',
+        'label' => t('Region'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'region',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'region',
+      'relationship' => 'none',
+    ),
+    'timestamp' => array(
+      'operator' => 'between',
+      'value' => array(
+        'type' => 'date',
+        'value' => '',
+        'min' => '',
+        'max' => '',
+      ),
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'timestamp_op',
+        'identifier' => 'timestamp',
+        'label' => t('Time'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'id' => 'timestamp',
+      'table' => 'smart_ip_detailed_statistics',
+      'field' => 'timestamp',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('access', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('cache', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('use_ajax', TRUE);
+  $handler->override_option('items_per_page', 20);
+  $handler->override_option('use_pager', '1');
+  $handler->override_option('style_plugin', 'table');
+  $handler->override_option('style_options', array(
+    'grouping' => '',
+    'override' => 1,
+    'sticky' => 1,
+    'order' => 'desc',
+    'columns' => array(
+      'country' => 'country',
+      'region' => 'region',
+      'city' => 'city',
+      'zip' => 'zip',
+      'latitude' => 'latitude',
+      'longitude' => 'longitude',
+      'path' => 'path',
+      'timestamp' => 'timestamp',
+      'name' => 'name',
+    ),
+    'info' => array(
+      'country' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'region' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'city' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'zip' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'latitude' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'longitude' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'path' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'timestamp' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'name' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'whois' => array(
+      'separator' => '',
+      ),
+    ),
+    'default' => '-1',
+  ));
+  $handler = $view->new_display('block', 'Block', 'block_1');
+  $handler->override_option('block_description', 'Smart IP Detailed Statistics');
+  $handler->override_option('block_caching', -1);
+  $views[$view->name] = $view;
+
+  /*
+   * View 'smart_ip_summary_statistics'
+   */
+  $view = new view;
+  $view->name = 'smart_ip_summary_statistics';
+  $view->description = 'Provides summary history of visits to your site tied to geolocation.';
+  $view->tag = 'smart_ip_statistics';
+  $view->view_php = '';
+  $view->base_table = 'smart_ip_summary_statistics';
+  $view->is_cacheable = FALSE;
+  $view->api_version = 2;
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->override_option('fields', array(
+    'country' => array(
+      'label' => t('Country'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'country',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'country',
+      'relationship' => 'none',
+    ),
+    'region' => array(
+      'label' => t('Region'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'region',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'region',
+      'relationship' => 'none',
+    ),
+    'city' => array(
+      'label' => t('City'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'city',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'city',
+      'relationship' => 'none',
+    ),
+    'zip' => array(
+      'label' => t('Zip'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'zip',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'zip',
+      'relationship' => 'none',
+    ),
+    'year' => array(
+      'label' => t('Year'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'year',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'year',
+      'relationship' => 'none',
+    ),
+    'month' => array(
+      'label' => t('Month'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'month',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'month',
+      'relationship' => 'none',
+    ),
+    'hits' => array(
+      'label' => t('Hits'),
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'hits',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'hits',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('sorts', array(
+    'hits' => array(
+      'order' => 'DESC',
+      'id' => 'hits',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'hits',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('filters', array(
+    'country' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'country_op',
+        'identifier' => 'country',
+        'label' => t('Country'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'country',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'country',
+      'override' => array(
+        'button' => 'Override',
+      ),
+      'relationship' => 'none',
+    ),
+    'region' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'region_op',
+        'identifier' => 'region',
+        'label' => t('Region'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'region',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'region',
+      'override' => array(
+        'button' => 'Override',
+      ),
+      'relationship' => 'none',
+    ),
+    'year' => array(
+      'operator' => '=',
+      'value' => array(
+        'value' => '',
+        'min' => '',
+        'max' => '',
+      ),
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'year_op',
+        'identifier' => 'year',
+        'label' => t('Year'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'id' => 'year',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'year',
+      'override' => array(
+        'button' => 'Override',
+      ),
+      'relationship' => 'none',
+    ),
+    'month' => array(
+      'operator' => '=',
+      'value' => array(
+        'value' => '',
+        'min' => '',
+        'max' => '',
+      ),
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'month_op',
+        'identifier' => 'month',
+        'label' => t('Month'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'id' => 'month',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'month',
+      'override' => array(
+        'button' => 'Override',
+      ),
+      'relationship' => 'none',
+    ),
+    'hits' => array(
+      'operator' => '>=',
+      'value' => array(
+        'value' => '',
+        'min' => '',
+        'max' => '',
+      ),
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'hits_op',
+        'identifier' => 'hits',
+        'label' => t('Hits'),
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'id' => 'hits',
+      'table' => 'smart_ip_summary_statistics',
+      'field' => 'hits',
+      'override' => array(
+        'button' => 'Override',
+      ),
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('access', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('cache', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('use_ajax', TRUE);
+  $handler->override_option('use_pager', '1');
+  $handler->override_option('style_plugin', 'table');
+  $handler->override_option('style_options', array(
+    'grouping' => '',
+    'override' => 1,
+    'sticky' => 1,
+    'order' => 'desc',
+    'columns' => array(
+      'city' => 'city',
+      'country' => 'country',
+      'hits' => 'hits',
+      'month' => 'month',
+      'region' => 'region',
+      'year' => 'year',
+      'zip' => 'zip',
+    ),
+    'info' => array(
+      'city' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'country' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'hits' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'month' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'region' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'year' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'zip' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+    ),
+    'default' => '-1',
+  ));
+  $handler = $view->new_display('block', 'Block', 'block_1');
+  $handler->override_option('block_description', 'Smart IP Summary Statistics');
+  $handler->override_option('block_caching', -1);
+  $views[$view->name] = $view;
+
+  return $views;
+}
\ No newline at end of file
Index: modules/smart_ip_statistics/views/smart_ip_statistics_handler_field_whois.inc
===================================================================
--- modules/smart_ip_statistics/views/smart_ip_statistics_handler_field_whois.inc	(revision 0)
+++ modules/smart_ip_statistics/views/smart_ip_statistics_handler_field_whois.inc	(revision 0)
@@ -0,0 +1,7 @@
+<?php
+class smart_ip_statistics_handler_field_whois extends views_handler_field {
+  function render($values){
+  	$value = $values->{$this->field_alias};
+    return l(t('Who Is'), 'http://whois.domaintools.com/' . $value);
+  }
+}
\ No newline at end of file
