Slimstat Analytics - Version 3.9.8

Version Description

  • [Note] The team who manages the WordPress Plugin Repository notified us that since the MaxMind GeoLite library used by Slimstat to geolocate visitors is released under the Creative Commons BY-SA 3.0 license, it violates the repository guidelines, and cannot be bundled with the plugin. We were required to remove the code and alter the plugin so that this functionality becomes optional. We apologize for the inconvenience. However, the only immediate consequence is that your visitors' country will not be identified; everything else will still work as usual. You can download the geolocation DB as a separate add-on on our store, free of charge. Don't forget to enter your license key in the corresponding field under Slimstat > Add-ons, to receive free updates!
  • [New] A few new options under Slimstat > Settings > General tab > WordPress Integration section, allow you to have more control over the information displayed in the Posts admin screen (thank you, Brad).
Download this release

Release Info

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

Code changes from version 3.9.7.1 to 3.9.8

admin/config/addons.php CHANGED
@@ -62,7 +62,7 @@ if (!is_array($list_addons)){
62
  </th>
63
  <td class="column-description desc">
64
  <div class="plugin-description"><p><?php echo $a_addon['description'] ?></p></div>
65
- <?php if ((is_plugin_active($a_addon['slug'].'/index.php') || is_plugin_active($a_addon['slug'].'/'.$a_addon['slug'].'.php')) && intval($a_addon['price']) > 0): $license_key_field = true; ?>
66
  <div class="active second">
67
  License Key: <input type="text" name="licenses[<?php echo $a_addon['slug'] ?>]" value="<?php echo !empty(wp_slimstat::$options['addon_licenses'][$a_addon['slug']])?wp_slimstat::$options['addon_licenses'][$a_addon['slug']]:'' ?>" size="50"/>
68
  </div>
@@ -72,8 +72,7 @@ if (!is_array($list_addons)){
72
  <?php endforeach ?>
73
  </tbody>
74
  </table>
75
- <?php if ($license_key_field): ?>
76
- <input type="submit" value="Save Changes" class="button-primary" name="Submit">
77
- <?php endif ?>
78
  </form>
79
  </div>
62
  </th>
63
  <td class="column-description desc">
64
  <div class="plugin-description"><p><?php echo $a_addon['description'] ?></p></div>
65
+ <?php if ((is_plugin_active($a_addon['slug'].'/index.php') || is_plugin_active($a_addon['slug'].'/'.$a_addon['slug'].'.php'))): ?>
66
  <div class="active second">
67
  License Key: <input type="text" name="licenses[<?php echo $a_addon['slug'] ?>]" value="<?php echo !empty(wp_slimstat::$options['addon_licenses'][$a_addon['slug']])?wp_slimstat::$options['addon_licenses'][$a_addon['slug']]:'' ?>" size="50"/>
68
  </div>
72
  <?php endforeach ?>
73
  </tbody>
74
  </table>
75
+ <input type="submit" value="Save Changes" class="button-primary" name="Submit">
76
+
 
77
  </form>
78
  </div>
admin/config/index.php CHANGED
@@ -25,7 +25,9 @@ switch ($config_tabs[$current_tab-1]){
25
 
26
  'general_integration_header' => array('description' => __('WordPress Integration','wp-slimstat'), 'type' => 'section_header'),
27
  'use_separate_menu' => array( 'description' => __('Menu Position','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Choose between a standalone admin menu for Slimstat or a drop down in the admin bar (if visible).','wp-slimstat'), 'custom_label_yes' => __('Side Menu','wp-slimstat'), 'custom_label_no' => __('Admin Bar','wp-slimstat') ),
28
- 'add_posts_column' => array( 'description' => __('Add Stats to Posts and Pages','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Add a new column to the Edit Posts/Pages screens, with the number of hits per post.','wp-slimstat') ),
 
 
29
 
30
  'general_database_header' => array('description' => __('Database','wp-slimstat'), 'type' => 'section_header'),
31
  'auto_purge' => array( 'description' => __('Retain data for','wp-slimstat'), 'type' => 'integer', 'long_description' => __("Clean-up log entries older than the number of days specified here above. Enter <strong>0</strong> (number zero) if you want to preserve your data regardless of its age.",'wp-slimstat').(wp_get_schedule('wp_slimstat_purge')?' '.__('Next clean-up on','wp-slimstat').' <strong>'.date_i18n(get_option('date_format').', '.get_option('time_format'), wp_next_scheduled('wp_slimstat_purge')).'</strong>. '.sprintf(__('Entries logged on or before %s will be archived or deleted according to the option here below.','wp-slimstat'), date_i18n(get_option('date_format'), strtotime('-'.wp_slimstat::$options['auto_purge'].' days'))):''), 'after_input_field' => __('days','wp-slimstat') ),
25
 
26
  'general_integration_header' => array('description' => __('WordPress Integration','wp-slimstat'), 'type' => 'section_header'),
27
  'use_separate_menu' => array( 'description' => __('Menu Position','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Choose between a standalone admin menu for Slimstat or a drop down in the admin bar (if visible).','wp-slimstat'), 'custom_label_yes' => __('Side Menu','wp-slimstat'), 'custom_label_no' => __('Admin Bar','wp-slimstat') ),
28
+ 'add_posts_column' => array( 'description' => __('Add Stats to Posts and Pages','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Add a new column to the Edit Posts/Pages screens, with the number of hits per post in the last 365 days.','wp-slimstat') ),
29
+ 'posts_column_day_interval' => array( 'description' => __('Interval','wp-slimstat'), 'type' => 'integer', 'long_description' => __('Enter the time range, in days, that should be used to calculate the value here above.','wp-slimstat') ),
30
+ 'posts_column_pageviews' => array( 'description' => __('Report Type','wp-slimstat'), 'type' => 'yesno', 'long_description' => __('Select what kind of information you would like to see displayed on the Posts admin screen. Pageviews include all the hits regardless of the user, Unique IPs consider only one hit per user in the given time range.','wp-slimstat'), 'custom_label_yes' => __('Pageviews','wp-slimstat'), 'custom_label_no' => __('Unique IPs','wp-slimstat') ),
31
 
32
  'general_database_header' => array('description' => __('Database','wp-slimstat'), 'type' => 'section_header'),
33
  'auto_purge' => array( 'description' => __('Retain data for','wp-slimstat'), 'type' => 'integer', 'long_description' => __("Clean-up log entries older than the number of days specified here above. Enter <strong>0</strong> (number zero) if you want to preserve your data regardless of its age.",'wp-slimstat').(wp_get_schedule('wp_slimstat_purge')?' '.__('Next clean-up on','wp-slimstat').' <strong>'.date_i18n(get_option('date_format').', '.get_option('time_format'), wp_next_scheduled('wp_slimstat_purge')).'</strong>. '.sprintf(__('Entries logged on or before %s will be archived or deleted according to the option here below.','wp-slimstat'), date_i18n(get_option('date_format'), strtotime('-'.wp_slimstat::$options['auto_purge'].' days'))):''), 'after_input_field' => __('days','wp-slimstat') ),
admin/view/wp-slimstat-db-new.php ADDED
@@ -0,0 +1,881 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 $filter_names = array();
7
+ public static $filters_normalized = array();
8
+
9
+ // Number and date formats
10
+ public static $formats = array('decimal' => ',', 'thousand' => '.');
11
+
12
+ // Filters as SQL clauses
13
+ public static $sql_filters = array();
14
+
15
+ /*
16
+ * Initializes the environment, sets the filters
17
+ */
18
+ public static function init($_filters = ''){
19
+ // Decimal and thousand separators
20
+ if (wp_slimstat::$options['use_european_separators'] == 'no'){
21
+ self::$formats['decimal'] = '.';
22
+ self::$formats['thousand'] = ',';
23
+ }
24
+
25
+ // Filters are defined as: browser equals Chrome|country starts_with en
26
+ if (!is_string($_filters) || empty($_filters)){
27
+ $_filters = '';
28
+ }
29
+
30
+ // List of supported filters and their friendly names
31
+ self::$filter_names = array(
32
+ 'no_filter_selected_1' => '&nbsp;',
33
+ 'browser' => __('Browser','wp-slimstat'),
34
+ 'country' => __('Country Code','wp-slimstat'),
35
+ 'ip' => __('IP Address','wp-slimstat'),
36
+ 'searchterms' => __('Search Terms','wp-slimstat'),
37
+ 'language' => __('Language Code','wp-slimstat'),
38
+ 'platform' => __('Operating System','wp-slimstat'),
39
+ 'resource' => __('Permalink','wp-slimstat'),
40
+ 'domain' => __('Domain','wp-slimstat'),
41
+ 'referer' => __('Referer','wp-slimstat'),
42
+ 'user' => __('Visitor\'s Name','wp-slimstat'),
43
+ 'page_performance' => __('Page Speed','wp-slimstat'),
44
+ 'no_filter_selected_2' => '&nbsp;',
45
+ 'no_filter_selected_3' => __('-- Advanced filters --','wp-slimstat'),
46
+ 'plugins' => __('Browser Capabilities','wp-slimstat'),
47
+ 'version' => __('Browser Version','wp-slimstat'),
48
+ 'type' => __('Browser Type','wp-slimstat'),
49
+ 'user_agent' => __('User Agent','wp-slimstat'),
50
+ 'colordepth' => __('Color Depth','wp-slimstat'),
51
+ 'css_version' => __('CSS Version','wp-slimstat'),
52
+ 'notes' => __('Pageview Attributes','wp-slimstat'),
53
+ 'server_latency' => __('Server Latency','wp-slimstat'),
54
+ 'outbound_resource' => __('Outbound Link','wp-slimstat'),
55
+ 'author' => __('Post Author','wp-slimstat'),
56
+ 'category' => __('Post Category ID','wp-slimstat'),
57
+ 'other_ip' => __('Originating IP','wp-slimstat'),
58
+ 'content_type' => __('Resource Content Type','wp-slimstat'),
59
+ 'content_id' => __('Resource ID','wp-slimstat'),
60
+ 'resolution' => __('Screen Resolution','wp-slimstat'),
61
+ 'visit_id' => __('Visit ID','wp-slimstat'),
62
+
63
+ // The following filters will not be displayed in the dropdown
64
+ 'minute' => __('Minute','wp-slimstat'),
65
+ 'hour' => __('Hour','wp-slimstat'),
66
+ 'day' => __('Day','wp-slimstat'),
67
+ 'month' => __('Month','wp-slimstat'),
68
+ 'year' => __('Year','wp-slimstat'),
69
+ 'interval_direction' => __('+/-','wp-slimstat'),
70
+ 'interval' => __('days','wp-slimstat'),
71
+ 'interval_hours' => __('hours','wp-slimstat'),
72
+ 'interval_minutes' => __('minutes','wp-slimstat'),
73
+
74
+ 'direction' => __('Order Direction','wp-slimstat'),
75
+ 'limit_results' => __('Limit Results','wp-slimstat'),
76
+ 'start_from' => __('Start From','wp-slimstat'),
77
+
78
+ // Misc Filters
79
+ 'strtotime' => 0
80
+ );
81
+
82
+ // Hook for the... filters
83
+ $_filters = apply_filters('slimstat_db_pre_filters', $_filters);
84
+
85
+ // Normalize the input (filters)
86
+ self::$filters_normalized = self::parse_filters($_filters);
87
+
88
+ // Hook for the array of normalized filters
89
+ self::$filters_normalized = apply_filters('slimstat_db_filters_normalized', self::$filters_normalized, $_filters);
90
+
91
+ // Temporarily disable any filter on date_i18n
92
+ $date_i18n_filters = array();
93
+ if (!empty($GLOBALS['wp_filter']['date_i18n'])){
94
+ $date_i18n_filters = $GLOBALS['wp_filter']['date_i18n'];
95
+ remove_all_filters('date_i18n');
96
+ }
97
+
98
+ // Date and time ranges
99
+ if (empty(self::$filters_normalized['date']['interval']) && empty(self::$filters_normalized['date']['interval_hours']) && empty(self::$filters_normalized['date']['interval_minutes'])){
100
+ if (!empty(self::$filters_normalized['date']['minute'])){
101
+ self::$filters_normalized['utime']['start'] = mktime(
102
+ !empty(self::$filters_normalized['date']['hour'])?self::$filters_normalized['date']['hour']:0,
103
+ self::$filters_normalized['date']['minute'],
104
+ 0,
105
+ !empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'),
106
+ !empty(self::$filters_normalized['date']['day'])?self::$filters_normalized['date']['day']:date_i18n('j'),
107
+ !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')
108
+ );
109
+ self::$filters_normalized['utime']['end'] = self::$filters_normalized['utime']['start'] + 60;
110
+ self::$filters_normalized['utime']['type'] = 'H';
111
+ }
112
+ else if (!empty(self::$filters_normalized['date']['hour'])){
113
+ self::$filters_normalized['utime']['start'] = mktime(
114
+ self::$filters_normalized['date']['hour'],
115
+ 0,
116
+ 0,
117
+ !empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'),
118
+ !empty(self::$filters_normalized['date']['day'])?self::$filters_normalized['date']['day']:date_i18n('j'),
119
+ !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')
120
+ );
121
+ self::$filters_normalized['utime']['end'] = self::$filters_normalized['utime']['start'] + 3599;
122
+ self::$filters_normalized['utime']['type'] = 'H';
123
+ }
124
+ else if (!empty(self::$filters_normalized['date']['day'])){
125
+ self::$filters_normalized['utime']['start'] = mktime(
126
+ 0,
127
+ 0,
128
+ 0,
129
+ !empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'),
130
+ self::$filters_normalized['date']['day'],
131
+ !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')
132
+ );
133
+ self::$filters_normalized['utime']['end'] = self::$filters_normalized['utime']['start'] + 86399;
134
+ self::$filters_normalized['utime']['type'] = 'd';
135
+ }
136
+ else if(!empty(self::$filters_normalized['date']['year']) && empty(self::$filters_normalized['date']['month'])){
137
+ self::$filters_normalized['utime']['start'] = mktime(0, 0, 0, 1, 1, self::$filters_normalized['date']['year']);
138
+ self::$filters_normalized['utime']['end'] = mktime(0, 0, 0, 1, 1, self::$filters_normalized['date']['year']+1)-1;
139
+ self::$filters_normalized['utime']['type'] = 'Y';
140
+ }
141
+ else{
142
+ self::$filters_normalized['utime']['start'] = mktime(
143
+ 0,
144
+ 0,
145
+ 0,
146
+ !empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'),
147
+ 1,
148
+ !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')
149
+ );
150
+
151
+ self::$filters_normalized['utime']['end'] = strtotime(
152
+ (!empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')).'-'.
153
+ (!empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n')).
154
+ '-01 00:00 +1 month UTC'
155
+ )-1;
156
+ self::$filters_normalized['utime']['type'] = 'm';
157
+ }
158
+ }
159
+ else{ // An interval was specified
160
+ self::$filters_normalized['utime']['type'] = 'interval';
161
+
162
+ self::$filters_normalized['utime']['start'] = mktime(
163
+ !empty(self::$filters_normalized['date']['hour'])?self::$filters_normalized['date']['hour']:0,
164
+ !empty(self::$filters_normalized['date']['minute'])?self::$filters_normalized['date']['minute']:0,
165
+ 0,
166
+ !empty(self::$filters_normalized['date']['month'])?self::$filters_normalized['date']['month']:date_i18n('n'),
167
+ !empty(self::$filters_normalized['date']['day'])?self::$filters_normalized['date']['day']:date_i18n('j'),
168
+ !empty(self::$filters_normalized['date']['year'])?self::$filters_normalized['date']['year']:date_i18n('Y')
169
+ );
170
+
171
+ $sign = (self::$filters_normalized['date']['interval_direction'] == 'plus')?'+':'-';
172
+
173
+ self::$filters_normalized['utime']['end'] = self::$filters_normalized['utime']['start'] + intval($sign.(
174
+ (!empty(self::$filters_normalized['date']['interval'])?intval(self::$filters_normalized['date']['interval'] + 1):0) * 86400 +
175
+ (!empty(self::$filters_normalized['date']['interval_hours'])?intval(self::$filters_normalized['date']['interval_hours']):0) * 3600 +
176
+ (!empty(self::$filters_normalized['date']['interval_minutes'])?intval(self::$filters_normalized['date']['interval_minutes']):0) * 60
177
+ )) - 1;
178
+
179
+ // Swap boundaries if we're going back
180
+ if (self::$filters_normalized['date']['interval_direction'] == 'minus'){
181
+ list(self::$filters_normalized['utime']['start'], self::$filters_normalized['utime']['end']) = array(self::$filters_normalized['utime']['end'] + 86401, self::$filters_normalized['utime']['start'] + 86399);
182
+ }
183
+ }
184
+
185
+ // If end is in the future, set it to now
186
+ if (self::$filters_normalized['utime']['end'] > date_i18n('U')){
187
+ self::$filters_normalized['utime']['end'] = date_i18n('U');
188
+ }
189
+
190
+ // If start is after end, set it to first of month
191
+ if (self::$filters_normalized['utime']['start'] > self::$filters_normalized['utime']['end']){
192
+ self::$filters_normalized['utime']['start'] = mktime(
193
+ 0,
194
+ 0,
195
+ 0,
196
+ date_i18n('n', self::$filters_normalized['utime']['end']),
197
+ 1,
198
+ date_i18n('Y', self::$filters_normalized['utime']['end'])
199
+ );
200
+ self::$filters_normalized['date']['hour'] = self::$filters_normalized['date']['day'] = self::$filters_normalized['date']['month'] = self::$filters_normalized['date']['year'] = 0;
201
+ }
202
+
203
+ // Restore filters on date_i18n
204
+ foreach ($date_i18n_filters as $i18n_priority => $i18n_func_list) {
205
+ foreach ($i18n_func_list as $func_name => $func_args) {
206
+ add_filter('date_i8n', $func_args['function'], $i18n_priority, $func_args['accepted_args']);
207
+ }
208
+ }
209
+
210
+ // Now let's translate our filters into SQL clauses
211
+ self::$sql_filters = array(
212
+ 'table_info' => array(
213
+ 'tb' => array('slim_browsers', 'browser_id'),
214
+ 'tci' => array('slim_content_info', 'content_info_id'),
215
+ 'tob' => array('slim_outbound', 'outbound_id'),
216
+ 'tss' => array('slim_screenres', 'screenres_id'),
217
+ ),
218
+
219
+ 'from' => array(
220
+ 't1' => "{$GLOBALS['wpdb']->prefix}slim_stats t1",
221
+
222
+ 'all' => '',
223
+ 'all_others' => ''
224
+ ),
225
+
226
+ 'where' => array(
227
+ 't1' => '',
228
+ 'tb' => array('browser_id' => ''),
229
+ 'tci' => array('content_info_id' => ''),
230
+ 'tob' => array('outbound_id' => ''),
231
+ 'tss' => array('screenres_id' => ''),
232
+
233
+ 'all' => '',
234
+ 'time_range' => ' AND (t1.dt BETWEEN '.self::$filters_normalized['utime']['start'].' AND '.self::$filters_normalized['utime']['end'].')'
235
+ ),
236
+
237
+ 'id' => array(
238
+ 'tb' => '',
239
+ 'tci' => '',
240
+ 'tob' => '',
241
+ 'tss' => ''
242
+ )
243
+ );
244
+
245
+ foreach (self::$filters_normalized['columns'] as $a_filter_column => $a_filter_data){
246
+ // Add-ons can set their own custom filters, which are ignored here
247
+ if (strpos($a_filter_column, 'addon_') !== false){
248
+ continue;
249
+ }
250
+
251
+ $filter_empty = '0';
252
+
253
+ // Table this column belongs to
254
+ $table_alias = self::get_table_alias($a_filter_column);
255
+
256
+ // Some columns require a special treatment
257
+ switch($a_filter_column){
258
+ case 'ip':
259
+ case 'other_ip':
260
+ $a_filter_column = "INET_NTOA($a_filter_column)";
261
+ $filter_empty = '0.0.0.0';
262
+ break;
263
+ default:
264
+ $a_filter_column = $table_alias.'.'.$a_filter_column;
265
+ break;
266
+ }
267
+
268
+ switch ($a_filter_data[0]){
269
+ case 'is_not_equal_to':
270
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column <> %s", $a_filter_data[1]);
271
+ break;
272
+ case 'contains':
273
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column LIKE %s", '%'.$a_filter_data[1].'%');
274
+ break;
275
+ case 'includes_in_set':
276
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("FIND_IN_SET(%s, $a_filter_column) > 0", $a_filter_data[1]);
277
+ break;
278
+ case 'does_not_contain':
279
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column NOT LIKE %s", '%'.$a_filter_data[1].'%');;
280
+ break;
281
+ case 'starts_with':
282
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column LIKE %s", $a_filter_data[1].'%');
283
+ break;
284
+ case 'ends_with':
285
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column LIKE %s", '%'.$a_filter_data[1]);
286
+ break;
287
+ case 'sounds_like':
288
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("SOUNDEX($a_filter_column) = SOUNDEX(%s)", $a_filter_data[1]);
289
+ break;
290
+ case 'is_empty':
291
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = "($a_filter_column = '' OR $a_filter_column = '$filter_empty')";
292
+ break;
293
+ case 'is_not_empty':
294
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = "($a_filter_column <> '' AND $a_filter_column <> '$filter_empty')";
295
+ break;
296
+ case 'is_greater_than':
297
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column > %d", $a_filter_data[1]);
298
+ break;
299
+ case 'is_less_than':
300
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column < %d", $a_filter_data[1]);
301
+ break;
302
+ case 'between':
303
+ $range = explode(',', $a_filter_data[1]);
304
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column BETWEEN %d AND %d", $range[0], $range[1]);
305
+ break;
306
+ case 'matches':
307
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column REGEXP %s", $a_filter_data[1]);
308
+ break;
309
+ case 'does_not_match':
310
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column NOT REGEXP %s", $a_filter_data[1]);
311
+ break;
312
+ default:
313
+ self::$sql_filters['where'][$table_alias][$a_filter_column] = $GLOBALS['wpdb']->prepare("$a_filter_column = %s", $a_filter_data[1]);
314
+ }
315
+ }
316
+
317
+ // Get the IDs from the lookup tables
318
+ foreach (array_keys(self::$sql_filters['id']) as $a_table_alias){
319
+ if (!empty(self::$sql_filters['where'][$a_table_alias])){
320
+ self::$sql_filters['id'][$a_table_alias] = self::get_results('
321
+ SELECT *
322
+ FROM '.$GLOBALS['wpdb']->base_prefix.self::$sql_filters['table_info'][$a_table_alias][0].' '.$a_table_alias.'
323
+ WHERE '.implode(' AND ', self::$sql_filters['where'][$a_table_alias]));
324
+
325
+ if (!empty(self::$sql_filters['id'][$a_table_alias])){
326
+ $table_ids = array();
327
+ foreach (self::$sql_filters['id'][$a_table_alias] as $a_result){
328
+ $table_ids[] = $a_result[self::$sql_filters['table_info'][$a_table_alias][1]];
329
+ }
330
+ self::$sql_filters['where'][$a_table_alias][self::$sql_filters['table_info'][$a_table_alias][1]] = 't1.'.self::$sql_filters['table_info'][$a_table_alias][1].' IN ('.implode(',', $table_ids).')';
331
+ }
332
+ }
333
+ }
334
+
335
+ // self::$sql_filters['from']['all_others'] = trim(self::$sql_filters['from']['tb'].' '.self::$sql_filters['from']['tci'].' '.self::$sql_filters['from']['tob'].' '.self::$sql_filters['from']['tss']);
336
+ // self::$sql_filters['from']['all'] = "{$GLOBALS['wpdb']->prefix}slim_stats t1 ".self::$sql_filters['from']['all_others'];
337
+
338
+ self::$sql_filters['where']['all'] = trim(
339
+ self::$sql_filters['where']['t1'].' '.
340
+ self::$sql_filters['where']['tb']['browser_id'].' '.
341
+ self::$sql_filters['where']['tci']['content_info_id'].' '.
342
+ self::$sql_filters['where']['tob']['outbound_id'].' '.
343
+ self::$sql_filters['where']['tss']['screenres_id']);
344
+ }
345
+ // end init
346
+
347
+ // The following methods retrieve the information from the database
348
+
349
+ public static function count_bouncing_pages(){
350
+ return intval(self::get_var('
351
+ SELECT COUNT(*) counthits
352
+ FROM (
353
+ SELECT t1.resource
354
+ FROM '.self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from('tci.content_type').'
355
+ WHERE t1.visit_id <> 0 AND t1.resource <> "" AND tci.content_type <> "404" '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
356
+ GROUP BY visit_id
357
+ HAVING COUNT(visit_id) = 1
358
+ ) as ts1',
359
+ 'SUM(counthits) AS counthits'));
360
+ }
361
+
362
+ public static function count_exit_pages(){
363
+ return intval(self::get_var('
364
+ SELECT COUNT(*) counthits
365
+ FROM (
366
+ SELECT resource, visit_id, dt
367
+ FROM '.self::$sql_filters['from']['all'].'
368
+ WHERE visit_id > 0 AND resource <> "" '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
369
+ GROUP BY visit_id
370
+ HAVING dt = MAX(dt)
371
+ ) AS ts1',
372
+ 'SUM(counthits) AS counthits'));
373
+ }
374
+
375
+ public static function count_records($_where_clause = '1=1', $_distinct_column = '*', $_use_filters = true, $_use_date_filters = true, $_join_tables = ''){
376
+ $column = ($_distinct_column != '*')?"DISTINCT $_distinct_column":$_distinct_column;
377
+ return intval(self::get_var("
378
+ SELECT COUNT($column) counthits
379
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 ".($_use_filters?self::$sql_filters['from']['all_others']:'').' '.self::_add_filters_to_sql_from($_where_clause.$_join_tables).'
380
+ WHERE '.(!empty($_where_clause)?$_where_clause:'1=1').' '.($_use_filters?self::$sql_filters['where']['all']:'').' '.($_use_date_filters?self::$sql_filters['where']['time_range']:''),
381
+ 'SUM(counthits) AS counthits'));
382
+ }
383
+
384
+ public static function count_outbound(){
385
+ return intval(self::get_var("
386
+ SELECT COUNT(outbound_id) counthits
387
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id ".self::$sql_filters['from']['all_others']."
388
+ WHERE 1=1 ".self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'],
389
+ 'SUM(counthits) AS counthits'));
390
+ }
391
+
392
+ public static function count_records_having($_where_clause = '1=1', $_column = 't1.ip', $_having_clause = ''){
393
+ return intval(self::get_var("
394
+ SELECT COUNT(*) counthits FROM (
395
+ SELECT $_column
396
+ FROM ".self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_where_clause)."
397
+ WHERE $_where_clause ".self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range']."
398
+ GROUP BY $_column
399
+ ".(!empty($_having_clause)?"HAVING $_having_clause":'').')
400
+ AS ts1',
401
+ 'SUM(counthits) AS counthits'));
402
+ }
403
+
404
+ public static function get_data_size(){
405
+ $suffix = 'KB';
406
+
407
+ $sql = 'SHOW TABLE STATUS LIKE "'.$GLOBALS['wpdb']->prefix.'slim_stats"';
408
+ $table_details = wp_slimstat::$wpdb->get_row($sql, 'ARRAY_A', 0);
409
+
410
+ $table_size = ( $table_details['Data_length'] / 1024 ) + ( $table_details['Index_length'] / 1024 );
411
+
412
+ if ($table_size > 1024){
413
+ $table_size /= 1024;
414
+ $suffix = 'MB';
415
+ }
416
+ return number_format($table_size, 2, self::$formats['decimal'], self::$formats['thousand']).' '.$suffix;
417
+ }
418
+
419
+ public static function get_max_and_average_pages_per_visit(){
420
+ return self::get_results('
421
+ SELECT AVG(ts1.counthits) AS avghits, MAX(ts1.counthits) AS maxhits FROM (
422
+ SELECT count(ip) counthits, visit_id
423
+ FROM '.self::$sql_filters['from']['all'].'
424
+ WHERE visit_id > 0 '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
425
+ GROUP BY visit_id
426
+ ) AS ts1',
427
+ 'blog_id',
428
+ '',
429
+ '',
430
+ 'AVG(avghits) AS avghits, MAX(maxhits) AS maxhits');
431
+ }
432
+
433
+ public static function get_oldest_visit($_where_clause = '1=1', $_use_filters = true){
434
+ return self::get_var("
435
+ SELECT t1.dt
436
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 ".($_use_filters?self::$sql_filters['from']['all_others']:'').' '.self::_add_filters_to_sql_from($_where_clause).'
437
+ WHERE '.(!empty($_where_clause)?$_where_clause:'1=1').' '.($_use_filters?self::$sql_filters['where']['all']:'').'
438
+ ORDER BY dt ASC
439
+ LIMIT 0,1',
440
+ 'MIN(dt)');
441
+ }
442
+
443
+ public static function get_popular($_column = 't1.id', $_custom_where = '', $_more_columns = '', $_having_clause = '', $_as_column = ''){
444
+ return self::get_results("
445
+ SELECT $_column ".(!empty($_as_column)?'AS '.$_as_column:'')." $_more_columns, COUNT(*) counthits
446
+ FROM ".self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_column.$_custom_where.$_more_columns).'
447
+ WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range']."
448
+ GROUP BY $_column $_having_clause
449
+ ORDER BY counthits ".self::$filters_normalized['misc']['direction']."
450
+ LIMIT ".self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
451
+ (!empty($_as_column)?$_as_column:$_column).' '.$_more_columns.', blog_id',
452
+ 'counthits '.self::$filters_normalized['misc']['direction'],
453
+ "$_column $_more_columns",
454
+ 'SUM(counthits) AS counthits');
455
+ }
456
+
457
+ public static function get_popular_outbound(){
458
+ return self::get_results("
459
+ SELECT tob.outbound_resource as resource, COUNT(*) counthits
460
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id ".self::$sql_filters['from']['tb'].' '.self::$sql_filters['from']['tss'].' '.self::$sql_filters['from']['tci']."
461
+ WHERE (tob.type = 0 OR tob.type = 1) ".self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
462
+ GROUP BY tob.outbound_resource
463
+ ORDER BY counthits '.self::$filters_normalized['misc']['direction'].'
464
+ LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
465
+ 'blog_id, resource',
466
+ 'counthits '.self::$filters_normalized['misc']['direction'],
467
+ 'outbound_resource',
468
+ 'SUM(counthits) AS counthits');
469
+ }
470
+
471
+ public static function get_popular_complete($_column = 't1.id', $_custom_where = '', $_join_tables = '', $_having_clause = '', $_outer_select_column = '', $_max_min = 'MAX'){
472
+ $column_for_select = empty($_outer_select_column)?$_column:$_outer_select_column;
473
+ return self::get_results("
474
+ SELECT $column_for_select, ts1.maxid, COUNT(*) counthits
475
+ FROM (
476
+ SELECT $_column, $_max_min(t1.id) maxid
477
+ FROM ".self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_column.$_custom_where).'
478
+ WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range']."
479
+ GROUP BY $_column $_having_clause
480
+ ) AS ts1 JOIN {$GLOBALS['wpdb']->prefix}slim_stats t1 ON ts1.maxid = t1.id ".
481
+ (!empty($_join_tables)?self::_add_filters_to_sql_from($_join_tables):'')."
482
+ GROUP BY $column_for_select
483
+ ORDER BY counthits ".self::$filters_normalized['misc']['direction'].'
484
+ LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
485
+ $column_for_select,
486
+ 'counthits '.self::$filters_normalized['misc']['direction'],
487
+ $column_for_select,
488
+ 'MAX(maxid), SUM(counthits)');
489
+ }
490
+
491
+ public static function get_recent($_column = 't1.id', $_custom_where = '', $_more_columns = '', $_having_clause = '', $_order_by = '', $_use_date_filters = true){
492
+ if ($_column == 't1.id'){
493
+ return self::get_results('
494
+ SELECT t1.*'.(!empty($_more_columns)?', '.$_more_columns:'').'
495
+ FROM '.self::$sql_filters['from']['t1'].'
496
+ WHERE '.(empty($_custom_where)?"$_column <> 0 ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.($_use_date_filters?self::$sql_filters['where']['time_range']:'').'
497
+ ORDER BY '.(empty($_order_by)?'t1.dt '.self::$filters_normalized['misc']['direction']:$_order_by).'
498
+ LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
499
+ '*',
500
+ empty($_order_by)?'t1.dt '.self::$filters_normalized['misc']['direction']:$_order_by);
501
+
502
+ }
503
+ else{
504
+ $table_alias = self::get_table_alias($_column);
505
+ if ($table_alias != 't1'){
506
+ $group_by = self::$sql_filters['table_info'][$table_alias][1];
507
+ }
508
+
509
+ $where_ids = self::_add_ids_to_sql_where($_custom_where, $_column);
510
+ // WHERE '.(empty($_custom_where)?"$_column <> '' ":$_custom_where).' '.self::$sql_filters['where']['all'].' '.($_use_date_filters?self::$sql_filters['where']['time_range']:'')."
511
+
512
+
513
+ return self::get_results('
514
+ SELECT t1.*
515
+ FROM (
516
+ SELECT MAX(t1.id) maxid
517
+ FROM '.self::$sql_filters['from']['t1'].'
518
+ WHERE '.$where_ids.' '.($_use_date_filters?self::$sql_filters['where']['time_range']:'').'
519
+ GROUP BY '.$group_by.' '.
520
+ $_having_clause.'
521
+ ) AS ts1 INNER JOIN '.$GLOBALS['wpdb']->prefix.'slim_stats t1 ON ts1.maxid = t1.id '.
522
+ (!empty($_more_columns)?self::_add_filters_to_sql_from($_more_columns):'').'
523
+ ORDER BY '.(empty($_order_by)?'t1.dt '.self::$filters_normalized['misc']['direction']:$_order_by).'
524
+ LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
525
+ 't1.*, '.(!empty($_more_columns)?$_more_columns:'ts1.maxid'),
526
+ empty($_order_by)?'t1.dt '.self::$filters_normalized['misc']['direction']:$_order_by);
527
+ }
528
+ }
529
+
530
+ public static function get_recent_outbound($_type = -1){
531
+ return self::get_results("
532
+ SELECT tob.outbound_id as visit_id, tob.outbound_domain, tob.outbound_resource as resource, tob.type, tob.notes, t1.ip, t1.other_ip, t1.user, 'local' as domain, t1.resource as referer, t1.country, tb.browser, tb.version, tb.platform, tob.dt
533
+ FROM {$GLOBALS['wpdb']->prefix}slim_stats t1 INNER JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON tob.id = t1.id INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers tb on t1.browser_id = tb.browser_id ".self::$sql_filters['from']['tci'].' '.self::$sql_filters['from']['tss'].'
534
+ WHERE '.(($_type != -1)?"tob.type = $_type":'tob.type > 1').' '.self::$sql_filters['where']['all'].' '.self::$sql_filters['where']['time_range'].'
535
+ ORDER BY tob.dt '.self::$filters_normalized['misc']['direction'].'
536
+ LIMIT '.self::$filters_normalized['misc']['start_from'].', '.self::$filters_normalized['misc']['limit_results'],
537
+ 'blog_id, visit_id, outbound_domain, resource, type, notes, ip, other_ip, user, domain, referer, country, browser, platform, dt',
538
+ 'dt '.self::$filters_normalized['misc']['direction']);
539
+ }
540
+
541
+ public static function get_data_for_chart($_data1, $_data2, $_custom_where_clause = '', $_sql_from_where = ''){
542
+ $previous = array('end' => self::$filters_normalized['utime']['start'] - 1);
543
+ $label_date_format = '';
544
+ $output = array();
545
+
546
+ // Each type has its own parameters
547
+ switch (self::$filters_normalized['utime']['type']){
548
+ case 'H':
549
+ $previous['start'] = self::$filters_normalized['utime']['start'] - 3600;
550
+ $label_date_format = wp_slimstat::$options['time_format'];
551
+ $group_by = array('HOUR', 'MINUTE', 'i');
552
+ $values_in_interval = array(59, 59, 0, 60);
553
+ break;
554
+ case 'd':
555
+ $previous['start'] = self::$filters_normalized['utime']['start'] - 86400;
556
+ $label_date_format = (self::$formats['decimal'] == '.')?'m/d':'d/m';
557
+ $group_by = array('DAY', 'HOUR', 'G');
558
+ $values_in_interval = array(23, 23, 0, 3600);
559
+ break;
560
+ case 'Y':
561
+ $previous['start'] = mktime(0, 0, 0, 1, 1, self::$filters_normalized['date']['year']-1);
562
+ $label_date_format = 'Y';
563
+ $group_by = array('YEAR', 'MONTH', 'n');
564
+ $values_in_interval = array(12, 12, 1, 2678400);
565
+ break;
566
+ case 'interval':
567
+ $group_by = array('MONTH', 'DAY', 'j');
568
+ $values_in_interval = array(abs(self::$filters_normalized['date']['interval']), abs(self::$filters_normalized['date']['interval']), 0, 86400);
569
+ break;
570
+ default:
571
+ $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'));
572
+ $label_date_format = 'm/Y';
573
+ $group_by = array('MONTH', 'DAY', 'j');
574
+ $values_in_interval = array(date('t', $previous['start']), date('t', self::$filters_normalized['utime']['start']), 1, 86400);
575
+ break;
576
+ }
577
+
578
+ // Custom intervals don't have a comparison chart ('previous' range)
579
+ $time_range = self::$sql_filters['where']['time_range'];
580
+ if (empty(self::$filters_normalized['date']['interval'])){
581
+ $time_range = 'AND (t1.dt BETWEEN '.$previous['start'].' AND '.$previous['end'].' OR t1.dt BETWEEN '.self::$filters_normalized['utime']['start'].' AND '.self::$filters_normalized['utime']['end'].')';
582
+ }
583
+
584
+ // Build the SQL query
585
+ $sql = "SELECT t1.dt, $_data1 first_metric, $_data2 second_metric";
586
+
587
+ // Panel 4 has a slightly different structure
588
+ if(empty($_sql_from_where)){
589
+ $sql .= ' FROM '.self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_data1.$_data2.$_custom_where_clause)."
590
+ WHERE 1=1 $time_range ".self::$sql_filters['where']['all'].' '.$_custom_where_clause;
591
+ }
592
+ else{
593
+ $sql_no_placeholders = str_replace('[from_tables]', self::$sql_filters['from']['all'].' '.self::_add_filters_to_sql_from($_data1.$_data2.$_custom_where_clause), $_sql_from_where);
594
+ $sql_no_placeholders = str_replace('[where_clause]', '1=1 '.$time_range.' '.self::$sql_filters['where']['all'].' '.$_custom_where_clause, $sql_no_placeholders);
595
+ $sql .= $sql_no_placeholders;
596
+ }
597
+
598
+ $group_by_string = "{$group_by[0]}(CONVERT_TZ(FROM_UNIXTIME(t1.dt), @@session.time_zone, '+00:00')), {$group_by[1]}(CONVERT_TZ(FROM_UNIXTIME(t1.dt), @@session.time_zone, '+00:00'))";
599
+ $sql .= " GROUP BY $group_by_string";
600
+
601
+ // Get the data
602
+ $results = self::get_results($sql, 'blog_id', '', $group_by_string, 'SUM(first_metric) AS first_metric, SUM(second_metric) AS second_metric');
603
+
604
+ // Fill the output array
605
+ $output['current']['label'] = '';
606
+ if (!empty($label_date_format)){
607
+ $output['current']['label'] = gmdate($label_date_format, self::$filters_normalized['utime']['start']);
608
+ $output['previous']['label'] = gmdate($label_date_format, $previous['start']);
609
+ }
610
+
611
+ $output['previous']['first_metric'] = array_fill($values_in_interval[2], $values_in_interval[0], 0);
612
+ $output['previous']['second_metric'] = array_fill($values_in_interval[2], $values_in_interval[0], 0);
613
+
614
+
615
+ $today_limit = floatval(date_i18n('Ymd.Hi'));
616
+ for ($i = $values_in_interval[2]; $i <= $values_in_interval[1]; $i++){
617
+ // Do not include dates in the future
618
+
619
+ $floatval = floatval(date('Ymd.Hi', wp_slimstat_db::$filters_normalized['utime']['start'] + ( ($i - $values_in_interval[2]) * $values_in_interval[3])));
620
+
621
+ if ($floatval > $today_limit){
622
+ continue;
623
+ }
624
+
625
+ $output['current']['first_metric'][$i] = 0;
626
+ $output['current']['second_metric'][$i] = 0;
627
+ }
628
+
629
+ // No data? No problem!
630
+ if (!is_array($results) || empty($results)){
631
+ return ($output);
632
+ }
633
+
634
+ // Rearrange the data and then format it for Flot
635
+ foreach ($results as $i => $a_result){
636
+ $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']);
637
+
638
+ if (empty(self::$filters_normalized['date']['interval']) && gmdate(self::$filters_normalized['utime']['type'], $a_result['dt']) == gmdate(self::$filters_normalized['utime']['type'], $previous['start'])){
639
+ $output['previous']['first_metric'][$index] = $a_result['first_metric'];
640
+ $output['previous']['second_metric'][$index] = $a_result['second_metric'];
641
+ }
642
+ 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'])){
643
+ $output['current']['first_metric'][$index] = $a_result['first_metric'];
644
+ $output['current']['second_metric'][$index] = $a_result['second_metric'];
645
+ }
646
+ }
647
+
648
+ return ($output);
649
+ }
650
+
651
+ public static function parse_filters($_filters = '', $_init_misc = true){
652
+ $filters_normalized = array(
653
+ 'columns' => array(),
654
+ 'date' => array(
655
+ 'interval_direction' => '',
656
+ 'is_past' => false
657
+ ),
658
+ 'misc' => $_init_misc?array(
659
+ 'direction' => 'desc',
660
+ 'limit_results' => wp_slimstat::$options['rows_to_show'],
661
+ 'start_from' => 0
662
+ ):array(),
663
+ 'utime' => array(
664
+ 'start' => 0,
665
+ 'end' => 0,
666
+ 'type' => 'm'
667
+ )
668
+ );
669
+
670
+ if (!empty($_filters)){
671
+ $matches = explode('&&&', $_filters);
672
+
673
+ foreach($matches as $idx => $a_match){
674
+ preg_match('/([^\s]+)\s([^\s]+)\s(.+)?/', urldecode($a_match), $a_filter);
675
+
676
+ if (empty($a_filter) || ((!array_key_exists($a_filter[1], self::$filter_names) || strpos($a_filter[1], 'no_filter') !== false) && strpos($a_filter[1], 'addon_') === false)){
677
+ continue;
678
+ }
679
+
680
+ switch($a_filter[1]){
681
+ case 'strtotime':
682
+ $custom_date = strtotime($a_filter[3].' UTC');
683
+ $filters_normalized['date']['day'] = date('j', $custom_date);
684
+ $filters_normalized['date']['month'] = date('n', $custom_date);
685
+ $filters_normalized['date']['year'] = date('Y', $custom_date);
686
+ break;
687
+ case 'minute':
688
+ case 'hour':
689
+ case 'day':
690
+ case 'month':
691
+ case 'year':
692
+ if (is_numeric($a_filter[3])){
693
+ $filters_normalized['date'][$a_filter[1]] = intval($a_filter[3]);
694
+ }
695
+ else{
696
+ // Try to apply strtotime to value
697
+ switch($a_filter[1]){
698
+ case 'minute':
699
+ $filters_normalized['date']['minute'] = date('i', strtotime($a_filter[3], date_i18n('U')));
700
+ $filters_normalized['date']['is_past'] = true;
701
+ break;
702
+ case 'hour':
703
+ $filters_normalized['date']['hour'] = date('H', strtotime($a_filter[3], date_i18n('U')));
704
+ $filters_normalized['date']['is_past'] = true;
705
+ break;
706
+ case 'day':
707
+ $filters_normalized['date']['day'] = date('j', strtotime($a_filter[3], date_i18n('U')));
708
+ break;
709
+ case 'month':
710
+ $filters_normalized['date']['month'] = date('n', strtotime($a_filter[3], date_i18n('U')));
711
+ break;
712
+ case 'year':
713
+ $filters_normalized['date']['year'] = date('Y', strtotime($a_filter[3], date_i18n('U')));
714
+ break;
715
+ default:
716
+ break;
717
+ }
718
+
719
+ if ($filters_normalized['date'][$a_filter[1]] === false){
720
+ unset($filters_normalized['date'][$a_filter[1]]);
721
+ }
722
+ }
723
+
724
+ switch($a_filter[1]){
725
+ case 'day':
726
+ if ($filters_normalized['date']['day'] != date_i18n('j')){
727
+ $filters_normalized['date']['is_past'] = true;
728
+ }
729
+ break;
730
+ case 'month':
731
+ if ($filters_normalized['date']['month'] != date_i18n('n')){
732
+ $filters_normalized['date']['is_past'] = true;
733
+ }
734
+ break;
735
+ case 'year':
736
+ if ($filters_normalized['date']['year'] != date_i18n('Y')){
737
+ $filters_normalized['date']['is_past'] = true;
738
+ }
739
+ break;
740
+ default:
741
+ break;
742
+ }
743
+
744
+ break;
745
+ case 'interval':
746
+ case 'interval_hours':
747
+ case 'interval_minutes':
748
+ $intval_filter = intval($a_filter[3]);
749
+ $filters_normalized['date'][$a_filter[1]] = abs($intval_filter);
750
+ if ($intval_filter < 0){
751
+ $filters_normalized['date']['interval_direction'] = 'minus';
752
+ }
753
+ break;
754
+ case 'interval_direction':
755
+ $filters_normalized['date'][$a_filter[1]] = in_array($a_filter[3], array('plus', 'minus'))?$a_filter[3]:'plus';
756
+ break;
757
+ case 'direction':
758
+ case 'limit_results':
759
+ case 'start_from':
760
+ $filters_normalized['misc'][$a_filter[1]] = str_replace('\\', '', htmlspecialchars_decode($a_filter[3]));
761
+ break;
762
+ default:
763
+ $filters_normalized['columns'][$a_filter[1]] = array($a_filter[2], isset($a_filter[3])?str_replace('\\', '', htmlspecialchars_decode($a_filter[3])):'');
764
+ break;
765
+ }
766
+ }
767
+ }
768
+
769
+ return $filters_normalized;
770
+ }
771
+
772
+ /**
773
+ * Associates tables and their 'SQL aliases'
774
+ */
775
+ public static function get_table_alias($_field = 'id'){
776
+ switch($_field){
777
+ case 'browser_id':
778
+ case 'browser':
779
+ case 'version':
780
+ case 'css_version':
781
+ case 'type':
782
+ case 'platform':
783
+ case 'user_agent':
784
+ return 'tb';
785
+ break;
786
+ case 'content_info_id':
787
+ case 'author':
788
+ case 'category':
789
+ case 'content_type':
790
+ case 'content_id':
791
+ return 'tci';
792
+ break;
793
+ case 'outbound_id':
794
+ case 'outbound_domain':
795
+ case 'outbound_resource':
796
+ case 'position':
797
+ return 'tob';
798
+ break;
799
+ case 'screenres_id':
800
+ case 'resolution':
801
+ case 'colordepth':
802
+ case 'antialias':
803
+ return 'tss';
804
+ break;
805
+ default:
806
+ return 't1';
807
+ break;
808
+ }
809
+ }
810
+ // end get_table_alias
811
+
812
+ public static function get_col($_sql = ''){
813
+ $_sql = apply_filters('slimstat_get_col_sql', $_sql);
814
+
815
+ if (wp_slimstat::$options['show_sql_debug'] == 'yes'){
816
+ self::_show_debug($_sql);
817
+ }
818
+
819
+ return wp_slimstat::$wpdb->get_col($_sql);
820
+ }
821
+
822
+ public static function get_results($_sql = '', $_select_no_aggregate_values = '', $_order_by = '', $_group_by = '', $_aggregate_values_add = ''){
823
+ $_sql = apply_filters('slimstat_get_results_sql', $_sql, $_select_no_aggregate_values, $_order_by, $_group_by, $_aggregate_values_add);
824
+
825
+ if (wp_slimstat::$options['show_sql_debug'] == 'yes'){
826
+ self::_show_debug($_sql);
827
+ }
828
+
829
+ return wp_slimstat::$wpdb->get_results($_sql, ARRAY_A);
830
+ }
831
+
832
+ public static function get_var($_sql = '', $_aggregate_value = ''){
833
+ $_sql = apply_filters('slimstat_get_var_sql', $_sql, $_aggregate_value);
834
+
835
+ if (wp_slimstat::$options['show_sql_debug'] == 'yes'){
836
+ self::_show_debug($_sql);
837
+ }
838
+
839
+ return wp_slimstat::$wpdb->get_var($_sql);
840
+ }
841
+
842
+ protected static function _add_filters_to_sql_from($_sql_tables = '', $_ignore_empty = false){
843
+ $sql_from = '';
844
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tb'])) && strpos($_sql_tables, 'tb.') !== false)
845
+ $sql_from .= " INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_browsers tb ON t1.browser_id = tb.browser_id";
846
+
847
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tci'])) && strpos($_sql_tables, 'tci.') !== false)
848
+ $sql_from .= " INNER JOIN {$GLOBALS['wpdb']->base_prefix}slim_content_info tci ON t1.content_info_id = tci.content_info_id";
849
+
850
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tob'])) && strpos($_sql_tables, 'tob.') !== false)
851
+ $sql_from .= " LEFT JOIN {$GLOBALS['wpdb']->prefix}slim_outbound tob ON t1.id = tob.id";
852
+
853
+ if (($_ignore_empty || empty(self::$sql_filters['from']['tss'])) && strpos($_sql_tables, 'tss.') !== false)
854
+ $sql_from .= " LEFT JOIN {$GLOBALS['wpdb']->base_prefix}slim_screenres tss ON t1.screenres_id = tss.screenres_id";
855
+
856
+ return $sql_from;
857
+ }
858
+
859
+ protected static function _get_lookup_rows($_where = array(), $_group_by_column = ''){
860
+ foreach ($_custom_where as $a_table_column => $a_filter_column){
861
+ $table_alias = self::get_table_alias($a_table_column);
862
+
863
+ $where_ids = self::get_results('
864
+ SELECT *
865
+ FROM '.$GLOBALS['wpdb']->base_prefix.self::$sql_filters['table_info'][$a_table_alias][0].' '.$a_table_alias.'
866
+ WHERE 1=1'.self::$sql_filters['where'][$a_table_alias]);
867
+
868
+ if (!empty(self::$sql_filters['id'][$a_table_alias])){
869
+ $table_ids = array();
870
+ foreach (self::$sql_filters['id'][$a_table_alias] as $a_result){
871
+ $table_ids[] = $a_result[self::$sql_filters['table_info'][$a_table_alias][1]];
872
+ }
873
+ self::$sql_filters['where'][$a_table_alias] = ' AND t1.'.self::$sql_filters['table_info'][$a_table_alias][1].' IN ('.implode(',', $table_ids).')';
874
+ }
875
+ }
876
+ }
877
+
878
+ protected static function _show_debug($_message = ''){
879
+ echo "<p class='debug'>$_message</p>";
880
+ }
881
+ }
admin/wp-slimstat-admin.php CHANGED
@@ -12,9 +12,8 @@ class wp_slimstat_admin{
12
  */
13
  public static function init(){
14
  if ((wp_slimstat::$options['enable_ads_network'] == 'yes' || wp_slimstat::$options['enable_ads_network'] == 'no')){
15
- self::$admin_notice = "The team who manages the WordPress Plugin Repository notified us that since the <a href='http://dev.maxmind.com/geoip/legacy/geolite/' target='_blank'>MaxMind GeoLite library</a> used by Slimstat to geolocate visitors is not released under the GNU General Public License (or compatible), it violates the repository guidelines, and cannot be shipped with the plugin any longer. We were required to remove the code and alter the plugin so that this functionality becomes optional. We apologize for the inconvenience, but we need to deactivate the geolocation functionality starting from now. The only consequence is that your visitors' country will not be identified for now. Everything else will still work as usual. As a temporary workaround, you can download the geolocation file from our <a href='https://github.com/getusedtoit/wp-slimstat/blob/master/databases/maxmind.dat?raw=true' target='_blank'>github repository</a> and upload it to <code>wp-content/plugins/wp-slimstat/databases</code>. We are already at work to find an alternative solution that satisfies the repo's rules and regulations. Stay tuned.";
16
 
17
- // 3.9.8 self::$admin_notice = "We've been working on the documentation for Slimstat. Now you can find <a href='https://slimstat.freshdesk.com/support/solutions' target='_blank'>detailed information</a> about all the actions and filters available in our source code. Developers, use them to build your own custom add-ons! We are also adding video tours of our add-ons, and organizing the source code to make it easier to read and understand.";
18
  // 3.9.9 self::$admin_notice = "Happy birthday, Slimstat. Nine years ago version 0.8.7 was released to the public, starting the unbelievable journey that has taken us here today. We would like to thank all the 25,000 users who appreciate and support our work in many great ways. We wouldn't have more than 1.3 million downloads, 4.8 out of 5 overall rating, 16 add-ons, video tutorials, etc... if it weren't for you!";
19
  }
20
  else {
@@ -613,7 +612,16 @@ class wp_slimstat_admin{
613
  * Adds a new column header to the Posts panel (to show the number of pageviews for each post)
614
  */
615
  public static function add_column_header($_columns){
616
- $_columns['wp-slimstat'] = '<span class="slimstat-icon" title="'.__('Pageviews in the last 365 days','wp-slimstat').'"></span>';
 
 
 
 
 
 
 
 
 
617
  return $_columns;
618
  }
619
  // end add_comment_column_header
@@ -626,12 +634,22 @@ class wp_slimstat_admin{
626
 
627
  include_once(dirname(__FILE__).'/view/wp-slimstat-reports.php');
628
  wp_slimstat_reports::init();
629
-
 
 
 
 
630
  $parsed_permalink = parse_url( get_permalink($_post_id) );
631
  $parsed_permalink = $parsed_permalink['path'].(!empty($parsed_permalink['query'])?'?'.$parsed_permalink['query']:'');
632
- wp_slimstat_db::init('resource contains '.$parsed_permalink.'&&&hour equals 0&&&day equals '.date_i18n('d').'&&&month equals '.date_i18n('m').'&&&year equals '.date_i18n('Y').'&&&interval equals 365&&&interval_direction equals minus');
633
- $count = wp_slimstat_db::count_records();
634
- echo '<a href="'.wp_slimstat_reports::fs_url("resource contains $parsed_permalink&&&day equals ".date_i18n('d').'&&&month equals '.date_i18n('m').'&&&year equals '.date_i18n('Y').'&&&interval equals 365&&&interval_direction equals minus').'">'.$count.'</a>';
 
 
 
 
 
 
635
  }
636
  // end add_column
637
 
12
  */
13
  public static function init(){
14
  if ((wp_slimstat::$options['enable_ads_network'] == 'yes' || wp_slimstat::$options['enable_ads_network'] == 'no')){
15
+ self::$admin_notice = "The team who manages the WordPress Plugin Repository notified us that since the <a href='http://dev.maxmind.com/geoip/legacy/geolite/' target='_blank'>MaxMind GeoLite library</a> used by Slimstat to geolocate visitors is released under the Creative Commons BY-SA 3.0 license, it violates the repository guidelines, and cannot be bundled with the plugin any longer. We were required to remove the code and alter the plugin so that this functionality becomes optional. We apologize for the inconvenience. However, the only immediate consequence is that your visitors' country will not be identified; everything else will still work as usual. You can download the geolocation DB as a <a href='http://slimstat.getused.to.it/downloads/get-country/' target='_blank'>separate add-on</a> on our store, free of charge. Don't forget to enter your license key in the corresponding field under Slimstat > Add-ons, to receive free updates!";
16
 
 
17
  // 3.9.9 self::$admin_notice = "Happy birthday, Slimstat. Nine years ago version 0.8.7 was released to the public, starting the unbelievable journey that has taken us here today. We would like to thank all the 25,000 users who appreciate and support our work in many great ways. We wouldn't have more than 1.3 million downloads, 4.8 out of 5 overall rating, 16 add-ons, video tutorials, etc... if it weren't for you!";
18
  }
19
  else {
612
  * Adds a new column header to the Posts panel (to show the number of pageviews for each post)
613
  */
614
  public static function add_column_header($_columns){
615
+ if (wp_slimstat::$options['posts_column_day_interval'] == 0){
616
+ wp_slimstat::$options['posts_column_day_interval'] = 30;
617
+ }
618
+
619
+ if (wp_slimstat::$options['posts_column_pageviews'] == 'yes'){
620
+ $_columns['wp-slimstat'] = '<span class="slimstat-icon" title="'.__('Pageviews in the last '.wp_slimstat::$options['posts_column_day_interval'].' days','wp-slimstat').'"></span>';
621
+ }
622
+ else{
623
+ $_columns['wp-slimstat'] = '<span class="slimstat-icon" title="'.__('Unique IPs in the last '.wp_slimstat::$options['posts_column_day_interval'].' days','wp-slimstat').'"></span>';
624
+ }
625
  return $_columns;
626
  }
627
  // end add_comment_column_header
634
 
635
  include_once(dirname(__FILE__).'/view/wp-slimstat-reports.php');
636
  wp_slimstat_reports::init();
637
+
638
+ if (wp_slimstat::$options['posts_column_day_interval'] == 0){
639
+ wp_slimstat::$options['posts_column_day_interval'] = 30;
640
+ }
641
+
642
  $parsed_permalink = parse_url( get_permalink($_post_id) );
643
  $parsed_permalink = $parsed_permalink['path'].(!empty($parsed_permalink['query'])?'?'.$parsed_permalink['query']:'');
644
+ wp_slimstat_db::init('resource contains '.$parsed_permalink.'&&&hour equals 0&&&day equals '.date_i18n('d').'&&&month equals '.date_i18n('m').'&&&year equals '.date_i18n('Y').'&&&interval equals '.wp_slimstat::$options['posts_column_day_interval'].'&&&interval_direction equals minus');
645
+
646
+ if (wp_slimstat::$options['posts_column_pageviews'] == 'yes'){
647
+ $count = wp_slimstat_db::count_records();
648
+ }
649
+ else{
650
+ $count = wp_slimstat_db::count_records('1=1', 't1.ip');
651
+ }
652
+ echo '<a href="'.wp_slimstat_reports::fs_url("resource contains $parsed_permalink&&&day equals ".date_i18n('d').'&&&month equals '.date_i18n('m').'&&&year equals '.date_i18n('Y').'&&&interval equals '.wp_slimstat::$options['posts_column_day_interval'].'&&interval_direction equals minus').'">'.$count.'</a>';
653
  }
654
  // end add_column
655
 
readme.txt CHANGED
@@ -4,10 +4,10 @@ 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.2
7
- Stable tag: 3.9.7.1
8
 
9
  == Description ==
10
- Visit our [website](http://slimstat.getused.to.it/) for more information and to [watch our introductory videos](http://slimstat.getused.to.it/features/video-tutorials/).
11
 
12
  = Key Features =
13
  * Real-time activity log, server latency, heatmaps, email reports, export data to Excel, and much more
@@ -16,6 +16,14 @@ Visit our [website](http://slimstat.getused.to.it/) for more information and to
16
  * Available in multiple languages: English, Chinese (沐熙工作室), Farsi ([Dean](http://www.mangallery.net)), French (Michael Bastin, Jean-Michel Venet, Yves Pouplard, Henrick Kac), German (TechnoViel), Italian, Japanese (h_a_l_f), Portuguese, Russian ([Vitaly](http://www.visbiz.org/)), Spanish ([WebHostingHub](http://www.webhostinghub.com/)), Swedish (Per Soderman). Is your language missing or incomplete? [Contact Us](http://support.getused.to.it/) if you would like to share your localization.
17
  * World Map that works on your mobile device, too (courtesy of [amMap](http://www.ammap.com/)).
18
 
 
 
 
 
 
 
 
 
19
  = What are people saying about Slimstat? =
20
  * One of the 15+ Cool Free SEO Plugins for WordPress - [udesign](http://www.pixeldetail.com/wordpress/free-seo-plugins-for-wordpress/)
21
  * Thanks you for such an excellent plugin. I am using it to kick Jetpack out of all the wordpress installations that I manage for myself and others - [robertwagnervt](http://wordpress.org/support/topic/plugin-wp-slimstat-excellent-but-some-errors-on-activating)
@@ -62,8 +70,10 @@ Our knowledge base is available on our [support center](https://slimstat.freshde
62
  5. **Responsive layout** - Keep an eye on your reports on the go
63
 
64
  == Changelog ==
65
- = 3.9.7.1 =
66
- * The team who manages the WordPress Plugin Repository notified us that since the [MaxMind GeoLite library](http://dev.maxmind.com/geoip/legacy/geolite/) used by Slimstat to geolocate visitors is not released under the GNU General Public License (or compatible), it violates the repository guidelines, and cannot be shipped with the plugin any longer. We were required to remove the code and alter the plugin so that this functionality becomes optional. We apologize for the inconvenience, and even if we really tried hard to convince the repo administrators of our good intentions, we are deactivating the geolocation functionality starting from now. We are already at work to find an alternative solution that satisfies the repo's rules and regulations. Stay tuned.
 
 
67
 
68
  = 3.9.7 =
69
  * [Note] The uninstall routine now deletes the archive table (wp_slim_stats_archive) along with all the other tables (thank you, KalleL)
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.2
7
+ Stable tag: 3.9.8
8
 
9
  == Description ==
10
+ [youtube https://www.youtube.com/watch?v=iJCtjxArq4U]
11
 
12
  = Key Features =
13
  * Real-time activity log, server latency, heatmaps, email reports, export data to Excel, and much more
16
  * Available in multiple languages: English, Chinese (沐熙工作室), Farsi ([Dean](http://www.mangallery.net)), French (Michael Bastin, Jean-Michel Venet, Yves Pouplard, Henrick Kac), German (TechnoViel), Italian, Japanese (h_a_l_f), Portuguese, Russian ([Vitaly](http://www.visbiz.org/)), Spanish ([WebHostingHub](http://www.webhostinghub.com/)), Swedish (Per Soderman). Is your language missing or incomplete? [Contact Us](http://support.getused.to.it/) if you would like to share your localization.
17
  * World Map that works on your mobile device, too (courtesy of [amMap](http://www.ammap.com/)).
18
 
19
+ = Track countries by IP Address =
20
+ Starting from Slimstat 3.9.8, we removed the functionality that allows the tracker to identify a visitor's country based on his IP address.
21
+ The team who manages the WordPress Plugin Repository notified us that since the [MaxMind GeoLite library](http://dev.maxmind.com/geoip/legacy/geolite/)
22
+ used by our plugin to geolocate visitors is released under the Creative Commons BY-SA 3.0 license, it violates the repository guidelines, and cannot
23
+ be bundled with the code. We were required to remove it and alter the plugin so that this functionality becomes optional. By default, your visitors' country
24
+ will be set to Unknown. You can download the geolocation DB as a [separate add-on](http://slimstat.getused.to.it/downloads/get-country/) on our website, free of charge,
25
+ to restore this feature. Don't forget to enter your license key in the corresponding field under Slimstat > Add-ons, to receive free updates!
26
+
27
  = What are people saying about Slimstat? =
28
  * One of the 15+ Cool Free SEO Plugins for WordPress - [udesign](http://www.pixeldetail.com/wordpress/free-seo-plugins-for-wordpress/)
29
  * Thanks you for such an excellent plugin. I am using it to kick Jetpack out of all the wordpress installations that I manage for myself and others - [robertwagnervt](http://wordpress.org/support/topic/plugin-wp-slimstat-excellent-but-some-errors-on-activating)
70
  5. **Responsive layout** - Keep an eye on your reports on the go
71
 
72
  == Changelog ==
73
+
74
+ = 3.9.8 =
75
+ * [Note] The team who manages the WordPress Plugin Repository notified us that since the [MaxMind GeoLite library](http://dev.maxmind.com/geoip/legacy/geolite/) used by Slimstat to geolocate visitors is released under the Creative Commons BY-SA 3.0 license, it violates the repository guidelines, and cannot be bundled with the plugin. We were required to remove the code and alter the plugin so that this functionality becomes optional. We apologize for the inconvenience. However, the only immediate consequence is that your visitors' country will not be identified; everything else will still work as usual. You can download the geolocation DB as a [separate add-on](http://slimstat.getused.to.it/downloads/get-country/) on our store, free of charge. Don't forget to enter your license key in the corresponding field under Slimstat > Add-ons, to receive free updates!
76
+ * [New] A few new options under Slimstat > Settings > General tab > WordPress Integration section, allow you to have more control over the information displayed in the Posts admin screen (thank you, Brad).
77
 
78
  = 3.9.7 =
79
  * [Note] The uninstall routine now deletes the archive table (wp_slim_stats_archive) along with all the other tables (thank you, KalleL)
wp-slimstat.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP Slimstat
4
  Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
  Description: The leading web analytics plugin for WordPress
6
- Version: 3.9.7.1
7
  Author: Camu
8
  Author URI: http://slimstat.getused.to.it/
9
  */
@@ -11,7 +11,7 @@ Author URI: http://slimstat.getused.to.it/
11
  if (!empty(wp_slimstat::$options)) return true;
12
 
13
  class wp_slimstat{
14
- public static $version = '3.9.7.1';
15
  public static $options = array();
16
 
17
  public static $wpdb = '';
@@ -489,7 +489,7 @@ class wp_slimstat{
489
  // end slimtrack
490
 
491
  /**
492
- * Searches for country associated to a given IP address
493
  */
494
  public static function get_country($_ipnum = 0){
495
  $float_ipnum = (float)sprintf("%u", $_ipnum);
@@ -501,39 +501,8 @@ class wp_slimstat{
501
  ($float_ipnum >= 3232235521 && $float_ipnum <= 3232301055) ){ // 192.168.0.1 - 192.168.255.255
502
  return 'xy';
503
  }
504
-
505
- $country_codes = array("","ap","eu","ad","ae","af","ag","ai","al","am","cw","ao","aq","ar","as","at","au","aw","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bm","bn","bo","br","bs","bt","bv","bw","by","bz","ca","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","cr","cu","cv","cx","cy","cz","de","dj","dk","dm","do","dz","ec","ee","eg","eh","er","es","et","fi","fj","fk","fm","fo","fr","sx","ga","gb","gd","ge","gf","gh","gi","gl","gm","gn","gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","in","io","iq","ir","is","it","jm","jo","jp","ke","kg","kh","ki","km","kn","kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md","mg","mh","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","na","nc","ne","nf","ng","ni","nl","no","np","nr","nu","nz","om","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","ps","pt","pw","py","qa","re","ro","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","st","sv","sy","sz","tc","td","tf","tg","th","tj","tk","tm","tn","to","tl","tr","tt","tv","tw","tz","ua","ug","um","us","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","ye","yt","rs","za","zm","me","zw","a1","a2","o1","ax","gg","im","je","bl","mf","bq","ss","o1");
506
- if (!$handle = fopen(WP_PLUGIN_DIR."/wp-slimstat/databases/maxmind.dat", "rb")){
507
- return 'xx';
508
- }
509
-
510
- $offset = 0;
511
- for ($depth = 31; $depth >= 0; --$depth) {
512
- if (fseek($handle, 6 * $offset, SEEK_SET) != 0) return 'xx';
513
- $buf = fread($handle, 6);
514
- $x = array(0,0);
515
- for ($i = 0; $i < 2; ++$i) {
516
- for ($j = 0; $j < 3; ++$j) {
517
- $x[$i] += ord(substr($buf, 3 * $i + $j, 1)) << ($j * 8);
518
- }
519
- }
520
 
521
- if ($_ipnum & (1 << $depth)) {
522
- if ($x[1] >= 16776960 && !empty($country_codes[$x[1] - 16776960])) {
523
- fclose($handle);
524
- return $country_codes[$x[1] - 16776960];
525
- }
526
- $offset = $x[1];
527
- } else {
528
- if ($x[0] >= 16776960 && !empty($country_codes[$x[0] - 16776960])) {
529
- fclose($handle);
530
- return $country_codes[$x[0] - 16776960];
531
- }
532
- $offset = $x[0];
533
- }
534
- }
535
- fclose($handle);
536
- return 'xx';
537
  }
538
  // end get_country
539
 
@@ -1182,6 +1151,8 @@ class wp_slimstat{
1182
  'enable_javascript' => $val_yes,
1183
  'javascript_mode' => $val_yes,
1184
  'add_posts_column' => $val_no,
 
 
1185
  'use_separate_menu' => $val_yes,
1186
  'auto_purge_delete' => $val_yes,
1187
  'auto_purge' => 0,
3
  Plugin Name: WP Slimstat
4
  Plugin URI: http://wordpress.org/plugins/wp-slimstat/
5
  Description: The leading web analytics plugin for WordPress
6
+ Version: 3.9.8
7
  Author: Camu
8
  Author URI: http://slimstat.getused.to.it/
9
  */
11
  if (!empty(wp_slimstat::$options)) return true;
12
 
13
  class wp_slimstat{
14
+ public static $version = '3.9.8';
15
  public static $options = array();
16
 
17
  public static $wpdb = '';
489
  // end slimtrack
490
 
491
  /**
492
+ * Searches for the country code associated to a given IP address
493
  */
494
  public static function get_country($_ipnum = 0){
495
  $float_ipnum = (float)sprintf("%u", $_ipnum);
501
  ($float_ipnum >= 3232235521 && $float_ipnum <= 3232301055) ){ // 192.168.0.1 - 192.168.255.255
502
  return 'xy';
503
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
 
505
+ return apply_filters('slimstat_get_country', 'xx', $_ipnum);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
  }
507
  // end get_country
508
 
1151
  'enable_javascript' => $val_yes,
1152
  'javascript_mode' => $val_yes,
1153
  'add_posts_column' => $val_no,
1154
+ 'posts_column_day_interval' => 30,
1155
+ 'posts_column_pageviews' => $val_yes,
1156
  'use_separate_menu' => $val_yes,
1157
  'auto_purge_delete' => $val_yes,
1158
  'auto_purge' => 0,