Slimstat Analytics - Version 4.7.2

Version Description

  • [New] As those who have been using Slimstat for a while know, we never stop doing our good share of research and development to improve our plugin. One feature on our wishlist was to make the geolocation functionality more accurate. Specifically, users have been asking us to track not just the Country of origin, but possibly the state and city. In order to geolocate visitors, our code has been leveraging a third-party data file provided by MaxMind.com. A while ago, they launched a new data format, which improves performance and offers a way to quickly determine the city of origin. However, the new library required a higher version of PHP, and up until now we had been hesitant to adopt it, to allow more people to use our plugin, over the chance of offering this feature. Now, after spending some time combing through their code, we found a way to get the best of both worlds: by customizing their PHP library, we were able to make it work with PHP 5.3! Which means that now Slimstat is able to tell you your visitors' city of origin (and State, when applicable) right out of the box. This information is available in the Access Log report and in a new 'Top Cities' report under the Audience tab. Please note: the MaxMind data file to enable this feature is approximately 60 Mb, and for this reason this new functionality is not enabled by default. You must go to Slimstat > Settings > Tracker and turn on the corresponding option. Then go to Slimstat > Settings > Maintenance and uninstall/install the GeoLite file to download the one that contains the city data. Please feel free to contact us if you have any questions.
  • [Update] Removed backward compatibility code for those updating from a version prior to 4.2. Hopefully most of our users are using a newer version that that. If you're not, please contact our support service for instructions on how to upgrade.
  • [Update] The format used to save your settings in the database has been changed. You MUST update your premium add-ons as soon as possible, and get the version compatible with this new format, or you might notice unexpected behaviors. Please contact us if you experience difficulties updating your add-ons.
  • [Update] Cleaned up some old CSS code affecting the reports.
Download this release

Release Info

Developer coolmann
Plugin Icon 128x128 Slimstat Analytics
Version 4.7.2
Comparing to
See all releases

Code changes from version 4.7.1 to 4.7.2

admin/config/index.php CHANGED
@@ -116,6 +116,7 @@ $settings = array(
116
  'track_users' => array( 'description' => __( 'Track WP Users', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option to track logged in users.', 'wp-slimstat' ) ),
117
  'session_duration' => array('description' => __( 'Session Duration', 'wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'How many seconds should a human session last? Google Analytics sets it to 1800 seconds.', 'wp-slimstat' ), 'after_input_field' => __( 'seconds', 'wp-slimstat' ) ),
118
  'extend_session' => array( 'description' => __( 'Extend Session', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Extend the duration of a session each time the user visits a new page.', 'wp-slimstat' ) ),
 
119
  'enable_cdn' => array('description' => __('Enable CDN','wp-slimstat'), 'type' => 'toggle', 'long_description' => __("Use <a href='http://www.jsdelivr.com/' target='_blank'>JSDelivr</a>'s CDN, by serving our tracking code from their fast and reliable network (free service).",'wp-slimstat')),
120
 
121
  'advanced_external_pages_header' => array('description' => __('External Pages','wp-slimstat'), 'type' => 'section_header'),
116
  'track_users' => array( 'description' => __( 'Track WP Users', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Enable this option to track logged in users.', 'wp-slimstat' ) ),
117
  'session_duration' => array('description' => __( 'Session Duration', 'wp-slimstat' ), 'type' => 'integer', 'long_description' => __( 'How many seconds should a human session last? Google Analytics sets it to 1800 seconds.', 'wp-slimstat' ), 'after_input_field' => __( 'seconds', 'wp-slimstat' ) ),
118
  'extend_session' => array( 'description' => __( 'Extend Session', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( 'Extend the duration of a session each time the user visits a new page.', 'wp-slimstat' ) ),
119
+ 'geolocation_country' => array( 'description' => __( 'Geolocation Precision', 'wp-slimstat' ), 'type' => 'toggle', 'long_description' => __( "When Slimstat determines your visitors' Country of origin, it uses a third-party data file <a href='https://dev.maxmind.com/geoip/geoip2/geolite2/' target='_blank'>provided by MaxMind</a>. They offer two precision levels: country and city. By default, Slimstat will install the smaller one (country), and you can decide to use the other one, if you don't mind its 60 Mb average size. After you change this option, please <strong>go to the Maintenance tab</strong> and reload (uninstall/install) the MaxMind GeoLite DB by clicking on the corresponding button.", 'wp-slimstat' ), 'custom_label_on' => __( 'Country', 'wp-slimstat' ), 'custom_label_off' => __( 'City', 'wp-slimstat' ) ),
120
  'enable_cdn' => array('description' => __('Enable CDN','wp-slimstat'), 'type' => 'toggle', 'long_description' => __("Use <a href='http://www.jsdelivr.com/' target='_blank'>JSDelivr</a>'s CDN, by serving our tracking code from their fast and reliable network (free service).",'wp-slimstat')),
121
 
122
  'advanced_external_pages_header' => array('description' => __('External Pages','wp-slimstat'), 'type' => 'section_header'),
admin/config/maintenance.php CHANGED
@@ -17,7 +17,7 @@ if ( !empty( $_REQUEST[ 'action' ] ) ) {
17
  break;
18
 
19
  case 'activate-sql-debug-mode':
20
- wp_slimstat::$settings[ 'show_sql_debug' ] = 'yes';
21
  break;
22
 
23
  case 'deactivate-indexes':
@@ -81,12 +81,9 @@ if ( !empty( $_REQUEST[ 'action' ] ) ) {
81
  case 'import-settings':
82
  $new_settings = @json_decode( stripslashes( $_POST[ 'import-slimstat-settings' ] ), true );
83
 
84
- if ( is_array( $new_settings ) ) {
85
- $new_settings = array_intersect_key( $new_settings, wp_slimstat::$settings );
86
- if ( !empty( $new_settings ) ) {
87
- foreach ( $new_settings as $a_setting_name => $a_setting_value ) {
88
- wp_slimstat::$settings[ $a_setting_name ] = $a_setting_value;
89
- }
90
  }
91
  wp_slimstat_admin::show_alert_message( __( 'Your new Slimstat settings have been imported and installed.', 'wp-slimstat' ) );
92
  }
@@ -176,7 +173,7 @@ $slim_browsers_exists =wp_slimstat::$wpdb->get_col( "SHOW TABLES LIKE '{$GLOBALS
176
  </td>
177
  </tr>
178
  <tr class="alternate">
179
- <?php if ( wp_slimstat::$settings[ 'show_sql_debug' ] != 'yes' ): ?>
180
  <th scope="row">
181
  <a class="button-secondary" href="<?php echo wp_slimstat_admin::$config_url.$current_tab ?>&amp;action=activate-sql-debug-mode"><?php _e("Enable SQL Debug",'wp-slimstat'); ?></a>
182
  </th>
@@ -313,7 +310,7 @@ $slim_browsers_exists =wp_slimstat::$wpdb->get_col( "SHOW TABLES LIKE '{$GLOBALS
313
  <form action="<?php echo wp_slimstat_admin::$config_url.$current_tab ?>" method="post">
314
  <?php wp_nonce_field( 'maintenance_wp_slimstat', 'maintenance_wp_slimstat_nonce', true, true ) ?>
315
  <input type="hidden" name="action" value="import-settings" />
316
- <textarea name="import-slimstat-settings" style="width:100%" rows="5" onClick="this.select();"><?php echo json_encode( wp_slimstat::$settings ) ?></textarea><br/>
317
  <input type="submit" value="<?php _e('Import','wp-slimstat') ?>" class="button-secondary"
318
  onclick="return(confirm('<?php _e('Are you sure you want to OVERWRITE your current settings?','wp-slimstat'); ?>'))">
319
  </form>
17
  break;
18
 
19
  case 'activate-sql-debug-mode':
20
+ wp_slimstat::$settings[ 'show_sql_debug' ] = 'on';
21
  break;
22
 
23
  case 'deactivate-indexes':
81
  case 'import-settings':
82
  $new_settings = @json_decode( stripslashes( $_POST[ 'import-slimstat-settings' ] ), true );
83
 
84
+ if ( is_array( $new_settings ) && !empty( $new_settings ) ) {
85
+ foreach ( $new_settings as $a_setting_name => $a_setting_value ) {
86
+ wp_slimstat::$settings[ $a_setting_name ] = $a_setting_value;
 
 
 
87
  }
88
  wp_slimstat_admin::show_alert_message( __( 'Your new Slimstat settings have been imported and installed.', 'wp-slimstat' ) );
89
  }
173
  </td>
174
  </tr>
175
  <tr class="alternate">
176
+ <?php if ( wp_slimstat::$settings[ 'show_sql_debug' ] != 'on' ): ?>
177
  <th scope="row">
178
  <a class="button-secondary" href="<?php echo wp_slimstat_admin::$config_url.$current_tab ?>&amp;action=activate-sql-debug-mode"><?php _e("Enable SQL Debug",'wp-slimstat'); ?></a>
179
  </th>
310
  <form action="<?php echo wp_slimstat_admin::$config_url.$current_tab ?>" method="post">
311
  <?php wp_nonce_field( 'maintenance_wp_slimstat', 'maintenance_wp_slimstat_nonce', true, true ) ?>
312
  <input type="hidden" name="action" value="import-settings" />
313
+ <textarea name="import-slimstat-settings" style="width:100%" rows="10"><?php echo json_encode( wp_slimstat::$settings ) ?></textarea><br/>
314
  <input type="submit" value="<?php _e('Import','wp-slimstat') ?>" class="button-secondary"
315
  onclick="return(confirm('<?php _e('Are you sure you want to OVERWRITE your current settings?','wp-slimstat'); ?>'))">
316
  </form>
admin/css/slimstat.css CHANGED
@@ -265,11 +265,10 @@
265
  background-position: 0 0;
266
  background-repeat: no-repeat;
267
  display: inline-block;
268
- font-size: 18px;
269
  height: 18px;
270
  line-height: 18px;
271
  margin-right: 5px;
272
- vertical-align: top;
273
  width: 16px;
274
  }
275
  [id^=slim_] .spaced{
265
  background-position: 0 0;
266
  background-repeat: no-repeat;
267
  display: inline-block;
 
268
  height: 18px;
269
  line-height: 18px;
270
  margin-right: 5px;
271
+ vertical-align: middle;
272
  width: 16px;
273
  }
274
  [id^=slim_] .spaced{
admin/view/index.php CHANGED
@@ -21,7 +21,7 @@
21
 
22
  $filter_value_html = '<label for="slimstat-filter-value">Filter value</label><input type="text" class="text" name="v" id="slimstat-filter-value" value="" size="20">';
23
 
24
- if (wp_slimstat::$settings['enable_sov'] == 'yes'){
25
  echo $filter_value_html.$filter_operator_html.$filter_name_html;
26
  }
27
  else{
@@ -138,15 +138,15 @@
138
  <?php endif; endforeach; ?>
139
  </form>
140
  <?php
141
- if ( !file_exists( wp_slimstat::$maxmind_path ) && ( empty( wp_slimstat::$settings[ 'no_maxmind_warning' ] ) || wp_slimstat::$settings[ 'no_maxmind_warning' ] != 'yes' ) ) {
142
  wp_slimstat_admin::show_alert_message( sprintf( __( "<a href='%s' class='noslimstat'>Install MaxMind's GeoLite DB</a> to determine your visitors' country of origin.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-geolite-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
143
  }
144
 
145
- if ( !file_exists( slim_browser::$browscap_autoload_path ) && ( empty( wp_slimstat::$settings[ 'no_browscap_warning' ] ) || wp_slimstat::$settings[ 'no_browscap_warning' ] != 'yes' ) ) {
146
  wp_slimstat_admin::show_alert_message( sprintf( __( "Install the Browscap <a href='%s' class='noslimstat'>User Agent Database</a> to accurately determine your visitors' browser and operating system.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-browscap-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
147
  }
148
 
149
- if ( wp_slimstat::$advanced_cache_exists && ( empty( wp_slimstat::$settings[ 'no_caching_warning' ] ) || wp_slimstat::$settings[ 'no_caching_warning' ] != 'yes' ) && ( empty( wp_slimstat::$settings[ 'javascript_mode' ] ) || wp_slimstat::$settings[ 'javascript_mode' ] != 'yes' ) ) {
150
  wp_slimstat_admin::show_alert_message( sprintf( __( "A caching plugin has been detected on your website. Please <a href='%s' target='_blank' class='noslimstat'>make sure to configure</a> Slimstat Analytics accordingly, to get accurate information.", 'wp-slimstat' ), 'https://slimstat.freshdesk.com/support/solutions/articles/5000528524-i-am-using-w3-total-cache-or-wp-super-cache-hypercache-etc-and-it-looks-like-slimstat-is-not-tra' ) . '<a id="slimstat-hide-caching-notice" class="slimstat-font-cancel slimstat-float-right" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
151
  }
152
 
21
 
22
  $filter_value_html = '<label for="slimstat-filter-value">Filter value</label><input type="text" class="text" name="v" id="slimstat-filter-value" value="" size="20">';
23
 
24
+ if ( wp_slimstat::$settings[ 'enable_sov' ] == 'on' ) {
25
  echo $filter_value_html.$filter_operator_html.$filter_name_html;
26
  }
27
  else{
138
  <?php endif; endforeach; ?>
139
  </form>
140
  <?php
141
+ if ( !file_exists( wp_slimstat::$maxmind_path ) && ( empty( wp_slimstat::$settings[ 'no_maxmind_warning' ] ) || wp_slimstat::$settings[ 'no_maxmind_warning' ] != 'on' ) ) {
142
  wp_slimstat_admin::show_alert_message( sprintf( __( "<a href='%s' class='noslimstat'>Install MaxMind's GeoLite DB</a> to determine your visitors' country of origin.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-geolite-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
143
  }
144
 
145
+ if ( !file_exists( slim_browser::$browscap_autoload_path ) && ( empty( wp_slimstat::$settings[ 'no_browscap_warning' ] ) || wp_slimstat::$settings[ 'no_browscap_warning' ] != 'on' ) ) {
146
  wp_slimstat_admin::show_alert_message( sprintf( __( "Install the Browscap <a href='%s' class='noslimstat'>User Agent Database</a> to accurately determine your visitors' browser and operating system.", 'wp-slimstat' ), self::$config_url . '6#wp-slimstat-external-data-files' ) . '<a id="slimstat-hide-browscap-notice" class="slimstat-font-cancel slimstat-float-right noslimstat" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
147
  }
148
 
149
+ if ( wp_slimstat::$advanced_cache_exists && ( empty( wp_slimstat::$settings[ 'no_caching_warning' ] ) || wp_slimstat::$settings[ 'no_caching_warning' ] != 'on' ) && ( empty( wp_slimstat::$settings[ 'javascript_mode' ] ) || wp_slimstat::$settings[ 'javascript_mode' ] != 'on' ) ) {
150
  wp_slimstat_admin::show_alert_message( sprintf( __( "A caching plugin has been detected on your website. Please <a href='%s' target='_blank' class='noslimstat'>make sure to configure</a> Slimstat Analytics accordingly, to get accurate information.", 'wp-slimstat' ), 'https://slimstat.freshdesk.com/support/solutions/articles/5000528524-i-am-using-w3-total-cache-or-wp-super-cache-hypercache-etc-and-it-looks-like-slimstat-is-not-tra' ) . '<a id="slimstat-hide-caching-notice" class="slimstat-font-cancel slimstat-float-right" title="Hide this notice" href="#"></a>', 'wp-ui-text-notification below-h2' );
151
  }
152
 
admin/view/layout.php CHANGED
@@ -55,7 +55,7 @@
55
  $already_seen = array();
56
 
57
  $current_user = wp_get_current_user();
58
- $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'yes' ) ? 'slimstat' : 'admin';
59
  ?>
60
 
61
  <div class="wrap slimstat-layout">
55
  $already_seen = array();
56
 
57
  $current_user = wp_get_current_user();
58
+ $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' ) ? 'slimstat' : 'admin';
59
  ?>
60
 
61
  <div class="wrap slimstat-layout">
admin/view/right-now.php CHANGED
@@ -4,7 +4,7 @@ if ( !function_exists( 'add_action' ) ) {
4
  exit(0);
5
  }
6
 
7
- if ( wp_slimstat::$settings[ 'async_load' ] == 'yes' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
8
  return '';
9
  }
10
 
@@ -36,7 +36,7 @@ echo wp_slimstat_db::$debug_message;
36
  if ( isset( $_args[ 'echo' ] ) && $_args[ 'echo' ] === false ) {
37
 
38
  // Massage the data before returning it
39
- if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'yes' ) {
40
  for ( $i = 0; $i < $count_page_results; $i++ ) {
41
  $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
42
  if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
@@ -63,7 +63,7 @@ else {
63
  // Loop through the results
64
  for ( $i=0; $i < $count_page_results; $i++ ) {
65
  $host_by_ip = $results[ $i ][ 'ip' ];
66
- if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'yes' ) {
67
  $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
68
  if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
69
  $host_by_ip .= ', ' . $gethostbyaddr;
@@ -79,11 +79,17 @@ else {
79
  $highlight_row = !empty($results[$i]['searchterms'])?' is-search-engine':(($results[$i]['browser_type'] != 1)?' is-direct':'');
80
 
81
  // Country
82
- $results[$i][ 'country' ] = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('country equals '.$results[$i]['country'])."'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/flags/{$results[$i]['country']}.png' width='16' height='16' title='" . __('c-'.$results[$i]['country'],'wp-slimstat') . "'></a>";
 
 
 
 
 
 
83
 
84
  // Browser
85
  if ($results[$i]['browser_version'] == 0) $results[$i]['browser_version'] = '';
86
- $browser_title = ( wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] != 'yes' ) ? "{$results[ $i ][ 'browser' ]} {$results[ $i ][ 'browser_version' ]}" : $results[ $i ][ 'user_agent' ];
87
  $browser_icon = $plugin_url.'/images/browsers/other-browsers-and-os.png';
88
  if (in_array($results[$i]['browser'], $supported_browser_icons)){
89
  $browser_icon = $plugin_url.'/images/browsers/'.sanitize_title($results[$i]['browser']).'.png';
@@ -109,7 +115,7 @@ else {
109
  }
110
  else{
111
  $display_user_name = $results[ $i ][ 'username' ];
112
- if ( wp_slimstat::$settings[ 'show_display_name' ] == 'yes' && strpos( $results[ $i ][ 'notes' ], 'user:' ) !== false ) {
113
  $display_real_name = get_user_by('login', $results[$i]['username']);
114
  if (is_object($display_real_name)) $display_user_name = $display_real_name->display_name;
115
  }
@@ -118,8 +124,10 @@ else {
118
  $highlight_row = (strpos( $results[$i]['notes'], 'user:') !== false)?' is-known-user':' is-known-visitor';
119
 
120
  }
121
- if ( is_admin() && !empty( wp_slimstat::$settings[ 'ip_lookup_service' ] ) ) {
122
- $ip_address = "<a class='slimstat-font-location-1 whois' href='" . wp_slimstat::$settings[ 'ip_lookup_service' ] . "{$results[ $i ][ 'ip' ]}' target='_blank' title='WHOIS: {$results[ $i ][ 'ip' ]}'></a> $ip_address";
 
 
123
  }
124
 
125
  // Originating IP Address
@@ -147,7 +155,7 @@ else {
147
  $screen_resolution = "<span class='pageview-screenres'>{$results[ $i ][ 'screen_width' ]}x{$results[ $i ][ 'screen_height' ]}</span>";
148
  }
149
 
150
- $row_output = "<p class='header$highlight_row'>{$results[$i]['country']} $browser_filtered $platform_filtered $browser_type_filtered $ip_address $other_ip_address <span class='plugins'>$plugins</span> $screen_resolution</p>";
151
 
152
  // Strip all the filter links, if this information is shown on the frontend
153
  if ( !is_admin() ) {
4
  exit(0);
5
  }
6
 
7
+ if ( wp_slimstat::$settings[ 'async_load' ] == 'on' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
8
  return '';
9
  }
10
 
36
  if ( isset( $_args[ 'echo' ] ) && $_args[ 'echo' ] === false ) {
37
 
38
  // Massage the data before returning it
39
+ if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
40
  for ( $i = 0; $i < $count_page_results; $i++ ) {
41
  $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
42
  if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
63
  // Loop through the results
64
  for ( $i=0; $i < $count_page_results; $i++ ) {
65
  $host_by_ip = $results[ $i ][ 'ip' ];
66
+ if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
67
  $gethostbyaddr = gethostbyaddr( $results[ $i ][ 'ip' ] );
68
  if ( $gethostbyaddr != $host_by_ip && !empty( $gethostbyaddr ) ) {
69
  $host_by_ip .= ', ' . $gethostbyaddr;
79
  $highlight_row = !empty($results[$i]['searchterms'])?' is-search-engine':(($results[$i]['browser_type'] != 1)?' is-direct':'');
80
 
81
  // Country
82
+ $country_filtered = "<a class='slimstat-filter-link inline-icon' href='".wp_slimstat_reports::fs_url('country equals '.$results[$i]['country'])."'><img class='slimstat-tooltip-trigger' src='$plugin_url/images/flags/{$results[$i]['country']}.png' width='16' height='16' title='" . __('c-'.$results[$i]['country'],'wp-slimstat') . "'></a>";
83
+
84
+ // City, if tracked
85
+ $city_filtered = '';
86
+ if ( !empty( $results[ $i ][ 'city' ] ) ) {
87
+ $city_filtered = "<a class='slimstat-filter-link' href='" . wp_slimstat_reports::fs_url( 'city equals ' . $results[ $i ][ 'city' ] ) . "'>{$results[ $i ][ 'city' ]}</a>";
88
+ }
89
 
90
  // Browser
91
  if ($results[$i]['browser_version'] == 0) $results[$i]['browser_version'] = '';
92
+ $browser_title = ( wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] != 'on' ) ? "{$results[ $i ][ 'browser' ]} {$results[ $i ][ 'browser_version' ]}" : $results[ $i ][ 'user_agent' ];
93
  $browser_icon = $plugin_url.'/images/browsers/other-browsers-and-os.png';
94
  if (in_array($results[$i]['browser'], $supported_browser_icons)){
95
  $browser_icon = $plugin_url.'/images/browsers/'.sanitize_title($results[$i]['browser']).'.png';
115
  }
116
  else{
117
  $display_user_name = $results[ $i ][ 'username' ];
118
+ if ( wp_slimstat::$settings[ 'show_display_name' ] == 'on' && strpos( $results[ $i ][ 'notes' ], 'user:' ) !== false ) {
119
  $display_real_name = get_user_by('login', $results[$i]['username']);
120
  if (is_object($display_real_name)) $display_user_name = $display_real_name->display_name;
121
  }
124
  $highlight_row = (strpos( $results[$i]['notes'], 'user:') !== false)?' is-known-user':' is-known-visitor';
125
 
126
  }
127
+
128
+ $whois_pin = '';
129
+ if ( is_admin() && !empty( wp_slimstat::$settings[ 'ip_lookup_service' ] ) && !in_array( $results[ $i ][ 'country' ], array( 'xx', 'xy' ) ) ) {
130
+ $whois_pin = "<a class='slimstat-font-location-1 whois' href='" . wp_slimstat::$settings[ 'ip_lookup_service' ] . "{$results[ $i ][ 'ip' ]}' target='_blank' title='WHOIS: {$results[ $i ][ 'ip' ]}'></a>";
131
  }
132
 
133
  // Originating IP Address
155
  $screen_resolution = "<span class='pageview-screenres'>{$results[ $i ][ 'screen_width' ]}x{$results[ $i ][ 'screen_height' ]}</span>";
156
  }
157
 
158
+ $row_output = "<p class='header$highlight_row'>$browser_filtered $platform_filtered $browser_type_filtered $country_filtered $whois_pin $city_filtered $ip_address $other_ip_address <span class='plugins'>$plugins</span> $screen_resolution</p>";
159
 
160
  // Strip all the filter links, if this information is shown on the frontend
161
  if ( !is_admin() ) {
admin/view/wp-slimstat-db.php CHANGED
@@ -48,6 +48,8 @@ class wp_slimstat_db {
48
  'browser_version' => array( __( 'Browser Version', 'wp-slimstat' ), 'varchar' ),
49
  'browser_type' => array( __( 'Browser Type', 'wp-slimstat' ), 'int' ),
50
  'user_agent' => array( __( 'User Agent', 'wp-slimstat' ), 'varchar' ),
 
 
51
  'notes' => array( __( 'Annotations', 'wp-slimstat' ), 'varchar' ),
52
  'server_latency' => array( __( 'Server Latency', 'wp-slimstat' ), 'int' ),
53
  'author' => array( __( 'Post Author', 'wp-slimstat' ), 'varchar' ),
@@ -61,6 +63,11 @@ class wp_slimstat_db {
61
  'visit_id' => array( __( 'Visit ID', 'wp-slimstat' ), 'int' )
62
  );
63
 
 
 
 
 
 
64
  // List of supported filters and their friendly names
65
  self::$operator_names = array(
66
  'equals' => __( 'equals', 'wp-slimstat' ),
@@ -143,7 +150,7 @@ class wp_slimstat_db {
143
  }
144
 
145
  // Hidden Filters
146
- if ( wp_slimstat::$settings[ 'restrict_authors_view' ] == 'yes' && !current_user_can( 'manage_options' ) && !empty( $GLOBALS[ 'current_user' ]->user_login ) ) {
147
  $filters_array[ 'author' ] = 'author equals ' . $GLOBALS[ 'current_user' ]->user_login;
148
  }
149
 
@@ -281,7 +288,7 @@ class wp_slimstat_db {
281
  $where = array( '', $_value );
282
  switch ( $_operator ) {
283
  case 'is_not_equal_to':
284
- $where[0] = "$column_with_alias <> %s";
285
  break;
286
 
287
  case 'contains':
@@ -289,7 +296,7 @@ class wp_slimstat_db {
289
  break;
290
 
291
  case 'includes_in_set':
292
- $where[0] = "FIND_IN_SET(%s, $column_with_alias) > 0";
293
  break;
294
 
295
  case 'does_not_contain':
@@ -305,7 +312,7 @@ class wp_slimstat_db {
305
  break;
306
 
307
  case 'sounds_like':
308
- $where[0] = "SOUNDEX($column_with_alias) = SOUNDEX(%s)";
309
  break;
310
 
311
  case 'is_empty':
@@ -317,32 +324,32 @@ class wp_slimstat_db {
317
  break;
318
 
319
  case 'is_greater_than':
320
- $where[0] = "$column_with_alias > %d";
321
  break;
322
 
323
  case 'is_less_than':
324
- $where[0] = "$column_with_alias < %d";
325
  break;
326
 
327
  case 'between':
328
- $range = explode(',', $_value);
329
- $where = array( "$column_with_alias BETWEEN %d AND %d", array( $range[0], $range[1] ) );
330
  break;
331
 
332
  case 'matches':
333
- $where[0] = "$column_with_alias REGEXP %s";
334
  break;
335
 
336
  case 'does_not_match':
337
- $where[0] = "$column_with_alias NOT REGEXP %s";
338
  break;
339
 
340
  default:
341
- $where[0] = "$column_with_alias = %s";
342
  break;
343
  }
344
 
345
- if ( isset( $where[ 1 ] ) ) {
346
  return $GLOBALS[ 'wpdb' ]->prepare( $where[ 0 ], $where[ 1 ] );
347
  }
348
  else {
@@ -353,7 +360,7 @@ class wp_slimstat_db {
353
  public static function get_results( $_sql = '', $_select_no_aggregate_values = '', $_order_by = '', $_group_by = '', $_aggregate_values_add = '' ) {
354
  $_sql = apply_filters( 'slimstat_get_results_sql', $_sql, $_select_no_aggregate_values, $_order_by, $_group_by, $_aggregate_values_add );
355
 
356
- if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'yes' ) {
357
  self::$debug_message .= "<p class='debug'>$_sql</p>";
358
  }
359
 
@@ -363,7 +370,7 @@ class wp_slimstat_db {
363
  public static function get_var( $_sql = '', $_aggregate_value = '' ) {
364
  $_sql = apply_filters( 'slimstat_get_var_sql', $_sql, $_aggregate_value );
365
 
366
- if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'yes' ) {
367
  self::$debug_message .= "<p class='debug'>$_sql</p>";
368
  }
369
 
@@ -482,7 +489,7 @@ class wp_slimstat_db {
482
  }
483
 
484
  // If the setting to use the last X days as default time span is enabled, we need to setup the "interval" variables
485
- if ( ( empty( wp_slimstat::$settings[ 'use_current_month_timespan' ] ) || wp_slimstat::$settings[ 'use_current_month_timespan' ] != 'yes' ) ) {
486
  // Do not set the interval if another date filter has already been set
487
  $is_date_filter_empty = true;
488
  if ( !empty( $filters_normalized[ 'date' ] ) ) {
@@ -884,7 +891,7 @@ class wp_slimstat_db {
884
  $columns .= ', ip, dt';
885
  }
886
  else {
887
- $columns = 'id, ip, other_ip, username, country, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt';
888
  }
889
 
890
  if ( !empty( $_more_columns ) ) {
48
  'browser_version' => array( __( 'Browser Version', 'wp-slimstat' ), 'varchar' ),
49
  'browser_type' => array( __( 'Browser Type', 'wp-slimstat' ), 'int' ),
50
  'user_agent' => array( __( 'User Agent', 'wp-slimstat' ), 'varchar' ),
51
+ 'city' => array( __( 'City', 'wp-slimstat' ), 'varchar' ),
52
+ 'location' => array( __( 'Coordinates', 'wp-slimstat' ), 'varchar' ),
53
  'notes' => array( __( 'Annotations', 'wp-slimstat' ), 'varchar' ),
54
  'server_latency' => array( __( 'Server Latency', 'wp-slimstat' ), 'int' ),
55
  'author' => array( __( 'Post Author', 'wp-slimstat' ), 'varchar' ),
63
  'visit_id' => array( __( 'Visit ID', 'wp-slimstat' ), 'int' )
64
  );
65
 
66
+ if ( wp_slimstat::$settings[ 'geolocation_country' ] == 'on' ) {
67
+ unset( self::$columns_names[ 'city' ] );
68
+ unset( self::$columns_names[ 'location' ] );
69
+ }
70
+
71
  // List of supported filters and their friendly names
72
  self::$operator_names = array(
73
  'equals' => __( 'equals', 'wp-slimstat' ),
150
  }
151
 
152
  // Hidden Filters
153
+ if ( wp_slimstat::$settings[ 'restrict_authors_view' ] == 'on' && !current_user_can( 'manage_options' ) && !empty( $GLOBALS[ 'current_user' ]->user_login ) ) {
154
  $filters_array[ 'author' ] = 'author equals ' . $GLOBALS[ 'current_user' ]->user_login;
155
  }
156
 
288
  $where = array( '', $_value );
289
  switch ( $_operator ) {
290
  case 'is_not_equal_to':
291
+ $where[ 0 ] = "$column_with_alias <> %s";
292
  break;
293
 
294
  case 'contains':
296
  break;
297
 
298
  case 'includes_in_set':
299
+ $where[ 0 ] = "FIND_IN_SET(%s, $column_with_alias) > 0";
300
  break;
301
 
302
  case 'does_not_contain':
312
  break;
313
 
314
  case 'sounds_like':
315
+ $where[ 0 ] = "SOUNDEX($column_with_alias) = SOUNDEX(%s)";
316
  break;
317
 
318
  case 'is_empty':
324
  break;
325
 
326
  case 'is_greater_than':
327
+ $where[ 0 ] = "$column_with_alias > %d";
328
  break;
329
 
330
  case 'is_less_than':
331
+ $where[ 0 ] = "$column_with_alias < %d";
332
  break;
333
 
334
  case 'between':
335
+ $range = explode( ',', $_value );
336
+ $where = array( "$column_with_alias BETWEEN %d AND %d", array( $range[ 0 ], $range[ 1 ] ) );
337
  break;
338
 
339
  case 'matches':
340
+ $where[ 0 ] = "$column_with_alias REGEXP %s";
341
  break;
342
 
343
  case 'does_not_match':
344
+ $where[ 0 ] = "$column_with_alias NOT REGEXP %s";
345
  break;
346
 
347
  default:
348
+ $where[ 0 ] = "$column_with_alias = %s";
349
  break;
350
  }
351
 
352
+ if ( !empty( $where[ 1 ] ) ) {
353
  return $GLOBALS[ 'wpdb' ]->prepare( $where[ 0 ], $where[ 1 ] );
354
  }
355
  else {
360
  public static function get_results( $_sql = '', $_select_no_aggregate_values = '', $_order_by = '', $_group_by = '', $_aggregate_values_add = '' ) {
361
  $_sql = apply_filters( 'slimstat_get_results_sql', $_sql, $_select_no_aggregate_values, $_order_by, $_group_by, $_aggregate_values_add );
362
 
363
+ if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'on' ) {
364
  self::$debug_message .= "<p class='debug'>$_sql</p>";
365
  }
366
 
370
  public static function get_var( $_sql = '', $_aggregate_value = '' ) {
371
  $_sql = apply_filters( 'slimstat_get_var_sql', $_sql, $_aggregate_value );
372
 
373
+ if ( wp_slimstat::$settings[ 'show_sql_debug' ] == 'on' ) {
374
  self::$debug_message .= "<p class='debug'>$_sql</p>";
375
  }
376
 
489
  }
490
 
491
  // If the setting to use the last X days as default time span is enabled, we need to setup the "interval" variables
492
+ if ( ( empty( wp_slimstat::$settings[ 'use_current_month_timespan' ] ) || wp_slimstat::$settings[ 'use_current_month_timespan' ] != 'on' ) ) {
493
  // Do not set the interval if another date filter has already been set
494
  $is_date_filter_empty = true;
495
  if ( !empty( $filters_normalized[ 'date' ] ) ) {
891
  $columns .= ', ip, dt';
892
  }
893
  else {
894
+ $columns = 'id, ip, other_ip, username, country, city, location, referer, resource, searchterms, plugins, notes, visit_id, server_latency, page_performance, browser, browser_version, browser_type, platform, language, user_agent, resolution, screen_width, screen_height, content_type, category, author, content_id, outbound_resource, dt_out, dt';
895
  }
896
 
897
  if ( !empty( $_more_columns ) ) {
admin/view/wp-slimstat-reports.php CHANGED
@@ -527,20 +527,6 @@ class wp_slimstat_reports {
527
  'screens' => array( 'slimview5', 'dashboard' )
528
  ),
529
 
530
- /*
531
- 'slim_p3_11' => array(
532
- 'title' => __( 'Recent Exit Pages', 'wp-slimstat' ),
533
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
534
- 'callback_args' => array(
535
- 'type' => 'recent',
536
- 'columns' => 'visit_id, resource', // raw_results_to_html knows to display the resource, when the column is visit_id
537
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
538
- ),
539
- 'classes' => array( 'normal' ),
540
- 'screens' => array( 'slimview5' )
541
- ),
542
- */
543
-
544
  'slim_p4_01' => array(
545
  'title' => __( 'Recent Outbound Links', 'wp-slimstat' ),
546
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
@@ -565,22 +551,6 @@ class wp_slimstat_reports {
565
  'classes' => array( 'normal' ),
566
  'screens' => array( 'slimview4' )
567
  ),
568
- /*
569
- 'slim_p4_03' => array(
570
- 'title' => __( 'Recent Bounce Pages', 'wp-slimstat' ),
571
- 'callback' => array( __CLASS__, 'raw_results_to_html' ),
572
- 'callback_args' => array(
573
- 'type' => 'recent',
574
- 'columns' => 'resource',
575
- 'where' => 'content_type <> "404"',
576
- 'having' => 'HAVING COUNT(visit_id) = 1',
577
- 'raw' => array( 'wp_slimstat_db', 'get_recent' )
578
- ),
579
- 'classes' => array( 'normal', 'hidden' ),
580
- 'screens' => array( 'slimview4' ),
581
- 'tooltip' => __( 'A <em>bounce page</em> is a single-page visit, or visit in which the person left your site from the entrance (landing) page.', 'wp-slimstat' )
582
- ),
583
- */
584
  'slim_p4_04' => array(
585
  'title' => __( 'Recent Feeds', 'wp-slimstat' ),
586
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
@@ -844,6 +814,21 @@ class wp_slimstat_reports {
844
  )
845
  );
846
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
847
  // Allow third party tools to manipulate this list here above: please use unique report IDs that don't interfere with built-in ones, if you add your own custom report
848
  self::$reports_info = apply_filters( 'slimstat_reports_info', self::$reports_info );
849
 
@@ -866,7 +851,7 @@ class wp_slimstat_reports {
866
 
867
  // Retrieve this user's list of active reports,
868
  $current_user = wp_get_current_user();
869
- $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'yes' ) ? 'slimstat' : 'admin';
870
 
871
  // Superadmins can customize the layout at network level, to override per-site settings
872
  self::$user_reports = get_user_option( "meta-box-order_slimstat_page_slimlayout-network", 1 );
@@ -998,7 +983,7 @@ class wp_slimstat_reports {
998
  }
999
 
1000
  public static function raw_results_to_html( $_args = array() ) {
1001
- if ( wp_slimstat::$settings[ 'async_load' ] == 'yes' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
1002
  return '';
1003
  }
1004
 
@@ -1020,7 +1005,7 @@ class wp_slimstat_reports {
1020
  echo "{$a_result[ 'metric' ]} <span>{$a_result[ 'value' ]}</span>";
1021
 
1022
  if ( !empty( $a_result[ 'details' ] ) ) {
1023
- $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'yes' ) ? ' expanded' : '';
1024
  echo "<b class='slimstat-tooltip-content$is_expanded'>{$a_result[ 'details' ]}</b>";
1025
  }
1026
 
@@ -1064,7 +1049,7 @@ class wp_slimstat_reports {
1064
 
1065
  echo self::report_pagination( $count_page_results, count( $all_results ) );
1066
 
1067
- $is_expanded = ( is_admin() && wp_slimstat::$settings[ 'expand_details' ] == 'yes' ) ? ' expanded' : '';
1068
  $permalinks_enabled = get_option( 'permalink_structure' );
1069
  $column_not_calculated = str_replace( '_calculated', '', $_args[ 'columns' ] );
1070
 
@@ -1077,7 +1062,7 @@ class wp_slimstat_reports {
1077
  switch ( $column_not_calculated ){
1078
 
1079
  case 'browser':
1080
- if ( !empty( $results[ $i ][ 'user_agent' ] ) && wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] == 'yes' ) {
1081
  $element_pre_value = self::inline_help($results[$i]['user_agent'], false);
1082
  }
1083
  $element_value = $results[$i]['browser'].((isset($results[$i]['browser_version']) && intval($results[$i]['browser_version']) != 0)?' '.$results[$i]['browser_version']:'');
@@ -1094,7 +1079,7 @@ class wp_slimstat_reports {
1094
  break;
1095
 
1096
  case 'ip':
1097
- if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'yes' ) {
1098
  $element_value = gethostbyaddr( $results[ $i ][ $_args[ 'columns' ] ] );
1099
  }
1100
  else{
@@ -1150,7 +1135,7 @@ class wp_slimstat_reports {
1150
 
1151
  case 'username':
1152
  $element_value = $results[ $i ][ 'username' ];
1153
- if ( wp_slimstat::$settings[ 'show_display_name' ] == 'yes' ) {
1154
  $element_custom_value = get_user_by( 'login', $results[ $i ][ 'username' ] );
1155
  if ( is_object( $element_custom_value ) ) {
1156
  $element_value = $element_custom_value->display_name;
@@ -1210,7 +1195,7 @@ class wp_slimstat_reports {
1210
  $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$element_url.'"></a> '.$element_value;
1211
  }
1212
 
1213
- if ( is_admin() && !empty( $results[ $i ][ 'ip' ]) && $_args[ 'columns' ] != 'ip' && wp_slimstat::$settings[ 'convert_ip_addresses' ] != 'yes' ) {
1214
  $row_details .= '<br> IP: <a class="slimstat-filter-link" href="'.self::fs_url( 'ip equals ' . $results[ $i ][ 'ip' ] ) . '">' . $results[ $i ][ 'ip' ] . '</a>' . ( !empty( $results[ $i ][ 'other_ip' ] ) ? ' / ' . $results[ $i ][ 'other_ip' ] : '' ) . '<a title="WHOIS: ' . $results[ $i ][ 'ip' ] . '" class="slimstat-font-location-1 whois" href="' . wp_slimstat::$settings[ 'ip_lookup_service' ] . $results[ $i ][ 'ip' ] . '"></a>';
1215
  }
1216
  if ( !empty( $row_details ) ) {
@@ -1821,7 +1806,7 @@ class wp_slimstat_reports {
1821
  }
1822
 
1823
  echo self::report_pagination( $count_page_results, count( $all_results ) );
1824
- $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'yes' ) ? ' expanded' : '';
1825
 
1826
  foreach ( $results as $a_result ) {
1827
  echo "<p class='slimstat-tooltip-trigger'>{$a_result[ 'notes' ]} <b class='slimstat-tooltip-content$is_expanded'>" . __( 'Type', 'wp-slimstat' ) . ": {$a_result[ 'type' ]}";
@@ -2037,7 +2022,7 @@ class wp_slimstat_reports {
2037
  public static function get_resource_title( $_resource = '' ) {
2038
  $resource_title = $_resource;
2039
 
2040
- if ( wp_slimstat::$settings[ 'convert_resource_urls_to_titles' ] != 'yes' ) {
2041
  return htmlentities( urldecode( $resource_title ), ENT_QUOTES, 'UTF-8' );
2042
  }
2043
 
527
  'screens' => array( 'slimview5', 'dashboard' )
528
  ),
529
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  'slim_p4_01' => array(
531
  'title' => __( 'Recent Outbound Links', 'wp-slimstat' ),
532
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
551
  'classes' => array( 'normal' ),
552
  'screens' => array( 'slimview4' )
553
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  'slim_p4_04' => array(
555
  'title' => __( 'Recent Feeds', 'wp-slimstat' ),
556
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
814
  )
815
  );
816
 
817
+ if ( wp_slimstat::$settings[ 'geolocation_country' ] != 'on' ) {
818
+ self::$reports_info [ 'slim_p2_23' ] = array(
819
+ 'title' => __( 'Top Cities', 'wp-slimstat' ),
820
+ 'callback' => array( __CLASS__, 'raw_results_to_html' ),
821
+ 'callback_args' => array(
822
+ 'type' => 'top',
823
+ 'columns' => 'city',
824
+
825
+ 'raw' => array( 'wp_slimstat_db', 'get_top' )
826
+ ),
827
+ 'classes' => array( 'normal' ),
828
+ 'screens' => array( 'slimview3' )
829
+ );
830
+ }
831
+
832
  // Allow third party tools to manipulate this list here above: please use unique report IDs that don't interfere with built-in ones, if you add your own custom report
833
  self::$reports_info = apply_filters( 'slimstat_reports_info', self::$reports_info );
834
 
851
 
852
  // Retrieve this user's list of active reports,
853
  $current_user = wp_get_current_user();
854
+ $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' ) ? 'slimstat' : 'admin';
855
 
856
  // Superadmins can customize the layout at network level, to override per-site settings
857
  self::$user_reports = get_user_option( "meta-box-order_slimstat_page_slimlayout-network", 1 );
983
  }
984
 
985
  public static function raw_results_to_html( $_args = array() ) {
986
+ if ( wp_slimstat::$settings[ 'async_load' ] == 'on' && ( !defined( 'DOING_AJAX' ) || !DOING_AJAX ) ) {
987
  return '';
988
  }
989
 
1005
  echo "{$a_result[ 'metric' ]} <span>{$a_result[ 'value' ]}</span>";
1006
 
1007
  if ( !empty( $a_result[ 'details' ] ) ) {
1008
+ $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1009
  echo "<b class='slimstat-tooltip-content$is_expanded'>{$a_result[ 'details' ]}</b>";
1010
  }
1011
 
1049
 
1050
  echo self::report_pagination( $count_page_results, count( $all_results ) );
1051
 
1052
+ $is_expanded = ( is_admin() && wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1053
  $permalinks_enabled = get_option( 'permalink_structure' );
1054
  $column_not_calculated = str_replace( '_calculated', '', $_args[ 'columns' ] );
1055
 
1062
  switch ( $column_not_calculated ){
1063
 
1064
  case 'browser':
1065
+ if ( !empty( $results[ $i ][ 'user_agent' ] ) && wp_slimstat::$settings[ 'show_complete_user_agent_tooltip' ] == 'on' ) {
1066
  $element_pre_value = self::inline_help($results[$i]['user_agent'], false);
1067
  }
1068
  $element_value = $results[$i]['browser'].((isset($results[$i]['browser_version']) && intval($results[$i]['browser_version']) != 0)?' '.$results[$i]['browser_version']:'');
1079
  break;
1080
 
1081
  case 'ip':
1082
+ if ( wp_slimstat::$settings[ 'convert_ip_addresses' ] == 'on' ) {
1083
  $element_value = gethostbyaddr( $results[ $i ][ $_args[ 'columns' ] ] );
1084
  }
1085
  else{
1135
 
1136
  case 'username':
1137
  $element_value = $results[ $i ][ 'username' ];
1138
+ if ( wp_slimstat::$settings[ 'show_display_name' ] == 'on' ) {
1139
  $element_custom_value = get_user_by( 'login', $results[ $i ][ 'username' ] );
1140
  if ( is_object( $element_custom_value ) ) {
1141
  $element_value = $element_custom_value->display_name;
1195
  $element_value = '<a target="_blank" class="slimstat-font-logout" title="'.__('Open this URL in a new window','wp-slimstat').'" href="'.$element_url.'"></a> '.$element_value;
1196
  }
1197
 
1198
+ if ( is_admin() && !empty( $results[ $i ][ 'ip' ]) && $_args[ 'columns' ] != 'ip' && wp_slimstat::$settings[ 'convert_ip_addresses' ] != 'on' ) {
1199
  $row_details .= '<br> IP: <a class="slimstat-filter-link" href="'.self::fs_url( 'ip equals ' . $results[ $i ][ 'ip' ] ) . '">' . $results[ $i ][ 'ip' ] . '</a>' . ( !empty( $results[ $i ][ 'other_ip' ] ) ? ' / ' . $results[ $i ][ 'other_ip' ] : '' ) . '<a title="WHOIS: ' . $results[ $i ][ 'ip' ] . '" class="slimstat-font-location-1 whois" href="' . wp_slimstat::$settings[ 'ip_lookup_service' ] . $results[ $i ][ 'ip' ] . '"></a>';
1200
  }
1201
  if ( !empty( $row_details ) ) {
1806
  }
1807
 
1808
  echo self::report_pagination( $count_page_results, count( $all_results ) );
1809
+ $is_expanded = ( wp_slimstat::$settings[ 'expand_details' ] == 'on' ) ? ' expanded' : '';
1810
 
1811
  foreach ( $results as $a_result ) {
1812
  echo "<p class='slimstat-tooltip-trigger'>{$a_result[ 'notes' ]} <b class='slimstat-tooltip-content$is_expanded'>" . __( 'Type', 'wp-slimstat' ) . ": {$a_result[ 'type' ]}";
2022
  public static function get_resource_title( $_resource = '' ) {
2023
  $resource_title = $_resource;
2024
 
2025
+ if ( wp_slimstat::$settings[ 'convert_resource_urls_to_titles' ] != 'on' ) {
2026
  return htmlentities( urldecode( $resource_title ), ENT_QUOTES, 'UTF-8' );
2027
  }
2028
 
admin/wp-slimstat-admin.php CHANGED
@@ -11,9 +11,7 @@ class wp_slimstat_admin {
11
  * Init -- Sets things up.
12
  */
13
  public static function init() {
14
- self::$admin_notice = "After WordPress rolled out their official REST API, we had received a few requests asking to add support for this feature to Slimstat. In an age where headless applications are all the rage, and given that WordPress has reached a stable state in implementing this functionality, we decided that it was time for us to move forward on this project. Starting with Slimstat 4.7, you can now access your metrics from any external application. Just follow these <a href='https://slimstat.freshdesk.com/solution/articles/12000033661-slimstat-rest-api' target='_blank'>simple steps</a> to get started (or contact our support team). Our REST API implementation is still experimental, so please do not hesitate to report any issues or concerns you might have. Also, don't forget that our Summer Madness Discount promotion continues. Get your <a href='http://www.wp-slimstat.com/addons/' target='_blank'>favorite Slimstat add-ons</a> (including the bundles) and pay only half the original price. Note that prices at checkout already factor in the discount; you don't need any special code or cryptic URL to participate!";
15
-
16
- // "As those who have been using Slimstat for a while know, we never stop doing our good share of research and development to imrpove this plugin. One request that has been sitting on our wishlist for a while is to make our geolocation functionality more accurate, and track not just a user's Country of origin, but possibly his State (where applicable) and city. In order to geolocate visitors, our code has been leveraging a third-party data file provided by <a href='https://www.maxmind.com/en/home' target='_blank'>MaxMind.com</a>. A while ago, they launched a new data format, which improves performance and offers a way to determine the city of origin. However, the new library required a higher version of PHP, and up until now we had preferred allowing more people to use our plugin, over the chance of offering this feature. Now, we found a way to get the best of both worlds: by customizing their PHP library, we were able to make it work with PHP 5.3! Which means that soon Slimstat will be able to tell you your visitors' city of origin right out of the box. Please contact us if you would like to test this feature in advance."
17
 
18
  self::$admin_notice .= '<br/><br/><a id="slimstat-hide-admin-notice" href="#" class="button-secondary">Got it, thanks</a>';
19
 
@@ -115,12 +113,12 @@ class wp_slimstat_admin {
115
  }
116
 
117
  // Remove spammers from the database
118
- if (wp_slimstat::$settings['ignore_spammers'] == 'yes'){
119
  add_action('transition_comment_status', array(__CLASS__, 'remove_spam'), 15, 3);
120
  }
121
 
122
  // Add a menu to the admin bar ( this function is declared here and not in wp_slimstat_admin because the latter is only initialized if is_admin(), and not in the front-end )
123
- if ( wp_slimstat::$settings[ 'use_separate_menu' ] != 'yes' && is_admin_bar_showing() ) {
124
  add_action( 'admin_bar_menu', array( __CLASS__, 'wp_slimstat_adminbar' ), 100 );
125
  }
126
 
@@ -130,7 +128,7 @@ class wp_slimstat_admin {
130
  add_action( 'admin_menu', array( __CLASS__, 'wp_slimstat_add_config_menu' ) );
131
 
132
  // Display the column in the Edit Posts / Pages screen
133
- if ( wp_slimstat::$settings[ 'add_posts_column' ] == 'yes' ) {
134
  $post_types = get_post_types( array( 'public' => true, 'show_ui' => true ), 'names' );
135
  include_once( dirname( __FILE__ ) . '/view/wp-slimstat-reports.php' );
136
  include_once( dirname( __FILE__ ) . '/view/wp-slimstat-db.php' );
@@ -169,7 +167,7 @@ class wp_slimstat_admin {
169
  }
170
 
171
  // Dashboard Widgets
172
- if ( wp_slimstat::$settings[ 'add_dashboard_widgets' ] == 'yes' ) {
173
  $temp = strlen( $_SERVER['REQUEST_URI'] ) - 10;
174
 
175
  if( strpos( $_SERVER['REQUEST_URI'], 'index.php' ) !== false || ( $temp >= 0 && strpos($_SERVER['REQUEST_URI'], '/wp-admin/', $temp) !== false ) ) {
@@ -194,7 +192,7 @@ class wp_slimstat_admin {
194
  }
195
 
196
  // Hide plugins
197
- if ( wp_slimstat::$settings[ 'hide_addons' ] == 'yes' ) {
198
  add_filter( 'all_plugins', array( __CLASS__, 'hide_addons' ) );
199
  }
200
  }
@@ -352,245 +350,11 @@ class wp_slimstat_admin {
352
  public static function update_tables_and_options(){
353
  $my_wpdb = apply_filters('slimstat_custom_wpdb', $GLOBALS['wpdb']);
354
 
355
- // --- Updates for version 3.8.4 ---
356
- if ( version_compare( wp_slimstat::$settings[ 'version' ], '3.8.4', '<' ) ) {
357
- $my_wpdb->query( "CREATE TABLE {$GLOBALS['wpdb']->prefix}slim_stats_archive LIKE {$GLOBALS['wpdb']->prefix}slim_stats" );
358
- }
359
- // --- END: Updates for version 3.8.4 ---
360
-
361
- // --- Updates for version 3.9.6 ---
362
- if ( version_compare( wp_slimstat::$settings[ 'version' ], '3.9.6', '<' ) ) {
363
- // Consolidate some settings
364
- $classes = wp_slimstat::string_to_array( wp_slimstat::$settings[ 'ignore_outbound_classes' ] );
365
- $rel = wp_slimstat::string_to_array( wp_slimstat::$settings[ 'ignore_outbound_rel' ] );
366
- $href = wp_slimstat::string_to_array( wp_slimstat::$settings[ 'ignore_outbound_href' ] );
367
- wp_slimstat::$settings[ 'ignore_outbound_classes_rel_href' ] = implode( ',', array_merge( $classes, $rel, $href ) );
368
-
369
- $classes = wp_slimstat::string_to_array( wp_slimstat::$settings[ 'do_not_track_outbound_classes' ] );
370
- $rel = wp_slimstat::string_to_array( wp_slimstat::$settings[ 'do_not_track_outbound_rel' ] );
371
- $href = wp_slimstat::string_to_array( wp_slimstat::$settings[ 'do_not_track_outbound_href' ] );
372
- wp_slimstat::$settings[ 'do_not_track_outbound_classes_rel_href' ] = implode( ',', array_merge( $classes, $rel, $href ) );
373
-
374
- // Make secret key really... secret!
375
- wp_slimstat::$settings[ 'secret' ] = wp_hash( uniqid( time(), true ) );
376
- }
377
- // --- END: Updates for version 3.9.6 ---
378
-
379
- // --- Updates for version 3.9.8.2 ---
380
- if ( version_compare( wp_slimstat::$settings[ 'version' ], '3.9.8.2', '<' ) ) {
381
- // The GeoLite DB is already installed, let's unzip it to improve the tracker's performance
382
- if ( file_exists( wp_slimstat::$maxmind_path.'.gz' ) ) {
383
- @unlink( wp_slimstat::$maxmind_path.'.gz' );
384
- wp_slimstat::download_maxmind_database();
385
- }
386
- }
387
- // --- END: Updates for version 3.9.8.2 ---
388
-
389
- // --- Updates for version 4.0 ---
390
- if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.0', '<' ) ) {
391
- $GLOBALS['wpdb']->query( "DELETE FROM {$GLOBALS[ 'wpdb' ]->prefix}usermeta WHERE meta_key LIKE 'meta-box-order_slimstat%'" );
392
-
393
- $have_innodb = $GLOBALS[ 'wpdb' ]->get_results( "SHOW VARIABLES LIKE 'have_innodb'", ARRAY_A );
394
- $use_innodb = ( !empty( $have_innodb[ 0 ] ) && $have_innodb[ 0 ][ 'Value' ] == 'YES' ) ? 'ENGINE=InnoDB' : '';
395
-
396
- // Create the new table
397
- self::_create_table ("
398
- CREATE TABLE IF NOT EXISTS {$GLOBALS['wpdb']->prefix}slim_stats_4 (
399
- id INT UNSIGNED NOT NULL auto_increment,
400
- ip INT UNSIGNED DEFAULT 0,
401
- other_ip INT UNSIGNED DEFAULT 0,
402
- username VARCHAR(255) DEFAULT NULL,
403
- country VARCHAR(16) DEFAULT NULL,
404
- referer VARCHAR(2048) DEFAULT NULL,
405
- resource VARCHAR(2048) DEFAULT NULL,
406
- searchterms VARCHAR(2048) DEFAULT NULL,
407
- plugins VARCHAR(255) DEFAULT NULL,
408
- notes VARCHAR(2048) DEFAULT NULL,
409
- visit_id INT UNSIGNED NOT NULL DEFAULT 0,
410
- server_latency INT(10) UNSIGNED DEFAULT 0,
411
- page_performance INT(10) UNSIGNED DEFAULT 0,
412
-
413
- browser VARCHAR(40) DEFAULT NULL,
414
- browser_version VARCHAR(15) DEFAULT NULL,
415
- browser_type TINYINT UNSIGNED DEFAULT 0,
416
- platform VARCHAR(15) DEFAULT NULL,
417
- language VARCHAR(5) DEFAULT NULL,
418
- user_agent VARCHAR(2048) DEFAULT NULL,
419
-
420
- resolution VARCHAR(12) DEFAULT NULL,
421
- screen_width SMALLINT UNSIGNED DEFAULT 0,
422
- screen_height SMALLINT UNSIGNED DEFAULT 0,
423
-
424
- content_type VARCHAR(64) DEFAULT NULL,
425
- category VARCHAR(256) DEFAULT NULL,
426
- author VARCHAR(64) DEFAULT NULL,
427
- content_id BIGINT(20) UNSIGNED DEFAULT 0,
428
-
429
- outbound_resource VARCHAR(2048) DEFAULT NULL,
430
-
431
- dt INT(10) UNSIGNED DEFAULT 0,
432
-
433
- CONSTRAINT PRIMARY KEY (id),
434
- INDEX idx_{$GLOBALS['wpdb']->prefix}slim_stats_dt (dt)
435
- ) COLLATE utf8_general_ci $use_innodb", $GLOBALS[ 'wpdb' ]->prefix . 'slim_stats_4', $my_wpdb );
436
-
437
- // Create the archive table
438
- $my_wpdb->query( "CREATE TABLE IF NOT EXISTS {$GLOBALS['wpdb']->prefix}slim_stats_archive_4 LIKE {$GLOBALS['wpdb']->prefix}slim_stats_4" );
439
-
440
- // Rename old and new tables
441
- $my_wpdb->query( "SET foreign_key_checks = 0" );
442
- $my_wpdb->query( "RENAME TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats TO {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_3" );
443
- $my_wpdb->query( "RENAME TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_4 TO {$GLOBALS[ 'wpdb' ]->prefix}slim_stats" );
444
- $my_wpdb->query( "RENAME TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_archive TO {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_archive_3" );
445
- $my_wpdb->query( "RENAME TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_archive_4 TO {$GLOBALS[ 'wpdb' ]->prefix}slim_stats_archive" );
446
-
447
- // Sometimes db users are not granted the capability to rename tables
448
- $slim_stats_4_exists = $my_wpdb->get_col( "SHOW TABLES LIKE '{$GLOBALS[ 'wpdb' ]->prefix}slim_stats_4'", 0 );
449
- $resolution_exists = $my_wpdb->get_results( "SHOW COLUMNS FROM {$GLOBALS[ 'wpdb' ]->prefix}slim_stats LIKE 'resolution'" );
450
-
451
- // Something went wrong during the upgrade
452
- if ( !empty( $slim_stats_4_exists ) && empty( $resolution_exists ) ) {
453
- self::$admin_notice = __( "Slimstat attempted to upgrade your database structure, but the procedure might not have been completed (temporary tables were detected in your database). This might be caused by restrictive user permissions that don't grant commands like RENAME, ALTER/CHANGE and others. You might need to manually consolidate your tables. No worries, we wrote a <a href='https://slimstat.freshdesk.com/support/solutions/articles/12000003148-how-do-i-update-the-table-structure-if-i-upgraded-from-a-version-prior-to-4-0' target='_blank'>step by step guide</a> on how to do that. Please feel free to contact our support team if you have any questions.", 'wp-slimstat' );
454
- self::$admin_notice .= '<br/><br/><a id="slimstat-hide-admin-notice" href="#" class="button-secondary">Got it, thanks</a>';
455
- return 0;
456
- }
457
-
458
- // Create the new events table
459
- $my_wpdb->query( "
460
- CREATE TABLE IF NOT EXISTS {$GLOBALS[ 'wpdb' ]->prefix}slim_events (
461
- event_id INT(10) NOT NULL AUTO_INCREMENT,
462
- type TINYINT UNSIGNED DEFAULT 0,
463
- event_description VARCHAR(64) DEFAULT NULL,
464
- notes VARCHAR(256) DEFAULT NULL,
465
- position VARCHAR(32) DEFAULT NULL,
466
- id INT UNSIGNED NOT NULL DEFAULT 0,
467
- dt INT(10) UNSIGNED DEFAULT 0,
468
-
469
- CONSTRAINT PRIMARY KEY (event_id),
470
- INDEX idx_{$GLOBALS['wpdb']->prefix}slim_events (dt),
471
- CONSTRAINT fk_{$GLOBALS['wpdb']->prefix}id FOREIGN KEY (id) REFERENCES {$GLOBALS['wpdb']->prefix}slim_stats(id) ON UPDATE CASCADE ON DELETE CASCADE
472
- ) COLLATE utf8_general_ci $use_innodb" );
473
-
474
- $my_wpdb->query( "
475
- INSERT INTO {$GLOBALS['wpdb']->prefix}slim_stats (
476
- id,
477
- ip,
478
- other_ip,
479
- username,
480
- country,
481
- referer,
482
- resource,
483
- searchterms,
484
- plugins,
485
- notes,
486
- visit_id,
487
- server_latency,
488
- page_performance,
489
-
490
- browser,
491
- browser_version,
492
- browser_type,
493
- platform,
494
- language,
495
- user_agent,
496
-
497
- screen_width,
498
- screen_height,
499
-
500
- content_type,
501
- category,
502
- author,
503
- content_id,
504
-
505
- outbound_resource,
506
-
507
- dt
508
- )
509
- SELECT
510
- t1.id,
511
- t1.ip,
512
- t1.other_ip,
513
- NULLIF(t1.user, ''),
514
- NULLIF(t1.country, ''),
515
- NULLIF(t1.referer, ''),
516
- NULLIF(t1.resource, ''),
517
- NULLIF(t1.searchterms, ''),
518
- NULLIF(t1.plugins, ''),
519
- NULLIF(t1.notes, ''),
520
- t1.visit_id,
521
- t1.server_latency,
522
- t1.page_performance,
523
-
524
- NULLIF(tb.browser, ''),
525
- NULLIF(tb.version, ''),
526
- tb.type,
527
- NULLIF(tb.platform, ''),
528
- NULLIF(t1.language, ''),
529
- NULLIF(tb.user_agent, ''),
530
-
531
- 9812,
532
- 9812,
533
-
534
- NULLIF(tci.content_type, ''),
535
- NULLIF(tci.category, ''),
536
- NULLIF(tci.author, ''),
537
- tci.content_id,
538
-
539
- NULL,
540
-
541
- t1.dt
542
-
543
- FROM {$GLOBALS['wpdb']->prefix}slim_stats_3 AS t1
544
- INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers AS tb ON t1.browser_id = tb.browser_id
545
- INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_content_info AS tci ON t1.content_info_id = tci.content_info_id" );
546
-
547
- // Copy the events
548
- $my_wpdb->query( "
549
- INSERT INTO {$GLOBALS['wpdb']->prefix}slim_events (
550
- type,
551
- event_description,
552
- notes,
553
- position,
554
- id,
555
- dt
556
- )
557
- SELECT
558
- tob.type,
559
- SUBSTRING(tob.notes, LOCATE('Event:', tob.notes)+6, LOCATE(',', tob.notes, LOCATE('Event:', tob.notes)+6) - LOCATE('Event:', tob.notes)-6),
560
- SUBSTRING(tob.notes, 1, LOCATE('Event:', tob.notes) - 3),
561
- tob.position,
562
- tob.id,
563
- tob.dt
564
- FROM {$GLOBALS['wpdb']->prefix}slim_outbound AS tob" );
565
-
566
- $my_wpdb->query( "SET foreign_key_checks = 1" );
567
- }
568
- // --- END: Updates for version 4.0 ---
569
-
570
- // --- Updates for version 4.1.3 ---
571
- if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.1.3', '<' ) ) {
572
- // Change column type to add IPv6 support
573
- $my_wpdb->query( "ALTER TABLE {$GLOBALS['wpdb']->prefix}slim_stats ADD ip_temp VARCHAR(39) DEFAULT NULL AFTER outbound_resource, ADD other_ip_temp VARCHAR(39) DEFAULT NULL AFTER id" );
574
- $my_wpdb->query( "UPDATE {$GLOBALS['wpdb']->prefix}slim_stats SET ip_temp = INET_NTOA(ip), other_ip_temp = INET_NTOA(other_ip)" );
575
- $my_wpdb->query( "ALTER TABLE {$GLOBALS['wpdb']->prefix}slim_stats CHANGE ip ip_num INT UNSIGNED DEFAULT 0" );
576
- $my_wpdb->query( "ALTER TABLE {$GLOBALS['wpdb']->prefix}slim_stats CHANGE other_ip other_ip_num INT UNSIGNED DEFAULT 0" );
577
- $my_wpdb->query( "ALTER TABLE {$GLOBALS['wpdb']->prefix}slim_stats CHANGE ip_temp ip VARCHAR(39) DEFAULT NULL AFTER id" );
578
- $my_wpdb->query( "ALTER TABLE {$GLOBALS['wpdb']->prefix}slim_stats CHANGE other_ip_temp other_ip VARCHAR(39) DEFAULT NULL AFTER ip" );
579
- }
580
- // --- END: Updates for version 4.1.3 ---
581
-
582
- // --- Updates for version 4.1.7 ---
583
- if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.1.7', '<' ) ) {
584
- // Change column type to add IPv6 support
585
- $my_wpdb->query( "ALTER TABLE {$GLOBALS[ 'wpdb' ]->prefix}slim_stats ADD dt_out INT(10) UNSIGNED DEFAULT 0 AFTER outbound_resource" );
586
- }
587
- // --- END: Updates for version 4.1.7 ---
588
-
589
  // --- Updates for version 4.2 ---
590
  if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.2', '<' ) ) {
591
  // Report arrangements are now stored as a global usermeta value. Migrate old values to new variable
592
  $current_user = wp_get_current_user();
593
- $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'yes' ) ? 'slimstat' : 'admin';
594
  $new_user_reports = array();
595
 
596
  for ( $i = 2; $i <= 5; $i++ ) {
@@ -618,7 +382,7 @@ class wp_slimstat_admin {
618
 
619
  // --- Updates for version 4.2.6 ---
620
  if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.2.6', '<' ) ) {
621
- wp_slimstat::$settings[ 'auto_purge_delete' ] = ( wp_slimstat::$settings[ 'auto_purge_delete' ] == 'yes' ) ? 'no' : 'yes';
622
  }
623
  // --- END: Updates for version 4.2.6 ---
624
 
@@ -658,6 +422,26 @@ class wp_slimstat_admin {
658
  }
659
  // --- END: Updates for version 4.4.5 ---
660
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  // Now we can update the version stored in the database
662
  wp_slimstat::$settings[ 'version' ] = wp_slimstat::$version;
663
 
@@ -773,7 +557,7 @@ class wp_slimstat_admin {
773
 
774
  // Get the current report assignments
775
  $new_entry = array();
776
- if ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'yes' || is_network_admin() ) {
777
  $parent = 'slimview1';
778
  $page_location = 'slimstat';
779
  $new_entry[] = add_menu_page(__('Slimstat','wp-slimstat'), __('Slimstat','wp-slimstat'), $minimum_capability, $parent, array( __CLASS__, 'wp_slimstat_include_view' ) );
@@ -829,7 +613,7 @@ class wp_slimstat_admin {
829
  $slimstat_view_url = get_admin_url($GLOBALS['blog_id'], "admin.php?page=");
830
  $slimstat_config_url = get_admin_url($GLOBALS['blog_id'], "admin.php?page=slimconfig");
831
 
832
- $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'yes' ) ? 'slimstat' : 'admin';
833
  $user_reports = get_user_option( "meta-box-order_{$page_location}_page_slimlayout", $GLOBALS[ 'current_user' ]->ID );
834
 
835
  $frontend_filter = '';
@@ -872,7 +656,7 @@ class wp_slimstat_admin {
872
  $minimum_capability = wp_slimstat::$settings[ 'capability_can_admin' ];
873
  }
874
 
875
- if ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'yes' ) {
876
  $new_entry = add_submenu_page( 'slimview1', __( 'Settings','wp-slimstat' ), __( 'Settings','wp-slimstat' ), $minimum_capability, 'slimconfig', array( __CLASS__, 'wp_slimstat_include_config' ) );
877
  }
878
  else {
@@ -928,7 +712,7 @@ class wp_slimstat_admin {
928
  wp_slimstat::$settings[ 'posts_column_day_interval' ] = 30;
929
  }
930
 
931
- if ( wp_slimstat::$settings[ 'posts_column_pageviews' ] == 'yes' ) {
932
  $_columns[ 'wp-slimstat' ] = '<span class="slimstat-icon" title="' . __( 'Pageviews in the last ' . wp_slimstat::$settings[ 'posts_column_day_interval' ] . ' days', 'wp-slimstat' ) . '"></span>';
933
  }
934
  else {
@@ -955,7 +739,7 @@ class wp_slimstat_admin {
955
  $parsed_permalink = $parsed_permalink[ 'path' ] . ( !empty( $parsed_permalink[ 'query' ] ) ? '?' . $parsed_permalink[ 'query' ] : '' );
956
  wp_slimstat_db::init( 'resource contains ' . $parsed_permalink . '&&&interval equals ' . wp_slimstat::$settings[ 'posts_column_day_interval' ] . '&&&interval_direction equals minus' );
957
 
958
- if ( wp_slimstat::$settings[ 'posts_column_pageviews' ] == 'yes' ) {
959
  $count = wp_slimstat_db::count_records();
960
  }
961
  else{
@@ -1042,15 +826,15 @@ class wp_slimstat_admin {
1042
  break;
1043
 
1044
  case 'wp_ajax_slimstat_hide_geolite_notice':
1045
- wp_slimstat::$settings[ 'no_maxmind_warning' ] = 'yes';
1046
  break;
1047
 
1048
  case 'wp_ajax_slimstat_hide_browscap_notice':
1049
- wp_slimstat::$settings[ 'no_browscap_warning' ] = 'yes';
1050
  break;
1051
 
1052
  case 'wp_ajax_slimstat_hide_caching_notice':
1053
- wp_slimstat::$settings[ 'no_caching_warning' ] = 'yes';
1054
  break;
1055
 
1056
  default:
@@ -1165,7 +949,7 @@ class wp_slimstat_admin {
1165
  <input class="slimstat-checkbox-toggle"
1166
  type="checkbox"
1167
  name="options[addon_network_settings_' . $_setting_slug . ']"' .
1168
- ( ( !empty( wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] ) && wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] == 'yes' ) ? ' checked="checked"' : '' ) . '
1169
  id="addon_network_settings_' . $_setting_slug . '"
1170
  data-size="mini" data-handle-width="50" data-on-color="warning" data-on-text="Network" data-off-text="Site">' : '';
1171
 
@@ -1191,7 +975,7 @@ class wp_slimstat_admin {
1191
  name="options[' . $_setting_slug . ']"
1192
  id="' . $_setting_slug . '"
1193
  data-size="mini" data-handle-width="50" data-on-color="success"' .
1194
- ( ( isset( wp_slimstat::$settings[ $_setting_slug ] ) && wp_slimstat::$settings[ $_setting_slug ] == 'yes' ) ? ' checked="checked"' : '' ) . '
1195
  data-on-text="' . ( !empty( $_setting_info[ 'custom_label_on' ] ) ? $_setting_info[ 'custom_label_on' ] : __( 'On', 'wp-slimstat' ) ) . '"
1196
  data-off-text="' . ( !empty( $_setting_info[ 'custom_label_off' ] ) ? $_setting_info[ 'custom_label_off' ] : __( 'Off', 'wp-slimstat' ) ) . '">' .
1197
  $network_override_checkbox . '
@@ -1280,14 +1064,9 @@ class wp_slimstat_admin {
1280
  continue;
1281
  }
1282
 
1283
- // For backward compatibility, we need to turn 'on' in 'yes'
1284
- if ( $_setting_info[ 'type' ] == 'toggle' ) {
1285
- if ( isset( $_POST[ 'options' ][ $_setting_slug ] ) && strtolower( $_POST[ 'options' ][ $_setting_slug ] == 'on' ) ) {
1286
- wp_slimstat::$settings[ $_setting_slug ] = 'yes';
1287
- }
1288
- else {
1289
- wp_slimstat::$settings[ $_setting_slug ] = 'no';
1290
- }
1291
  }
1292
  else if ( isset( $_POST[ 'options' ][ $_setting_slug ] ) ) {
1293
  wp_slimstat::$settings[ $_setting_slug ] = $_POST[ 'options' ][ $_setting_slug ];
@@ -1295,11 +1074,8 @@ class wp_slimstat_admin {
1295
 
1296
  // If the Network Settings add-on is enabled, there might be a switch to decide if this option needs to override what single sites have set
1297
  if ( is_network_admin() ) {
1298
- if ( isset( $_POST[ 'options' ][ 'addon_network_settings_' . $_setting_slug ] ) && strtolower( $_POST[ 'options' ][ 'addon_network_settings_' . $_setting_slug ] == 'on' ) ) {
1299
- wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] = 'yes';
1300
- }
1301
- else {
1302
- wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] = 'no';
1303
  }
1304
  }
1305
  else if ( isset( wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] ) ) {
11
  * Init -- Sets things up.
12
  */
13
  public static function init() {
14
+ self::$admin_notice = "As those who have been using Slimstat for a while know, we never stop doing our good share of research and development to improve our plugin. One feature on our wishlist was to make the geolocation functionality more accurate. Specifically, users have been asking us to track not just the Country of origin, but possibly the state and city. In order to geolocate visitors, our code has been leveraging a third-party data file provided by <a href='https://www.maxmind.com/en/home' target='_blank'>MaxMind.com</a>. A while ago, they launched a new data format, which improves performance and offers a way to quickly determine the city of origin. However, the new library required a higher version of PHP, and up until now we had been hesitant to adopt it, to allow more people to use our plugin, over the chance of offering this feature. Now, after spending some time combing through their code, we found a way to get the best of both worlds: by customizing their PHP library, we were able to make it work with PHP 5.3! Which means that now Slimstat is able to tell you your visitors' city of origin (and State, when applicable) right out of the box. This information is available in the Access Log report and in a new 'Top Cities' report under the Audience tab. Please note: the MaxMind data file to enable this feature is approximately 60 Mb, and for this reason <strong>this new functionality is not enabled by default</strong>. You must go to Slimstat > Settings > Tracker and turn on the corresponding option. Then go to Slimstat > Settings > Maintenance and uninstall/install the GeoLite file to download the one that contains the city data. Please feel free to contact us if you have any questions.";
 
 
15
 
16
  self::$admin_notice .= '<br/><br/><a id="slimstat-hide-admin-notice" href="#" class="button-secondary">Got it, thanks</a>';
17
 
113
  }
114
 
115
  // Remove spammers from the database
116
+ if ( wp_slimstat::$settings[ 'ignore_spammers' ] == 'on' ) {
117
  add_action('transition_comment_status', array(__CLASS__, 'remove_spam'), 15, 3);
118
  }
119
 
120
  // Add a menu to the admin bar ( this function is declared here and not in wp_slimstat_admin because the latter is only initialized if is_admin(), and not in the front-end )
121
+ if ( wp_slimstat::$settings[ 'use_separate_menu' ] != 'on' && is_admin_bar_showing() ) {
122
  add_action( 'admin_bar_menu', array( __CLASS__, 'wp_slimstat_adminbar' ), 100 );
123
  }
124
 
128
  add_action( 'admin_menu', array( __CLASS__, 'wp_slimstat_add_config_menu' ) );
129
 
130
  // Display the column in the Edit Posts / Pages screen
131
+ if ( wp_slimstat::$settings[ 'add_posts_column' ] == 'on' ) {
132
  $post_types = get_post_types( array( 'public' => true, 'show_ui' => true ), 'names' );
133
  include_once( dirname( __FILE__ ) . '/view/wp-slimstat-reports.php' );
134
  include_once( dirname( __FILE__ ) . '/view/wp-slimstat-db.php' );
167
  }
168
 
169
  // Dashboard Widgets
170
+ if ( wp_slimstat::$settings[ 'add_dashboard_widgets' ] == 'on' ) {
171
  $temp = strlen( $_SERVER['REQUEST_URI'] ) - 10;
172
 
173
  if( strpos( $_SERVER['REQUEST_URI'], 'index.php' ) !== false || ( $temp >= 0 && strpos($_SERVER['REQUEST_URI'], '/wp-admin/', $temp) !== false ) ) {
192
  }
193
 
194
  // Hide plugins
195
+ if ( wp_slimstat::$settings[ 'hide_addons' ] == 'on' ) {
196
  add_filter( 'all_plugins', array( __CLASS__, 'hide_addons' ) );
197
  }
198
  }
350
  public static function update_tables_and_options(){
351
  $my_wpdb = apply_filters('slimstat_custom_wpdb', $GLOBALS['wpdb']);
352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  // --- Updates for version 4.2 ---
354
  if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.2', '<' ) ) {
355
  // Report arrangements are now stored as a global usermeta value. Migrate old values to new variable
356
  $current_user = wp_get_current_user();
357
+ $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' ) ? 'slimstat' : 'admin';
358
  $new_user_reports = array();
359
 
360
  for ( $i = 2; $i <= 5; $i++ ) {
382
 
383
  // --- Updates for version 4.2.6 ---
384
  if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.2.6', '<' ) ) {
385
+ wp_slimstat::$settings[ 'auto_purge_delete' ] = ( wp_slimstat::$settings[ 'auto_purge_delete' ] == 'on' ) ? 'no' : 'on';
386
  }
387
  // --- END: Updates for version 4.2.6 ---
388
 
422
  }
423
  // --- END: Updates for version 4.4.5 ---
424
 
425
+ // --- Updates for version 4.7.2 ---
426
+ if ( version_compare( wp_slimstat::$settings[ 'version' ], '4.7.2', '<' ) ) {
427
+ // Changing our toggle option values from 'yes' to 'on'
428
+ foreach ( wp_slimstat::$settings as $a_key => $a_value ) {
429
+ if ( $a_value == 'yes' ) {
430
+ wp_slimstat::$settings[ $a_key ] = 'on';
431
+ }
432
+ }
433
+
434
+ // If MaxMind DB is enabled, download the new GeoLite 2 data file
435
+ $old_maxmind_path = str_replace( '.mmdb', '.dat', wp_slimstat::$maxmind_path );
436
+ if ( file_exists( $old_maxmind_path ) ) {
437
+ @unlink( $old_maxmind_path );
438
+ wp_slimstat::download_maxmind_database();
439
+ }
440
+
441
+ $my_wpdb->query( "ALTER TABLE {$GLOBALS['wpdb']->prefix}slim_stats ADD COLUMN city VARCHAR(255) DEFAULT NULL AFTER country, ADD COLUMN location VARCHAR(36) DEFAULT NULL AFTER country" );
442
+ }
443
+ // --- END: Updates for version 4.7.2 ---
444
+
445
  // Now we can update the version stored in the database
446
  wp_slimstat::$settings[ 'version' ] = wp_slimstat::$version;
447
 
557
 
558
  // Get the current report assignments
559
  $new_entry = array();
560
+ if ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' || is_network_admin() ) {
561
  $parent = 'slimview1';
562
  $page_location = 'slimstat';
563
  $new_entry[] = add_menu_page(__('Slimstat','wp-slimstat'), __('Slimstat','wp-slimstat'), $minimum_capability, $parent, array( __CLASS__, 'wp_slimstat_include_view' ) );
613
  $slimstat_view_url = get_admin_url($GLOBALS['blog_id'], "admin.php?page=");
614
  $slimstat_config_url = get_admin_url($GLOBALS['blog_id'], "admin.php?page=slimconfig");
615
 
616
+ $page_location = ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' ) ? 'slimstat' : 'admin';
617
  $user_reports = get_user_option( "meta-box-order_{$page_location}_page_slimlayout", $GLOBALS[ 'current_user' ]->ID );
618
 
619
  $frontend_filter = '';
656
  $minimum_capability = wp_slimstat::$settings[ 'capability_can_admin' ];
657
  }
658
 
659
+ if ( wp_slimstat::$settings[ 'use_separate_menu' ] == 'on' ) {
660
  $new_entry = add_submenu_page( 'slimview1', __( 'Settings','wp-slimstat' ), __( 'Settings','wp-slimstat' ), $minimum_capability, 'slimconfig', array( __CLASS__, 'wp_slimstat_include_config' ) );
661
  }
662
  else {
712
  wp_slimstat::$settings[ 'posts_column_day_interval' ] = 30;
713
  }
714
 
715
+ if ( wp_slimstat::$settings[ 'posts_column_pageviews' ] == 'on' ) {
716
  $_columns[ 'wp-slimstat' ] = '<span class="slimstat-icon" title="' . __( 'Pageviews in the last ' . wp_slimstat::$settings[ 'posts_column_day_interval' ] . ' days', 'wp-slimstat' ) . '"></span>';
717
  }
718
  else {
739
  $parsed_permalink = $parsed_permalink[ 'path' ] . ( !empty( $parsed_permalink[ 'query' ] ) ? '?' . $parsed_permalink[ 'query' ] : '' );
740
  wp_slimstat_db::init( 'resource contains ' . $parsed_permalink . '&&&interval equals ' . wp_slimstat::$settings[ 'posts_column_day_interval' ] . '&&&interval_direction equals minus' );
741
 
742
+ if ( wp_slimstat::$settings[ 'posts_column_pageviews' ] == 'on' ) {
743
  $count = wp_slimstat_db::count_records();
744
  }
745
  else{
826
  break;
827
 
828
  case 'wp_ajax_slimstat_hide_geolite_notice':
829
+ wp_slimstat::$settings[ 'no_maxmind_warning' ] = 'on';
830
  break;
831
 
832
  case 'wp_ajax_slimstat_hide_browscap_notice':
833
+ wp_slimstat::$settings[ 'no_browscap_warning' ] = 'on';
834
  break;
835
 
836
  case 'wp_ajax_slimstat_hide_caching_notice':
837
+ wp_slimstat::$settings[ 'no_caching_warning' ] = 'on';
838
  break;
839
 
840
  default:
949
  <input class="slimstat-checkbox-toggle"
950
  type="checkbox"
951
  name="options[addon_network_settings_' . $_setting_slug . ']"' .
952
+ ( ( !empty( wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] ) && wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] == 'on' ) ? ' checked="checked"' : '' ) . '
953
  id="addon_network_settings_' . $_setting_slug . '"
954
  data-size="mini" data-handle-width="50" data-on-color="warning" data-on-text="Network" data-off-text="Site">' : '';
955
 
975
  name="options[' . $_setting_slug . ']"
976
  id="' . $_setting_slug . '"
977
  data-size="mini" data-handle-width="50" data-on-color="success"' .
978
+ ( ( isset( wp_slimstat::$settings[ $_setting_slug ] ) && wp_slimstat::$settings[ $_setting_slug ] == 'on' ) ? ' checked="checked"' : '' ) . '
979
  data-on-text="' . ( !empty( $_setting_info[ 'custom_label_on' ] ) ? $_setting_info[ 'custom_label_on' ] : __( 'On', 'wp-slimstat' ) ) . '"
980
  data-off-text="' . ( !empty( $_setting_info[ 'custom_label_off' ] ) ? $_setting_info[ 'custom_label_off' ] : __( 'Off', 'wp-slimstat' ) ) . '">' .
981
  $network_override_checkbox . '
1064
  continue;
1065
  }
1066
 
1067
+ // An empty toggle option is saved in the database as 'no'
1068
+ if ( $_setting_info[ 'type' ] == 'toggle' && ( !isset( $_POST[ 'options' ][ $_setting_slug ] ) || strtolower( $_POST[ 'options' ][ $_setting_slug ] != 'on' ) ) ) {
1069
+ wp_slimstat::$settings[ $_setting_slug ] = 'no';
 
 
 
 
 
1070
  }
1071
  else if ( isset( $_POST[ 'options' ][ $_setting_slug ] ) ) {
1072
  wp_slimstat::$settings[ $_setting_slug ] = $_POST[ 'options' ][ $_setting_slug ];
1074
 
1075
  // If the Network Settings add-on is enabled, there might be a switch to decide if this option needs to override what single sites have set
1076
  if ( is_network_admin() ) {
1077
+ if ( !isset( $_POST[ 'options' ][ 'addon_network_settings_' . $_setting_slug ] ) || strtolower( $_POST[ 'options' ][ 'addon_network_settings_' . $_setting_slug ] != 'on' ) ) {
1078
+ wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] = 'no';
 
 
 
1079
  }
1080
  }
1081
  else if ( isset( wp_slimstat::$settings[ 'addon_network_settings_' . $_setting_slug ] ) ) {
maxmind.php CHANGED
@@ -1,33 +1,36 @@
1
  <?php
2
 
3
  class maxmind_geolite2_connector {
4
- public static $upload_dir = '';
5
- public static $maxmind_path = '';
6
 
7
- public static function get_geolocation_info( $_ip = '' ) {
 
8
 
9
- self::$upload_dir = wp_upload_dir();
10
- self::$upload_dir = self::$upload_dir[ 'basedir' ];
11
-
12
- if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
13
- self::$upload_dir = str_replace( '/sites/' . get_current_blog_id(), '', self::$upload_dir );
 
14
  }
15
-
16
- self::$upload_dir .= '/wp-slimstat';
17
- self::$upload_dir = apply_filters( 'slimstat_maxmind_path', self::$upload_dir );
18
-
19
- self::$maxmind_path = self::$upload_dir . '/GeoLite2-City.mmdb';
20
-
21
- $reader = new MaxMindReader( self::$maxmind_path );
22
- $record = $reader->get( $_ip );
23
-
24
- if ( !empty( $record[ 'city' ][ 'names' ][ 'en' ] ) ) {
25
- // Work in progress
 
 
 
 
26
  }
27
- }
28
 
29
- public function download_maxmind_db() {
30
- // Work in progress
31
  }
32
  }
33
 
@@ -36,590 +39,590 @@ class maxmind_geolite2_connector {
36
  * addresses can be looked up using the <code>get</code> method.
37
  */
38
  class MaxMindReader {
39
- private static $DATA_SECTION_SEPARATOR_SIZE = 16;
40
- private static $METADATA_START_MARKER = "\xAB\xCD\xEFMaxMind.com";
41
- private static $METADATA_START_MARKER_LENGTH = 14;
42
- private static $METADATA_MAX_SIZE = 131072; // 128 * 1024 = 128KB
43
-
44
- private $decoder;
45
- private $fileHandle;
46
- private $fileSize;
47
- private $ipV4Start;
48
- private $metadata;
49
-
50
- /**
51
- * Constructs a MaxMindReader for the MaxMind DB format. The file passed to it must
52
- * be a valid MaxMind DB file such as a GeoIp2 database file.
53
- *
54
- * @param string $database
55
- * the MaxMind DB file to use.
56
- * @throws \InvalidArgumentException for invalid database path or unknown arguments
57
- * @throws \MaxMind\Db\Reader\InvalidDatabaseException
58
- * if the database is invalid or there is an error reading
59
- * from it.
60
- */
61
- public function __construct($database)
62
- {
63
- if (func_num_args() != 1) {
64
- throw new \InvalidArgumentException(
65
- 'The constructor takes exactly one argument.'
66
- );
67
- }
68
-
69
- if (!is_readable($database)) {
70
- throw new \InvalidArgumentException(
71
- "The file \"$database\" does not exist or is not readable."
72
- );
73
- }
74
- $this->fileHandle = @fopen($database, 'rb');
75
- if ($this->fileHandle === false) {
76
- throw new \InvalidArgumentException(
77
- "Error opening \"$database\"."
78
- );
79
- }
80
- $this->fileSize = @filesize($database);
81
- if ($this->fileSize === false) {
82
- throw new \UnexpectedValueException(
83
- "Error determining the size of \"$database\"."
84
- );
85
- }
86
-
87
- $start = $this->findMetadataStart($database);
88
- $metadataDecoder = new MaxMindDecoder($this->fileHandle, $start);
89
- list($metadataArray) = $metadataDecoder->decode($start);
90
- $this->metadata = new MaxMindMetadata($metadataArray);
91
- $this->decoder = new MaxMindDecoder(
92
- $this->fileHandle,
93
- $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE
94
- );
95
- }
96
-
97
- /**
98
- * Looks up the <code>address</code> in the MaxMind DB.
99
- *
100
- * @param string $ipAddress
101
- * the IP address to look up.
102
- * @return array the record for the IP address.
103
- * @throws \BadMethodCallException if this method is called on a closed database.
104
- * @throws \InvalidArgumentException if something other than a single IP address is passed to the method.
105
- * @throws InvalidDatabaseException
106
- * if the database is invalid or there is an error reading
107
- * from it.
108
- */
109
- public function get($ipAddress)
110
- {
111
- if (func_num_args() != 1) {
112
- throw new \InvalidArgumentException(
113
- 'Method takes exactly one argument.'
114
- );
115
- }
116
-
117
- if (!is_resource($this->fileHandle)) {
118
- throw new \BadMethodCallException(
119
- 'Attempt to read from a closed MaxMind DB.'
120
- );
121
- }
122
-
123
- if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) {
124
- throw new \InvalidArgumentException(
125
- "The value \"$ipAddress\" is not a valid IP address."
126
- );
127
- }
128
-
129
- if ($this->metadata->ipVersion == 4 && strrpos($ipAddress, ':')) {
130
- throw new \InvalidArgumentException(
131
- "Error looking up $ipAddress. You attempted to look up an"
132
- . " IPv6 address in an IPv4-only database."
133
- );
134
- }
135
- $pointer = $this->findAddressInTree($ipAddress);
136
- if ($pointer == 0) {
137
- return null;
138
- }
139
- return $this->resolveDataPointer($pointer);
140
- }
141
-
142
- private function findAddressInTree($ipAddress)
143
- {
144
- // XXX - could simplify. Done as a byte array to ease porting
145
- $rawAddress = array_merge(unpack('C*', inet_pton($ipAddress)));
146
-
147
- $bitCount = count($rawAddress) * 8;
148
-
149
- // The first node of the tree is always node 0, at the beginning of the
150
- // value
151
- $node = $this->startNode($bitCount);
152
-
153
- for ($i = 0; $i < $bitCount; $i++) {
154
- if ($node >= $this->metadata->nodeCount) {
155
- break;
156
- }
157
- $tempBit = 0xFF & $rawAddress[$i >> 3];
158
- $bit = 1 & ($tempBit >> 7 - ($i % 8));
159
-
160
- $node = $this->readNode($node, $bit);
161
- }
162
- if ($node == $this->metadata->nodeCount) {
163
- // Record is empty
164
- return 0;
165
- } elseif ($node > $this->metadata->nodeCount) {
166
- // Record is a data pointer
167
- return $node;
168
- }
169
- throw new InvalidDatabaseException("Something bad happened");
170
- }
171
-
172
-
173
- private function startNode($length)
174
- {
175
- // Check if we are looking up an IPv4 address in an IPv6 tree. If this
176
- // is the case, we can skip over the first 96 nodes.
177
- if ($this->metadata->ipVersion == 6 && $length == 32) {
178
- return $this->ipV4StartNode();
179
- }
180
- // The first node of the tree is always node 0, at the beginning of the
181
- // value
182
- return 0;
183
- }
184
-
185
- private function ipV4StartNode()
186
- {
187
- // This is a defensive check. There is no reason to call this when you
188
- // have an IPv4 tree.
189
- if ($this->metadata->ipVersion == 4) {
190
- return 0;
191
- }
192
-
193
- if ($this->ipV4Start != 0) {
194
- return $this->ipV4Start;
195
- }
196
- $node = 0;
197
-
198
- for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; $i++) {
199
- $node = $this->readNode($node, 0);
200
- }
201
- $this->ipV4Start = $node;
202
- return $node;
203
- }
204
-
205
- private function readNode($nodeNumber, $index)
206
- {
207
- $baseOffset = $nodeNumber * $this->metadata->nodeByteSize;
208
-
209
- // XXX - probably could condense this.
210
- switch ($this->metadata->recordSize) {
211
- case 24:
212
- $bytes = MaxMindUtil::read($this->fileHandle, $baseOffset + $index * 3, 3);
213
- list(, $node) = unpack('N', "\x00" . $bytes);
214
- return $node;
215
- case 28:
216
- $middleByte = MaxMindUtil::read($this->fileHandle, $baseOffset + 3, 1);
217
- list(, $middle) = unpack('C', $middleByte);
218
- if ($index == 0) {
219
- $middle = (0xF0 & $middle) >> 4;
220
- } else {
221
- $middle = 0x0F & $middle;
222
- }
223
- $bytes = MaxMindUtil::read($this->fileHandle, $baseOffset + $index * 4, 3);
224
- list(, $node) = unpack('N', chr($middle) . $bytes);
225
- return $node;
226
- case 32:
227
- $bytes = MaxMindUtil::read($this->fileHandle, $baseOffset + $index * 4, 4);
228
- list(, $node) = unpack('N', $bytes);
229
- return $node;
230
- default:
231
- throw new InvalidDatabaseException(
232
- 'Unknown record size: '
233
- . $this->metadata->recordSize
234
- );
235
- }
236
- }
237
-
238
- private function resolveDataPointer($pointer)
239
- {
240
- $resolved = $pointer - $this->metadata->nodeCount
241
- + $this->metadata->searchTreeSize;
242
- if ($resolved > $this->fileSize) {
243
- throw new InvalidDatabaseException(
244
- "The MaxMind DB file's search tree is corrupt"
245
- );
246
- }
247
-
248
- list($data) = $this->decoder->decode($resolved);
249
- return $data;
250
- }
251
-
252
- /*
253
- * This is an extremely naive but reasonably readable implementation. There
254
- * are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
255
- * an issue, but I suspect it won't be.
256
- */
257
- private function findMetadataStart($filename)
258
- {
259
- $handle = $this->fileHandle;
260
- $fstat = fstat($handle);
261
- $fileSize = $fstat['size'];
262
- $marker = self::$METADATA_START_MARKER;
263
- $markerLength = self::$METADATA_START_MARKER_LENGTH;
264
- $metadataMaxLengthExcludingMarker
265
- = min(self::$METADATA_MAX_SIZE, $fileSize) - $markerLength;
266
-
267
- for ($i = 0; $i <= $metadataMaxLengthExcludingMarker; $i++) {
268
- for ($j = 0; $j < $markerLength; $j++) {
269
- fseek($handle, $fileSize - $i - $j - 1);
270
- $matchBit = fgetc($handle);
271
- if ($matchBit != $marker[$markerLength - $j - 1]) {
272
- continue 2;
273
- }
274
- }
275
- return $fileSize - $i;
276
- }
277
- throw new InvalidDatabaseException(
278
- "Error opening database file ($filename). " .
279
- 'Is this a valid MaxMind DB file?'
280
- );
281
- }
282
-
283
- /**
284
- * @throws \InvalidArgumentException if arguments are passed to the method.
285
- * @throws \BadMethodCallException if the database has been closed.
286
- * @return Metadata object for the database.
287
- */
288
- public function metadata()
289
- {
290
- if (func_num_args()) {
291
- throw new \InvalidArgumentException(
292
- 'Method takes no arguments.'
293
- );
294
- }
295
-
296
- // Not technically required, but this makes it consistent with
297
- // C extension and it allows us to change our implementation later.
298
- if (!is_resource($this->fileHandle)) {
299
- throw new \BadMethodCallException(
300
- 'Attempt to read from a closed MaxMind DB.'
301
- );
302
- }
303
-
304
- return $this->metadata;
305
- }
306
-
307
- /**
308
- * Closes the MaxMind DB and returns resources to the system.
309
- *
310
- * @throws \Exception
311
- * if an I/O error occurs.
312
- */
313
- public function close()
314
- {
315
- if (!is_resource($this->fileHandle)) {
316
- throw new \BadMethodCallException(
317
- 'Attempt to close a closed MaxMind DB.'
318
- );
319
- }
320
- fclose($this->fileHandle);
321
- }
322
  }
323
 
324
  class MaxMindDecoder {
325
- private $fileStream;
326
- private $pointerBase;
327
- // This is only used for unit testing
328
- private $pointerTestHack;
329
- private $switchByteOrder;
330
-
331
- private $types = array(
332
- 0 => 'extended',
333
- 1 => 'pointer',
334
- 2 => 'utf8_string',
335
- 3 => 'double',
336
- 4 => 'bytes',
337
- 5 => 'uint16',
338
- 6 => 'uint32',
339
- 7 => 'map',
340
- 8 => 'int32',
341
- 9 => 'uint64',
342
- 10 => 'uint128',
343
- 11 => 'array',
344
- 12 => 'container',
345
- 13 => 'end_marker',
346
- 14 => 'boolean',
347
- 15 => 'float',
348
- );
349
-
350
- public function __construct(
351
- $fileStream,
352
- $pointerBase = 0,
353
- $pointerTestHack = false
354
- ) {
355
- $this->fileStream = $fileStream;
356
- $this->pointerBase = $pointerBase;
357
- $this->pointerTestHack = $pointerTestHack;
358
-
359
- $this->switchByteOrder = $this->isPlatformLittleEndian();
360
- }
361
-
362
-
363
- public function decode($offset)
364
- {
365
- list(, $ctrlByte) = unpack(
366
- 'C',
367
- MaxMindUtil::read($this->fileStream, $offset, 1)
368
- );
369
- $offset++;
370
-
371
- $type = $this->types[$ctrlByte >> 5];
372
-
373
- // Pointers are a special case, we don't read the next $size bytes, we
374
- // use the size to determine the length of the pointer and then follow
375
- // it.
376
- if ($type == 'pointer') {
377
- list($pointer, $offset) = $this->decodePointer($ctrlByte, $offset);
378
-
379
- // for unit testing
380
- if ($this->pointerTestHack) {
381
- return array($pointer);
382
- }
383
-
384
- list($result) = $this->decode($pointer);
385
-
386
- return array($result, $offset);
387
- }
388
-
389
- if ($type == 'extended') {
390
- list(, $nextByte) = unpack(
391
- 'C',
392
- MaxMindUtil::read($this->fileStream, $offset, 1)
393
- );
394
-
395
- $typeNum = $nextByte + 7;
396
-
397
- if ($typeNum < 8) {
398
- throw new InvalidDatabaseException(
399
- "Something went horribly wrong in the decoder. An extended type "
400
- . "resolved to a type number < 8 ("
401
- . $this->types[$typeNum]
402
- . ")"
403
- );
404
- }
405
-
406
- $type = $this->types[$typeNum];
407
- $offset++;
408
- }
409
-
410
- list($size, $offset) = $this->sizeFromCtrlByte($ctrlByte, $offset);
411
-
412
- return $this->decodeByType($type, $offset, $size);
413
- }
414
-
415
- private function decodeByType($type, $offset, $size)
416
- {
417
- switch ($type) {
418
- case 'map':
419
- return $this->decodeMap($size, $offset);
420
- case 'array':
421
- return $this->decodeArray($size, $offset);
422
- case 'boolean':
423
- return array($this->decodeBoolean($size), $offset);
424
- }
425
-
426
- $newOffset = $offset + $size;
427
- $bytes = MaxMindUtil::read($this->fileStream, $offset, $size);
428
- switch ($type) {
429
- case 'utf8_string':
430
- return array($this->decodeString($bytes), $newOffset);
431
- case 'double':
432
- $this->verifySize(8, $size);
433
- return array($this->decodeDouble($bytes), $newOffset);
434
- case 'float':
435
- $this->verifySize(4, $size);
436
- return array($this->decodeFloat($bytes), $newOffset);
437
- case 'bytes':
438
- return array($bytes, $newOffset);
439
- case 'uint16':
440
- case 'uint32':
441
- return array($this->decodeUint($bytes), $newOffset);
442
- case 'int32':
443
- return array($this->decodeInt32($bytes), $newOffset);
444
- case 'uint64':
445
- case 'uint128':
446
- return array($this->decodeBigUint($bytes, $size), $newOffset);
447
- default:
448
- throw new InvalidDatabaseException(
449
- "Unknown or unexpected type: " . $type
450
- );
451
- }
452
- }
453
-
454
- private function verifySize($expected, $actual)
455
- {
456
- if ($expected != $actual) {
457
- throw new InvalidDatabaseException(
458
- "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"
459
- );
460
- }
461
- }
462
-
463
- private function decodeArray($size, $offset)
464
- {
465
- $array = array();
466
-
467
- for ($i = 0; $i < $size; $i++) {
468
- list($value, $offset) = $this->decode($offset);
469
- array_push($array, $value);
470
- }
471
-
472
- return array($array, $offset);
473
- }
474
-
475
- private function decodeBoolean($size)
476
- {
477
- return $size == 0 ? false : true;
478
- }
479
-
480
- private function decodeDouble($bits)
481
- {
482
- // XXX - Assumes IEEE 754 double on platform
483
- list(, $double) = unpack('d', $this->maybeSwitchByteOrder($bits));
484
- return $double;
485
- }
486
-
487
- private function decodeFloat($bits)
488
- {
489
- // XXX - Assumes IEEE 754 floats on platform
490
- list(, $float) = unpack('f', $this->maybeSwitchByteOrder($bits));
491
- return $float;
492
- }
493
-
494
- private function decodeInt32($bytes)
495
- {
496
- $bytes = $this->zeroPadLeft($bytes, 4);
497
- list(, $int) = unpack('l', $this->maybeSwitchByteOrder($bytes));
498
- return $int;
499
- }
500
-
501
- private function decodeMap($size, $offset)
502
- {
503
-
504
- $map = array();
505
-
506
- for ($i = 0; $i < $size; $i++) {
507
- list($key, $offset) = $this->decode($offset);
508
- list($value, $offset) = $this->decode($offset);
509
- $map[$key] = $value;
510
- }
511
-
512
- return array($map, $offset);
513
- }
514
-
515
- private $pointerValueOffset = array(
516
- 1 => 0,
517
- 2 => 2048,
518
- 3 => 526336,
519
- 4 => 0,
520
- );
521
-
522
- private function decodePointer($ctrlByte, $offset)
523
- {
524
- $pointerSize = (($ctrlByte >> 3) & 0x3) + 1;
525
-
526
- $buffer = MaxMindUtil::read($this->fileStream, $offset, $pointerSize);
527
- $offset = $offset + $pointerSize;
528
-
529
- $packed = $pointerSize == 4
530
- ? $buffer
531
- : (pack('C', $ctrlByte & 0x7)) . $buffer;
532
-
533
- $unpacked = $this->decodeUint($packed);
534
- $pointer = $unpacked + $this->pointerBase
535
- + $this->pointerValueOffset[$pointerSize];
536
-
537
- return array($pointer, $offset);
538
- }
539
-
540
- private function decodeUint($bytes)
541
- {
542
- list(, $int) = unpack('N', $this->zeroPadLeft($bytes, 4));
543
- return $int;
544
- }
545
-
546
- private function decodeBigUint($bytes, $byteLength)
547
- {
548
- $maxUintBytes = log(PHP_INT_MAX, 2) / 8;
549
-
550
- if ($byteLength == 0) {
551
- return 0;
552
- }
553
-
554
- $numberOfLongs = ceil($byteLength / 4);
555
- $paddedLength = $numberOfLongs * 4;
556
- $paddedBytes = $this->zeroPadLeft($bytes, $paddedLength);
557
- $unpacked = array_merge(unpack("N$numberOfLongs", $paddedBytes));
558
-
559
- $integer = 0;
560
-
561
- // 2^32
562
- $twoTo32 = '4294967296';
563
-
564
- foreach ($unpacked as $part) {
565
- // We only use gmp or bcmath if the final value is too big
566
- if ($byteLength <= $maxUintBytes) {
567
- $integer = ($integer << 32) + $part;
568
- } elseif (extension_loaded('gmp')) {
569
- $integer = gmp_strval(gmp_add(gmp_mul($integer, $twoTo32), $part));
570
- } elseif (extension_loaded('bcmath')) {
571
- $integer = bcadd(bcmul($integer, $twoTo32), $part);
572
- } else {
573
- throw new \RuntimeException(
574
- 'The gmp or bcmath extension must be installed to read this database.'
575
- );
576
- }
577
- }
578
- return $integer;
579
- }
580
-
581
- private function decodeString($bytes)
582
- {
583
- // XXX - NOOP. As far as I know, the end user has to explicitly set the
584
- // encoding in PHP. Strings are just bytes.
585
- return $bytes;
586
- }
587
-
588
- private function sizeFromCtrlByte($ctrlByte, $offset)
589
- {
590
- $size = $ctrlByte & 0x1f;
591
- $bytesToRead = $size < 29 ? 0 : $size - 28;
592
- $bytes = MaxMindUtil::read($this->fileStream, $offset, $bytesToRead);
593
- $decoded = $this->decodeUint($bytes);
594
-
595
- if ($size == 29) {
596
- $size = 29 + $decoded;
597
- } elseif ($size == 30) {
598
- $size = 285 + $decoded;
599
- } elseif ($size > 30) {
600
- $size = ($decoded & (0x0FFFFFFF >> (32 - (8 * $bytesToRead))))
601
- + 65821;
602
- }
603
-
604
- return array($size, $offset + $bytesToRead);
605
- }
606
-
607
- private function zeroPadLeft($content, $desiredLength)
608
- {
609
- return str_pad($content, $desiredLength, "\x00", STR_PAD_LEFT);
610
- }
611
-
612
- private function maybeSwitchByteOrder($bytes)
613
- {
614
- return $this->switchByteOrder ? strrev($bytes) : $bytes;
615
- }
616
-
617
- private function isPlatformLittleEndian()
618
- {
619
- $testint = 0x00FF;
620
- $packed = pack('S', $testint);
621
- return $testint === current(unpack('v', $packed));
622
- }
623
  }
624
 
625
  /**
@@ -666,57 +669,57 @@ class InvalidDatabaseException extends \Exception
666
  * values will be a description in that language as a UTF-8 string. May be
667
  * undefined for some databases.
668
  */
669
- class MaxMindDecoder {
670
- private $binaryFormatMajorVersion;
671
- private $binaryFormatMinorVersion;
672
- private $buildEpoch;
673
- private $databaseType;
674
- private $description;
675
- private $ipVersion;
676
- private $languages;
677
- private $nodeByteSize;
678
- private $nodeCount;
679
- private $recordSize;
680
- private $searchTreeSize;
681
-
682
- public function __construct($metadata) {
683
- $this->binaryFormatMajorVersion =
684
- $metadata['binary_format_major_version'];
685
- $this->binaryFormatMinorVersion =
686
- $metadata['binary_format_minor_version'];
687
- $this->buildEpoch = $metadata['build_epoch'];
688
- $this->databaseType = $metadata['database_type'];
689
- $this->languages = $metadata['languages'];
690
- $this->description = $metadata['description'];
691
- $this->ipVersion = $metadata['ip_version'];
692
- $this->nodeCount = $metadata['node_count'];
693
- $this->recordSize = $metadata['record_size'];
694
- $this->nodeByteSize = $this->recordSize / 4;
695
- $this->searchTreeSize = $this->nodeCount * $this->nodeByteSize;
696
- }
697
-
698
- public function __get($var) {
699
- return $this->$var;
700
- }
701
  }
702
 
703
  class MaxMindUtil {
704
- public static function read( $stream, $offset, $numberOfBytes ) {
705
- if ( $numberOfBytes == 0 ) {
706
- return '';
707
- }
708
- if ( fseek( $stream, $offset ) == 0 ) {
709
- $value = fread( $stream, $numberOfBytes );
710
-
711
- // We check that the number of bytes read is equal to the number
712
- // asked for. We use ftell as getting the length of $value is
713
- // much slower.
714
- if ( ftell( $stream ) - $offset === $numberOfBytes ) {
715
- return $value;
716
- }
717
- }
718
- throw new InvalidDatabaseException(
719
- "The MaxMind DB file contains bad data"
720
- );
721
- }
722
  }
1
  <?php
2
 
3
  class maxmind_geolite2_connector {
4
+ public static function get_geolocation_info( $_ip_address = '' ) {
 
5
 
6
+ $ipnum = sprintf( '%u', ip2long( $_ip_address ) );
7
+ $geo_output = array( 'country' => array( 'iso_code' => 'xx' ) );
8
 
9
+ // Is this a RFC1918 (local) IP?
10
+ if ( $ipnum == 2130706433 || // 127.0.0.1
11
+ ( $ipnum >= 167772160 && $ipnum <= 184549375 ) || // 10.0.0.1 - 10.255.255.255
12
+ ( $ipnum >= 2886729728 && $ipnum <= 2887778303 ) || // 172.16.0.1 - 172.31.255.255
13
+ ( $ipnum >= 3232235521 && $ipnum <= 3232301055 ) ) { // 192.168.0.1 - 192.168.255.255
14
+ $geo_output[ 'country' ][ 'iso_code' ] = 'xy';
15
  }
16
+ else if ( file_exists( wp_slimstat::$maxmind_path ) ) {
17
+ // Do we need to update our data file?
18
+ if ( false !== ( $file_stat = stat( wp_slimstat::$maxmind_path ) ) ) {
19
+ // Is the database more than 30 days old?
20
+ if ( ( date( 'U' ) - $file_stat[ 'mtime' ] > 2629740 ) ) {
21
+ add_action( 'shutdown', array( 'wp_slimstat', 'download_maxmind_database' ) );
22
+ }
23
+ }
24
+
25
+ $reader = new MaxMindReader( wp_slimstat::$maxmind_path );
26
+ $geo_maxmind = $reader->get( $_ip_address );
27
+
28
+ if ( !empty( $geo_maxmind ) ) {
29
+ $geo_output = $geo_maxmind;
30
+ }
31
  }
 
32
 
33
+ return apply_filters( 'slimstat_get_country', $geo_output, $_ip_address );
 
34
  }
35
  }
36
 
39
  * addresses can be looked up using the <code>get</code> method.
40
  */
41
  class MaxMindReader {
42
+ private static $DATA_SECTION_SEPARATOR_SIZE = 16;
43
+ private static $METADATA_START_MARKER = "\xAB\xCD\xEFMaxMind.com";
44
+ private static $METADATA_START_MARKER_LENGTH = 14;
45
+ private static $METADATA_MAX_SIZE = 131072; // 128 * 1024 = 128KB
46
+
47
+ private $decoder;
48
+ private $fileHandle;
49
+ private $fileSize;
50
+ private $ipV4Start;
51
+ private $metadata;
52
+
53
+ /**
54
+ * Constructs a MaxMindReader for the MaxMind DB format. The file passed to it must
55
+ * be a valid MaxMind DB file such as a GeoIp2 database file.
56
+ *
57
+ * @param string $database
58
+ * the MaxMind DB file to use.
59
+ * @throws \InvalidArgumentException for invalid database path or unknown arguments
60
+ * @throws \MaxMind\Db\Reader\InvalidDatabaseException
61
+ * if the database is invalid or there is an error reading
62
+ * from it.
63
+ */
64
+ public function __construct($database)
65
+ {
66
+ if (func_num_args() != 1) {
67
+ throw new \InvalidArgumentException(
68
+ 'The constructor takes exactly one argument.'
69
+ );
70
+ }
71
+
72
+ if (!is_readable($database)) {
73
+ throw new \InvalidArgumentException(
74
+ "The file \"$database\" does not exist or is not readable."
75
+ );
76
+ }
77
+ $this->fileHandle = @fopen($database, 'rb');
78
+ if ($this->fileHandle === false) {
79
+ throw new \InvalidArgumentException(
80
+ "Error opening \"$database\"."
81
+ );
82
+ }
83
+ $this->fileSize = @filesize($database);
84
+ if ($this->fileSize === false) {
85
+ throw new \UnexpectedValueException(
86
+ "Error determining the size of \"$database\"."
87
+ );
88
+ }
89
+
90
+ $start = $this->findMetadataStart($database);
91
+ $metadataDecoder = new MaxMindDecoder($this->fileHandle, $start);
92
+ list($metadataArray) = $metadataDecoder->decode($start);
93
+ $this->metadata = new MaxMindMetadata($metadataArray);
94
+ $this->decoder = new MaxMindDecoder(
95
+ $this->fileHandle,
96
+ $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Looks up the <code>address</code> in the MaxMind DB.
102
+ *
103
+ * @param string $ipAddress
104
+ * the IP address to look up.
105
+ * @return array the record for the IP address.
106
+ * @throws \BadMethodCallException if this method is called on a closed database.
107
+ * @throws \InvalidArgumentException if something other than a single IP address is passed to the method.
108
+ * @throws InvalidDatabaseException
109
+ * if the database is invalid or there is an error reading
110
+ * from it.
111
+ */
112
+ public function get($ipAddress)
113
+ {
114
+ if (func_num_args() != 1) {
115
+ throw new \InvalidArgumentException(
116
+ 'Method takes exactly one argument.'
117
+ );
118
+ }
119
+
120
+ if (!is_resource($this->fileHandle)) {
121
+ throw new \BadMethodCallException(
122
+ 'Attempt to read from a closed MaxMind DB.'
123
+ );
124
+ }
125
+
126
+ if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) {
127
+ throw new \InvalidArgumentException(
128
+ "The value \"$ipAddress\" is not a valid IP address."
129
+ );
130
+ }
131
+
132
+ if ($this->metadata->ipVersion == 4 && strrpos($ipAddress, ':')) {
133
+ throw new \InvalidArgumentException(
134
+ "Error looking up $ipAddress. You attempted to look up an"
135
+ . " IPv6 address in an IPv4-only database."
136
+ );
137
+ }
138
+ $pointer = $this->findAddressInTree($ipAddress);
139
+ if ($pointer == 0) {
140
+ return null;
141
+ }
142
+ return $this->resolveDataPointer($pointer);
143
+ }
144
+
145
+ private function findAddressInTree($ipAddress)
146
+ {
147
+ // XXX - could simplify. Done as a byte array to ease porting
148
+ $rawAddress = array_merge(unpack('C*', inet_pton($ipAddress)));
149
+
150
+ $bitCount = count($rawAddress) * 8;
151
+
152
+ // The first node of the tree is always node 0, at the beginning of the
153
+ // value
154
+ $node = $this->startNode($bitCount);
155
+
156
+ for ($i = 0; $i < $bitCount; $i++) {
157
+ if ($node >= $this->metadata->nodeCount) {
158
+ break;
159
+ }
160
+ $tempBit = 0xFF & $rawAddress[$i >> 3];
161
+ $bit = 1 & ($tempBit >> 7 - ($i % 8));
162
+
163
+ $node = $this->readNode($node, $bit);
164
+ }
165
+ if ($node == $this->metadata->nodeCount) {
166
+ // Record is empty
167
+ return 0;
168
+ } elseif ($node > $this->metadata->nodeCount) {
169
+ // Record is a data pointer
170
+ return $node;
171
+ }
172
+ throw new InvalidDatabaseException("Something bad happened");
173
+ }
174
+
175
+
176
+ private function startNode($length)
177
+ {
178
+ // Check if we are looking up an IPv4 address in an IPv6 tree. If this
179
+ // is the case, we can skip over the first 96 nodes.
180
+ if ($this->metadata->ipVersion == 6 && $length == 32) {
181
+ return $this->ipV4StartNode();
182
+ }
183
+ // The first node of the tree is always node 0, at the beginning of the
184
+ // value
185
+ return 0;
186
+ }
187
+
188
+ private function ipV4StartNode()
189
+ {
190
+ // This is a defensive check. There is no reason to call this when you
191
+ // have an IPv4 tree.
192
+ if ($this->metadata->ipVersion == 4) {
193
+ return 0;
194
+ }
195
+
196
+ if ($this->ipV4Start != 0) {
197
+ return $this->ipV4Start;
198
+ }
199
+ $node = 0;
200
+
201
+ for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; $i++) {
202
+ $node = $this->readNode($node, 0);
203
+ }
204
+ $this->ipV4Start = $node;
205
+ return $node;
206
+ }
207
+
208
+ private function readNode($nodeNumber, $index)
209
+ {
210
+ $baseOffset = $nodeNumber * $this->metadata->nodeByteSize;
211
+
212
+ // XXX - probably could condense this.
213
+ switch ($this->metadata->recordSize) {
214
+ case 24:
215
+ $bytes = MaxMindUtil::read($this->fileHandle, $baseOffset + $index * 3, 3);
216
+ list(, $node) = unpack('N', "\x00" . $bytes);
217
+ return $node;
218
+ case 28:
219
+ $middleByte = MaxMindUtil::read($this->fileHandle, $baseOffset + 3, 1);
220
+ list(, $middle) = unpack('C', $middleByte);
221
+ if ($index == 0) {
222
+ $middle = (0xF0 & $middle) >> 4;
223
+ } else {
224
+ $middle = 0x0F & $middle;
225
+ }
226
+ $bytes = MaxMindUtil::read($this->fileHandle, $baseOffset + $index * 4, 3);
227
+ list(, $node) = unpack('N', chr($middle) . $bytes);
228
+ return $node;
229
+ case 32:
230
+ $bytes = MaxMindUtil::read($this->fileHandle, $baseOffset + $index * 4, 4);
231
+ list(, $node) = unpack('N', $bytes);
232
+ return $node;
233
+ default:
234
+ throw new InvalidDatabaseException(
235
+ 'Unknown record size: '
236
+ . $this->metadata->recordSize
237
+ );
238
+ }
239
+ }
240
+
241
+ private function resolveDataPointer($pointer)
242
+ {
243
+ $resolved = $pointer - $this->metadata->nodeCount
244
+ + $this->metadata->searchTreeSize;
245
+ if ($resolved > $this->fileSize) {
246
+ throw new InvalidDatabaseException(
247
+ "The MaxMind DB file's search tree is corrupt"
248
+ );
249
+ }
250
+
251
+ list($data) = $this->decoder->decode($resolved);
252
+ return $data;
253
+ }
254
+
255
+ /*
256
+ * This is an extremely naive but reasonably readable implementation. There
257
+ * are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
258
+ * an issue, but I suspect it won't be.
259
+ */
260
+ private function findMetadataStart($filename)
261
+ {
262
+ $handle = $this->fileHandle;
263
+ $fstat = fstat($handle);
264
+ $fileSize = $fstat['size'];
265
+ $marker = self::$METADATA_START_MARKER;
266
+ $markerLength = self::$METADATA_START_MARKER_LENGTH;
267
+ $metadataMaxLengthExcludingMarker
268
+ = min(self::$METADATA_MAX_SIZE, $fileSize) - $markerLength;
269
+
270
+ for ($i = 0; $i <= $metadataMaxLengthExcludingMarker; $i++) {
271
+ for ($j = 0; $j < $markerLength; $j++) {
272
+ fseek($handle, $fileSize - $i - $j - 1);
273
+ $matchBit = fgetc($handle);
274
+ if ($matchBit != $marker[$markerLength - $j - 1]) {
275
+ continue 2;
276
+ }
277
+ }
278
+ return $fileSize - $i;
279
+ }
280
+ throw new InvalidDatabaseException(
281
+ "Error opening database file ($filename). " .
282
+ 'Is this a valid MaxMind DB file?'
283
+ );
284
+ }
285
+
286
+ /**
287
+ * @throws \InvalidArgumentException if arguments are passed to the method.
288
+ * @throws \BadMethodCallException if the database has been closed.
289
+ * @return Metadata object for the database.
290
+ */
291
+ public function metadata()
292
+ {
293
+ if (func_num_args()) {
294
+ throw new \InvalidArgumentException(
295
+ 'Method takes no arguments.'
296
+ );
297
+ }
298
+
299
+ // Not technically required, but this makes it consistent with
300
+ // C extension and it allows us to change our implementation later.
301
+ if (!is_resource($this->fileHandle)) {
302
+ throw new \BadMethodCallException(
303
+ 'Attempt to read from a closed MaxMind DB.'
304
+ );
305
+ }
306
+
307
+ return $this->metadata;
308
+ }
309
+
310
+ /**
311
+ * Closes the MaxMind DB and returns resources to the system.
312
+ *
313
+ * @throws \Exception
314
+ * if an I/O error occurs.
315
+ */
316
+ public function close()
317
+ {
318
+ if (!is_resource($this->fileHandle)) {
319
+ throw new \BadMethodCallException(
320
+ 'Attempt to close a closed MaxMind DB.'
321
+ );
322
+ }
323
+ fclose($this->fileHandle);
324
+ }
325
  }
326
 
327
  class MaxMindDecoder {
328
+ private $fileStream;
329
+ private $pointerBase;
330
+ // This is only used for unit testing
331
+ private $pointerTestHack;
332
+ private $switchByteOrder;
333
+
334
+ private $types = array(
335
+ 0 => 'extended',
336
+ 1 => 'pointer',
337
+ 2 => 'utf8_string',
338
+ 3 => 'double',
339
+ 4 => 'bytes',
340
+ 5 => 'uint16',
341
+ 6 => 'uint32',
342
+ 7 => 'map',
343
+ 8 => 'int32',
344
+ 9 => 'uint64',
345
+ 10 => 'uint128',
346
+ 11 => 'array',
347
+ 12 => 'container',
348
+ 13 => 'end_marker',
349
+ 14 => 'boolean',
350
+ 15 => 'float',
351
+ );
352
+
353
+ public function __construct(
354
+ $fileStream,
355
+ $pointerBase = 0,
356
+ $pointerTestHack = false
357
+ ) {
358
+ $this->fileStream = $fileStream;
359
+ $this->pointerBase = $pointerBase;
360
+ $this->pointerTestHack = $pointerTestHack;
361
+
362
+ $this->switchByteOrder = $this->isPlatformLittleEndian();
363
+ }
364
+
365
+
366
+ public function decode($offset)
367
+ {
368
+ list(, $ctrlByte) = unpack(
369
+ 'C',
370
+ MaxMindUtil::read($this->fileStream, $offset, 1)
371
+ );
372
+ $offset++;
373
+
374
+ $type = $this->types[$ctrlByte >> 5];
375
+
376
+ // Pointers are a special case, we don't read the next $size bytes, we
377
+ // use the size to determine the length of the pointer and then follow
378
+ // it.
379
+ if ($type == 'pointer') {
380
+ list($pointer, $offset) = $this->decodePointer($ctrlByte, $offset);
381
+
382
+ // for unit testing
383
+ if ($this->pointerTestHack) {
384
+ return array($pointer);
385
+ }
386
+
387
+ list($result) = $this->decode($pointer);
388
+
389
+ return array($result, $offset);
390
+ }
391
+
392
+ if ($type == 'extended') {
393
+ list(, $nextByte) = unpack(
394
+ 'C',
395
+ MaxMindUtil::read($this->fileStream, $offset, 1)
396
+ );
397
+
398
+ $typeNum = $nextByte + 7;
399
+
400
+ if ($typeNum < 8) {
401
+ throw new InvalidDatabaseException(
402
+ "Something went horribly wrong in the decoder. An extended type "
403
+ . "resolved to a type number < 8 ("
404
+ . $this->types[$typeNum]
405
+ . ")"
406
+ );
407
+ }
408
+
409
+ $type = $this->types[$typeNum];
410
+ $offset++;
411
+ }
412
+
413
+ list($size, $offset) = $this->sizeFromCtrlByte($ctrlByte, $offset);
414
+
415
+ return $this->decodeByType($type, $offset, $size);
416
+ }
417
+
418
+ private function decodeByType($type, $offset, $size)
419
+ {
420
+ switch ($type) {
421
+ case 'map':
422
+ return $this->decodeMap($size, $offset);
423
+ case 'array':
424
+ return $this->decodeArray($size, $offset);
425
+ case 'boolean':
426
+ return array($this->decodeBoolean($size), $offset);
427
+ }
428
+
429
+ $newOffset = $offset + $size;
430
+ $bytes = MaxMindUtil::read($this->fileStream, $offset, $size);
431
+ switch ($type) {
432
+ case 'utf8_string':
433
+ return array($this->decodeString($bytes), $newOffset);
434
+ case 'double':
435
+ $this->verifySize(8, $size);
436
+ return array($this->decodeDouble($bytes), $newOffset);
437
+ case 'float':
438
+ $this->verifySize(4, $size);
439
+ return array($this->decodeFloat($bytes), $newOffset);
440
+ case 'bytes':
441
+ return array($bytes, $newOffset);
442
+ case 'uint16':
443
+ case 'uint32':
444
+ return array($this->decodeUint($bytes), $newOffset);
445
+ case 'int32':
446
+ return array($this->decodeInt32($bytes), $newOffset);
447
+ case 'uint64':
448
+ case 'uint128':
449
+ return array($this->decodeBigUint($bytes, $size), $newOffset);
450
+ default:
451
+ throw new InvalidDatabaseException(
452
+ "Unknown or unexpected type: " . $type
453
+ );
454
+ }
455
+ }
456
+
457
+ private function verifySize($expected, $actual)
458
+ {
459
+ if ($expected != $actual) {
460
+ throw new InvalidDatabaseException(
461
+ "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"
462
+ );
463
+ }
464
+ }
465
+
466
+ private function decodeArray($size, $offset)
467
+ {
468
+ $array = array();
469
+
470
+ for ($i = 0; $i < $size; $i++) {
471
+ list($value, $offset) = $this->decode($offset);
472
+ array_push($array, $value);
473
+ }
474
+
475
+ return array($array, $offset);
476
+ }
477
+
478
+ private function decodeBoolean($size)
479
+ {
480
+ return $size == 0 ? false : true;
481
+ }
482
+
483
+ private function decodeDouble($bits)
484
+ {
485
+ // XXX - Assumes IEEE 754 double on platform
486
+ list(, $double) = unpack('d', $this->maybeSwitchByteOrder($bits));
487
+ return $double;
488
+ }
489
+
490
+ private function decodeFloat($bits)
491
+ {
492
+ // XXX - Assumes IEEE 754 floats on platform
493
+ list(, $float) = unpack('f', $this->maybeSwitchByteOrder($bits));
494
+ return $float;
495
+ }
496
+
497
+ private function decodeInt32($bytes)
498
+ {
499
+ $bytes = $this->zeroPadLeft($bytes, 4);
500
+ list(, $int) = unpack('l', $this->maybeSwitchByteOrder($bytes));
501
+ return $int;
502
+ }
503
+
504
+ private function decodeMap($size, $offset)
505
+ {
506
+
507
+ $map = array();
508
+
509
+ for ($i = 0; $i < $size; $i++) {
510
+ list($key, $offset) = $this->decode($offset);
511
+ list($value, $offset) = $this->decode($offset);
512
+ $map[$key] = $value;
513
+ }
514
+
515
+ return array($map, $offset);
516
+ }
517
+
518
+ private $pointerValueOffset = array(
519
+ 1 => 0,
520
+ 2 => 2048,
521
+ 3 => 526336,
522
+ 4 => 0,
523
+ );
524
+
525
+ private function decodePointer($ctrlByte, $offset)
526
+ {
527
+ $pointerSize = (($ctrlByte >> 3) & 0x3) + 1;
528
+
529
+ $buffer = MaxMindUtil::read($this->fileStream, $offset, $pointerSize);
530
+ $offset = $offset + $pointerSize;
531
+
532
+ $packed = $pointerSize == 4
533
+ ? $buffer
534
+ : (pack('C', $ctrlByte & 0x7)) . $buffer;
535
+
536
+ $unpacked = $this->decodeUint($packed);
537
+ $pointer = $unpacked + $this->pointerBase
538
+ + $this->pointerValueOffset[$pointerSize];
539
+
540
+ return array($pointer, $offset);
541
+ }
542
+
543
+ private function decodeUint($bytes)
544
+ {
545
+ list(, $int) = unpack('N', $this->zeroPadLeft($bytes, 4));
546
+ return $int;
547
+ }
548
+
549
+ private function decodeBigUint($bytes, $byteLength)
550
+ {
551
+ $maxUintBytes = log(PHP_INT_MAX, 2) / 8;
552
+
553
+ if ($byteLength == 0) {
554
+ return 0;
555
+ }
556
+
557
+ $numberOfLongs = ceil($byteLength / 4);
558
+ $paddedLength = $numberOfLongs * 4;
559
+ $paddedBytes = $this->zeroPadLeft($bytes, $paddedLength);
560
+ $unpacked = array_merge(unpack("N$numberOfLongs", $paddedBytes));
561
+
562
+ $integer = 0;
563
+
564
+ // 2^32
565
+ $twoTo32 = '4294967296';
566
+
567
+ foreach ($unpacked as $part) {
568
+ // We only use gmp or bcmath if the final value is too big
569
+ if ($byteLength <= $maxUintBytes) {
570
+ $integer = ($integer << 32) + $part;
571
+ } elseif (extension_loaded('gmp')) {
572
+ $integer = gmp_strval(gmp_add(gmp_mul($integer, $twoTo32), $part));
573
+ } elseif (extension_loaded('bcmath')) {
574
+ $integer = bcadd(bcmul($integer, $twoTo32), $part);
575
+ } else {
576
+ throw new \RuntimeException(
577
+ 'The gmp or bcmath extension must be installed to read this database.'
578
+ );
579
+ }
580
+ }
581
+ return $integer;
582
+ }
583
+
584
+ private function decodeString($bytes)
585
+ {
586
+ // XXX - NOOP. As far as I know, the end user has to explicitly set the
587
+ // encoding in PHP. Strings are just bytes.
588
+ return $bytes;
589
+ }
590
+
591
+ private function sizeFromCtrlByte($ctrlByte, $offset)
592
+ {
593
+ $size = $ctrlByte & 0x1f;
594
+ $bytesToRead = $size < 29 ? 0 : $size - 28;
595
+ $bytes = MaxMindUtil::read($this->fileStream, $offset, $bytesToRead);
596
+ $decoded = $this->decodeUint($bytes);
597
+
598
+ if ($size == 29) {
599
+ $size = 29 + $decoded;
600
+ } elseif ($size == 30) {
601
+ $size = 285 + $decoded;
602
+ } elseif ($size > 30) {
603
+ $size = ($decoded & (0x0FFFFFFF >> (32 - (8 * $bytesToRead))))
604
+ + 65821;
605
+ }
606
+
607
+ return array($size, $offset + $bytesToRead);
608
+ }
609
+
610
+ private function zeroPadLeft($content, $desiredLength)
611
+ {
612
+ return str_pad($content, $desiredLength, "\x00", STR_PAD_LEFT);
613
+ }
614
+
615
+ private function maybeSwitchByteOrder($bytes)
616
+ {
617
+ return $this->switchByteOrder ? strrev($bytes) : $bytes;
618
+ }
619
+
620
+ private function isPlatformLittleEndian()
621
+ {
622
+ $testint = 0x00FF;
623
+ $packed = pack('S', $testint);
624
+ return $testint === current(unpack('v', $packed));
625
+ }
626
  }
627
 
628
  /**
669
  * values will be a description in that language as a UTF-8 string. May be
670
  * undefined for some databases.
671
  */
672
+ class MaxMindMetadata {
673
+ private $binaryFormatMajorVersion;
674
+ private $binaryFormatMinorVersion;
675
+ private $buildEpoch;
676
+ private $databaseType;
677
+ private $description;
678
+ private $ipVersion;
679
+ private $languages;
680
+ private $nodeByteSize;
681
+ private $nodeCount;
682
+ private $recordSize;
683
+ private $searchTreeSize;
684
+
685
+ public function __construct($metadata) {
686
+ $this->binaryFormatMajorVersion =
687
+ $metadata['binary_format_major_version'];
688
+ $this->binaryFormatMinorVersion =
689
+ $metadata['binary_format_minor_version'];
690
+ $this->buildEpoch = $metadata['build_epoch'];
691
+ $this->databaseType = $metadata['database_type'];
692
+ $this->languages = $metadata['languages'];
693
+ $this->description = $metadata['description'];
694
+ $this->ipVersion = $metadata['ip_version'];
695
+ $this->nodeCount = $metadata['node_count'];
696
+ $this->recordSize = $metadata['record_size'];
697
+ $this->nodeByteSize = $this->recordSize / 4;
698
+ $this->searchTreeSize = $this->nodeCount * $this->nodeByteSize;
699
+ }
700
+
701
+ public function __get($var) {
702
+ return $this->$var;
703
+ }
704
  }
705
 
706
  class MaxMindUtil {
707
+ public static function read( $stream, $offset, $numberOfBytes ) {
708
+ if ( $numberOfBytes == 0 ) {
709
+ return '';
710
+ }
711
+ if ( fseek( $stream, $offset ) == 0 ) {
712
+ $value = fread( $stream, $numberOfBytes );
713
+
714
+ // We check that the number of bytes read is equal to the number
715
+ // asked for. We use ftell as getting the length of $value is
716
+ // much slower.
717
+ if ( ftell( $stream ) - $offset === $numberOfBytes ) {
718
+ return $value;
719
+ }
720
+ }
721
+ throw new InvalidDatabaseException(
722
+ "The MaxMind DB file contains bad data"
723
+ );
724
+ }
725
  }
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: analytics, statistics, counter, tracking, reports, wassup, geolocation, on
5
  Text Domain: wp-slimstat
6
  Requires at least: 3.8
7
  Tested up to: 4.8.2
8
- Stable tag: 4.7.1
9
 
10
  == Description ==
11
  The leading web analytics plugin for WordPress. Track returning customers and registered users, monitor Javascript events, detect intrusions, analyze email campaigns. Thousands of WordPress sites are already using it.
@@ -71,6 +71,12 @@ Our knowledge base is available on our [support center](http://docs.wp-slimstat.
71
  5. **Responsive layout** - Keep an eye on your reports on the go
72
 
73
  == Changelog ==
 
 
 
 
 
 
74
  = 4.7.1 =
75
  * [Fix] The new feature introduced in version 4.6.9.1 to allow our users to customize the default time range for the reports, had introduced a regression bug. Thank you to all our users who volunteered to test the bugfix.
76
  * [Fix] A vulnerability has been disclosed by [Pluginvulnerabilities.com](pluginvulnerabilities.com): an attacker with admin credentials could leverage the import/export mechanism for the plugin's settings to inject some malicious code. We recommend that you upgrade to the latest version of Slimstat as soon as possible.
5
  Text Domain: wp-slimstat
6
  Requires at least: 3.8
7
  Tested up to: 4.8.2
8
+ Stable tag: 4.7.2
9
 
10
  == Description ==
11
  The leading web analytics plugin for WordPress. Track returning customers and registered users, monitor Javascript events, detect intrusions, analyze email campaigns. Thousands of WordPress sites are already using it.
71
  5. **Responsive layout** - Keep an eye on your reports on the go
72
 
73
  == Changelog ==
74
+ = 4.7.2 =
75
+ * [New] As those who have been using Slimstat for a while know, we never stop doing our good share of research and development to improve our plugin. One feature on our wishlist was to make the geolocation functionality more accurate. Specifically, users have been asking us to track not just the Country of origin, but possibly the state and city. In order to geolocate visitors, our code has been leveraging a third-party data file provided by [MaxMind.com](https://www.maxmind.com/en/home). A while ago, they launched a new data format, which improves performance and offers a way to quickly determine the city of origin. However, the new library required a higher version of PHP, and up until now we had been hesitant to adopt it, to allow more people to use our plugin, over the chance of offering this feature. Now, after spending some time combing through their code, we found a way to get the best of both worlds: by customizing their PHP library, we were able to make it work with PHP 5.3! Which means that now Slimstat is able to tell you your visitors' city of origin (and State, when applicable) right out of the box. This information is available in the Access Log report and in a new 'Top Cities' report under the Audience tab. Please note: the MaxMind data file to enable this feature is approximately 60 Mb, and for this reason <strong>this new functionality is not enabled by default</strong>. You must go to Slimstat > Settings > Tracker and turn on the corresponding option. Then go to Slimstat > Settings > Maintenance and uninstall/install the GeoLite file to download the one that contains the city data. Please feel free to contact us if you have any questions.
76
+ * [Update] Removed backward compatibility code for those updating from a version prior to 4.2. Hopefully most of our users are using a newer version that that. If you're not, please contact our support service for instructions on how to upgrade.
77
+ * [Update] The format used to save your settings in the database has been changed. You MUST update your premium add-ons as soon as possible, and get the version compatible with this new format, or you might notice unexpected behaviors. Please contact us if you experience difficulties updating your add-ons.
78
+ * [Update] Cleaned up some old CSS code affecting the reports.
79
+
80
  = 4.7.1 =
81
  * [Fix] The new feature introduced in version 4.6.9.1 to allow our users to customize the default time range for the reports, had introduced a regression bug. Thank you to all our users who volunteered to test the bugfix.
82
  * [Fix] A vulnerability has been disclosed by [Pluginvulnerabilities.com](pluginvulnerabilities.com): an attacker with admin credentials could leverage the import/export mechanism for the plugin's settings to inject some malicious code. We recommend that you upgrade to the latest version of Slimstat as soon as possible.
wp-slimstat.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Slimstat Analytics
4
  Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
  Description: The leading web analytics plugin for WordPress
6
- Version: 4.7.1
7
  Author: Jason Crouse
8
  Author URI: http://www.wp-slimstat.com/
9
  Text Domain: wp-slimstat
@@ -15,7 +15,7 @@ if ( !empty( wp_slimstat::$settings ) ) {
15
  }
16
 
17
  class wp_slimstat {
18
- public static $version = '4.7.1';
19
  public static $settings = array();
20
 
21
  public static $wpdb = '';
@@ -72,7 +72,7 @@ class wp_slimstat {
72
  self::$upload_dir .= '/wp-slimstat';
73
  self::$upload_dir = apply_filters( 'slimstat_maxmind_path', self::$upload_dir );
74
 
75
- self::$maxmind_path = self::$upload_dir . '/maxmind.dat';
76
 
77
  // Path to wp-content folder, used to detect caching plugins via advanced-cache.php
78
  if ( file_exists( dirname( dirname( plugin_dir_path( __FILE__ ) ) ) . '/advanced-cache.php' ) ) {
@@ -80,25 +80,25 @@ class wp_slimstat {
80
  }
81
 
82
  // Enable the tracker (both server- and client-side)
83
- if ( !is_admin() || self::$settings[ 'track_admin_pages' ] == 'yes' ) {
84
 
85
  // Allow add-ons to turn off the tracker based on other conditions
86
  $is_tracking_filter = apply_filters( 'slimstat_filter_pre_tracking', strpos( self::get_request_uri(), 'wp-admin/admin-ajax.php' ) === false );
87
  $is_tracking_filter_js = apply_filters( 'slimstat_filter_pre_tracking_js', true );
88
 
89
  // Is server-side tracking active?
90
- if ( self::$settings[ 'javascript_mode' ] != 'yes' && self::$settings[ 'is_tracking' ] == 'yes' && $is_tracking_filter ) {
91
  add_action( is_admin() ? 'admin_init' : 'wp', array( __CLASS__, 'slimtrack' ), 5 );
92
 
93
- if ( self::$settings[ 'track_users' ] == 'yes' ) {
94
  add_action( 'login_init', array( __CLASS__, 'slimtrack' ), 10 );
95
  }
96
  }
97
 
98
  // Slimstat tracks screen resolutions, outbound links and other client-side information using javascript
99
- if ((self::$settings['enable_javascript'] == 'yes' || self::$settings['javascript_mode'] == 'yes') && self::$settings['is_tracking'] == 'yes' && $is_tracking_filter_js){
100
  add_action( is_admin() ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts' , array(__CLASS__, 'wp_slimstat_enqueue_tracking_script'), 15);
101
- if ( self::$settings[ 'track_users' ] == 'yes' ) {
102
  add_action( 'login_enqueue_scripts', array( __CLASS__, 'wp_slimstat_enqueue_tracking_script' ), 10 );
103
  }
104
  }
@@ -133,7 +133,7 @@ class wp_slimstat {
133
  */
134
  public static function slimtrack_ajax() {
135
  // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
136
- if ( self::$settings[ 'is_tracking' ] != 'yes' ) {
137
  self::$stat[ 'id' ] = -204;
138
  self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
139
  self::slimstat_save_options();
@@ -252,7 +252,7 @@ class wp_slimstat {
252
  */
253
  public static function slimtrack( $_argument = '' ) {
254
  // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
255
- if ( self::$settings['is_tracking'] != 'yes' ) {
256
  self::$stat[ 'id' ] = -204;
257
  self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
258
  return $_argument;
@@ -308,7 +308,7 @@ class wp_slimstat {
308
 
309
  if ( !empty( self::$stat[ 'referer' ] ) ) {
310
  $parsed_site_url_host = parse_url( get_site_url(), PHP_URL_HOST );
311
- if ( !empty( $referer[ 'host' ] ) && $referer[ 'host' ] == $parsed_site_url_host && self::$settings[ 'track_same_domain_referers' ] != 'yes' ) {
312
  unset( self::$stat[ 'referer' ] );
313
  }
314
  else {
@@ -466,7 +466,7 @@ class wp_slimstat {
466
  LIMIT 0,1", self::$stat[ 'ip' ] ), ARRAY_A );
467
 
468
  if ( !empty( $spam_comment[ 'comment_count' ] ) ) {
469
- if ( self::$settings[ 'ignore_spammers' ] == 'yes' ){
470
  self::$stat[ 'id' ] = -306;
471
  self::_set_error_array( sprintf( __( 'Spammer %s not tracked', 'wp-slimstat' ), $spam_comment[ 'comment_author' ] ), true );
472
  return $_argument;
@@ -502,12 +502,33 @@ class wp_slimstat {
502
  }
503
  }
504
 
505
- // Country and Language
506
  self::$stat[ 'language' ] = self::_get_language();
507
- self::$stat[ 'country' ] = self::get_country( self::$stat[ 'ip' ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
 
509
  // Anonymize IP Address?
510
- if ( self::$settings[ 'anonymize_ip' ] == 'yes' ) {
511
  // IPv4 or IPv6
512
  $needle = '.';
513
  $replace = '.0';
@@ -533,7 +554,7 @@ class wp_slimstat {
533
  // Mark or ignore Firefox/Safari prefetching requests (X-Moz: Prefetch and X-purpose: Preview)
534
  if ( ( isset( $_SERVER[ 'HTTP_X_MOZ' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_MOZ' ] ) == 'prefetch' ) ) ||
535
  ( isset( $_SERVER[ 'HTTP_X_PURPOSE' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_PURPOSE' ] ) == 'preview' ) ) ) {
536
- if ( self::$settings[ 'ignore_prefetch' ] == 'yes' ) {
537
  self::$stat[ 'id' ] = -309;
538
  self::_set_error_array( __( 'Prefetch requests are ignored', 'wp-slimstat' ), true );
539
  return $_argument;
@@ -549,7 +570,7 @@ class wp_slimstat {
549
  }
550
 
551
  // Are we ignoring bots?
552
- if ( ( self::$settings[ 'javascript_mode' ] == 'yes' || self::$settings[ 'ignore_bots' ] == 'yes' ) && self::$browser[ 'browser_type' ] == 1 ) {
553
  self::$stat[ 'id' ] = -310;
554
  self::_set_error_array( __( 'Bot not tracked', 'wp-slimstat' ), true );
555
  return $_argument;
@@ -641,7 +662,7 @@ class wp_slimstat {
641
  COOKIEPATH
642
  );
643
  }
644
- elseif ( !$cookie_has_been_set && self::$settings[ 'extend_session' ] == 'yes' && self::$stat[ 'visit_id' ] > 0 ) {
645
  @setcookie(
646
  'slimstat_tracking_code',
647
  self::_get_id_with_checksum( self::$stat[ 'visit_id' ] ),
@@ -655,74 +676,6 @@ class wp_slimstat {
655
  }
656
  // end slimtrack
657
 
658
- /**
659
- * Searches for the country code associated to a given IP address
660
- */
661
- public static function get_country( $_ip_address = '0.0.0.0' ){
662
- $ipnum = ip2long( $_ip_address );
663
- $country_output = 'xx';
664
-
665
- // Is this a RFC1918 (local) IP?
666
- if ( $ipnum == 2130706433 || // 127.0.0.1
667
- ( $ipnum >= 167772160 && $ipnum <= 184549375 ) || // 10.0.0.1 - 10.255.255.255
668
- ( $ipnum >= 2886729728 && $ipnum <= 2887778303 ) || // 172.16.0.1 - 172.31.255.255
669
- ( $ipnum >= 3232235521 && $ipnum <= 3232301055 ) ) { // 192.168.0.1 - 192.168.255.255
670
- $country_output = 'xy';
671
- }
672
- else {
673
- $country_codes = array("","ap","eu","ad","ae","af","ag","ai","al","am","cw","ao","aq","ar","as","at","au","aw","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bm","bn","bo","br","bs","bt","bv","bw","by","bz","ca","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","cr","cu","cv","cx","cy","cz","de","dj","dk","dm","do","dz","ec","ee","eg","eh","er","es","et","fi","fj","fk","fm","fo","fr","sx","ga","gb","gd","ge","gf","gh","gi","gl","gm","gn","gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","in","io","iq","ir","is","it","jm","jo","jp","ke","kg","kh","ki","km","kn","kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md","mg","mh","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","na","nc","ne","nf","ng","ni","nl","no","np","nr","nu","nz","om","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","ps","pt","pw","py","qa","re","ro","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","st","sv","sy","sz","tc","td","tf","tg","th","tj","tk","tm","tn","to","tl","tr","tt","tv","tw","tz","ua","ug","um","us","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","ye","yt","rs","za","zm","me","zw","a1","a2","o1","ax","gg","im","je","bl","mf","bq","ss","o1");
674
-
675
- if ( file_exists( self::$maxmind_path ) && ( $handle = fopen( self::$maxmind_path, "rb" ) ) ) {
676
-
677
- // Do we need to update the file?
678
- if (false !== ($file_stat = stat(self::$maxmind_path))){
679
-
680
- // Is the database more than 30 days old?
681
- if ((date('U') - $file_stat['mtime'] > 2629740)){
682
- fclose($handle);
683
-
684
- add_action('shutdown', array(__CLASS__, 'download_maxmind_database'));
685
-
686
- if (false === ($handle = fopen(self::$maxmind_path, "rb"))){
687
- return apply_filters( 'slimstat_get_country', 'xx', $_ip_address );
688
- }
689
- }
690
- }
691
-
692
- $offset = 0;
693
- for ( $depth = 31; $depth >= 0; --$depth ) {
694
- if ( fseek($handle, 6 * $offset, SEEK_SET ) != 0 ) {
695
- break;
696
- }
697
- $buf = fread( $handle, 6 );
698
- $x = array(0,0);
699
- for ($i = 0; $i < 2; ++$i) {
700
- for ($j = 0; $j < 3; ++$j) {
701
- $x[$i] += ord(substr($buf, 3 * $i + $j, 1)) << ($j * 8);
702
- }
703
- }
704
-
705
- if ( $ipnum & ( 1 << $depth ) ) {
706
- if ($x[1] >= 16776960 && !empty($country_codes[$x[1] - 16776960])) {
707
- $country_output = $country_codes[$x[1] - 16776960];
708
- break;
709
- }
710
- $offset = $x[1];
711
- } else {
712
- if ($x[0] >= 16776960 && !empty($country_codes[$x[0] - 16776960])) {
713
- $country_output = $country_codes[$x[0] - 16776960];
714
- break;
715
- }
716
- $offset = $x[0];
717
- }
718
- }
719
- fclose($handle);
720
- }
721
- }
722
- return apply_filters( 'slimstat_get_country', $country_output, $_ip_address );
723
- }
724
- // end get_country
725
-
726
  /**
727
  * Decodes the permalink
728
  */
@@ -1032,7 +985,7 @@ class wp_slimstat {
1032
  }
1033
 
1034
  // User doesn't have an active session
1035
- if ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'yes' ) ) {
1036
  if ( empty( self::$settings[ 'session_duration' ] ) ) {
1037
  self::$settings[ 'session_duration' ] = 1800;
1038
  }
@@ -1066,7 +1019,7 @@ class wp_slimstat {
1066
  WHERE id = %d AND visit_id = 0", self::$stat[ 'visit_id' ], $identifier
1067
  ) );
1068
  }
1069
- return ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'yes' ) );
1070
  }
1071
  // end _set_visit_id
1072
 
@@ -1187,7 +1140,7 @@ class wp_slimstat {
1187
  /**
1188
  * Opens given domains during CORS requests to admin-ajax.php
1189
  */
1190
- public static function open_cors_admin_ajax( $_allowed_origins = array() ){
1191
  $exploded_domains = self::string_to_array( self::$settings[ 'external_domains' ] );
1192
 
1193
  if ( !empty( $exploded_domains ) && !empty( $exploded_domains[ 0 ] ) ) {
@@ -1200,7 +1153,7 @@ class wp_slimstat {
1200
  /**
1201
  * Downloads the MaxMind geolocation database from their repository
1202
  */
1203
- public static function download_maxmind_database(){
1204
  // Create the folder, if it doesn't exist
1205
  if ( !file_exists( dirname( self::$maxmind_path ) ) ) {
1206
  mkdir( dirname( self::$maxmind_path ) );
@@ -1208,12 +1161,18 @@ class wp_slimstat {
1208
 
1209
  // Download the most recent database directly from MaxMind's repository
1210
  if ( !function_exists( 'download_url' ) ) {
1211
- require_once(ABSPATH . 'wp-admin/includes/file.php');
 
 
 
 
 
 
 
1212
  }
1213
- $maxmind_tmp = download_url( 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz', 5 );
1214
 
1215
- if (is_wp_error($maxmind_tmp)){
1216
- return __('There was an error downloading the MaxMind Geolite DB:', 'wp-slimstat').' '.$maxmind_tmp->get_error_message();
1217
  }
1218
 
1219
  $zh = false;
@@ -1235,22 +1194,22 @@ class wp_slimstat {
1235
  }
1236
 
1237
  if ( false === ( $fh = fopen( self::$maxmind_path, 'wb' ) ) ) {
1238
- return __('There was an error opening the unzipped MaxMind Geolite DB.','wp-slimstat');
1239
  }
1240
 
1241
- while(($data = gzread($zh, 4096)) != false){
1242
- fwrite($fh, $data);
1243
  }
1244
 
1245
- @gzclose($zh);
1246
- @fclose($fh);
1247
 
1248
- @unlink($maxmind_tmp);
1249
 
1250
  return '';
1251
  }
1252
 
1253
- public static function slimstat_shortcode( $_attributes = '', $_content = '' ){
1254
  extract( shortcode_atts( array(
1255
  'f' => '', // recent, popular, count, widget
1256
  'w' => '', // column to use (for recent, popular and count) or widget to use
@@ -1550,48 +1509,49 @@ class wp_slimstat {
1550
  'browscap_last_modified' => 0,
1551
 
1552
  // General
1553
- 'is_tracking' => 'yes',
1554
- 'javascript_mode' => 'yes',
1555
- 'enable_javascript' => 'yes',
1556
  'track_admin_pages' => 'no',
1557
- 'use_separate_menu' => 'yes',
1558
  'add_posts_column' => 'no',
1559
  'posts_column_day_interval' => 30,
1560
- 'posts_column_pageviews' => 'yes',
1561
- 'add_dashboard_widgets' => 'yes',
1562
  'hide_addons' => 'no',
1563
  'auto_purge' => 0,
1564
- 'auto_purge_delete' => 'yes',
1565
 
1566
  // Tracker
1567
  'do_not_track_outbound_classes_rel_href' => 'noslimstat,ab-item',
1568
  'extensions_to_track' => 'pdf,doc,xls,zip',
1569
- 'track_same_domain_referers' => 'yes',
1570
  'session_duration' => 1800,
1571
  'extend_session' => 'no',
1572
- 'enable_cdn' => 'yes',
 
1573
  'external_domains' => '',
1574
 
1575
  // Filters
1576
- 'track_users' => 'yes',
1577
  'ignore_users' => '',
1578
  'ignore_ip' => '',
1579
  'ignore_capabilities' => '',
1580
- 'ignore_spammers' => 'yes',
1581
  'ignore_bots' => 'no',
1582
  'ignore_resources' => '',
1583
  'ignore_countries' => '',
1584
  'ignore_browsers' => '',
1585
  'ignore_referers' => '',
1586
  'anonymize_ip' => 'no',
1587
- 'ignore_prefetch' => 'yes',
1588
 
1589
  // Reports
1590
- 'use_european_separators' => 'yes',
1591
  'date_format' => 'm-d-y',
1592
  'time_format' => 'h:i a',
1593
  'show_display_name' => 'no',
1594
- 'convert_resource_urls_to_titles' => 'yes',
1595
  'convert_ip_addresses' => 'no',
1596
  'async_load' => 'no',
1597
  'use_current_month_timespan' => 'no',
@@ -1609,7 +1569,7 @@ class wp_slimstat {
1609
  'enable_sov' => 'no',
1610
 
1611
  // Access Control
1612
- 'restrict_authors_view' => 'yes',
1613
  'capability_can_view' => 'activate_plugins',
1614
  'can_view' => '',
1615
  'rest_api_tokens' => wp_hash( uniqid( time() - 3600, true ) ),
@@ -1665,7 +1625,7 @@ class wp_slimstat {
1665
  $params[ 'outbound_classes_rel_href_to_not_track' ] = str_replace( ' ', '', self::$settings[ 'do_not_track_outbound_classes_rel_href' ] );
1666
  }
1667
 
1668
- if ( self::$settings[ 'javascript_mode' ] != 'yes' ) {
1669
  // Do not enqueue the tracker if this page view was not tracked for some reason
1670
  if ( intval( self::$stat[ 'id' ] ) < 0 ) {
1671
  return false;
@@ -1691,12 +1651,12 @@ class wp_slimstat {
1691
 
1692
  $params = apply_filters( 'slimstat_js_params', $params );
1693
 
1694
- if ( self::$settings[ 'enable_cdn' ] == 'yes' ) {
1695
  $schema = is_ssl() ? 'https' : 'http';
1696
  wp_register_script( 'wp_slimstat', $schema . '://cdn.jsdelivr.net/wp/wp-slimstat/tags/' . self::$version . '/wp-slimstat.min.js', array(), null, true );
1697
  }
1698
  else{
1699
- wp_register_script('wp_slimstat', plugins_url('/wp-slimstat.min.js', __FILE__), array(), null, true);
1700
  }
1701
 
1702
  wp_enqueue_script( 'wp_slimstat' );
3
  Plugin Name: Slimstat Analytics
4
  Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
  Description: The leading web analytics plugin for WordPress
6
+ Version: 4.7.2
7
  Author: Jason Crouse
8
  Author URI: http://www.wp-slimstat.com/
9
  Text Domain: wp-slimstat
15
  }
16
 
17
  class wp_slimstat {
18
+ public static $version = '4.7.2';
19
  public static $settings = array();
20
 
21
  public static $wpdb = '';
72
  self::$upload_dir .= '/wp-slimstat';
73
  self::$upload_dir = apply_filters( 'slimstat_maxmind_path', self::$upload_dir );
74
 
75
+ self::$maxmind_path = self::$upload_dir . '/maxmind.mmdb';
76
 
77
  // Path to wp-content folder, used to detect caching plugins via advanced-cache.php
78
  if ( file_exists( dirname( dirname( plugin_dir_path( __FILE__ ) ) ) . '/advanced-cache.php' ) ) {
80
  }
81
 
82
  // Enable the tracker (both server- and client-side)
83
+ if ( !is_admin() || self::$settings[ 'track_admin_pages' ] == 'on' ) {
84
 
85
  // Allow add-ons to turn off the tracker based on other conditions
86
  $is_tracking_filter = apply_filters( 'slimstat_filter_pre_tracking', strpos( self::get_request_uri(), 'wp-admin/admin-ajax.php' ) === false );
87
  $is_tracking_filter_js = apply_filters( 'slimstat_filter_pre_tracking_js', true );
88
 
89
  // Is server-side tracking active?
90
+ if ( self::$settings[ 'javascript_mode' ] != 'on' && self::$settings[ 'is_tracking' ] == 'on' && $is_tracking_filter ) {
91
  add_action( is_admin() ? 'admin_init' : 'wp', array( __CLASS__, 'slimtrack' ), 5 );
92
 
93
+ if ( self::$settings[ 'track_users' ] == 'on' ) {
94
  add_action( 'login_init', array( __CLASS__, 'slimtrack' ), 10 );
95
  }
96
  }
97
 
98
  // Slimstat tracks screen resolutions, outbound links and other client-side information using javascript
99
+ if ( ( self::$settings[ 'enable_javascript' ] == 'on' || self::$settings[ 'javascript_mode' ] == 'on' ) && self::$settings[ 'is_tracking' ] == 'on' && $is_tracking_filter_js ) {
100
  add_action( is_admin() ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts' , array(__CLASS__, 'wp_slimstat_enqueue_tracking_script'), 15);
101
+ if ( self::$settings[ 'track_users' ] == 'on' ) {
102
  add_action( 'login_enqueue_scripts', array( __CLASS__, 'wp_slimstat_enqueue_tracking_script' ), 10 );
103
  }
104
  }
133
  */
134
  public static function slimtrack_ajax() {
135
  // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
136
+ if ( self::$settings[ 'is_tracking' ] != 'on' ) {
137
  self::$stat[ 'id' ] = -204;
138
  self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
139
  self::slimstat_save_options();
252
  */
253
  public static function slimtrack( $_argument = '' ) {
254
  // If the website is using a caching plugin, the tracking code might still be there, even if the user turned off tracking
255
+ if ( self::$settings[ 'is_tracking' ] != 'on' ) {
256
  self::$stat[ 'id' ] = -204;
257
  self::_set_error_array( __( 'Tracker is turned off, but client-side tracking code is still running.', 'wp-slimstat' ), true );
258
  return $_argument;
308
 
309
  if ( !empty( self::$stat[ 'referer' ] ) ) {
310
  $parsed_site_url_host = parse_url( get_site_url(), PHP_URL_HOST );
311
+ if ( !empty( $referer[ 'host' ] ) && $referer[ 'host' ] == $parsed_site_url_host && self::$settings[ 'track_same_domain_referers' ] != 'on' ) {
312
  unset( self::$stat[ 'referer' ] );
313
  }
314
  else {
466
  LIMIT 0,1", self::$stat[ 'ip' ] ), ARRAY_A );
467
 
468
  if ( !empty( $spam_comment[ 'comment_count' ] ) ) {
469
+ if ( self::$settings[ 'ignore_spammers' ] == 'on' ){
470
  self::$stat[ 'id' ] = -306;
471
  self::_set_error_array( sprintf( __( 'Spammer %s not tracked', 'wp-slimstat' ), $spam_comment[ 'comment_author' ] ), true );
472
  return $_argument;
502
  }
503
  }
504
 
505
+ // Language
506
  self::$stat[ 'language' ] = self::_get_language();
507
+
508
+ // Geolocation
509
+ include_once ( plugin_dir_path( __FILE__ ) . 'maxmind.php' );
510
+ $geolocation_data = maxmind_geolite2_connector::get_geolocation_info( self::$stat[ 'ip' ] );
511
+
512
+ if ( !empty( $geolocation_data[ 'country' ][ 'iso_code' ] ) ) {
513
+ self::$stat[ 'country' ] = strtolower( $geolocation_data[ 'country' ][ 'iso_code' ] );
514
+ }
515
+
516
+ if ( !empty( $geolocation_data[ 'city' ][ 'names' ][ 'en' ] ) ) {
517
+ self::$stat[ 'city' ] = $geolocation_data[ 'city' ][ 'names' ][ 'en' ];
518
+ }
519
+
520
+ if ( !empty( $geolocation_data[ 'subdivisions' ][ 0 ][ 'iso_code' ] ) && !empty( self::$stat[ 'city' ] ) ) {
521
+ self::$stat[ 'city' ] .= ' (' . $geolocation_data[ 'subdivisions' ][ 0 ][ 'iso_code' ] . ')';
522
+ }
523
+
524
+ if ( !empty( $geolocation_data[ 'location' ][ 'latitude' ] ) && !empty( $geolocation_data[ 'location' ][ 'longitude' ] ) ) {
525
+ self::$stat[ 'location' ] = $geolocation_data[ 'location' ][ 'latitude' ] . ',' . $geolocation_data[ 'location' ][ 'longitude' ];
526
+ }
527
+
528
+ unset( $geolocation_data );
529
 
530
  // Anonymize IP Address?
531
+ if ( self::$settings[ 'anonymize_ip' ] == 'on' ) {
532
  // IPv4 or IPv6
533
  $needle = '.';
534
  $replace = '.0';
554
  // Mark or ignore Firefox/Safari prefetching requests (X-Moz: Prefetch and X-purpose: Preview)
555
  if ( ( isset( $_SERVER[ 'HTTP_X_MOZ' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_MOZ' ] ) == 'prefetch' ) ) ||
556
  ( isset( $_SERVER[ 'HTTP_X_PURPOSE' ] ) && ( strtolower( $_SERVER[ 'HTTP_X_PURPOSE' ] ) == 'preview' ) ) ) {
557
+ if ( self::$settings[ 'ignore_prefetch' ] == 'on' ) {
558
  self::$stat[ 'id' ] = -309;
559
  self::_set_error_array( __( 'Prefetch requests are ignored', 'wp-slimstat' ), true );
560
  return $_argument;
570
  }
571
 
572
  // Are we ignoring bots?
573
+ if ( ( self::$settings[ 'javascript_mode' ] == 'on' || self::$settings[ 'ignore_bots' ] == 'on' ) && self::$browser[ 'browser_type' ] == 1 ) {
574
  self::$stat[ 'id' ] = -310;
575
  self::_set_error_array( __( 'Bot not tracked', 'wp-slimstat' ), true );
576
  return $_argument;
662
  COOKIEPATH
663
  );
664
  }
665
+ elseif ( !$cookie_has_been_set && self::$settings[ 'extend_session' ] == 'on' && self::$stat[ 'visit_id' ] > 0 ) {
666
  @setcookie(
667
  'slimstat_tracking_code',
668
  self::_get_id_with_checksum( self::$stat[ 'visit_id' ] ),
676
  }
677
  // end slimtrack
678
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
679
  /**
680
  * Decodes the permalink
681
  */
985
  }
986
 
987
  // User doesn't have an active session
988
+ if ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'on' ) ) {
989
  if ( empty( self::$settings[ 'session_duration' ] ) ) {
990
  self::$settings[ 'session_duration' ] = 1800;
991
  }
1019
  WHERE id = %d AND visit_id = 0", self::$stat[ 'visit_id' ], $identifier
1020
  ) );
1021
  }
1022
+ return ( $is_new_session && ( $_force_assign || self::$settings[ 'javascript_mode' ] == 'on' ) );
1023
  }
1024
  // end _set_visit_id
1025
 
1140
  /**
1141
  * Opens given domains during CORS requests to admin-ajax.php
1142
  */
1143
+ public static function open_cors_admin_ajax( $_allowed_origins = array() ) {
1144
  $exploded_domains = self::string_to_array( self::$settings[ 'external_domains' ] );
1145
 
1146
  if ( !empty( $exploded_domains ) && !empty( $exploded_domains[ 0 ] ) ) {
1153
  /**
1154
  * Downloads the MaxMind geolocation database from their repository
1155
  */
1156
+ public static function download_maxmind_database() {
1157
  // Create the folder, if it doesn't exist
1158
  if ( !file_exists( dirname( self::$maxmind_path ) ) ) {
1159
  mkdir( dirname( self::$maxmind_path ) );
1161
 
1162
  // Download the most recent database directly from MaxMind's repository
1163
  if ( !function_exists( 'download_url' ) ) {
1164
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
1165
+ }
1166
+
1167
+ if ( self::$settings[ 'geolocation_country' ] == 'on' ) {
1168
+ $maxmind_tmp = download_url( 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz', 5 );
1169
+ }
1170
+ else {
1171
+ $maxmind_tmp = download_url( 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz', 25 );
1172
  }
 
1173
 
1174
+ if ( is_wp_error( $maxmind_tmp ) ) {
1175
+ return __( 'There was an error downloading the MaxMind Geolite DB:', 'wp-slimstat' ) . ' ' . $maxmind_tmp->get_error_message();
1176
  }
1177
 
1178
  $zh = false;
1194
  }
1195
 
1196
  if ( false === ( $fh = fopen( self::$maxmind_path, 'wb' ) ) ) {
1197
+ return __( 'There was an error opening the MaxMind Geolite DB.', 'wp-slimstat' );
1198
  }
1199
 
1200
+ while ( ( $data = gzread( $zh, 4096 ) ) != false ) {
1201
+ fwrite( $fh, $data );
1202
  }
1203
 
1204
+ @gzclose( $zh );
1205
+ @fclose( $fh );
1206
 
1207
+ @unlink( $maxmind_tmp );
1208
 
1209
  return '';
1210
  }
1211
 
1212
+ public static function slimstat_shortcode( $_attributes = '', $_content = '' ) {
1213
  extract( shortcode_atts( array(
1214
  'f' => '', // recent, popular, count, widget
1215
  'w' => '', // column to use (for recent, popular and count) or widget to use
1509
  'browscap_last_modified' => 0,
1510
 
1511
  // General
1512
+ 'is_tracking' => 'on',
1513
+ 'javascript_mode' => 'on',
1514
+ 'enable_javascript' => 'on',
1515
  'track_admin_pages' => 'no',
1516
+ 'use_separate_menu' => 'on',
1517
  'add_posts_column' => 'no',
1518
  'posts_column_day_interval' => 30,
1519
+ 'posts_column_pageviews' => 'on',
1520
+ 'add_dashboard_widgets' => 'on',
1521
  'hide_addons' => 'no',
1522
  'auto_purge' => 0,
1523
+ 'auto_purge_delete' => 'on',
1524
 
1525
  // Tracker
1526
  'do_not_track_outbound_classes_rel_href' => 'noslimstat,ab-item',
1527
  'extensions_to_track' => 'pdf,doc,xls,zip',
1528
+ 'track_same_domain_referers' => 'on',
1529
  'session_duration' => 1800,
1530
  'extend_session' => 'no',
1531
+ 'geolocation_country' => 'on',
1532
+ 'enable_cdn' => 'on',
1533
  'external_domains' => '',
1534
 
1535
  // Filters
1536
+ 'track_users' => 'on',
1537
  'ignore_users' => '',
1538
  'ignore_ip' => '',
1539
  'ignore_capabilities' => '',
1540
+ 'ignore_spammers' => 'on',
1541
  'ignore_bots' => 'no',
1542
  'ignore_resources' => '',
1543
  'ignore_countries' => '',
1544
  'ignore_browsers' => '',
1545
  'ignore_referers' => '',
1546
  'anonymize_ip' => 'no',
1547
+ 'ignore_prefetch' => 'on',
1548
 
1549
  // Reports
1550
+ 'use_european_separators' => 'on',
1551
  'date_format' => 'm-d-y',
1552
  'time_format' => 'h:i a',
1553
  'show_display_name' => 'no',
1554
+ 'convert_resource_urls_to_titles' => 'on',
1555
  'convert_ip_addresses' => 'no',
1556
  'async_load' => 'no',
1557
  'use_current_month_timespan' => 'no',
1569
  'enable_sov' => 'no',
1570
 
1571
  // Access Control
1572
+ 'restrict_authors_view' => 'on',
1573
  'capability_can_view' => 'activate_plugins',
1574
  'can_view' => '',
1575
  'rest_api_tokens' => wp_hash( uniqid( time() - 3600, true ) ),
1625
  $params[ 'outbound_classes_rel_href_to_not_track' ] = str_replace( ' ', '', self::$settings[ 'do_not_track_outbound_classes_rel_href' ] );
1626
  }
1627
 
1628
+ if ( self::$settings[ 'javascript_mode' ] != 'on' ) {
1629
  // Do not enqueue the tracker if this page view was not tracked for some reason
1630
  if ( intval( self::$stat[ 'id' ] ) < 0 ) {
1631
  return false;
1651
 
1652
  $params = apply_filters( 'slimstat_js_params', $params );
1653
 
1654
+ if ( self::$settings[ 'enable_cdn' ] == 'on' ) {
1655
  $schema = is_ssl() ? 'https' : 'http';
1656
  wp_register_script( 'wp_slimstat', $schema . '://cdn.jsdelivr.net/wp/wp-slimstat/tags/' . self::$version . '/wp-slimstat.min.js', array(), null, true );
1657
  }
1658
  else{
1659
+ wp_register_script( 'wp_slimstat', plugins_url( '/wp-slimstat.min.js', __FILE__ ), array(), null, true );
1660
  }
1661
 
1662
  wp_enqueue_script( 'wp_slimstat' );