Slimstat Analytics - Version 3.9.4

Version Description

  • [Note] The URL of the CDN has changed, and is now using the official WordPress repository as a source: cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js - Please update your "external" tracking codes accordingly.
  • [Note] The structure of the array wp_slimstat_db::$sql_filters has changed! Please make sure to update your custom code accordingly. Feel free to contact us for more information.
  • [New] The wait is over. Our heatmap add-on is finally available on our store! We would like to thank all those who provided helpful feedback to improve this initial release!
  • [New] Our knowledge base has been extended with a list of all the actions and filters available in Slimstat.
  • [Fix] The Add-on update checker had a bug preventing the functionality to work as expected. Please make sure to get the latest version of your premium add-ons!
  • [Fix] Date intervals were not accurate because of a bug related to calculating timezones in MySQL (thank you, Chrisssssi).
  • [Fix] Line height of report rows has been added to avoid conflicts with other plugins tweaking this parameter in the admin (thank you, yk11).
Download this release

Release Info

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

Code changes from version 3.9.3 to 3.9.4

admin/config/addons.php CHANGED
@@ -7,14 +7,16 @@ if (!empty($_POST['licenses'])){
7
  wp_slimstat::$options['addon_licenses'] = $_POST['licenses'];
8
  }
9
 
10
- if (false === ($response = get_transient('wp_slimstat_addon_list'))){
 
11
  $response = wp_remote_get('http://slimstat.getused.to.it/update-checker/', array('headers' => array('referer' => get_site_url())));
12
  if(is_wp_error($response) || $response['response']['code'] != 200){
13
  $error_message = is_wp_error($response)?$response->get_error_message():$response['response']['code'].' '. $response['response']['message'];
14
  echo '<p>'.__('There was an error retrieving the add-ons list from the server. Please try again later. Error Message:','wp-slimstat').' '.$error_message.'</p></div>';
15
  return;
16
  }
17
- set_transient('wp_slimstat_addon_list', $response, 3600);
 
18
  }
19
 
20
  $license_key_field = false;
@@ -27,7 +29,14 @@ if (!is_array($list_addons)){
27
 
28
  <div class="wrap slimstat">
29
  <h2><?php _e('Add-ons','wp-slimstat') ?></h2>
30
- <p><?php _e('Add-ons extend the functionality of Slimstat in many interesting ways. We offer both free and premium (paid) extensions. Each add-on can be installed as a separate plugin, which will receive regular updates via the WordPress Plugins panel. In order to be notified when a new version of a premium add-on is available, please enter the <strong>license key</strong> you received when you purchased it.','wp-slimstat') ?></p>
 
 
 
 
 
 
 
31
 
32
  <form method="post" id="form-slimstat-options-tab-addons">
33
  <table class="wp-list-table widefat plugins slimstat-addons" cellspacing="0">
@@ -44,10 +53,10 @@ if (!is_array($list_addons)){
44
  <strong><a target="_blank" href="<?php echo $a_addon['download_url'] ?>"><?php echo $a_addon['name'] ?></a></strong>
45
  <div class="row-actions-visible"><?php
46
  if (is_plugin_active($a_addon['slug'].'/index.php') || is_plugin_active($a_addon['slug'].'/'.$a_addon['slug'].'.php')){
47
- echo 'Version '.$a_addon['version'].'<br/>Installed and Active';
48
  }
49
  else{
50
- echo 'Version '.$a_addon['version'].'<br/>Price: '.(is_numeric($a_addon['price'])?'$'.$a_addon['price']:$a_addon['price']);
51
  } ?>
52
  </div>
53
  </th>
7
  wp_slimstat::$options['addon_licenses'] = $_POST['licenses'];
8
  }
9
 
10
+ $response = get_transient('wp_slimstat_addon_list');
11
+ if (!empty($_GET['force_refresh']) || false === $response){
12
  $response = wp_remote_get('http://slimstat.getused.to.it/update-checker/', array('headers' => array('referer' => get_site_url())));
13
  if(is_wp_error($response) || $response['response']['code'] != 200){
14
  $error_message = is_wp_error($response)?$response->get_error_message():$response['response']['code'].' '. $response['response']['message'];
15
  echo '<p>'.__('There was an error retrieving the add-ons list from the server. Please try again later. Error Message:','wp-slimstat').' '.$error_message.'</p></div>';
16
  return;
17
  }
18
+
19
+ set_transient('wp_slimstat_addon_list', $response, 86400);
20
  }
21
 
22
  $license_key_field = false;
29
 
30
  <div class="wrap slimstat">
31
  <h2><?php _e('Add-ons','wp-slimstat') ?></h2>
32
+ <p><?php _e('Add-ons extend the functionality of Slimstat in many interesting ways. We offer both free and premium (paid) extensions. Each add-on can be installed as a separate plugin, which will receive regular updates via the WordPress Plugins panel. In order to be notified when a new version of a premium add-on is available, please enter the <strong>license key</strong> you received when you purchased it.','wp-slimstat') ?>
33
+ <?php
34
+ if (empty($_GET['force_refresh'])){
35
+
36
+ printf(__('This list is cached daily on your server: <a href="%s&amp;force_refresh=true">click here</a> to clear the cache.','wp-slimstat'), $_SERVER['REQUEST_URI']);
37
+ }
38
+ ?>
39
+ </p>
40
 
41
  <form method="post" id="form-slimstat-options-tab-addons">
42
  <table class="wp-list-table widefat plugins slimstat-addons" cellspacing="0">
53
  <strong><a target="_blank" href="<?php echo $a_addon['download_url'] ?>"><?php echo $a_addon['name'] ?></a></strong>
54
  <div class="row-actions-visible"><?php
55
  if (is_plugin_active($a_addon['slug'].'/index.php') || is_plugin_active($a_addon['slug'].'/'.$a_addon['slug'].'.php')){
56
+ echo 'Latest Version: '.$a_addon['version'].'<br/>Installed and Active';
57
  }
58
  else{
59
+ echo 'Latest Version: '.$a_addon['version'].'<br/>Price: '.(is_numeric($a_addon['price'])?'$'.$a_addon['price']:$a_addon['price']);
60
  } ?>
61
  </div>
62
  </th>
admin/config/index.php CHANGED
@@ -47,7 +47,6 @@ switch ($config_tabs[$current_tab-1]){
47
  'views_basic_header' => array('description' => __('Data and Formats','wp-slimstat'), 'type' => 'section_header'),
48
  'convert_ip_addresses' => array('description' => __('Convert IP Addresses','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Display provider names instead of IP addresses.','wp-slimstat')),
49
  'use_european_separators' => array('description' => __('Number Format','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Choose the number format you want to use for your reports.','wp-slimstat'), 'custom_label_yes' => '1.234,56', 'custom_label_no' => '1,234.56'),
50
- 'reset_timezone' => array('description' => __('Reset Timezone','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Enable this option if your DB server and your web site use different timezones.','wp-slimstat')),
51
  'enable_sov' => array('description' => __('Enable SOV','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('In linguistic typology, a subject-object-verb (SOV) language is one in which the subject, object, and verb of a sentence appear in that order, like in Japanese.','wp-slimstat')),
52
  'show_display_name' => array('description' => __('Show Display Name','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('By default, users are listed by their usernames. Use this option to visualize their display names instead.','wp-slimstat')),
53
  'show_complete_user_agent_tooltip' => array('description' => __('Show User Agent','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Choose if you want to see the browser name or a complete user agent string when hovering on browser icons.','wp-slimstat')),
@@ -241,12 +240,12 @@ var SlimStatParams = {
241
  };
242
  /* ]]&gt; */
243
  &lt;/script&gt;
244
- &lt;script type="text/javascript" src="http://cdn.jsdelivr.net/wp-slimstat/'.wp_slimstat::$version.'/wp-slimstat.js"&gt;&lt;/script&gt;'),
245
 
246
  'advanced_misc_header' => array('description' => __('Miscellaneous','wp-slimstat'), 'type' => 'section_header'),
247
  'show_sql_debug' => array('description' => __('Debug Mode','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Display the SQL queries used to retrieve the data.','wp-slimstat')),
248
  'ip_lookup_service' => array('description' => __('IP Lookup','wp-slimstat'), 'type' => 'text', 'long_description' => __('Customize the Geolocation service to be used in the reports.','wp-slimstat')),
249
- 'custom_css' => array('description' => __('Custom CSS','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("Paste here your custom stylesheet to personalize the way your reports look. <a href='http://wordpress.org/plugins/wp-slimstat/faq/' target='_blank'>Check the FAQ</a> for more information on how to use this setting.",'wp-slimstat')),
250
  'enable_ads_network' => array('description' => __('Enable UAN','wp-slimstat'), 'type' => 'yesno', 'long_description' => __("Send anonymous data about user agents to our server for analysis. This allows us to contribute to the <a href='http://browscap.org/' target='_blank'>BrowsCap opensource project</a>, and improve the accuracy of Slimstat's browser detection functionality. It also enables our transparent ads network. No worries, your site will not be affected in any way.",'wp-slimstat'))
251
  );
252
  break;
47
  'views_basic_header' => array('description' => __('Data and Formats','wp-slimstat'), 'type' => 'section_header'),
48
  'convert_ip_addresses' => array('description' => __('Convert IP Addresses','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Display provider names instead of IP addresses.','wp-slimstat')),
49
  'use_european_separators' => array('description' => __('Number Format','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Choose the number format you want to use for your reports.','wp-slimstat'), 'custom_label_yes' => '1.234,56', 'custom_label_no' => '1,234.56'),
 
50
  'enable_sov' => array('description' => __('Enable SOV','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('In linguistic typology, a subject-object-verb (SOV) language is one in which the subject, object, and verb of a sentence appear in that order, like in Japanese.','wp-slimstat')),
51
  'show_display_name' => array('description' => __('Show Display Name','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('By default, users are listed by their usernames. Use this option to visualize their display names instead.','wp-slimstat')),
52
  'show_complete_user_agent_tooltip' => array('description' => __('Show User Agent','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Choose if you want to see the browser name or a complete user agent string when hovering on browser icons.','wp-slimstat')),
240
  };
241
  /* ]]&gt; */
242
  &lt;/script&gt;
243
+ &lt;script type="text/javascript" src="http://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js"&gt;&lt;/script&gt;'),
244
 
245
  'advanced_misc_header' => array('description' => __('Miscellaneous','wp-slimstat'), 'type' => 'section_header'),
246
  'show_sql_debug' => array('description' => __('Debug Mode','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Display the SQL queries used to retrieve the data.','wp-slimstat')),
247
  'ip_lookup_service' => array('description' => __('IP Lookup','wp-slimstat'), 'type' => 'text', 'long_description' => __('Customize the Geolocation service to be used in the reports.','wp-slimstat')),
248
+ 'custom_css' => array('description' => __('Custom CSS','wp-slimstat'), 'type' => 'textarea', 'long_description' => __("Paste here your custom stylesheet to personalize the way your reports look. <a href='https://slimstat.freshdesk.com/support/solutions/articles/5000528528-how-can-i-change-the-colors-associated-to-color-coded-pageviews-known-user-known-visitors-search-e' target='_blank'>Check the FAQ</a> for more information on how to use this setting.",'wp-slimstat')),
249
  'enable_ads_network' => array('description' => __('Enable UAN','wp-slimstat'), 'type' => 'yesno', 'long_description' => __("Send anonymous data about user agents to our server for analysis. This allows us to contribute to the <a href='http://browscap.org/' target='_blank'>BrowsCap opensource project</a>, and improve the accuracy of Slimstat's browser detection functionality. It also enables our transparent ads network. No worries, your site will not be affected in any way.",'wp-slimstat'))
250
  );
251
  break;
admin/css/slimstat.css CHANGED
@@ -198,6 +198,7 @@
198
  [id^=slim_] p{
199
  border-bottom: 1px solid #ddd;
200
  font-weight: 300;
 
201
  margin: 0;
202
  min-height: 20px;
203
  overflow: auto;
198
  [id^=slim_] p{
199
  border-bottom: 1px solid #ddd;
200
  font-weight: 300;
201
+ line-height: 1.5;
202
  margin: 0;
203
  min-height: 20px;
204
  overflow: auto;
admin/view/index.php CHANGED
@@ -188,6 +188,9 @@
188
  case 'slim_p2_01':
189
  wp_slimstat_reports::report_header($a_box_id, 'wide chart', wp_slimstat_reports::$chart_tooltip, wp_slimstat_reports::chart_title(__('Human Visits', 'wp-slimstat')));
190
  break;
 
 
 
191
  case 'slim_p2_05':
192
  wp_slimstat_reports::report_header($a_box_id, 'wide', __('Internet Service Provider: a company which provides other companies or individuals with access to the Internet. Your DSL or cable internet service is provided to you by your ISP.<br><br>You can ignore specific IP addresses by setting the corresponding filter under Settings > Slimstat > Filters.','wp-slimstat'));
193
  break;
188
  case 'slim_p2_01':
189
  wp_slimstat_reports::report_header($a_box_id, 'wide chart', wp_slimstat_reports::$chart_tooltip, wp_slimstat_reports::chart_title(__('Human Visits', 'wp-slimstat')));
190
  break;
191
+ case 'slim_p2_02':
192
+ wp_slimstat_reports::report_header($a_box_id, 'normal', __('Where not otherwise specified, the metrics in this report are referred to human visitors.','wp-slimstat'));
193
+ break;
194
  case 'slim_p2_05':
195
  wp_slimstat_reports::report_header($a_box_id, 'wide', __('Internet Service Provider: a company which provides other companies or individuals with access to the Internet. Your DSL or cable internet service is provided to you by your ISP.<br><br>You can ignore specific IP addresses by setting the corresponding filter under Settings > Slimstat > Filters.','wp-slimstat'));
196
  break;
admin/view/wp-slimstat-db.php CHANGED
@@ -16,13 +16,6 @@ class wp_slimstat_db {
16
  * Initializes the environment, sets the filters
17
  */
18
  public static function init($_filters = ''){
19
- // Reset MySQL timezone settings, our dates and times are recorded using WP settings
20
-
21
- if (wp_slimstat::$options['reset_timezone'] == 'yes'){
22
- wp_slimstat::$wpdb->query("SET @@session.time_zone = '+00:00'");
23
- date_default_timezone_set('UTC');
24
- }
25
-
26
  // Decimal and thousand separators
27
  if (wp_slimstat::$options['use_european_separators'] == 'no'){
28
  self::$formats['decimal'] = '.';
@@ -103,7 +96,6 @@ class wp_slimstat_db {
103
  }
104
 
105
  if (empty(self::$filters_normalized['date']['interval']) && empty(self::$filters_normalized['date']['interval_hours']) && empty(self::$filters_normalized['date']['interval_minutes'])){
106
-
107
  if (!empty(self::$filters_normalized['date']['minute'])){
108
  self::$filters_normalized['utime']['start'] = mktime(
109
  !empty(self::$filters_normalized['date']['hour'])?self::$filters_normalized['date']['hour']:0,
@@ -167,27 +159,25 @@ class wp_slimstat_db {
167
  self::$filters_normalized['utime']['type'] = 'interval';
168
 
169
  self::$filters_normalized['utime']['start'] = mktime(
170
- !empty(self::$filters_normalized['date']['hour'])?self::$filters_normalized['date']['hour']:date_i18n('G'),
171
- !empty(self::$filters_normalized['date']['minute'])?self::$filters_normalized['date']['minute']:date_i18n('i'),
172
  0,
173
  !empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'),
174
  !empty(self::$filters_normalized['date']['day'])?self::$filters_normalized['date']['day']:date_i18n('j'),
175
  !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')
176
  );
177
-
178
  $sign = (self::$filters_normalized['date']['interval_direction'] == 'plus')?'+':'-';
179
 
180
- self::$filters_normalized['utime']['end'] = date('U',
181
- strtotime($sign.(
182
- (!empty(self::$filters_normalized['date']['interval'])?intval(self::$filters_normalized['date']['interval']):0) * 1440 +
183
- (!empty(self::$filters_normalized['date']['interval_hours'])?intval(self::$filters_normalized['date']['interval_hours']):0) * 60 +
184
- (!empty(self::$filters_normalized['date']['interval_minutes'])?intval(self::$filters_normalized['date']['interval_minutes']):0)
185
- ).' minutes', self::$filters_normalized['utime']['start'])
186
- );
187
 
188
  // Swap boundaries if we're going back
189
  if (self::$filters_normalized['date']['interval_direction'] == 'minus'){
190
- list(self::$filters_normalized['utime']['start'], self::$filters_normalized['utime']['end']) = array(self::$filters_normalized['utime']['end'], self::$filters_normalized['utime']['start']);
191
  }
192
  }
193
 
@@ -216,43 +206,57 @@ class wp_slimstat_db {
216
  }
217
  }
218
 
219
- // Now let's translate our filters into SQL clauses
220
  self::$sql_filters = array(
221
  'from' => array(
222
- 'browsers' => '',
223
- 'screenres' => '',
224
- 'content_info' => '',
225
- 'outbound' => '',
226
- 'all_tables' => '',
227
- 'all_other_tables' => ''
 
228
  ),
229
- 'where' => '',
230
- 'where_time_range' => ' AND (t1.dt BETWEEN '.self::$filters_normalized['utime']['start'].' AND '.self::$filters_normalized['utime']['end'].')'
 
 
 
 
 
 
 
 
 
231
  );
232
 
233
  foreach (self::$filters_normalized['columns'] as $a_filter_column => $a_filter_data){
234
- $a_filter_empty = '0';
235
-
236
  // Add-ons can set their own custom filters, which are ignored here
237
  if (strpos($a_filter_column, 'addon_') !== false){
238
  continue;
239
  }
240
 
 
 
 
 
 
241
  // Some columns are in separate tables, so we need to join them
242
- switch (self::get_table_identifier($a_filter_column)){
243
- case 'tb.':
244
- self::$sql_filters['from']['browsers'] = "INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers tb ON t1.browser_id = tb.browser_id";
245
  break;
246
- case 'tci.':
247
- self::$sql_filters['from']['content_info'] = "INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_content_info tci ON t1.content_info_id = tci.content_info_id";
248
  break;
249
- case 'tss.':
250
- self::$sql_filters['from']['screenres'] = "LEFT JOIN {$GLOBALS['wpdb']->base_prefix}slim_screenres tss ON t1.screenres_id = tss.screenres_id";
251
  break;
252
- case 'tob.':
253
- self::$sql_filters['from']['outbound'] = "LEFT JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON (t1.id = tob.id)";
254
  break;
255
  default:
 
256
  }
257
 
258
  // Some columns require a special treatment
@@ -260,70 +264,73 @@ class wp_slimstat_db {
260
  case 'ip':
261
  case 'other_ip':
262
  $a_filter_column = "INET_NTOA($a_filter_column)";
263
- $a_filter_empty = '0.0.0.0';
264
  break;
265
  default:
266
- $a_filter_column = self::get_table_identifier($a_filter_column).$a_filter_column;
267
  break;
268
  }
269
 
270
  switch ($a_filter_data[0]){
271
  case 'is_not_equal_to':
272
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column <> %s", $a_filter_data[1]);
273
  break;
274
  case 'contains':
275
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column LIKE %s", '%'.$a_filter_data[1].'%');
276
  break;
277
  case 'includes_in_set':
278
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND FIND_IN_SET(%s, $a_filter_column) > 0", $a_filter_data[1]);
279
  break;
280
  case 'does_not_contain':
281
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column NOT LIKE %s", '%'.$a_filter_data[1].'%');;
282
  break;
283
  case 'starts_with':
284
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column LIKE %s", $a_filter_data[1].'%');
285
  break;
286
  case 'ends_with':
287
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column LIKE %s", '%'.$a_filter_data[1]);
288
  break;
289
  case 'sounds_like':
290
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND SOUNDEX($a_filter_column) = SOUNDEX(%s)", $a_filter_data[1]);
291
  break;
292
  case 'is_empty':
293
- self::$sql_filters['where'] .= " AND ($a_filter_column = '' OR $a_filter_column = '$a_filter_empty')";
294
  break;
295
  case 'is_not_empty':
296
- self::$sql_filters['where'] .= " AND $a_filter_column <> '' AND $a_filter_column <> '$a_filter_empty'";
297
  break;
298
  case 'is_greater_than':
299
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column > %s", $a_filter_data[1]);
300
  break;
301
  case 'is_less_than':
302
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column < %s", $a_filter_data[1]);
303
  break;
304
  case 'between':
305
  $range = explode(',', $a_filter_data[1]);
306
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column BETWEEN %s AND %s", $range[0], $range[1]);
307
  break;
308
  case 'matches':
309
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column REGEXP %s", $a_filter_data[1]);
310
  break;
311
  case 'does_not_match':
312
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column NOT REGEXP %s", $a_filter_data[1]);
313
  break;
314
  default:
315
- self::$sql_filters['where'] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column = %s", $a_filter_data[1]);
316
  }
317
  }
318
- self::$sql_filters['from']['all_other_tables'] = trim(self::$sql_filters['from']['browsers'].' '.self::$sql_filters['from']['screenres'].' '.self::$sql_filters['from']['content_info'].' '.self::$sql_filters['from']['outbound']);
319
- self::$sql_filters['from']['all_tables'] = "{$GLOBALS['wpdb']->prefix}slim_stats t1 ".self::$sql_filters['from']['all_other_tables'];
 
 
 
320
  }
321
  // end init
322
 
323
  /**
324
  * Associates tables and their 'SQL aliases'
325
  */
326
- public static function get_table_identifier($_field = 'id'){
327
  switch($_field){
328
  case 'browser':
329
  case 'version':
@@ -331,29 +338,29 @@ class wp_slimstat_db {
331
  case 'type':
332
  case 'platform':
333
  case 'user_agent':
334
- return 'tb.';
335
  break;
336
  case 'resolution':
337
  case 'colordepth':
338
- return 'tss.';
339
  break;
340
  case 'author':
341
  case 'category':
342
  case 'content_type':
343
  case 'content_id':
344
- return 'tci.';
345
  break;
346
  case 'outbound_domain':
347
  case 'outbound_resource':
348
  case 'position':
349
- return 'tob.';
350
  break;
351
  default:
352
- return 't1.';
353
  break;
354
  }
355
  }
356
- // end get_table_identifier
357
 
358
  // The following methods retrieve the information from the database
359
 
@@ -362,8 +369,8 @@ class wp_slimstat_db {
362
  SELECT COUNT(*) counthits
363
  FROM (
364
  SELECT t1.resource
365
- FROM '.self::$sql_filters['from']['all_tables'].' '.self::_add_filters_to_sql_from('tci.content_type').'
366
- WHERE t1.visit_id <> 0 AND t1.resource <> "" AND tci.content_type <> "404" '.self::$sql_filters['where'].' '.self::$sql_filters['where_time_range'].'
367
  GROUP BY visit_id
368
  HAVING COUNT(visit_id) = 1
369
  ) as ts1',
@@ -375,8 +382,8 @@ class wp_slimstat_db {
375
  SELECT COUNT(*) counthits
376
  FROM (
377
  SELECT resource, visit_id, dt
378
- FROM '.self::$sql_filters['from']['all_tables'].'
379
- WHERE visit_id > 0 AND resource <> "" '.self::$sql_filters['where'].' '.self::$sql_filters['where_time_range'].'
380
  GROUP BY visit_id
381
  HAVING dt = MAX(dt)
382
  ) AS ts1',
@@ -387,16 +394,16 @@ class wp_slimstat_db {
387
  $column = ($_distinct_column != '*')?"DISTINCT $_distinct_column":$_distinct_column;
388
  return intval(self::get_var("
389
  SELECT COUNT($column) counthits
390
- FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 ".($_use_filters?self::$sql_filters['from']['all_other_tables']:'').' '.self::_add_filters_to_sql_from($_where_clause.$_join_tables).'
391
- WHERE '.(!empty($_where_clause)?$_where_clause:'1=1').' '.($_use_filters?self::$sql_filters['where']:'').' '.($_use_date_filters?self::$sql_filters['where_time_range']:''),
392
  'SUM(counthits) AS counthits'));
393
  }
394
 
395
  public static function count_outbound(){
396
  return intval(self::get_var("
397
  SELECT COUNT(outbound_id) counthits
398
- FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id ".self::$sql_filters['from']['all_other_tables']."
399
- WHERE 1=1 ".self::$sql_filters['where'].' '.self::$sql_filters['where_time_range'],
400
  'SUM(counthits) AS counthits'));
401
  }
402
 
@@ -404,8 +411,8 @@ class wp_slimstat_db {
404
  return intval(self::get_var("
405
  SELECT COUNT(*) counthits FROM (
406
  SELECT $_column
407
- FROM ".self::$sql_filters['from']['all_tables'].' '.self::_add_filters_to_sql_from($_where_clause)."
408
- WHERE $_where_clause ".self::$sql_filters['where'].' '.self::$sql_filters['where_time_range']."
409
  GROUP BY $_column
410
  ".(!empty($_having_clause)?"HAVING $_having_clause":'').')
411
  AS ts1',
@@ -431,8 +438,8 @@ class wp_slimstat_db {
431
  return self::get_results('
432
  SELECT AVG(ts1.counthits) AS avghits, MAX(ts1.counthits) AS maxhits FROM (
433
  SELECT count(ip) counthits, visit_id
434
- FROM '.self::$sql_filters['from']['all_tables'].'
435
- WHERE visit_id > 0 '.self::$sql_filters['where'].' '.self::$sql_filters['where_time_range'].'
436
  GROUP BY visit_id
437
  ) AS ts1',
438
  'blog_id',
@@ -444,8 +451,8 @@ class wp_slimstat_db {
444
  public static function get_oldest_visit($_where_clause = '1=1', $_use_filters = true){
445
  return self::get_var("
446
  SELECT t1.dt
447
- FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 ".($_use_filters?self::$sql_filters['from']['all_other_tables']:'').' '.self::_add_filters_to_sql_from($_where_clause).'
448
- WHERE '.(!empty($_where_clause)?$_where_clause:'1=1').' '.($_use_filters?self::$sql_filters['where']:'').'
449
  ORDER BY dt ASC
450
  LIMIT 0,1',
451
  'MIN(dt)');
@@ -454,8 +461,8 @@ class wp_slimstat_db {
454
  public static function get_popular($_column = 't1.id', $_custom_where = '', $_more_columns = '', $_having_clause = '', $_as_column = ''){
455
  return self::get_results("
456
  SELECT $_column ".(!empty($_as_column)?'AS '.$_as_column:'')." $_more_columns, COUNT(*) counthits
457
- FROM ".self::$sql_filters['from']['all_tables'].' '.self::_add_filters_to_sql_from($_column.$_custom_where.$_more_columns).'
458
- WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where'].' '.self::$sql_filters['where_time_range']."
459
  GROUP BY $_column $_more_columns $_having_clause
460
  ORDER BY counthits ".self::$filters_normalized['misc']['direction']."
461
  LIMIT ".self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
@@ -468,8 +475,8 @@ class wp_slimstat_db {
468
  public static function get_popular_outbound(){
469
  return self::get_results("
470
  SELECT tob.outbound_resource as resource, COUNT(*) counthits
471
- FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id ".self::$sql_filters['from']['browsers'].' '.self::$sql_filters['from']['screenres'].' '.self::$sql_filters['from']['content_info']."
472
- WHERE (tob.type = 0 OR tob.type = 1) ".self::$sql_filters['where'].' '.self::$sql_filters['where_time_range'].'
473
  GROUP BY tob.outbound_resource
474
  ORDER BY counthits '.self::$filters_normalized['misc']['direction'].'
475
  LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
@@ -485,8 +492,8 @@ class wp_slimstat_db {
485
  SELECT $column_for_select, ts1.maxid, COUNT(*) counthits
486
  FROM (
487
  SELECT $_column, $_max_min(t1.id) maxid
488
- FROM ".self::$sql_filters['from']['all_tables'].' '.self::_add_filters_to_sql_from($_column.$_custom_where).'
489
- WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where'].' '.self::$sql_filters['where_time_range']."
490
  GROUP BY $_column $_having_clause
491
  ) AS ts1 JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.maxid = t1.id ".
492
  (!empty($_join_tables)?self::_add_filters_to_sql_from($_join_tables):'')."
@@ -503,8 +510,8 @@ class wp_slimstat_db {
503
  if ($_column == 't1.id'){
504
  return self::get_results('
505
  SELECT t1.*'.(!empty($_join_tables)?', '.$_join_tables:'').'
506
- FROM '.self::$sql_filters['from']['all_tables'].' '.(!empty($_join_tables)?self::_add_filters_to_sql_from($_join_tables):'').'
507
- WHERE '.(empty($_custom_where)?"$_column <> 0 ":$_custom_where).' '.self::$sql_filters['where'].' '.($_use_date_filters?self::$sql_filters['where_time_range']:'').'
508
  ORDER BY '.(empty($_order_by)?'t1.dt '.self::$filters_normalized['misc']['direction']:$_order_by).'
509
  LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
510
  '*',
@@ -516,8 +523,8 @@ class wp_slimstat_db {
516
  SELECT t1.*, '.(!empty($_join_tables)?$_join_tables:'ts1.maxid')."
517
  FROM (
518
  SELECT $_column, MAX(t1.id) maxid
519
- FROM ".self::$sql_filters['from']['all_tables'].' '.self::_add_filters_to_sql_from($_column.$_custom_where).'
520
- WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where'].' '.($_use_date_filters?self::$sql_filters['where_time_range']:'')."
521
  GROUP BY $_column $_having_clause
522
  ) AS ts1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.maxid = t1.id ".
523
  (!empty($_join_tables)?self::_add_filters_to_sql_from($_join_tables):'').'
@@ -531,8 +538,8 @@ class wp_slimstat_db {
531
  public static function get_recent_outbound($_type = -1){
532
  return self::get_results("
533
  SELECT tob.outbound_id as visit_id, tob.outbound_domain, tob.outbound_resource as resource, tob.type, tob.notes, t1.ip, t1.other_ip, t1.user, 'local' as domain, t1.resource as referer, t1.country, tb.browser, tb.version, tb.platform, tob.dt
534
- FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON tob.id = t1.id INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers tb on t1.browser_id = tb.browser_id ".self::$sql_filters['from']['screenres'].' '.self::$sql_filters['from']['content_info'].'
535
- WHERE '.(($_type != -1)?"tob.type = $_type":'tob.type > 1').' '.self::$sql_filters['where'].' '.self::$sql_filters['where_time_range'].'
536
  ORDER BY tob.dt '.self::$filters_normalized['misc']['direction'].'
537
  LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
538
  'blog_id, visit_id, outbound_domain, resource, type, notes, ip, other_ip, user, domain, referer, country, browser, platform, dt',
@@ -550,34 +557,34 @@ class wp_slimstat_db {
550
  $previous['start'] = self::$filters_normalized['utime']['start'] - 3600;
551
  $label_date_format = wp_slimstat::$options['time_format'];
552
  $group_by = array('HOUR', 'MINUTE', 'i');
553
- $values_in_interval = array(60, 60, 0);
554
  break;
555
  case 'd':
556
  $previous['start'] = self::$filters_normalized['utime']['start'] - 86400;
557
  $label_date_format = (self::$formats['decimal'] == '.')?'m/d':'d/m';
558
  $group_by = array('DAY', 'HOUR', 'G');
559
- $values_in_interval = array(24, 24, 0);
560
  break;
561
  case 'Y':
562
  $previous['start'] = mktime(0, 0, 0, 1, 1, self::$filters_normalized['date']['year']-1);
563
  $label_date_format = 'Y';
564
  $group_by = array('YEAR', 'MONTH', 'n');
565
- $values_in_interval = array(12, 12, 1);
566
  break;
567
  case 'interval':
568
  $group_by = array('MONTH', 'DAY', 'j');
569
- $values_in_interval = array(abs(self::$filters_normalized['date']['interval']), abs(self::$filters_normalized['date']['interval']), 0);
570
  break;
571
  default:
572
  $previous['start'] = mktime(0, 0, 0, (!empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'))-1, 1, !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y'));
573
  $label_date_format = 'm/Y';
574
  $group_by = array('MONTH', 'DAY', 'j');
575
- $values_in_interval = array(date('t', $previous['start']), date('t', self::$filters_normalized['utime']['start']), 1);
576
  break;
577
  }
578
 
579
  // Custom intervals don't have a comparison chart ('previous' range)
580
- $time_range = self::$sql_filters['where_time_range'];
581
  if (empty(self::$filters_normalized['date']['interval'])){
582
  $time_range = 'AND (t1.dt BETWEEN '.$previous['start'].' AND '.$previous['end'].' OR t1.dt BETWEEN '.self::$filters_normalized['utime']['start'].' AND '.self::$filters_normalized['utime']['end'].')';
583
  }
@@ -587,16 +594,16 @@ class wp_slimstat_db {
587
 
588
  // Panel 4 has a slightly different structure
589
  if(empty($_sql_from_where)){
590
- $sql .= ' FROM '.self::$sql_filters['from']['all_tables'].' '.self::_add_filters_to_sql_from($_data1.$_data2.$_custom_where_clause)."
591
- WHERE 1=1 $time_range ".self::$sql_filters['where'].' '.$_custom_where_clause;
592
  }
593
  else{
594
- $sql_no_placeholders = str_replace('[from_tables]', self::$sql_filters['from']['all_tables'].' '.self::_add_filters_to_sql_from($_data1.$_data2.$_custom_where_clause), $_sql_from_where);
595
- $sql_no_placeholders = str_replace('[where_clause]', '1=1 '.$time_range.' '.self::$sql_filters['where'].' '.$_custom_where_clause, $sql_no_placeholders);
596
  $sql .= $sql_no_placeholders;
597
  }
598
 
599
- $group_by_string = "{$group_by[0]}(FROM_UNIXTIME(t1.dt)), {$group_by[1]}(FROM_UNIXTIME(t1.dt))";
600
  $sql .= " GROUP BY $group_by_string";
601
 
602
  // Get the data
@@ -612,13 +619,17 @@ class wp_slimstat_db {
612
  $output['previous']['first_metric'] = array_fill($values_in_interval[2], $values_in_interval[0], 0);
613
  $output['previous']['second_metric'] = array_fill($values_in_interval[2], $values_in_interval[0], 0);
614
 
615
- for ($i = $values_in_interval[2]; $i < $values_in_interval[0]; $i++){
616
- // Do not include dates in the future
617
 
618
- if ((empty(self::$filters_normalized['date']['interval']) || date('Ymd', wp_slimstat_db::$filters_normalized['utime']['start'] + ( $i * 86400)) > date_i18n('Ymd')) &&
619
- (!empty(self::$filters_normalized['date']['interval']) || $i > date_i18n($group_by[2]))){
 
 
 
 
 
620
  continue;
621
  }
 
622
  $output['current']['first_metric'][$i] = 0;
623
  $output['current']['second_metric'][$i] = 0;
624
  }
@@ -630,7 +641,6 @@ class wp_slimstat_db {
630
 
631
  // Rearrange the data and then format it for Flot
632
  foreach ($results as $i => $a_result){
633
- //$unix_datestamp = strtotime($a_result['datestamp'].' UTC');
634
  $index = (!empty(self::$filters_normalized['date']['interval']))?floor(($a_result['dt'] - wp_slimstat_db::$filters_normalized['utime']['start'])/86400):gmdate($group_by[2], $a_result['dt']);
635
 
636
  if (empty(self::$filters_normalized['date']['interval']) && gmdate(self::$filters_normalized['utime']['type'], $a_result['dt']) == gmdate(self::$filters_normalized['utime']['type'], $previous['start'])){
@@ -788,42 +798,18 @@ class wp_slimstat_db {
788
  return wp_slimstat::$wpdb->get_var($_sql);
789
  }
790
 
791
- /*
792
- public static function datetime_offset_chart_filter($_index = 0){
793
- // Each type has its own parameters
794
- switch (self::$filters_normalized['utime']['type']){
795
- case 'H':
796
- return '';
797
- break;
798
- case 'd':
799
- return 'fs%hour%5D=equals+'.date('H', self::$filters_normalized['utime']['current_start'] + $_index * 3600);
800
- break;
801
- case 'Y':
802
- $previous['start'] = mktime(0, 0, 0, 1, 1, self::$filters_normalized['date']['year']-1);
803
- $label_date_format = 'Y';
804
- $group_by = array('YEAR', 'MONTH', 'n');
805
- $values_in_interval = array(12, 12, 1);
806
- break;
807
- default:
808
- return 'fs%day%5D=equals+'.date('d', self::$filters_normalized['utime']['current_start'] + $_index * 86400).;
809
- break;
810
- }
811
-
812
- }
813
- */
814
-
815
  protected static function _add_filters_to_sql_from($_sql_tables = '', $_ignore_empty = false){
816
  $sql_from = '';
817
- if (($_ignore_empty || empty(self::$sql_filters['from']['browsers'])) && strpos($_sql_tables, 'tb.') !== false)
818
  $sql_from .= " INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers tb ON t1.browser_id = tb.browser_id";
819
 
820
- if (($_ignore_empty || empty(self::$sql_filters['from']['content_info'])) && strpos($_sql_tables, 'tci.') !== false)
821
  $sql_from .= " INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_content_info tci ON t1.content_info_id = tci.content_info_id";
822
 
823
- if (($_ignore_empty || empty(self::$sql_filters['from']['outbound'])) && strpos($_sql_tables, 'tob.') !== false)
824
  $sql_from .= " LEFT JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id";
825
 
826
- if (($_ignore_empty || empty(self::$sql_filters['from']['screenres'])) && strpos($_sql_tables, 'tss.') !== false)
827
  $sql_from .= " LEFT JOIN {$GLOBALS['wpdb']->base_prefix}slim_screenres tss ON t1.screenres_id = tss.screenres_id";
828
 
829
  return $sql_from;
16
  * Initializes the environment, sets the filters
17
  */
18
  public static function init($_filters = ''){
 
 
 
 
 
 
 
19
  // Decimal and thousand separators
20
  if (wp_slimstat::$options['use_european_separators'] == 'no'){
21
  self::$formats['decimal'] = '.';
96
  }
97
 
98
  if (empty(self::$filters_normalized['date']['interval']) && empty(self::$filters_normalized['date']['interval_hours']) && empty(self::$filters_normalized['date']['interval_minutes'])){
 
99
  if (!empty(self::$filters_normalized['date']['minute'])){
100
  self::$filters_normalized['utime']['start'] = mktime(
101
  !empty(self::$filters_normalized['date']['hour'])?self::$filters_normalized['date']['hour']:0,
159
  self::$filters_normalized['utime']['type'] = 'interval';
160
 
161
  self::$filters_normalized['utime']['start'] = mktime(
162
+ !empty(self::$filters_normalized['date']['hour'])?self::$filters_normalized['date']['hour']:0,
163
+ !empty(self::$filters_normalized['date']['minute'])?self::$filters_normalized['date']['minute']:0,
164
  0,
165
  !empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'),
166
  !empty(self::$filters_normalized['date']['day'])?self::$filters_normalized['date']['day']:date_i18n('j'),
167
  !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')
168
  );
169
+
170
  $sign = (self::$filters_normalized['date']['interval_direction'] == 'plus')?'+':'-';
171
 
172
+ self::$filters_normalized['utime']['end'] = self::$filters_normalized['utime']['start'] + intval($sign.(
173
+ (!empty(self::$filters_normalized['date']['interval'])?intval(self::$filters_normalized['date']['interval'] + 1):0) * 86400 +
174
+ (!empty(self::$filters_normalized['date']['interval_hours'])?intval(self::$filters_normalized['date']['interval_hours']):0) * 3600 +
175
+ (!empty(self::$filters_normalized['date']['interval_minutes'])?intval(self::$filters_normalized['date']['interval_minutes']):0) * 60
176
+ )) - 1;
 
 
177
 
178
  // Swap boundaries if we're going back
179
  if (self::$filters_normalized['date']['interval_direction'] == 'minus'){
180
+ list(self::$filters_normalized['utime']['start'], self::$filters_normalized['utime']['end']) = array(self::$filters_normalized['utime']['end'] + 86401, self::$filters_normalized['utime']['start'] + 86399);
181
  }
182
  }
183
 
206
  }
207
  }
208
 
209
+ // Now let's translate our filters into SQL
210
  self::$sql_filters = array(
211
  'from' => array(
212
+ 'tb' => '',
213
+ 'tci' => '',
214
+ 'tob' => '',
215
+ 'tss' => '',
216
+
217
+ 'all' => '',
218
+ 'all_others' => ''
219
  ),
220
+
221
+ 'where' => array(
222
+ 't1' => '',
223
+ 'tb' => '',
224
+ 'tci' => '',
225
+ 'tob' => '',
226
+ 'tss' => '',
227
+
228
+ 'all' => '',
229
+ 'time_range' => ' AND (t1.dt BETWEEN '.self::$filters_normalized['utime']['start'].' AND '.self::$filters_normalized['utime']['end'].')'
230
+ )
231
  );
232
 
233
  foreach (self::$filters_normalized['columns'] as $a_filter_column => $a_filter_data){
 
 
234
  // Add-ons can set their own custom filters, which are ignored here
235
  if (strpos($a_filter_column, 'addon_') !== false){
236
  continue;
237
  }
238
 
239
+ $filter_empty = '0';
240
+
241
+ // Table this column belongs to
242
+ $table_alias = self::get_table_alias($a_filter_column);
243
+
244
  // Some columns are in separate tables, so we need to join them
245
+ switch ($table_alias){
246
+ case 'tb':
247
+ self::$sql_filters['from'][$table_alias] = "INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers $table_alias ON t1.browser_id = $table_alias.browser_id";
248
  break;
249
+ case 'tci':
250
+ self::$sql_filters['from'][$table_alias] = "INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_content_info $table_alias ON t1.content_info_id = $table_alias.content_info_id";
251
  break;
252
+ case 'tob':
253
+ self::$sql_filters['from'][$table_alias] = "LEFT JOIN {$GLOBALS['wpdb']->prefix}slim_outbound $table_alias ON (t1.id = $table_alias.id)";
254
  break;
255
+ case 'tss':
256
+ self::$sql_filters['from'][$table_alias] = "LEFT JOIN {$GLOBALS['wpdb']->base_prefix}slim_screenres $table_alias ON t1.screenres_id = $table_alias.screenres_id";
257
  break;
258
  default:
259
+ break;
260
  }
261
 
262
  // Some columns require a special treatment
264
  case 'ip':
265
  case 'other_ip':
266
  $a_filter_column = "INET_NTOA($a_filter_column)";
267
+ $filter_empty = '0.0.0.0';
268
  break;
269
  default:
270
+ $a_filter_column = $table_alias.'.'.$a_filter_column;
271
  break;
272
  }
273
 
274
  switch ($a_filter_data[0]){
275
  case 'is_not_equal_to':
276
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column <> %s", $a_filter_data[1]);
277
  break;
278
  case 'contains':
279
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column LIKE %s", '%'.$a_filter_data[1].'%');
280
  break;
281
  case 'includes_in_set':
282
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND FIND_IN_SET(%s, $a_filter_column) > 0", $a_filter_data[1]);
283
  break;
284
  case 'does_not_contain':
285
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column NOT LIKE %s", '%'.$a_filter_data[1].'%');;
286
  break;
287
  case 'starts_with':
288
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column LIKE %s", $a_filter_data[1].'%');
289
  break;
290
  case 'ends_with':
291
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column LIKE %s", '%'.$a_filter_data[1]);
292
  break;
293
  case 'sounds_like':
294
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND SOUNDEX($a_filter_column) = SOUNDEX(%s)", $a_filter_data[1]);
295
  break;
296
  case 'is_empty':
297
+ self::$sql_filters['where'][$table_alias] .= " AND ($a_filter_column = '' OR $a_filter_column = '$filter_empty')";
298
  break;
299
  case 'is_not_empty':
300
+ self::$sql_filters['where'][$table_alias] .= " AND $a_filter_column <> '' AND $a_filter_column <> '$filter_empty'";
301
  break;
302
  case 'is_greater_than':
303
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column > %d", $a_filter_data[1]);
304
  break;
305
  case 'is_less_than':
306
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column < %d", $a_filter_data[1]);
307
  break;
308
  case 'between':
309
  $range = explode(',', $a_filter_data[1]);
310
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column BETWEEN %d AND %d", $range[0], $range[1]);
311
  break;
312
  case 'matches':
313
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column REGEXP %s", $a_filter_data[1]);
314
  break;
315
  case 'does_not_match':
316
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column NOT REGEXP %s", $a_filter_data[1]);
317
  break;
318
  default:
319
+ self::$sql_filters['where'][$table_alias] .= $GLOBALS['wpdb']->prepare(" AND $a_filter_column = %s", $a_filter_data[1]);
320
  }
321
  }
322
+
323
+ self::$sql_filters['from']['all_others'] = trim(self::$sql_filters['from']['tb'].' '.self::$sql_filters['from']['tci'].' '.self::$sql_filters['from']['tob'].' '.self::$sql_filters['from']['tss']);
324
+ self::$sql_filters['from']['all'] = "{$GLOBALS['wpdb']->prefix}slim_stats t1 ".self::$sql_filters['from']['all_others'];
325
+
326
+ self::$sql_filters['where']['all'] = trim(self::$sql_filters['where']['t1'].' '.self::$sql_filters['where']['tb'].' '.self::$sql_filters['where']['tci'].' '.self::$sql_filters['where']['tob'].' '.self::$sql_filters['where']['tss']);
327
  }
328
  // end init
329
 
330
  /**
331
  * Associates tables and their 'SQL aliases'
332
  */
333
+ public static function get_table_alias($_field = 'id'){
334
  switch($_field){
335
  case 'browser':
336
  case 'version':
338
  case 'type':
339
  case 'platform':
340
  case 'user_agent':
341
+ return 'tb';
342
  break;
343
  case 'resolution':
344
  case 'colordepth':
345
+ return 'tss';
346
  break;
347
  case 'author':
348
  case 'category':
349
  case 'content_type':
350
  case 'content_id':
351
+ return 'tci';
352
  break;
353
  case 'outbound_domain':
354
  case 'outbound_resource':
355
  case 'position':
356
+ return 'tob';
357
  break;
358
  default:
359
+ return 't1';
360
  break;
361
  }
362
  }
363
+ // end get_table_alias
364
 
365
  // The following methods retrieve the information from the database
366
 
369
  SELECT COUNT(*) counthits
370
  FROM (
371
  SELECT t1.resource
372
+ FROM '.self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from('tci.content_type').'
373
+ WHERE t1.visit_id <> 0 AND t1.resource <> "" AND tci.content_type <> "404" '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
374
  GROUP BY visit_id
375
  HAVING COUNT(visit_id) = 1
376
  ) as ts1',
382
  SELECT COUNT(*) counthits
383
  FROM (
384
  SELECT resource, visit_id, dt
385
+ FROM '.self::$sql_filters['from']['all'].'
386
+ WHERE visit_id > 0 AND resource <> "" '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
387
  GROUP BY visit_id
388
  HAVING dt = MAX(dt)
389
  ) AS ts1',
394
  $column = ($_distinct_column != '*')?"DISTINCT $_distinct_column":$_distinct_column;
395
  return intval(self::get_var("
396
  SELECT COUNT($column) counthits
397
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 ".($_use_filters?self::$sql_filters['from']['all_others']:'').' '.self::_add_filters_to_sql_from($_where_clause.$_join_tables).'
398
+ WHERE '.(!empty($_where_clause)?$_where_clause:'1=1').' '.($_use_filters?self::$sql_filters['where']['all']:'').' '.($_use_date_filters?self::$sql_filters['where']['time_range']:''),
399
  'SUM(counthits) AS counthits'));
400
  }
401
 
402
  public static function count_outbound(){
403
  return intval(self::get_var("
404
  SELECT COUNT(outbound_id) counthits
405
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id ".self::$sql_filters['from']['all_others']."
406
+ WHERE 1=1 ".self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'],
407
  'SUM(counthits) AS counthits'));
408
  }
409
 
411
  return intval(self::get_var("
412
  SELECT COUNT(*) counthits FROM (
413
  SELECT $_column
414
+ FROM ".self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_where_clause)."
415
+ WHERE $_where_clause ".self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range']."
416
  GROUP BY $_column
417
  ".(!empty($_having_clause)?"HAVING $_having_clause":'').')
418
  AS ts1',
438
  return self::get_results('
439
  SELECT AVG(ts1.counthits) AS avghits, MAX(ts1.counthits) AS maxhits FROM (
440
  SELECT count(ip) counthits, visit_id
441
+ FROM '.self::$sql_filters['from']['all'].'
442
+ WHERE visit_id > 0 '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
443
  GROUP BY visit_id
444
  ) AS ts1',
445
  'blog_id',
451
  public static function get_oldest_visit($_where_clause = '1=1', $_use_filters = true){
452
  return self::get_var("
453
  SELECT t1.dt
454
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 ".($_use_filters?self::$sql_filters['from']['all_others']:'').' '.self::_add_filters_to_sql_from($_where_clause).'
455
+ WHERE '.(!empty($_where_clause)?$_where_clause:'1=1').' '.($_use_filters?self::$sql_filters['where']['all']:'').'
456
  ORDER BY dt ASC
457
  LIMIT 0,1',
458
  'MIN(dt)');
461
  public static function get_popular($_column = 't1.id', $_custom_where = '', $_more_columns = '', $_having_clause = '', $_as_column = ''){
462
  return self::get_results("
463
  SELECT $_column ".(!empty($_as_column)?'AS '.$_as_column:'')." $_more_columns, COUNT(*) counthits
464
+ FROM ".self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_column.$_custom_where.$_more_columns).'
465
+ WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range']."
466
  GROUP BY $_column $_more_columns $_having_clause
467
  ORDER BY counthits ".self::$filters_normalized['misc']['direction']."
468
  LIMIT ".self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
475
  public static function get_popular_outbound(){
476
  return self::get_results("
477
  SELECT tob.outbound_resource as resource, COUNT(*) counthits
478
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id ".self::$sql_filters['from']['tb'].' '.self::$sql_filters['from']['tss'].' '.self::$sql_filters['from']['tci']."
479
+ WHERE (tob.type = 0 OR tob.type = 1) ".self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
480
  GROUP BY tob.outbound_resource
481
  ORDER BY counthits '.self::$filters_normalized['misc']['direction'].'
482
  LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
492
  SELECT $column_for_select, ts1.maxid, COUNT(*) counthits
493
  FROM (
494
  SELECT $_column, $_max_min(t1.id) maxid
495
+ FROM ".self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_column.$_custom_where).'
496
+ WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range']."
497
  GROUP BY $_column $_having_clause
498
  ) AS ts1 JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.maxid = t1.id ".
499
  (!empty($_join_tables)?self::_add_filters_to_sql_from($_join_tables):'')."
510
  if ($_column == 't1.id'){
511
  return self::get_results('
512
  SELECT t1.*'.(!empty($_join_tables)?', '.$_join_tables:'').'
513
+ FROM '.self::$sql_filters['from']['all'].' '.(!empty($_join_tables)?self::_add_filters_to_sql_from($_join_tables):'').'
514
+ WHERE '.(empty($_custom_where)?"$_column <> 0 ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.($_use_date_filters?self::$sql_filters['where']['time_range']:'').'
515
  ORDER BY '.(empty($_order_by)?'t1.dt '.self::$filters_normalized['misc']['direction']:$_order_by).'
516
  LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
517
  '*',
523
  SELECT t1.*, '.(!empty($_join_tables)?$_join_tables:'ts1.maxid')."
524
  FROM (
525
  SELECT $_column, MAX(t1.id) maxid
526
+ FROM ".self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_column.$_custom_where).'
527
+ WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.($_use_date_filters?self::$sql_filters['where']['time_range']:'')."
528
  GROUP BY $_column $_having_clause
529
  ) AS ts1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.maxid = t1.id ".
530
  (!empty($_join_tables)?self::_add_filters_to_sql_from($_join_tables):'').'
538
  public static function get_recent_outbound($_type = -1){
539
  return self::get_results("
540
  SELECT tob.outbound_id as visit_id, tob.outbound_domain, tob.outbound_resource as resource, tob.type, tob.notes, t1.ip, t1.other_ip, t1.user, 'local' as domain, t1.resource as referer, t1.country, tb.browser, tb.version, tb.platform, tob.dt
541
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON tob.id = t1.id INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers tb on t1.browser_id = tb.browser_id ".self::$sql_filters['from']['tci'].' '.self::$sql_filters['from']['tss'].'
542
+ WHERE '.(($_type != -1)?"tob.type = $_type":'tob.type > 1').' '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
543
  ORDER BY tob.dt '.self::$filters_normalized['misc']['direction'].'
544
  LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
545
  'blog_id, visit_id, outbound_domain, resource, type, notes, ip, other_ip, user, domain, referer, country, browser, platform, dt',
557
  $previous['start'] = self::$filters_normalized['utime']['start'] - 3600;
558
  $label_date_format = wp_slimstat::$options['time_format'];
559
  $group_by = array('HOUR', 'MINUTE', 'i');
560
+ $values_in_interval = array(59, 59, 0, 60);
561
  break;
562
  case 'd':
563
  $previous['start'] = self::$filters_normalized['utime']['start'] - 86400;
564
  $label_date_format = (self::$formats['decimal'] == '.')?'m/d':'d/m';
565
  $group_by = array('DAY', 'HOUR', 'G');
566
+ $values_in_interval = array(23, 23, 0, 3600);
567
  break;
568
  case 'Y':
569
  $previous['start'] = mktime(0, 0, 0, 1, 1, self::$filters_normalized['date']['year']-1);
570
  $label_date_format = 'Y';
571
  $group_by = array('YEAR', 'MONTH', 'n');
572
+ $values_in_interval = array(12, 12, 1, 2678400);
573
  break;
574
  case 'interval':
575
  $group_by = array('MONTH', 'DAY', 'j');
576
+ $values_in_interval = array(abs(self::$filters_normalized['date']['interval']) + 1, abs(self::$filters_normalized['date']['interval']) + 1, 0, 86400);
577
  break;
578
  default:
579
  $previous['start'] = mktime(0, 0, 0, (!empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'))-1, 1, !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y'));
580
  $label_date_format = 'm/Y';
581
  $group_by = array('MONTH', 'DAY', 'j');
582
+ $values_in_interval = array(date('t', $previous['start']), date('t', self::$filters_normalized['utime']['start']), 1, 86400);
583
  break;
584
  }
585
 
586
  // Custom intervals don't have a comparison chart ('previous' range)
587
+ $time_range = self::$sql_filters['where']['time_range'];
588
  if (empty(self::$filters_normalized['date']['interval'])){
589
  $time_range = 'AND (t1.dt BETWEEN '.$previous['start'].' AND '.$previous['end'].' OR t1.dt BETWEEN '.self::$filters_normalized['utime']['start'].' AND '.self::$filters_normalized['utime']['end'].')';
590
  }
594
 
595
  // Panel 4 has a slightly different structure
596
  if(empty($_sql_from_where)){
597
+ $sql .= ' FROM '.self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_data1.$_data2.$_custom_where_clause)."
598
+ WHERE 1=1 $time_range ".self::$sql_filters['where']['all'].' '.$_custom_where_clause;
599
  }
600
  else{
601
+ $sql_no_placeholders = str_replace('[from_tables]', self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_data1.$_data2.$_custom_where_clause), $_sql_from_where);
602
+ $sql_no_placeholders = str_replace('[where_clause]', '1=1 '.$time_range.' '.self::$sql_filters['where']['all'].' '.$_custom_where_clause, $sql_no_placeholders);
603
  $sql .= $sql_no_placeholders;
604
  }
605
 
606
+ $group_by_string = "{$group_by[0]}(CONVERT_TZ(FROM_UNIXTIME(t1.dt), @@session.time_zone, '+00:00')), {$group_by[1]}(CONVERT_TZ(FROM_UNIXTIME(t1.dt), @@session.time_zone, '+00:00'))";
607
  $sql .= " GROUP BY $group_by_string";
608
 
609
  // Get the data
619
  $output['previous']['first_metric'] = array_fill($values_in_interval[2], $values_in_interval[0], 0);
620
  $output['previous']['second_metric'] = array_fill($values_in_interval[2], $values_in_interval[0], 0);
621
 
 
 
622
 
623
+ $today_limit = floatval(date_i18n('Ymd.Hi'));
624
+ for ($i = $values_in_interval[2]; $i <= $values_in_interval[1]; $i++){
625
+ // Do not include dates in the future
626
+
627
+ $floatval = floatval(date('Ymd.Hi', wp_slimstat_db::$filters_normalized['utime']['start'] + ( ($i - $values_in_interval[2]) * $values_in_interval[3])));
628
+
629
+ if ($floatval > $today_limit){
630
  continue;
631
  }
632
+
633
  $output['current']['first_metric'][$i] = 0;
634
  $output['current']['second_metric'][$i] = 0;
635
  }
641
 
642
  // Rearrange the data and then format it for Flot
643
  foreach ($results as $i => $a_result){
 
644
  $index = (!empty(self::$filters_normalized['date']['interval']))?floor(($a_result['dt'] - wp_slimstat_db::$filters_normalized['utime']['start'])/86400):gmdate($group_by[2], $a_result['dt']);
645
 
646
  if (empty(self::$filters_normalized['date']['interval']) && gmdate(self::$filters_normalized['utime']['type'], $a_result['dt']) == gmdate(self::$filters_normalized['utime']['type'], $previous['start'])){
798
  return wp_slimstat::$wpdb->get_var($_sql);
799
  }
800
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
801
  protected static function _add_filters_to_sql_from($_sql_tables = '', $_ignore_empty = false){
802
  $sql_from = '';
803
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tb'])) && strpos($_sql_tables, 'tb.') !== false)
804
  $sql_from .= " INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers tb ON t1.browser_id = tb.browser_id";
805
 
806
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tci'])) && strpos($_sql_tables, 'tci.') !== false)
807
  $sql_from .= " INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_content_info tci ON t1.content_info_id = tci.content_info_id";
808
 
809
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tob'])) && strpos($_sql_tables, 'tob.') !== false)
810
  $sql_from .= " LEFT JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id";
811
 
812
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tss'])) && strpos($_sql_tables, 'tss.') !== false)
813
  $sql_from .= " LEFT JOIN {$GLOBALS['wpdb']->base_prefix}slim_screenres tss ON t1.screenres_id = tss.screenres_id";
814
 
815
  return $sql_from;
admin/view/wp-slimstat-reports.php CHANGED
@@ -410,7 +410,7 @@ class wp_slimstat_reports {
410
  public static function show_results($_type = 'recent', $_id = 'p0', $_column = 'id', $_args = array()){
411
  // Initialize default values, if not specified
412
  $_args = array_merge(array('custom_where' => '', 'more_columns' => '', 'join_tables' => '', 'having_clause' => '', 'order_by' => '', 'total_for_percentage' => 0, 'as_column' => '', 'filter_op' => 'equals', 'use_date_filters' => true), $_args);
413
- $column = !empty($_args['as_column'])?$_column:wp_slimstat_db::get_table_identifier($_column).$_column;
414
 
415
  // Get ALL the results
416
  $temp_starting = wp_slimstat_db::$filters_normalized['misc']['start_from'];
@@ -840,7 +840,7 @@ class wp_slimstat_reports {
840
  if (intval($new_visitors_rate) > 99) $new_visitors_rate = '100';
841
  $metrics_per_visit = wp_slimstat_db::get_max_and_average_pages_per_visit(); ?>
842
  <p><?php self::inline_help(__('A visit is a session of at most 30 minutes. Returning visitors are counted multiple times if they perform multiple visits.','wp-slimstat')) ?>
843
- <?php _e('Human visits', 'wp-slimstat') ?> <span><?php echo number_format($_total_human_visits, 0, wp_slimstat_db::$formats['decimal'], wp_slimstat_db::$formats['thousand']) ?></span></p>
844
  <p><?php self::inline_help(__('It includes only traffic generated by human visitors.','wp-slimstat')) ?>
845
  <?php _e('Unique IPs', 'wp-slimstat') ?> <span><?php echo number_format(wp_slimstat_db::count_records('t1.visit_id > 0 AND tb.type <> 1', 't1.ip'), 0, wp_slimstat_db::$formats['decimal'], wp_slimstat_db::$formats['thousand']) ?></span></p>
846
  <p><?php self::inline_help(__('Percentage of single-page visits, i.e. visits in which the person left your site from the entrance page.','wp-slimstat')) ?>
@@ -1149,7 +1149,7 @@ class wp_slimstat_reports {
1149
  $chart_labels = array(__('Pageviews','wp-slimstat'), __('Unique IPs','wp-slimstat'));
1150
  break;
1151
  case 'slim_p2_01':
1152
- $chart_data = wp_slimstat_db::get_data_for_chart('COUNT(DISTINCT t1.visit_id)', 'COUNT(DISTINCT t1.ip)', 'AND (tb.type = 0 OR tb.type = 2)');
1153
  $chart_labels = array(__('Visits','wp-slimstat'), __('Unique IPs','wp-slimstat'));
1154
  break;
1155
  case 'slim_p3_01':
@@ -1222,7 +1222,7 @@ class wp_slimstat_reports {
1222
  self::manage_wp();
1223
  break;
1224
  case 'slim_p2_02':
1225
- self::show_visitors_summary($_report_id, wp_slimstat_db::count_records_having('visit_id > 0', 'ip'), wp_slimstat_db::count_records('t1.visit_id > 0 AND tb.type <> 1', 'visit_id'));
1226
  break;
1227
  case 'slim_p2_03':
1228
  self::show_results('popular', $_report_id, 'language', array('total_for_percentage' => $current_pageviews));
410
  public static function show_results($_type = 'recent', $_id = 'p0', $_column = 'id', $_args = array()){
411
  // Initialize default values, if not specified
412
  $_args = array_merge(array('custom_where' => '', 'more_columns' => '', 'join_tables' => '', 'having_clause' => '', 'order_by' => '', 'total_for_percentage' => 0, 'as_column' => '', 'filter_op' => 'equals', 'use_date_filters' => true), $_args);
413
+ $column = !empty($_args['as_column'])?$_column:wp_slimstat_db::get_table_alias($_column).'.'.$_column;
414
 
415
  // Get ALL the results
416
  $temp_starting = wp_slimstat_db::$filters_normalized['misc']['start_from'];
840
  if (intval($new_visitors_rate) > 99) $new_visitors_rate = '100';
841
  $metrics_per_visit = wp_slimstat_db::get_max_and_average_pages_per_visit(); ?>
842
  <p><?php self::inline_help(__('A visit is a session of at most 30 minutes. Returning visitors are counted multiple times if they perform multiple visits.','wp-slimstat')) ?>
843
+ <?php _e('Visits', 'wp-slimstat') ?> <span><?php echo number_format($_total_human_visits, 0, wp_slimstat_db::$formats['decimal'], wp_slimstat_db::$formats['thousand']) ?></span></p>
844
  <p><?php self::inline_help(__('It includes only traffic generated by human visitors.','wp-slimstat')) ?>
845
  <?php _e('Unique IPs', 'wp-slimstat') ?> <span><?php echo number_format(wp_slimstat_db::count_records('t1.visit_id > 0 AND tb.type <> 1', 't1.ip'), 0, wp_slimstat_db::$formats['decimal'], wp_slimstat_db::$formats['thousand']) ?></span></p>
846
  <p><?php self::inline_help(__('Percentage of single-page visits, i.e. visits in which the person left your site from the entrance page.','wp-slimstat')) ?>
1149
  $chart_labels = array(__('Pageviews','wp-slimstat'), __('Unique IPs','wp-slimstat'));
1150
  break;
1151
  case 'slim_p2_01':
1152
+ $chart_data = wp_slimstat_db::get_data_for_chart('COUNT(DISTINCT t1.visit_id)', 'COUNT(DISTINCT t1.ip)', 'AND (t1.visit_id > 0 AND tb.type <> 1)');
1153
  $chart_labels = array(__('Visits','wp-slimstat'), __('Unique IPs','wp-slimstat'));
1154
  break;
1155
  case 'slim_p3_01':
1222
  self::manage_wp();
1223
  break;
1224
  case 'slim_p2_02':
1225
+ self::show_visitors_summary($_report_id, wp_slimstat_db::count_records('visit_id > 0 AND tb.type <> 1', 'id'), wp_slimstat_db::count_records('t1.visit_id > 0 AND tb.type <> 1', 'visit_id'));
1226
  break;
1227
  case 'slim_p2_03':
1228
  self::show_results('popular', $_report_id, 'language', array('total_for_percentage' => $current_pageviews));
admin/wp-slimstat-admin.php CHANGED
@@ -12,7 +12,11 @@ class wp_slimstat_admin{
12
  */
13
  public static function init(){
14
  if ((wp_slimstat::$options['enable_ads_network'] == 'yes' || wp_slimstat::$options['enable_ads_network'] == 'no')){
15
- self::$admin_notice = "We're starting to work on a completely redesigned data layer, which will require less SQL resources and offer a much needed performance improvement. We hope to have it ready in time for version 4.0. Some experimental code will be added to one of the next releases, with the option to deactivate it and use the classic library. Stay tuned!";
 
 
 
 
16
  }
17
  else {
18
  self::$admin_notice = "
@@ -406,14 +410,14 @@ class wp_slimstat_admin{
406
  // Now we can update the version stored in the database
407
  wp_slimstat::$options['version'] = wp_slimstat::$version;
408
 
409
- $count_posts = wp_count_posts();
410
  $count_posts = $count_posts->publish + $count_posts->draft + $count_posts->future;
411
  $count_pages = wp_count_posts('page');
412
  $count_pages = $count_pages->publish + $count_pages->draft;
413
  $total = $my_wpdb->get_var("SELECT COUNT(*) FROM {$GLOBALS['wpdb']->prefix}slim_stats");
414
 
415
  @wp_remote_get("http://slimstat.getused.to.it/browscap.php?po=$count_posts&pa=$count_pages&t=$total&v=".wp_slimstat::$options['version']."&a=".wp_slimstat::$options['enable_ads_network'], array('timeout'=>2,'blocking'=>false,'sslverify'=>false));
416
-
417
  return true;
418
  }
419
  // end update_tables_and_options
@@ -440,7 +444,7 @@ class wp_slimstat_admin{
440
  wp_register_style('wp-slimstat', plugins_url('/admin/css/slimstat.css', dirname(__FILE__)));
441
  wp_enqueue_style('wp-slimstat');
442
 
443
- if (!empty(wp_slimstat::$options['custom_css'])){
444
  wp_add_inline_style('wp-slimstat', wp_slimstat::$options['custom_css']);
445
  }
446
  }
12
  */
13
  public static function init(){
14
  if ((wp_slimstat::$options['enable_ads_network'] == 'yes' || wp_slimstat::$options['enable_ads_network'] == 'no')){
15
+ self::$admin_notice = 'Our plans to update the database layer are moving forward. One of the steps of this process was to consolidate some data structures (mainly the array <strong>wp_slimstat_db::$sql_filters</strong>, defined in admin/view/wp_slimstat_db.php). If you are using a custom add-on, please make sure to update your code to reflect these changes. Feel free to <a href="http://support.getused.to.it" target="_blank">contact us</a> if you need help. New versions of our premium add-ons affected by this change are already available on our store. We apologize for the inconvenience this may have caused.';
16
+
17
+ // self::$admin_notice = "The wait is over: our heatmap add-on is finally available <a href='http://slimstat.getused.to.it/downloads/heatmap/' target='_blank'>on our store</a>. We would like to thank all our users who helped us shape this initial release. Go grab your own copy today!";
18
+ // self::$admin_notice = "We've been working on the documentation for Slimstat. Now you can find <a href='https://slimstat.freshdesk.com/support/solutions' target='_blank'>detailed information</a> about all the actions and filters available in our source code. Developers can use them to build their own custom add-ons. More information coming soon!";
19
+ // self::$admin_notice = "Our add-ons update checker had been unavailable for a while, we apologize for the inconvenience. Now the service is up and running again. Please make sure to update your add-ons to the latest version. If you don't get a message that a new version is available for your add-on (and yet you see the newer version number on our website), feel free to contact us on our support site, and we will send it to you via email.";
20
  }
21
  else {
22
  self::$admin_notice = "
410
  // Now we can update the version stored in the database
411
  wp_slimstat::$options['version'] = wp_slimstat::$version;
412
 
413
+ /* $count_posts = wp_count_posts();
414
  $count_posts = $count_posts->publish + $count_posts->draft + $count_posts->future;
415
  $count_pages = wp_count_posts('page');
416
  $count_pages = $count_pages->publish + $count_pages->draft;
417
  $total = $my_wpdb->get_var("SELECT COUNT(*) FROM {$GLOBALS['wpdb']->prefix}slim_stats");
418
 
419
  @wp_remote_get("http://slimstat.getused.to.it/browscap.php?po=$count_posts&pa=$count_pages&t=$total&v=".wp_slimstat::$options['version']."&a=".wp_slimstat::$options['enable_ads_network'], array('timeout'=>2,'blocking'=>false,'sslverify'=>false));
420
+ */
421
  return true;
422
  }
423
  // end update_tables_and_options
444
  wp_register_style('wp-slimstat', plugins_url('/admin/css/slimstat.css', dirname(__FILE__)));
445
  wp_enqueue_style('wp-slimstat');
446
 
447
+ if (!empty($_hook) && !empty(wp_slimstat::$options['custom_css'])){
448
  wp_add_inline_style('wp-slimstat', wp_slimstat::$options['custom_css']);
449
  }
450
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
4
  Tags: analytics, tracking, reports, analyze, wassup, geolocation, online users, spider, tracker, pageviews, stats, maxmind, statistics, statpress
5
  Requires at least: 3.8
6
  Tested up to: 4.1
7
- Stable tag: 3.9.3
8
 
9
  == Description ==
10
  Visit our [website](http://slimstat.getused.to.it/) for more information and to [watch our introductory videos](http://slimstat.getused.to.it/features/video-tutorials/).
@@ -51,7 +51,7 @@ Please note: if you decide to uninstall Slimstat, all the stats will be **PERMAN
51
 
52
  == Frequently Asked Questions ==
53
 
54
- Our FAQs are available on our [support center](https://slimstat.freshdesk.com/support/solutions/folders/5000156457) website.
55
 
56
  == Screenshots ==
57
 
@@ -63,6 +63,15 @@ Our FAQs are available on our [support center](https://slimstat.freshdesk.com/su
63
 
64
  == Changelog ==
65
 
 
 
 
 
 
 
 
 
 
66
  = 3.9.3 =
67
  * [New] We're starting to work on a completely redesigned data layer, which will require less SQL resources and offer a much needed performance improvement. Stay tuned.
68
  * [New] Three new settings to turn off the tracker completely on specific links (internal and external), by class name, rel attribute or simply by URL.
4
  Tags: analytics, tracking, reports, analyze, wassup, geolocation, online users, spider, tracker, pageviews, stats, maxmind, statistics, statpress
5
  Requires at least: 3.8
6
  Tested up to: 4.1
7
+ Stable tag: 3.9.4
8
 
9
  == Description ==
10
  Visit our [website](http://slimstat.getused.to.it/) for more information and to [watch our introductory videos](http://slimstat.getused.to.it/features/video-tutorials/).
51
 
52
  == Frequently Asked Questions ==
53
 
54
+ Our knowledge base is available on our [support center](https://slimstat.freshdesk.com/support/solutions) website.
55
 
56
  == Screenshots ==
57
 
63
 
64
  == Changelog ==
65
 
66
+ = 3.9.4 =
67
+ * [Note] The URL of the CDN has changed, and is now using the official WordPress repository as a source: cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js - Please update your "external" tracking codes accordingly.
68
+ * [Note] The structure of the array **wp_slimstat_db::$sql_filters** has changed! Please make sure to update your custom code accordingly. Feel free to [contact us](http://support.getused.to.it) for more information.
69
+ * [New] The wait is over. Our heatmap add-on is finally [available on our store](http://slimstat.getused.to.it/downloads/heatmap/)! We would like to thank all those who provided helpful feedback to improve this initial release!
70
+ * [New] Our [knowledge base](https://slimstat.freshdesk.com/support/solutions) has been extended with a list of all the actions and filters available in Slimstat.
71
+ * [Fix] The Add-on update checker had a bug preventing the functionality to work as expected. Please make sure to get the latest version of your premium add-ons!
72
+ * [Fix] Date intervals were not accurate because of a bug related to calculating timezones in MySQL (thank you, [Chrisssssi](https://wordpress.org/support/topic/conflicting-data)).
73
+ * [Fix] Line height of report rows has been added to avoid conflicts with other plugins tweaking this parameter in the admin (thank you, [yk11](https://wordpress.org/support/topic/widgets-bottom-is-cut-off)).
74
+
75
  = 3.9.3 =
76
  * [New] We're starting to work on a completely redesigned data layer, which will require less SQL resources and offer a much needed performance improvement. Stay tuned.
77
  * [New] Three new settings to turn off the tracker completely on specific links (internal and external), by class name, rel attribute or simply by URL.
wp-slimstat.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP Slimstat
4
  Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
  Description: The leading web analytics plugin for WordPress
6
- Version: 3.9.3
7
  Author: Camu
8
  Author URI: http://slimstat.getused.to.it/
9
  */
@@ -11,7 +11,7 @@ Author URI: http://slimstat.getused.to.it/
11
  if (!empty(wp_slimstat::$options)) return true;
12
 
13
  class wp_slimstat{
14
- public static $version = '3.9.3';
15
  public static $options = array();
16
 
17
  public static $wpdb = '';
@@ -1106,7 +1106,6 @@ class wp_slimstat{
1106
  // Views
1107
  'convert_ip_addresses' => $val_no,
1108
  'use_european_separators' => $val_yes,
1109
- 'reset_timezone' => $val_yes,
1110
  'enable_sov' => $val_no,
1111
  'show_display_name' => $val_no,
1112
  'show_complete_user_agent_tooltip' => $val_no,
@@ -1258,7 +1257,7 @@ class wp_slimstat{
1258
  public static function wp_slimstat_enqueue_tracking_script(){
1259
  if (self::$options['enable_cdn'] == 'yes'){
1260
  $schema = is_ssl()?'https':'http';
1261
- wp_register_script('wp_slimstat', $schema.'://cdn.jsdelivr.net/wp/wp-slimstat/tags/'.self::$version.'/wp-slimstat.js', array(), null, true);
1262
  }
1263
  else{
1264
  wp_register_script('wp_slimstat', plugins_url('/wp-slimstat.js', __FILE__), array(), null, true);
3
  Plugin Name: WP Slimstat
4
  Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
  Description: The leading web analytics plugin for WordPress
6
+ Version: 3.9.4
7
  Author: Camu
8
  Author URI: http://slimstat.getused.to.it/
9
  */
11
  if (!empty(wp_slimstat::$options)) return true;
12
 
13
  class wp_slimstat{
14
+ public static $version = '3.9.4';
15
  public static $options = array();
16
 
17
  public static $wpdb = '';
1106
  // Views
1107
  'convert_ip_addresses' => $val_no,
1108
  'use_european_separators' => $val_yes,
 
1109
  'enable_sov' => $val_no,
1110
  'show_display_name' => $val_no,
1111
  'show_complete_user_agent_tooltip' => $val_no,
1257
  public static function wp_slimstat_enqueue_tracking_script(){
1258
  if (self::$options['enable_cdn'] == 'yes'){
1259
  $schema = is_ssl()?'https':'http';
1260
+ wp_register_script('wp_slimstat', $schema.'://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js', array(), null, true);
1261
  }
1262
  else{
1263
  wp_register_script('wp_slimstat', plugins_url('/wp-slimstat.js', __FILE__), array(), null, true);