Slimstat Analytics - Version 4.1.6.1

Version Description

  • [New] Contextual counters are now added not just to pages and posts, but to other custom post types available on your website.
  • [Update] Optimized SQL query that retrieves the data for the Access Log report.
  • [Update] New link for GetSocial.io partnership.
  • [Fix] Patched a remote XSS vulnerability related to forged referrer URLs.
  • [Fix] Bug in refreshing Access Log (second try).
  • [Fix] Bug in calculating Unique IP counters for pages and posts.
  • [Fix] Link to install the GeoLocation DB was pointing to the wrong tab under Settings.
  • [Fix] When selecting the filter in Overview > Top Pages, reports were returning empty datasets.
  • [Fix] Resetting the report layout was not always working as expected, if Slimstat was displayed in the admin bar.
Download this release

Release Info

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

Code changes from version 4.1.6 to 4.1.6.1

admin/config/addons.php CHANGED
@@ -54,11 +54,23 @@ if (!is_array($list_addons)){
54
  <strong><a target="_blank" href="<?php echo $a_addon['download_url'] ?>"><?php echo $a_addon['name'] ?></a></strong>
55
  <div class="row-actions-visible"><?php
56
  if ( !empty( $a_addon['version'] ) ) {
57
- echo 'Version: '.$a_addon['version'].'<br/>';
58
  }
59
 
60
  if ( $is_active ){
61
- echo 'Installed and Active';
 
 
 
 
 
 
 
 
 
 
 
 
62
  $at_least_one_add_on_active = true;
63
  }
64
  else{
54
  <strong><a target="_blank" href="<?php echo $a_addon['download_url'] ?>"><?php echo $a_addon['name'] ?></a></strong>
55
  <div class="row-actions-visible"><?php
56
  if ( !empty( $a_addon['version'] ) ) {
57
+ echo ( $is_active ? __( 'Repo Version', 'wp-slimstat' ) : __( 'Version', 'wp-slimstat' ) ) . ': ' . $a_addon[ 'version' ].'<br/>';
58
  }
59
 
60
  if ( $is_active ){
61
+ if ( is_plugin_active($a_addon['slug'].'/index.php') ) {
62
+ $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $a_addon['slug'] . '/index.php' );
63
+ }
64
+ else {
65
+ $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $a_addon['slug'] . '/' . $a_addon['slug'] );
66
+ }
67
+
68
+ if ( !empty( $plugin_data[ 'Version' ] ) ) {
69
+ echo __( 'Your Version:', 'wp-slimstat' ) . ' ' . $plugin_data[ 'Version' ];
70
+ }
71
+ else{
72
+ _e( 'Installed and Active', 'wp-slimstat' );
73
+ }
74
  $at_least_one_add_on_active = true;
75
  }
76
  else{
admin/config/index.php CHANGED
@@ -242,7 +242,7 @@ $options = array(
242
  'show_sql_debug' => array('description' => __('Debug Mode','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Display the SQL queries used to retrieve the data.','wp-slimstat')),
243
  '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')),
244
  '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')),
245
- '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'))
246
  )
247
  ),
248
 
242
  'show_sql_debug' => array('description' => __('Debug Mode','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Display the SQL queries used to retrieve the data.','wp-slimstat')),
243
  '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')),
244
  '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')),
245
+ '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. Please note, this option is enabled only when the setting is explicitly set to YES.",'wp-slimstat'))
246
  )
247
  ),
248
 
admin/config/maintenance.php CHANGED
@@ -159,6 +159,10 @@ if (!empty($_REQUEST['action'])){
159
  break;
160
 
161
  case 'restore-views':
 
 
 
 
162
  $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%meta-box-order_slimstat%'");
163
  $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%metaboxhidden_slimstat%'");
164
  $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%closedpostboxes_slimstat%'");
159
  break;
160
 
161
  case 'restore-views':
162
+ $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%meta-box-order_admin_page_wp-slim-view-%'");
163
+ $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%meta-box-order_slimstat_page_wp-slim-view-%'");
164
+ $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%mmetaboxhidden_admin_page_wp-slim-view-%'");
165
+ $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%mmetaboxhidden_slimstat_page_wp-slim-view-%'");
166
  $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%meta-box-order_slimstat%'");
167
  $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%metaboxhidden_slimstat%'");
168
  $GLOBALS['wpdb']->query("DELETE FROM {$GLOBALS['wpdb']->prefix}usermeta WHERE meta_key LIKE '%closedpostboxes_slimstat%'");
admin/view/wp-slimstat-db.php CHANGED
@@ -1,927 +1,957 @@
1
- <?php
2
-
3
- // Let's define the main class with all the methods that we need
4
- class wp_slimstat_db {
5
- // Filters
6
- public static $columns_names = array();
7
- public static $filters_normalized = array();
8
-
9
- // Number and date formats
10
- public static $formats = array( 'decimal' => ',', 'thousand' => '.' );
11
-
12
- // Structure that maps filters to SQL information (table names, clauses, lookup tables, etc)
13
- public static $sql_where = array( 'columns' => '', 'time_range' => '' );
14
-
15
- // Filters that are not visible in the dropdown
16
- public static $all_columns_names = array();
17
-
18
- // Debug message
19
- public static $debug_message = '';
20
-
21
- /*
22
- * Sets the filters and other structures needed to store the data retrieved from the DB
23
- */
24
- public static function init( $_filters = '' ){
25
- // Decimal and thousand separators
26
- if ( wp_slimstat::$options[ 'use_european_separators' ] == 'no' ){
27
- self::$formats[ 'decimal' ] = '.';
28
- self::$formats[ 'thousand' ] = ',';
29
- }
30
-
31
- // Filters are defined as: browser equals Chrome|country starts_with en
32
- if ( !is_string( $_filters ) || empty( $_filters ) ){
33
- $_filters = '';
34
- }
35
-
36
- // List of supported filters and their friendly names
37
- self::$columns_names = array(
38
- 'no_filter_selected_1' => array( '&nbsp;', 'none' ),
39
- 'browser' => array( __( 'Browser', 'wp-slimstat' ), 'varchar' ),
40
- 'country' => array( __( 'Country Code', 'wp-slimstat' ), 'varchar' ),
41
- 'ip' => array( __( 'IP Address', 'wp-slimstat' ), 'varchar' ),
42
- 'searchterms' => array( __( 'Search Terms', 'wp-slimstat' ), 'varchar' ),
43
- 'language' => array( __( 'Language Code', 'wp-slimstat' ), 'varchar' ),
44
- 'platform' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
45
- 'resource' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
46
- 'referer' => array( __( 'Referer', 'wp-slimstat' ), 'varchar' ),
47
- 'username' => array( __( 'Visitor\'s Name', 'wp-slimstat' ), 'varchar' ),
48
- 'outbound_resource' => array( __( 'Outbound Link', 'wp-slimstat' ), 'varchar' ),
49
- 'page_performance' => array( __( 'Page Speed', 'wp-slimstat' ), 'int' ),
50
- 'no_filter_selected_2' => array( '&nbsp;', 'none' ),
51
- 'no_filter_selected_3' => array( __( '-- Advanced filters --', 'wp-slimstat' ), 'none' ),
52
- 'plugins' => array( __( 'Browser Capabilities', 'wp-slimstat' ), 'varchar' ),
53
- 'browser_version' => array( __( 'Browser Version', 'wp-slimstat' ), 'varchar' ),
54
- 'browser_type' => array( __( 'Browser Type', 'wp-slimstat' ), 'int' ),
55
- 'user_agent' => array( __( 'User Agent', 'wp-slimstat' ), 'varchar' ),
56
- 'notes' => array( __( 'Annotations', 'wp-slimstat' ), 'varchar' ),
57
- 'server_latency' => array( __( 'Server Latency', 'wp-slimstat' ), 'int' ),
58
- 'author' => array( __( 'Post Author', 'wp-slimstat' ), 'varchar' ),
59
- 'category' => array( __( 'Post Category ID', 'wp-slimstat' ), 'varchar' ),
60
- 'other_ip' => array( __( 'Originating IP', 'wp-slimstat' ), 'varchar' ),
61
- 'content_type' => array( __( 'Resource Content Type', 'wp-slimstat' ), 'varchar' ),
62
- 'content_id' => array( __( 'Resource ID', 'wp-slimstat' ), 'int' ),
63
- 'screen_width' => array( __( 'Screen Width', 'wp-slimstat' ), 'int' ),
64
- 'screen_height' => array( __( 'Screen Height', 'wp-slimstat' ), 'int' ),
65
- 'resolution' => array( __( 'Viewport Size', 'wp-slimstat' ), 'varchar' ),
66
- 'visit_id' => array( __( 'Visit ID', 'wp-slimstat' ), 'int' )
67
- );
68
-
69
- // The following filters will not be displayed in the dropdown
70
- self::$all_columns_names = array_merge( array(
71
-
72
- // Date and Time
73
- 'minute' => array( __( 'Minute', 'wp-slimstat' ), 'int' ),
74
- 'hour' => array( __( 'Hour', 'wp-slimstat' ), 'int' ),
75
- 'day' => array( __( 'Day', 'wp-slimstat' ), 'int' ),
76
- 'month' => array( __( 'Month', 'wp-slimstat' ), 'int' ),
77
- 'year' => array( __( 'Year', 'wp-slimstat' ), 'int' ),
78
- 'interval_direction' => array( __( '+/-', 'wp-slimstat' ), 'int' ),
79
- 'interval' => array( __( 'days', 'wp-slimstat' ), 'int' ),
80
- 'interval_hours' => array( __( 'hours', 'wp-slimstat' ), 'int' ),
81
- 'interval_minutes' => array( __( 'minutes', 'wp-slimstat' ), 'int' ),
82
- 'dt' => array( __( 'Unix Timestamp', 'wp-slimstat' ), 'int' ),
83
-
84
- // Other columns
85
- 'language_calculated' => array( __( 'Language', 'wp-slimstat' ), 'varchar' ),
86
- 'platform_calculated' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
87
- 'resource_calculated' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
88
- 'metric' => array( __( 'Metric', 'wp-slimstat' ), 'varchar' ),
89
- 'value' => array( __( 'Value', 'wp-slimstat' ), 'varchar' ),
90
- 'tooltip' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
91
- 'details' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
92
-
93
- // Events
94
- 'event_id' => array( __( 'Event ID', 'wp-slimstat' ), 'int' ),
95
- 'type' => array( __( 'Type', 'wp-slimstat' ), 'int' ),
96
- 'event_description' => array( __( 'Event Description', 'wp-slimstat' ), 'varchar' ),
97
- 'position' => array( __( 'Event Coordinates', 'wp-slimstat' ), 'int' ),
98
-
99
- 'direction' => array( __( 'Direction', 'wp-slimstat' ), 'varchar' ),
100
- 'limit_results' => array( __( 'Max Results', 'wp-slimstat' ), 'int' ),
101
- 'start_from' => array( __( 'Offset', 'wp-slimstat' ), 'int' ),
102
-
103
- // Misc Filters
104
- 'strtotime' => array( 0, 'int' )
105
- ), self::$columns_names );
106
-
107
- // Allow third party plugins to add even more column names to the array
108
- self::$all_columns_names = apply_filters( 'slimstat_column_names', self::$all_columns_names );
109
-
110
- // Hook for the... filters
111
- $_filters = apply_filters( 'slimstat_db_pre_filters', $_filters );
112
-
113
- // Normalize the input (filters)
114
- self::$filters_normalized = self::parse_filters( $_filters );
115
-
116
- // Hook for the array of normalized filters
117
- self::$filters_normalized = apply_filters( 'slimstat_db_filters_normalized', self::$filters_normalized, $_filters );
118
- }
119
- // end init
120
-
121
- /**
122
- * Builds the array of WHERE clauses to be used later in our SQL queries
123
- */
124
- protected static function _get_sql_where( $_filters_normalized = array(), $_slim_stats_table_alias = '' ) {
125
- $sql_array = array();
126
-
127
- foreach ( $_filters_normalized as $a_filter_column => $a_filter_data ) {
128
- // Add-ons can set their own custom filters, which are ignored here
129
- if ( strpos( $a_filter_column, 'addon_' ) !== false ) {
130
- continue;
131
- }
132
-
133
- $sql_array[] = self::get_single_where_clause( $a_filter_column, $a_filter_data[ 0 ], $a_filter_data[ 1 ], $_slim_stats_table_alias );
134
- }
135
-
136
- // Flatten array
137
- if ( !empty( $sql_array ) ) {
138
- return implode( ' AND ', $sql_array );
139
- }
140
-
141
- return '';
142
- }
143
-
144
- public static function get_combined_where( $_where = '', $_column = '*', $_use_date_filters = true, $_slim_stats_table_alias = '' ) {
145
- $dt_with_alias = 'dt';
146
- if ( !empty( $_slim_stats_table_alias ) ) {
147
- $dt_with_alias = $_slim_stats_table_alias . '.' . $dt_with_alias;
148
- }
149
-
150
- $time_range_condition = '';
151
- if ( empty( $_where ) ) {
152
- if ( !empty( self::$filters_normalized[ 'columns' ] ) ) {
153
- $_where = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
154
-
155
- if ($_use_date_filters) {
156
- $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
157
- }
158
-
159
- }
160
- elseif ( $_use_date_filters ) {
161
- $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
162
- }
163
- else {
164
- $_where = '1=1';
165
- }
166
- }
167
- else {
168
- if ( $_where != '1=1' && !empty( self::$filters_normalized[ 'columns' ] ) ) {
169
- $new_clause = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
170
-
171
- // This condition could be empty if it's related to a custom column
172
- if ( !empty( $new_clause ) ) {
173
- $_where .= ' AND ' . $new_clause;
174
- }
175
- }
176
- if ( $_use_date_filters ) {
177
- $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
178
- }
179
- }
180
-
181
- if ( !empty( $_where ) && !empty( $time_range_condition ) ) {
182
- $_where = "$_where AND $time_range_condition";
183
- }
184
- else {
185
- $_where = trim( "$_where $time_range_condition" );
186
- }
187
-
188
- if ( !empty( $_column ) && !empty( self::$columns_names[ $_column ] ) ) {
189
- $_column = str_replace( '_calculated', '', $_column );
190
- $column_with_alias = $_column;
191
- if ( !empty( $_slim_stats_table_alias ) ) {
192
- $column_with_alias = $_slim_stats_table_alias . '.' . $column_with_alias;
193
- }
194
-
195
- $filter_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0' );
196
- $filter_not_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0' );
197
-
198
- if ( strpos( $_where, $filter_empty ) === false && strpos( $_where, $filter_not_empty) === false) {
199
- $_where = "$filter_not_empty AND $_where";
200
- }
201
- }
202
-
203
- return $_where;
204
- }
205
-
206
- /**
207
- * Translates user-friendly operators into SQL conditions
208
- */
209
- public static function get_single_where_clause( $_column = 'id', $_operator = 'equals', $_value = '', $_slim_stats_table_alias = '' ) {
210
- $filter_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0';
211
- $filter_not_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0';
212
-
213
- $_column = str_replace( '_calculated', '', $_column );
214
-
215
- $column_with_alias = $_column;
216
- if ( !empty( $_slim_stats_table_alias ) ) {
217
- $column_with_alias = $_slim_stats_table_alias . '.' . $_column;
218
- }
219
-
220
- switch( $_column ) {
221
- case 'ip':
222
- case 'other_ip':
223
- $filter_empty = '= "0.0.0.0"';
224
- break;
225
- default:
226
- break;
227
- }
228
-
229
- $where = array( '', $_value );
230
- switch ( $_operator ) {
231
- case 'is_not_equal_to':
232
- $where[0] = "$column_with_alias <> %s";
233
- break;
234
-
235
- case 'contains':
236
- $where = array( "$column_with_alias LIKE %s", '%'.$_value.'%' );
237
- break;
238
-
239
- case 'includes_in_set':
240
- $where[0] = "FIND_IN_SET(%s, $column_with_alias) > 0";
241
- break;
242
-
243
- case 'does_not_contain':
244
- $where = array( "$column_with_alias NOT LIKE %s", '%'.$_value.'%' );
245
- break;
246
-
247
- case 'starts_with':
248
- $where = array( "$column_with_alias LIKE %s", $_value.'%' );
249
- break;
250
-
251
- case 'ends_with':
252
- $where = array( "$column_with_alias LIKE %s", '%'.$_value );
253
- break;
254
-
255
- case 'sounds_like':
256
- $where[0] = "SOUNDEX($column_with_alias) = SOUNDEX(%s)";
257
- break;
258
-
259
- case 'is_empty':
260
- $where = array( "$column_with_alias $filter_empty", '' );
261
- break;
262
-
263
- case 'is_not_empty':
264
- $where = array( "$column_with_alias $filter_not_empty", '' );
265
- break;
266
-
267
- case 'is_greater_than':
268
- $where[0] = "$column_with_alias > %d";
269
- break;
270
-
271
- case 'is_less_than':
272
- $where[0] = "$column_with_alias < %d";
273
- break;
274
-
275
- case 'between':
276
- $range = explode(',', $_value);
277
- $where = array( "$column_with_alias BETWEEN %d AND %d", array( $range[0], $range[1] ) );
278
- break;
279
-
280
- case 'matches':
281
- $where[0] = "$column_with_alias REGEXP %s";
282
- break;
283
-
284
- case 'does_not_match':
285
- $where[0] = "$column_with_alias NOT REGEXP %s";
286
- break;
287
-
288
- default:
289
- $where[0] = "$column_with_alias = %s";
290
- break;
291
- }
292
-
293
- if ( !empty( $where[ 1 ] ) ) {
294
- return $GLOBALS[ 'wpdb' ]->prepare( $where[ 0 ], $where[ 1 ] );
295
- }
296
- else {
297
- return $where[ 0 ];
298
- }
299
- }
300
-
301
- public static function get_results( $_sql = '', $_select_no_aggregate_values = '', $_order_by = '', $_group_by = '', $_aggregate_values_add = '' ) {
302
- $_sql = apply_filters( 'slimstat_get_results_sql', $_sql, $_select_no_aggregate_values, $_order_by, $_group_by, $_aggregate_values_add );
303
-
304
- if ( wp_slimstat::$options[ 'show_sql_debug' ] == 'yes' ) {
305
- self::$debug_message .= "<p class='debug'>$_sql</p>";
306
- }
307
-
308
- return wp_slimstat::$wpdb->get_results( $_sql, ARRAY_A );
309
- }
310
-
311
- public static function get_var( $_sql = '', $_aggregate_value = '' ) {
312
- $_sql = apply_filters( 'slimstat_get_var_sql', $_sql, $_aggregate_value );
313
-
314
- if ( wp_slimstat::$options[ 'show_sql_debug' ] == 'yes' ) {
315
- self::$debug_message .= "<p class='debug'>$_sql</p>";
316
- }
317
-
318
- return wp_slimstat::$wpdb->get_var( $_sql );
319
- }
320
-
321
- public static function parse_filters( $_filters = '', $_init_misc = true ) {
322
- $filters_normalized = array(
323
- 'columns' => array(),
324
- 'date' => array(
325
- 'interval_direction' => '',
326
- 'is_past' => false
327
- ),
328
- 'misc' => $_init_misc?array(
329
- 'direction' => 'DESC',
330
- 'limit_results' => wp_slimstat::$options[ 'limit_results' ],
331
- 'start_from' => 0
332
- ) : array(),
333
- 'utime' => array(
334
- 'start' => 0,
335
- 'end' => 0,
336
- 'type' => 'm'
337
- )
338
- );
339
-
340
- if ( !empty( $_filters ) ) {
341
- $matches = explode( '&&&', $_filters );
342
- foreach( $matches as $idx => $a_match ) {
343
- preg_match( '/([^\s]+)\s([^\s]+)\s(.+)?/', urldecode( $a_match ), $a_filter );
344
-
345
- if ( empty( $a_filter ) || ( ( !array_key_exists( $a_filter[ 1 ], self::$all_columns_names ) || strpos( $a_filter[ 1 ], 'no_filter' ) !== false ) && strpos( $a_filter[ 1 ], 'addon_' ) === false ) ) {
346
- continue;
347
- }
348
-
349
- switch( $a_filter[ 1 ] ) {
350
- case 'strtotime':
351
- $custom_date = strtotime( $a_filter[ 3 ], date_i18n( 'U' ) );
352
-
353
- $filters_normalized[ 'date' ][ 'minute' ] = intval( date( 'i', $custom_date ) );
354
- $filters_normalized[ 'date' ][ 'hour' ] = intval( date( 'H', $custom_date ) );
355
- $filters_normalized[ 'date' ][ 'day' ] = intval( date( 'j', $custom_date ) );
356
- $filters_normalized[ 'date' ][ 'month' ] = intval( date( 'n', $custom_date ) );
357
- $filters_normalized[ 'date' ][ 'year' ] = intval( date( 'Y', $custom_date ) );
358
- break;
359
-
360
- case 'minute':
361
- case 'hour':
362
- case 'day':
363
- case 'month':
364
- case 'year':
365
- if ( is_numeric( $a_filter[ 3 ] ) ) {
366
- $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = intval( $a_filter[ 3 ] );
367
- }
368
- else{
369
- // Try to apply strtotime to value
370
- switch( $a_filter[ 1 ] ) {
371
- case 'minute':
372
- $filters_normalized[ 'date' ][ 'minute' ] = intval( date( 'i', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
373
- $filters_normalized[ 'date' ][ 'is_past' ] = true;
374
- break;
375
-
376
- case 'hour':
377
- $filters_normalized[ 'date' ][ 'hour' ] = intval( date( 'H', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
378
- $filters_normalized[ 'date' ][ 'is_past' ] = true;
379
- break;
380
-
381
- case 'day':
382
- $filters_normalized[ 'date' ][ 'day' ] = intval( date( 'j', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
383
- break;
384
-
385
- case 'month':
386
- $filters_normalized[ 'date' ][ 'month' ] = intval( date( 'n', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
387
- break;
388
-
389
- case 'year':
390
- $filters_normalized[ 'date' ][ 'year' ] = intval( date( 'Y', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
391
- break;
392
-
393
- default:
394
- break;
395
- }
396
-
397
- if ( $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] === false ) {
398
- unset( $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] );
399
- }
400
- }
401
-
402
- switch( $a_filter[ 1 ] ) {
403
- case 'day':
404
- if ( $filters_normalized[ 'date' ][ 'day' ] != date_i18n( 'j' ) ) {
405
- $filters_normalized[ 'date' ][ 'is_past' ] = true;
406
- }
407
- break;
408
-
409
- case 'month':
410
- if ( $filters_normalized[ 'date' ][ 'month' ] != date_i18n( 'n' ) ) {
411
- $filters_normalized[ 'date' ][ 'is_past' ] = true;
412
- }
413
- break;
414
-
415
- case 'year':
416
- if ( $filters_normalized[ 'date' ][ 'year' ] != date_i18n( 'Y' ) ) {
417
- $filters_normalized[ 'date' ][ 'is_past' ] = true;
418
- }
419
- break;
420
-
421
- default:
422
- break;
423
- }
424
- break;
425
-
426
- case 'interval':
427
- case 'interval_hours':
428
- case 'interval_minutes':
429
- $intval_filter = intval( $a_filter[ 3 ] );
430
- $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = abs( $intval_filter );
431
- if ( $intval_filter < 0 ) {
432
- $filters_normalized[ 'date' ][ 'interval_direction' ] = 'minus';
433
- }
434
- break;
435
-
436
- case 'interval_direction':
437
- $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = in_array( $a_filter[ 3 ], array( 'plus', 'minus' ) ) ? $a_filter[ 3 ] : 'plus';
438
- break;
439
-
440
- case 'direction':
441
- case 'limit_results':
442
- case 'start_from':
443
- $filters_normalized[ 'misc' ][ $a_filter[ 1 ] ] = str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) );
444
- break;
445
-
446
- default:
447
- $filters_normalized[ 'columns' ][ $a_filter[ 1 ] ] = array( $a_filter[ 2 ], isset( $a_filter[ 3 ] ) ? str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) ) : '' );
448
- break;
449
- }
450
- }
451
- }
452
-
453
- // Temporarily disable any filters on date_i18n
454
- $date_i18n_filters = array();
455
- if ( !empty( $GLOBALS[ 'wp_filter' ][ 'date_i18n' ] ) ) {
456
- $date_i18n_filters = $GLOBALS[ 'wp_filter' ][ 'date_i18n' ];
457
- remove_all_filters( 'date_i18n' );
458
- }
459
-
460
- // Let's calculate our time range, based on date filters
461
- if ( empty( $filters_normalized[ 'date' ][ 'interval' ] ) && empty( $filters_normalized[ 'date' ][ 'interval_hours' ] ) && empty( $filters_normalized[ 'date' ][ 'interval_minutes' ] ) ) {
462
- if ( !empty( $filters_normalized[ 'date' ][ 'minute' ] ) ) {
463
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
464
- !empty( $filters_normalized[ 'date' ][ 'hour' ] )?$filters_normalized[ 'date' ][ 'hour' ]:0,
465
- $filters_normalized[ 'date' ][ 'minute' ],
466
- 0,
467
- !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
468
- !empty( $filters_normalized[ 'date' ][ 'day' ] )?$filters_normalized[ 'date' ][ 'day' ]:date_i18n( 'j' ),
469
- !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
470
- );
471
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 60;
472
- $filters_normalized[ 'utime' ][ 'type' ] = 'H';
473
- }
474
- else if ( !empty( $filters_normalized[ 'date' ][ 'hour' ] ) ) {
475
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
476
- $filters_normalized[ 'date' ][ 'hour' ],
477
- 0,
478
- 0,
479
- !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
480
- !empty( $filters_normalized[ 'date' ][ 'day' ] )?$filters_normalized[ 'date' ][ 'day' ]:date_i18n( 'j' ),
481
- !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
482
- );
483
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 3599;
484
- $filters_normalized[ 'utime' ][ 'type' ] = 'H';
485
- }
486
- else if ( !empty( $filters_normalized[ 'date' ][ 'day' ] ) ) {
487
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
488
- 0,
489
- 0,
490
- 0,
491
- !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
492
- $filters_normalized[ 'date' ][ 'day' ],
493
- !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
494
- );
495
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 86399;
496
- $filters_normalized[ 'utime' ][ 'type' ] = 'd';
497
- }
498
- else if( !empty( $filters_normalized[ 'date' ][ 'year' ] ) && empty( $filters_normalized[ 'date' ][ 'month' ] ) ) {
499
- $filters_normalized[ 'utime' ][ 'start' ] = mktime( 0, 0, 0, 1, 1, $filters_normalized[ 'date' ][ 'year' ] );
500
- $filters_normalized[ 'utime' ][ 'end' ] = mktime( 0, 0, 0, 1, 1, $filters_normalized[ 'date' ][ 'year' ]+1 )-1;
501
- $filters_normalized[ 'utime' ][ 'type' ] = 'Y';
502
- }
503
- else {
504
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
505
- 0,
506
- 0,
507
- 0,
508
- !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
509
- 1,
510
- !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
511
- );
512
-
513
- $filters_normalized[ 'utime' ][ 'end' ] = strtotime(
514
- ( !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' ) ).'-'.
515
- ( !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ) ).
516
- '-01 00:00 +1 month UTC'
517
- )-1;
518
- $filters_normalized[ 'utime' ][ 'type' ] = 'm';
519
- }
520
- }
521
- else { // An interval was specified
522
- $filters_normalized[ 'utime' ][ 'type' ] = 'interval';
523
-
524
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
525
- !empty( $filters_normalized[ 'date' ][ 'hour' ] )?$filters_normalized[ 'date' ][ 'hour' ]:0,
526
- !empty( $filters_normalized[ 'date' ][ 'minute' ] )?$filters_normalized[ 'date' ][ 'minute' ]:0,
527
- 0,
528
- !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
529
- !empty( $filters_normalized[ 'date' ][ 'day' ] )?$filters_normalized[ 'date' ][ 'day' ]:date_i18n( 'j' ),
530
- !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
531
- );
532
-
533
- $sign = ( $filters_normalized[ 'date' ][ 'interval_direction' ] == 'plus' )?'+':'-';
534
-
535
- $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + intval( $sign.(
536
- ( !empty( $filters_normalized[ 'date' ][ 'interval' ] )?intval( $filters_normalized[ 'date' ][ 'interval' ] + 1):0 ) * 86400 +
537
- ( !empty( $filters_normalized[ 'date' ][ 'interval_hours' ] )?intval( $filters_normalized[ 'date' ][ 'interval_hours' ] ):0 ) * 3600 +
538
- ( !empty( $filters_normalized[ 'date' ][ 'interval_minutes' ] )?intval( $filters_normalized[ 'date' ][ 'interval_minutes' ] ):0 ) * 60
539
- ) ) - 1;
540
-
541
- // Swap boundaries if we're going back in time
542
- if ( $filters_normalized[ 'date' ][ 'interval_direction' ] == 'minus' ) {
543
- list( $filters_normalized[ 'utime' ][ 'start' ], $filters_normalized[ 'utime' ][ 'end' ] ) = array( $filters_normalized[ 'utime' ][ 'end' ] + 1, $filters_normalized[ 'utime' ][ 'start' ] - 1 );
544
- }
545
- }
546
-
547
- // If end is in the future, set it to now
548
- if ( $filters_normalized[ 'utime' ][ 'end' ] > date_i18n( 'U' ) ) {
549
- $filters_normalized[ 'utime' ][ 'end' ] = date_i18n( 'U' );
550
- }
551
-
552
- // If start is after end, set it to first of month
553
- if ( $filters_normalized[ 'utime' ][ 'start' ] > $filters_normalized[ 'utime' ][ 'end' ] ) {
554
- $filters_normalized[ 'utime' ][ 'start' ] = mktime(
555
- 0,
556
- 0,
557
- 0,
558
- date_i18n( 'n', $filters_normalized[ 'utime' ][ 'end' ] ),
559
- 1,
560
- date_i18n( 'Y', $filters_normalized[ 'utime' ][ 'end' ] )
561
- );
562
- $filters_normalized[ 'date' ][ 'hour' ] = $filters_normalized[ 'date' ][ 'day' ] = $filters_normalized[ 'date' ][ 'month' ] = $filters_normalized[ 'date' ][ 'year' ] = 0;
563
- }
564
-
565
- // Restore filters on date_i18n
566
- foreach ($date_i18n_filters as $i18n_priority => $i18n_func_list) {
567
- foreach ($i18n_func_list as $func_name => $func_args) {
568
- add_filter('date_i8n', $func_args[ 'function' ], $i18n_priority, $func_args[ 'accepted_args' ]);
569
- }
570
- }
571
-
572
- return $filters_normalized;
573
- }
574
-
575
- // The following methods retrieve the information from the database
576
-
577
- public static function count_bouncing_pages() {
578
- $where = self::get_combined_where( 'visit_id > 0 AND content_type <> "404"', 'resource' );
579
-
580
- return intval( self::get_var( "
581
- SELECT COUNT(*) counthits
582
- FROM (
583
- SELECT resource, visit_id
584
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
585
- WHERE $where
586
- GROUP BY resource
587
- HAVING COUNT(visit_id) = 1
588
- ) as ts1",
589
- 'SUM(counthits) AS counthits' ) );
590
- }
591
-
592
- public static function count_exit_pages() {
593
- $where = self::get_combined_where( 'visit_id > 0', 'resource' );
594
-
595
- return intval( self::get_var( "
596
- SELECT COUNT(*) counthits
597
- FROM (
598
- SELECT resource, dt
599
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
600
- WHERE $where
601
- GROUP BY resource
602
- HAVING dt = MAX(dt)
603
- ) AS ts1",
604
- 'SUM(counthits) AS counthits' ) );
605
- }
606
-
607
- public static function count_records( $_column = 'id', $_where = '', $_use_date_filters = true ) {
608
- $distinct_column = ( $_column != 'id' ) ? "DISTINCT $_column" : $_column;
609
- $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
610
-
611
- return intval( self::get_var( "
612
- SELECT COUNT($distinct_column) counthits
613
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
614
- WHERE $_where",
615
- 'SUM(counthits) AS counthits' ) );
616
- }
617
-
618
- public static function count_records_having( $_column = 'id', $_where = '', $_having = '' ) {
619
- $_where = self::get_combined_where( $_where, $_column );
620
-
621
- return intval( self::get_var( "
622
- SELECT COUNT(*) counthits FROM (
623
- SELECT $_column
624
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
625
- WHERE $_where
626
- GROUP BY $_column
627
- HAVING $_having
628
- ) AS ts1",
629
- 'SUM(counthits) AS counthits' ) );
630
- }
631
-
632
- public static function get_data_for_chart( $_data1 = '', $_data2 = '', $_where = '' ) {
633
- $previous = array( 'end' => self::$filters_normalized[ 'utime' ][ 'start' ] - 1 );
634
- $label_date_format = '';
635
- $output = array();
636
-
637
- // Each type has its own parameters
638
- switch (self::$filters_normalized[ 'utime' ][ 'type' ]) {
639
- case 'H':
640
- $previous[ 'start' ] = self::$filters_normalized[ 'utime' ][ 'start' ] - 3600;
641
- $label_date_format = wp_slimstat::$options[ 'time_format' ];
642
- $group_by = array( 'HOUR', 'MINUTE', 'i' );
643
- $values_in_interval = array( 59, 59, 0, 60 );
644
- break;
645
-
646
- case 'd':
647
- $previous[ 'start' ] = self::$filters_normalized[ 'utime' ][ 'start' ] - 86400;
648
- $label_date_format = ( self::$formats[ 'decimal' ] == '.' ) ? 'm/d' : 'd/m';
649
- $group_by = array( 'DAY', 'HOUR', 'G' );
650
- $values_in_interval = array( 23, 23, 0, 3600 );
651
- break;
652
-
653
- case 'Y':
654
- $previous[ 'start' ] = mktime( 0, 0, 0, 1, 1, self::$filters_normalized[ 'date' ][ 'year' ] - 1 );
655
- $label_date_format = 'Y';
656
- $group_by = array( 'YEAR', 'MONTH', 'n' );
657
- $values_in_interval = array( 12, 12, 1, 2678400 );
658
- break;
659
-
660
- case 'interval':
661
- $group_by = array( 'MONTH', 'DAY', 'j' );
662
- $values_in_interval = array( abs( self::$filters_normalized[ 'date' ][ 'interval' ] ), abs( self::$filters_normalized[ 'date' ][ 'interval' ] ), 0, 86400 );
663
- break;
664
-
665
- default:
666
- $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' ) );
667
- $label_date_format = 'm/Y';
668
- $group_by = array( 'MONTH', 'DAY', 'j' );
669
- $values_in_interval = array( date( 't', $previous[ 'start' ] ), date( 't', self::$filters_normalized[ 'utime' ][ 'start' ] ), 1, 86400 );
670
- break;
671
- }
672
-
673
- // Custom intervals don't have a comparison chart ('previous' range)
674
- if ( empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) ) {
675
- $_where = self::get_combined_where( $_where, '*', false );
676
- $previous_time_range = ' AND (dt BETWEEN '.$previous[ 'start' ].' AND '.$previous[ 'end' ].' OR dt BETWEEN '.self::$filters_normalized[ 'utime' ][ 'start' ].' AND '.self::$filters_normalized[ 'utime' ][ 'end' ].')';
677
- }
678
- else {
679
- $_where = self::get_combined_where( $_where );
680
- $previous_time_range = '';
681
- }
682
-
683
- // Build the SQL query
684
- $group_by_string = "GROUP BY {$group_by[0]}(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00')), {$group_by[1]}(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00'))";
685
- $sql = "
686
- SELECT dt, $_data1 first_metric, $_data2 second_metric
687
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
688
- WHERE $_where $previous_time_range
689
- $group_by_string";
690
-
691
- // Get the data
692
- $results = self::get_results( $sql, 'blog_id', '', $group_by_string, 'SUM(first_metric) AS first_metric, SUM(second_metric) AS second_metric' );
693
-
694
- // Fill the output array
695
- $output[ 'current' ][ 'label' ] = '';
696
- if ( !empty( $label_date_format ) ) {
697
- $output[ 'current' ][ 'label' ] = gmdate( $label_date_format, self::$filters_normalized[ 'utime' ][ 'start' ] );
698
- $output[ 'previous' ][ 'label' ] = gmdate( $label_date_format, $previous[ 'start' ] );
699
- }
700
-
701
- $output[ 'previous' ][ 'first_metric' ] = array_fill( $values_in_interval[ 2 ], $values_in_interval[ 0 ], 0 );
702
- $output['previous']['second_metric'] = array_fill( $values_in_interval[ 2 ], $values_in_interval[ 0 ], 0 );
703
-
704
- $today_limit = floatval( date_i18n( 'Ymd.Hi' ) );
705
- for ( $i = $values_in_interval[ 2 ]; $i <= $values_in_interval[ 1 ]; $i++ ){
706
- // Do not include dates in the future
707
- if ( floatval( date( 'Ymd.Hi', wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] + ( ( $i - $values_in_interval[ 2 ]) * $values_in_interval[ 3 ] ) ) ) > $today_limit ) {
708
- continue;
709
- }
710
-
711
- $output[ 'current' ][ 'first_metric' ][ $i ] = 0;
712
- $output[ 'current' ][ 'second_metric' ][ $i ] = 0;
713
- }
714
-
715
- // No data? No problem!
716
- if ( !is_array( $results ) || empty( $results ) ) {
717
- return $output;
718
- }
719
-
720
- // Rearrange the data and then format it for Flot
721
- foreach ($results as $i => $a_result ) {
722
- $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' ] );
723
-
724
- if ( empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) && gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $a_result[ 'dt' ] ) == gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $previous[ 'start' ] ) ){
725
- $output[ 'previous' ][ 'first_metric' ][ $index ] = $a_result[ 'first_metric' ];
726
- $output[ 'previous' ][ 'second_metric' ][ $index ] = $a_result[ 'second_metric' ];
727
- }
728
- if ( !empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) || gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $a_result[ 'dt' ] ) == gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], self::$filters_normalized[ 'utime' ][ 'start' ] ) ){
729
- $output[ 'current' ][ 'first_metric' ][ $index ] = $a_result[ 'first_metric' ];
730
- $output[ 'current' ][ 'second_metric' ][ $index ] = $a_result[ 'second_metric' ];
731
- }
732
- }
733
-
734
- return $output;
735
- }
736
-
737
- public static function get_data_size() {
738
- $suffix = 'KB';
739
-
740
- $sql = 'SHOW TABLE STATUS LIKE "'.$GLOBALS[ 'wpdb' ]->prefix.'slim_stats"';
741
- $table_details = wp_slimstat::$wpdb->get_row( $sql, 'ARRAY_A', 0 );
742
-
743
- $table_size = ( $table_details[ 'Data_length' ] / 1024 ) + ( $table_details[ 'Index_length' ] / 1024 );
744
-
745
- if ( $table_size > 1024 ) {
746
- $table_size /= 1024;
747
- $suffix = 'MB';
748
- }
749
- return number_format( $table_size, 2, self::$formats[ 'decimal' ], self::$formats[ 'thousand' ] ).' '.$suffix;
750
- }
751
-
752
- public static function get_max_and_average_pages_per_visit() {
753
- $where = self::get_combined_where( 'visit_id > 0' );
754
-
755
- return self::get_results( "
756
- SELECT AVG(ts1.counthits) AS avghits, MAX(ts1.counthits) AS maxhits FROM (
757
- SELECT count(ip) counthits, visit_id
758
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
759
- WHERE $where
760
- GROUP BY visit_id
761
- ) AS ts1",
762
- 'blog_id',
763
- '',
764
- '',
765
- 'AVG(avghits) AS avghits, MAX(maxhits) AS maxhits' );
766
- }
767
-
768
- public static function get_oldest_visit() {
769
- return self::get_var( "
770
- SELECT dt
771
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
772
- ORDER BY dt ASC
773
- LIMIT 0, 1",
774
- 'MIN(dt)' );
775
- }
776
-
777
- public static function get_recent( $_column = '*', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '' ) {
778
- // This function can be passed individual arguments, or an array of arguments
779
- if ( is_array( $_column ) ) {
780
- $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
781
- $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
782
- $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
783
- $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
784
- $_column = $_column[ 'columns' ];
785
- }
786
-
787
- if ( !empty( $_as_column ) ) {
788
- $_column = "$_column AS $_as_column";
789
- }
790
- else {
791
- $_as_column = $_column;
792
- }
793
-
794
- $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
795
-
796
- if ( $_column == 'id' || $_column == '*' ) {
797
- return self::get_results( "
798
- SELECT *
799
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
800
- WHERE $_where
801
- ORDER BY dt DESC
802
- LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
803
- $_column,
804
- 'dt DESC' );
805
- }
806
- else {
807
- return self::get_results( "
808
- SELECT t1.*
809
- FROM (
810
- SELECT $_column, MAX(id) maxid
811
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
812
- WHERE $_where
813
- GROUP BY $_as_column $_having
814
- ) AS ts1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.maxid = t1.id
815
- ORDER BY t1.dt DESC
816
- LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
817
- ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ).', blog_id',
818
- 't1.dt DESC' );
819
- }
820
- }
821
-
822
- public static function get_recent_events() {
823
- if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
824
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
825
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
826
- }
827
- else {
828
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
829
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
830
- }
831
-
832
- return self::get_results( "
833
- SELECT *
834
- FROM $from
835
- WHERE $where
836
- ORDER BY te.dt DESC"
837
- );
838
- }
839
-
840
- public static function get_top( $_column = 'id', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '' ){
841
- // This function can be passed individual arguments, or an array of arguments
842
- if ( is_array( $_column ) ) {
843
- $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
844
- $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
845
- $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
846
- $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
847
- $_column = $_column[ 'columns' ];
848
- }
849
-
850
- if ( !empty( $_as_column ) ) {
851
- $_column = "$_column AS $_as_column";
852
- }
853
- else {
854
- $_as_column = $_column;
855
- }
856
-
857
- $_where = self::get_combined_where( $_where, $_as_column, $_use_date_filters );
858
-
859
- return self::get_results( "
860
- SELECT $_column, COUNT(*) counthits
861
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
862
- WHERE $_where
863
- GROUP BY $_as_column $_having
864
- ORDER BY counthits DESC
865
- LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
866
- ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ).', blog_id',
867
- 'counthits DESC',
868
- $_column,
869
- 'SUM(counthits) AS counthits' );
870
- }
871
-
872
- public static function get_top_aggr( $_column = 'id', $_where = '', $_outer_select_column = '', $_aggr_function = 'MAX' ) {
873
- if ( is_array( $_column ) ) {
874
- $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
875
- $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
876
- $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
877
- $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
878
- $_outer_select_column = !empty( $_column[ 'outer_select_column' ] ) ? $_column[ 'outer_select_column' ] : '';
879
- $_aggr_function = !empty( $_column[ 'aggr_function' ] ) ? $_column[ 'aggr_function' ] : '';
880
- $_column = $_column[ 'columns' ];
881
- }
882
-
883
- if ( !empty( $_as_column ) ) {
884
- $_column = "$_column AS $_as_column";
885
- }
886
- else {
887
- $_as_column = $_column;
888
- }
889
-
890
- $_where = self::get_combined_where( $_where, $_column );
891
-
892
- return self::get_results( "
893
- SELECT $_outer_select_column, ts1.aggrid as $_column, COUNT(*) counthits
894
- FROM (
895
- SELECT $_column, $_aggr_function(id) aggrid
896
- FROM {$GLOBALS['wpdb']->prefix}slim_stats
897
- WHERE $_where
898
- GROUP BY $_column
899
- ) AS ts1 JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.aggrid = t1.id
900
- GROUP BY $_outer_select_column
901
- ORDER BY counthits DESC
902
- LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
903
- $_outer_select_column,
904
- 'counthits DESC',
905
- $_outer_select_column,
906
- "$_aggr_function(aggrid), SUM(counthits)" );
907
- }
908
-
909
- public static function get_top_events() {
910
- if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
911
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
912
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
913
- }
914
- else {
915
- $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
916
- $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
917
- }
918
-
919
- return self::get_results( "
920
- SELECT te.notes, te.type, COUNT(*) counthits
921
- FROM $from
922
- WHERE $where
923
- GROUP BY te.notes, te.type
924
- ORDER BY counthits DESC"
925
- );
926
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
927
  }
1
+ <?php
2
+
3
+ // Let's define the main class with all the methods that we need
4
+ class wp_slimstat_db {
5
+ // Filters
6
+ public static $columns_names = array();
7
+ public static $filters_normalized = array();
8
+
9
+ // Number and date formats
10
+ public static $formats = array( 'decimal' => ',', 'thousand' => '.' );
11
+
12
+ // Structure that maps filters to SQL information (table names, clauses, lookup tables, etc)
13
+ public static $sql_where = array( 'columns' => '', 'time_range' => '' );
14
+
15
+ // Filters that are not visible in the dropdown
16
+ public static $all_columns_names = array();
17
+
18
+ // Debug message
19
+ public static $debug_message = '';
20
+
21
+ /*
22
+ * Sets the filters and other structures needed to store the data retrieved from the DB
23
+ */
24
+ public static function init( $_filters = '' ){
25
+ // Decimal and thousand separators
26
+ if ( wp_slimstat::$options[ 'use_european_separators' ] == 'no' ){
27
+ self::$formats[ 'decimal' ] = '.';
28
+ self::$formats[ 'thousand' ] = ',';
29
+ }
30
+
31
+ // Filters are defined as: browser equals Chrome|country starts_with en
32
+ if ( !is_string( $_filters ) || empty( $_filters ) ){
33
+ $_filters = '';
34
+ }
35
+
36
+ // List of supported filters and their friendly names
37
+ self::$columns_names = array(
38
+ 'no_filter_selected_1' => array( '&nbsp;', 'none' ),
39
+ 'browser' => array( __( 'Browser', 'wp-slimstat' ), 'varchar' ),
40
+ 'country' => array( __( 'Country Code', 'wp-slimstat' ), 'varchar' ),
41
+ 'ip' => array( __( 'IP Address', 'wp-slimstat' ), 'varchar' ),
42
+ 'searchterms' => array( __( 'Search Terms', 'wp-slimstat' ), 'varchar' ),
43
+ 'language' => array( __( 'Language Code', 'wp-slimstat' ), 'varchar' ),
44
+ 'platform' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
45
+ 'resource' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
46
+ 'referer' => array( __( 'Referer', 'wp-slimstat' ), 'varchar' ),
47
+ 'username' => array( __( 'Visitor\'s Name', 'wp-slimstat' ), 'varchar' ),
48
+ 'outbound_resource' => array( __( 'Outbound Link', 'wp-slimstat' ), 'varchar' ),
49
+ 'page_performance' => array( __( 'Page Speed', 'wp-slimstat' ), 'int' ),
50
+ 'no_filter_selected_2' => array( '&nbsp;', 'none' ),
51
+ 'no_filter_selected_3' => array( __( '-- Advanced filters --', 'wp-slimstat' ), 'none' ),
52
+ 'plugins' => array( __( 'Browser Capabilities', 'wp-slimstat' ), 'varchar' ),
53
+ 'browser_version' => array( __( 'Browser Version', 'wp-slimstat' ), 'varchar' ),
54
+ 'browser_type' => array( __( 'Browser Type', 'wp-slimstat' ), 'int' ),
55
+ 'user_agent' => array( __( 'User Agent', 'wp-slimstat' ), 'varchar' ),
56
+ 'notes' => array( __( 'Annotations', 'wp-slimstat' ), 'varchar' ),
57
+ 'server_latency' => array( __( 'Server Latency', 'wp-slimstat' ), 'int' ),
58
+ 'author' => array( __( 'Post Author', 'wp-slimstat' ), 'varchar' ),
59
+ 'category' => array( __( 'Post Category ID', 'wp-slimstat' ), 'varchar' ),
60
+ 'other_ip' => array( __( 'Originating IP', 'wp-slimstat' ), 'varchar' ),
61
+ 'content_type' => array( __( 'Resource Content Type', 'wp-slimstat' ), 'varchar' ),
62
+ 'content_id' => array( __( 'Resource ID', 'wp-slimstat' ), 'int' ),
63
+ 'screen_width' => array( __( 'Screen Width', 'wp-slimstat' ), 'int' ),
64
+ 'screen_height' => array( __( 'Screen Height', 'wp-slimstat' ), 'int' ),
65
+ 'resolution' => array( __( 'Viewport Size', 'wp-slimstat' ), 'varchar' ),
66
+ 'visit_id' => array( __( 'Visit ID', 'wp-slimstat' ), 'int' )
67
+ );
68
+
69
+ // The following filters will not be displayed in the dropdown
70
+ self::$all_columns_names = array_merge( array(
71
+
72
+ // Date and Time
73
+ 'minute' => array( __( 'Minute', 'wp-slimstat' ), 'int' ),
74
+ 'hour' => array( __( 'Hour', 'wp-slimstat' ), 'int' ),
75
+ 'day' => array( __( 'Day', 'wp-slimstat' ), 'int' ),
76
+ 'month' => array( __( 'Month', 'wp-slimstat' ), 'int' ),
77
+ 'year' => array( __( 'Year', 'wp-slimstat' ), 'int' ),
78
+ 'interval_direction' => array( __( '+/-', 'wp-slimstat' ), 'int' ),
79
+ 'interval' => array( __( 'days', 'wp-slimstat' ), 'int' ),
80
+ 'interval_hours' => array( __( 'hours', 'wp-slimstat' ), 'int' ),
81
+ 'interval_minutes' => array( __( 'minutes', 'wp-slimstat' ), 'int' ),
82
+ 'dt' => array( __( 'Unix Timestamp', 'wp-slimstat' ), 'int' ),
83
+
84
+ // Other columns
85
+ 'language_calculated' => array( __( 'Language', 'wp-slimstat' ), 'varchar' ),
86
+ 'platform_calculated' => array( __( 'Operating System', 'wp-slimstat' ), 'varchar' ),
87
+ 'resource_calculated' => array( __( 'Permalink', 'wp-slimstat' ), 'varchar' ),
88
+ 'metric' => array( __( 'Metric', 'wp-slimstat' ), 'varchar' ),
89
+ 'value' => array( __( 'Value', 'wp-slimstat' ), 'varchar' ),
90
+ 'tooltip' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
91
+ 'details' => array( __( 'Notes', 'wp-slimstat' ), 'varchar' ),
92
+
93
+ // Events
94
+ 'event_id' => array( __( 'Event ID', 'wp-slimstat' ), 'int' ),
95
+ 'type' => array( __( 'Type', 'wp-slimstat' ), 'int' ),
96
+ 'event_description' => array( __( 'Event Description', 'wp-slimstat' ), 'varchar' ),
97
+ 'position' => array( __( 'Event Coordinates', 'wp-slimstat' ), 'int' ),
98
+
99
+ 'direction' => array( __( 'Direction', 'wp-slimstat' ), 'varchar' ),
100
+ 'limit_results' => array( __( 'Max Results', 'wp-slimstat' ), 'int' ),
101
+ 'start_from' => array( __( 'Offset', 'wp-slimstat' ), 'int' ),
102
+
103
+ // Misc Filters
104
+ 'strtotime' => array( 0, 'int' )
105
+ ), self::$columns_names );
106
+
107
+ // Allow third party plugins to add even more column names to the array
108
+ self::$all_columns_names = apply_filters( 'slimstat_column_names', self::$all_columns_names );
109
+
110
+ // Hook for the... filters
111
+ $_filters = apply_filters( 'slimstat_db_pre_filters', $_filters );
112
+
113
+ // Normalize the input (filters)
114
+ self::$filters_normalized = self::parse_filters( $_filters );
115
+
116
+ // Hook for the array of normalized filters
117
+ self::$filters_normalized = apply_filters( 'slimstat_db_filters_normalized', self::$filters_normalized, $_filters );
118
+ }
119
+ // end init
120
+
121
+ /**
122
+ * Builds the array of WHERE clauses to be used later in our SQL queries
123
+ */
124
+ protected static function _get_sql_where( $_filters_normalized = array(), $_slim_stats_table_alias = '' ) {
125
+ $sql_array = array();
126
+
127
+ foreach ( $_filters_normalized as $a_filter_column => $a_filter_data ) {
128
+ // Add-ons can set their own custom filters, which are ignored here
129
+ if ( strpos( $a_filter_column, 'addon_' ) !== false ) {
130
+ continue;
131
+ }
132
+
133
+ $sql_array[] = self::get_single_where_clause( $a_filter_column, $a_filter_data[ 0 ], $a_filter_data[ 1 ], $_slim_stats_table_alias );
134
+ }
135
+
136
+ // Flatten array
137
+ if ( !empty( $sql_array ) ) {
138
+ return implode( ' AND ', $sql_array );
139
+ }
140
+
141
+ return '';
142
+ }
143
+
144
+ public static function get_combined_where( $_where = '', $_column = '*', $_use_date_filters = true, $_slim_stats_table_alias = '' ) {
145
+ $dt_with_alias = 'dt';
146
+ if ( !empty( $_slim_stats_table_alias ) ) {
147
+ $dt_with_alias = $_slim_stats_table_alias . '.' . $dt_with_alias;
148
+ }
149
+
150
+ $time_range_condition = '';
151
+ if ( empty( $_where ) ) {
152
+ if ( !empty( self::$filters_normalized[ 'columns' ] ) ) {
153
+ $_where = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
154
+
155
+ if ($_use_date_filters) {
156
+ $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
157
+ }
158
+
159
+ }
160
+ elseif ( $_use_date_filters ) {
161
+ $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
162
+ }
163
+ else {
164
+ $_where = '1=1';
165
+ }
166
+ }
167
+ else {
168
+ if ( $_where != '1=1' && !empty( self::$filters_normalized[ 'columns' ] ) ) {
169
+ $new_clause = self::_get_sql_where( self::$filters_normalized[ 'columns' ], $_slim_stats_table_alias );
170
+
171
+ // This condition could be empty if it's related to a custom column
172
+ if ( !empty( $new_clause ) ) {
173
+ $_where .= ' AND ' . $new_clause;
174
+ }
175
+ }
176
+ if ( $_use_date_filters ) {
177
+ $time_range_condition = "$dt_with_alias BETWEEN " . self::$filters_normalized[ 'utime' ][ 'start' ] . ' AND ' . self::$filters_normalized[ 'utime' ][ 'end' ];
178
+ }
179
+ }
180
+
181
+ if ( !empty( $_where ) && !empty( $time_range_condition ) ) {
182
+ $_where = "$_where AND $time_range_condition";
183
+ }
184
+ else {
185
+ $_where = trim( "$_where $time_range_condition" );
186
+ }
187
+
188
+ if ( !empty( $_column ) && !empty( self::$columns_names[ $_column ] ) ) {
189
+ $_column = str_replace( '_calculated', '', $_column );
190
+ $column_with_alias = $_column;
191
+ if ( !empty( $_slim_stats_table_alias ) ) {
192
+ $column_with_alias = $_slim_stats_table_alias . '.' . $column_with_alias;
193
+ }
194
+
195
+ $filter_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0' );
196
+ $filter_not_empty = "$column_with_alias " . ( ( self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0' );
197
+
198
+ if ( strpos( $_where, $filter_empty ) === false && strpos( $_where, $filter_not_empty) === false) {
199
+ $_where = "$filter_not_empty AND $_where";
200
+ }
201
+ }
202
+
203
+ return $_where;
204
+ }
205
+
206
+ /**
207
+ * Translates user-friendly operators into SQL conditions
208
+ */
209
+ public static function get_single_where_clause( $_column = 'id', $_operator = 'equals', $_value = '', $_slim_stats_table_alias = '' ) {
210
+ $filter_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NULL' : '= 0';
211
+ $filter_not_empty = ( !empty( self::$columns_names[ $_column ] ) && self::$columns_names[ $_column ] [ 1 ] == 'varchar' ) ? 'IS NOT NULL' : '<> 0';
212
+
213
+ $_column = str_replace( '_calculated', '', $_column );
214
+
215
+ $column_with_alias = $_column;
216
+ if ( !empty( $_slim_stats_table_alias ) ) {
217
+ $column_with_alias = $_slim_stats_table_alias . '.' . $_column;
218
+ }
219
+
220
+ switch( $_column ) {
221
+ case 'ip':
222
+ case 'other_ip':
223
+ $filter_empty = '= "0.0.0.0"';
224
+ break;
225
+ default:
226
+ break;
227
+ }
228
+
229
+ $where = array( '', $_value );
230
+ switch ( $_operator ) {
231
+ case 'is_not_equal_to':
232
+ $where[0] = "$column_with_alias <> %s";
233
+ break;
234
+
235
+ case 'contains':
236
+ $where = array( "$column_with_alias LIKE %s", '%'.$_value.'%' );
237
+ break;
238
+
239
+ case 'includes_in_set':
240
+ $where[0] = "FIND_IN_SET(%s, $column_with_alias) > 0";
241
+ break;
242
+
243
+ case 'does_not_contain':
244
+ $where = array( "$column_with_alias NOT LIKE %s", '%'.$_value.'%' );
245
+ break;
246
+
247
+ case 'starts_with':
248
+ $where = array( "$column_with_alias LIKE %s", $_value.'%' );
249
+ break;
250
+
251
+ case 'ends_with':
252
+ $where = array( "$column_with_alias LIKE %s", '%'.$_value );
253
+ break;
254
+
255
+ case 'sounds_like':
256
+ $where[0] = "SOUNDEX($column_with_alias) = SOUNDEX(%s)";
257
+ break;
258
+
259
+ case 'is_empty':
260
+ $where = array( "$column_with_alias $filter_empty", '' );
261
+ break;
262
+
263
+ case 'is_not_empty':
264
+ $where = array( "$column_with_alias $filter_not_empty", '' );
265
+ break;
266
+
267
+ case 'is_greater_than':
268
+ $where[0] = "$column_with_alias > %d";
269
+ break;
270
+
271
+ case 'is_less_than':
272
+ $where[0] = "$column_with_alias < %d";
273
+ break;
274
+
275
+ case 'between':
276
+ $range = explode(',', $_value);
277
+ $where = array( "$column_with_alias BETWEEN %d AND %d", array( $range[0], $range[1] ) );
278
+ break;
279
+
280
+ case 'matches':
281
+ $where[0] = "$column_with_alias REGEXP %s";
282
+ break;
283
+
284
+ case 'does_not_match':
285
+ $where[0] = "$column_with_alias NOT REGEXP %s";
286
+ break;
287
+
288
+ default:
289
+ $where[0] = "$column_with_alias = %s";
290
+ break;
291
+ }
292
+
293
+ if ( !empty( $where[ 1 ] ) ) {
294
+ return $GLOBALS[ 'wpdb' ]->prepare( $where[ 0 ], $where[ 1 ] );
295
+ }
296
+ else {
297
+ return $where[ 0 ];
298
+ }
299
+ }
300
+
301
+ public static function get_results( $_sql = '', $_select_no_aggregate_values = '', $_order_by = '', $_group_by = '', $_aggregate_values_add = '' ) {
302
+ $_sql = apply_filters( 'slimstat_get_results_sql', $_sql, $_select_no_aggregate_values, $_order_by, $_group_by, $_aggregate_values_add );
303
+
304
+ if ( wp_slimstat::$options[ 'show_sql_debug' ] == 'yes' ) {
305
+ self::$debug_message .= "<p class='debug'>$_sql</p>";
306
+ }
307
+
308
+ return wp_slimstat::$wpdb->get_results( $_sql, ARRAY_A );
309
+ }
310
+
311
+ public static function get_var( $_sql = '', $_aggregate_value = '' ) {
312
+ $_sql = apply_filters( 'slimstat_get_var_sql', $_sql, $_aggregate_value );
313
+
314
+ if ( wp_slimstat::$options[ 'show_sql_debug' ] == 'yes' ) {
315
+ self::$debug_message .= "<p class='debug'>$_sql</p>";
316
+ }
317
+
318
+ return wp_slimstat::$wpdb->get_var( $_sql );
319
+ }
320
+
321
+ public static function parse_filters( $_filters = '', $_init_misc = true ) {
322
+ $filters_normalized = array(
323
+ 'columns' => array(),
324
+ 'date' => array(
325
+ 'interval_direction' => '',
326
+ 'is_past' => false
327
+ ),
328
+ 'misc' => $_init_misc?array(
329
+ 'direction' => 'DESC',
330
+ 'limit_results' => wp_slimstat::$options[ 'limit_results' ],
331
+ 'start_from' => 0
332
+ ) : array(),
333
+ 'utime' => array(
334
+ 'start' => 0,
335
+ 'end' => 0,
336
+ 'type' => 'm'
337
+ )
338
+ );
339
+
340
+ if ( !empty( $_filters ) ) {
341
+ $matches = explode( '&&&', $_filters );
342
+ foreach( $matches as $idx => $a_match ) {
343
+ preg_match( '/([^\s]+)\s([^\s]+)\s(.+)?/', urldecode( $a_match ), $a_filter );
344
+
345
+ if ( empty( $a_filter ) || ( ( !array_key_exists( $a_filter[ 1 ], self::$all_columns_names ) || strpos( $a_filter[ 1 ], 'no_filter' ) !== false ) && strpos( $a_filter[ 1 ], 'addon_' ) === false ) ) {
346
+ continue;
347
+ }
348
+
349
+ switch( $a_filter[ 1 ] ) {
350
+ case 'strtotime':
351
+ $custom_date = strtotime( $a_filter[ 3 ], date_i18n( 'U' ) );
352
+
353
+ $filters_normalized[ 'date' ][ 'minute' ] = intval( date( 'i', $custom_date ) );
354
+ $filters_normalized[ 'date' ][ 'hour' ] = intval( date( 'H', $custom_date ) );
355
+ $filters_normalized[ 'date' ][ 'day' ] = intval( date( 'j', $custom_date ) );
356
+ $filters_normalized[ 'date' ][ 'month' ] = intval( date( 'n', $custom_date ) );
357
+ $filters_normalized[ 'date' ][ 'year' ] = intval( date( 'Y', $custom_date ) );
358
+ break;
359
+
360
+ case 'minute':
361
+ case 'hour':
362
+ case 'day':
363
+ case 'month':
364
+ case 'year':
365
+ if ( is_numeric( $a_filter[ 3 ] ) ) {
366
+ $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = intval( $a_filter[ 3 ] );
367
+ }
368
+ else{
369
+ // Try to apply strtotime to value
370
+ switch( $a_filter[ 1 ] ) {
371
+ case 'minute':
372
+ $filters_normalized[ 'date' ][ 'minute' ] = intval( date( 'i', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
373
+ $filters_normalized[ 'date' ][ 'is_past' ] = true;
374
+ break;
375
+
376
+ case 'hour':
377
+ $filters_normalized[ 'date' ][ 'hour' ] = intval( date( 'H', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
378
+ $filters_normalized[ 'date' ][ 'is_past' ] = true;
379
+ break;
380
+
381
+ case 'day':
382
+ $filters_normalized[ 'date' ][ 'day' ] = intval( date( 'j', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
383
+ break;
384
+
385
+ case 'month':
386
+ $filters_normalized[ 'date' ][ 'month' ] = intval( date( 'n', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
387
+ break;
388
+
389
+ case 'year':
390
+ $filters_normalized[ 'date' ][ 'year' ] = intval( date( 'Y', strtotime( $a_filter[ 3 ], date_i18n( 'U' ) ) ) );
391
+ break;
392
+
393
+ default:
394
+ break;
395
+ }
396
+
397
+ if ( $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] === false ) {
398
+ unset( $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] );
399
+ }
400
+ }
401
+
402
+ switch( $a_filter[ 1 ] ) {
403
+ case 'day':
404
+ if ( $filters_normalized[ 'date' ][ 'day' ] != date_i18n( 'j' ) ) {
405
+ $filters_normalized[ 'date' ][ 'is_past' ] = true;
406
+ }
407
+ break;
408
+
409
+ case 'month':
410
+ if ( $filters_normalized[ 'date' ][ 'month' ] != date_i18n( 'n' ) ) {
411
+ $filters_normalized[ 'date' ][ 'is_past' ] = true;
412
+ }
413
+ break;
414
+
415
+ case 'year':
416
+ if ( $filters_normalized[ 'date' ][ 'year' ] != date_i18n( 'Y' ) ) {
417
+ $filters_normalized[ 'date' ][ 'is_past' ] = true;
418
+ }
419
+ break;
420
+
421
+ default:
422
+ break;
423
+ }
424
+ break;
425
+
426
+ case 'interval':
427
+ case 'interval_hours':
428
+ case 'interval_minutes':
429
+ $intval_filter = intval( $a_filter[ 3 ] );
430
+ $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = abs( $intval_filter );
431
+ if ( $intval_filter < 0 ) {
432
+ $filters_normalized[ 'date' ][ 'interval_direction' ] = 'minus';
433
+ }
434
+ break;
435
+
436
+ case 'interval_direction':
437
+ $filters_normalized[ 'date' ][ $a_filter[ 1 ] ] = in_array( $a_filter[ 3 ], array( 'plus', 'minus' ) ) ? $a_filter[ 3 ] : 'plus';
438
+ break;
439
+
440
+ case 'direction':
441
+ case 'limit_results':
442
+ case 'start_from':
443
+ $filters_normalized[ 'misc' ][ $a_filter[ 1 ] ] = str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) );
444
+ break;
445
+
446
+ default:
447
+ $filters_normalized[ 'columns' ][ $a_filter[ 1 ] ] = array( $a_filter[ 2 ], isset( $a_filter[ 3 ] ) ? str_replace( '\\', '', htmlspecialchars_decode( $a_filter[ 3 ] ) ) : '' );
448
+ break;
449
+ }
450
+ }
451
+ }
452
+
453
+ // Temporarily disable any filters on date_i18n
454
+ $date_i18n_filters = array();
455
+ if ( !empty( $GLOBALS[ 'wp_filter' ][ 'date_i18n' ] ) ) {
456
+ $date_i18n_filters = $GLOBALS[ 'wp_filter' ][ 'date_i18n' ];
457
+ remove_all_filters( 'date_i18n' );
458
+ }
459
+
460
+ // Let's calculate our time range, based on date filters
461
+ if ( empty( $filters_normalized[ 'date' ][ 'interval' ] ) && empty( $filters_normalized[ 'date' ][ 'interval_hours' ] ) && empty( $filters_normalized[ 'date' ][ 'interval_minutes' ] ) ) {
462
+ if ( !empty( $filters_normalized[ 'date' ][ 'minute' ] ) ) {
463
+ $filters_normalized[ 'utime' ][ 'start' ] = mktime(
464
+ !empty( $filters_normalized[ 'date' ][ 'hour' ] )?$filters_normalized[ 'date' ][ 'hour' ]:0,
465
+ $filters_normalized[ 'date' ][ 'minute' ],
466
+ 0,
467
+ !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
468
+ !empty( $filters_normalized[ 'date' ][ 'day' ] )?$filters_normalized[ 'date' ][ 'day' ]:date_i18n( 'j' ),
469
+ !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
470
+ );
471
+ $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 60;
472
+ $filters_normalized[ 'utime' ][ 'type' ] = 'H';
473
+ }
474
+ else if ( !empty( $filters_normalized[ 'date' ][ 'hour' ] ) ) {
475
+ $filters_normalized[ 'utime' ][ 'start' ] = mktime(
476
+ $filters_normalized[ 'date' ][ 'hour' ],
477
+ 0,
478
+ 0,
479
+ !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
480
+ !empty( $filters_normalized[ 'date' ][ 'day' ] )?$filters_normalized[ 'date' ][ 'day' ]:date_i18n( 'j' ),
481
+ !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
482
+ );
483
+ $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 3599;
484
+ $filters_normalized[ 'utime' ][ 'type' ] = 'H';
485
+ }
486
+ else if ( !empty( $filters_normalized[ 'date' ][ 'day' ] ) ) {
487
+ $filters_normalized[ 'utime' ][ 'start' ] = mktime(
488
+ 0,
489
+ 0,
490
+ 0,
491
+ !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
492
+ $filters_normalized[ 'date' ][ 'day' ],
493
+ !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
494
+ );
495
+ $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + 86399;
496
+ $filters_normalized[ 'utime' ][ 'type' ] = 'd';
497
+ }
498
+ else if( !empty( $filters_normalized[ 'date' ][ 'year' ] ) && empty( $filters_normalized[ 'date' ][ 'month' ] ) ) {
499
+ $filters_normalized[ 'utime' ][ 'start' ] = mktime( 0, 0, 0, 1, 1, $filters_normalized[ 'date' ][ 'year' ] );
500
+ $filters_normalized[ 'utime' ][ 'end' ] = mktime( 0, 0, 0, 1, 1, $filters_normalized[ 'date' ][ 'year' ]+1 )-1;
501
+ $filters_normalized[ 'utime' ][ 'type' ] = 'Y';
502
+ }
503
+ else {
504
+ $filters_normalized[ 'utime' ][ 'start' ] = mktime(
505
+ 0,
506
+ 0,
507
+ 0,
508
+ !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
509
+ 1,
510
+ !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
511
+ );
512
+
513
+ $filters_normalized[ 'utime' ][ 'end' ] = strtotime(
514
+ ( !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' ) ).'-'.
515
+ ( !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ) ).
516
+ '-01 00:00 +1 month UTC'
517
+ )-1;
518
+ $filters_normalized[ 'utime' ][ 'type' ] = 'm';
519
+ }
520
+ }
521
+ else { // An interval was specified
522
+ $filters_normalized[ 'utime' ][ 'type' ] = 'interval';
523
+
524
+ $filters_normalized[ 'utime' ][ 'start' ] = mktime(
525
+ !empty( $filters_normalized[ 'date' ][ 'hour' ] )?$filters_normalized[ 'date' ][ 'hour' ]:0,
526
+ !empty( $filters_normalized[ 'date' ][ 'minute' ] )?$filters_normalized[ 'date' ][ 'minute' ]:0,
527
+ 0,
528
+ !empty( $filters_normalized[ 'date' ][ 'month' ] )?$filters_normalized[ 'date' ][ 'month' ]:date_i18n( 'n' ),
529
+ !empty( $filters_normalized[ 'date' ][ 'day' ] )?$filters_normalized[ 'date' ][ 'day' ]:date_i18n( 'j' ),
530
+ !empty( $filters_normalized[ 'date' ][ 'year' ] )?$filters_normalized[ 'date' ][ 'year' ]:date_i18n( 'Y' )
531
+ );
532
+
533
+ $sign = ( $filters_normalized[ 'date' ][ 'interval_direction' ] == 'plus' )?'+':'-';
534
+
535
+ $filters_normalized[ 'utime' ][ 'end' ] = $filters_normalized[ 'utime' ][ 'start' ] + intval( $sign.(
536
+ ( !empty( $filters_normalized[ 'date' ][ 'interval' ] )?intval( $filters_normalized[ 'date' ][ 'interval' ] + 1):0 ) * 86400 +
537
+ ( !empty( $filters_normalized[ 'date' ][ 'interval_hours' ] )?intval( $filters_normalized[ 'date' ][ 'interval_hours' ] ):0 ) * 3600 +
538
+ ( !empty( $filters_normalized[ 'date' ][ 'interval_minutes' ] )?intval( $filters_normalized[ 'date' ][ 'interval_minutes' ] ):0 ) * 60
539
+ ) ) - 1;
540
+
541
+ // Swap boundaries if we're going back in time
542
+ if ( $filters_normalized[ 'date' ][ 'interval_direction' ] == 'minus' ) {
543
+ list( $filters_normalized[ 'utime' ][ 'start' ], $filters_normalized[ 'utime' ][ 'end' ] ) = array( $filters_normalized[ 'utime' ][ 'end' ] + 1, $filters_normalized[ 'utime' ][ 'start' ] - 1 );
544
+ }
545
+ }
546
+
547
+ // If end is in the future, set it to now
548
+ if ( $filters_normalized[ 'utime' ][ 'end' ] > date_i18n( 'U' ) ) {
549
+ $filters_normalized[ 'utime' ][ 'end' ] = date_i18n( 'U' );
550
+ }
551
+
552
+ // If start is after end, set it to first of month
553
+ if ( $filters_normalized[ 'utime' ][ 'start' ] > $filters_normalized[ 'utime' ][ 'end' ] ) {
554
+ $filters_normalized[ 'utime' ][ 'start' ] = mktime(
555
+ 0,
556
+ 0,
557
+ 0,
558
+ date_i18n( 'n', $filters_normalized[ 'utime' ][ 'end' ] ),
559
+ 1,
560
+ date_i18n( 'Y', $filters_normalized[ 'utime' ][ 'end' ] )
561
+ );
562
+ $filters_normalized[ 'date' ][ 'hour' ] = $filters_normalized[ 'date' ][ 'day' ] = $filters_normalized[ 'date' ][ 'month' ] = $filters_normalized[ 'date' ][ 'year' ] = 0;
563
+ }
564
+
565
+ // Restore filters on date_i18n
566
+ foreach ($date_i18n_filters as $i18n_priority => $i18n_func_list) {
567
+ foreach ($i18n_func_list as $func_name => $func_args) {
568
+ add_filter('date_i8n', $func_args[ 'function' ], $i18n_priority, $func_args[ 'accepted_args' ]);
569
+ }
570
+ }
571
+
572
+ return $filters_normalized;
573
+ }
574
+
575
+ // The following methods retrieve the information from the database
576
+
577
+ public static function count_bouncing_pages() {
578
+ $where = self::get_combined_where( 'visit_id > 0 AND content_type <> "404"', 'resource' );
579
+
580
+ return intval( self::get_var( "
581
+ SELECT COUNT(*) counthits
582
+ FROM (
583
+ SELECT resource, visit_id
584
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
585
+ WHERE $where
586
+ GROUP BY resource
587
+ HAVING COUNT(visit_id) = 1
588
+ ) as ts1",
589
+ 'SUM(counthits) AS counthits' ) );
590
+ }
591
+
592
+ public static function count_exit_pages() {
593
+ $where = self::get_combined_where( 'visit_id > 0', 'resource' );
594
+
595
+ return intval( self::get_var( "
596
+ SELECT COUNT(*) counthits
597
+ FROM (
598
+ SELECT resource, dt
599
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
600
+ WHERE $where
601
+ GROUP BY resource
602
+ HAVING dt = MAX(dt)
603
+ ) AS ts1",
604
+ 'SUM(counthits) AS counthits' ) );
605
+ }
606
+
607
+ public static function count_records( $_column = 'id', $_where = '', $_use_date_filters = true ) {
608
+ $distinct_column = ( $_column != 'id' ) ? "DISTINCT $_column" : $_column;
609
+ $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
610
+
611
+ return intval( self::get_var( "
612
+ SELECT COUNT($distinct_column) counthits
613
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
614
+ WHERE $_where",
615
+ 'SUM(counthits) AS counthits' ) );
616
+ }
617
+
618
+ public static function count_records_having( $_column = 'id', $_where = '', $_having = '' ) {
619
+ $_where = self::get_combined_where( $_where, $_column );
620
+
621
+ return intval( self::get_var( "
622
+ SELECT COUNT(*) counthits FROM (
623
+ SELECT $_column
624
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
625
+ WHERE $_where
626
+ GROUP BY $_column
627
+ HAVING $_having
628
+ ) AS ts1",
629
+ 'SUM(counthits) AS counthits' ) );
630
+ }
631
+
632
+ public static function get_data_for_chart( $_data1 = '', $_data2 = '', $_where = '' ) {
633
+ $previous = array( 'end' => self::$filters_normalized[ 'utime' ][ 'start' ] - 1 );
634
+ $label_date_format = '';
635
+ $output = array();
636
+
637
+ // Each type has its own parameters
638
+ switch (self::$filters_normalized[ 'utime' ][ 'type' ]) {
639
+ case 'H':
640
+ $previous[ 'start' ] = self::$filters_normalized[ 'utime' ][ 'start' ] - 3600;
641
+ $label_date_format = wp_slimstat::$options[ 'time_format' ];
642
+ $group_by = array( 'HOUR', 'MINUTE', 'i' );
643
+ $values_in_interval = array( 59, 59, 0, 60 );
644
+ break;
645
+
646
+ case 'd':
647
+ $previous[ 'start' ] = self::$filters_normalized[ 'utime' ][ 'start' ] - 86400;
648
+ $label_date_format = ( self::$formats[ 'decimal' ] == '.' ) ? 'm/d' : 'd/m';
649
+ $group_by = array( 'DAY', 'HOUR', 'G' );
650
+ $values_in_interval = array( 23, 23, 0, 3600 );
651
+ break;
652
+
653
+ case 'Y':
654
+ $previous[ 'start' ] = mktime( 0, 0, 0, 1, 1, self::$filters_normalized[ 'date' ][ 'year' ] - 1 );
655
+ $label_date_format = 'Y';
656
+ $group_by = array( 'YEAR', 'MONTH', 'n' );
657
+ $values_in_interval = array( 12, 12, 1, 2678400 );
658
+ break;
659
+
660
+ case 'interval':
661
+ $group_by = array( 'MONTH', 'DAY', 'j' );
662
+ $values_in_interval = array( abs( self::$filters_normalized[ 'date' ][ 'interval' ] ), abs( self::$filters_normalized[ 'date' ][ 'interval' ] ), 0, 86400 );
663
+ break;
664
+
665
+ default:
666
+ $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' ) );
667
+ $label_date_format = 'm/Y';
668
+ $group_by = array( 'MONTH', 'DAY', 'j' );
669
+ $values_in_interval = array( date( 't', $previous[ 'start' ] ), date( 't', self::$filters_normalized[ 'utime' ][ 'start' ] ), 1, 86400 );
670
+ break;
671
+ }
672
+
673
+ // Custom intervals don't have a comparison chart ('previous' range)
674
+ if ( empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) ) {
675
+ $_where = self::get_combined_where( $_where, '*', false );
676
+ $previous_time_range = ' AND (dt BETWEEN '.$previous[ 'start' ].' AND '.$previous[ 'end' ].' OR dt BETWEEN '.self::$filters_normalized[ 'utime' ][ 'start' ].' AND '.self::$filters_normalized[ 'utime' ][ 'end' ].')';
677
+ }
678
+ else {
679
+ $_where = self::get_combined_where( $_where );
680
+ $previous_time_range = '';
681
+ }
682
+
683
+ // Build the SQL query
684
+ $group_by_string = "GROUP BY {$group_by[0]}(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00')), {$group_by[1]}(CONVERT_TZ(FROM_UNIXTIME(dt), @@session.time_zone, '+00:00'))";
685
+ $sql = "
686
+ SELECT dt, $_data1 first_metric, $_data2 second_metric
687
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
688
+ WHERE $_where $previous_time_range
689
+ $group_by_string";
690
+
691
+ // Get the data
692
+ $results = self::get_results( $sql, 'blog_id', '', $group_by_string, 'SUM(first_metric) AS first_metric, SUM(second_metric) AS second_metric' );
693
+
694
+ // Fill the output array
695
+ $output[ 'current' ][ 'label' ] = '';
696
+ if ( !empty( $label_date_format ) ) {
697
+ $output[ 'current' ][ 'label' ] = gmdate( $label_date_format, self::$filters_normalized[ 'utime' ][ 'start' ] );
698
+ $output[ 'previous' ][ 'label' ] = gmdate( $label_date_format, $previous[ 'start' ] );
699
+ }
700
+
701
+ $output[ 'previous' ][ 'first_metric' ] = array_fill( $values_in_interval[ 2 ], $values_in_interval[ 0 ], 0 );
702
+ $output['previous']['second_metric'] = array_fill( $values_in_interval[ 2 ], $values_in_interval[ 0 ], 0 );
703
+
704
+ $today_limit = floatval( date_i18n( 'Ymd.Hi' ) );
705
+ for ( $i = $values_in_interval[ 2 ]; $i <= $values_in_interval[ 1 ]; $i++ ){
706
+ // Do not include dates in the future
707
+ if ( floatval( date( 'Ymd.Hi', wp_slimstat_db::$filters_normalized[ 'utime' ][ 'start' ] + ( ( $i - $values_in_interval[ 2 ]) * $values_in_interval[ 3 ] ) ) ) > $today_limit ) {
708
+ continue;
709
+ }
710
+
711
+ $output[ 'current' ][ 'first_metric' ][ $i ] = 0;
712
+ $output[ 'current' ][ 'second_metric' ][ $i ] = 0;
713
+ }
714
+
715
+ // No data? No problem!
716
+ if ( !is_array( $results ) || empty( $results ) ) {
717
+ return $output;
718
+ }
719
+
720
+ // Rearrange the data and then format it for Flot
721
+ foreach ($results as $i => $a_result ) {
722
+ $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' ] );
723
+
724
+ if ( empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) && gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $a_result[ 'dt' ] ) == gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $previous[ 'start' ] ) ){
725
+ $output[ 'previous' ][ 'first_metric' ][ $index ] = $a_result[ 'first_metric' ];
726
+ $output[ 'previous' ][ 'second_metric' ][ $index ] = $a_result[ 'second_metric' ];
727
+ }
728
+ if ( !empty( self::$filters_normalized[ 'date' ][ 'interval' ] ) || gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], $a_result[ 'dt' ] ) == gmdate( self::$filters_normalized[ 'utime' ][ 'type' ], self::$filters_normalized[ 'utime' ][ 'start' ] ) ){
729
+ $output[ 'current' ][ 'first_metric' ][ $index ] = $a_result[ 'first_metric' ];
730
+ $output[ 'current' ][ 'second_metric' ][ $index ] = $a_result[ 'second_metric' ];
731
+ }
732
+ }
733
+
734
+ return $output;
735
+ }
736
+
737
+ public static function get_data_size() {
738
+ $suffix = 'KB';
739
+
740
+ $sql = 'SHOW TABLE STATUS LIKE "'.$GLOBALS[ 'wpdb' ]->prefix.'slim_stats"';
741
+ $table_details = wp_slimstat::$wpdb->get_row( $sql, 'ARRAY_A', 0 );
742
+
743
+ $table_size = ( $table_details[ 'Data_length' ] / 1024 ) + ( $table_details[ 'Index_length' ] / 1024 );
744
+
745
+ if ( $table_size > 1024 ) {
746
+ $table_size /= 1024;
747
+ $suffix = 'MB';
748
+ }
749
+ return number_format( $table_size, 2, self::$formats[ 'decimal' ], self::$formats[ 'thousand' ] ).' '.$suffix;
750
+ }
751
+
752
+ public static function get_max_and_average_pages_per_visit() {
753
+ $where = self::get_combined_where( 'visit_id > 0' );
754
+
755
+ return self::get_results( "
756
+ SELECT AVG(ts1.counthits) AS avghits, MAX(ts1.counthits) AS maxhits FROM (
757
+ SELECT count(ip) counthits, visit_id
758
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
759
+ WHERE $where
760
+ GROUP BY visit_id
761
+ ) AS ts1",
762
+ 'blog_id',
763
+ '',
764
+ '',
765
+ 'AVG(avghits) AS avghits, MAX(maxhits) AS maxhits' );
766
+ }
767
+
768
+ public static function get_oldest_visit() {
769
+ return self::get_var( "
770
+ SELECT dt
771
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
772
+ ORDER BY dt ASC
773
+ LIMIT 0, 1",
774
+ 'MIN(dt)' );
775
+ }
776
+
777
+ public static function get_recent( $_column = 'id', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '', $_more_columns = '' ) {
778
+ // This function can be passed individual arguments, or an array of arguments
779
+ if ( is_array( $_column ) ) {
780
+ $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
781
+ $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
782
+ $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
783
+ $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
784
+ $_more_columns = !empty( $_column[ 'more_columns' ] ) ? $_column[ 'more_columns' ] : '';
785
+ $_column = $_column[ 'columns' ];
786
+ }
787
+
788
+ $columns = $_column;
789
+ if ( !empty( $_as_column ) ) {
790
+ $columns = "$_column AS $_as_column";
791
+ }
792
+
793
+ if ( $_column != '*' ) {
794
+ $columns .= ', ip, dt';
795
+ }
796
+
797
+ if ( !empty( $_more_columns ) ) {
798
+ $columns .= ', ' . $_more_columns;
799
+ }
800
+
801
+ $_where = self::get_combined_where( $_where, $_column, $_use_date_filters );
802
+
803
+ //if ( $_column == 'id' || $_column == '*' ) {
804
+ $results = self::get_results( "
805
+ SELECT $columns
806
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
807
+ WHERE $_where
808
+ ORDER BY dt DESC
809
+ LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
810
+ $columns,
811
+ 'dt DESC' );
812
+
813
+ if ( $_column != '*' ) {
814
+ $column_values = array_map( 'unserialize', array_unique( array_map( 'serialize', self::array_column( $results, explode( ',', $_column ) ) ) ) );
815
+ $results = array_intersect_key( $results, $column_values );
816
+ }
817
+
818
+ return $results;
819
+
820
+ //}
821
+ //else {
822
+ // return self::get_results( "
823
+ // SELECT t1.*
824
+ // FROM (
825
+ // SELECT $_column, MAX(id) maxid
826
+ // FROM {$GLOBALS['wpdb']->prefix}slim_stats
827
+ // WHERE $_where
828
+ // GROUP BY $_as_column $_having
829
+ // ) AS ts1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.maxid = t1.id
830
+ // ORDER BY t1.dt DESC
831
+ // LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
832
+ // ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ).', blog_id',
833
+ // 't1.dt DESC' );
834
+ //}
835
+ }
836
+
837
+ public static function get_recent_events() {
838
+ if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
839
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
840
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
841
+ }
842
+ else {
843
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
844
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
845
+ }
846
+
847
+ return self::get_results( "
848
+ SELECT *
849
+ FROM $from
850
+ WHERE $where
851
+ ORDER BY te.dt DESC"
852
+ );
853
+ }
854
+
855
+ public static function get_top( $_column = 'id', $_where = '', $_having = '', $_use_date_filters = true, $_as_column = '' ){
856
+ // This function can be passed individual arguments, or an array of arguments
857
+ if ( is_array( $_column ) ) {
858
+ $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
859
+ $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
860
+ $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
861
+ $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
862
+ $_column = $_column[ 'columns' ];
863
+ }
864
+
865
+ if ( !empty( $_as_column ) ) {
866
+ $_column = "$_column AS $_as_column";
867
+ }
868
+ else {
869
+ $_as_column = $_column;
870
+ }
871
+
872
+ $_where = self::get_combined_where( $_where, $_as_column, $_use_date_filters );
873
+
874
+ return self::get_results( "
875
+ SELECT $_column, COUNT(*) counthits
876
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
877
+ WHERE $_where
878
+ GROUP BY $_as_column $_having
879
+ ORDER BY counthits DESC
880
+ LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
881
+ ( ( !empty( $_as_column ) && $_as_column != $_column ) ? $_as_column : $_column ).', blog_id',
882
+ 'counthits DESC',
883
+ $_column,
884
+ 'SUM(counthits) AS counthits' );
885
+ }
886
+
887
+ public static function get_top_aggr( $_column = 'id', $_where = '', $_outer_select_column = '', $_aggr_function = 'MAX' ) {
888
+ if ( is_array( $_column ) ) {
889
+ $_where = !empty( $_column[ 'where' ] ) ? $_column[ 'where' ] : '';
890
+ $_having = !empty( $_column[ 'having' ] ) ? $_column[ 'having' ] : '';
891
+ $_use_date_filters = !empty( $_column[ 'use_date_filters' ] ) ? $_column[ 'use_date_filters' ] : true;
892
+ $_as_column = !empty( $_column[ 'as_column' ] ) ? $_column[ 'as_column' ] : '';
893
+ $_outer_select_column = !empty( $_column[ 'outer_select_column' ] ) ? $_column[ 'outer_select_column' ] : '';
894
+ $_aggr_function = !empty( $_column[ 'aggr_function' ] ) ? $_column[ 'aggr_function' ] : '';
895
+ $_column = $_column[ 'columns' ];
896
+ }
897
+
898
+ if ( !empty( $_as_column ) ) {
899
+ $_column = "$_column AS $_as_column";
900
+ }
901
+ else {
902
+ $_as_column = $_column;
903
+ }
904
+
905
+ $_where = self::get_combined_where( $_where, $_column );
906
+
907
+ return self::get_results( "
908
+ SELECT $_outer_select_column, ts1.aggrid as $_column, COUNT(*) counthits
909
+ FROM (
910
+ SELECT $_column, $_aggr_function(id) aggrid
911
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats
912
+ WHERE $_where
913
+ GROUP BY $_column
914
+ ) AS ts1 JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.aggrid = t1.id
915
+ GROUP BY $_outer_select_column
916
+ ORDER BY counthits DESC
917
+ LIMIT 0, " . self::$filters_normalized[ 'misc' ][ 'limit_results' ],
918
+ $_outer_select_column,
919
+ 'counthits DESC',
920
+ $_outer_select_column,
921
+ "$_aggr_function(aggrid), SUM(counthits)" );
922
+ }
923
+
924
+ public static function get_top_events() {
925
+ if ( empty( self::$filters_normalized[ 'columns' ] ) ) {
926
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te";
927
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes' );
928
+ }
929
+ else {
930
+ $from = "{$GLOBALS['wpdb']->prefix}slim_events te INNER JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON te.id = t1.id";
931
+ $where = wp_slimstat_db::get_combined_where( 'te.type > 1', 'notes', true, 't1' );
932
+ }
933
+
934
+ return self::get_results( "
935
+ SELECT te.notes, te.type, COUNT(*) counthits
936
+ FROM $from
937
+ WHERE $where
938
+ GROUP BY te.notes, te.type
939
+ ORDER BY counthits DESC"
940
+ );
941
+ }
942
+
943
+ protected static function array_column( $input = array(), $columns = array() ) {
944
+ $output = array();
945
+
946
+ foreach ( $input as $a_key => $a_row ) {
947
+ foreach ( $columns as $a_column ) {
948
+ $a_column = trim( $a_column );
949
+ if ( $a_row[ $a_column ] != NULL ) {
950
+ $output[ $a_key ][ $a_column ] = $a_row[ $a_column ];
951
+ }
952
+ }
953
+ }
954
+
955
+ return $output;
956
+ }
957
  }
admin/view/wp-slimstat-reports.php CHANGED
@@ -85,7 +85,7 @@ class wp_slimstat_reports {
85
  'callback' => array( __CLASS__, 'show_activity_log' ),
86
  'callback_args' => array(
87
  'type' => 'recent',
88
- 'columns' => 'id',
89
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
90
  ),
91
  'classes' => array( 'full-width', 'tall' ),
@@ -143,6 +143,7 @@ class wp_slimstat_reports {
143
  'callback_args' => array(
144
  'type' => 'recent',
145
  'columns' => 'searchterms',
 
146
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
147
  ),
148
  'classes' => array( 'normal' ),
@@ -169,7 +170,7 @@ class wp_slimstat_reports {
169
  'callback_args' => array(
170
  'type' => 'top',
171
  'columns' => 'referer',
172
- 'where' => 'referer NOT LIKE "%' . home_url() . '%"',
173
  'raw' => array( 'wp_slimstat_db', 'get_top' )
174
  ),
175
  'classes' => array( 'normal' ),
@@ -474,24 +475,27 @@ class wp_slimstat_reports {
474
  'classes' => array( 'normal' ),
475
  'screens' => array( 'wp-slim-view-5', 'dashboard' )
476
  ),
 
 
477
  'slim_p3_11' => array(
478
  'title' => __( 'Recent Exit Pages', 'wp-slimstat' ),
479
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
480
  'callback_args' => array(
481
  'type' => 'recent',
482
- 'columns' => 'visit_id', // raw_results_to_html knows to display the resource, when the column is visit_id
483
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
484
  ),
485
  'classes' => array( 'normal' ),
486
  'screens' => array( 'wp-slim-view-5' )
487
  ),
 
488
 
489
  'slim_p4_01' => array(
490
  'title' => __( 'Recent Outbound Links', 'wp-slimstat' ),
491
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
492
  'callback_args' => array(
493
  'type' => 'recent',
494
- 'columns' => 'outbound_resource', // raw_results_to_html knows to display the resource, when the column is visit_id
495
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
496
  ),
497
  'classes' => array( 'wide' ),
@@ -510,6 +514,7 @@ class wp_slimstat_reports {
510
  'classes' => array( 'normal' ),
511
  'screens' => array( 'wp-slim-view-4' )
512
  ),
 
513
  'slim_p4_03' => array(
514
  'title' => __( 'Recent Bounce Pages', 'wp-slimstat' ),
515
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
@@ -524,6 +529,7 @@ class wp_slimstat_reports {
524
  'screens' => array( 'wp-slim-view-4' ),
525
  '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' )
526
  ),
 
527
  'slim_p4_04' => array(
528
  'title' => __( 'Recent Feeds', 'wp-slimstat' ),
529
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
@@ -1009,14 +1015,14 @@ class wp_slimstat_reports {
1009
  break;
1010
 
1011
  case 'searchterms':
1012
- if ($_args[ 'type' ] == 'recent'){
1013
- $domain = parse_url( $results[ $i ][ 'resource' ], PHP_URL_HOST );
1014
-
1015
- $row_details = '<br>'.__('Referrer','wp-slimstat').": $domain";
1016
- $element_value = self::get_search_terms_info($results[$i]['searchterms'], $results[$i]['referer'], true);
1017
  }
1018
  else{
1019
- $element_value = htmlentities($results[$i]['searchterms'], ENT_QUOTES, 'UTF-8');
1020
  }
1021
  break;
1022
  case 'username':
85
  'callback' => array( __CLASS__, 'show_activity_log' ),
86
  'callback_args' => array(
87
  'type' => 'recent',
88
+ 'columns' => '*',
89
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
90
  ),
91
  'classes' => array( 'full-width', 'tall' ),
143
  'callback_args' => array(
144
  'type' => 'recent',
145
  'columns' => 'searchterms',
146
+ 'more_columns' => 'referer, resource',
147
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
148
  ),
149
  'classes' => array( 'normal' ),
170
  'callback_args' => array(
171
  'type' => 'top',
172
  'columns' => 'referer',
173
+ 'where' => 'referer NOT LIKE "%' . str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) ) . '%"',
174
  'raw' => array( 'wp_slimstat_db', 'get_top' )
175
  ),
176
  'classes' => array( 'normal' ),
475
  'classes' => array( 'normal' ),
476
  'screens' => array( 'wp-slim-view-5', 'dashboard' )
477
  ),
478
+
479
+ /*
480
  'slim_p3_11' => array(
481
  'title' => __( 'Recent Exit Pages', 'wp-slimstat' ),
482
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
483
  'callback_args' => array(
484
  'type' => 'recent',
485
+ 'columns' => 'visit_id, resource', // raw_results_to_html knows to display the resource, when the column is visit_id
486
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
487
  ),
488
  'classes' => array( 'normal' ),
489
  'screens' => array( 'wp-slim-view-5' )
490
  ),
491
+ */
492
 
493
  'slim_p4_01' => array(
494
  'title' => __( 'Recent Outbound Links', 'wp-slimstat' ),
495
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
496
  'callback_args' => array(
497
  'type' => 'recent',
498
+ 'columns' => 'outbound_resource',
499
  'raw' => array( 'wp_slimstat_db', 'get_recent' )
500
  ),
501
  'classes' => array( 'wide' ),
514
  'classes' => array( 'normal' ),
515
  'screens' => array( 'wp-slim-view-4' )
516
  ),
517
+ /*
518
  'slim_p4_03' => array(
519
  'title' => __( 'Recent Bounce Pages', 'wp-slimstat' ),
520
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
529
  'screens' => array( 'wp-slim-view-4' ),
530
  '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' )
531
  ),
532
+ */
533
  'slim_p4_04' => array(
534
  'title' => __( 'Recent Feeds', 'wp-slimstat' ),
535
  'callback' => array( __CLASS__, 'raw_results_to_html' ),
1015
  break;
1016
 
1017
  case 'searchterms':
1018
+ if ( $_args[ 'type' ] == 'recent' ) {
1019
+ $domain = parse_url( $results[ $i ][ 'referer' ], PHP_URL_HOST );
1020
+
1021
+ $row_details = '<br>' . __( 'Referrer', 'wp-slimstat' ) . ": $domain";
1022
+ $element_value = self::get_search_terms_info( $results[ $i ][ 'searchterms' ], $results[ $i ][ 'referer' ], true );
1023
  }
1024
  else{
1025
+ $element_value = htmlentities( $results[ $i ][ 'searchterms' ], ENT_QUOTES, 'UTF-8' );
1026
  }
1027
  break;
1028
  case 'username':
admin/wp-slimstat-admin.php CHANGED
@@ -11,8 +11,8 @@ class wp_slimstat_admin{
11
  */
12
  public static function init(){
13
  if ((wp_slimstat::$options['enable_ads_network'] == 'yes' || wp_slimstat::$options['enable_ads_network'] == 'no')){
14
- self::$admin_notice = "Two new members just joined the big family of add-ons available on our store: <a href='http://www.wp-slimstat.com/downloads/heartbeat/' target='_blank'>Heartbeat</a> and <a href='http://www.wp-slimstat.com/downloads/whitelist-manager/' target='_blank'>Whitelist Manager</a>. The former increases the accuracy of visit durations, and the latter allows you to determine what pageviews should be tracked by Slimstat, based on criteria like username, IP address, permalink, country and referring URL. Go get your copy today!";
15
- // self::$admin_notice = "Isn't it nice when a WordPress Forum moderator <a href='https://wordpress.org/support/topic/statistics-30' target='_blank'>recommends our plugin</a> to users asking what analytics tool to use for their website? And what about the joy of being listed on the <a href='http://plugintable.com/' target='_blank'>Periodic Table of WordPress Plugins</a> ranking? Thank you 1.6 million times for showing your appreciation and support, and making Slimstat one of the leading analytics tools for WordPress.";
16
  self::$admin_notice .= '<br/><br/><a id="slimstat-hide-admin-notice" href="#" class="button-secondary">Got it, thanks</a>';
17
  }
18
  else {
11
  */
12
  public static function init(){
13
  if ((wp_slimstat::$options['enable_ads_network'] == 'yes' || wp_slimstat::$options['enable_ads_network'] == 'no')){
14
+ self::$admin_notice = "Isn't it nice when a WordPress Forum moderator <a href='https://wordpress.org/support/topic/statistics-30' target='_blank'>recommends Slimstat</a> to those who ask what analytics tool to use for their website? Thank you 1.6 million times for showing your appreciation and support, and making Slimstat one of the leading analytics tools for WordPress. We are celebrating by updating our website with a new homepage and new information on this plugin. <a href='http://www.wp-slimstat.com/' target='_blank'>Go take a look</a>.";
15
+ // self::$admin_notice = "Performance, performance, performance. This has been our mantra in the last few weeks. The new database API introduced in version 4 allowed us to clean up our code and reveal little hidden paths that lead to code perfection. <a href='https://wordpress.org/support/view/plugin-reviews/wp-slimstat#postform' target='_blank'>Let us know</a> if you notice any difference in the amount of time needed to generate your reports.";
16
  self::$admin_notice .= '<br/><br/><a id="slimstat-hide-admin-notice" href="#" class="button-secondary">Got it, thanks</a>';
17
  }
18
  else {
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.3
7
- Stable tag: 4.1.6
8
 
9
  == Description ==
10
  [youtube https://www.youtube.com/watch?v=iJCtjxArq4U]
@@ -22,13 +22,13 @@ Stable tag: 4.1.6
22
  * I like Slimstat very much and so I decided to use it instead of Piwik - [Joannes](http://wordpress.org/support/topic/plugin-wp-slimstat-slimstat-and-privacy)
23
  * Read all the [reviews](http://wordpress.org/support/view/plugin-reviews/wp-slimstat) and feel free to post your own!
24
 
25
- = Requirements =
26
- * WordPress 3.8+
27
- * PHP 5.3+
28
- * MySQL 5.0.3+
29
- * At least 5 MB of free web space
30
- * At least 5 MB of free DB space
31
- * At least 4 Mb of free PHP memory for the tracker (peak memory usage)
32
  * IE9+ or any browser supporting HTML5, to access the reports
33
 
34
  = Premium Add-ons =
@@ -59,6 +59,17 @@ Our knowledge base is available on our [support center](http://docs.wp-slimstat.
59
 
60
  == Changelog ==
61
 
 
 
 
 
 
 
 
 
 
 
 
62
  = 4.1.6 =
63
  * [New] Administrators can now set the maximum number of records that should be retrieved from the database when generating the reports (Settings > Reports). This allows those with powerful servers and unlimited PHP resources to increase this limit and get a more accurate picture of their visitors.
64
  * [New] Extended the export functionality (via our premium Export to Excel add-on) to reports like At a Glance, Rankings, Audience Overview, etc (thank you, Tiffany).
@@ -158,42 +169,6 @@ Our knowledge base is available on our [support center](http://docs.wp-slimstat.
158
  * [Fix] In Client Mode (aka Javascript mode), all page content types were being set to 'admin', under certain circumstances.
159
  * [Fix] THe uninstall script was not removing the 'old' tables (wp_slim_stats_3, wp_slim_stats_archive_3).
160
 
161
- = 4.0.2 =
162
- * [Note] There seem to be some issues with the tracker not being updated throughout the CDN. If you are using this service, please disable it temporarily (Settings > Advanced > Enable CDN = No) until this is resolved. [We are in touch](https://github.com/jsdelivr/jsdelivr/issues/2632#issuecomment-101994217) with the team managing the CDN.
163
- * [Fix] Some users reported a PHP syntax error message related to a short syntax used by the heuristic browser detection script (thank you, [engesco](https://wordpress.org/support/topic/error-with-new-plug-in-update?replies=27#post-6949271))
164
- * [Fix] A PHP warning was being displayed for some undefined indexes (thank you, [mark_kay](https://wordpress.org/support/topic/error-with-new-plug-in-update?replies=29#post-6948972))
165
-
166
- = 4.0.1 =
167
- * [Note] Version 4.0 had a bumpy start, but that's expected when something radically new is released to the public. We thank you for your patience while we addressed the bugs that didn't surface during our tests.
168
- * [Note] Make sure to uninstall the Dashboard Widgets add-on before upgrading to Slimstat 4.0, or you might get a white screen of death. If this is the case, please remove the folder wp-content/plugins/wp-slimstat-dashboard-widgets via FTP. You will not lose your data.
169
- * [New] Say hello to your new Dashboard Widgets. We decided to merge our free add-on into the main plugin: this way you don't have to deal with a separate software, our update cycle is streamlined, and performance increases. You can always deactivate this integration by using the corresponding option under Settings > General.
170
- * [Fix] A few people pointed out a Unexpected T_FUNCTION parse error. Slimstat officially requires PHP 5.3 to function properly. Nevertheless, we implemented a workaround so that people with PHP 5.2 can still enjoy all the power of our plugin. Thank you for your patience.
171
- * [Fix] MySQL Error 121 was preventing the plugin from creating the new table structure, if MySQL was configured to work in strict mode (thank you, [wvploeg](https://wordpress.org/support/topic/after-update-it-stopped-working?replies=6))
172
- * [Fix] If you compile PHP with certain flags on Ubuntu, gzopen is not available (thank you, [larryisthere](https://wordpress.org/support/topic/geolite-db-installation-issue-on-ubuntu-trusty?replies=2))
173
-
174
- = 4.0 =
175
- * [Note] A brave new world is now ready to be explored: Slimstat 4.0. This version introduces a totally redesigned database architecture, new streamlined tracking code, new heuristic user agent parser, new filters and much more. You will surely notice the performance improvements!
176
- * [Note] Our dev team should have read [this article](http://blog.codinghorror.com/maybe-normalizing-isnt-normal/) a long time ago. But it's never too late, and we can guarantee you that the new denormalized table structure will make your report generation so quick that your jaw will drop. Sure, the table size will increase 50%, but in the age where space is cheap, the real precious resource is time. The time you won't have to wait for your report to appear!
177
- * [Note] Upon update, Slimstat will convert the old table structure to the new one. Just to stay on the safe side, the old tables will not be removed (wp_slim_stats will be renamed to wp_slim_stats_3). After a transition period, we will offer the option to remove the old tables with a button in the settings.
178
- * [Note] Please make sure that your MySQL user can issue a RENAME command.
179
- * [Note] We are now working on our premium add-ons to make them compatible with Slimstat 4.0. Some of them might stop working until a new update is available.
180
- * [New] MaxMind upload folder path can now be filtered (thank you, [chrisl27](https://wordpress.org/support/topic/filter-for-maxmind-path)).
181
- * [New] The new tracker is measuring both the screen resolution and the viewport size. [Here](http://www.quirksmode.org/mobile/viewports.html) you can find more information on this topic.
182
- * [New] The library wp-slimstat-db.php has been cleaned up and reorganized (20% smaller!). Please note: some of the function signatures have changed (order of parameters), please update your custom code accordingly or contact us for more information.
183
- * [New] Internal downloads are now tracked as regular pageviews, with content_type = download. This allows to make our filters more intuitive and our reports faster.
184
- * [New] The table wp_slim_events will now store all the information regarding events happening on your pages (including the coordinates of clicks for the heatmap).
185
- * [New] Inline data attributes on links will tell you right away if an external URL will be tracked or not.
186
- * [New] Custom reports can now be added to ANY screen, and soon you will be able to move any built-in report to any screen.
187
- * [Update] We are making our source code easier to read, by applying some well established best practices about indentation, spacing and variable names.
188
- * [Update] Removed the chart Average Pageviews per Visit, which required a complex SQL query to be generated, and didn't convey any key information, according to a quick survey we had among some of our users.
189
- * [Update] The Spy View report under the Overview tab has been merged with the Real-Time log, since users were pointing out that it was confusing to have two separate reports displaying pretty much the same information.
190
- * [Update] We decided to hide some reports under the Site Analysis tab by default. They are not gone, and can be quickly activated by enabling the corresponding checkbox under Screen Options.
191
- * [Update] The browser CSS version is not tracked anymore.
192
- * [Update] Google+1 clicks are not tracked/supported anymore.
193
- * [Update] Vitaly has sent us the latest version of the Russian localization. Way to go!
194
- * [Fix] Implemented a more robust fix for the issue with download_url throwing an undefined function error (this is supposed to be part of [WP Core](https://codex.wordpress.org/Function_Reference/download_url)!)
195
- * [Fix] When dragging boxes around, the placeholder was not being displayed in the right place.
196
-
197
  == Special Thanks To ==
198
 
199
  * [Vitaly](http://www.visbiz.org/), who volunteers quite a lot of time for QA, testing, and for his Russian localization.
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.3
7
+ Stable tag: 4.1.6.1
8
 
9
  == Description ==
10
  [youtube https://www.youtube.com/watch?v=iJCtjxArq4U]
22
  * I like Slimstat very much and so I decided to use it instead of Piwik - [Joannes](http://wordpress.org/support/topic/plugin-wp-slimstat-slimstat-and-privacy)
23
  * Read all the [reviews](http://wordpress.org/support/view/plugin-reviews/wp-slimstat) and feel free to post your own!
24
 
25
+ = Minimum Requirements =
26
+ * WordPress 3.8
27
+ * PHP 5.3
28
+ * MySQL 5.0.3
29
+ * 25 MB of free space on your filesystem
30
+ * 5 MB of free DB space
31
+ * 10 Mb of free PHP memory for the tracker (peak memory usage)
32
  * IE9+ or any browser supporting HTML5, to access the reports
33
 
34
  = Premium Add-ons =
59
 
60
  == Changelog ==
61
 
62
+ = 4.1.6.1 =
63
+ * [New] Contextual counters are now added not just to pages and posts, but to other custom post types available on your website.
64
+ * [Update] Optimized SQL query that retrieves the data for the Access Log report.
65
+ * [Update] New link for GetSocial.io partnership.
66
+ * [Fix] Patched a remote XSS vulnerability related to forged referrer URLs.
67
+ * [Fix] Bug in refreshing Access Log (second try).
68
+ * [Fix] Bug in calculating Unique IP counters for pages and posts.
69
+ * [Fix] Link to install the GeoLocation DB was pointing to the wrong tab under Settings.
70
+ * [Fix] When selecting the filter in Overview > Top Pages, reports were returning empty datasets.
71
+ * [Fix] Resetting the report layout was not always working as expected, if Slimstat was displayed in the admin bar.
72
+
73
  = 4.1.6 =
74
  * [New] Administrators can now set the maximum number of records that should be retrieved from the database when generating the reports (Settings > Reports). This allows those with powerful servers and unlimited PHP resources to increase this limit and get a more accurate picture of their visitors.
75
  * [New] Extended the export functionality (via our premium Export to Excel add-on) to reports like At a Glance, Rankings, Audience Overview, etc (thank you, Tiffany).
169
  * [Fix] In Client Mode (aka Javascript mode), all page content types were being set to 'admin', under certain circumstances.
170
  * [Fix] THe uninstall script was not removing the 'old' tables (wp_slim_stats_3, wp_slim_stats_archive_3).
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  == Special Thanks To ==
173
 
174
  * [Vitaly](http://www.visbiz.org/), who volunteers quite a lot of time for QA, testing, and for his Russian localization.
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: 4.1.6
7
  Author: Camu
8
  Author URI: http://www.wp-slimstat.com/
9
  */
@@ -11,7 +11,7 @@ Author URI: http://www.wp-slimstat.com/
11
  if ( !empty( wp_slimstat::$options ) ) return true;
12
 
13
  class wp_slimstat {
14
- public static $version = '4.1.6';
15
  public static $options = array();
16
 
17
  public static $wpdb = '';
@@ -32,6 +32,13 @@ class wp_slimstat {
32
 
33
  // Load all the settings
34
  self::$options = ( is_network_admin() && ( empty($_GET[ 'page' ] ) || strpos( $_GET[ 'page' ], 'wp-slim-view' ) === false ) ) ? get_site_option( 'slimstat_options', array() ) : get_option( 'slimstat_options', array() );
 
 
 
 
 
 
 
35
  self::$options = array_merge( self::init_options(), self::$options );
36
 
37
  // Allow third party tools to edit the options
@@ -1457,13 +1464,13 @@ class wp_slimstat {
1457
  */
1458
  public static function init_options(){
1459
  $val_yes = 'yes'; $val_no = 'no';
1460
- if (is_network_admin() && (empty($_GET['page']) || strpos($_GET['page'], 'wp-slim-view') === false)){
1461
  $val_yes = $val_no = 'null';
1462
  }
1463
 
1464
  $options = array(
1465
  'version' => self::$version,
1466
- 'secret' => wp_hash(uniqid(time(), true)),
1467
  'show_admin_notice' => 0,
1468
 
1469
  // General
@@ -1482,18 +1489,17 @@ class wp_slimstat {
1482
 
1483
  // Views
1484
  'use_european_separators' => $val_yes,
1485
- 'date_format' => ($val_yes == 'null')?'':'m-d-y',
1486
- 'time_format' => ($val_yes == 'null')?'':'h:i a',
1487
  'show_display_name' => $val_no,
1488
  'convert_resource_urls_to_titles' => $val_yes,
1489
  'convert_ip_addresses' => $val_no,
1490
  'use_slimscroll' => $val_yes,
1491
  'expand_details' => $val_no,
1492
- 'rows_to_show' => ($val_yes == 'null')?'0':'20',
1493
- 'limit_results' => ($val_yes == 'null')?'0':'5000',
1494
- 'refresh_interval' => ($val_yes == 'null')?'0':'60',
1495
- 'number_results_raw_data' => ($val_yes == 'null')?'0':'50',
1496
- // 'include_outbound_links_right_now' => $val_yes,
1497
  'show_complete_user_agent_tooltip' => $val_no,
1498
  'no_maxmind_warning' => $val_no,
1499
  'enable_sov' => $val_no,
@@ -1519,9 +1525,9 @@ class wp_slimstat {
1519
 
1520
  // Permissions
1521
  'restrict_authors_view' => $val_yes,
1522
- 'capability_can_view' => ($val_yes == 'null')?'':'activate_plugins',
1523
  'can_view' => '',
1524
- 'capability_can_admin' => ($val_yes == 'null')?'':'activate_plugins',
1525
  'can_admin' => '',
1526
 
1527
  // Advanced
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: 4.1.6.1
7
  Author: Camu
8
  Author URI: http://www.wp-slimstat.com/
9
  */
11
  if ( !empty( wp_slimstat::$options ) ) return true;
12
 
13
  class wp_slimstat {
14
+ public static $version = '4.1.6.1';
15
  public static $options = array();
16
 
17
  public static $wpdb = '';
32
 
33
  // Load all the settings
34
  self::$options = ( is_network_admin() && ( empty($_GET[ 'page' ] ) || strpos( $_GET[ 'page' ], 'wp-slim-view' ) === false ) ) ? get_site_option( 'slimstat_options', array() ) : get_option( 'slimstat_options', array() );
35
+
36
+ // If no settings were found, we might be in the Network Admin with no "Network View" add-on enabled
37
+ if ( empty( self::$options ) && is_network_admin() ) {
38
+ self::$options = get_blog_option( 1, 'slimstat_options', array() );
39
+ }
40
+
41
+ // Initialize the options, if needed
42
  self::$options = array_merge( self::init_options(), self::$options );
43
 
44
  // Allow third party tools to edit the options
1464
  */
1465
  public static function init_options(){
1466
  $val_yes = 'yes'; $val_no = 'no';
1467
+ if ( is_network_admin() && ( empty( $_GET[ 'page' ] ) || strpos( $_GET[ 'page' ], 'wp-slim-view' ) === false ) ) {
1468
  $val_yes = $val_no = 'null';
1469
  }
1470
 
1471
  $options = array(
1472
  'version' => self::$version,
1473
+ 'secret' => wp_hash( uniqid( time(), true ) ),
1474
  'show_admin_notice' => 0,
1475
 
1476
  // General
1489
 
1490
  // Views
1491
  'use_european_separators' => $val_yes,
1492
+ 'date_format' => ( $val_yes == 'null' ) ? '' : 'm-d-y',
1493
+ 'time_format' => ( $val_yes == 'null' ) ? '' : 'h:i a',
1494
  'show_display_name' => $val_no,
1495
  'convert_resource_urls_to_titles' => $val_yes,
1496
  'convert_ip_addresses' => $val_no,
1497
  'use_slimscroll' => $val_yes,
1498
  'expand_details' => $val_no,
1499
+ 'rows_to_show' => ( $val_yes == 'null' ) ? '0' : '20',
1500
+ 'limit_results' => ( $val_yes == 'null' ) ? '0' : '1000',
1501
+ 'refresh_interval' => ( $val_yes == 'null' ) ? '0' : '60',
1502
+ 'number_results_raw_data' => ( $val_yes == 'null' ) ? '0' : '50',
 
1503
  'show_complete_user_agent_tooltip' => $val_no,
1504
  'no_maxmind_warning' => $val_no,
1505
  'enable_sov' => $val_no,
1525
 
1526
  // Permissions
1527
  'restrict_authors_view' => $val_yes,
1528
+ 'capability_can_view' => ( $val_yes == 'null' ) ? '' : 'activate_plugins',
1529
  'can_view' => '',
1530
+ 'capability_can_admin' => ( $val_yes == 'null' ) ? '' : 'activate_plugins',
1531
  'can_admin' => '',
1532
 
1533
  // Advanced